| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package IO::File::AtomicChange; |
|
2
|
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
744345
|
use strict; |
|
|
7
|
|
|
|
|
62
|
|
|
|
7
|
|
|
|
|
180
|
|
|
4
|
7
|
|
|
7
|
|
31
|
use warnings; |
|
|
7
|
|
|
|
|
13
|
|
|
|
7
|
|
|
|
|
361
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.08'; |
|
7
|
|
|
|
|
|
|
|
|
8
|
7
|
|
|
7
|
|
44
|
use base qw(IO::File); |
|
|
7
|
|
|
|
|
11
|
|
|
|
7
|
|
|
|
|
978
|
|
|
9
|
7
|
|
|
7
|
|
7163
|
use Carp; |
|
|
7
|
|
|
|
|
12
|
|
|
|
7
|
|
|
|
|
410
|
|
|
10
|
7
|
|
|
7
|
|
649
|
use File::Temp qw(:mktemp); |
|
|
7
|
|
|
|
|
11196
|
|
|
|
7
|
|
|
|
|
1073
|
|
|
11
|
7
|
|
|
7
|
|
3328
|
use File::Copy; |
|
|
7
|
|
|
|
|
14447
|
|
|
|
7
|
|
|
|
|
6057
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub new { |
|
14
|
20
|
|
|
20
|
1
|
33984
|
my $class = shift; |
|
15
|
20
|
|
|
|
|
129
|
my $self = $class->SUPER::new(); |
|
16
|
20
|
|
|
|
|
790
|
$self->_temp_file(""); |
|
17
|
20
|
|
|
|
|
72
|
$self->_target_file(""); |
|
18
|
20
|
|
|
|
|
63
|
$self->_backup_dir(""); |
|
19
|
20
|
50
|
|
|
|
92
|
$self->open(@_) if @_; |
|
20
|
20
|
|
|
|
|
2025
|
$self; |
|
21
|
|
|
|
|
|
|
} |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub _accessor { |
|
24
|
174
|
|
|
174
|
|
310
|
my($self, $tag, $val) = @_; |
|
25
|
174
|
100
|
|
|
|
320
|
${*$self}{$tag} = $val if $val; |
|
|
44
|
|
|
|
|
175
|
|
|
26
|
174
|
|
|
|
|
765
|
return ${*$self}{$tag}; |
|
|
174
|
|
|
|
|
3665
|
|
|
27
|
|
|
|
|
|
|
} |
|
28
|
59
|
|
|
59
|
|
147
|
sub _temp_file { return shift->_accessor("io_file_atomicchange_temp", @_) } |
|
29
|
69
|
|
|
69
|
|
164
|
sub _target_file { return shift->_accessor("io_file_atomicchange_path", @_) } |
|
30
|
46
|
|
|
46
|
|
126
|
sub _backup_dir { return shift->_accessor("io_file_atomicchange_back", @_) } |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub DESTROY { |
|
33
|
20
|
100
|
|
20
|
|
1839
|
carp "[CAUTION] disposed object without closing file handle." unless $_[0]->_closed; |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub open { |
|
37
|
20
|
|
|
20
|
1
|
47
|
my ($self, $path, $mode, $opt) = @_; |
|
38
|
20
|
50
|
|
|
|
54
|
ref($self) or $self = $self->new; |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Because we do rename(2) atomically, temporary file must be in same |
|
41
|
|
|
|
|
|
|
# partion with target file. |
|
42
|
20
|
|
|
|
|
142
|
my $temp = mktemp("${path}.XXXXXX"); |
|
43
|
20
|
|
|
|
|
4443
|
$self->_temp_file($temp); |
|
44
|
20
|
|
|
|
|
93
|
$self->_target_file($path); |
|
45
|
|
|
|
|
|
|
|
|
46
|
20
|
100
|
|
|
|
341
|
copy_preserving_attr($path, $temp) if -f $path; |
|
47
|
20
|
100
|
|
|
|
97
|
if (exists $opt->{backup_dir}) { |
|
48
|
4
|
50
|
|
|
|
48
|
unless (-d $opt->{backup_dir}) { |
|
49
|
0
|
|
|
|
|
0
|
croak "no such directory: $opt->{backup_dir}"; |
|
50
|
|
|
|
|
|
|
} |
|
51
|
4
|
|
|
|
|
17
|
$self->_backup_dir($opt->{backup_dir}); |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
20
|
50
|
|
|
|
115
|
$self->SUPER::open($temp, $mode) ? $self : undef; |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub _closed { |
|
58
|
39
|
|
|
39
|
|
87
|
my $self = shift; |
|
59
|
39
|
|
|
|
|
148
|
my $tag = "io_file_atomicchange_closed"; |
|
60
|
|
|
|
|
|
|
|
|
61
|
39
|
|
|
|
|
61
|
my $oldval = ${*$self}{$tag}; |
|
|
39
|
|
|
|
|
389
|
|
|
62
|
39
|
100
|
|
|
|
157
|
${*$self}{$tag} = shift if @_; |
|
|
19
|
|
|
|
|
60
|
|
|
63
|
39
|
|
|
|
|
465
|
return $oldval; |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub close { |
|
67
|
19
|
|
|
19
|
0
|
693
|
my ($self, $die) = @_; |
|
68
|
19
|
50
|
|
|
|
151116
|
$self->sync() or croak "sync: $!"; |
|
69
|
19
|
50
|
|
|
|
189
|
unless ($self->_closed(1)) { |
|
70
|
19
|
50
|
|
|
|
178
|
if ($self->SUPER::close()) { |
|
71
|
|
|
|
|
|
|
|
|
72
|
19
|
100
|
100
|
|
|
1424
|
$self->backup if ($self->_backup_dir && -f $self->_target_file); |
|
73
|
|
|
|
|
|
|
|
|
74
|
19
|
0
|
|
|
|
351
|
rename($self->_temp_file, $self->_target_file) |
|
|
|
50
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
or ($die ? croak "close (rename) atomic file: $!\n" : return); |
|
76
|
|
|
|
|
|
|
} else { |
|
77
|
0
|
0
|
|
|
|
0
|
$die ? croak "close atomic file: $!\n" : return; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
} |
|
80
|
19
|
|
|
|
|
122
|
1; |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub copy_modown_to_temp { |
|
84
|
0
|
|
|
0
|
0
|
0
|
my($self) = @_; |
|
85
|
|
|
|
|
|
|
|
|
86
|
0
|
|
|
|
|
0
|
my($mode, $uid, $gid) = (stat($self->_target_file))[2,4,5]; |
|
87
|
0
|
|
|
|
|
0
|
chown $uid, $gid, $self->_temp_file; |
|
88
|
0
|
|
|
|
|
0
|
chmod $mode, $self->_temp_file; |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub backup { |
|
92
|
3
|
|
|
3
|
0
|
7
|
my($self) = @_; |
|
93
|
|
|
|
|
|
|
|
|
94
|
3
|
|
|
|
|
26
|
require Path::Class; |
|
95
|
3
|
|
|
|
|
1040
|
require POSIX; |
|
96
|
3
|
|
|
|
|
9883
|
require Time::HiRes; |
|
97
|
|
|
|
|
|
|
|
|
98
|
3
|
|
|
|
|
2355
|
my $basename = Path::Class::file($self->_target_file)->basename; |
|
99
|
|
|
|
|
|
|
|
|
100
|
3
|
|
|
|
|
588
|
my $backup_file; |
|
101
|
3
|
|
|
|
|
6
|
my $n = 0; |
|
102
|
3
|
|
|
|
|
11
|
while ($n < 7) { |
|
103
|
3
|
50
|
|
|
|
19
|
$backup_file = sprintf("%s/%s_%s.%d_%d%s", |
|
104
|
|
|
|
|
|
|
$self->_backup_dir, |
|
105
|
|
|
|
|
|
|
$basename, |
|
106
|
|
|
|
|
|
|
POSIX::strftime("%Y-%m-%d_%H%M%S",localtime()), |
|
107
|
|
|
|
|
|
|
(Time::HiRes::gettimeofday())[1], |
|
108
|
|
|
|
|
|
|
$$, |
|
109
|
|
|
|
|
|
|
($n == 0 ? "" : ".$n"), |
|
110
|
|
|
|
|
|
|
); |
|
111
|
3
|
50
|
|
|
|
76
|
last unless -f $backup_file; |
|
112
|
0
|
|
|
|
|
0
|
$n++; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
3
|
50
|
|
|
|
31
|
croak "already exists backup file: $backup_file" if -f $backup_file; |
|
115
|
|
|
|
|
|
|
|
|
116
|
3
|
|
|
|
|
11
|
copy_preserving_attr($self->_target_file, $backup_file); |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub delete { |
|
121
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
122
|
0
|
0
|
|
|
|
0
|
unless ($self->_closed(1)) { |
|
123
|
0
|
|
|
|
|
0
|
$self->SUPER::close(); |
|
124
|
0
|
|
|
|
|
0
|
return unlink($self->_temp_file); |
|
125
|
|
|
|
|
|
|
} |
|
126
|
0
|
|
|
|
|
0
|
1; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub detach { |
|
130
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
131
|
0
|
0
|
|
|
|
0
|
$self->SUPER::close() unless ($self->_closed(1)); |
|
132
|
0
|
|
|
|
|
0
|
1; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub copy_preserving_attr { |
|
136
|
15
|
|
|
15
|
0
|
44
|
my($from, $to) = @_; |
|
137
|
|
|
|
|
|
|
|
|
138
|
15
|
50
|
|
|
|
71
|
File::Copy::copy($from, $to) or croak $!; |
|
139
|
|
|
|
|
|
|
|
|
140
|
15
|
|
|
|
|
4997
|
my($mode, $uid, $gid, $atime, $mtime) = (stat($from))[2,4,5,8,9]; |
|
141
|
15
|
|
|
|
|
304
|
chown $uid, $gid, $to; |
|
142
|
15
|
|
|
|
|
446
|
chmod $mode, $to; |
|
143
|
15
|
|
|
|
|
339
|
utime $atime, $mtime, $to; |
|
144
|
15
|
|
|
|
|
53
|
1; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
1; |
|
149
|
|
|
|
|
|
|
__END__ |