1 /* `rm' file deletion utility for GNU.
2 Copyright (C) 1988, 1990, 1991, 1994, 1995 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
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Written by Paul Rubin, David MacKenzie, and Richard Stallman. */
23 #include <sys/types.h>
27 #include "safe-lstat.h"
30 #ifdef D_INO_IN_DIRENT
31 #define D_INO(dp) ((dp)->d_ino)
33 /* POSIX.1 doesn't have inodes, so fake them to avoid lots of ifdefs. */
43 void strip_trailing_slashes ();
45 static int clear_directory ();
46 static int duplicate_entry ();
47 static int remove_dir ();
48 static int remove_file ();
52 /* Name this program was run with. */
55 /* Path of file now being processed; extended as necessary. */
56 static char *pathname;
58 /* Number of bytes currently allocated for `pathname';
59 made larger when necessary, but never smaller. */
62 /* If nonzero, display the name of each file removed. */
65 /* If nonzero, ignore nonexistant files. */
66 static int ignore_missing_files;
68 /* If nonzero, recursively remove directories. */
71 /* If nonzero, query the user about whether to remove each file. */
72 static int interactive;
74 /* If nonzero, remove directories with unlink instead of rmdir, and don't
75 require a directory to be empty before trying to unlink it.
76 Only works for the super-user. */
77 static int unlink_dirs;
79 /* If nonzero, stdin is a tty. */
82 /* If non-zero, display usage information and exit. */
85 /* If non-zero, print the version on standard output and exit. */
86 static int show_version;
88 static struct option const long_opts[] =
90 {"directory", no_argument, &unlink_dirs, 1},
91 {"force", no_argument, NULL, 'f'},
92 {"interactive", no_argument, NULL, 'i'},
93 {"recursive", no_argument, &recursive, 1},
94 {"verbose", no_argument, &verbose, 1},
95 {"help", no_argument, &show_help, 1},
96 {"version", no_argument, &show_version, 1},
108 verbose = ignore_missing_files = recursive = interactive
111 pathname = xmalloc (pnsize);
112 program_name = argv[0];
114 while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF)
118 case 0: /* Long option. */
125 ignore_missing_files = 1;
129 ignore_missing_files = 0;
145 printf ("%s\n", version_string);
154 if (ignore_missing_files)
158 error (0, 0, "too few arguments");
163 stdin_tty = isatty (STDIN_FILENO);
165 for (; optind < argc; optind++)
169 /* Stripping slashes is harmless for rmdir;
170 if the arg is not a directory, it will fail with ENOTDIR. */
171 strip_trailing_slashes (argv[optind]);
172 len = strlen (argv[optind]);
173 if (len + 1 > pnsize)
176 pnsize = 2 * (len + 1);
177 pathname = xmalloc (pnsize);
179 strcpy (pathname, argv[optind]);
186 /* Remove file or directory `pathname' after checking appropriate things.
187 Return 0 if `pathname' is removed, 1 if not. */
192 struct stat path_stats;
193 char *base = basename (pathname);
195 if (base[0] == '.' && (base[1] == '\0'
196 || (base[1] == '.' && base[2] == '\0')))
198 error (0, 0, "cannot remove `.' or `..'");
202 if (safe_lstat (pathname, &path_stats))
204 if (errno == ENOENT && ignore_missing_files)
206 error (0, errno, "%s", pathname);
210 if (S_ISDIR (path_stats.st_mode) && !unlink_dirs)
211 return remove_dir (&path_stats);
213 return remove_file (&path_stats);
216 /* Query the user if appropriate, and if ok try to remove the
217 non-directory `pathname', which STATP contains info about.
218 Return 0 if `pathname' is removed, 1 if not. */
224 if (!ignore_missing_files && (interactive || stdin_tty)
225 && eaccess_stat (statp, W_OK, pathname)
227 && !S_ISLNK (statp->st_mode)
231 fprintf (stderr, "%s: remove %s`%s', overriding mode %04o? ",
233 S_ISDIR (statp->st_mode) ? "directory " : "",
235 (unsigned int) (statp->st_mode & 07777));
239 else if (interactive)
241 fprintf (stderr, "%s: remove %s`%s'? ", program_name,
242 S_ISDIR (statp->st_mode) ? "directory " : "",
249 printf ("%s\n", pathname);
251 if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
253 error (0, errno, "%s", pathname);
259 /* If not in recursive mode, print an error message and return 1.
260 Otherwise, query the user if appropriate, then try to recursively
261 remove directory `pathname', which STATP contains info about.
262 Return 0 if `pathname' is removed, 1 if not. */
272 error (0, 0, "%s: is a directory", pathname);
276 if (!ignore_missing_files && (interactive || stdin_tty)
277 && eaccess_stat (statp, W_OK, pathname))
280 "%s: descend directory `%s', overriding mode %04o? ",
281 program_name, pathname,
282 (unsigned int) (statp->st_mode & 07777));
286 else if (interactive)
288 fprintf (stderr, "%s: descend directory `%s'? ",
289 program_name, pathname);
295 printf ("%s\n", pathname);
297 err = clear_directory (statp);
302 fprintf (stderr, "%s: remove directory `%s' (might be nonempty)? ",
303 program_name, pathname);
305 fprintf (stderr, "%s: remove directory `%s'? ",
306 program_name, pathname);
311 if (rmdir (pathname) && (errno != ENOENT || !ignore_missing_files))
313 error (0, errno, "%s", pathname);
319 /* An element in a stack of pointers into `pathname'.
320 `pathp' points to where in `pathname' the terminating '\0' goes
321 for this level's directory name. */
324 struct pathstack *next;
329 /* Linked list of pathnames of directories in progress in recursive rm.
330 The entries actually contain pointers into `pathname'.
331 `pathstack' is the current deepest level. */
332 static struct pathstack *pathstack = NULL;
334 /* Read directory `pathname' and remove all of its entries,
335 avoiding use of chdir.
336 On entry, STATP points to the results of stat on `pathname'.
337 Return 0 for success, error count for failure.
338 Upon return, `pathname' will have the same contents as before,
339 but its address might be different; in that case, `pnsize' will
340 be larger, as well. */
343 clear_directory (statp)
348 char *name_space; /* Copy of directory's filenames. */
349 char *namep; /* Current entry in `name_space'. */
350 unsigned name_size; /* Bytes allocated for `name_space'. */
351 int name_length; /* Length of filename in `namep' plus '\0'. */
352 int pathname_length; /* Length of `pathname'. */
353 ino_t *inode_space; /* Copy of directory's inodes. */
354 ino_t *inodep; /* Current entry in `inode_space'. */
355 unsigned n_inodes_allocated; /* There is space for this many inodes
357 int err = 0; /* Return status. */
358 struct pathstack pathframe; /* New top of stack. */
359 struct pathstack *pp; /* Temporary. */
361 name_size = statp->st_size;
362 name_space = (char *) xmalloc (name_size);
364 n_inodes_allocated = (statp->st_size + sizeof (ino_t) - 1) / sizeof (ino_t);
365 inode_space = (ino_t *) xmalloc (n_inodes_allocated * sizeof (ino_t));
370 inodep = inode_space;
373 dirp = opendir (pathname);
376 if (errno != ENOENT || !ignore_missing_files)
378 error (0, errno, "%s", pathname);
386 while ((dp = readdir (dirp)) != NULL)
388 /* Skip "." and ".." (some NFS filesystems' directories lack them). */
389 if (dp->d_name[0] != '.'
390 || (dp->d_name[1] != '\0'
391 && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
393 unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
395 if (size_needed > name_size)
397 char *new_name_space;
399 while (size_needed > name_size)
402 new_name_space = xrealloc (name_space, name_size);
403 namep += new_name_space - name_space;
404 name_space = new_name_space;
406 namep = stpcpy (namep, dp->d_name) + 1;
408 if (inodep == inode_space + n_inodes_allocated)
410 ino_t *new_inode_space;
412 n_inodes_allocated += 1024;
413 new_inode_space = (ino_t *) xrealloc (inode_space,
414 n_inodes_allocated * sizeof (ino_t));
415 inodep += new_inode_space - inode_space;
416 inode_space = new_inode_space;
418 *inodep++ = D_INO (dp);
424 error (0, errno, "%s", pathname);
428 pathname_length = strlen (pathname);
430 for (namep = name_space, inodep = inode_space; *namep != '\0';
431 namep += name_length, inodep++)
433 name_length = strlen (namep) + 1;
435 /* Satisfy GNU requirement that filenames can be arbitrarily long. */
436 if (pathname_length + 1 + name_length > pnsize)
440 pnsize = (pathname_length + 1 + name_length) * 2;
441 new_pathname = xrealloc (pathname, pnsize);
442 /* Update the all the pointers in the stack to use the new area. */
443 for (pp = pathstack; pp != NULL; pp = pp->next)
444 pp->pathp += new_pathname - pathname;
445 pathname = new_pathname;
448 /* Add a new frame to the top of the path stack. */
449 pathframe.pathp = pathname + pathname_length;
450 pathframe.inum = *inodep;
451 pathframe.next = pathstack;
452 pathstack = &pathframe;
454 /* Append '/' and the filename to current pathname, take care of the
455 file (which could result in recursive calls), and take the filename
458 *pathstack->pathp = '/';
459 strcpy (pathstack->pathp + 1, namep);
461 /* If the i-number has already appeared, there's an error. */
462 if (duplicate_entry (pathstack->next, pathstack->inum))
467 *pathstack->pathp = '\0';
468 pathstack = pathstack->next; /* Pop the stack. */
471 /* Keep trying while there are still files to remove. */
472 while (namep > name_space && err == 0);
479 /* If STACK does not already have an entry with the same i-number as INUM,
480 return 0. Otherwise, ask the user whether to continue;
481 if yes, return 1, and if no, exit.
482 This assumes that no one tries to remove filesystem mount points;
483 doing so could cause duplication of i-numbers that would not indicate
484 a corrupted file system. */
487 duplicate_entry (stack, inum)
488 struct pathstack *stack;
491 #ifndef _POSIX_SOURCE
494 for (p = stack; p != NULL; p = p->next)
499 %s: WARNING: Circular directory structure.\n\
500 This almost certainly means that you have a corrupted file system.\n\
501 NOTIFY YOUR SYSTEM MANAGER.\n\
504 is the same file as\n", program_name, pathname);
505 *p->pathp = '\0'; /* Truncate pathname. */
506 fprintf (stderr, "%s\n", pathname);
507 *p->pathp = '/'; /* Put it back. */
510 fprintf (stderr, "%s: continue? ", program_name);
528 fprintf (stderr, "Try `%s --help' for more information.\n",
532 printf ("Usage: %s [OPTION]... PATH...\n", program_name);
535 -d, --directory unlink directory, even if non-empty (super-user only)\n\
536 -f, --force ignore nonexistent files, never prompt\n\
537 -i, --interactive prompt before any removal\n\
538 -v, --verbose explain what is being done\n\
539 -r, -R, --recursive remove the contents of directories recursively\n\
540 --help display this help and exit\n\
541 --version output version information and exit\n");