File Coverage

lib/App/MtAws/IntermediateFile.pm
Criterion Covered Total %
statement 62 64 96.8
branch 18 24 75.0
condition 6 9 66.6
subroutine 14 14 100.0
pod 0 3 0.0
total 100 114 87.7


line stmt bran cond sub pod time code
1             # mt-aws-glacier - Amazon Glacier sync client
2             # Copyright (C) 2012-2014 Victor Efimov
3             # http://mt-aws.com (also http://vs-dev.com) vs@vs-dev.com
4             # License: GPLv3
5             #
6             # This file is part of "mt-aws-glacier"
7             #
8             # mt-aws-glacier is free software: you can redistribute it and/or modify
9             # it under the terms of the GNU General Public License as published by
10             # the Free Software Foundation, either version 3 of the License, or
11             # (at your option) any later version.
12             #
13             # mt-aws-glacier is distributed in the hope that it will be useful,
14             # but WITHOUT ANY WARRANTY; without even the implied warranty of
15             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16             # GNU General Public License for more details.
17             #
18             # You should have received a copy of the GNU General Public License
19             # along with this program. If not, see <http://www.gnu.org/licenses/>.
20              
21             package App::MtAws::IntermediateFile;
22              
23             our $VERSION = '1.114_2';
24              
25 24     24   250619 use strict;
  24         48  
  24         696  
26 24     24   101 use warnings;
  24         31  
  24         594  
27 24     24   100 use utf8;
  24         34  
  24         151  
28 24     24   538 use Carp;
  24         24  
  24         1331  
29 24     24   106 use File::Temp 0.16 ();
  24         870  
  24         472  
30 24     24   107 use File::Path;
  24         33  
  24         1124  
31 24     24   102 use File::Basename;
  24         55  
  24         1248  
32 24     24   119 use App::MtAws::Utils;
  24         37  
  24         3062  
33 24     24   125 use App::MtAws::Exceptions;
  24         46  
  24         16247  
34              
35              
36             sub new
37             {
38 27     27 0 10477751 my ($class, %args) = @_;
39 27         136 my $self = {};
40 27 100       437 defined ($self->{target_file} = delete $args{target_file}) or confess "target_file expected";
41 26         142 $self->{mtime} = delete $args{mtime};
42 26 100       315 confess "unknown arguments" if %args;
43 25         85 bless $self, $class;
44 25         211 $self->_init();
45 25         109 $self->{_init_pid} = $$;
46 25         92 return $self;
47             }
48              
49             sub _init
50             {
51 24     24   51 my ($self) = @_;
52 24         2669 my $dir = dirname($self->{target_file});
53 24         250 my $binary_dirname = binaryfilename $dir;
54 24 50       5173 eval { mkpath($binary_dirname); 1 } or do {
  24         2519  
  24         120  
55 0         0 die exception 'cannot_create_directory' =>
56             'Cannot create directory %string dir%, errors: %error%',
57             dir => $dir, error => hex_dump_string($@);
58             };
59             $self->{tmp} = eval {
60             # PID is needed cause child processes re-use random number generators, improves performance only, no risk of race cond.
61 24         468 File::Temp->new(TEMPLATE => "__mtglacier_temp${$}_XXXXXX", UNLINK => 1, SUFFIX => '.tmp', DIR => $binary_dirname)
62 24 50       62 } or do {
63 0         0 die exception 'cannot_create_tempfile' =>
64             'Cannot create temporary file in directory %string dir%, errors: %error%',
65             dir => $dir, error => hex_dump_string($@);
66             };
67 24         14044 my $binary_tempfile = $self->{tmp}->filename;
68 24         237 $self->{tempfile} = characterfilename($binary_tempfile);
69             # it's important to close file, it's filename can be passed to different process, and it can be locked
70 24 50       1833 close $self->{tmp} or confess;
71             }
72              
73             sub tempfilename
74             {
75 25 100   25 0 1631 shift->{tempfile} or confess;
76             }
77              
78             sub make_permanent
79             {
80 9     9 0 3541 my $self = shift;
81 9 100       98 confess "unknown arguments" if @_;
82 8         27 my $binary_target_filename = binaryfilename($self->{target_file});
83              
84 8 100       442 my $character_tempfile = delete $self->{tempfile} or confess "file already permanent or not initialized";
85 7         27 $self->{tmp}->unlink_on_destroy(0);
86 7         66 undef $self->{tmp};
87 7         190 my $binary_tempfile = binaryfilename($character_tempfile);
88              
89 7 50       365 chmod((0666 & ~umask), $binary_tempfile) or confess "cannot chmod file $character_tempfile";
90 7 100 33     67 utime $self->{mtime}, $self->{mtime}, $binary_tempfile or confess "cannot change mtime for $character_tempfile" if defined $self->{mtime};
91             rename $binary_tempfile, $binary_target_filename or
92             die exception "cannot_rename_file" => "Cannot rename file %string from% to %string to%",
93 7 50       293 from => $character_tempfile, to => $self->{target_file};
94             }
95              
96             # File::Temp < 0.19 does not have protection from calling destructor in fork'ed child
97             # and forking can happen any moments, some code in File::Spec/Cwd etc call it to exec external commands
98             # this workaround prevents this, however destruction order is undefined so that might just fail
99              
100             # we can try use File::Temp::tempfile() but it destroys temp files only on program exit
101             # (can workaround with DESTROY) + when handle is closed! (thats bad)
102             sub DESTROY
103             {
104 285     285   1293968 my ($self) = @_;
105 285         1585 local ($!, $@, $?);
106 1         40 eval { $self->{tmp}->unlink_on_destroy(0) }
107 285 50 100     3660 if ($self->{_init_pid} && $self->{_init_pid} != $$ && $self->{tmp});
      66        
108             }
109              
110             1;