More tests for when fieldhash magic (doesn't) trigger.
[p5sagit/p5-mst-13.2.git] / lib / File / Path.pm
CommitLineData
1fc4cb55 1package File::Path;
fed7345c 2
3=head1 NAME
4
12c2e016 5File::Path - Create or remove directory trees
6
7=head1 VERSION
8
0b3d36bd 9This document describes version 2.00_11 of File::Path, released
102007-09-08.
fed7345c 11
12=head1 SYNOPSIS
13
8b87c192 14 use File::Path;
fed7345c 15
12c2e016 16 # modern
17 mkpath( 'foo/bar/baz', '/zug/zwang', {verbose => 1} );
18
19 rmtree(
20 'foo/bar/baz', '/zug/zwang',
91c4f65e 21 { verbose => 1, error => \my $err_list }
12c2e016 22 );
23
24 # traditional
8b87c192 25 mkpath(['/foo/bar/baz', 'blurfl/quux'], 1, 0711);
26 rmtree(['foo/bar/baz', 'blurfl/quux'], 1, 1);
fed7345c 27
28=head1 DESCRIPTION
29
0b3d36bd 30The C<mkpath> function provides a convenient way to create directories
31of arbitrary depth. Similarly, the C<rmtree> function provides a
32convenient way to delete an entire directory subtree from the
33filesystem, much like the Unix command C<rm -r>.
12c2e016 34
35Both functions may be called in one of two ways, the traditional,
36compatible with code written since the dawn of time, and modern,
37that offers a more flexible and readable idiom. New code should use
38the modern interface.
39
40=head2 FUNCTIONS
41
0b3d36bd 42The modern way of calling C<mkpath> and C<rmtree> is with a list
43of directories to create, or remove, respectively, followed by an
44optional hash reference containing keys to control the
45function's behaviour.
12c2e016 46
47=head3 C<mkpath>
48
0b3d36bd 49The following keys are recognised as parameters to C<mkpath>.
50The function returns the list of files actually created during the
51call.
12c2e016 52
53 my @created = mkpath(
54 qw(/tmp /flub /home/nobody),
55 {verbose => 1, mode => 0750},
56 );
57 print "created $_\n" for @created;
58
59=over 4
60
61=item mode
62
0b3d36bd 63The numeric permissions mode to apply to each created directory
64(defaults to 0777), to be modified by the current C<umask>. If the
65directory already exists (and thus does not need to be created),
66the permissions will not be modified.
67
68C<mask> is recognised as an alias for this parameter.
12c2e016 69
70=item verbose
71
72If present, will cause C<mkpath> to print the name of each directory
73as it is created. By default nothing is printed.
74
75=item error
76
77If present, will be interpreted as a reference to a list, and will
78be used to store any errors that are encountered. See the ERROR
0b3d36bd 79HANDLING section for more information.
12c2e016 80
0b3d36bd 81If this parameter is not used, certain error conditions may raise
82a fatal error that will cause the program will halt, unless trapped
83in an C<eval> block.
12c2e016 84
85=back
86
87=head3 C<rmtree>
88
89=over 4
90
91=item verbose
92
93If present, will cause C<rmtree> to print the name of each file as
94it is unlinked. By default nothing is printed.
95
96=item skip_others
97
0b3d36bd 98When set to a true value, will cause C<rmtree> to skip the files
99for which the process lacks the required privileges needed to delete
100files, such as delete privileges on VMS.
12c2e016 101
102=item keep_root
103
0b3d36bd 104When set to a true value, will cause all files and subdirectories
105to be removed, except the initially specified directories. This comes
106in handy when cleaning out an application's scratch directory.
12c2e016 107
108 rmtree( '/tmp', {keep_root => 1} );
109
110=item result
111
112If present, will be interpreted as a reference to a list, and will
113be used to store the list of all files and directories unlinked
114during the call. If nothing is unlinked, a reference to an empty
115list is returned (rather than C<undef>).
116
117 rmtree( '/tmp', {result => \my $list} );
118 print "unlinked $_\n" for @$list;
119
0b3d36bd 120This is a useful alternative to the C<verbose> key.
121
12c2e016 122=item error
123
124If present, will be interpreted as a reference to a list,
125and will be used to store any errors that are encountered.
0b3d36bd 126See the ERROR HANDLING section for more information.
12c2e016 127
0b3d36bd 128Removing things is a much more dangerous proposition than
129creating things. As such, there are certain conditions that
130C<rmtree> may encounter that are so dangerous that the only
131sane action left is to kill the program.
132
133Use C<error> to trap all that is reasonable (problems with
134permissions and the like), and let it die if things get out
135of hand. This is the safest course of action.
12c2e016 136
137=back
138
139=head2 TRADITIONAL INTERFACE
140
0b3d36bd 141The old interfaces of C<mkpath> and C<rmtree> take a reference to
142a list of directories (to create or remove), followed by a series
143of positional, numeric, modal parameters that control their behaviour.
144
145This design made it difficult to add additional functionality, as
146well as posed the problem of what to do when the calling code only
147needs to set the last parameter. Even though the code doesn't care
148how the initial positional parameters are set, the programmer is
149forced to learn what the defaults are, and specify them.
12c2e016 150
0b3d36bd 151Worse, if it turns out in the future that it would make more sense
152to change the default behaviour of the first parameter (for example,
153to avoid a security vulnerability), all existing code will remain
154hard-wired to the wrong defaults.
12c2e016 155
0b3d36bd 156Finally, a series of numeric parameters are much less self-documenting
157in terms of communicating to the reader what the code is doing. Named
158parameters do not have this problem.
159
160In the traditional API, C<mkpath> takes three arguments:
fed7345c 161
162=over 4
163
164=item *
165
0b3d36bd 166The name of the path to create, or a reference to a list of paths
167to create,
fed7345c 168
169=item *
170
0b3d36bd 171a boolean value, which if TRUE will cause C<mkpath> to print the
172name of each directory as it is created (defaults to FALSE), and
fed7345c 173
174=item *
175
0b3d36bd 176the numeric mode to use when creating the directories (defaults to
1770777), to be modified by the current umask.
fed7345c 178
179=back
180
037c8c09 181It returns a list of all directories (including intermediates, determined
cc61921f 182using the Unix '/' separator) created. In scalar context it returns
183the number of directories created.
fed7345c 184
070ed461 185If a system error prevents a directory from being created, then the
99c4c5e8 186C<mkpath> function throws a fatal error with C<Carp::croak>. This error
187can be trapped with an C<eval> block:
070ed461 188
189 eval { mkpath($dir) };
190 if ($@) {
191 print "Couldn't create $dir: $@";
192 }
193
0b3d36bd 194In the traditional API, C<rmtree> takes three arguments:
fed7345c 195
196=over 4
197
198=item *
199
0b3d36bd 200the root of the subtree to delete, or a reference to a list of
201roots. All of the files and directories below each root, as well
202as the roots themselves, will be deleted. If you want to keep
203the roots themselves, you must use the modern API.
fed7345c 204
205=item *
206
0b3d36bd 207a boolean value, which if TRUE will cause C<rmtree> to print a
208message each time it examines a file, giving the name of the file,
209and indicating whether it's using C<rmdir> or C<unlink> to remove
210it, or that it's skipping it. (defaults to FALSE)
fed7345c 211
212=item *
213
0b3d36bd 214a boolean value, which if TRUE will cause C<rmtree> to skip any
215files to which you do not have delete access (if running under VMS)
216or write access (if running under another OS). This will change
217in the future when a criterion for 'delete permission' under OSs
218other than VMS is settled. (defaults to FALSE)
fed7345c 219
220=back
221
cc61921f 222It returns the number of files, directories and symlinks successfully
223deleted. Symlinks are simply deleted and not followed.
fed7345c 224
12c2e016 225Note also that the occurrence of errors in C<rmtree> using the
226traditional interface can be determined I<only> by trapping diagnostic
227messages using C<$SIG{__WARN__}>; it is not apparent from the return
228value. (The modern interface may use the C<error> parameter to
0b3d36bd 229record any problems encountered).
12c2e016 230
231=head2 ERROR HANDLING
232
233If C<mkpath> or C<rmtree> encounter an error, a diagnostic message
234will be printed to C<STDERR> via C<carp> (for non-fatal errors),
235or via C<croak> (for fatal errors).
236
237If this behaviour is not desirable, the C<error> attribute may be
238used to hold a reference to a variable, which will be used to store
239the diagnostics. The result is a reference to a list of hash
240references. For each hash reference, the key is the name of the
241file, and the value is the error message (usually the contents of
242C<$!>). An example usage looks like:
243
244 rmpath( 'foo/bar', 'bar/rat', {error => \my $err} );
245 for my $diag (@$err) {
246 my ($file, $message) = each %$diag;
247 print "problem unlinking $file: $message\n";
248 }
249
250If no errors are encountered, C<$err> will point to an empty list
251(thus there is no need to test for C<undef>). If a general error
252is encountered (for instance, C<rmtree> attempts to remove a directory
253tree that does not exist), the diagnostic key will be empty, only
254the value will be set:
255
256 rmpath( '/no/such/path', {error => \my $err} );
257 for my $diag (@$err) {
258 my ($file, $message) = each %$diag;
259 if ($file eq '') {
260 print "general error: $message\n";
261 }
262 }
263
264=head2 NOTES
265
0b3d36bd 266C<File::Path> blindly exports C<mkpath> and C<rmtree> into the
267current namespace. These days, this is considered bad style, but
268to change it now would break too much code. Nonetheless, you are
269invited to specify what it is you are expecting to use:
270
271 use File::Path 'rmtree';
272
12c2e016 273=head3 HEURISTICS
274
275The functions detect (as far as possible) which way they are being
276called and will act appropriately. It is important to remember that
277the heuristic for detecting the old style is either the presence
278of an array reference, or two or three parameters total and second
279and third parameters are numeric. Hence...
280
0b3d36bd 281 mkpath 486, 487, 488;
12c2e016 282
283... will not assume the modern style and create three directories, rather
284it will create one directory verbosely, setting the permission to
2850750 (488 being the decimal equivalent of octal 750). Here, old
286style trumps new. It must, for backwards compatibility reasons.
e2ba98a1 287
12c2e016 288If you want to ensure there is absolutely no ambiguity about which
289way the function will behave, make sure the first parameter is a
290reference to a one-element list, to force the old style interpretation:
e2ba98a1 291
0b3d36bd 292 mkpath [486], 487, 488;
12c2e016 293
294and get only one directory created. Or add a reference to an empty
295parameter hash, to force the new style:
296
0b3d36bd 297 mkpath 486, 487, 488, {};
12c2e016 298
299... and hence create the three directories. If the empty hash
300reference seems a little strange to your eyes, or you suspect a
301subsequent programmer might I<helpfully> optimise it away, you
302can add a parameter set to a default value:
303
0b3d36bd 304 mkpath 486, 487, 488, {verbose => 0};
12c2e016 305
0b3d36bd 306=head3 SECURITY CONSIDERATIONS
12c2e016 307
0b3d36bd 308There were race conditions 1.x implementations of File::Path's
309C<rmtree> function (although sometimes patched depending on the OS
310distribution or platform). The 2.0 version contains code to avoid the
311problem mentioned in CVE-2002-0435.
12c2e016 312
0b3d36bd 313See the following pages for more information:
12c2e016 314
0b3d36bd 315 http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=286905
316 http://www.nntp.perl.org/group/perl.perl5.porters/2005/01/msg97623.html
317 http://www.debian.org/security/2005/dsa-696
12c2e016 318
0b3d36bd 319Additionally, unless the C<skip_others> parameter is set (or the
320third parameter in the traditional inferface is TRUE), should a
321C<rmtree> be interrupted, files that were originally in read-only
322mode may now have their permissions set to a read-write (or "delete
323OK") mode.
96e4d5b1 324
b8d5f521 325=head1 DIAGNOSTICS
326
0b3d36bd 327FATAL errors will cause the program to halt (C<croak>), since the
328problem is so severe that it would be dangerous to continue. (This
329can always be trapped with C<eval>, but it's not a good idea. Under
330the circumstances, dying is the best thing to do).
331
332SEVERE errors may be trapped using the modern interface. If the
333they are not trapped, or the old interface is used, such an error
334will cause the program will halt.
335
336All other errors may be trapped using the modern interface, otherwise
337they will be C<carp>ed about. Program execution will not be halted.
338
b8d5f521 339=over 4
340
0b3d36bd 341=item mkdir [ppath]: [errmsg] (SEVERE)
342
343C<mkpath> was unable to create the path. Probably some sort of
344permissions error at the point of departure, or insufficient resources
345(such as free inodes on Unix).
346
347=item No root path(s) specified
348
349C<mkpath> was not given any paths to create. This message is only
350emitted if the routine is called with the traditional interface.
351The modern interface will remain silent if given nothing to do.
352
353=item No such file or directory
354
355On Windows, if C<mkpath> gives you this warning, it may mean that
356you have exceeded your filesystem's maximum path length.
357
358=item cannot fetch initial working directory: [errmsg]
359
360C<rmtree> attempted to determine the initial directory by calling
361C<Cwd::getcwd>, but the call failed for some reason. No attempt
362will be made to delete anything.
363
364=item cannot stat initial working directory: [errmsg]
365
366C<rmtree> attempted to stat the initial directory (after having
367successfully obtained its name via C<getcwd>), however, the call
368failed for some reason. No attempt will be made to delete anything.
369
370=item cannot chdir to [dir]: [errmsg]
371
372C<rmtree> attempted to set the working directory in order to
373begin deleting the objects therein, but was unsuccessful. This is
374usually a permissions issue. The routine will continue to delete
375other things, but this directory will be left intact.
376
377=item directory [dir] changed before chdir, expected dev=[n] inode=[n], actual dev=[n] ino=[n], aborting. (FATAL)
378
379C<rmtree> recorded the device and inode of a directory, and then
380moved into it. It then performed a C<stat> on the current directory
381and detected that the device and inode were no longer the same. As
382this is at the heart of the race condition problem, the program
383will die at this point.
384
385=item cannot make directory [dir] read+writeable: [errmsg]
386
387C<rmtree> attempted to change the permissions on the current directory
388to ensure that subsequent unlinkings would not run into problems,
389but was unable to do so. The permissions remain as they were, and
390the program will carry on, doing the best it can.
391
392=item cannot read [dir]: [errmsg]
393
394C<rmtree> tried to read the contents of the directory in order
395to acquire the names of the directory entries to be unlinked, but
396was unsuccessful. This is usually a permissions issue. The
397program will continue, but the files in this directory will remain
398after the call.
399
400=item cannot reset chmod [dir]: [errmsg]
401
402C<rmtree>, after having deleted everything in a directory, attempted
403to restore its permissions to the original state but failed. The
404directory may wind up being left behind.
405
406=item cannot chdir to [parent-dir] from [child-dir]: [errmsg], aborting. (FATAL)
407
408C<rmtree>, after having deleted everything and restored the permissions
409of a directory, was unable to chdir back to the parent. This is usually
410a sign that something evil this way comes.
411
412=item cannot stat prior working directory [dir]: [errmsg], aborting. (FATAL)
413
414C<rmtree> was unable to stat the parent directory after have returned
415from the child. Since there is no way of knowing if we returned to
416where we think we should be (by comparing device and inode) the only
417way out is to C<croak>.
418
419=item previous directory [parent-dir] changed before entering [child-dir], expected dev=[n] inode=[n], actual dev=[n] ino=[n], aborting. (FATAL)
420
421When C<rmtree> returned from deleting files in a child directory, a
422check revealed that the parent directory it returned to wasn't the one
423it started out from. This is considered a sign of malicious activity.
424
425=item cannot make directory [dir] writeable: [errmsg]
426
427Just before removing a directory (after having successfully removed
428everything it contained), C<rmtree> attempted to set the permissions
429on the directory to ensure it could be removed and failed. Program
430execution continues, but the directory may possibly not be deleted.
431
432=item cannot remove directory [dir]: [errmsg]
433
434C<rmtree> attempted to remove a directory, but failed. This may because
435some objects that were unable to be removed remain in the directory, or
436a permissions issue. The directory will be left behind.
437
438=item cannot restore permissions of [dir] to [0nnn]: [errmsg]
439
440After having failed to remove a directory, C<rmtree> was unable to
441restore its permissions from a permissive state back to a possibly
442more restrictive setting. (Permissions given in octal).
443
444=item cannot make file [file] writeable: [errmsg]
445
446C<rmtree> attempted to force the permissions of a file to ensure it
447could be deleted, but failed to do so. It will, however, still attempt
448to unlink the file.
449
450=item cannot unlink file [file]: [errmsg]
b8d5f521 451
0b3d36bd 452C<rmtree> failed to remove a file. Probably a permissions issue.
453
454=item cannot restore permissions of [file] to [0nnn]: [errmsg]
455
456After having failed to remove a file, C<rmtree> was also unable
457to restore the permissions on the file to a possibily less permissive
458setting. (Permissions given in octal).
b8d5f521 459
460=back
461
12c2e016 462=head1 SEE ALSO
463
464=over 4
465
466=item *
467
468L<Find::File::Rule>
469
0b3d36bd 470When removing directory trees, if you want to examine each file to
471decide whether to delete it (and possibly leaving large swathes
472alone), F<File::Find::Rule> offers a convenient and flexible approach
473to examining directory trees.
12c2e016 474
475=back
476
477=head1 BUGS
478
479Please report all bugs on the RT queue:
480
481L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=File-Path>
482
0b3d36bd 483=head1 ACKNOWLEDGEMENTS
fed7345c 484
0b3d36bd 485Paul Szabo identified the race condition orignially, and Brendan
486O'Dea wrote an implementation for Debian that addressed the problem.
487That code was used as a basis for the current code. Their efforts
488are greatly appreciated.
12c2e016 489
0b3d36bd 490=head1 AUTHORS
491
492Tim Bunce <F<Tim.Bunce@ig.co.uk>> and Charles Bailey
493<F<bailey@newman.upenn.edu>>. Currently maintained by David Landgren
494<F<david@landgren.net>>.
12c2e016 495
496=head1 COPYRIGHT
497
498This module is copyright (C) Charles Bailey, Tim Bunce and
499David Landgren 1995-2007. All rights reserved.
500
501=head1 LICENSE
502
503This library is free software; you can redistribute it and/or modify
504it under the same terms as Perl itself.
fed7345c 505
fed7345c 506=cut
507
12c2e016 508use 5.005_04;
037c8c09 509use strict;
68dc0745 510
0b3d36bd 511use Cwd 'getcwd';
12c2e016 512use File::Basename ();
513use File::Spec ();
0b3d36bd 514
12c2e016 515BEGIN {
91c4f65e 516 if ($] < 5.006) {
12c2e016 517 # can't say 'opendir my $dh, $dirname'
518 # need to initialise $dh
519 eval "use Symbol";
520 }
521}
522
523use Exporter ();
524use vars qw($VERSION @ISA @EXPORT);
b5400373 525$VERSION = '2.00_12';
12c2e016 526@ISA = qw(Exporter);
527@EXPORT = qw(mkpath rmtree);
fed7345c 528
68dc0745 529my $Is_VMS = $^O eq 'VMS';
ffb9ee5f 530my $Is_MacOS = $^O eq 'MacOS';
037c8c09 531
532# These OSes complain if you want to remove a file that you have no
533# write permission to:
12c2e016 534my $Force_Writeable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' ||
fa6a1c44 535 $^O eq 'amigaos' || $^O eq 'MacOS' || $^O eq 'epoc');
748a9306 536
12c2e016 537sub _carp {
8878f897 538 require Carp;
539 goto &Carp::carp;
540}
541
12c2e016 542sub _croak {
8878f897 543 require Carp;
544 goto &Carp::croak;
545}
546
0b3d36bd 547sub _error {
548 my $arg = shift;
549 my $message = shift;
550 my $object = shift;
551
552 if ($arg->{error}) {
553 $object = '' unless defined $object;
554 push @{${$arg->{error}}}, {$object => "$message: $!"};
555 }
556 else {
557 _carp(defined($object) ? "$message for $object: $!" : "$message: $!");
558 }
559}
560
a5f75d66 561sub mkpath {
cd117d8b 562 my $old_style = (
3376a30f 563 UNIVERSAL::isa($_[0],'ARRAY')
b92ffa59 564 or (@_ == 2 and (defined $_[1] ? $_[1] =~ /\A\d+\z/ : 1))
565 or (@_ == 3
566 and (defined $_[1] ? $_[1] =~ /\A\d+\z/ : 1)
567 and (defined $_[2] ? $_[2] =~ /\A\d+\z/ : 1)
568 )
cd117d8b 569 ) ? 1 : 0;
12c2e016 570
571 my $arg;
572 my $paths;
573
cd117d8b 574 if ($old_style) {
575 my ($verbose, $mode);
576 ($paths, $verbose, $mode) = @_;
577 $paths = [$paths] unless UNIVERSAL::isa($paths,'ARRAY');
578 $arg->{verbose} = defined $verbose ? $verbose : 0;
579 $arg->{mode} = defined $mode ? $mode : 0777;
580 }
581 else {
3376a30f 582 if (@_ > 0 and UNIVERSAL::isa($_[-1], 'HASH')) {
12c2e016 583 $arg = pop @_;
584 exists $arg->{mask} and $arg->{mode} = delete $arg->{mask};
585 $arg->{mode} = 0777 unless exists $arg->{mode};
586 ${$arg->{error}} = [] if exists $arg->{error};
587 }
588 else {
589 @{$arg}{qw(verbose mode)} = (0, 0777);
590 }
591 $paths = [@_];
592 }
12c2e016 593 return _mkpath($arg, $paths);
594}
595
596sub _mkpath {
597 my $arg = shift;
598 my $paths = shift;
599
ffb9ee5f 600 local($")=$Is_MacOS ? ":" : "/";
037c8c09 601 my(@created,$path);
68dc0745 602 foreach $path (@$paths) {
12c2e016 603 next unless length($path);
1b1e14d3 604 $path .= '/' if $^O eq 'os2' and $path =~ /^\w:\z/s; # feature of CRT
037c8c09 605 # Logic wants Unix paths, so go with the flow.
e3830a4e 606 if ($Is_VMS) {
607 next if $path eq '/';
608 $path = VMS::Filespec::unixify($path);
491527d0 609 }
e3830a4e 610 next if -d $path;
611 my $parent = File::Basename::dirname($path);
612 unless (-d $parent or $path eq $parent) {
12c2e016 613 push(@created,_mkpath($arg, [$parent]));
614 }
615 print "mkdir $path\n" if $arg->{verbose};
616 if (mkdir($path,$arg->{mode})) {
617 push(@created, $path);
dde45d8e 618 }
12c2e016 619 else {
620 my $save_bang = $!;
621 my ($e, $e1) = ($save_bang, $^E);
dde45d8e 622 $e .= "; $e1" if $e ne $e1;
c3420933 623 # allow for another process to have created it meanwhile
12c2e016 624 if (!-d $path) {
625 $! = $save_bang;
626 if ($arg->{error}) {
627 push @{${$arg->{error}}}, {$path => $e};
628 }
629 else {
630 _croak("mkdir $path: $e");
631 }
67e4c828 632 }
fed7345c 633 }
12c2e016 634 }
635 return @created;
fed7345c 636}
637
638sub rmtree {
cd117d8b 639 my $old_style = (
3376a30f 640 UNIVERSAL::isa($_[0],'ARRAY')
b92ffa59 641 or (@_ == 2 and (defined $_[1] ? $_[1] =~ /\A\d+\z/ : 1))
642 or (@_ == 3
643 and (defined $_[1] ? $_[1] =~ /\A\d+\z/ : 1)
644 and (defined $_[2] ? $_[2] =~ /\A\d+\z/ : 1)
645 )
cd117d8b 646 ) ? 1 : 0;
12c2e016 647
648 my $arg;
649 my $paths;
650
cd117d8b 651 if ($old_style) {
12c2e016 652 my ($verbose, $safe);
653 ($paths, $verbose, $safe) = @_;
12c2e016 654 $arg->{verbose} = defined $verbose ? $verbose : 0;
655 $arg->{safe} = defined $safe ? $safe : 0;
fed7345c 656
3376a30f 657 if (defined($paths) and length($paths)) {
658 $paths = [$paths] unless UNIVERSAL::isa($paths,'ARRAY');
659 }
660 else {
cd117d8b 661 _carp ("No root path(s) specified\n");
662 return 0;
663 }
ee79a11f 664 }
665 else {
cd117d8b 666 if (@_ > 0 and UNIVERSAL::isa($_[-1],'HASH')) {
667 $arg = pop @_;
668 ${$arg->{error}} = [] if exists $arg->{error};
669 ${$arg->{result}} = [] if exists $arg->{result};
12c2e016 670 }
cd117d8b 671 else {
672 @{$arg}{qw(verbose safe)} = (0, 0);
ee79a11f 673 }
cd117d8b 674 $paths = [@_];
3376a30f 675 }
0b3d36bd 676
677 $arg->{prefix} = '';
678 $arg->{depth} = 0;
679
680 $arg->{cwd} = getcwd() or do {
681 _error($arg, "cannot fetch initial working directory");
682 return 0;
683 };
684 for ($arg->{cwd}) { /\A(.*)\Z/; $_ = $1 } # untaint
685
686 @{$arg}{qw(device inode)} = (stat $arg->{cwd})[0,1] or do {
687 _error($arg, "cannot stat initial working directory", $arg->{cwd});
688 return 0;
689 };
690
12c2e016 691 return _rmtree($arg, $paths);
692}
ee79a11f 693
12c2e016 694sub _rmtree {
695 my $arg = shift;
696 my $paths = shift;
0b3d36bd 697
698 my $count = 0;
699 my $curdir = File::Spec->curdir();
700 my $updir = File::Spec->updir();
701
12c2e016 702 my (@files, $root);
cd117d8b 703 foreach $root (@$paths) {
ffb9ee5f 704 if ($Is_MacOS) {
0b3d36bd 705 $root = ":$root" unless $root =~ /:/;
706 $root .= ":" unless $root =~ /:\z/;
12c2e016 707 }
708 else {
0b3d36bd 709 $root =~ s{/\z}{};
ffb9ee5f 710 }
0b3d36bd 711
712 # since we chdir into each directory, it may not be obvious
713 # to figure out where we are if we generate a message about
714 # a file name. We therefore construct a semi-canonical
715 # filename, anchored from the directory being unlinked (as
716 # opposed to being truly canonical, anchored from the root (/).
717
718 my $canon = $arg->{prefix}
b5400373 719 ? File::Spec->catfile($arg->{prefix}, $root)
0b3d36bd 720 : $root
721 ;
722
b5400373 723 my ($ldev, $lino, $perm) = (lstat $root)[0,1,2] or next;
724
7025f710 725 if ( -d _ ) {
b5400373 726 $root = VMS::Filespec::pathify($root) if $Is_VMS;
0b3d36bd 727 if (!chdir($root)) {
728 # see if we can escalate privileges to get in
729 # (e.g. funny protection mask such as -w- instead of rwx)
730 $perm &= 07777;
731 my $nperm = $perm | 0700;
732 if (!($arg->{safe} or $nperm == $perm or chmod($nperm, $root))) {
733 _error($arg, "cannot make child directory read-write-exec", $canon);
734 next;
735 }
736 elsif (!chdir($root)) {
737 _error($arg, "cannot chdir to child", $canon);
738 next;
739 }
740 }
741
742 my ($device, $inode, $perm) = (stat $curdir)[0,1,2] or do {
743 _error($arg, "cannot stat current working directory", $canon);
744 return $count;
745 };
746
747 ($ldev eq $device and $lino eq $inode)
748 or _croak("directory $canon changed before chdir, expected dev=$ldev inode=$lino, actual dev=$device ino=$inode, aborting.");
749
750 $perm &= 07777; # don't forget setuid, setgid, sticky bits
751 my $nperm = $perm | 0700;
752
e2ba98a1 753 # notabene: 0700 is for making readable in the first place,
037c8c09 754 # it's also intended to change it to writable in case we have
755 # to recurse in which case we are better than rm -rf for
756 # subtrees with strange permissions
0b3d36bd 757
758 if (!($arg->{safe} or $nperm == $perm or chmod($nperm, $curdir))) {
759 _error($arg, "cannot make directory read+writeable", $canon);
760 $nperm = $perm;
12c2e016 761 }
762
763 my $d;
764 $d = gensym() if $] < 5.006;
0b3d36bd 765 if (!opendir $d, $curdir) {
766 _error($arg, "cannot opendir", $canon);
12c2e016 767 @files = ();
768 }
769 else {
7068481f 770 no strict 'refs';
771 if (!defined ${"\cTAINT"} or ${"\cTAINT"}) {
12c2e016 772 # Blindly untaint dir names if taint mode is
773 # active, or any perl < 5.006
774 @files = map { /\A(.*)\z/s; $1 } readdir $d;
775 }
776 else {
7068481f 777 @files = readdir $d;
778 }
ff21075d 779 closedir $d;
780 }
037c8c09 781
463ea4b9 782 if ($Is_VMS) {
0b3d36bd 783 # Deleting large numbers of files from VMS Files-11
784 # filesystems is faster if done in reverse ASCIIbetical order.
785 # include '.' to '.;' from blead patch #31775
786 @files = map {$_ eq '.' ? '.;' : $_} reverse @files;
787 ($root = VMS::Filespec::unixify($root)) =~ s/\.dir\z//;
788 }
789 @files = grep {$_ ne $updir and $_ ne $curdir} @files;
790
791 if (@files) {
792 # remove the contained files before the directory itself
793 my $narg = {%$arg};
794 @{$narg}{qw(device inode cwd prefix depth)}
795 = ($device, $inode, $updir, $canon, $arg->{depth}+1);
796 $count += _rmtree($narg, \@files);
797 }
798
799 # restore directory permissions of required now (in case the rmdir
800 # below fails), while we are still in the directory and may do so
801 # without a race via '.'
802 if ($nperm != $perm and not chmod($perm, $curdir)) {
803 _error($arg, "cannot reset chmod", $canon);
12c2e016 804 }
0b3d36bd 805
806 # don't leave the client code in an unexpected directory
807 chdir($arg->{cwd})
808 or _croak("cannot chdir to $arg->{cwd} from $canon: $!, aborting.");
809
810 # ensure that a chdir upwards didn't take us somewhere other
811 # than we expected (see CVE-2002-0435)
812 ($device, $inode) = (stat $curdir)[0,1]
813 or _croak("cannot stat prior working directory $arg->{cwd}: $!, aborting.");
814
815 ($arg->{device} eq $device and $arg->{inode} eq $inode)
816 or _croak("previous directory $arg->{cwd} changed before entering $canon, expected dev=$ldev inode=$lino, actual dev=$device ino=$inode, aborting.");
817
12c2e016 818 if ($arg->{depth} or !$arg->{keep_root}) {
819 if ($arg->{safe} &&
037c8c09 820 ($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) {
12c2e016 821 print "skipped $root\n" if $arg->{verbose};
037c8c09 822 next;
823 }
0b3d36bd 824 if (!chmod $perm | 0700, $root) {
12c2e016 825 if ($Force_Writeable) {
0b3d36bd 826 _error($arg, "cannot make directory writeable", $canon);
12c2e016 827 }
828 }
829 print "rmdir $root\n" if $arg->{verbose};
96e4d5b1 830 if (rmdir $root) {
12c2e016 831 push @{${$arg->{result}}}, $root if $arg->{result};
96e4d5b1 832 ++$count;
833 }
834 else {
0b3d36bd 835 _error($arg, "cannot remove directory", $canon);
836 if (!chmod($perm, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
12c2e016 837 ) {
0b3d36bd 838 _error($arg, sprintf("cannot restore permissions to 0%o",$perm), $canon);
12c2e016 839 }
840 }
841 }
842 }
843 else {
0b3d36bd 844 # not a directory
b5400373 845
846 $root = VMS::Filespec::vmsify("./$root")
847 if $Is_VMS && !File::Spec->file_name_is_absolute($root);
848
12c2e016 849 if ($arg->{safe} &&
64f6ddac 850 ($Is_VMS ? !&VMS::Filespec::candelete($root)
851 : !(-l $root || -w $root)))
852 {
12c2e016 853 print "skipped $root\n" if $arg->{verbose};
037c8c09 854 next;
855 }
0b3d36bd 856
857 my $nperm = $perm & 07777 | 0600;
858 if ($nperm != $perm and not chmod $nperm, $root) {
12c2e016 859 if ($Force_Writeable) {
0b3d36bd 860 _error($arg, "cannot make file writeable", $canon);
12c2e016 861 }
862 }
0b3d36bd 863 print "unlink $canon\n" if $arg->{verbose};
037c8c09 864 # delete all versions under VMS
94d4f21c 865 for (;;) {
12c2e016 866 if (unlink $root) {
867 push @{${$arg->{result}}}, $root if $arg->{result};
868 }
869 else {
0b3d36bd 870 _error($arg, "cannot unlink file", $canon);
871 $Force_Writeable and chmod($perm, $root) or
872 _error($arg, sprintf("cannot restore permissions to 0%o",$perm), $canon);
94d4f21c 873 last;
96e4d5b1 874 }
94d4f21c 875 ++$count;
876 last unless $Is_VMS && lstat $root;
037c8c09 877 }
878 }
fed7345c 879 }
880
12c2e016 881 return $count;
fed7345c 882}
883
8841;