← Index
NYTProf Performance Profile   « line view »
For Makefile.PL
  Run on Sun Mar 1 16:04:44 2015
Reported on Sun Mar 1 16:09:02 2015

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