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 /* FIXME: describe and explain ordering: `ok' increasing in 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 /* FIXME: describe */
174 static struct obstack dir_stack;
176 /* Stack of lengths of directory names (including trailing slash)
177 appended to dir_stack. We have to have a separate stack of lengths
178 (rather than just popping back to previous slash) because the first
179 element pushed onto the dir stack may contain slashes. */
180 static struct obstack len_stack;
182 /* Set of `active' directories from the current command-line parameter
183 to the level in the hierarchy at which files are being removed.
184 A directory is added to the active set when RM begins removing it
185 (or its entries), and it is removed from the set just after RM has
186 finished processing it.
188 This is actually a map (not a set), implemented with a hash table.
189 For each active directory, it maps the directory's inode number to the
190 depth of that directory relative to the root of the tree being deleted.
191 A directory specified on the command line has depth zero.
192 This construct is used to detect directory cycles so that RM can warn
193 about them rather than iterating endlessly. */
194 static struct HT *active_dir_map;
196 /* An entry in the active_dir_map. */
197 struct active_dir_ent
203 static struct option const long_opts[] =
205 {"directory", no_argument, &unlink_dirs, 1},
206 {"force", no_argument, NULL, 'f'},
207 {"interactive", no_argument, NULL, 'i'},
208 {"recursive", no_argument, &recursive, 1},
209 {"verbose", no_argument, &verbose, 1},
210 {"help", no_argument, &show_help, 1},
211 {"version", no_argument, &show_version, 1},
215 static inline unsigned int
218 return obstack_object_size (&len_stack) / sizeof (size_t);
222 print_nth_dir (FILE *stream, unsigned int depth)
224 size_t *length = (size_t *) obstack_base (&len_stack);
225 char *dir_name = (char *) obstack_base (&dir_stack);
226 unsigned int sum = 0;
229 assert (0 <= depth && depth < current_depth ());
231 for (i = 0; i <= depth; i++)
236 fwrite (dir_name, 1, sum, stream);
239 static inline struct active_dir_ent *
240 make_active_dir_ent (ino_t inum, unsigned int depth)
242 struct active_dir_ent *ent;
243 ent = (struct active_dir_ent *) xmalloc (sizeof *ent);
250 hash_active_dir_ent (void const *x, unsigned int table_size)
252 struct active_dir_ent const *ade = x;
253 return ade->inum % table_size;
257 hash_compare_active_dir_ents (void const *x, void const *y)
259 struct active_dir_ent const *a = x;
260 struct active_dir_ent const *b = y;
261 return (a->inum == b->inum ? 0 : 1);
264 /* A hash function for null-terminated char* strings using
265 the method described in Aho, Sethi, & Ullman, p 436. */
268 hash_pjw (const void *x, unsigned int tablesize)
277 if ((g = h & 0xf0000000U) != 0)
278 h = (h ^ (g >> 24)) ^ g;
281 return (h % tablesize);
285 hash_compare_strings (void const *x, void const *y)
287 return strcmp (x, y);
294 fprintf (stderr, _("Try `%s --help' for more information.\n"),
298 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
300 Remove (unlink) the FILE(s).\n\
302 -d, --directory unlink directory, even if non-empty (super-user only)\n\
303 -f, --force ignore nonexistent files, never prompt\n\
304 -i, --interactive prompt before any removal\n\
305 -r, -R, --recursive remove the contents of directories recursively\n\
306 -v, --verbose explain what is being done\n\
307 --help display this help and exit\n\
308 --version output version information and exit\n\
310 puts (_("\nReport bugs to <fileutils-bugs@gnu.ai.mit.edu>."));
316 push_dir (const char *dir_name)
320 len = strlen (dir_name);
322 /* Append the string onto the stack. */
323 obstack_grow (&dir_stack, dir_name, len);
325 /* Append a trailing slash. */
326 obstack_1grow (&dir_stack, '/');
328 /* Add one for the slash. */
331 /* Push the length (including slash) onto its stack. */
332 obstack_grow (&len_stack, &len, sizeof (len));
338 int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
339 size_t *length = (size_t *) obstack_base (&len_stack);
342 assert (n_lengths > 0);
343 top_len = length[n_lengths - 1];
344 assert (top_len >= 2);
346 /* Pop off the specified length of pathname. */
347 assert (obstack_object_size (&dir_stack) >= top_len);
348 obstack_blank (&dir_stack, -top_len);
350 /* Pop the length stack, too. */
351 assert (obstack_object_size (&len_stack) >= sizeof (size_t));
352 obstack_blank (&len_stack, -(sizeof (size_t)));
355 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
356 buffer, DST, so that the last source byte is at the end of the destination
357 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
358 Set *RESULT to point to the beginning of (the portion of) the source data
359 in DST. Return the number of bytes remaining in the destination buffer. */
362 right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
363 char **result, int *truncated)
368 if (src_len <= dst_len)
371 dp = dst + (dst_len - src_len);
376 sp = src + (src_len - dst_len);
382 memcpy (dp, sp, src_len);
385 return dst_len - src_len;
388 /* Using the global directory name obstack, create the full path to FILENAME.
389 Return it in sometimes-realloc'd space that should not be freed by the
390 caller. Realloc as necessary. If realloc fails, use a static buffer
391 and put as long a suffix in that buffer as possible. */
394 full_filename (const char *filename)
396 static char *buf = NULL;
397 static size_t n_allocated = 0;
399 int dir_len = obstack_object_size (&dir_stack);
400 char *dir_name = (char *) obstack_base (&dir_stack);
401 size_t n_bytes_needed;
404 filename_len = strlen (filename);
405 n_bytes_needed = dir_len + filename_len + 1;
407 if (n_bytes_needed > n_allocated)
409 /* This code requires that realloc accept NULL as the first arg.
410 This function must not use xrealloc. Otherwise, an out-of-memory
411 error involving a file name to be expanded here wouldn't ever
412 be issued. Use realloc and fall back on using a static buffer
413 if memory allocation fails. */
414 buf = realloc (buf, n_bytes_needed);
415 n_allocated = n_bytes_needed;
419 #define SBUF_SIZE 512
420 #define ELLIPSES_PREFIX "[...]"
421 static char static_buf[SBUF_SIZE];
426 len = right_justify (static_buf, SBUF_SIZE, filename,
427 filename_len + 1, &p, &truncated);
428 right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
431 memcpy (static_buf, ELLIPSES_PREFIX,
432 sizeof (ELLIPSES_PREFIX) - 1);
438 /* Copy directory part, including trailing slash. */
439 memcpy (buf, dir_name, dir_len);
441 /* Append filename part, including trailing zero byte. */
442 memcpy (buf + dir_len, filename, filename_len + 1);
444 assert (strlen (buf) + 1 == n_bytes_needed);
450 fspec_init_file (struct File_spec *fs, const char *filename)
452 fs->filename = (char *) filename;
453 fs->have_full_mode = 0;
454 fs->have_filetype_mode = 0;
458 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
460 fs->filename = dp->d_name;
461 fs->have_full_mode = 0;
462 fs->have_filetype_mode = 0;
463 fs->inum = D_INO (dp);
465 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN)
466 if (filetype_mode != DT_UNKNOWN)
468 fs->have_filetype_mode = 1;
469 fs->mode = filetype_mode;
475 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
477 struct stat stat_buf;
479 if (fs->have_full_mode)
481 *full_mode = fs->mode;
485 if (lstat (fs->filename, &stat_buf))
488 fs->have_full_mode = 1;
489 fs->have_filetype_mode = 1;
490 fs->mode = stat_buf.st_mode;
491 fs->inum = stat_buf.st_ino;
493 *full_mode = stat_buf.st_mode;
498 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
502 if (fs->have_filetype_mode)
504 *filetype_mode = fs->mode;
509 fail = fspec_get_full_mode (fs, filetype_mode);
516 fspec_filetype_mode (const struct File_spec *fs)
518 assert (fs->have_filetype_mode);
522 /* Recursively remove all of the entries in the current directory.
523 Return an indication of the success of the operation. */
526 remove_cwd_entries (void)
528 /* NOTE: this is static. */
529 static DIR *dirp = NULL;
531 /* NULL or a malloc'd and initialized hash table of entries in the
532 current directory that have been processed but not removed --
533 due either to an error or to an interactive `no' response. */
534 struct HT *ht = NULL;
537 enum RM_status status = RM_OK;
543 /* FIXME-someday: but this is actually the previously opened dir. */
544 error (0, errno, "%s", full_filename ("."));
553 dirp = opendir (".");
556 if (errno != ENOENT || !ignore_missing_files)
558 error (0, errno, "%s", full_filename ("."));
564 while ((dp = readdir (dirp)) != NULL)
568 enum RM_status tmp_status;
570 /* Skip this entry if it's `.' or `..'. */
571 if (DOT_OR_DOTDOT (dp->d_name))
574 /* Skip this entry if it's in the table of ones we've already
576 if (ht && hash_query_in_table (ht, (dp)->d_name))
579 fspec_init_dp (&fs, dp);
581 /* Save a copy of the name of this entry, in case we have
582 to add it to the set of unremoved entries below. */
583 ASSIGN_STRDUPA (entry_name, dp->d_name);
585 /* CAUTION: after this call to rm, DP may not be valid --
586 it may have been freed due to a close in a recursive call
587 (through rm and remove_dir) to this function. */
588 tmp_status = rm (&fs, 0);
591 if (tmp_status > status)
593 assert (VALID_STATUS (status));
595 /* If this entry was not removed (due either to an error or to
596 an interactive `no' response), record it in the hash table so
597 we don't consider it again if we reopen this directory later. */
604 ht = hash_initialize (HT_INITIAL_CAPACITY, free,
605 hash_pjw, hash_compare_strings);
607 error (1, 0, _("Memory exhausted"));
609 HASH_INSERT_NEW_ITEM (ht, entry_name, &fail);
611 error (1, 0, _("Memory exhausted"));
618 while (dirp == NULL);
622 error (0, errno, "%s", full_filename ("."));
635 /* Query the user if appropriate, and if ok try to remove the
636 file or directory specified by FS. Return RM_OK if it is removed,
637 and RM_ERROR or RM_USER_DECLINED if not. */
639 static enum RM_status
640 remove_file (struct File_spec *fs)
643 char *pathname = fs->filename;
645 if (!ignore_missing_files && (interactive || stdin_tty)
646 && euidaccess (pathname, W_OK) )
648 if (!S_ISLNK (fspec_filetype_mode (fs)))
651 (S_ISDIR (fspec_filetype_mode (fs))
652 ? _("remove write-protected directory `%s'? ")
653 : _("remove write-protected file `%s'? ")),
654 full_filename (pathname));
656 return RM_USER_DECLINED;
662 if (!asked && interactive)
665 (S_ISDIR (fspec_filetype_mode (fs))
666 ? _("remove directory `%s'? ")
667 : _("remove `%s'? ")),
668 full_filename (pathname));
670 return RM_USER_DECLINED;
674 printf ("%s\n", full_filename (pathname));
676 if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
678 error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
684 /* If not in recursive mode, print an error message and return RM_ERROR.
685 Otherwise, query the user if appropriate, then try to recursively
686 remove directory `pathname', which STATP contains info about.
687 Return 0 if `pathname' is removed, 1 if not.
688 FIXME: describe need_save_cwd parameter. */
690 static enum RM_status
691 remove_dir (struct File_spec *fs, int need_save_cwd)
693 enum RM_status status;
694 struct saved_cwd cwd;
695 char *dir_name = fs->filename;
696 const char *fmt = NULL;
700 error (0, 0, _("%s: is a directory"), full_filename (dir_name));
704 if (!ignore_missing_files && (interactive || stdin_tty)
705 && euidaccess (dir_name, W_OK))
707 fmt = _("directory `%s' is write protected; descend into it anyway? ");
709 else if (interactive)
711 fmt = _("descend into directory `%s'? ");
716 error (0, 0, fmt, full_filename (dir_name));
718 return RM_USER_DECLINED;
722 printf ("%s\n", full_filename (dir_name));
724 /* Save cwd if needed. */
725 if (need_save_cwd && save_cwd (&cwd))
728 /* Make target directory the current one. */
729 if (chdir (dir_name) < 0)
731 error (0, errno, _("cannot change to directory %s"),
732 full_filename (dir_name));
740 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
741 dir_name because dir_name is just a pointer to the dir entry's d_name
742 field, and remove_cwd_entries may close the directory. */
743 ASSIGN_STRDUPA (dir_name, dir_name);
745 status = remove_cwd_entries ();
752 if (restore_cwd (&cwd, NULL, NULL))
759 else if (chdir ("..") < 0)
761 error (0, errno, _("cannot change back to directory %s via `..'"),
762 full_filename (dir_name));
768 error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
769 (status != RM_OK ? _(" (might be nonempty)") : ""));
772 return RM_USER_DECLINED;
776 if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
778 error (0, errno, _("cannot remove directory `%s'"),
779 full_filename (dir_name));
786 /* Remove the file or directory specified by FS after checking appropriate
787 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
788 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
789 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
790 name (and hence must specify a file in the current directory). */
792 static enum RM_status
793 rm (struct File_spec *fs, int user_specified_name)
795 mode_t filetype_mode;
797 if (user_specified_name)
799 char *base = base_name (fs->filename);
801 if (DOT_OR_DOTDOT (base))
803 error (0, 0, _("cannot remove `.' or `..'"));
808 if (fspec_get_filetype_mode (fs, &filetype_mode))
810 if (ignore_missing_files && errno == ENOENT)
813 error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
817 if (S_ISDIR (filetype_mode))
820 struct active_dir_ent *old_ent;
822 /* Insert this directory in the active_dir_map.
823 If there is already a directory in the map with the same inum,
824 then there's *probably* a directory cycle. This test can get
825 a false positive if two directories have the same inode number
826 but different device numbers and one directory contains the
827 other. But since people don't often try to delete hierarchies
828 containing mount points, and when they do, duplicate inode
829 numbers are not that likely, this isn't worth detecting. */
830 old_ent = hash_insert_if_absent (active_dir_map,
831 make_active_dir_ent (fs->inum,
835 error (1, 0, _("Memory exhausted"));
840 WARNING: Circular directory structure.\n\
841 This almost certainly means that you have a corrupted file system.\n\
842 NOTIFY YOUR SYSTEM MANAGER.\n\
843 The following two directories have the same inode number:\n"));
844 /* FIXME: test this!! */
845 print_nth_dir (stderr, current_depth ());
846 fputc ('\n', stderr);
847 print_nth_dir (stderr, old_ent->depth);
848 fputc ('\n', stderr);
855 error (0, 0, _("continue? "));
863 if (!S_ISDIR (filetype_mode) || unlink_dirs)
865 return remove_file (fs);
869 int need_save_cwd = user_specified_name;
870 enum RM_status status;
871 struct active_dir_ent tmp;
872 struct active_dir_ent *old_ent;
875 need_save_cwd = (strchr (fs->filename, '/') != NULL);
877 status = remove_dir (fs, need_save_cwd);
879 /* Remove this directory from the active_dir_map. */
881 old_ent = hash_delete_if_present (active_dir_map, &tmp);
882 assert (old_ent != NULL);
890 main (int argc, char **argv)
895 program_name = argv[0];
896 setlocale (LC_ALL, "");
897 bindtextdomain (PACKAGE, LOCALEDIR);
898 textdomain (PACKAGE);
900 verbose = ignore_missing_files = recursive = interactive
903 while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
907 case 0: /* Long option. */
914 ignore_missing_files = 1;
918 ignore_missing_files = 0;
934 printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
943 if (ignore_missing_files)
947 error (0, 0, _("too few arguments"));
952 stdin_tty = isatty (STDIN_FILENO);
954 /* Initialize dir-stack obstacks. */
955 obstack_init (&dir_stack);
956 obstack_init (&len_stack);
958 active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, free,
960 hash_compare_active_dir_ents);
962 for (; optind < argc; optind++)
965 enum RM_status status;
967 /* Stripping slashes is harmless for rmdir;
968 if the arg is not a directory, it will fail with ENOTDIR. */
969 strip_trailing_slashes (argv[optind]);
970 fspec_init_file (&fs, argv[optind]);
971 status = rm (&fs, 1);
972 assert (VALID_STATUS (status));
973 if (status == RM_ERROR)
977 hash_free (active_dir_map);