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;
136 void strip_trailing_slashes ();
140 /* Forward dcl for recursively called function. */
141 static enum RM_status rm PARAMS ((struct File_spec *fs,
142 int user_specified_name));
144 /* Name this program was run with. */
147 /* If nonzero, display the name of each file removed. */
150 /* If nonzero, ignore nonexistant files. */
151 static int ignore_missing_files;
153 /* If nonzero, recursively remove directories. */
154 static int recursive;
156 /* If nonzero, query the user about whether to remove each file. */
157 static int interactive;
159 /* If nonzero, remove directories with unlink instead of rmdir, and don't
160 require a directory to be empty before trying to unlink it.
161 Only works for the super-user. */
162 static int unlink_dirs;
164 /* If nonzero, stdin is a tty. */
165 static int stdin_tty;
167 /* If nonzero, display usage information and exit. */
168 static int show_help;
170 /* If nonzero, print the version on standard output and exit. */
171 static int show_version;
173 /* The name of the directory (starting with and relative to a command
174 line argument) being processed. When a subdirectory is entered, a new
175 component is appended (pushed). When RM chdir's out of a directory,
176 the top component is removed (popped). This is used to form a full
177 file name when necessary. */
178 static struct obstack dir_stack;
180 /* Stack of lengths of directory names (including trailing slash)
181 appended to dir_stack. We have to have a separate stack of lengths
182 (rather than just popping back to previous slash) because the first
183 element pushed onto the dir stack may contain slashes. */
184 static struct obstack len_stack;
186 /* Set of `active' directories from the current command-line argument
187 to the level in the hierarchy at which files are being removed.
188 A directory is added to the active set when RM begins removing it
189 (or its entries), and it is removed from the set just after RM has
190 finished processing it.
192 This is actually a map (not a set), implemented with a hash table.
193 For each active directory, it maps the directory's inode number to the
194 depth of that directory relative to the root of the tree being deleted.
195 A directory specified on the command line has depth zero.
196 This construct is used to detect directory cycles so that RM can warn
197 about them rather than iterating endlessly. */
198 static struct HT *active_dir_map;
200 /* An entry in the active_dir_map. */
201 struct active_dir_ent
207 static struct option const long_opts[] =
209 {"directory", no_argument, &unlink_dirs, 1},
210 {"force", no_argument, NULL, 'f'},
211 {"interactive", no_argument, NULL, 'i'},
212 {"recursive", no_argument, &recursive, 1},
213 {"verbose", no_argument, &verbose, 1},
214 {"help", no_argument, &show_help, 1},
215 {"version", no_argument, &show_version, 1},
219 static inline unsigned int
222 return obstack_object_size (&len_stack) / sizeof (size_t);
226 print_nth_dir (FILE *stream, unsigned int depth)
228 size_t *length = (size_t *) obstack_base (&len_stack);
229 char *dir_name = (char *) obstack_base (&dir_stack);
230 unsigned int sum = 0;
233 assert (0 <= depth && depth < current_depth ());
235 for (i = 0; i <= depth; i++)
240 fwrite (dir_name, 1, sum, stream);
243 static inline struct active_dir_ent *
244 make_active_dir_ent (ino_t inum, unsigned int depth)
246 struct active_dir_ent *ent;
247 ent = (struct active_dir_ent *) xmalloc (sizeof *ent);
254 hash_active_dir_ent (void const *x, unsigned int table_size)
256 struct active_dir_ent const *ade = x;
257 return ade->inum % table_size;
261 hash_compare_active_dir_ents (void const *x, void const *y)
263 struct active_dir_ent const *a = x;
264 struct active_dir_ent const *b = y;
265 return (a->inum == b->inum ? 0 : 1);
268 /* A hash function for null-terminated char* strings using
269 the method described in Aho, Sethi, & Ullman, p 436. */
272 hash_pjw (const void *x, unsigned int tablesize)
281 if ((g = h & 0xf0000000U) != 0)
282 h = (h ^ (g >> 24)) ^ g;
285 return (h % tablesize);
289 hash_compare_strings (void const *x, void const *y)
291 return strcmp (x, y);
298 fprintf (stderr, _("Try `%s --help' for more information.\n"),
302 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
304 Remove (unlink) the FILE(s).\n\
306 -d, --directory unlink directory, even if non-empty (super-user only)\n\
307 -f, --force ignore nonexistent files, never prompt\n\
308 -i, --interactive prompt before any removal\n\
309 -r, -R, --recursive remove the contents of directories recursively\n\
310 -v, --verbose explain what is being done\n\
311 --help display this help and exit\n\
312 --version output version information and exit\n\
314 puts (_("\nReport bugs to <fileutils-bugs@gnu.ai.mit.edu>."));
320 push_dir (const char *dir_name)
324 len = strlen (dir_name);
326 /* Append the string onto the stack. */
327 obstack_grow (&dir_stack, dir_name, len);
329 /* Append a trailing slash. */
330 obstack_1grow (&dir_stack, '/');
332 /* Add one for the slash. */
335 /* Push the length (including slash) onto its stack. */
336 obstack_grow (&len_stack, &len, sizeof (len));
342 int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
343 size_t *length = (size_t *) obstack_base (&len_stack);
346 assert (n_lengths > 0);
347 top_len = length[n_lengths - 1];
348 assert (top_len >= 2);
350 /* Pop off the specified length of pathname. */
351 assert (obstack_object_size (&dir_stack) >= top_len);
352 obstack_blank (&dir_stack, -top_len);
354 /* Pop the length stack, too. */
355 assert (obstack_object_size (&len_stack) >= sizeof (size_t));
356 obstack_blank (&len_stack, -(sizeof (size_t)));
359 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
360 buffer, DST, so that the last source byte is at the end of the destination
361 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
362 Set *RESULT to point to the beginning of (the portion of) the source data
363 in DST. Return the number of bytes remaining in the destination buffer. */
366 right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
367 char **result, int *truncated)
372 if (src_len <= dst_len)
375 dp = dst + (dst_len - src_len);
380 sp = src + (src_len - dst_len);
386 memcpy (dp, sp, src_len);
389 return dst_len - src_len;
392 /* Using the global directory name obstack, create the full path to FILENAME.
393 Return it in sometimes-realloc'd space that should not be freed by the
394 caller. Realloc as necessary. If realloc fails, use a static buffer
395 and put as long a suffix in that buffer as possible. */
398 full_filename (const char *filename)
400 static char *buf = NULL;
401 static size_t n_allocated = 0;
403 int dir_len = obstack_object_size (&dir_stack);
404 char *dir_name = (char *) obstack_base (&dir_stack);
405 size_t n_bytes_needed;
408 filename_len = strlen (filename);
409 n_bytes_needed = dir_len + filename_len + 1;
411 if (n_bytes_needed > n_allocated)
413 /* This code requires that realloc accept NULL as the first arg.
414 This function must not use xrealloc. Otherwise, an out-of-memory
415 error involving a file name to be expanded here wouldn't ever
416 be issued. Use realloc and fall back on using a static buffer
417 if memory allocation fails. */
418 buf = realloc (buf, n_bytes_needed);
419 n_allocated = n_bytes_needed;
423 #define SBUF_SIZE 512
424 #define ELLIPSES_PREFIX "[...]"
425 static char static_buf[SBUF_SIZE];
430 len = right_justify (static_buf, SBUF_SIZE, filename,
431 filename_len + 1, &p, &truncated);
432 right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
435 memcpy (static_buf, ELLIPSES_PREFIX,
436 sizeof (ELLIPSES_PREFIX) - 1);
442 /* Copy directory part, including trailing slash. */
443 memcpy (buf, dir_name, dir_len);
445 /* Append filename part, including trailing zero byte. */
446 memcpy (buf + dir_len, filename, filename_len + 1);
448 assert (strlen (buf) + 1 == n_bytes_needed);
454 fspec_init_file (struct File_spec *fs, const char *filename)
456 fs->filename = (char *) filename;
457 fs->have_full_mode = 0;
458 fs->have_filetype_mode = 0;
462 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
464 fs->filename = dp->d_name;
465 fs->have_full_mode = 0;
466 fs->have_filetype_mode = 0;
467 fs->inum = D_INO (dp);
469 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
470 if (dp->d_type != DT_UNKNOWN)
472 fs->have_filetype_mode = 1;
473 fs->mode = DTTOIF (dp->d_type);
479 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
481 struct stat stat_buf;
483 if (fs->have_full_mode)
485 *full_mode = fs->mode;
489 if (lstat (fs->filename, &stat_buf))
492 fs->have_full_mode = 1;
493 fs->have_filetype_mode = 1;
494 fs->mode = stat_buf.st_mode;
495 fs->inum = stat_buf.st_ino;
497 *full_mode = stat_buf.st_mode;
502 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
506 if (fs->have_filetype_mode)
508 *filetype_mode = fs->mode;
513 fail = fspec_get_full_mode (fs, filetype_mode);
520 fspec_filetype_mode (const struct File_spec *fs)
522 assert (fs->have_filetype_mode);
526 /* Recursively remove all of the entries in the current directory.
527 Return an indication of the success of the operation. */
530 remove_cwd_entries (void)
532 /* NOTE: this is static. */
533 static DIR *dirp = NULL;
535 /* NULL or a malloc'd and initialized hash table of entries in the
536 current directory that have been processed but not removed --
537 due either to an error or to an interactive `no' response. */
538 struct HT *ht = NULL;
541 enum RM_status status = RM_OK;
547 /* FIXME-someday: but this is actually the previously opened dir. */
548 error (0, errno, "%s", full_filename ("."));
557 dirp = opendir (".");
560 if (errno != ENOENT || !ignore_missing_files)
562 error (0, errno, "%s", full_filename ("."));
568 while ((dp = readdir (dirp)) != NULL)
572 enum RM_status tmp_status;
574 /* Skip this entry if it's `.' or `..'. */
575 if (DOT_OR_DOTDOT (dp->d_name))
578 /* Skip this entry if it's in the table of ones we've already
580 if (ht && hash_query_in_table (ht, (dp)->d_name))
583 fspec_init_dp (&fs, dp);
585 /* Save a copy of the name of this entry, in case we have
586 to add it to the set of unremoved entries below. */
587 ASSIGN_STRDUPA (entry_name, dp->d_name);
589 /* CAUTION: after this call to rm, DP may not be valid --
590 it may have been freed due to a close in a recursive call
591 (through rm and remove_dir) to this function. */
592 tmp_status = rm (&fs, 0);
595 if (tmp_status > status)
597 assert (VALID_STATUS (status));
599 /* If this entry was not removed (due either to an error or to
600 an interactive `no' response), record it in the hash table so
601 we don't consider it again if we reopen this directory later. */
608 ht = hash_initialize (HT_INITIAL_CAPACITY, free,
609 hash_pjw, hash_compare_strings);
611 error (1, 0, _("Memory exhausted"));
613 HASH_INSERT_NEW_ITEM (ht, entry_name, &fail);
615 error (1, 0, _("Memory exhausted"));
622 while (dirp == NULL);
626 error (0, errno, "%s", full_filename ("."));
639 /* Query the user if appropriate, and if ok try to remove the
640 file or directory specified by FS. Return RM_OK if it is removed,
641 and RM_ERROR or RM_USER_DECLINED if not. */
643 static enum RM_status
644 remove_file (struct File_spec *fs)
647 char *pathname = fs->filename;
649 if (!ignore_missing_files && (interactive || stdin_tty)
650 && euidaccess (pathname, W_OK) )
652 if (!S_ISLNK (fspec_filetype_mode (fs)))
655 (S_ISDIR (fspec_filetype_mode (fs))
656 ? _("%s: remove write-protected directory `%s'? ")
657 : _("%s: remove write-protected file `%s'? ")),
658 program_name, full_filename (pathname));
660 return RM_USER_DECLINED;
666 if (!asked && interactive)
669 (S_ISDIR (fspec_filetype_mode (fs))
670 ? _("%s: remove directory `%s'? ")
671 : _("%s: remove `%s'? ")),
672 program_name, full_filename (pathname));
674 return RM_USER_DECLINED;
678 printf ("%s\n", full_filename (pathname));
680 if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
682 error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
688 /* If not in recursive mode, print an error message and return RM_ERROR.
689 Otherwise, query the user if appropriate, then try to recursively
690 remove the directory specified by FS. Return RM_OK if it is removed,
691 and RM_ERROR or RM_USER_DECLINED if not.
692 FIXME: describe need_save_cwd parameter. */
694 static enum RM_status
695 remove_dir (struct File_spec *fs, int need_save_cwd)
697 enum RM_status status;
698 struct saved_cwd cwd;
699 char *dir_name = fs->filename;
700 const char *fmt = NULL;
704 error (0, 0, _("%s: is a directory"), full_filename (dir_name));
708 if (!ignore_missing_files && (interactive || stdin_tty)
709 && euidaccess (dir_name, W_OK))
711 fmt = _("%s: directory `%s' is write protected; descend into it anyway? ");
713 else if (interactive)
715 fmt = _("%s: descend into directory `%s'? ");
720 fprintf (stderr, fmt, program_name, full_filename (dir_name));
722 return RM_USER_DECLINED;
726 printf ("%s\n", full_filename (dir_name));
728 /* Save cwd if needed. */
729 if (need_save_cwd && save_cwd (&cwd))
732 /* Make target directory the current one. */
733 if (chdir (dir_name) < 0)
735 error (0, errno, _("cannot change to directory %s"),
736 full_filename (dir_name));
744 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
745 dir_name because dir_name is just a pointer to the dir entry's d_name
746 field, and remove_cwd_entries may close the directory. */
747 ASSIGN_STRDUPA (dir_name, dir_name);
749 status = remove_cwd_entries ();
756 if (restore_cwd (&cwd, NULL, NULL))
763 else if (chdir ("..") < 0)
765 error (0, errno, _("cannot change back to directory %s via `..'"),
766 full_filename (dir_name));
772 error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
773 (status != RM_OK ? _(" (might be nonempty)") : ""));
776 return RM_USER_DECLINED;
780 if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
782 error (0, errno, _("cannot remove directory `%s'"),
783 full_filename (dir_name));
790 /* Remove the file or directory specified by FS after checking appropriate
791 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
792 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
793 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
794 name (and hence must specify a file in the current directory). */
796 static enum RM_status
797 rm (struct File_spec *fs, int user_specified_name)
799 mode_t filetype_mode;
801 if (user_specified_name)
803 char *base = base_name (fs->filename);
805 if (DOT_OR_DOTDOT (base))
807 error (0, 0, _("cannot remove `.' or `..'"));
812 if (fspec_get_filetype_mode (fs, &filetype_mode))
814 if (ignore_missing_files && errno == ENOENT)
817 error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
821 if (S_ISDIR (filetype_mode))
824 struct active_dir_ent *old_ent;
826 /* Insert this directory in the active_dir_map.
827 If there is already a directory in the map with the same inum,
828 then there's *probably* a directory cycle. This test can get
829 a false positive if two directories have the same inode number
830 but different device numbers and one directory contains the
831 other. But since people don't often try to delete hierarchies
832 containing mount points, and when they do, duplicate inode
833 numbers are not that likely, this isn't worth detecting. */
834 old_ent = hash_insert_if_absent (active_dir_map,
835 make_active_dir_ent (fs->inum,
839 error (1, 0, _("Memory exhausted"));
844 WARNING: Circular directory structure.\n\
845 This almost certainly means that you have a corrupted file system.\n\
846 NOTIFY YOUR SYSTEM MANAGER.\n\
847 The following two directories have the same inode number:\n"));
848 /* FIXME: test this!! */
849 print_nth_dir (stderr, current_depth ());
850 fputc ('\n', stderr);
851 print_nth_dir (stderr, old_ent->depth);
852 fputc ('\n', stderr);
859 error (0, 0, _("continue? "));
867 if (!S_ISDIR (filetype_mode) || unlink_dirs)
869 return remove_file (fs);
873 int need_save_cwd = user_specified_name;
874 enum RM_status status;
875 struct active_dir_ent tmp;
876 struct active_dir_ent *old_ent;
879 need_save_cwd = (strchr (fs->filename, '/') != NULL);
881 status = remove_dir (fs, need_save_cwd);
883 /* Remove this directory from the active_dir_map. */
885 old_ent = hash_delete_if_present (active_dir_map, &tmp);
886 assert (old_ent != NULL);
894 main (int argc, char **argv)
899 program_name = argv[0];
900 setlocale (LC_ALL, "");
901 bindtextdomain (PACKAGE, LOCALEDIR);
902 textdomain (PACKAGE);
904 verbose = ignore_missing_files = recursive = interactive
907 while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
911 case 0: /* Long option. */
918 ignore_missing_files = 1;
922 ignore_missing_files = 0;
938 printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
947 if (ignore_missing_files)
951 error (0, 0, _("too few arguments"));
956 stdin_tty = isatty (STDIN_FILENO);
958 /* Initialize dir-stack obstacks. */
959 obstack_init (&dir_stack);
960 obstack_init (&len_stack);
962 active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, free,
964 hash_compare_active_dir_ents);
966 for (; optind < argc; optind++)
969 enum RM_status status;
971 /* Stripping slashes is harmless for rmdir;
972 if the arg is not a directory, it will fail with ENOTDIR. */
973 strip_trailing_slashes (argv[optind]);
974 fspec_init_file (&fs, argv[optind]);
975 status = rm (&fs, 1);
976 assert (VALID_STATUS (status));
977 if (status == RM_ERROR)
981 hash_free (active_dir_map);