1 /* remove.c -- core functions for removing files and directories
2 Copyright (C) 88, 90, 91, 94, 95, 96, 97, 1998 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;
43 #define obstack_chunk_alloc malloc
44 #define obstack_chunk_free free
47 # if defined (__GNUC__) || __STDC__
48 # define PARAMS(args) args
50 # define PARAMS(args) ()
54 #ifdef D_INO_IN_DIRENT
55 # define D_INO(dp) ((dp)->d_ino)
56 # define ENABLE_CYCLE_CHECK
58 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
62 #if !defined (S_ISLNK)
63 # define S_ISLNK(Mode) 0
66 #define DOT_OR_DOTDOT(Basename) \
67 (Basename[0] == '.' && (Basename[1] == '\0' \
68 || (Basename[1] == '.' && Basename[2] == '\0')))
71 # define ASSIGN_STRDUPA(DEST, S) \
72 do { DEST = strdupa(S); } while (0)
74 # define ASSIGN_STRDUPA(DEST, S) \
77 const char *s_ = (S); \
78 size_t len_ = strlen (s_) + 1; \
79 char *tmp_dest_ = (char *) alloca (len_); \
80 DEST = memcpy (tmp_dest_, (s_), len_); \
85 /* Initial capacity of per-directory hash table of entries that have
86 been processed but not been deleted. */
87 #define HT_INITIAL_CAPACITY 13
89 /* Initial capacity of the active directory hash table. This table will
90 be resized only for hierarchies more than about 45 levels deep. */
91 #define ACTIVE_DIR_INITIAL_CAPACITY 53
97 extern char *program_name
;
99 /* state initialized by remove_init, freed by remove_fini */
101 /* An entry in the active_dir_map. */
102 struct active_dir_ent
108 /* The name of the directory (starting with and relative to a command
109 line argument) being processed. When a subdirectory is entered, a new
110 component is appended (pushed). When RM chdir's out of a directory,
111 the top component is removed (popped). This is used to form a full
112 file name when necessary. */
113 static struct obstack dir_stack
;
115 /* Stack of lengths of directory names (including trailing slash)
116 appended to dir_stack. We have to have a separate stack of lengths
117 (rather than just popping back to previous slash) because the first
118 element pushed onto the dir stack may contain slashes. */
119 static struct obstack len_stack
;
121 /* Set of `active' directories from the current command-line argument
122 to the level in the hierarchy at which files are being removed.
123 A directory is added to the active set when RM begins removing it
124 (or its entries), and it is removed from the set just after RM has
125 finished processing it.
127 This is actually a map (not a set), implemented with a hash table.
128 For each active directory, it maps the directory's inode number to the
129 depth of that directory relative to the root of the tree being deleted.
130 A directory specified on the command line has depth zero.
131 This construct is used to detect directory cycles so that RM can warn
132 about them rather than iterating endlessly. */
133 #ifdef ENABLE_CYCLE_CHECK
134 static struct hash_table
*active_dir_map
;
137 static inline unsigned int
140 return obstack_object_size (&len_stack
) / sizeof (size_t);
144 print_nth_dir (FILE *stream
, unsigned int depth
)
146 size_t *length
= (size_t *) obstack_base (&len_stack
);
147 char *dir_name
= (char *) obstack_base (&dir_stack
);
148 unsigned int sum
= 0;
151 assert (depth
< current_depth ());
153 for (i
= 0; i
<= depth
; i
++)
158 fwrite (dir_name
, 1, sum
, stream
);
161 static inline struct active_dir_ent
*
162 make_active_dir_ent (ino_t inum
, unsigned int depth
)
164 struct active_dir_ent
*ent
;
165 ent
= (struct active_dir_ent
*) xmalloc (sizeof *ent
);
172 hash_active_dir_ent (void const *x
, unsigned int table_size
)
174 struct active_dir_ent
const *ade
= x
;
175 return ade
->inum
% table_size
;
179 hash_compare_active_dir_ents (void const *x
, void const *y
)
181 struct active_dir_ent
const *a
= x
;
182 struct active_dir_ent
const *b
= y
;
183 return a
->inum
== b
->inum
;
186 /* A hash function for null-terminated char* strings using
187 the method described in Aho, Sethi, & Ullman, p 436. */
190 hash_pjw (const void *x
, unsigned int tablesize
)
199 if ((g
= h
& (unsigned int) 0xf0000000) != 0)
200 h
= (h
^ (g
>> 24)) ^ g
;
203 return (h
% tablesize
);
207 hash_compare_strings (void const *x
, void const *y
)
209 return STREQ (x
, y
) ? true : false;
213 push_dir (const char *dir_name
)
217 len
= strlen (dir_name
);
219 /* Append the string onto the stack. */
220 obstack_grow (&dir_stack
, dir_name
, len
);
222 /* Append a trailing slash. */
223 obstack_1grow (&dir_stack
, '/');
225 /* Add one for the slash. */
228 /* Push the length (including slash) onto its stack. */
229 obstack_grow (&len_stack
, &len
, sizeof (len
));
235 int n_lengths
= obstack_object_size (&len_stack
) / sizeof (size_t);
236 size_t *length
= (size_t *) obstack_base (&len_stack
);
239 assert (n_lengths
> 0);
240 top_len
= length
[n_lengths
- 1];
241 assert (top_len
>= 2);
243 /* Pop off the specified length of pathname. */
244 assert (obstack_object_size (&dir_stack
) >= top_len
);
245 obstack_blank (&dir_stack
, -top_len
);
247 /* Pop the length stack, too. */
248 assert (obstack_object_size (&len_stack
) >= sizeof (size_t));
249 obstack_blank (&len_stack
, -(sizeof (size_t)));
252 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
253 buffer, DST, so that the last source byte is at the end of the destination
254 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
255 Set *RESULT to point to the beginning of (the portion of) the source data
256 in DST. Return the number of bytes remaining in the destination buffer. */
259 right_justify (char *dst
, size_t dst_len
, const char *src
, size_t src_len
,
260 char **result
, int *truncated
)
265 if (src_len
<= dst_len
)
268 dp
= dst
+ (dst_len
- src_len
);
273 sp
= src
+ (src_len
- dst_len
);
279 *result
= memcpy (dp
, sp
, src_len
);
280 return dst_len
- src_len
;
283 /* Using the global directory name obstack, create the full path to FILENAME.
284 Return it in sometimes-realloc'd space that should not be freed by the
285 caller. Realloc as necessary. If realloc fails, use a static buffer
286 and put as long a suffix in that buffer as possible. */
289 full_filename (const char *filename
)
291 static char *buf
= NULL
;
292 static size_t n_allocated
= 0;
294 int dir_len
= obstack_object_size (&dir_stack
);
295 char *dir_name
= (char *) obstack_base (&dir_stack
);
296 size_t n_bytes_needed
;
299 filename_len
= strlen (filename
);
300 n_bytes_needed
= dir_len
+ filename_len
+ 1;
302 if (n_bytes_needed
> n_allocated
)
304 /* This code requires that realloc accept NULL as the first arg.
305 This function must not use xrealloc. Otherwise, an out-of-memory
306 error involving a file name to be expanded here wouldn't ever
307 be issued. Use realloc and fall back on using a static buffer
308 if memory allocation fails. */
309 buf
= realloc (buf
, n_bytes_needed
);
310 n_allocated
= n_bytes_needed
;
314 #define SBUF_SIZE 512
315 #define ELLIPSES_PREFIX "[...]"
316 static char static_buf
[SBUF_SIZE
];
321 len
= right_justify (static_buf
, SBUF_SIZE
, filename
,
322 filename_len
+ 1, &p
, &truncated
);
323 right_justify (static_buf
, len
, dir_name
, dir_len
, &p
, &truncated
);
326 memcpy (static_buf
, ELLIPSES_PREFIX
,
327 sizeof (ELLIPSES_PREFIX
) - 1);
333 /* Copy directory part, including trailing slash, and then
334 append the filename part, including a trailing zero byte. */
335 memcpy (mempcpy (buf
, dir_name
, dir_len
), filename
, filename_len
+ 1);
337 assert (strlen (buf
) + 1 == n_bytes_needed
);
343 fspec_init_file (struct File_spec
*fs
, const char *filename
)
345 fs
->filename
= (char *) filename
;
346 fs
->have_full_mode
= 0;
347 fs
->have_filetype_mode
= 0;
351 fspec_init_dp (struct File_spec
*fs
, struct dirent
*dp
)
353 fs
->filename
= dp
->d_name
;
354 fs
->have_full_mode
= 0;
355 fs
->have_filetype_mode
= 0;
356 fs
->inum
= D_INO (dp
);
358 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
359 if (dp
->d_type
!= DT_UNKNOWN
)
361 fs
->have_filetype_mode
= 1;
362 fs
->mode
= DTTOIF (dp
->d_type
);
368 fspec_get_full_mode (struct File_spec
*fs
, mode_t
*full_mode
)
370 struct stat stat_buf
;
372 if (fs
->have_full_mode
)
374 *full_mode
= fs
->mode
;
378 if (lstat (fs
->filename
, &stat_buf
))
381 fs
->have_full_mode
= 1;
382 fs
->have_filetype_mode
= 1;
383 fs
->mode
= stat_buf
.st_mode
;
384 fs
->inum
= stat_buf
.st_ino
;
386 *full_mode
= stat_buf
.st_mode
;
391 fspec_get_filetype_mode (struct File_spec
*fs
, mode_t
*filetype_mode
)
395 if (fs
->have_filetype_mode
)
397 *filetype_mode
= fs
->mode
;
402 fail
= fspec_get_full_mode (fs
, filetype_mode
);
409 fspec_filetype_mode (const struct File_spec
*fs
)
411 assert (fs
->have_filetype_mode
);
416 same_file (const char *file_1
, const char *file_2
)
418 struct stat sb1
, sb2
;
419 return (lstat (file_1
, &sb1
) == 0
420 && lstat (file_2
, &sb2
) == 0
421 && SAME_INODE (sb1
, sb2
));
425 /* Recursively remove all of the entries in the current directory.
426 Return an indication of the success of the operation. */
429 remove_cwd_entries (const struct rm_options
*x
)
431 /* NOTE: this is static. */
432 static DIR *dirp
= NULL
;
434 /* NULL or a malloc'd and initialized hash table of entries in the
435 current directory that have been processed but not removed --
436 due either to an error or to an interactive `no' response. */
437 struct hash_table
*ht
= NULL
;
439 /* FIXME: describe */
440 static struct obstack entry_name_pool
;
441 static int first_call
= 1;
443 enum RM_status status
= RM_OK
;
448 obstack_init (&entry_name_pool
);
455 /* FIXME-someday: but this is actually the previously opened dir. */
456 error (0, errno
, "%s", full_filename ("."));
464 /* FIXME: why do this? */
467 dirp
= opendir (".");
470 if (errno
!= ENOENT
|| !x
->ignore_missing_files
)
472 error (0, errno
, "%s", full_filename ("."));
482 enum RM_status tmp_status
;
485 /* FILE should be skipped if it is `.' or `..', or if it is in
486 the table, HT, of entries we've already processed. */
487 #define SKIPPABLE(Ht, File) \
488 (DOT_OR_DOTDOT(File) || (Ht && hash_lookup (Ht, File)))
490 /* FIXME: use readdir_r directly into an obstack to avoid
491 the obstack_copy0 below --
492 Suggestion from Uli. Be careful -- there are different
493 prototypes on e.g. Solaris.
495 Do something like this:
496 #define NAME_MAX_FOR(Parent_dir) pathconf ((Parent_dir),
498 dp = obstack_alloc (sizeof (struct dirent)
499 + NAME_MAX_FOR (".") + 1);
500 fail = xreaddir (dirp, dp);
501 where xreaddir is ...
503 But what about systems like the hurd where NAME_MAX is supposed
504 to be effectively unlimited. We don't want to have to allocate
505 a huge buffer to accommodate maximum possible entry name. */
509 #if ! HAVE_WORKING_READDIR
512 /* Since we have probably modified the directory since it
513 was opened, readdir returning NULL does not necessarily
514 mean we have read the last entry. Rewind it and check
515 again. This happens on SunOS4.1.4 with 254 or more files
518 while ((dp
= readdir (dirp
)) && SKIPPABLE (ht
, dp
->d_name
))
528 if (SKIPPABLE (ht
, dp
->d_name
))
531 fspec_init_dp (&fs
, dp
);
533 /* Save a copy of the name of this entry, in case we have
534 to add it to the set of unremoved entries below. */
535 entry_name
= obstack_copy0 (&entry_name_pool
,
536 dp
->d_name
, NLENGTH (dp
));
538 /* CAUTION: after this call to rm, DP may not be valid --
539 it may have been freed due to a close in a recursive call
540 (through rm and remove_dir) to this function. */
541 tmp_status
= rm (&fs
, 0, x
);
544 if (tmp_status
> status
)
546 assert (VALID_STATUS (status
));
548 /* If this entry was not removed (due either to an error or to
549 an interactive `no' response), record it in the hash table so
550 we don't consider it again if we reopen this directory later. */
557 ht
= hash_initialize (HT_INITIAL_CAPACITY
, hash_pjw
,
558 hash_compare_strings
, NULL
);
560 error (1, 0, _("virtual memory exhausted"));
562 hash_insert (ht
, entry_name
, &done
);
564 error (1, 0, _("virtual memory exhausted"));
568 /* This entry was not saved in the hash table. Free it. */
569 obstack_free (&entry_name_pool
, entry_name
);
576 while (dirp
== NULL
);
580 error (0, errno
, "%s", full_filename ("."));
590 if (obstack_object_size (&entry_name_pool
) > 0)
591 obstack_free (&entry_name_pool
, obstack_base (&entry_name_pool
));
596 /* Query the user if appropriate, and if ok try to remove the
597 file or directory specified by FS. Return RM_OK if it is removed,
598 and RM_ERROR or RM_USER_DECLINED if not. */
600 static enum RM_status
601 remove_file (struct File_spec
*fs
, const struct rm_options
*x
)
604 char *pathname
= fs
->filename
;
606 if (!x
->ignore_missing_files
&& (x
->interactive
|| x
->stdin_tty
)
607 && euidaccess (pathname
, W_OK
))
609 if (!S_ISLNK (fspec_filetype_mode (fs
)))
612 (S_ISDIR (fspec_filetype_mode (fs
))
613 ? _("%s: remove write-protected directory `%s'? ")
614 : _("%s: remove write-protected file `%s'? ")),
615 program_name
, full_filename (pathname
));
617 return RM_USER_DECLINED
;
623 if (!asked
&& x
->interactive
)
625 /* FIXME: use a variant of error (instead of fprintf) that doesn't
626 append a newline. Then we won't have to declare program_name in
629 (S_ISDIR (fspec_filetype_mode (fs
))
630 ? _("%s: remove directory `%s'? ")
631 : _("%s: remove `%s'? ")),
632 program_name
, full_filename (pathname
));
634 return RM_USER_DECLINED
;
638 printf (_("removing %s\n"), full_filename (pathname
));
640 if (unlink (pathname
) && (errno
!= ENOENT
|| !x
->ignore_missing_files
))
642 error (0, errno
, _("cannot unlink `%s'"), full_filename (pathname
));
648 /* If not in recursive mode, print an error message and return RM_ERROR.
649 Otherwise, query the user if appropriate, then try to recursively
650 remove the directory specified by FS. Return RM_OK if it is removed,
651 and RM_ERROR or RM_USER_DECLINED if not.
652 FIXME: describe need_save_cwd parameter. */
654 static enum RM_status
655 remove_dir (struct File_spec
*fs
, int need_save_cwd
, const struct rm_options
*x
)
657 enum RM_status status
;
658 struct saved_cwd cwd
;
659 char *dir_name
= fs
->filename
;
660 const char *fmt
= NULL
;
664 error (0, 0, _("%s: is a directory"), full_filename (dir_name
));
668 if (!x
->ignore_missing_files
&& (x
->interactive
|| x
->stdin_tty
)
669 && euidaccess (dir_name
, W_OK
))
671 fmt
= _("%s: directory `%s' is write protected; descend into it anyway? ");
673 else if (x
->interactive
)
675 fmt
= _("%s: descend into directory `%s'? ");
680 fprintf (stderr
, fmt
, program_name
, full_filename (dir_name
));
682 return RM_USER_DECLINED
;
686 printf (_("removing all entries of directory %s\n"),
687 full_filename (dir_name
));
689 /* Save cwd if needed. */
690 if (need_save_cwd
&& save_cwd (&cwd
))
693 /* Make target directory the current one. */
694 if (chdir (dir_name
) < 0)
696 error (0, errno
, _("cannot change to directory %s"),
697 full_filename (dir_name
));
705 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
706 it because it is just a pointer to the dir entry's d_name field, and
707 remove_cwd_entries may close the directory. */
708 ASSIGN_STRDUPA (dir_name
, dir_name
);
710 status
= remove_cwd_entries (x
);
717 if (restore_cwd (&cwd
, NULL
, NULL
))
724 else if (chdir ("..") < 0)
726 error (0, errno
, _("cannot change back to directory %s via `..'"),
727 full_filename (dir_name
));
733 fprintf (stderr
, _("%s: remove directory `%s'%s? "),
735 full_filename (dir_name
),
736 (status
!= RM_OK
? _(" (might be nonempty)") : ""));
739 return RM_USER_DECLINED
;
744 printf (_("removing the directory itself: %s\n"), full_filename (dir_name
));
746 if (rmdir (dir_name
) && (errno
!= ENOENT
|| !x
->ignore_missing_files
))
748 int saved_errno
= errno
;
753 /* See if rmdir just failed because DIR_NAME is the current directory.
754 If so, give a better diagnostic than `rm: cannot remove directory
755 `...': Invalid argument' */
756 if (errno
== EINVAL
&& same_file (".", dir_name
))
758 error (0, 0, _("cannot remove current directory `%s'"),
759 full_filename (dir_name
));
763 error (0, saved_errno
, _("cannot remove directory `%s'"),
764 full_filename (dir_name
));
772 /* Remove the file or directory specified by FS after checking appropriate
773 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
774 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
775 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
776 name (and hence must specify a file in the current directory). */
779 rm (struct File_spec
*fs
, int user_specified_name
, const struct rm_options
*x
)
781 mode_t filetype_mode
;
783 if (user_specified_name
)
785 char *base
= base_name (fs
->filename
);
787 if (DOT_OR_DOTDOT (base
))
789 error (0, 0, _("cannot remove `.' or `..'"));
794 if (fspec_get_filetype_mode (fs
, &filetype_mode
))
796 if (x
->ignore_missing_files
&& errno
== ENOENT
)
799 error (0, errno
, _("cannot remove `%s'"), full_filename (fs
->filename
));
803 #ifdef ENABLE_CYCLE_CHECK
804 if (S_ISDIR (filetype_mode
))
807 struct active_dir_ent
*old_ent
;
809 /* Insert this directory in the active_dir_map.
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. */
817 old_ent
= hash_insert (active_dir_map
,
818 make_active_dir_ent (fs
->inum
, current_depth ()),
821 error (1, 0, _("virtual memory exhausted"));
826 WARNING: Circular directory structure.\n\
827 This almost certainly means that you have a corrupted file system.\n\
828 NOTIFY YOUR SYSTEM MANAGER.\n\
829 The following two directories have the same inode number:\n"));
830 /* FIXME: test this!! */
831 print_nth_dir (stderr
, current_depth ());
832 fputc ('\n', stderr
);
833 print_nth_dir (stderr
, old_ent
->depth
);
834 fputc ('\n', stderr
);
841 error (0, 0, _("continue? "));
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
,
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
);