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 hash_table *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_1 (void const *x)
252 struct active_dir_ent const *ade = x;
253 return_INTEGER_HASH_1 (ade->inum);
257 hash_active_dir_ent_2 (void const *x)
259 struct active_dir_ent const *ade = x;
260 return_INTEGER_HASH_2 (ade->inum);
264 hash_compare_active_dir_ents (void const *x, void const *y)
266 struct active_dir_ent const *a = x;
267 struct active_dir_ent const *b = y;
268 return_INTEGER_COMPARE (a->inum, b->inum);
272 hash_string_1 (void const *x)
274 return_STRING_HASH_1 (x);
278 hash_string_2 (void const *x)
280 return_STRING_HASH_2 (x);
284 hash_compare_strings (void const *x, void const *y)
286 return strcmp (x, y);
293 fprintf (stderr, _("Try `%s --help' for more information.\n"),
297 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
299 Remove (unlink) the FILE(s).\n\
301 -d, --directory unlink directory, even if non-empty (super-user only)\n\
302 -f, --force ignore nonexistent files, never prompt\n\
303 -i, --interactive prompt before any removal\n\
304 -r, -R, --recursive remove the contents of directories recursively\n\
305 -v, --verbose explain what is being done\n\
306 --help display this help and exit\n\
307 --version output version information and exit\n\
309 puts (_("\nReport bugs to <fileutils-bugs@gnu.ai.mit.edu>."));
315 push_dir (const char *dir_name)
319 len = strlen (dir_name);
321 /* Append the string onto the stack. */
322 obstack_grow (&dir_stack, dir_name, len);
324 /* Append a trailing slash. */
325 obstack_1grow (&dir_stack, '/');
327 /* Add one for the slash. */
330 /* Push the length (including slash) onto its stack. */
331 obstack_grow (&len_stack, &len, sizeof (len));
337 int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
338 size_t *length = (size_t *) obstack_base (&len_stack);
341 assert (n_lengths > 0);
342 top_len = length[n_lengths - 1];
343 assert (top_len >= 2);
345 /* Pop off the specified length of pathname. */
346 assert (obstack_object_size (&dir_stack) >= top_len);
347 obstack_blank (&dir_stack, -top_len);
349 /* Pop the length stack, too. */
350 assert (obstack_object_size (&len_stack) >= sizeof (size_t));
351 obstack_blank (&len_stack, -(sizeof (size_t)));
354 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
355 buffer, DST, so that the last source byte is at the end of the destination
356 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
357 Set *RESULT to point to the beginning of (the portion of) the source data
358 in DST. Return the number of bytes remaining in the destination buffer. */
361 right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
362 char **result, int *truncated)
367 if (src_len <= dst_len)
370 dp = dst + (dst_len - src_len);
375 sp = src + (src_len - dst_len);
381 memcpy (dp, sp, src_len);
384 return dst_len - src_len;
387 /* Using the global directory name obstack, create the full path to FILENAME.
388 Return it in sometimes-realloc'd space that should not be freed by the
389 caller. Realloc as necessary. If realloc fails, use a static buffer
390 and put as long a suffix in that buffer as possible. */
393 full_filename (const char *filename)
395 static char *buf = NULL;
396 static size_t n_allocated = 0;
398 int dir_len = obstack_object_size (&dir_stack);
399 char *dir_name = (char *) obstack_base (&dir_stack);
400 size_t n_bytes_needed;
403 filename_len = strlen (filename);
404 n_bytes_needed = dir_len + filename_len + 1;
406 if (n_bytes_needed > n_allocated)
408 /* This code requires that realloc accept NULL as the first arg.
409 This function must not use xrealloc. Otherwise, an out-of-memory
410 error involving a file name to be expanded here wouldn't ever
411 be issued. Use realloc and fall back on using a static buffer
412 if memory allocation fails. */
413 buf = realloc (buf, n_bytes_needed);
414 n_allocated = n_bytes_needed;
418 #define SBUF_SIZE 512
419 #define ELLIPSES_PREFIX "[...]"
420 static char static_buf[SBUF_SIZE];
425 len = right_justify (static_buf, SBUF_SIZE, filename,
426 filename_len + 1, &p, &truncated);
427 right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
430 memcpy (static_buf, ELLIPSES_PREFIX,
431 sizeof (ELLIPSES_PREFIX) - 1);
437 /* Copy directory part, including trailing slash. */
438 memcpy (buf, dir_name, dir_len);
440 /* Append filename part, including trailing zero byte. */
441 memcpy (buf + dir_len, filename, filename_len + 1);
443 assert (strlen (buf) + 1 == n_bytes_needed);
449 fspec_init_file (struct File_spec *fs, const char *filename)
451 fs->filename = (char *) filename;
452 fs->have_full_mode = 0;
453 fs->have_filetype_mode = 0;
457 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
459 fs->filename = dp->d_name;
460 fs->have_full_mode = 0;
461 fs->have_filetype_mode = 0;
462 fs->inum = D_INO (dp);
464 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN)
465 if (filetype_mode != DT_UNKNOWN)
467 fs->have_filetype_mode = 1;
468 fs->mode = filetype_mode;
474 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
476 struct stat stat_buf;
478 if (fs->have_full_mode)
480 *full_mode = fs->mode;
484 if (lstat (fs->filename, &stat_buf))
487 fs->have_full_mode = 1;
488 fs->have_filetype_mode = 1;
489 fs->mode = stat_buf.st_mode;
490 fs->inum = stat_buf.st_ino;
492 *full_mode = stat_buf.st_mode;
497 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
501 if (fs->have_filetype_mode)
503 *filetype_mode = fs->mode;
508 fail = fspec_get_full_mode (fs, filetype_mode);
515 fspec_filetype_mode (const struct File_spec *fs)
517 assert (fs->have_filetype_mode);
521 /* Recursively remove all of the entries in the current directory.
522 Return an indication of the success of the operation. */
525 remove_cwd_entries (void)
527 /* NOTE: this is static. */
528 static DIR *dirp = NULL;
530 /* NULL or a malloc'd and initialized hash table of entries in the
531 current directory that have been processed but not removed --
532 due either to an error or to an interactive `no' response. */
533 struct hash_table *ht = NULL;
536 enum RM_status status = RM_OK;
542 /* FIXME-someday: but this is actually the previously opened dir. */
543 error (0, errno, "%s", full_filename ("."));
551 dirp = opendir (".");
554 if (errno != ENOENT || !ignore_missing_files)
556 error (0, errno, "%s", full_filename ("."));
562 while ((dp = readdir (dirp)) != NULL)
564 /* Skip this entry if it's `.' or `..'. */
565 if (DOT_OR_DOTDOT (dp->d_name))
568 /* Skip this entry if it's in the table of ones we've already
570 if (ht && hash_find_item (ht, (dp)->d_name))
575 enum RM_status tmp_status;
576 fspec_init_dp (&fs, dp);
577 tmp_status = rm (&fs, 0);
579 if (tmp_status > status)
581 assert (VALID_STATUS (status));
584 /* If this entry was not removed (due either to an error or to
585 an interactive `no' response), record it in the hash table so
586 we don't consider it again if we reopen this directory later. */
595 ht = hash_init_table (NULL, HT_INITIAL_CAPACITY, 0, 0,
596 hash_string_1, hash_string_2,
597 hash_compare_strings);
599 error (1, 0, _("Memory exhausted"));
601 p = xmalloc (NLENGTH (dp) + 1);
602 memcpy (p, (dp)->d_name, NLENGTH (dp) + 1);
603 fail = hash_insert_item (ht, p, &old_item);
604 assert (old_item == NULL);
606 error (1, 0, _("Memory exhausted"));
613 while (dirp == NULL);
617 error (0, errno, "%s", full_filename ("."));
624 hash_free_items (ht);
625 hash_free_table (ht);
632 /* Query the user if appropriate, and if ok try to remove the
633 file or directory specified by FS. Return RM_OK if it is removed,
634 and RM_ERROR or RM_USER_DECLINED if not. */
636 static enum RM_status
637 remove_file (struct File_spec *fs)
640 char *pathname = fs->filename;
642 if (!ignore_missing_files && (interactive || stdin_tty)
643 && euidaccess (pathname, W_OK) )
645 if (!S_ISLNK (fspec_filetype_mode (fs)))
648 (S_ISDIR (fspec_filetype_mode (fs))
649 ? _("remove write-protected directory `%s'? ")
650 : _("remove write-protected file `%s'? ")),
651 full_filename (pathname));
653 return RM_USER_DECLINED;
659 if (!asked && interactive)
662 (S_ISDIR (fspec_filetype_mode (fs))
663 ? _("remove directory `%s'? ")
664 : _("remove `%s'? ")),
665 full_filename (pathname));
667 return RM_USER_DECLINED;
671 printf ("%s\n", full_filename (pathname));
673 if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
675 error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
681 /* If not in recursive mode, print an error message and return RM_ERROR.
682 Otherwise, query the user if appropriate, then try to recursively
683 remove directory `pathname', which STATP contains info about.
684 Return 0 if `pathname' is removed, 1 if not.
685 FIXME: describe need_save_cwd parameter. */
687 static enum RM_status
688 remove_dir (struct File_spec *fs, int need_save_cwd)
690 enum RM_status status;
691 struct saved_cwd cwd;
692 char *dir_name = fs->filename;
693 const char *fmt = NULL;
697 error (0, 0, _("%s: is a directory"), full_filename (dir_name));
701 if (!ignore_missing_files && (interactive || stdin_tty)
702 && euidaccess (dir_name, W_OK))
704 fmt = _("directory `%s' is write protected; descend into it anyway? ");
706 else if (interactive)
708 fmt = _("descend into directory `%s'? ");
713 error (0, 0, fmt, full_filename (dir_name));
715 return RM_USER_DECLINED;
719 printf ("%s\n", full_filename (dir_name));
721 /* Save cwd if needed. */
722 if (need_save_cwd && save_cwd (&cwd))
725 /* Make target directory the current one. */
726 if (chdir (dir_name) < 0)
728 error (0, errno, _("cannot change to directory %s"),
729 full_filename (dir_name));
737 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
738 dir_name because dir_name is just a pointer to the dir entry's d_name
739 field, and remove_cwd_entries may close the directory. */
740 ASSIGN_STRDUPA (dir_name, dir_name);
742 status = remove_cwd_entries ();
749 if (restore_cwd (&cwd, NULL, NULL))
756 else if (chdir ("..") < 0)
758 error (0, errno, _("cannot change back to directory %s via `..'"),
759 full_filename (dir_name));
765 error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
766 (status != RM_OK ? _(" (might be nonempty)") : ""));
769 return RM_USER_DECLINED;
773 if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
775 error (0, errno, _("cannot remove directory `%s'"),
776 full_filename (dir_name));
783 /* Remove the file or directory specified by FS after checking appropriate
784 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
785 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
786 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
787 name (and hence must specify a file in the current directory). */
789 static enum RM_status
790 rm (struct File_spec *fs, int user_specified_name)
792 mode_t filetype_mode;
794 if (user_specified_name)
796 char *base = base_name (fs->filename);
798 if (DOT_OR_DOTDOT (base))
800 error (0, 0, _("cannot remove `.' or `..'"));
805 if (fspec_get_filetype_mode (fs, &filetype_mode))
807 if (ignore_missing_files && errno == ENOENT)
810 error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
814 if (S_ISDIR (filetype_mode))
817 struct active_dir_ent *old_ent;
819 /* Insert this directory in the active_dir_map.
820 If there is already a directory in the map with the same inum,
821 then there's *probably* a directory cycle. This test can get
822 a false positive if two directories have the same inode number
823 but different device numbers and one directory contains the
824 other. But since people don't often try to delete hierarchies
825 containing mount points, and when they do, duplicate inode
826 numbers are not that likely, this isn't worth detecting. */
827 fail = hash_insert_item (active_dir_map,
828 make_active_dir_ent (fs->inum, current_depth ()),
831 error (1, 0, _("Memory exhausted"));
836 WARNING: Circular directory structure.\n\
837 This almost certainly means that you have a corrupted file system.\n\
838 NOTIFY YOUR SYSTEM MANAGER.\n\
839 The following two directories have the same inode number:\n"));
840 print_nth_dir (stderr, current_depth ());
841 fputc ('\n', stderr);
842 print_nth_dir (stderr, old_ent->depth);
843 fputc ('\n', stderr);
850 error (0, 0, _("continue? "));
858 if (!S_ISDIR (filetype_mode) || unlink_dirs)
860 return remove_file (fs);
864 int need_save_cwd = user_specified_name;
865 enum RM_status status;
866 struct active_dir_ent tmp;
867 struct active_dir_ent *old_ent;
870 need_save_cwd = (strchr (fs->filename, '/') != NULL);
872 status = remove_dir (fs, need_save_cwd);
874 /* Remove this directory from the active_dir_map. */
876 hash_delete_item (active_dir_map, &tmp, (void **) &old_ent);
877 assert (old_ent != NULL);
885 main (int argc, char **argv)
890 program_name = argv[0];
891 setlocale (LC_ALL, "");
892 bindtextdomain (PACKAGE, LOCALEDIR);
893 textdomain (PACKAGE);
895 verbose = ignore_missing_files = recursive = interactive
898 while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
902 case 0: /* Long option. */
909 ignore_missing_files = 1;
913 ignore_missing_files = 0;
929 printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
938 if (ignore_missing_files)
942 error (0, 0, _("too few arguments"));
947 stdin_tty = isatty (STDIN_FILENO);
949 /* Initialize dir-stack obstacks. */
950 obstack_init (&dir_stack);
951 obstack_init (&len_stack);
953 active_dir_map = hash_init_table (NULL, ACTIVE_DIR_INITIAL_CAPACITY, 0, 0,
954 hash_active_dir_ent_1,
955 hash_active_dir_ent_2,
956 hash_compare_active_dir_ents);
958 for (; optind < argc; optind++)
961 enum RM_status status;
963 /* Stripping slashes is harmless for rmdir;
964 if the arg is not a directory, it will fail with ENOTDIR. */
965 strip_trailing_slashes (argv[optind]);
966 fspec_init_file (&fs, argv[optind]);
967 status = rm (&fs, 1);
968 assert (VALID_STATUS (status));
969 if (status == RM_ERROR)