1 /* `rm' file deletion utility for GNU.
2 Copyright (C) 88, 90, 91, 94, 95, 96, 1997 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 /* Written by Paul Rubin, David MacKenzie, and Richard Stallman.
19 Reworked to use chdir and hash tables by Jim Meyering. */
21 /* Implementation overview:
23 In the `usual' case RM saves no state for directories it is processing.
24 When a removal fails (either due to an error or to an interactive `no'
25 reply), the failure is noted (see descriptin of `ht' remove_cwd_entries)
26 so that when/if the containing directory is reopened, RM doesn't try to
27 remove the entry again.
29 RM may delete arbitrarily deep hierarchies -- even ones in which file
30 names (from root to leaf) are longer than the system-imposed maximum.
31 It does this by using chdir to change to each directory in turn before
32 removing the entries in that directory.
34 RM detects directory cycles by maintaining a table of the currently
35 active directories. See the description of active_dir_map below.
37 RM is careful to avoid forming full file names whenever possible.
38 A full file name is formed only when it is about to be used -- e.g.
39 in a diagnostic or in an interactive-mode prompt.
41 RM minimizes the number of lstat system calls it makes. On systems
42 that have valid d_type data in directory entries, RM makes only one
43 lstat call per command line argument -- regardless of the depth of
49 #include <sys/types.h>
59 # if defined (__GNUC__) || __STDC__
60 # define PARAMS(args) args
62 # define PARAMS(args) ()
66 #define obstack_chunk_alloc malloc
67 #define obstack_chunk_free free
69 #ifdef D_INO_IN_DIRENT
70 # define D_INO(dp) ((dp)->d_ino)
72 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
76 #if !defined (S_ISLNK)
77 # define S_ISLNK(Mode) 0
80 #define DOT_OR_DOTDOT(Basename) \
81 (Basename[0] == '.' && (Basename[1] == '\0' \
82 || (Basename[1] == '.' && Basename[2] == '\0')))
85 # define ASSIGN_STRDUPA(DEST, S) \
86 do { DEST = strdupa(S); } while (0)
88 # define ASSIGN_STRDUPA(DEST, S) \
91 size_t len_ = strlen (S) + 1; \
92 char *tmp_dest_ = alloca (len_); \
93 memcpy (tmp_dest_, (S), len_); \
101 /* These must be listed in order of increasing seriousness. */
107 #define VALID_STATUS(S) \
108 ((S) == RM_OK || (S) == RM_USER_DECLINED || (S) == RM_ERROR)
110 /* Initial capacity of per-directory hash table of entries that have
111 been processed but not been deleted. */
112 #define HT_INITIAL_CAPACITY 13
114 /* Initial capacity of the active directory hash table. This table will
115 be resized only for hierarchies more than about 45 levels deep. */
116 #define ACTIVE_DIR_INITIAL_CAPACITY 53
121 unsigned int have_filetype_mode:1;
122 unsigned int have_full_mode:1;
129 void strip_trailing_slashes ();
133 /* Forward dcl for recursively called function. */
134 static enum RM_status rm PARAMS ((struct File_spec *fs,
135 int user_specified_name));
137 /* Name this program was run with. */
140 /* If nonzero, display the name of each file removed. */
143 /* If nonzero, ignore nonexistant files. */
144 static int ignore_missing_files;
146 /* If nonzero, recursively remove directories. */
147 static int recursive;
149 /* If nonzero, query the user about whether to remove each file. */
150 static int interactive;
152 /* If nonzero, remove directories with unlink instead of rmdir, and don't
153 require a directory to be empty before trying to unlink it.
154 Only works for the super-user. */
155 static int unlink_dirs;
157 /* If nonzero, stdin is a tty. */
158 static int stdin_tty;
160 /* If nonzero, display usage information and exit. */
161 static int show_help;
163 /* If nonzero, print the version on standard output and exit. */
164 static int show_version;
166 /* The name of the directory (starting with and relative to a command
167 line argument) being processed. When a subdirectory is entered, a new
168 component is appended (pushed). When RM chdir's out of a directory,
169 the top component is removed (popped). This is used to form a full
170 file name when necessary. */
171 static struct obstack dir_stack;
173 /* Stack of lengths of directory names (including trailing slash)
174 appended to dir_stack. We have to have a separate stack of lengths
175 (rather than just popping back to previous slash) because the first
176 element pushed onto the dir stack may contain slashes. */
177 static struct obstack len_stack;
179 /* Set of `active' directories from the current command-line argument
180 to the level in the hierarchy at which files are being removed.
181 A directory is added to the active set when RM begins removing it
182 (or its entries), and it is removed from the set just after RM has
183 finished processing it.
185 This is actually a map (not a set), implemented with a hash table.
186 For each active directory, it maps the directory's inode number to the
187 depth of that directory relative to the root of the tree being deleted.
188 A directory specified on the command line has depth zero.
189 This construct is used to detect directory cycles so that RM can warn
190 about them rather than iterating endlessly. */
191 static struct HT *active_dir_map;
193 /* An entry in the active_dir_map. */
194 struct active_dir_ent
200 static struct option const long_opts[] =
202 {"directory", no_argument, &unlink_dirs, 1},
203 {"force", no_argument, NULL, 'f'},
204 {"interactive", no_argument, NULL, 'i'},
205 {"recursive", no_argument, &recursive, 1},
206 {"verbose", no_argument, &verbose, 1},
207 {"help", no_argument, &show_help, 1},
208 {"version", no_argument, &show_version, 1},
212 static inline unsigned int
215 return obstack_object_size (&len_stack) / sizeof (size_t);
219 print_nth_dir (FILE *stream, unsigned int depth)
221 size_t *length = (size_t *) obstack_base (&len_stack);
222 char *dir_name = (char *) obstack_base (&dir_stack);
223 unsigned int sum = 0;
226 assert (0 <= depth && depth < current_depth ());
228 for (i = 0; i <= depth; i++)
233 fwrite (dir_name, 1, sum, stream);
236 static inline struct active_dir_ent *
237 make_active_dir_ent (ino_t inum, unsigned int depth)
239 struct active_dir_ent *ent;
240 ent = (struct active_dir_ent *) xmalloc (sizeof *ent);
247 hash_active_dir_ent (void const *x, unsigned int table_size)
249 struct active_dir_ent const *ade = x;
250 return ade->inum % table_size;
254 hash_compare_active_dir_ents (void const *x, void const *y)
256 struct active_dir_ent const *a = x;
257 struct active_dir_ent const *b = y;
258 return (a->inum == b->inum ? 0 : 1);
261 /* A hash function for null-terminated char* strings using
262 the method described in Aho, Sethi, & Ullman, p 436. */
265 hash_pjw (const void *x, unsigned int tablesize)
274 if ((g = h & 0xf0000000U) != 0)
275 h = (h ^ (g >> 24)) ^ g;
278 return (h % tablesize);
282 hash_compare_strings (void const *x, void const *y)
284 return strcmp (x, y);
291 fprintf (stderr, _("Try `%s --help' for more information.\n"),
295 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
297 Remove (unlink) the FILE(s).\n\
299 -d, --directory unlink directory, even if non-empty (super-user only)\n\
300 -f, --force ignore nonexistent files, never prompt\n\
301 -i, --interactive prompt before any removal\n\
302 -r, -R, --recursive remove the contents of directories recursively\n\
303 -v, --verbose explain what is being done\n\
304 --help display this help and exit\n\
305 --version output version information and exit\n\
307 puts (_("\nReport bugs to <fileutils-bugs@gnu.org>."));
313 push_dir (const char *dir_name)
317 len = strlen (dir_name);
319 /* Append the string onto the stack. */
320 obstack_grow (&dir_stack, dir_name, len);
322 /* Append a trailing slash. */
323 obstack_1grow (&dir_stack, '/');
325 /* Add one for the slash. */
328 /* Push the length (including slash) onto its stack. */
329 obstack_grow (&len_stack, &len, sizeof (len));
335 int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
336 size_t *length = (size_t *) obstack_base (&len_stack);
339 assert (n_lengths > 0);
340 top_len = length[n_lengths - 1];
341 assert (top_len >= 2);
343 /* Pop off the specified length of pathname. */
344 assert (obstack_object_size (&dir_stack) >= top_len);
345 obstack_blank (&dir_stack, -top_len);
347 /* Pop the length stack, too. */
348 assert (obstack_object_size (&len_stack) >= sizeof (size_t));
349 obstack_blank (&len_stack, -(sizeof (size_t)));
352 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
353 buffer, DST, so that the last source byte is at the end of the destination
354 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
355 Set *RESULT to point to the beginning of (the portion of) the source data
356 in DST. Return the number of bytes remaining in the destination buffer. */
359 right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
360 char **result, int *truncated)
365 if (src_len <= dst_len)
368 dp = dst + (dst_len - src_len);
373 sp = src + (src_len - dst_len);
379 memcpy (dp, sp, src_len);
382 return dst_len - src_len;
385 /* Using the global directory name obstack, create the full path to FILENAME.
386 Return it in sometimes-realloc'd space that should not be freed by the
387 caller. Realloc as necessary. If realloc fails, use a static buffer
388 and put as long a suffix in that buffer as possible. */
391 full_filename (const char *filename)
393 static char *buf = NULL;
394 static size_t n_allocated = 0;
396 int dir_len = obstack_object_size (&dir_stack);
397 char *dir_name = (char *) obstack_base (&dir_stack);
398 size_t n_bytes_needed;
401 filename_len = strlen (filename);
402 n_bytes_needed = dir_len + filename_len + 1;
404 if (n_bytes_needed > n_allocated)
406 /* This code requires that realloc accept NULL as the first arg.
407 This function must not use xrealloc. Otherwise, an out-of-memory
408 error involving a file name to be expanded here wouldn't ever
409 be issued. Use realloc and fall back on using a static buffer
410 if memory allocation fails. */
411 buf = realloc (buf, n_bytes_needed);
412 n_allocated = n_bytes_needed;
416 #define SBUF_SIZE 512
417 #define ELLIPSES_PREFIX "[...]"
418 static char static_buf[SBUF_SIZE];
423 len = right_justify (static_buf, SBUF_SIZE, filename,
424 filename_len + 1, &p, &truncated);
425 right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
428 memcpy (static_buf, ELLIPSES_PREFIX,
429 sizeof (ELLIPSES_PREFIX) - 1);
435 /* Copy directory part, including trailing slash. */
436 memcpy (buf, dir_name, dir_len);
438 /* Append filename part, including trailing zero byte. */
439 memcpy (buf + dir_len, filename, filename_len + 1);
441 assert (strlen (buf) + 1 == n_bytes_needed);
447 fspec_init_file (struct File_spec *fs, const char *filename)
449 fs->filename = (char *) filename;
450 fs->have_full_mode = 0;
451 fs->have_filetype_mode = 0;
455 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
457 fs->filename = dp->d_name;
458 fs->have_full_mode = 0;
459 fs->have_filetype_mode = 0;
460 fs->inum = D_INO (dp);
462 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
463 if (dp->d_type != DT_UNKNOWN)
465 fs->have_filetype_mode = 1;
466 fs->mode = DTTOIF (dp->d_type);
472 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
474 struct stat stat_buf;
476 if (fs->have_full_mode)
478 *full_mode = fs->mode;
482 if (lstat (fs->filename, &stat_buf))
485 fs->have_full_mode = 1;
486 fs->have_filetype_mode = 1;
487 fs->mode = stat_buf.st_mode;
488 fs->inum = stat_buf.st_ino;
490 *full_mode = stat_buf.st_mode;
495 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
499 if (fs->have_filetype_mode)
501 *filetype_mode = fs->mode;
506 fail = fspec_get_full_mode (fs, filetype_mode);
513 fspec_filetype_mode (const struct File_spec *fs)
515 assert (fs->have_filetype_mode);
519 /* Recursively remove all of the entries in the current directory.
520 Return an indication of the success of the operation. */
523 remove_cwd_entries (void)
525 /* NOTE: this is static. */
526 static DIR *dirp = NULL;
528 /* NULL or a malloc'd and initialized hash table of entries in the
529 current directory that have been processed but not removed --
530 due either to an error or to an interactive `no' response. */
531 struct HT *ht = NULL;
533 enum RM_status status = RM_OK;
539 /* FIXME-someday: but this is actually the previously opened dir. */
540 error (0, errno, "%s", full_filename ("."));
548 /* FIXME: why do this? */
551 dirp = opendir (".");
554 if (errno != ENOENT || !ignore_missing_files)
556 error (0, errno, "%s", full_filename ("."));
566 enum RM_status tmp_status;
569 /* FILE should be skipped if it is `.' or `..', or if it is in
570 the table, HT, of entries we've already processed. */
571 #define SKIPPABLE(Ht, File) (DOT_OR_DOTDOT(File) \
572 || (Ht && hash_query_in_table (Ht, File)))
577 /* Since we have probably modified the directory since it
578 was opened, readdir returning NULL does not necessarily
579 mean we have read the last entry. Rewind it and check
580 again. This happens on SunOS4.1.4 with 254 or more files
583 while ((dp = readdir (dirp)) && SKIPPABLE (ht, dp->d_name))
592 if (SKIPPABLE (ht, dp->d_name))
595 fspec_init_dp (&fs, dp);
597 /* Save a copy of the name of this entry, in case we have
598 to add it to the set of unremoved entries below. */
599 ASSIGN_STRDUPA (entry_name, dp->d_name);
601 /* CAUTION: after this call to rm, DP may not be valid --
602 it may have been freed due to a close in a recursive call
603 (through rm and remove_dir) to this function. */
604 tmp_status = rm (&fs, 0);
607 if (tmp_status > status)
609 assert (VALID_STATUS (status));
611 /* If this entry was not removed (due either to an error or to
612 an interactive `no' response), record it in the hash table so
613 we don't consider it again if we reopen this directory later. */
620 ht = hash_initialize (HT_INITIAL_CAPACITY, NULL,
621 hash_pjw, hash_compare_strings);
623 error (1, 0, _("Memory exhausted"));
625 HASH_INSERT_NEW_ITEM (ht, entry_name, &fail);
627 error (1, 0, _("Memory exhausted"));
634 while (dirp == NULL);
638 error (0, errno, "%s", full_filename ("."));
651 /* Query the user if appropriate, and if ok try to remove the
652 file or directory specified by FS. Return RM_OK if it is removed,
653 and RM_ERROR or RM_USER_DECLINED if not. */
655 static enum RM_status
656 remove_file (struct File_spec *fs)
659 char *pathname = fs->filename;
661 if (!ignore_missing_files && (interactive || stdin_tty)
662 && euidaccess (pathname, W_OK) )
664 if (!S_ISLNK (fspec_filetype_mode (fs)))
667 (S_ISDIR (fspec_filetype_mode (fs))
668 ? _("%s: remove write-protected directory `%s'? ")
669 : _("%s: remove write-protected file `%s'? ")),
670 program_name, full_filename (pathname));
672 return RM_USER_DECLINED;
678 if (!asked && interactive)
681 (S_ISDIR (fspec_filetype_mode (fs))
682 ? _("%s: remove directory `%s'? ")
683 : _("%s: remove `%s'? ")),
684 program_name, full_filename (pathname));
686 return RM_USER_DECLINED;
690 printf ("%s\n", full_filename (pathname));
692 if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
694 error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
700 /* If not in recursive mode, print an error message and return RM_ERROR.
701 Otherwise, query the user if appropriate, then try to recursively
702 remove the directory specified by FS. Return RM_OK if it is removed,
703 and RM_ERROR or RM_USER_DECLINED if not.
704 FIXME: describe need_save_cwd parameter. */
706 static enum RM_status
707 remove_dir (struct File_spec *fs, int need_save_cwd)
709 enum RM_status status;
710 struct saved_cwd cwd;
711 char *dir_name = fs->filename;
712 const char *fmt = NULL;
716 error (0, 0, _("%s: is a directory"), full_filename (dir_name));
720 if (!ignore_missing_files && (interactive || stdin_tty)
721 && euidaccess (dir_name, W_OK))
723 fmt = _("%s: directory `%s' is write protected; descend into it anyway? ");
725 else if (interactive)
727 fmt = _("%s: descend into directory `%s'? ");
732 fprintf (stderr, fmt, program_name, full_filename (dir_name));
734 return RM_USER_DECLINED;
738 printf ("%s\n", full_filename (dir_name));
740 /* Save cwd if needed. */
741 if (need_save_cwd && save_cwd (&cwd))
744 /* Make target directory the current one. */
745 if (chdir (dir_name) < 0)
747 error (0, errno, _("cannot change to directory %s"),
748 full_filename (dir_name));
756 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
757 dir_name because dir_name is just a pointer to the dir entry's d_name
758 field, and remove_cwd_entries may close the directory. */
759 ASSIGN_STRDUPA (dir_name, dir_name);
761 status = remove_cwd_entries ();
768 if (restore_cwd (&cwd, NULL, NULL))
775 else if (chdir ("..") < 0)
777 error (0, errno, _("cannot change back to directory %s via `..'"),
778 full_filename (dir_name));
784 error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
785 (status != RM_OK ? _(" (might be nonempty)") : ""));
788 return RM_USER_DECLINED;
792 if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
794 error (0, errno, _("cannot remove directory `%s'"),
795 full_filename (dir_name));
802 /* Remove the file or directory specified by FS after checking appropriate
803 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
804 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
805 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
806 name (and hence must specify a file in the current directory). */
808 static enum RM_status
809 rm (struct File_spec *fs, int user_specified_name)
811 mode_t filetype_mode;
813 if (user_specified_name)
815 char *base = base_name (fs->filename);
817 if (DOT_OR_DOTDOT (base))
819 error (0, 0, _("cannot remove `.' or `..'"));
824 if (fspec_get_filetype_mode (fs, &filetype_mode))
826 if (ignore_missing_files && errno == ENOENT)
829 error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
833 if (S_ISDIR (filetype_mode))
836 struct active_dir_ent *old_ent;
838 /* Insert this directory in the active_dir_map.
839 If there is already a directory in the map with the same inum,
840 then there's *probably* a directory cycle. This test can get
841 a false positive if two directories have the same inode number
842 but different device numbers and one directory contains the
843 other. But since people don't often try to delete hierarchies
844 containing mount points, and when they do, duplicate inode
845 numbers are not that likely, this isn't worth detecting. */
846 old_ent = hash_insert_if_absent (active_dir_map,
847 make_active_dir_ent (fs->inum,
851 error (1, 0, _("Memory exhausted"));
856 WARNING: Circular directory structure.\n\
857 This almost certainly means that you have a corrupted file system.\n\
858 NOTIFY YOUR SYSTEM MANAGER.\n\
859 The following two directories have the same inode number:\n"));
860 /* FIXME: test this!! */
861 print_nth_dir (stderr, current_depth ());
862 fputc ('\n', stderr);
863 print_nth_dir (stderr, old_ent->depth);
864 fputc ('\n', stderr);
871 error (0, 0, _("continue? "));
879 if (!S_ISDIR (filetype_mode) || unlink_dirs)
881 return remove_file (fs);
885 int need_save_cwd = user_specified_name;
886 enum RM_status status;
887 struct active_dir_ent tmp;
888 struct active_dir_ent *old_ent;
891 need_save_cwd = (strchr (fs->filename, '/') != NULL);
893 status = remove_dir (fs, need_save_cwd);
895 /* Remove this directory from the active_dir_map. */
897 old_ent = hash_delete_if_present (active_dir_map, &tmp);
898 assert (old_ent != NULL);
906 main (int argc, char **argv)
911 program_name = argv[0];
912 setlocale (LC_ALL, "");
913 bindtextdomain (PACKAGE, LOCALEDIR);
914 textdomain (PACKAGE);
916 verbose = ignore_missing_files = recursive = interactive
919 while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
923 case 0: /* Long option. */
930 ignore_missing_files = 1;
934 ignore_missing_files = 0;
950 printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
959 if (ignore_missing_files)
963 error (0, 0, _("too few arguments"));
968 stdin_tty = isatty (STDIN_FILENO);
970 /* Initialize dir-stack obstacks. */
971 obstack_init (&dir_stack);
972 obstack_init (&len_stack);
974 active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, free,
976 hash_compare_active_dir_ents);
978 for (; optind < argc; optind++)
981 enum RM_status status;
983 /* Stripping slashes is harmless for rmdir;
984 if the arg is not a directory, it will fail with ENOTDIR. */
985 strip_trailing_slashes (argv[optind]);
986 fspec_init_file (&fs, argv[optind]);
987 status = rm (&fs, 1);
988 assert (VALID_STATUS (status));
989 if (status == RM_ERROR)
993 hash_free (active_dir_map);