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 ("."));
552 dirp = opendir (".");
555 if (errno != ENOENT || !ignore_missing_files)
557 error (0, errno, "%s", full_filename ("."));
563 while ((dp = readdir (dirp)) != NULL)
567 enum RM_status tmp_status;
569 /* Skip this entry if it's `.' or `..'. */
570 if (DOT_OR_DOTDOT (dp->d_name))
573 /* Skip this entry if it's in the table of ones we've already
575 if (ht && hash_find_item (ht, (dp)->d_name))
578 fspec_init_dp (&fs, dp);
580 /* Save a copy of the name of this entry, in case we have
581 to add it to the set of unremoved entries below. */
582 ASSIGN_STRDUPA (entry_name, dp->d_name);
584 /* CAUTION: after this call to rm, DP may not be valid --
585 it may have been freed due to a close in a recursive call
586 (through rm and remove_dir) to this function. */
587 tmp_status = rm (&fs, 0);
590 if (tmp_status > status)
592 assert (VALID_STATUS (status));
594 /* If this entry was not removed (due either to an error or to
595 an interactive `no' response), record it in the hash table so
596 we don't consider it again if we reopen this directory later. */
604 ht = hash_init_table (NULL, HT_INITIAL_CAPACITY, 0, 0,
605 hash_string_1, hash_string_2,
606 hash_compare_strings);
608 error (1, 0, _("Memory exhausted"));
610 fail = hash_insert_item (ht, entry_name, &old_item);
611 assert (old_item == NULL);
613 error (1, 0, _("Memory exhausted"));
620 while (dirp == NULL);
624 error (0, errno, "%s", full_filename ("."));
631 hash_free_table (ht);
638 /* Query the user if appropriate, and if ok try to remove the
639 file or directory specified by FS. Return RM_OK if it is removed,
640 and RM_ERROR or RM_USER_DECLINED if not. */
642 static enum RM_status
643 remove_file (struct File_spec *fs)
646 char *pathname = fs->filename;
648 if (!ignore_missing_files && (interactive || stdin_tty)
649 && euidaccess (pathname, W_OK) )
651 if (!S_ISLNK (fspec_filetype_mode (fs)))
654 (S_ISDIR (fspec_filetype_mode (fs))
655 ? _("remove write-protected directory `%s'? ")
656 : _("remove write-protected file `%s'? ")),
657 full_filename (pathname));
659 return RM_USER_DECLINED;
665 if (!asked && interactive)
668 (S_ISDIR (fspec_filetype_mode (fs))
669 ? _("remove directory `%s'? ")
670 : _("remove `%s'? ")),
671 full_filename (pathname));
673 return RM_USER_DECLINED;
677 printf ("%s\n", full_filename (pathname));
679 if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
681 error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
687 /* If not in recursive mode, print an error message and return RM_ERROR.
688 Otherwise, query the user if appropriate, then try to recursively
689 remove directory `pathname', which STATP contains info about.
690 Return 0 if `pathname' is removed, 1 if not.
691 FIXME: describe need_save_cwd parameter. */
693 static enum RM_status
694 remove_dir (struct File_spec *fs, int need_save_cwd)
696 enum RM_status status;
697 struct saved_cwd cwd;
698 char *dir_name = fs->filename;
699 const char *fmt = NULL;
703 error (0, 0, _("%s: is a directory"), full_filename (dir_name));
707 if (!ignore_missing_files && (interactive || stdin_tty)
708 && euidaccess (dir_name, W_OK))
710 fmt = _("directory `%s' is write protected; descend into it anyway? ");
712 else if (interactive)
714 fmt = _("descend into directory `%s'? ");
719 error (0, 0, fmt, full_filename (dir_name));
721 return RM_USER_DECLINED;
725 printf ("%s\n", full_filename (dir_name));
727 /* Save cwd if needed. */
728 if (need_save_cwd && save_cwd (&cwd))
731 /* Make target directory the current one. */
732 if (chdir (dir_name) < 0)
734 error (0, errno, _("cannot change to directory %s"),
735 full_filename (dir_name));
743 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
744 dir_name because dir_name is just a pointer to the dir entry's d_name
745 field, and remove_cwd_entries may close the directory. */
746 ASSIGN_STRDUPA (dir_name, dir_name);
748 status = remove_cwd_entries ();
755 if (restore_cwd (&cwd, NULL, NULL))
762 else if (chdir ("..") < 0)
764 error (0, errno, _("cannot change back to directory %s via `..'"),
765 full_filename (dir_name));
771 error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
772 (status != RM_OK ? _(" (might be nonempty)") : ""));
775 return RM_USER_DECLINED;
779 if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
781 error (0, errno, _("cannot remove directory `%s'"),
782 full_filename (dir_name));
789 /* Remove the file or directory specified by FS after checking appropriate
790 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
791 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
792 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
793 name (and hence must specify a file in the current directory). */
795 static enum RM_status
796 rm (struct File_spec *fs, int user_specified_name)
798 mode_t filetype_mode;
800 if (user_specified_name)
802 char *base = base_name (fs->filename);
804 if (DOT_OR_DOTDOT (base))
806 error (0, 0, _("cannot remove `.' or `..'"));
811 if (fspec_get_filetype_mode (fs, &filetype_mode))
813 if (ignore_missing_files && errno == ENOENT)
816 error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
820 if (S_ISDIR (filetype_mode))
823 struct active_dir_ent *old_ent;
825 /* Insert this directory in the active_dir_map.
826 If there is already a directory in the map with the same inum,
827 then there's *probably* a directory cycle. This test can get
828 a false positive if two directories have the same inode number
829 but different device numbers and one directory contains the
830 other. But since people don't often try to delete hierarchies
831 containing mount points, and when they do, duplicate inode
832 numbers are not that likely, this isn't worth detecting. */
833 fail = hash_insert_item (active_dir_map,
834 make_active_dir_ent (fs->inum, current_depth ()),
837 error (1, 0, _("Memory exhausted"));
842 WARNING: Circular directory structure.\n\
843 This almost certainly means that you have a corrupted file system.\n\
844 NOTIFY YOUR SYSTEM MANAGER.\n\
845 The following two directories have the same inode number:\n"));
846 print_nth_dir (stderr, current_depth ());
847 fputc ('\n', stderr);
848 print_nth_dir (stderr, old_ent->depth);
849 fputc ('\n', stderr);
856 error (0, 0, _("continue? "));
864 if (!S_ISDIR (filetype_mode) || unlink_dirs)
866 return remove_file (fs);
870 int need_save_cwd = user_specified_name;
871 enum RM_status status;
872 struct active_dir_ent tmp;
873 struct active_dir_ent *old_ent;
876 need_save_cwd = (strchr (fs->filename, '/') != NULL);
878 status = remove_dir (fs, need_save_cwd);
880 /* Remove this directory from the active_dir_map. */
882 hash_delete_item (active_dir_map, &tmp, (void **) &old_ent);
883 assert (old_ent != NULL);
891 main (int argc, char **argv)
896 program_name = argv[0];
897 setlocale (LC_ALL, "");
898 bindtextdomain (PACKAGE, LOCALEDIR);
899 textdomain (PACKAGE);
901 verbose = ignore_missing_files = recursive = interactive
904 while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
908 case 0: /* Long option. */
915 ignore_missing_files = 1;
919 ignore_missing_files = 0;
935 printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
944 if (ignore_missing_files)
948 error (0, 0, _("too few arguments"));
953 stdin_tty = isatty (STDIN_FILENO);
955 /* Initialize dir-stack obstacks. */
956 obstack_init (&dir_stack);
957 obstack_init (&len_stack);
959 active_dir_map = hash_init_table (NULL, ACTIVE_DIR_INITIAL_CAPACITY, 0, 0,
960 hash_active_dir_ent_1,
961 hash_active_dir_ent_2,
962 hash_compare_active_dir_ents);
964 for (; optind < argc; optind++)
967 enum RM_status status;
969 /* Stripping slashes is harmless for rmdir;
970 if the arg is not a directory, it will fail with ENOTDIR. */
971 strip_trailing_slashes (argv[optind]);
972 fspec_init_file (&fs, argv[optind]);
973 status = rm (&fs, 1);
974 assert (VALID_STATUS (status));
975 if (status == RM_ERROR)