Filename | /usr/local/perls/perl-5.26.1/lib/5.26.1/File/Path.pm |
Statements | Executed 2372028 statements in 53.6s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
102039 | 1 | 1 | 23.5s | 23.5s | CORE:unlink (opcode) | File::Path::
35412 | 2 | 1 | 8.81s | 53.9s | _rmtree (recurses: max depth 14, inclusive time 274s) | File::Path::
137596 | 2 | 1 | 7.09s | 7.09s | CORE:lstat (opcode) | File::Path::
35056 | 1 | 1 | 5.39s | 5.39s | CORE:rmdir (opcode) | File::Path::
35056 | 1 | 1 | 2.68s | 2.68s | CORE:readdir (opcode) | File::Path::
70112 | 2 | 1 | 628ms | 628ms | CORE:stat (opcode) | File::Path::
35056 | 1 | 1 | 622ms | 622ms | CORE:open_dir (opcode) | File::Path::
70112 | 2 | 1 | 417ms | 417ms | CORE:chdir (opcode) | File::Path::
35056 | 1 | 1 | 189ms | 189ms | CORE:closedir (opcode) | File::Path::
137100 | 2 | 1 | 109ms | 109ms | CORE:ftdir (opcode) | File::Path::
500 | 3 | 2 | 51.6ms | 54.0s | rmtree | File::Path::
500 | 1 | 1 | 25.3ms | 36.2ms | _is_subdir | File::Path::
500 | 1 | 1 | 2.89ms | 2.89ms | CORE:match (opcode) | File::Path::
505 | 2 | 1 | 2.60ms | 2.60ms | __is_arg | File::Path::
500 | 1 | 1 | 952µs | 952µs | CORE:subst (opcode) | File::Path::
5 | 5 | 4 | 180µs | 466µs | mkpath | File::Path::
5 | 1 | 1 | 79µs | 231µs | _mkpath | File::Path::
0 | 0 | 0 | 0s | 0s | BEGIN | File::Path::
0 | 0 | 0 | 0s | 0s | _carp | File::Path::
0 | 0 | 0 | 0s | 0s | _croak | File::Path::
0 | 0 | 0 | 0s | 0s | _error | File::Path::
0 | 0 | 0 | 0s | 0s | _slash_lc | File::Path::
0 | 0 | 0 | 0s | 0s | make_path | File::Path::
0 | 0 | 0 | 0s | 0s | remove_tree | File::Path::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package File::Path; | ||||
2 | |||||
3 | use 5.005_04; | ||||
4 | use strict; | ||||
5 | |||||
6 | use Cwd 'getcwd'; | ||||
7 | use File::Basename (); | ||||
8 | use File::Spec (); | ||||
9 | |||||
10 | BEGIN { | ||||
11 | if ( $] < 5.006 ) { | ||||
12 | |||||
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.12_01'; | ||||
22 | $VERSION = eval $VERSION; | ||||
23 | @ISA = qw(Exporter); | ||||
24 | @EXPORT = qw(mkpath rmtree); | ||||
25 | @EXPORT_OK = qw(make_path remove_tree); | ||||
26 | |||||
27 | BEGIN { | ||||
28 | for (qw(VMS MacOS MSWin32 os2)) { | ||||
29 | no strict 'refs'; | ||||
30 | *{"_IS_\U$_"} = $^O eq $_ ? sub () { 1 } : sub () { 0 }; | ||||
31 | } | ||||
32 | |||||
33 | # These OSes complain if you want to remove a file that you have no | ||||
34 | # write permission to: | ||||
35 | *_FORCE_WRITABLE = ( | ||||
36 | grep { $^O eq $_ } qw(amigaos dos epoc MSWin32 MacOS os2) | ||||
37 | ) ? sub () { 1 } : sub () { 0 }; | ||||
38 | |||||
39 | # Unix-like systems need to stat each directory in order to detect | ||||
40 | # race condition. MS-Windows is immune to this particular attack. | ||||
41 | *_NEED_STAT_CHECK = !(_IS_MSWIN32()) ? sub () { 1 } : sub () { 0 }; | ||||
42 | } | ||||
43 | |||||
44 | sub _carp { | ||||
45 | require Carp; | ||||
46 | goto &Carp::carp; | ||||
47 | } | ||||
48 | |||||
49 | sub _croak { | ||||
50 | require Carp; | ||||
51 | goto &Carp::croak; | ||||
52 | } | ||||
53 | |||||
54 | sub _error { | ||||
55 | my $arg = shift; | ||||
56 | my $message = shift; | ||||
57 | my $object = shift; | ||||
58 | |||||
59 | if ( $arg->{error} ) { | ||||
60 | $object = '' unless defined $object; | ||||
61 | $message .= ": $!" if $!; | ||||
62 | push @{ ${ $arg->{error} } }, { $object => $message }; | ||||
63 | } | ||||
64 | else { | ||||
65 | _carp( defined($object) ? "$message for $object: $!" : "$message: $!" ); | ||||
66 | } | ||||
67 | } | ||||
68 | |||||
69 | sub __is_arg { | ||||
70 | 505 | 555µs | my ($arg) = @_; | ||
71 | |||||
72 | # If client code blessed an array ref to HASH, this will not work | ||||
73 | # properly. We could have done $arg->isa() wrapped in eval, but | ||||
74 | # that would be expensive. This implementation should suffice. | ||||
75 | # We could have also used Scalar::Util:blessed, but we choose not | ||||
76 | # to add this dependency | ||||
77 | 505 | 3.76ms | return ( ref $arg eq 'HASH' ); | ||
78 | } | ||||
79 | |||||
80 | sub make_path { | ||||
81 | push @_, {} unless @_ and __is_arg( $_[-1] ); | ||||
82 | goto &mkpath; | ||||
83 | } | ||||
84 | |||||
85 | # spent 466µs (180+286) within File::Path::mkpath which was called 5 times, avg 93µs/call:
# once (26µs+102µs) by CPAN::Distribution::_find_prefs at line 2392 of CPAN/Distribution.pm
# once (60µs+51µs) by CPAN::checklock at line 796 of CPAN.pm
# once (48µs+53µs) by CPAN::Distribution::run_preps_on_packagedir at line 557 of CPAN/Distribution.pm
# once (25µs+47µs) by CPAN::FTP::_ftp_statistics at line 31 of CPAN/FTP.pm
# once (21µs+33µs) by CPAN::CacheMgr::new at line 206 of CPAN/CacheMgr.pm | ||||
86 | 5 | 36µs | 5 | 34µs | my $old_style = !( @_ and __is_arg( $_[-1] ) ); # spent 34µs making 5 calls to File::Path::__is_arg, avg 7µs/call |
87 | |||||
88 | 5 | 3µs | my $arg; | ||
89 | my $paths; | ||||
90 | |||||
91 | 5 | 2µs | if ($old_style) { | ||
92 | 5 | 3µs | my ( $verbose, $mode ); | ||
93 | 5 | 3µs | ( $paths, $verbose, $mode ) = @_; | ||
94 | 5 | 61µs | 5 | 21µs | $paths = [$paths] unless UNIVERSAL::isa( $paths, 'ARRAY' ); # spent 21µs making 5 calls to UNIVERSAL::isa, avg 4µs/call |
95 | 5 | 13µs | $arg->{verbose} = $verbose; | ||
96 | 5 | 7µs | $arg->{mode} = defined $mode ? $mode : oct '777'; | ||
97 | } | ||||
98 | else { | ||||
99 | my %args_permitted = map { $_ => 1 } ( qw| | ||||
100 | chmod | ||||
101 | error | ||||
102 | group | ||||
103 | mask | ||||
104 | mode | ||||
105 | owner | ||||
106 | uid | ||||
107 | user | ||||
108 | verbose | ||||
109 | | ); | ||||
110 | my @bad_args = (); | ||||
111 | $arg = pop @_; | ||||
112 | for my $k (sort keys %{$arg}) { | ||||
113 | push @bad_args, $k unless $args_permitted{$k}; | ||||
114 | } | ||||
115 | _carp("Unrecognized option(s) passed to make_path(): @bad_args") | ||||
116 | if @bad_args; | ||||
117 | $arg->{mode} = delete $arg->{mask} if exists $arg->{mask}; | ||||
118 | $arg->{mode} = oct '777' unless exists $arg->{mode}; | ||||
119 | ${ $arg->{error} } = [] if exists $arg->{error}; | ||||
120 | $arg->{owner} = delete $arg->{user} if exists $arg->{user}; | ||||
121 | $arg->{owner} = delete $arg->{uid} if exists $arg->{uid}; | ||||
122 | if ( exists $arg->{owner} and $arg->{owner} =~ /\D/ ) { | ||||
123 | my $uid = ( getpwnam $arg->{owner} )[2]; | ||||
124 | if ( defined $uid ) { | ||||
125 | $arg->{owner} = $uid; | ||||
126 | } | ||||
127 | else { | ||||
128 | _error( $arg, | ||||
129 | "unable to map $arg->{owner} to a uid, ownership not changed" | ||||
130 | ); | ||||
131 | delete $arg->{owner}; | ||||
132 | } | ||||
133 | } | ||||
134 | if ( exists $arg->{group} and $arg->{group} =~ /\D/ ) { | ||||
135 | my $gid = ( getgrnam $arg->{group} )[2]; | ||||
136 | if ( defined $gid ) { | ||||
137 | $arg->{group} = $gid; | ||||
138 | } | ||||
139 | else { | ||||
140 | _error( $arg, | ||||
141 | "unable to map $arg->{group} to a gid, group ownership not changed" | ||||
142 | ); | ||||
143 | delete $arg->{group}; | ||||
144 | } | ||||
145 | } | ||||
146 | if ( exists $arg->{owner} and not exists $arg->{group} ) { | ||||
147 | $arg->{group} = -1; # chown will leave group unchanged | ||||
148 | } | ||||
149 | if ( exists $arg->{group} and not exists $arg->{owner} ) { | ||||
150 | $arg->{owner} = -1; # chown will leave owner unchanged | ||||
151 | } | ||||
152 | $paths = [@_]; | ||||
153 | } | ||||
154 | 5 | 67µs | 5 | 231µs | return _mkpath( $arg, $paths ); # spent 231µs making 5 calls to File::Path::_mkpath, avg 46µs/call |
155 | } | ||||
156 | |||||
157 | # spent 231µs (79+152) within File::Path::_mkpath which was called 5 times, avg 46µs/call:
# 5 times (79µs+152µs) by File::Path::mkpath at line 154, avg 46µs/call | ||||
158 | 5 | 2µs | my $arg = shift; | ||
159 | 5 | 2µs | my $paths = shift; | ||
160 | |||||
161 | 5 | 3µs | my ( @created ); | ||
162 | 5 | 10µs | foreach my $path ( @{$paths} ) { | ||
163 | 5 | 5µs | next unless defined($path) and length($path); | ||
164 | $path .= '/' if _IS_OS2 and $path =~ /^\w:\z/s; # feature of CRT | ||||
165 | |||||
166 | # Logic wants Unix paths, so go with the flow. | ||||
167 | if (_IS_VMS) { | ||||
168 | next if $path eq '/'; | ||||
169 | $path = VMS::Filespec::unixify($path); | ||||
170 | } | ||||
171 | 5 | 181µs | 5 | 152µs | next if -d $path; # spent 152µs making 5 calls to File::Path::CORE:ftdir, avg 30µs/call |
172 | my $parent = File::Basename::dirname($path); | ||||
173 | unless ( -d $parent or $path eq $parent ) { | ||||
174 | push( @created, _mkpath( $arg, [$parent] ) ); | ||||
175 | } | ||||
176 | print "mkdir $path\n" if $arg->{verbose}; | ||||
177 | if ( mkdir( $path, $arg->{mode} ) ) { | ||||
178 | push( @created, $path ); | ||||
179 | if ( exists $arg->{owner} ) { | ||||
180 | |||||
181 | # NB: $arg->{group} guaranteed to be set during initialisation | ||||
182 | if ( !chown $arg->{owner}, $arg->{group}, $path ) { | ||||
183 | _error( $arg, | ||||
184 | "Cannot change ownership of $path to $arg->{owner}:$arg->{group}" | ||||
185 | ); | ||||
186 | } | ||||
187 | } | ||||
188 | if ( exists $arg->{chmod} ) { | ||||
189 | if ( !chmod $arg->{chmod}, $path ) { | ||||
190 | _error( $arg, | ||||
191 | "Cannot change permissions of $path to $arg->{chmod}" ); | ||||
192 | } | ||||
193 | } | ||||
194 | } | ||||
195 | else { | ||||
196 | my $save_bang = $!; | ||||
197 | my ( $e, $e1 ) = ( $save_bang, $^E ); | ||||
198 | $e .= "; $e1" if $e ne $e1; | ||||
199 | |||||
200 | # allow for another process to have created it meanwhile | ||||
201 | if ( ! -d $path ) { | ||||
202 | $! = $save_bang; | ||||
203 | if ( $arg->{error} ) { | ||||
204 | push @{ ${ $arg->{error} } }, { $path => $e }; | ||||
205 | } | ||||
206 | else { | ||||
207 | _croak("mkdir $path: $e"); | ||||
208 | } | ||||
209 | } | ||||
210 | } | ||||
211 | } | ||||
212 | 5 | 21µs | return @created; | ||
213 | } | ||||
214 | |||||
215 | sub remove_tree { | ||||
216 | push @_, {} unless @_ and __is_arg( $_[-1] ); | ||||
217 | goto &rmtree; | ||||
218 | } | ||||
219 | |||||
220 | # spent 36.2ms (25.3+10.9) within File::Path::_is_subdir which was called 500 times, avg 72µs/call:
# 500 times (25.3ms+10.9ms) by File::Path::rmtree at line 296, avg 72µs/call | ||||
221 | 500 | 538µs | my ( $dir, $test ) = @_; | ||
222 | |||||
223 | 500 | 2.73ms | 500 | 3.89ms | my ( $dv, $dd ) = File::Spec->splitpath( $dir, 1 ); # spent 3.89ms making 500 calls to File::Spec::Unix::splitpath, avg 8µs/call |
224 | 500 | 1.49ms | 500 | 1.54ms | my ( $tv, $td ) = File::Spec->splitpath( $test, 1 ); # spent 1.54ms making 500 calls to File::Spec::Unix::splitpath, avg 3µs/call |
225 | |||||
226 | # not on same volume | ||||
227 | 500 | 341µs | return 0 if $dv ne $tv; | ||
228 | |||||
229 | 500 | 1.93ms | 500 | 4.26ms | my @d = File::Spec->splitdir($dd); # spent 4.26ms making 500 calls to File::Spec::Unix::splitdir, avg 9µs/call |
230 | 500 | 1.27ms | 500 | 1.21ms | my @t = File::Spec->splitdir($td); # spent 1.21ms making 500 calls to File::Spec::Unix::splitdir, avg 2µs/call |
231 | |||||
232 | # @t can't be a subdir if it's shorter than @d | ||||
233 | 500 | 4.22ms | return 0 if @t < @d; | ||
234 | |||||
235 | 2 | 24µs | return join( '/', @d ) eq join( '/', splice @t, 0, +@d ); | ||
236 | } | ||||
237 | |||||
238 | # spent 54.0s (51.6ms+53.9) within File::Path::rmtree which was called 500 times, avg 108ms/call:
# 498 times (51.1ms+53.9s) by CPAN::CacheMgr::_clean_cache at line 161 of CPAN/CacheMgr.pm, avg 108ms/call
# once (344µs+1.17ms) by CPAN::Distribution::run_preps_on_packagedir at line 590 of CPAN/Distribution.pm
# once (78µs+195µs) by CPAN::Distribution::run_preps_on_packagedir at line 474 of CPAN/Distribution.pm | ||||
239 | 500 | 2.48ms | 500 | 2.57ms | my $old_style = !( @_ and __is_arg( $_[-1] ) ); # spent 2.57ms making 500 calls to File::Path::__is_arg, avg 5µs/call |
240 | |||||
241 | 500 | 147µs | my $arg; | ||
242 | my $paths; | ||||
243 | |||||
244 | 500 | 547µs | if ($old_style) { | ||
245 | 500 | 182µs | my ( $verbose, $safe ); | ||
246 | 500 | 534µs | ( $paths, $verbose, $safe ) = @_; | ||
247 | 500 | 1.47ms | $arg->{verbose} = $verbose; | ||
248 | 500 | 631µs | $arg->{safe} = defined $safe ? $safe : 0; | ||
249 | |||||
250 | 500 | 7.59ms | 500 | 3.45ms | if ( defined($paths) and length($paths) ) { # spent 3.45ms making 500 calls to UNIVERSAL::isa, avg 7µs/call |
251 | $paths = [$paths] unless UNIVERSAL::isa( $paths, 'ARRAY' ); | ||||
252 | } | ||||
253 | else { | ||||
254 | _carp("No root path(s) specified\n"); | ||||
255 | return 0; | ||||
256 | } | ||||
257 | } | ||||
258 | else { | ||||
259 | my %args_permitted = map { $_ => 1 } ( qw| | ||||
260 | error | ||||
261 | keep_root | ||||
262 | result | ||||
263 | safe | ||||
264 | verbose | ||||
265 | | ); | ||||
266 | my @bad_args = (); | ||||
267 | $arg = pop @_; | ||||
268 | for my $k (sort keys %{$arg}) { | ||||
269 | push @bad_args, $k unless $args_permitted{$k}; | ||||
270 | } | ||||
271 | _carp("Unrecognized option(s) passed to remove_tree(): @bad_args") | ||||
272 | if @bad_args; | ||||
273 | ${ $arg->{error} } = [] if exists $arg->{error}; | ||||
274 | ${ $arg->{result} } = [] if exists $arg->{result}; | ||||
275 | $paths = [@_]; | ||||
276 | } | ||||
277 | |||||
278 | 500 | 603µs | $arg->{prefix} = ''; | ||
279 | 500 | 431µs | $arg->{depth} = 0; | ||
280 | |||||
281 | 500 | 236µs | my @clean_path; | ||
282 | 500 | 24.9ms | 500 | 21.0ms | $arg->{cwd} = getcwd() or do { # spent 21.0ms making 500 calls to Cwd::getcwd, avg 42µs/call |
283 | _error( $arg, "cannot fetch initial working directory" ); | ||||
284 | return 0; | ||||
285 | }; | ||||
286 | 1500 | 6.85ms | 500 | 2.89ms | for ( $arg->{cwd} ) { /\A(.*)\Z/s; $_ = $1 } # untaint # spent 2.89ms making 500 calls to File::Path::CORE:match, avg 6µs/call |
287 | |||||
288 | 500 | 725µs | for my $p (@$paths) { | ||
289 | |||||
290 | # need to fixup case and map \ to / on Windows | ||||
291 | 500 | 323µs | my $ortho_root = _IS_MSWIN32 ? _slash_lc($p) : $p; | ||
292 | my $ortho_cwd = | ||||
293 | 500 | 539µs | _IS_MSWIN32 ? _slash_lc( $arg->{cwd} ) : $arg->{cwd}; | ||
294 | 500 | 331µs | my $ortho_root_length = length($ortho_root); | ||
295 | $ortho_root_length-- if _IS_VMS; # don't compare '.' with ']' | ||||
296 | 500 | 1.70ms | 500 | 36.2ms | if ( $ortho_root_length && _is_subdir( $ortho_root, $ortho_cwd ) ) { # spent 36.2ms making 500 calls to File::Path::_is_subdir, avg 72µs/call |
297 | local $! = 0; | ||||
298 | _error( $arg, "cannot remove path when cwd is $arg->{cwd}", $p ); | ||||
299 | next; | ||||
300 | } | ||||
301 | |||||
302 | 500 | 295µs | if (_IS_MACOS) { | ||
303 | $p = ":$p" unless $p =~ /:/; | ||||
304 | $p .= ":" unless $p =~ /:\z/; | ||||
305 | } | ||||
306 | elsif ( _IS_MSWIN32 ) { | ||||
307 | $p =~ s{[/\\]\z}{}; | ||||
308 | } | ||||
309 | else { | ||||
310 | 500 | 3.44ms | 500 | 952µs | $p =~ s{/\z}{}; # spent 952µs making 500 calls to File::Path::CORE:subst, avg 2µs/call |
311 | } | ||||
312 | 500 | 1.88ms | push @clean_path, $p; | ||
313 | } | ||||
314 | |||||
315 | 500 | 11.9ms | 500 | 5.99ms | @{$arg}{qw(device inode perm)} = ( lstat $arg->{cwd} )[ 0, 1 ] or do { # spent 5.99ms making 500 calls to File::Path::CORE:lstat, avg 12µs/call |
316 | _error( $arg, "cannot stat initial working directory", $arg->{cwd} ); | ||||
317 | return 0; | ||||
318 | }; | ||||
319 | |||||
320 | 500 | 7.82ms | 500 | 53.9s | return _rmtree( $arg, \@clean_path ); # spent 53.9s making 500 calls to File::Path::_rmtree, avg 108ms/call |
321 | } | ||||
322 | |||||
323 | sub _rmtree { | ||||
324 | 35412 | 19.5ms | my $arg = shift; | ||
325 | 35412 | 9.36ms | my $paths = shift; | ||
326 | |||||
327 | 35412 | 11.3ms | my $count = 0; | ||
328 | 35412 | 196ms | 35412 | 71.6ms | my $curdir = File::Spec->curdir(); # spent 71.6ms making 35412 calls to File::Spec::Unix::curdir, avg 2µs/call |
329 | 35412 | 81.5ms | 35412 | 56.0ms | my $updir = File::Spec->updir(); # spent 56.0ms making 35412 calls to File::Spec::Unix::updir, avg 2µs/call |
330 | |||||
331 | 35412 | 8.78ms | my ( @files, $root ); | ||
332 | ROOT_DIR: | ||||
333 | 35412 | 39.0ms | foreach my $root (@$paths) { | ||
334 | |||||
335 | # since we chdir into each directory, it may not be obvious | ||||
336 | # to figure out where we are if we generate a message about | ||||
337 | # a file name. We therefore construct a semi-canonical | ||||
338 | # filename, anchored from the directory being unlinked (as | ||||
339 | # opposed to being truly canonical, anchored from the root (/). | ||||
340 | |||||
341 | my $canon = | ||||
342 | $arg->{prefix} | ||||
343 | 137096 | 5.57s | 546384 | 6.48s | ? File::Spec->catfile( $arg->{prefix}, $root ) # spent 4.32s making 136596 calls to File::Spec::Unix::catfile, avg 32µs/call
# spent 1.60s making 136596 calls to File::Spec::Unix::catdir, avg 12µs/call
# spent 564ms making 273192 calls to File::Spec::Unix::canonpath, avg 2µs/call |
344 | : $root; | ||||
345 | |||||
346 | 137096 | 8.20s | 137096 | 7.08s | my ( $ldev, $lino, $perm ) = ( lstat $root )[ 0, 1, 2 ] # spent 7.08s making 137096 calls to File::Path::CORE:lstat, avg 52µs/call |
347 | or next ROOT_DIR; | ||||
348 | |||||
349 | 137095 | 891ms | 137095 | 109ms | if ( -d _ ) { # spent 109ms making 137095 calls to File::Path::CORE:ftdir, avg 794ns/call |
350 | $root = VMS::Filespec::vmspath( VMS::Filespec::pathify($root) ) | ||||
351 | if _IS_VMS; | ||||
352 | |||||
353 | 35056 | 309ms | 35056 | 180ms | if ( !chdir($root) ) { # spent 180ms making 35056 calls to File::Path::CORE:chdir, avg 5µs/call |
354 | |||||
355 | # see if we can escalate privileges to get in | ||||
356 | # (e.g. funny protection mask such as -w- instead of rwx) | ||||
357 | $perm &= oct '7777'; | ||||
358 | my $nperm = $perm | oct '700'; | ||||
359 | if ( | ||||
360 | !( | ||||
361 | $arg->{safe} | ||||
362 | or $nperm == $perm | ||||
363 | or chmod( $nperm, $root ) | ||||
364 | ) | ||||
365 | ) | ||||
366 | { | ||||
367 | _error( $arg, | ||||
368 | "cannot make child directory read-write-exec", $canon ); | ||||
369 | next ROOT_DIR; | ||||
370 | } | ||||
371 | elsif ( !chdir($root) ) { | ||||
372 | _error( $arg, "cannot chdir to child", $canon ); | ||||
373 | next ROOT_DIR; | ||||
374 | } | ||||
375 | } | ||||
376 | |||||
377 | my ( $cur_dev, $cur_inode, $perm ) = ( stat $curdir )[ 0, 1, 2 ] | ||||
378 | 35056 | 527ms | 35056 | 273ms | or do { # spent 273ms making 35056 calls to File::Path::CORE:stat, avg 8µs/call |
379 | _error( $arg, "cannot stat current working directory", $canon ); | ||||
380 | next ROOT_DIR; | ||||
381 | }; | ||||
382 | |||||
383 | 35056 | 51.8ms | if (_NEED_STAT_CHECK) { | ||
384 | ( $ldev eq $cur_dev and $lino eq $cur_inode ) | ||||
385 | or _croak( | ||||
386 | "directory $canon changed before chdir, expected dev=$ldev ino=$lino, actual dev=$cur_dev ino=$cur_inode, aborting." | ||||
387 | ); | ||||
388 | } | ||||
389 | |||||
390 | 35056 | 18.3ms | $perm &= oct '7777'; # don't forget setuid, setgid, sticky bits | ||
391 | 35056 | 24.4ms | my $nperm = $perm | oct '700'; | ||
392 | |||||
393 | # notabene: 0700 is for making readable in the first place, | ||||
394 | # it's also intended to change it to writable in case we have | ||||
395 | # to recurse in which case we are better than rm -rf for | ||||
396 | # subtrees with strange permissions | ||||
397 | |||||
398 | 35056 | 40.6ms | if ( | ||
399 | !( | ||||
400 | $arg->{safe} | ||||
401 | or $nperm == $perm | ||||
402 | or chmod( $nperm, $curdir ) | ||||
403 | ) | ||||
404 | ) | ||||
405 | { | ||||
406 | _error( $arg, "cannot make directory read+writeable", $canon ); | ||||
407 | $nperm = $perm; | ||||
408 | } | ||||
409 | |||||
410 | 35056 | 7.87ms | my $d; | ||
411 | 35056 | 21.8ms | $d = gensym() if $] < 5.006; | ||
412 | 35056 | 965ms | 35056 | 622ms | if ( !opendir $d, $curdir ) { # spent 622ms making 35056 calls to File::Path::CORE:open_dir, avg 18µs/call |
413 | _error( $arg, "cannot opendir", $canon ); | ||||
414 | @files = (); | ||||
415 | } | ||||
416 | else { | ||||
417 | 35056 | 92.3ms | if ( !defined ${^TAINT} or ${^TAINT} ) { | ||
418 | # Blindly untaint dir names if taint mode is active | ||||
419 | @files = map { /\A(.*)\z/s; $1 } readdir $d; | ||||
420 | } | ||||
421 | else { | ||||
422 | 35056 | 3.04s | 35056 | 2.68s | @files = readdir $d; # spent 2.68s making 35056 calls to File::Path::CORE:readdir, avg 76µs/call |
423 | } | ||||
424 | 35056 | 360ms | 35056 | 189ms | closedir $d; # spent 189ms making 35056 calls to File::Path::CORE:closedir, avg 5µs/call |
425 | } | ||||
426 | |||||
427 | if (_IS_VMS) { | ||||
428 | |||||
429 | # Deleting large numbers of files from VMS Files-11 | ||||
430 | # filesystems is faster if done in reverse ASCIIbetical order. | ||||
431 | # include '.' to '.;' from blead patch #31775 | ||||
432 | @files = map { $_ eq '.' ? '.;' : $_ } reverse @files; | ||||
433 | } | ||||
434 | |||||
435 | 35056 | 215ms | @files = grep { $_ ne $updir and $_ ne $curdir } @files; | ||
436 | |||||
437 | 35056 | 39.8ms | if (@files) { | ||
438 | |||||
439 | # remove the contained files before the directory itself | ||||
440 | 34912 | 332ms | my $narg = {%$arg}; | ||
441 | @{$narg}{qw(device inode cwd prefix depth)} = | ||||
442 | 34912 | 112ms | ( $cur_dev, $cur_inode, $updir, $canon, $arg->{depth} + 1 ); | ||
443 | 34912 | 228ms | 34912 | 0s | $count += _rmtree( $narg, \@files ); # spent 274s making 34912 calls to File::Path::_rmtree, avg 7.84ms/call, recursion: max depth 14, sum of overlapping time 274s |
444 | } | ||||
445 | |||||
446 | # restore directory permissions of required now (in case the rmdir | ||||
447 | # below fails), while we are still in the directory and may do so | ||||
448 | # without a race via '.' | ||||
449 | 35056 | 20.7ms | if ( $nperm != $perm and not chmod( $perm, $curdir ) ) { | ||
450 | _error( $arg, "cannot reset chmod", $canon ); | ||||
451 | } | ||||
452 | |||||
453 | # don't leave the client code in an unexpected directory | ||||
454 | chdir( $arg->{cwd} ) | ||||
455 | 35056 | 417ms | 35056 | 237ms | or # spent 237ms making 35056 calls to File::Path::CORE:chdir, avg 7µs/call |
456 | _croak("cannot chdir to $arg->{cwd} from $canon: $!, aborting."); | ||||
457 | |||||
458 | # ensure that a chdir upwards didn't take us somewhere other | ||||
459 | # than we expected (see CVE-2002-0435) | ||||
460 | 35056 | 607ms | 35056 | 355ms | ( $cur_dev, $cur_inode ) = ( stat $curdir )[ 0, 1 ] # spent 355ms making 35056 calls to File::Path::CORE:stat, avg 10µs/call |
461 | or _croak( | ||||
462 | "cannot stat prior working directory $arg->{cwd}: $!, aborting." | ||||
463 | ); | ||||
464 | |||||
465 | 35056 | 70.1ms | if (_NEED_STAT_CHECK) { | ||
466 | ( $arg->{device} eq $cur_dev and $arg->{inode} eq $cur_inode ) | ||||
467 | or _croak( "previous directory $arg->{cwd} " | ||||
468 | . "changed before entering $canon, " | ||||
469 | . "expected dev=$ldev ino=$lino, " | ||||
470 | . "actual dev=$cur_dev ino=$cur_inode, aborting." | ||||
471 | ); | ||||
472 | } | ||||
473 | |||||
474 | 35056 | 189ms | if ( $arg->{depth} or !$arg->{keep_root} ) { | ||
475 | 35056 | 14.9ms | if ( $arg->{safe} | ||
476 | && ( _IS_VMS | ||||
477 | ? !&VMS::Filespec::candelete($root) | ||||
478 | : !-w $root ) ) | ||||
479 | { | ||||
480 | print "skipped $root\n" if $arg->{verbose}; | ||||
481 | next ROOT_DIR; | ||||
482 | } | ||||
483 | if ( _FORCE_WRITABLE and !chmod $perm | oct '700', $root ) { | ||||
484 | _error( $arg, "cannot make directory writeable", $canon ); | ||||
485 | } | ||||
486 | 35056 | 13.0ms | print "rmdir $root\n" if $arg->{verbose}; | ||
487 | 35056 | 5.67s | 35056 | 5.39s | if ( rmdir $root ) { # spent 5.39s making 35056 calls to File::Path::CORE:rmdir, avg 154µs/call |
488 | 35056 | 33.6ms | push @{ ${ $arg->{result} } }, $root if $arg->{result}; | ||
489 | 35056 | 14.3ms | ++$count; | ||
490 | } | ||||
491 | else { | ||||
492 | _error( $arg, "cannot remove directory", $canon ); | ||||
493 | if ( | ||||
494 | _FORCE_WRITABLE | ||||
495 | && !chmod( $perm, | ||||
496 | ( _IS_VMS ? VMS::Filespec::fileify($root) : $root ) | ||||
497 | ) | ||||
498 | ) | ||||
499 | { | ||||
500 | _error( | ||||
501 | $arg, | ||||
502 | sprintf( "cannot restore permissions to 0%o", | ||||
503 | $perm ), | ||||
504 | $canon | ||||
505 | ); | ||||
506 | } | ||||
507 | } | ||||
508 | } | ||||
509 | } | ||||
510 | else { | ||||
511 | # not a directory | ||||
512 | $root = VMS::Filespec::vmsify("./$root") | ||||
513 | if _IS_VMS | ||||
514 | && !File::Spec->file_name_is_absolute($root) | ||||
515 | && ( $root !~ m/(?<!\^)[\]>]+/ ); # not already in VMS syntax | ||||
516 | |||||
517 | 102039 | 75.5ms | if ( | ||
518 | $arg->{safe} | ||||
519 | && ( | ||||
520 | _IS_VMS | ||||
521 | ? !&VMS::Filespec::candelete($root) | ||||
522 | : !( -l $root || -w $root ) | ||||
523 | ) | ||||
524 | ) | ||||
525 | { | ||||
526 | print "skipped $root\n" if $arg->{verbose}; | ||||
527 | next ROOT_DIR; | ||||
528 | } | ||||
529 | |||||
530 | 102039 | 77.4ms | my $nperm = $perm & oct '7777' | oct '600'; | ||
531 | if ( _FORCE_WRITABLE | ||||
532 | and $nperm != $perm | ||||
533 | and not chmod $nperm, $root ) | ||||
534 | { | ||||
535 | _error( $arg, "cannot make file writeable", $canon ); | ||||
536 | } | ||||
537 | 102039 | 35.2ms | print "unlink $canon\n" if $arg->{verbose}; | ||
538 | |||||
539 | # delete all versions under VMS | ||||
540 | 102039 | 23.8ms | for ( ; ; ) { | ||
541 | 102039 | 24.3s | 102039 | 23.5s | if ( unlink $root ) { # spent 23.5s making 102039 calls to File::Path::CORE:unlink, avg 230µs/call |
542 | push @{ ${ $arg->{result} } }, $root if $arg->{result}; | ||||
543 | } | ||||
544 | else { | ||||
545 | _error( $arg, "cannot unlink file", $canon ); | ||||
546 | _FORCE_WRITABLE and chmod( $perm, $root ) | ||||
547 | or _error( $arg, | ||||
548 | sprintf( "cannot restore permissions to 0%o", $perm ), | ||||
549 | $canon ); | ||||
550 | last; | ||||
551 | } | ||||
552 | 102039 | 37.3ms | ++$count; | ||
553 | 102039 | 106ms | last unless _IS_VMS && lstat $root; | ||
554 | } | ||||
555 | } | ||||
556 | } | ||||
557 | 35412 | 328ms | return $count; | ||
558 | } | ||||
559 | |||||
560 | sub _slash_lc { | ||||
561 | |||||
562 | # fix up slashes and case on MSWin32 so that we can determine that | ||||
563 | # c:\path\to\dir is underneath C:/Path/To | ||||
564 | my $path = shift; | ||||
565 | $path =~ tr{\\}{/}; | ||||
566 | return lc($path); | ||||
567 | } | ||||
568 | |||||
569 | 1; | ||||
570 | |||||
571 | __END__ | ||||
sub File::Path::CORE:chdir; # opcode | |||||
# spent 189ms within File::Path::CORE:closedir which was called 35056 times, avg 5µs/call:
# 35056 times (189ms+0s) by File::Path::_rmtree at line 424, avg 5µs/call | |||||
sub File::Path::CORE:ftdir; # opcode | |||||
sub File::Path::CORE:lstat; # opcode | |||||
# spent 2.89ms within File::Path::CORE:match which was called 500 times, avg 6µs/call:
# 500 times (2.89ms+0s) by File::Path::rmtree at line 286, avg 6µs/call | |||||
# spent 622ms within File::Path::CORE:open_dir which was called 35056 times, avg 18µs/call:
# 35056 times (622ms+0s) by File::Path::_rmtree at line 412, avg 18µs/call | |||||
# spent 2.68s within File::Path::CORE:readdir which was called 35056 times, avg 76µs/call:
# 35056 times (2.68s+0s) by File::Path::_rmtree at line 422, avg 76µs/call | |||||
# spent 5.39s within File::Path::CORE:rmdir which was called 35056 times, avg 154µs/call:
# 35056 times (5.39s+0s) by File::Path::_rmtree at line 487, avg 154µs/call | |||||
sub File::Path::CORE:stat; # opcode | |||||
# spent 952µs within File::Path::CORE:subst which was called 500 times, avg 2µs/call:
# 500 times (952µs+0s) by File::Path::rmtree at line 310, avg 2µs/call | |||||
# spent 23.5s within File::Path::CORE:unlink which was called 102039 times, avg 230µs/call:
# 102039 times (23.5s+0s) by File::Path::_rmtree at line 541, avg 230µs/call |