1 /* remove.c -- core functions for removing files and directories
2 Copyright (C) 88, 90, 91, 1994-2001 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Extracted from rm.c and librarified by Jim Meyering. */
26 #include <sys/types.h>
32 typedef enum {false = 0, true = 1} bool;
43 #define obstack_chunk_alloc malloc
44 #define obstack_chunk_free free
47 # if defined (__GNUC__) || __STDC__
48 # define PARAMS(args) args
50 # define PARAMS(args) ()
54 /* On systems with an lstat function that accepts the empty string,
55 arrange to make lstat calls go through the wrapper function. */
56 #if HAVE_LSTAT_EMPTY_STRING_BUG
57 int rpl_lstat
PARAMS((const char *, struct stat
*));
58 # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
61 #ifdef D_INO_IN_DIRENT
62 # define D_INO(dp) ((dp)->d_ino)
63 # define ENABLE_CYCLE_CHECK
65 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
70 # define S_ISLNK(Mode) 0
73 /* Initial capacity of per-directory hash table of entries that have
74 been processed but not been deleted. */
75 #define HT_INITIAL_CAPACITY 13
77 /* Initial capacity of the active directory hash table. This table will
78 be resized only for hierarchies more than about 45 levels deep. */
79 #define ACTIVE_DIR_INITIAL_CAPACITY 53
84 extern char *program_name
;
86 /* state initialized by remove_init, freed by remove_fini */
88 /* An entry in the active_dir_map. */
96 /* The name of the directory (starting with and relative to a command
97 line argument) being processed. When a subdirectory is entered, a new
98 component is appended (pushed). When RM chdir's out of a directory,
99 the top component is removed (popped). This is used to form a full
100 file name when necessary. */
101 static struct obstack dir_stack
;
103 /* Stack of lengths of directory names (including trailing slash)
104 appended to dir_stack. We have to have a separate stack of lengths
105 (rather than just popping back to previous slash) because the first
106 element pushed onto the dir stack may contain slashes. */
107 static struct obstack len_stack
;
109 /* Set of `active' directories from the current command-line argument
110 to the level in the hierarchy at which files are being removed.
111 A directory is added to the active set when RM begins removing it
112 (or its entries), and it is removed from the set just after RM has
113 finished processing it.
115 This is actually a map (not a set), implemented with a hash table.
116 For each active directory, it maps the directory's inode number to the
117 depth of that directory relative to the root of the tree being deleted.
118 A directory specified on the command line has depth zero.
119 This construct is used to detect directory cycles so that RM can warn
120 about them rather than iterating endlessly. */
121 #ifdef ENABLE_CYCLE_CHECK
122 static struct hash_table
*active_dir_map
;
125 static inline unsigned int
128 return obstack_object_size (&len_stack
) / sizeof (size_t);
132 print_nth_dir (FILE *stream
, unsigned int depth
)
134 size_t *length
= (size_t *) obstack_base (&len_stack
);
135 char *dir_name
= (char *) obstack_base (&dir_stack
);
136 unsigned int sum
= 0;
139 assert (depth
< current_depth ());
141 for (i
= 0; i
<= depth
; i
++)
146 fwrite (dir_name
, 1, sum
- 1, stream
);
149 static inline struct active_dir_ent
*
150 make_active_dir_ent (ino_t inum
, dev_t device
, unsigned int depth
)
152 struct active_dir_ent
*ent
;
153 ent
= (struct active_dir_ent
*) xmalloc (sizeof *ent
);
155 ent
->st_dev
= device
;
161 hash_active_dir_ent (void const *x
, unsigned int table_size
)
163 struct active_dir_ent
const *ade
= x
;
165 /* Ignoring the device number here should be fine. */
166 return ade
->st_ino
% table_size
;
170 hash_compare_active_dir_ents (void const *x
, void const *y
)
172 struct active_dir_ent
const *a
= x
;
173 struct active_dir_ent
const *b
= y
;
174 return SAME_INODE (*a
, *b
) ? true : false;
177 /* A hash function for null-terminated char* strings using
178 the method described in Aho, Sethi, & Ullman, p 436. */
181 hash_pjw (const void *x
, unsigned int tablesize
)
190 if ((g
= h
& (unsigned int) 0xf0000000) != 0)
191 h
= (h
^ (g
>> 24)) ^ g
;
194 return (h
% tablesize
);
198 hash_compare_strings (void const *x
, void const *y
)
200 return STREQ (x
, y
) ? true : false;
204 push_dir (const char *dir_name
)
208 len
= strlen (dir_name
);
210 /* Append the string onto the stack. */
211 obstack_grow (&dir_stack
, dir_name
, len
);
213 /* Append a trailing slash. */
214 obstack_1grow (&dir_stack
, '/');
216 /* Add one for the slash. */
219 /* Push the length (including slash) onto its stack. */
220 obstack_grow (&len_stack
, &len
, sizeof (len
));
226 int n_lengths
= obstack_object_size (&len_stack
) / sizeof (size_t);
227 size_t *length
= (size_t *) obstack_base (&len_stack
);
230 assert (n_lengths
> 0);
231 top_len
= length
[n_lengths
- 1];
232 assert (top_len
>= 2);
234 /* Pop off the specified length of pathname. */
235 assert (obstack_object_size (&dir_stack
) >= top_len
);
236 obstack_blank (&dir_stack
, -top_len
);
238 /* Pop the length stack, too. */
239 assert (obstack_object_size (&len_stack
) >= sizeof (size_t));
240 obstack_blank (&len_stack
, (int) -(sizeof (size_t)));
243 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
244 buffer, DST, so that the last source byte is at the end of the destination
245 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
246 Set *RESULT to point to the beginning of (the portion of) the source data
247 in DST. Return the number of bytes remaining in the destination buffer. */
250 right_justify (char *dst
, size_t dst_len
, const char *src
, size_t src_len
,
251 char **result
, int *truncated
)
256 if (src_len
<= dst_len
)
259 dp
= dst
+ (dst_len
- src_len
);
264 sp
= src
+ (src_len
- dst_len
);
270 *result
= memcpy (dp
, sp
, src_len
);
271 return dst_len
- src_len
;
274 /* Using the global directory name obstack, create the full path to FILENAME.
275 Return it in sometimes-realloc'd space that should not be freed by the
276 caller. Realloc as necessary. If realloc fails, use a static buffer
277 and put as long a suffix in that buffer as possible. */
280 full_filename (const char *filename
)
282 static char *buf
= NULL
;
283 static size_t n_allocated
= 0;
285 int dir_len
= obstack_object_size (&dir_stack
);
286 char *dir_name
= (char *) obstack_base (&dir_stack
);
287 size_t n_bytes_needed
;
290 filename_len
= strlen (filename
);
291 n_bytes_needed
= dir_len
+ filename_len
+ 1;
293 if (n_bytes_needed
> n_allocated
)
295 /* This code requires that realloc accept NULL as the first arg.
296 This function must not use xrealloc. Otherwise, an out-of-memory
297 error involving a file name to be expanded here wouldn't ever
298 be issued. Use realloc and fall back on using a static buffer
299 if memory allocation fails. */
300 buf
= realloc (buf
, n_bytes_needed
);
301 n_allocated
= n_bytes_needed
;
305 #define SBUF_SIZE 512
306 #define ELLIPSES_PREFIX "[...]"
307 static char static_buf
[SBUF_SIZE
];
312 len
= right_justify (static_buf
, SBUF_SIZE
, filename
,
313 filename_len
+ 1, &p
, &truncated
);
314 right_justify (static_buf
, len
, dir_name
, dir_len
, &p
, &truncated
);
317 memcpy (static_buf
, ELLIPSES_PREFIX
,
318 sizeof (ELLIPSES_PREFIX
) - 1);
324 /* Copy directory part, including trailing slash, and then
325 append the filename part, including a trailing zero byte. */
326 memcpy (mempcpy (buf
, dir_name
, dir_len
), filename
, filename_len
+ 1);
328 assert (strlen (buf
) + 1 == n_bytes_needed
);
334 fspec_init_common (struct File_spec
*fs
)
336 fs
->have_full_mode
= 0;
337 fs
->have_filetype_mode
= 0;
342 fspec_init_file (struct File_spec
*fs
, const char *filename
)
344 fs
->filename
= (char *) filename
;
345 fspec_init_common (fs
);
349 fspec_init_dp (struct File_spec
*fs
, struct dirent
*dp
)
351 fs
->filename
= dp
->d_name
;
352 fspec_init_common (fs
);
353 fs
->st_ino
= D_INO (dp
);
355 #if D_TYPE_IN_DIRENT && defined DT_UNKNOWN && defined DTTOIF
356 if (dp
->d_type
!= DT_UNKNOWN
)
358 fs
->have_filetype_mode
= 1;
359 fs
->mode
= DTTOIF (dp
->d_type
);
365 fspec_get_full_mode (struct File_spec
*fs
)
367 struct stat stat_buf
;
369 if (fs
->have_full_mode
)
372 if (lstat (fs
->filename
, &stat_buf
))
375 fs
->have_full_mode
= 1;
376 fs
->have_filetype_mode
= 1;
377 fs
->mode
= stat_buf
.st_mode
;
378 fs
->st_ino
= stat_buf
.st_ino
;
380 fs
->st_dev
= stat_buf
.st_dev
;
386 fspec_get_device_number (struct File_spec
*fs
)
388 struct stat stat_buf
;
393 if (lstat (fs
->filename
, &stat_buf
))
396 fs
->have_full_mode
= 1;
397 fs
->have_filetype_mode
= 1;
398 fs
->mode
= stat_buf
.st_mode
;
399 fs
->st_ino
= stat_buf
.st_ino
;
401 fs
->st_dev
= stat_buf
.st_dev
;
407 fspec_get_filetype_mode (struct File_spec
*fs
, mode_t
*filetype_mode
)
411 fail
= fs
->have_filetype_mode
? 0 : fspec_get_full_mode (fs
);
413 *filetype_mode
= fs
->mode
;
419 fspec_filetype_mode (const struct File_spec
*fs
)
421 assert (fs
->have_filetype_mode
);
426 same_file (const char *file_1
, const char *file_2
)
428 struct stat sb1
, sb2
;
429 return (lstat (file_1
, &sb1
) == 0
430 && lstat (file_2
, &sb2
) == 0
431 && SAME_INODE (sb1
, sb2
));
435 /* Recursively remove all of the entries in the current directory.
436 Return an indication of the success of the operation. */
438 static enum RM_status
439 remove_cwd_entries (const struct rm_options
*x
)
441 /* NOTE: this is static. */
442 static DIR *dirp
= NULL
;
444 /* NULL or a malloc'd and initialized hash table of entries in the
445 current directory that have been processed but not removed --
446 due either to an error or to an interactive `no' response. */
447 struct hash_table
*ht
= NULL
;
449 /* FIXME: describe */
450 static struct obstack entry_name_pool
;
451 static int first_call
= 1;
453 enum RM_status status
= RM_OK
;
458 obstack_init (&entry_name_pool
);
465 /* FIXME-someday: but this is actually the previously opened dir. */
466 error (0, errno
, "%s", quote (full_filename (".")));
474 /* FIXME: why do this? */
477 dirp
= opendir (".");
480 if (errno
!= ENOENT
|| !x
->ignore_missing_files
)
482 error (0, errno
, _("cannot open directory %s"),
483 quote (full_filename (".")));
493 enum RM_status tmp_status
;
496 /* FILE should be skipped if it is `.' or `..', or if it is in
497 the table, HT, of entries we've already processed. */
498 #define SKIPPABLE(Ht, File) \
499 (DOT_OR_DOTDOT(File) || (Ht && hash_lookup (Ht, File)))
501 /* FIXME: use readdir_r directly into an obstack to avoid
502 the obstack_copy0 below --
503 Suggestion from Uli. Be careful -- there are different
504 prototypes on e.g. Solaris.
506 Do something like this:
507 #define NAME_MAX_FOR(Parent_dir) pathconf ((Parent_dir),
509 dp = obstack_alloc (sizeof (struct dirent)
510 + NAME_MAX_FOR (".") + 1);
511 fail = xreaddir (dirp, dp);
512 where xreaddir is ...
514 But what about systems like the hurd where NAME_MAX is supposed
515 to be effectively unlimited. We don't want to have to allocate
516 a huge buffer to accommodate maximum possible entry name. */
520 #if ! HAVE_WORKING_READDIR
523 /* Since we have probably modified the directory since it
524 was opened, readdir returning NULL does not necessarily
525 mean we have read the last entry. Rewind it and check
526 again. This happens on SunOS4.1.4 with 254 or more files
529 while ((dp
= readdir (dirp
)) && SKIPPABLE (ht
, dp
->d_name
))
539 if (SKIPPABLE (ht
, dp
->d_name
))
542 fspec_init_dp (&fs
, dp
);
544 /* Save a copy of the name of this entry, in case we have
545 to add it to the set of unremoved entries below. */
546 entry_name
= obstack_copy0 (&entry_name_pool
,
547 dp
->d_name
, NLENGTH (dp
));
549 /* CAUTION: after this call to rm, DP may not be valid --
550 it may have been freed due to a close in a recursive call
551 (through rm and remove_dir) to this function. */
552 tmp_status
= rm (&fs
, 0, x
);
555 if (tmp_status
> status
)
557 assert (VALID_STATUS (status
));
559 /* If this entry was not removed (due either to an error or to
560 an interactive `no' response), record it in the hash table so
561 we don't consider it again if we reopen this directory later. */
566 ht
= hash_initialize (HT_INITIAL_CAPACITY
, NULL
, hash_pjw
,
567 hash_compare_strings
, NULL
);
571 if (! hash_insert (ht
, entry_name
))
576 /* This entry was not saved in the hash table. Free it. */
577 obstack_free (&entry_name_pool
, entry_name
);
584 while (dirp
== NULL
);
590 error (0, errno
, _("closing directory %s"),
591 quote (full_filename (".")));
602 if (obstack_object_size (&entry_name_pool
) > 0)
603 obstack_free (&entry_name_pool
, obstack_base (&entry_name_pool
));
608 /* Query the user if appropriate, and if ok try to remove the
609 file or directory specified by FS. Return RM_OK if it is removed,
610 and RM_ERROR or RM_USER_DECLINED if not. */
612 static enum RM_status
613 remove_file (struct File_spec
*fs
, const struct rm_options
*x
)
616 char *pathname
= fs
->filename
;
618 if (!x
->ignore_missing_files
&& (x
->interactive
|| x
->stdin_tty
)
619 && euidaccess (pathname
, W_OK
))
621 if (!S_ISLNK (fspec_filetype_mode (fs
)))
624 (S_ISDIR (fspec_filetype_mode (fs
))
625 ? _("%s: remove write-protected directory %s? ")
626 : _("%s: remove write-protected file %s? ")),
627 program_name
, quote (full_filename (pathname
)));
629 return RM_USER_DECLINED
;
635 if (!asked
&& x
->interactive
)
637 /* FIXME: use a variant of error (instead of fprintf) that doesn't
638 append a newline. Then we won't have to declare program_name in
641 (S_ISDIR (fspec_filetype_mode (fs
))
642 ? _("%s: remove directory %s? ")
643 : _("%s: remove %s? ")),
644 program_name
, quote (full_filename (pathname
)));
646 return RM_USER_DECLINED
;
650 printf (_("removing %s\n"), quote (full_filename (pathname
)));
652 if (unlink (pathname
) && (errno
!= ENOENT
|| !x
->ignore_missing_files
))
654 error (0, errno
, _("cannot unlink %s"), quote (full_filename (pathname
)));
660 /* If not in recursive mode, print an error message and return RM_ERROR.
661 Otherwise, query the user if appropriate, then try to recursively
662 remove the directory specified by FS. Return RM_OK if it is removed,
663 and RM_ERROR or RM_USER_DECLINED if not.
664 FIXME: describe need_save_cwd parameter. */
666 static enum RM_status
667 remove_dir (struct File_spec
*fs
, int need_save_cwd
, const struct rm_options
*x
)
669 enum RM_status status
;
670 struct saved_cwd cwd
;
671 char *dir_name
= fs
->filename
;
672 const char *fmt
= NULL
;
676 error (0, 0, _("%s is a directory"), quote (full_filename (dir_name
)));
680 if (!x
->ignore_missing_files
&& (x
->interactive
|| x
->stdin_tty
)
681 && euidaccess (dir_name
, W_OK
))
683 fmt
= _("%s: directory %s is write protected; descend into it anyway? ");
685 else if (x
->interactive
)
687 fmt
= _("%s: descend into directory %s? ");
692 fprintf (stderr
, fmt
, program_name
, quote (full_filename (dir_name
)));
694 return RM_USER_DECLINED
;
698 printf (_("removing all entries of directory %s\n"),
699 quote (full_filename (dir_name
)));
701 /* Save cwd if needed. */
702 if (need_save_cwd
&& save_cwd (&cwd
))
705 /* Make target directory the current one. */
706 if (chdir (dir_name
) < 0)
708 error (0, errno
, _("cannot change to directory %s"),
709 quote (full_filename (dir_name
)));
715 /* Verify that the device and inode numbers of `.' are the same as
716 the ones we recorded for dir_name before we cd'd into it. This
717 detects the scenario in which an attacker tries to make Bob's rm
718 command remove some other directory belonging to Bob. The method
719 would be to replace an existing lstat'd but-not-yet-removed directory
720 with a symlink to the target directory. */
723 if (lstat (".", &sb
))
724 error (EXIT_FAILURE
, errno
,
725 _("cannot lstat `.' in %s"), quote (full_filename (dir_name
)));
727 assert (fs
->have_device
);
728 if (!SAME_INODE (sb
, *fs
))
730 error (EXIT_FAILURE
, 0,
731 _("ERROR: the directory %s initially had device/inode\n\
732 numbers %lu/%lu, but now (after a chdir into it), the numbers for `.'\n\
733 are %lu/%lu. That means that while rm was running, the directory\n\
734 was replaced with either another directory or a link to another directory."),
735 quote (full_filename (dir_name
)),
736 (unsigned long)(fs
->st_dev
),
737 (unsigned long)(fs
->st_ino
),
738 (unsigned long)(sb
.st_dev
),
739 (unsigned long)(sb
.st_ino
));
745 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
746 it because it is just a pointer to the dir entry's d_name field, and
747 remove_cwd_entries may close the directory. */
748 ASSIGN_STRDUPA (dir_name
, dir_name
);
750 status
= remove_cwd_entries (x
);
757 if (restore_cwd (&cwd
, NULL
, NULL
))
764 else if (chdir ("..") < 0)
766 error (0, errno
, _("cannot change back to directory %s via `..'"),
767 quote (full_filename (dir_name
)));
773 fprintf (stderr
, _("%s: remove directory %s%s? "),
775 quote (full_filename (dir_name
)),
776 (status
!= RM_OK
? _(" (might be nonempty)") : ""));
779 return RM_USER_DECLINED
;
784 printf (_("removing the directory itself: %s\n"),
785 quote (full_filename (dir_name
)));
787 if (rmdir (dir_name
) && (errno
!= ENOENT
|| !x
->ignore_missing_files
))
789 int saved_errno
= errno
;
794 /* See if rmdir just failed because DIR_NAME is the current directory.
795 If so, give a better diagnostic than `rm: cannot remove directory
796 `...': Invalid argument' */
797 if (errno
== EINVAL
&& same_file (".", dir_name
))
799 error (0, 0, _("cannot remove current directory %s"),
800 quote (full_filename (dir_name
)));
804 error (0, saved_errno
, _("cannot remove directory %s"),
805 quote (full_filename (dir_name
)));
813 /* Remove the file or directory specified by FS after checking appropriate
814 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
815 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
816 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
817 name (and hence must specify a file in the current directory). */
820 rm (struct File_spec
*fs
, int user_specified_name
, const struct rm_options
*x
)
822 mode_t filetype_mode
;
824 if (user_specified_name
)
826 /* CAUTION: this use of base_name works only because any
827 trailing slashes in fs->filename have already been removed. */
828 char *base
= base_name (fs
->filename
);
830 if (DOT_OR_DOTDOT (base
))
832 error (0, 0, _("cannot remove `.' or `..'"));
837 if (fspec_get_filetype_mode (fs
, &filetype_mode
))
839 if (x
->ignore_missing_files
&& errno
== ENOENT
)
842 error (0, errno
, _("cannot remove %s"),
843 quote (full_filename (fs
->filename
)));
847 #ifdef ENABLE_CYCLE_CHECK
848 if (S_ISDIR (filetype_mode
))
850 struct active_dir_ent
*old_ent
;
851 struct active_dir_ent
*new_ent
;
853 /* If there is already a directory in the map with the same device
854 and inode numbers, then there is a directory cycle. */
856 if (fspec_get_device_number (fs
))
858 error (0, errno
, _("cannot stat %s"),
859 quote (full_filename (fs
->filename
)));
862 new_ent
= make_active_dir_ent (fs
->st_ino
, fs
->st_dev
, current_depth ());
863 old_ent
= hash_lookup (active_dir_map
, new_ent
);
867 WARNING: Circular directory structure.\n\
868 This almost certainly means that you have a corrupted file system.\n\
869 NOTIFY YOUR SYSTEM MANAGER.\n\
870 The following two directories have the same inode number:\n"));
871 print_nth_dir (stderr
, old_ent
->depth
);
872 fprintf (stderr
, "\n%s\n", quote (full_filename (fs
->filename
)));
877 error (0, 0, _("continue? "));
884 /* Put this directory in the active_dir_map. */
885 if (! hash_insert (active_dir_map
, new_ent
))
890 if (!S_ISDIR (filetype_mode
) || x
->unlink_dirs
)
892 return remove_file (fs
, x
);
896 int need_save_cwd
= user_specified_name
;
897 enum RM_status status
;
900 need_save_cwd
= (strchr (fs
->filename
, '/') != NULL
);
902 status
= remove_dir (fs
, need_save_cwd
, x
);
904 #ifdef ENABLE_CYCLE_CHECK
906 struct active_dir_ent tmp
;
907 struct active_dir_ent
*old_ent
;
909 /* Remove this directory from the active_dir_map. */
910 tmp
.st_ino
= fs
->st_ino
;
911 assert (fs
->have_device
);
912 tmp
.st_dev
= fs
->st_dev
;
913 old_ent
= hash_delete (active_dir_map
, &tmp
);
914 assert (old_ent
!= NULL
);
926 /* Initialize dir-stack obstacks. */
927 obstack_init (&dir_stack
);
928 obstack_init (&len_stack
);
930 #ifdef ENABLE_CYCLE_CHECK
931 active_dir_map
= hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY
, NULL
,
933 hash_compare_active_dir_ents
, free
);
940 #ifdef ENABLE_CYCLE_CHECK
941 hash_free (active_dir_map
);
944 obstack_free (&dir_stack
, NULL
);
945 obstack_free (&len_stack
, NULL
);