line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#line 1 |
2
|
|
|
|
|
|
|
package File::Path; |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
use 5.005_04; |
5
|
|
|
|
|
|
|
use strict; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use Cwd 'getcwd'; |
8
|
|
|
|
|
|
|
use File::Basename (); |
9
|
|
|
|
|
|
|
use File::Spec (); |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
BEGIN { |
12
|
|
|
|
|
|
|
if ($] < 5.006) { |
13
|
|
|
|
|
|
|
# can't say 'opendir my $dh, $dirname' |
14
|
|
|
|
|
|
|
# need to initialise $dh |
15
|
|
|
|
|
|
|
eval "use Symbol"; |
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
} |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
use Exporter (); |
20
|
|
|
|
|
|
|
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); |
21
|
|
|
|
|
|
|
$VERSION = '2.08_01'; |
22
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
23
|
|
|
|
|
|
|
@EXPORT = qw(mkpath rmtree); |
24
|
|
|
|
|
|
|
@EXPORT_OK = qw(make_path remove_tree); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $Is_VMS = $^O eq 'VMS'; |
27
|
|
|
|
|
|
|
my $Is_MacOS = $^O eq 'MacOS'; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# These OSes complain if you want to remove a file that you have no |
30
|
|
|
|
|
|
|
# write permission to: |
31
|
|
|
|
|
|
|
my $Force_Writeable = grep {$^O eq $_} qw(amigaos dos epoc MSWin32 MacOS os2); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Unix-like systems need to stat each directory in order to detect |
34
|
|
|
|
|
|
|
# race condition. MS-Windows is immune to this particular attack. |
35
|
|
|
|
|
|
|
my $Need_Stat_Check = !($^O eq 'MSWin32'); |
36
|
|
|
|
|
|
|
|
37
|
0
|
|
|
0
|
|
|
sub _carp { |
38
|
0
|
|
|
|
|
|
require Carp; |
39
|
|
|
|
|
|
|
goto &Carp::carp; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
0
|
|
|
0
|
|
|
sub _croak { |
43
|
0
|
|
|
|
|
|
require Carp; |
44
|
|
|
|
|
|
|
goto &Carp::croak; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
0
|
|
|
0
|
|
|
sub _error { |
48
|
0
|
|
|
|
|
|
my $arg = shift; |
49
|
0
|
|
|
|
|
|
my $message = shift; |
50
|
|
|
|
|
|
|
my $object = shift; |
51
|
0
|
0
|
|
|
|
|
|
52
|
0
|
0
|
|
|
|
|
if ($arg->{error}) { |
53
|
0
|
0
|
|
|
|
|
$object = '' unless defined $object; |
54
|
0
|
|
|
|
|
|
$message .= ": $!" if $!; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
push @{${$arg->{error}}}, {$object => $message}; |
56
|
|
|
|
|
|
|
} |
57
|
0
|
0
|
|
|
|
|
else { |
58
|
|
|
|
|
|
|
_carp(defined($object) ? "$message for $object: $!" : "$message: $!"); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
0
|
0
|
0
|
0
|
1
|
|
sub make_path { |
63
|
0
|
|
|
|
|
|
push @_, {} unless @_ and UNIVERSAL::isa($_[-1],'HASH'); |
64
|
|
|
|
|
|
|
goto &mkpath; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
0
|
|
0
|
0
|
1
|
|
sub mkpath { |
68
|
|
|
|
|
|
|
my $old_style = !(@_ and UNIVERSAL::isa($_[-1],'HASH')); |
69
|
0
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
my $arg; |
71
|
|
|
|
|
|
|
my $paths; |
72
|
0
|
0
|
|
|
|
|
|
73
|
0
|
|
|
|
|
|
if ($old_style) { |
74
|
0
|
|
|
|
|
|
my ($verbose, $mode); |
75
|
0
|
0
|
|
|
|
|
($paths, $verbose, $mode) = @_; |
76
|
0
|
|
|
|
|
|
$paths = [$paths] unless UNIVERSAL::isa($paths,'ARRAY'); |
77
|
0
|
0
|
|
|
|
|
$arg->{verbose} = $verbose; |
78
|
|
|
|
|
|
|
$arg->{mode} = defined $mode ? $mode : 0777; |
79
|
|
|
|
|
|
|
} |
80
|
0
|
|
|
|
|
|
else { |
81
|
0
|
0
|
|
|
|
|
$arg = pop @_; |
82
|
0
|
0
|
|
|
|
|
$arg->{mode} = delete $arg->{mask} if exists $arg->{mask}; |
83
|
0
|
0
|
|
|
|
|
$arg->{mode} = 0777 unless exists $arg->{mode}; |
|
0
|
|
|
|
|
|
|
84
|
0
|
0
|
|
|
|
|
${$arg->{error}} = [] if exists $arg->{error}; |
85
|
0
|
0
|
|
|
|
|
$arg->{owner} = delete $arg->{user} if exists $arg->{user}; |
86
|
0
|
0
|
0
|
|
|
|
$arg->{owner} = delete $arg->{uid} if exists $arg->{uid}; |
87
|
0
|
|
|
|
|
|
if (exists $arg->{owner} and $arg->{owner} =~ /\D/) { |
88
|
0
|
0
|
|
|
|
|
my $uid = (getpwnam $arg->{owner})[2]; |
89
|
0
|
|
|
|
|
|
if (defined $uid) { |
90
|
|
|
|
|
|
|
$arg->{owner} = $uid; |
91
|
|
|
|
|
|
|
} |
92
|
0
|
|
|
|
|
|
else { |
93
|
0
|
|
|
|
|
|
_error($arg, "unable to map $arg->{owner} to a uid, ownership not changed"); |
94
|
|
|
|
|
|
|
delete $arg->{owner}; |
95
|
|
|
|
|
|
|
} |
96
|
0
|
0
|
0
|
|
|
|
} |
97
|
0
|
|
|
|
|
|
if (exists $arg->{group} and $arg->{group} =~ /\D/) { |
98
|
0
|
0
|
|
|
|
|
my $gid = (getgrnam $arg->{group})[2]; |
99
|
0
|
|
|
|
|
|
if (defined $gid) { |
100
|
|
|
|
|
|
|
$arg->{group} = $gid; |
101
|
|
|
|
|
|
|
} |
102
|
0
|
|
|
|
|
|
else { |
103
|
0
|
|
|
|
|
|
_error($arg, "unable to map $arg->{group} to a gid, group ownership not changed"); |
104
|
|
|
|
|
|
|
delete $arg->{group}; |
105
|
|
|
|
|
|
|
} |
106
|
0
|
0
|
0
|
|
|
|
} |
107
|
0
|
|
|
|
|
|
if (exists $arg->{owner} and not exists $arg->{group}) { |
108
|
|
|
|
|
|
|
$arg->{group} = -1; # chown will leave group unchanged |
109
|
0
|
0
|
0
|
|
|
|
} |
110
|
0
|
|
|
|
|
|
if (exists $arg->{group} and not exists $arg->{owner}) { |
111
|
|
|
|
|
|
|
$arg->{owner} = -1; # chown will leave owner unchanged |
112
|
0
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
$paths = [@_]; |
114
|
0
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
return _mkpath($arg, $paths); |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
0
|
|
|
sub _mkpath { |
119
|
0
|
|
|
|
|
|
my $arg = shift; |
120
|
|
|
|
|
|
|
my $paths = shift; |
121
|
0
|
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
my(@created,$path); |
123
|
0
|
0
|
0
|
|
|
|
foreach $path (@$paths) { |
124
|
0
|
0
|
0
|
|
|
|
next unless defined($path) and length($path); |
125
|
|
|
|
|
|
|
$path .= '/' if $^O eq 'os2' and $path =~ /^\w:\z/s; # feature of CRT |
126
|
0
|
0
|
|
|
|
|
# Logic wants Unix paths, so go with the flow. |
127
|
0
|
0
|
|
|
|
|
if ($Is_VMS) { |
128
|
0
|
|
|
|
|
|
next if $path eq '/'; |
129
|
|
|
|
|
|
|
$path = VMS::Filespec::unixify($path); |
130
|
0
|
0
|
|
|
|
|
} |
131
|
0
|
|
|
|
|
|
next if -d $path; |
132
|
0
|
0
|
0
|
|
|
|
my $parent = File::Basename::dirname($path); |
133
|
0
|
|
|
|
|
|
unless (-d $parent or $path eq $parent) { |
134
|
|
|
|
|
|
|
push(@created,_mkpath($arg, [$parent])); |
135
|
0
|
0
|
|
|
|
|
} |
136
|
0
|
0
|
|
|
|
|
print "mkdir $path\n" if $arg->{verbose}; |
137
|
0
|
|
|
|
|
|
if (mkdir($path,$arg->{mode})) { |
138
|
0
|
0
|
|
|
|
|
push(@created, $path); |
139
|
|
|
|
|
|
|
if (exists $arg->{owner}) { |
140
|
0
|
0
|
|
|
|
|
# NB: $arg->{group} guaranteed to be set during initialisation |
141
|
0
|
|
|
|
|
|
if (!chown $arg->{owner}, $arg->{group}, $path) { |
142
|
|
|
|
|
|
|
_error($arg, "Cannot change ownership of $path to $arg->{owner}:$arg->{group}"); |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
} |
146
|
0
|
|
|
|
|
|
else { |
147
|
0
|
|
|
|
|
|
my $save_bang = $!; |
148
|
0
|
0
|
|
|
|
|
my ($e, $e1) = ($save_bang, $^E); |
149
|
|
|
|
|
|
|
$e .= "; $e1" if $e ne $e1; |
150
|
0
|
0
|
|
|
|
|
# allow for another process to have created it meanwhile |
151
|
0
|
|
|
|
|
|
if (!-d $path) { |
152
|
0
|
0
|
|
|
|
|
$! = $save_bang; |
153
|
0
|
|
|
|
|
|
if ($arg->{error}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
push @{${$arg->{error}}}, {$path => $e}; |
155
|
|
|
|
|
|
|
} |
156
|
0
|
|
|
|
|
|
else { |
157
|
|
|
|
|
|
|
_croak("mkdir $path: $e"); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
} |
161
|
0
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
return @created; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
0
|
0
|
0
|
0
|
1
|
|
sub remove_tree { |
166
|
0
|
|
|
|
|
|
push @_, {} unless @_ and UNIVERSAL::isa($_[-1],'HASH'); |
167
|
|
|
|
|
|
|
goto &rmtree; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
0
|
|
|
0
|
|
|
sub _is_subdir { |
171
|
|
|
|
|
|
|
my($dir, $test) = @_; |
172
|
0
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
my($dv, $dd) = File::Spec->splitpath($dir, 1); |
174
|
|
|
|
|
|
|
my($tv, $td) = File::Spec->splitpath($test, 1); |
175
|
|
|
|
|
|
|
|
176
|
0
|
0
|
|
|
|
|
# not on same volume |
177
|
|
|
|
|
|
|
return 0 if $dv ne $tv; |
178
|
0
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
my @d = File::Spec->splitdir($dd); |
180
|
|
|
|
|
|
|
my @t = File::Spec->splitdir($td); |
181
|
|
|
|
|
|
|
|
182
|
0
|
0
|
|
|
|
|
# @t can't be a subdir if it's shorter than @d |
183
|
|
|
|
|
|
|
return 0 if @t < @d; |
184
|
0
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
return join('/', @d) eq join('/', splice @t, 0, +@d); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
0
|
|
0
|
0
|
1
|
|
sub rmtree { |
189
|
|
|
|
|
|
|
my $old_style = !(@_ and UNIVERSAL::isa($_[-1],'HASH')); |
190
|
0
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
my $arg; |
192
|
|
|
|
|
|
|
my $paths; |
193
|
0
|
0
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
if ($old_style) { |
195
|
0
|
|
|
|
|
|
my ($verbose, $safe); |
196
|
0
|
|
|
|
|
|
($paths, $verbose, $safe) = @_; |
197
|
0
|
0
|
|
|
|
|
$arg->{verbose} = $verbose; |
198
|
|
|
|
|
|
|
$arg->{safe} = defined $safe ? $safe : 0; |
199
|
0
|
0
|
0
|
|
|
|
|
200
|
0
|
0
|
|
|
|
|
if (defined($paths) and length($paths)) { |
201
|
|
|
|
|
|
|
$paths = [$paths] unless UNIVERSAL::isa($paths,'ARRAY'); |
202
|
|
|
|
|
|
|
} |
203
|
0
|
|
|
|
|
|
else { |
204
|
0
|
|
|
|
|
|
_carp ("No root path(s) specified\n"); |
205
|
|
|
|
|
|
|
return 0; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
} |
208
|
0
|
|
|
|
|
|
else { |
209
|
0
|
0
|
|
|
|
|
$arg = pop @_; |
|
0
|
|
|
|
|
|
|
210
|
0
|
0
|
|
|
|
|
${$arg->{error}} = [] if exists $arg->{error}; |
|
0
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
|
${$arg->{result}} = [] if exists $arg->{result}; |
212
|
|
|
|
|
|
|
$paths = [@_]; |
213
|
|
|
|
|
|
|
} |
214
|
0
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
|
$arg->{prefix} = ''; |
216
|
|
|
|
|
|
|
$arg->{depth} = 0; |
217
|
0
|
|
|
|
|
|
|
218
|
0
|
0
|
|
|
|
|
my @clean_path; |
219
|
0
|
|
|
|
|
|
$arg->{cwd} = getcwd() or do { |
220
|
0
|
|
|
|
|
|
_error($arg, "cannot fetch initial working directory"); |
221
|
|
|
|
|
|
|
return 0; |
222
|
0
|
|
|
|
|
|
}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
for ($arg->{cwd}) { /\A(.*)\Z/; $_ = $1 } # untaint |
224
|
0
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
for my $p (@$paths) { |
226
|
0
|
0
|
|
|
|
|
# need to fixup case and map \ to / on Windows |
227
|
0
|
0
|
|
|
|
|
my $ortho_root = $^O eq 'MSWin32' ? _slash_lc($p) : $p; |
228
|
0
|
|
|
|
|
|
my $ortho_cwd = $^O eq 'MSWin32' ? _slash_lc($arg->{cwd}) : $arg->{cwd}; |
229
|
0
|
0
|
|
|
|
|
my $ortho_root_length = length($ortho_root); |
230
|
0
|
0
|
0
|
|
|
|
$ortho_root_length-- if $^O eq 'VMS'; # don't compare '.' with ']' |
231
|
0
|
|
|
|
|
|
if ($ortho_root_length && _is_subdir($ortho_root, $ortho_cwd)) { |
232
|
0
|
|
|
|
|
|
local $! = 0; |
233
|
0
|
|
|
|
|
|
_error($arg, "cannot remove path when cwd is $arg->{cwd}", $p); |
234
|
|
|
|
|
|
|
next; |
235
|
|
|
|
|
|
|
} |
236
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
237
|
0
|
0
|
|
|
|
|
if ($Is_MacOS) { |
238
|
0
|
0
|
|
|
|
|
$p = ":$p" unless $p =~ /:/; |
239
|
|
|
|
|
|
|
$p .= ":" unless $p =~ /:\z/; |
240
|
|
|
|
|
|
|
} |
241
|
0
|
|
|
|
|
|
elsif ($^O eq 'MSWin32') { |
242
|
|
|
|
|
|
|
$p =~ s{[/\\]\z}{}; |
243
|
|
|
|
|
|
|
} |
244
|
0
|
|
|
|
|
|
else { |
245
|
|
|
|
|
|
|
$p =~ s{/\z}{}; |
246
|
0
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
push @clean_path, $p; |
248
|
|
|
|
|
|
|
} |
249
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
250
|
0
|
|
|
|
|
|
@{$arg}{qw(device inode perm)} = (lstat $arg->{cwd})[0,1] or do { |
251
|
0
|
|
|
|
|
|
_error($arg, "cannot stat initial working directory", $arg->{cwd}); |
252
|
|
|
|
|
|
|
return 0; |
253
|
|
|
|
|
|
|
}; |
254
|
0
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
return _rmtree($arg, \@clean_path); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
0
|
|
|
0
|
|
|
sub _rmtree { |
259
|
0
|
|
|
|
|
|
my $arg = shift; |
260
|
|
|
|
|
|
|
my $paths = shift; |
261
|
0
|
|
|
|
|
|
|
262
|
0
|
|
|
|
|
|
my $count = 0; |
263
|
0
|
|
|
|
|
|
my $curdir = File::Spec->curdir(); |
264
|
|
|
|
|
|
|
my $updir = File::Spec->updir(); |
265
|
0
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
my (@files, $root); |
267
|
0
|
|
|
|
|
|
ROOT_DIR: |
268
|
|
|
|
|
|
|
foreach $root (@$paths) { |
269
|
|
|
|
|
|
|
# since we chdir into each directory, it may not be obvious |
270
|
|
|
|
|
|
|
# to figure out where we are if we generate a message about |
271
|
|
|
|
|
|
|
# a file name. We therefore construct a semi-canonical |
272
|
|
|
|
|
|
|
# filename, anchored from the directory being unlinked (as |
273
|
|
|
|
|
|
|
# opposed to being truly canonical, anchored from the root (/). |
274
|
0
|
0
|
|
|
|
|
|
275
|
|
|
|
|
|
|
my $canon = $arg->{prefix} |
276
|
|
|
|
|
|
|
? File::Spec->catfile($arg->{prefix}, $root) |
277
|
|
|
|
|
|
|
: $root |
278
|
|
|
|
|
|
|
; |
279
|
0
|
0
|
|
|
|
|
|
280
|
|
|
|
|
|
|
my ($ldev, $lino, $perm) = (lstat $root)[0,1,2] or next ROOT_DIR; |
281
|
0
|
0
|
|
|
|
|
|
282
|
0
|
0
|
|
|
|
|
if ( -d _ ) { |
283
|
|
|
|
|
|
|
$root = VMS::Filespec::vmspath(VMS::Filespec::pathify($root)) if $Is_VMS; |
284
|
0
|
0
|
|
|
|
|
|
285
|
|
|
|
|
|
|
if (!chdir($root)) { |
286
|
|
|
|
|
|
|
# see if we can escalate privileges to get in |
287
|
0
|
|
|
|
|
|
# (e.g. funny protection mask such as -w- instead of rwx) |
288
|
0
|
|
|
|
|
|
$perm &= 07777; |
289
|
0
|
0
|
0
|
|
|
|
my $nperm = $perm | 0700; |
|
|
0
|
|
|
|
|
|
290
|
0
|
|
|
|
|
|
if (!($arg->{safe} or $nperm == $perm or chmod($nperm, $root))) { |
291
|
0
|
|
|
|
|
|
_error($arg, "cannot make child directory read-write-exec", $canon); |
292
|
|
|
|
|
|
|
next ROOT_DIR; |
293
|
|
|
|
|
|
|
} |
294
|
0
|
|
|
|
|
|
elsif (!chdir($root)) { |
295
|
0
|
|
|
|
|
|
_error($arg, "cannot chdir to child", $canon); |
296
|
|
|
|
|
|
|
next ROOT_DIR; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
} |
299
|
0
|
0
|
|
|
|
|
|
300
|
0
|
|
|
|
|
|
my ($cur_dev, $cur_inode, $perm) = (stat $curdir)[0,1,2] or do { |
301
|
0
|
|
|
|
|
|
_error($arg, "cannot stat current working directory", $canon); |
302
|
|
|
|
|
|
|
next ROOT_DIR; |
303
|
|
|
|
|
|
|
}; |
304
|
0
|
0
|
|
|
|
|
|
305
|
0
|
0
|
0
|
|
|
|
if ($Need_Stat_Check) { |
306
|
|
|
|
|
|
|
($ldev eq $cur_dev and $lino eq $cur_inode) |
307
|
|
|
|
|
|
|
or _croak("directory $canon changed before chdir, expected dev=$ldev ino=$lino, actual dev=$cur_dev ino=$cur_inode, aborting."); |
308
|
|
|
|
|
|
|
} |
309
|
0
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
|
$perm &= 07777; # don't forget setuid, setgid, sticky bits |
311
|
|
|
|
|
|
|
my $nperm = $perm | 0700; |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
# notabene: 0700 is for making readable in the first place, |
314
|
|
|
|
|
|
|
# it's also intended to change it to writable in case we have |
315
|
|
|
|
|
|
|
# to recurse in which case we are better than rm -rf for |
316
|
|
|
|
|
|
|
# subtrees with strange permissions |
317
|
0
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
318
|
0
|
|
|
|
|
|
if (!($arg->{safe} or $nperm == $perm or chmod($nperm, $curdir))) { |
319
|
0
|
|
|
|
|
|
_error($arg, "cannot make directory read+writeable", $canon); |
320
|
|
|
|
|
|
|
$nperm = $perm; |
321
|
|
|
|
|
|
|
} |
322
|
0
|
|
|
|
|
|
|
323
|
0
|
0
|
|
|
|
|
my $d; |
324
|
0
|
0
|
|
|
|
|
$d = gensym() if $] < 5.006; |
325
|
0
|
|
|
|
|
|
if (!opendir $d, $curdir) { |
326
|
0
|
|
|
|
|
|
_error($arg, "cannot opendir", $canon); |
327
|
|
|
|
|
|
|
@files = (); |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
else { |
330
|
0
|
0
|
0
|
|
|
|
no strict 'refs'; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
if (!defined ${"\cTAINT"} or ${"\cTAINT"}) { |
332
|
|
|
|
|
|
|
# Blindly untaint dir names if taint mode is |
333
|
0
|
|
|
|
|
|
# active, or any perl < 5.006 |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
@files = map { /\A(.*)\z/s; $1 } readdir $d; |
335
|
|
|
|
|
|
|
} |
336
|
0
|
|
|
|
|
|
else { |
337
|
|
|
|
|
|
|
@files = readdir $d; |
338
|
0
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
closedir $d; |
340
|
|
|
|
|
|
|
} |
341
|
0
|
0
|
|
|
|
|
|
342
|
|
|
|
|
|
|
if ($Is_VMS) { |
343
|
|
|
|
|
|
|
# Deleting large numbers of files from VMS Files-11 |
344
|
|
|
|
|
|
|
# filesystems is faster if done in reverse ASCIIbetical order. |
345
|
0
|
0
|
|
|
|
|
# include '.' to '.;' from blead patch #31775 |
|
0
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
@files = map {$_ eq '.' ? '.;' : $_} reverse @files; |
347
|
|
|
|
|
|
|
} |
348
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
@files = grep {$_ ne $updir and $_ ne $curdir} @files; |
350
|
0
|
0
|
|
|
|
|
|
351
|
|
|
|
|
|
|
if (@files) { |
352
|
0
|
|
|
|
|
|
# remove the contained files before the directory itself |
353
|
0
|
|
|
|
|
|
my $narg = {%$arg}; |
|
0
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
@{$narg}{qw(device inode cwd prefix depth)} |
355
|
0
|
|
|
|
|
|
= ($cur_dev, $cur_inode, $updir, $canon, $arg->{depth}+1); |
356
|
|
|
|
|
|
|
$count += _rmtree($narg, \@files); |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
# restore directory permissions of required now (in case the rmdir |
360
|
|
|
|
|
|
|
# below fails), while we are still in the directory and may do so |
361
|
0
|
0
|
0
|
|
|
|
# without a race via '.' |
362
|
0
|
|
|
|
|
|
if ($nperm != $perm and not chmod($perm, $curdir)) { |
363
|
|
|
|
|
|
|
_error($arg, "cannot reset chmod", $canon); |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
|
366
|
0
|
0
|
|
|
|
|
# don't leave the client code in an unexpected directory |
367
|
|
|
|
|
|
|
chdir($arg->{cwd}) |
368
|
|
|
|
|
|
|
or _croak("cannot chdir to $arg->{cwd} from $canon: $!, aborting."); |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# ensure that a chdir upwards didn't take us somewhere other |
371
|
0
|
0
|
|
|
|
|
# than we expected (see CVE-2002-0435) |
372
|
|
|
|
|
|
|
($cur_dev, $cur_inode) = (stat $curdir)[0,1] |
373
|
|
|
|
|
|
|
or _croak("cannot stat prior working directory $arg->{cwd}: $!, aborting."); |
374
|
0
|
0
|
|
|
|
|
|
375
|
0
|
0
|
0
|
|
|
|
if ($Need_Stat_Check) { |
376
|
|
|
|
|
|
|
($arg->{device} eq $cur_dev and $arg->{inode} eq $cur_inode) |
377
|
|
|
|
|
|
|
or _croak("previous directory $arg->{cwd} changed before entering $canon, expected dev=$ldev ino=$lino, actual dev=$cur_dev ino=$cur_inode, aborting."); |
378
|
|
|
|
|
|
|
} |
379
|
0
|
0
|
0
|
|
|
|
|
380
|
0
|
0
|
0
|
|
|
|
if ($arg->{depth} or !$arg->{keep_root}) { |
|
|
0
|
|
|
|
|
|
381
|
|
|
|
|
|
|
if ($arg->{safe} && |
382
|
0
|
0
|
|
|
|
|
($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) { |
383
|
0
|
|
|
|
|
|
print "skipped $root\n" if $arg->{verbose}; |
384
|
|
|
|
|
|
|
next ROOT_DIR; |
385
|
0
|
0
|
0
|
|
|
|
} |
386
|
0
|
|
|
|
|
|
if ($Force_Writeable and !chmod $perm | 0700, $root) { |
387
|
|
|
|
|
|
|
_error($arg, "cannot make directory writeable", $canon); |
388
|
0
|
0
|
|
|
|
|
} |
389
|
0
|
0
|
|
|
|
|
print "rmdir $root\n" if $arg->{verbose}; |
390
|
0
|
0
|
|
|
|
|
if (rmdir $root) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
391
|
0
|
|
|
|
|
|
push @{${$arg->{result}}}, $root if $arg->{result}; |
392
|
|
|
|
|
|
|
++$count; |
393
|
|
|
|
|
|
|
} |
394
|
0
|
|
|
|
|
|
else { |
395
|
0
|
0
|
0
|
|
|
|
_error($arg, "cannot remove directory", $canon); |
|
|
0
|
|
|
|
|
|
396
|
|
|
|
|
|
|
if ($Force_Writeable && !chmod($perm, ($Is_VMS ? VMS::Filespec::fileify($root) : $root)) |
397
|
0
|
|
|
|
|
|
) { |
398
|
|
|
|
|
|
|
_error($arg, sprintf("cannot restore permissions to 0%o",$perm), $canon); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
else { |
404
|
0
|
0
|
0
|
|
|
|
# not a directory |
|
|
|
0
|
|
|
|
|
405
|
|
|
|
|
|
|
$root = VMS::Filespec::vmsify("./$root") |
406
|
|
|
|
|
|
|
if $Is_VMS |
407
|
|
|
|
|
|
|
&& !File::Spec->file_name_is_absolute($root) |
408
|
|
|
|
|
|
|
&& ($root !~ m/(?]+/); # not already in VMS syntax |
409
|
0
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
410
|
|
|
|
|
|
|
if ($arg->{safe} && |
411
|
|
|
|
|
|
|
($Is_VMS ? !&VMS::Filespec::candelete($root) |
412
|
|
|
|
|
|
|
: !(-l $root || -w $root))) |
413
|
0
|
0
|
|
|
|
|
{ |
414
|
0
|
|
|
|
|
|
print "skipped $root\n" if $arg->{verbose}; |
415
|
|
|
|
|
|
|
next ROOT_DIR; |
416
|
|
|
|
|
|
|
} |
417
|
0
|
|
|
|
|
|
|
418
|
0
|
0
|
0
|
|
|
|
my $nperm = $perm & 07777 | 0600; |
|
|
|
0
|
|
|
|
|
419
|
0
|
|
|
|
|
|
if ($Force_Writeable and $nperm != $perm and not chmod $nperm, $root) { |
420
|
|
|
|
|
|
|
_error($arg, "cannot make file writeable", $canon); |
421
|
0
|
0
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
print "unlink $canon\n" if $arg->{verbose}; |
423
|
0
|
|
|
|
|
|
# delete all versions under VMS |
424
|
0
|
0
|
|
|
|
|
for (;;) { |
425
|
0
|
0
|
|
|
|
|
if (unlink $root) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
push @{${$arg->{result}}}, $root if $arg->{result}; |
427
|
|
|
|
|
|
|
} |
428
|
0
|
|
|
|
|
|
else { |
429
|
0
|
0
|
0
|
|
|
|
_error($arg, "cannot unlink file", $canon); |
430
|
|
|
|
|
|
|
$Force_Writeable and chmod($perm, $root) or |
431
|
0
|
|
|
|
|
|
_error($arg, sprintf("cannot restore permissions to 0%o",$perm), $canon); |
432
|
|
|
|
|
|
|
last; |
433
|
0
|
|
|
|
|
|
} |
434
|
0
|
0
|
0
|
|
|
|
++$count; |
435
|
|
|
|
|
|
|
last unless $Is_VMS && lstat $root; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
} |
438
|
0
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
return $count; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub _slash_lc { |
443
|
|
|
|
|
|
|
# fix up slashes and case on MSWin32 so that we can determine that |
444
|
0
|
|
|
0
|
|
|
# c:\path\to\dir is underneath C:/Path/To |
445
|
0
|
|
|
|
|
|
my $path = shift; |
446
|
0
|
|
|
|
|
|
$path =~ tr{\\}{/}; |
447
|
|
|
|
|
|
|
return lc($path); |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
1; |
451
|
|
|
|
|
|
|
__END__ |