1 /* remove.c -- core functions for removing files and directories
2 Copyright (C) 88, 90, 91, 1994-1999 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 /* Extracted from rm.c and librarified by Jim Meyering. */
26 #include <sys/types.h>
32 typedef enum {false = 0, true = 1} bool;
42 #define obstack_chunk_alloc malloc
43 #define obstack_chunk_free free
46 # if defined (__GNUC__) || __STDC__
47 # define PARAMS(args) args
49 # define PARAMS(args) ()
53 /* On systems with an lstat function that accepts the empty string,
54 arrange to make lstat calls go through the wrapper function. */
55 #if HAVE_LSTAT_EMPTY_STRING_BUG
56 int rpl_lstat
PARAMS((const char *, struct stat
*));
57 # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
60 #ifdef D_INO_IN_DIRENT
61 # define D_INO(dp) ((dp)->d_ino)
62 # define ENABLE_CYCLE_CHECK
64 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
68 #if !defined (S_ISLNK)
69 # define S_ISLNK(Mode) 0
73 # define ASSIGN_STRDUPA(DEST, S) \
74 do { DEST = strdupa(S); } while (0)
76 # define ASSIGN_STRDUPA(DEST, S) \
79 const char *s_ = (S); \
80 size_t len_ = strlen (s_) + 1; \
81 char *tmp_dest_ = (char *) alloca (len_); \
82 DEST = memcpy (tmp_dest_, (s_), len_); \
87 /* Initial capacity of per-directory hash table of entries that have
88 been processed but not been deleted. */
89 #define HT_INITIAL_CAPACITY 13
91 /* Initial capacity of the active directory hash table. This table will
92 be resized only for hierarchies more than about 45 levels deep. */
93 #define ACTIVE_DIR_INITIAL_CAPACITY 53
98 extern char *program_name
;
100 /* state initialized by remove_init, freed by remove_fini */
102 /* An entry in the active_dir_map. */
103 struct active_dir_ent
109 /* The name of the directory (starting with and relative to a command
110 line argument) being processed. When a subdirectory is entered, a new
111 component is appended (pushed). When RM chdir's out of a directory,
112 the top component is removed (popped). This is used to form a full
113 file name when necessary. */
114 static struct obstack dir_stack
;
116 /* Stack of lengths of directory names (including trailing slash)
117 appended to dir_stack. We have to have a separate stack of lengths
118 (rather than just popping back to previous slash) because the first
119 element pushed onto the dir stack may contain slashes. */
120 static struct obstack len_stack
;
122 /* Set of `active' directories from the current command-line argument
123 to the level in the hierarchy at which files are being removed.
124 A directory is added to the active set when RM begins removing it
125 (or its entries), and it is removed from the set just after RM has
126 finished processing it.
128 This is actually a map (not a set), implemented with a hash table.
129 For each active directory, it maps the directory's inode number to the
130 depth of that directory relative to the root of the tree being deleted.
131 A directory specified on the command line has depth zero.
132 This construct is used to detect directory cycles so that RM can warn
133 about them rather than iterating endlessly. */
134 #ifdef ENABLE_CYCLE_CHECK
135 static struct hash_table
*active_dir_map
;
138 static inline unsigned int
141 return obstack_object_size (&len_stack
) / sizeof (size_t);
145 print_nth_dir (FILE *stream
, unsigned int depth
)
147 size_t *length
= (size_t *) obstack_base (&len_stack
);
148 char *dir_name
= (char *) obstack_base (&dir_stack
);
149 unsigned int sum
= 0;
152 assert (depth
< current_depth ());
154 for (i
= 0; i
<= depth
; i
++)
159 fwrite (dir_name
, 1, sum
, stream
);
162 static inline struct active_dir_ent
*
163 make_active_dir_ent (ino_t inum
, unsigned int depth
)
165 struct active_dir_ent
*ent
;
166 ent
= (struct active_dir_ent
*) xmalloc (sizeof *ent
);
173 hash_active_dir_ent (void const *x
, unsigned int table_size
)
175 struct active_dir_ent
const *ade
= x
;
176 return ade
->inum
% table_size
;
180 hash_compare_active_dir_ents (void const *x
, void const *y
)
182 struct active_dir_ent
const *a
= x
;
183 struct active_dir_ent
const *b
= y
;
184 return a
->inum
== b
->inum
;
187 /* A hash function for null-terminated char* strings using
188 the method described in Aho, Sethi, & Ullman, p 436. */
191 hash_pjw (const void *x
, unsigned int tablesize
)
200 if ((g
= h
& (unsigned int) 0xf0000000) != 0)
201 h
= (h
^ (g
>> 24)) ^ g
;
204 return (h
% tablesize
);
208 hash_compare_strings (void const *x
, void const *y
)
210 return STREQ (x
, y
) ? true : false;
214 push_dir (const char *dir_name
)
218 len
= strlen (dir_name
);
220 /* Append the string onto the stack. */
221 obstack_grow (&dir_stack
, dir_name
, len
);
223 /* Append a trailing slash. */
224 obstack_1grow (&dir_stack
, '/');
226 /* Add one for the slash. */
229 /* Push the length (including slash) onto its stack. */
230 obstack_grow (&len_stack
, &len
, sizeof (len
));
236 int n_lengths
= obstack_object_size (&len_stack
) / sizeof (size_t);
237 size_t *length
= (size_t *) obstack_base (&len_stack
);
240 assert (n_lengths
> 0);
241 top_len
= length
[n_lengths
- 1];
242 assert (top_len
>= 2);
244 /* Pop off the specified length of pathname. */
245 assert (obstack_object_size (&dir_stack
) >= top_len
);
246 obstack_blank (&dir_stack
, -top_len
);
248 /* Pop the length stack, too. */
249 assert (obstack_object_size (&len_stack
) >= sizeof (size_t));
250 obstack_blank (&len_stack
, (int) -(sizeof (size_t)));
253 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
254 buffer, DST, so that the last source byte is at the end of the destination
255 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
256 Set *RESULT to point to the beginning of (the portion of) the source data
257 in DST. Return the number of bytes remaining in the destination buffer. */
260 right_justify (char *dst
, size_t dst_len
, const char *src
, size_t src_len
,
261 char **result
, int *truncated
)
266 if (src_len
<= dst_len
)
269 dp
= dst
+ (dst_len
- src_len
);
274 sp
= src
+ (src_len
- dst_len
);
280 *result
= memcpy (dp
, sp
, src_len
);
281 return dst_len
- src_len
;
284 /* Using the global directory name obstack, create the full path to FILENAME.
285 Return it in sometimes-realloc'd space that should not be freed by the
286 caller. Realloc as necessary. If realloc fails, use a static buffer
287 and put as long a suffix in that buffer as possible. */
290 full_filename (const char *filename
)
292 static char *buf
= NULL
;
293 static size_t n_allocated
= 0;
295 int dir_len
= obstack_object_size (&dir_stack
);
296 char *dir_name
= (char *) obstack_base (&dir_stack
);
297 size_t n_bytes_needed
;
300 filename_len
= strlen (filename
);
301 n_bytes_needed
= dir_len
+ filename_len
+ 1;
303 if (n_bytes_needed
> n_allocated
)
305 /* This code requires that realloc accept NULL as the first arg.
306 This function must not use xrealloc. Otherwise, an out-of-memory
307 error involving a file name to be expanded here wouldn't ever
308 be issued. Use realloc and fall back on using a static buffer
309 if memory allocation fails. */
310 buf
= realloc (buf
, n_bytes_needed
);
311 n_allocated
= n_bytes_needed
;
315 #define SBUF_SIZE 512
316 #define ELLIPSES_PREFIX "[...]"
317 static char static_buf
[SBUF_SIZE
];
322 len
= right_justify (static_buf
, SBUF_SIZE
, filename
,
323 filename_len
+ 1, &p
, &truncated
);
324 right_justify (static_buf
, len
, dir_name
, dir_len
, &p
, &truncated
);
327 memcpy (static_buf
, ELLIPSES_PREFIX
,
328 sizeof (ELLIPSES_PREFIX
) - 1);
334 /* Copy directory part, including trailing slash, and then
335 append the filename part, including a trailing zero byte. */
336 memcpy (mempcpy (buf
, dir_name
, dir_len
), filename
, filename_len
+ 1);
338 assert (strlen (buf
) + 1 == n_bytes_needed
);
344 fspec_init_file (struct File_spec
*fs
, const char *filename
)
346 fs
->filename
= (char *) filename
;
347 fs
->have_full_mode
= 0;
348 fs
->have_filetype_mode
= 0;
352 fspec_init_dp (struct File_spec
*fs
, struct dirent
*dp
)
354 fs
->filename
= dp
->d_name
;
355 fs
->have_full_mode
= 0;
356 fs
->have_filetype_mode
= 0;
357 fs
->inum
= D_INO (dp
);
359 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
360 if (dp
->d_type
!= DT_UNKNOWN
)
362 fs
->have_filetype_mode
= 1;
363 fs
->mode
= DTTOIF (dp
->d_type
);
369 fspec_get_full_mode (struct File_spec
*fs
, mode_t
*full_mode
)
371 struct stat stat_buf
;
373 if (fs
->have_full_mode
)
375 *full_mode
= fs
->mode
;
379 if (lstat (fs
->filename
, &stat_buf
))
382 fs
->have_full_mode
= 1;
383 fs
->have_filetype_mode
= 1;
384 fs
->mode
= stat_buf
.st_mode
;
385 fs
->inum
= stat_buf
.st_ino
;
387 *full_mode
= stat_buf
.st_mode
;
392 fspec_get_filetype_mode (struct File_spec
*fs
, mode_t
*filetype_mode
)
396 if (fs
->have_filetype_mode
)
398 *filetype_mode
= fs
->mode
;
403 fail
= fspec_get_full_mode (fs
, filetype_mode
);
410 fspec_filetype_mode (const struct File_spec
*fs
)
412 assert (fs
->have_filetype_mode
);
417 same_file (const char *file_1
, const char *file_2
)
419 struct stat sb1
, sb2
;
420 return (lstat (file_1
, &sb1
) == 0
421 && lstat (file_2
, &sb2
) == 0
422 && SAME_INODE (sb1
, sb2
));
426 /* Recursively remove all of the entries in the current directory.
427 Return an indication of the success of the operation. */
429 static enum RM_status
430 remove_cwd_entries (const struct rm_options
*x
)
432 /* NOTE: this is static. */
433 static DIR *dirp
= NULL
;
435 /* NULL or a malloc'd and initialized hash table of entries in the
436 current directory that have been processed but not removed --
437 due either to an error or to an interactive `no' response. */
438 struct hash_table
*ht
= NULL
;
440 /* FIXME: describe */
441 static struct obstack entry_name_pool
;
442 static int first_call
= 1;
444 enum RM_status status
= RM_OK
;
449 obstack_init (&entry_name_pool
);
456 /* FIXME-someday: but this is actually the previously opened dir. */
457 error (0, errno
, "%s", full_filename ("."));
465 /* FIXME: why do this? */
468 dirp
= opendir (".");
471 if (errno
!= ENOENT
|| !x
->ignore_missing_files
)
473 error (0, errno
, "%s", full_filename ("."));
483 enum RM_status tmp_status
;
486 /* FILE should be skipped if it is `.' or `..', or if it is in
487 the table, HT, of entries we've already processed. */
488 #define SKIPPABLE(Ht, File) \
489 (DOT_OR_DOTDOT(File) || (Ht && hash_lookup (Ht, File)))
491 /* FIXME: use readdir_r directly into an obstack to avoid
492 the obstack_copy0 below --
493 Suggestion from Uli. Be careful -- there are different
494 prototypes on e.g. Solaris.
496 Do something like this:
497 #define NAME_MAX_FOR(Parent_dir) pathconf ((Parent_dir),
499 dp = obstack_alloc (sizeof (struct dirent)
500 + NAME_MAX_FOR (".") + 1);
501 fail = xreaddir (dirp, dp);
502 where xreaddir is ...
504 But what about systems like the hurd where NAME_MAX is supposed
505 to be effectively unlimited. We don't want to have to allocate
506 a huge buffer to accommodate maximum possible entry name. */
510 #if ! HAVE_WORKING_READDIR
513 /* Since we have probably modified the directory since it
514 was opened, readdir returning NULL does not necessarily
515 mean we have read the last entry. Rewind it and check
516 again. This happens on SunOS4.1.4 with 254 or more files
519 while ((dp
= readdir (dirp
)) && SKIPPABLE (ht
, dp
->d_name
))
529 if (SKIPPABLE (ht
, dp
->d_name
))
532 fspec_init_dp (&fs
, dp
);
534 /* Save a copy of the name of this entry, in case we have
535 to add it to the set of unremoved entries below. */
536 entry_name
= obstack_copy0 (&entry_name_pool
,
537 dp
->d_name
, NLENGTH (dp
));
539 /* CAUTION: after this call to rm, DP may not be valid --
540 it may have been freed due to a close in a recursive call
541 (through rm and remove_dir) to this function. */
542 tmp_status
= rm (&fs
, 0, x
);
545 if (tmp_status
> status
)
547 assert (VALID_STATUS (status
));
549 /* If this entry was not removed (due either to an error or to
550 an interactive `no' response), record it in the hash table so
551 we don't consider it again if we reopen this directory later. */
556 ht
= hash_initialize (HT_INITIAL_CAPACITY
, NULL
, hash_pjw
,
557 hash_compare_strings
, NULL
);
559 error (1, 0, _("virtual memory exhausted"));
561 if (! hash_insert (ht
, entry_name
))
562 error (1, 0, _("virtual memory exhausted"));
566 /* This entry was not saved in the hash table. Free it. */
567 obstack_free (&entry_name_pool
, entry_name
);
574 while (dirp
== NULL
);
580 error (0, errno
, "%s", full_filename ("."));
591 if (obstack_object_size (&entry_name_pool
) > 0)
592 obstack_free (&entry_name_pool
, obstack_base (&entry_name_pool
));
597 /* Query the user if appropriate, and if ok try to remove the
598 file or directory specified by FS. Return RM_OK if it is removed,
599 and RM_ERROR or RM_USER_DECLINED if not. */
601 static enum RM_status
602 remove_file (struct File_spec
*fs
, const struct rm_options
*x
)
605 char *pathname
= fs
->filename
;
607 if (!x
->ignore_missing_files
&& (x
->interactive
|| x
->stdin_tty
)
608 && euidaccess (pathname
, W_OK
))
610 if (!S_ISLNK (fspec_filetype_mode (fs
)))
613 (S_ISDIR (fspec_filetype_mode (fs
))
614 ? _("%s: remove write-protected directory `%s'? ")
615 : _("%s: remove write-protected file `%s'? ")),
616 program_name
, full_filename (pathname
));
618 return RM_USER_DECLINED
;
624 if (!asked
&& x
->interactive
)
626 /* FIXME: use a variant of error (instead of fprintf) that doesn't
627 append a newline. Then we won't have to declare program_name in
630 (S_ISDIR (fspec_filetype_mode (fs
))
631 ? _("%s: remove directory `%s'? ")
632 : _("%s: remove `%s'? ")),
633 program_name
, full_filename (pathname
));
635 return RM_USER_DECLINED
;
639 printf (_("removing %s\n"), full_filename (pathname
));
641 if (unlink (pathname
) && (errno
!= ENOENT
|| !x
->ignore_missing_files
))
643 error (0, errno
, _("cannot unlink `%s'"), full_filename (pathname
));
649 /* If not in recursive mode, print an error message and return RM_ERROR.
650 Otherwise, query the user if appropriate, then try to recursively
651 remove the directory specified by FS. Return RM_OK if it is removed,
652 and RM_ERROR or RM_USER_DECLINED if not.
653 FIXME: describe need_save_cwd parameter. */
655 static enum RM_status
656 remove_dir (struct File_spec
*fs
, int need_save_cwd
, const struct rm_options
*x
)
658 enum RM_status status
;
659 struct saved_cwd cwd
;
660 char *dir_name
= fs
->filename
;
661 const char *fmt
= NULL
;
665 error (0, 0, _("%s: is a directory"), full_filename (dir_name
));
669 if (!x
->ignore_missing_files
&& (x
->interactive
|| x
->stdin_tty
)
670 && euidaccess (dir_name
, W_OK
))
672 fmt
= _("%s: directory `%s' is write protected; descend into it anyway? ");
674 else if (x
->interactive
)
676 fmt
= _("%s: descend into directory `%s'? ");
681 fprintf (stderr
, fmt
, program_name
, full_filename (dir_name
));
683 return RM_USER_DECLINED
;
687 printf (_("removing all entries of directory %s\n"),
688 full_filename (dir_name
));
690 /* Save cwd if needed. */
691 if (need_save_cwd
&& save_cwd (&cwd
))
694 /* Make target directory the current one. */
695 if (chdir (dir_name
) < 0)
697 error (0, errno
, _("cannot change to directory %s"),
698 full_filename (dir_name
));
706 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
707 it because it is just a pointer to the dir entry's d_name field, and
708 remove_cwd_entries may close the directory. */
709 ASSIGN_STRDUPA (dir_name
, dir_name
);
711 status
= remove_cwd_entries (x
);
718 if (restore_cwd (&cwd
, NULL
, NULL
))
725 else if (chdir ("..") < 0)
727 error (0, errno
, _("cannot change back to directory %s via `..'"),
728 full_filename (dir_name
));
734 fprintf (stderr
, _("%s: remove directory `%s'%s? "),
736 full_filename (dir_name
),
737 (status
!= RM_OK
? _(" (might be nonempty)") : ""));
740 return RM_USER_DECLINED
;
745 printf (_("removing the directory itself: %s\n"), full_filename (dir_name
));
747 if (rmdir (dir_name
) && (errno
!= ENOENT
|| !x
->ignore_missing_files
))
749 int saved_errno
= errno
;
754 /* See if rmdir just failed because DIR_NAME is the current directory.
755 If so, give a better diagnostic than `rm: cannot remove directory
756 `...': Invalid argument' */
757 if (errno
== EINVAL
&& same_file (".", dir_name
))
759 error (0, 0, _("cannot remove current directory `%s'"),
760 full_filename (dir_name
));
764 error (0, saved_errno
, _("cannot remove directory `%s'"),
765 full_filename (dir_name
));
773 /* Remove the file or directory specified by FS after checking appropriate
774 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
775 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
776 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
777 name (and hence must specify a file in the current directory). */
780 rm (struct File_spec
*fs
, int user_specified_name
, const struct rm_options
*x
)
782 mode_t filetype_mode
;
784 if (user_specified_name
)
786 char *base
= base_name (fs
->filename
);
788 if (DOT_OR_DOTDOT (base
))
790 error (0, 0, _("cannot remove `.' or `..'"));
795 if (fspec_get_filetype_mode (fs
, &filetype_mode
))
797 if (x
->ignore_missing_files
&& errno
== ENOENT
)
800 error (0, errno
, _("cannot remove `%s'"), full_filename (fs
->filename
));
804 #ifdef ENABLE_CYCLE_CHECK
805 if (S_ISDIR (filetype_mode
))
807 struct active_dir_ent
*old_ent
;
808 struct active_dir_ent
*new_ent
;
810 /* If there is already a directory in the map with the same inum,
811 then there's *probably* a directory cycle. This test can get
812 a false positive if two directories have the same inode number
813 but different device numbers, and one directory contains the
814 other. But since people don't often try to delete hierarchies
815 containing mount points, and when they do, duplicate inode
816 numbers are not that likely, this isn't worth detecting. */
818 new_ent
= make_active_dir_ent (fs
->inum
, current_depth ());
819 if (hash_lookup (active_dir_map
, new_ent
))
822 WARNING: Circular directory structure.\n\
823 This almost certainly means that you have a corrupted file system.\n\
824 NOTIFY YOUR SYSTEM MANAGER.\n\
825 The following two directories have the same inode number:\n"));
826 /* FIXME: test this!! */
827 print_nth_dir (stderr
, current_depth ());
828 fputc ('\n', stderr
);
829 print_nth_dir (stderr
, old_ent
->depth
);
830 fputc ('\n', stderr
);
837 error (0, 0, _("continue? "));
844 /* Put this directory in the active_dir_map. */
845 if (! hash_insert (active_dir_map
, new_ent
))
846 error (1, 0, _("virtual memory exhausted"));
850 if (!S_ISDIR (filetype_mode
) || x
->unlink_dirs
)
852 return remove_file (fs
, x
);
856 int need_save_cwd
= user_specified_name
;
857 enum RM_status status
;
860 need_save_cwd
= (strchr (fs
->filename
, '/') != NULL
);
862 status
= remove_dir (fs
, need_save_cwd
, x
);
864 #ifdef ENABLE_CYCLE_CHECK
866 struct active_dir_ent tmp
;
867 struct active_dir_ent
*old_ent
;
869 /* Remove this directory from the active_dir_map. */
871 old_ent
= hash_delete (active_dir_map
, &tmp
);
872 assert (old_ent
!= NULL
);
884 /* Initialize dir-stack obstacks. */
885 obstack_init (&dir_stack
);
886 obstack_init (&len_stack
);
888 #ifdef ENABLE_CYCLE_CHECK
889 active_dir_map
= hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY
, NULL
,
891 hash_compare_active_dir_ents
, free
);
898 #ifdef ENABLE_CYCLE_CHECK
899 hash_free (active_dir_map
);
902 obstack_free (&dir_stack
, NULL
);
903 obstack_free (&len_stack
, NULL
);