(add_tabstop): Give correct size when reallocating tab_list buffer.
[platform/upstream/coreutils.git] / src / rm.c
1 /* `rm' file deletion utility for GNU.
2    Copyright (C) 1988, 1990, 1991, 1994, 1995 Free Software Foundation, Inc.
3
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)
7    any later version.
8
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.
13
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.  */
17
18 /* Written by Paul Rubin, David MacKenzie, and Richard Stallman. */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24
25 #include "system.h"
26 #include "version.h"
27 #include "safe-lstat.h"
28 #include "error.h"
29
30 #ifdef D_INO_IN_DIRENT
31 #define D_INO(dp) ((dp)->d_ino)
32 #else
33 /* POSIX.1 doesn't have inodes, so fake them to avoid lots of ifdefs. */
34 #define D_INO(dp) 1
35 #endif
36
37 char *basename ();
38 char *stpcpy ();
39 char *xmalloc ();
40 char *xrealloc ();
41 int eaccess_stat ();
42 int yesno ();
43 void strip_trailing_slashes ();
44
45 static int clear_directory ();
46 static int duplicate_entry ();
47 static int remove_dir ();
48 static int remove_file ();
49 static int rm ();
50 static void usage ();
51
52 /* Name this program was run with.  */
53 char *program_name;
54
55 /* Path of file now being processed; extended as necessary. */
56 static char *pathname;
57
58 /* Number of bytes currently allocated for `pathname';
59    made larger when necessary, but never smaller.  */
60 static int pnsize;
61
62 /* If nonzero, display the name of each file removed. */
63 static int verbose;
64
65 /* If nonzero, ignore nonexistant files. */
66 static int ignore_missing_files;
67
68 /* If nonzero, recursively remove directories. */
69 static int recursive;
70
71 /* If nonzero, query the user about whether to remove each file. */
72 static int interactive;
73
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;
78
79 /* If nonzero, stdin is a tty. */
80 static int stdin_tty;
81
82 /* If non-zero, display usage information and exit.  */
83 static int show_help;
84
85 /* If non-zero, print the version on standard output and exit.  */
86 static int show_version;
87
88 static struct option const long_opts[] =
89 {
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},
97   {NULL, 0, NULL, 0}
98 };
99
100 void
101 main (argc, argv)
102      int argc;
103      char **argv;
104 {
105   int err = 0;
106   int c;
107
108   verbose = ignore_missing_files = recursive = interactive
109     = unlink_dirs = 0;
110   pnsize = 256;
111   pathname = xmalloc (pnsize);
112   program_name = argv[0];
113
114   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF)
115     {
116       switch (c)
117         {
118         case 0:         /* Long option. */
119           break;
120         case 'd':
121           unlink_dirs = 1;
122           break;
123         case 'f':
124           interactive = 0;
125           ignore_missing_files = 1;
126           break;
127         case 'i':
128           interactive = 1;
129           ignore_missing_files = 0;
130           break;
131         case 'r':
132         case 'R':
133           recursive = 1;
134           break;
135         case 'v':
136           verbose = 1;
137           break;
138         default:
139           usage (1);
140         }
141     }
142
143   if (show_version)
144     {
145       printf ("%s\n", version_string);
146       exit (0);
147     }
148
149   if (show_help)
150     usage (0);
151
152   if (optind == argc)
153     {
154       if (ignore_missing_files)
155         exit (0);
156       else
157         {
158           error (0, 0, "too few arguments");
159           usage (1);
160         }
161     }
162
163   stdin_tty = isatty (STDIN_FILENO);
164
165   for (; optind < argc; optind++)
166     {
167       int len;
168
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)
174         {
175           free (pathname);
176           pnsize = 2 * (len + 1);
177           pathname = xmalloc (pnsize);
178         }
179       strcpy (pathname, argv[optind]);
180       err += rm ();
181     }
182
183   exit (err > 0);
184 }
185
186 /* Remove file or directory `pathname' after checking appropriate things.
187    Return 0 if `pathname' is removed, 1 if not. */
188
189 static int
190 rm ()
191 {
192   struct stat path_stats;
193   char *base = basename (pathname);
194
195   if (base[0] == '.' && (base[1] == '\0'
196                              || (base[1] == '.' && base[2] == '\0')))
197     {
198       error (0, 0, "cannot remove `.' or `..'");
199       return 1;
200     }
201
202   if (safe_lstat (pathname, &path_stats))
203     {
204       if (errno == ENOENT && ignore_missing_files)
205         return 0;
206       error (0, errno, "%s", pathname);
207       return 1;
208     }
209
210   if (S_ISDIR (path_stats.st_mode) && !unlink_dirs)
211     return remove_dir (&path_stats);
212   else
213     return remove_file (&path_stats);
214 }
215
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. */
219
220 static int
221 remove_file (statp)
222      struct stat *statp;
223 {
224   if (!ignore_missing_files && (interactive || stdin_tty)
225       && eaccess_stat (statp, W_OK, pathname)
226 #ifdef S_ISLNK
227       && !S_ISLNK (statp->st_mode)
228 #endif
229       )
230     {
231       fprintf (stderr, "%s: remove %s`%s', overriding mode %04o? ",
232                program_name,
233                S_ISDIR (statp->st_mode) ? "directory " : "",
234                pathname,
235                (unsigned int) (statp->st_mode & 07777));
236       if (!yesno ())
237         return 1;
238     }
239   else if (interactive)
240     {
241       fprintf (stderr, "%s: remove %s`%s'? ", program_name,
242                S_ISDIR (statp->st_mode) ? "directory " : "",
243                pathname);
244       if (!yesno ())
245         return 1;
246     }
247
248   if (verbose)
249     printf ("%s\n", pathname);
250
251   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
252     {
253       error (0, errno, "%s", pathname);
254       return 1;
255     }
256   return 0;
257 }
258
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. */
263
264 static int
265 remove_dir (statp)
266      struct stat *statp;
267 {
268   int err;
269
270   if (!recursive)
271     {
272       error (0, 0, "%s: is a directory", pathname);
273       return 1;
274     }
275
276   if (!ignore_missing_files && (interactive || stdin_tty)
277       && eaccess_stat (statp, W_OK, pathname))
278     {
279       fprintf (stderr,
280                "%s: descend directory `%s', overriding mode %04o? ",
281                program_name, pathname,
282                (unsigned int) (statp->st_mode & 07777));
283       if (!yesno ())
284         return 1;
285     }
286   else if (interactive)
287     {
288       fprintf (stderr, "%s: descend directory `%s'? ",
289                program_name, pathname);
290       if (!yesno ())
291         return 1;
292     }
293
294   if (verbose)
295     printf ("%s\n", pathname);
296
297   err = clear_directory (statp);
298
299   if (interactive)
300     {
301       if (err)
302         fprintf (stderr, "%s: remove directory `%s' (might be nonempty)? ",
303                  program_name, pathname);
304       else
305         fprintf (stderr, "%s: remove directory `%s'? ",
306                  program_name, pathname);
307       if (!yesno ())
308         return 1;
309     }
310
311   if (rmdir (pathname) && (errno != ENOENT || !ignore_missing_files))
312     {
313       error (0, errno, "%s", pathname);
314       return 1;
315     }
316   return 0;
317 }
318
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. */
322 struct pathstack
323 {
324   struct pathstack *next;
325   char *pathp;
326   ino_t inum;
327 };
328
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;
333
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. */
341
342 static int
343 clear_directory (statp)
344      struct stat *statp;
345 {
346   DIR *dirp;
347   struct dirent *dp;
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
356                                           in `inode_space'. */
357   int err = 0;                  /* Return status. */
358   struct pathstack pathframe;   /* New top of stack. */
359   struct pathstack *pp;         /* Temporary. */
360
361   name_size = statp->st_size;
362   name_space = (char *) xmalloc (name_size);
363
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));
366
367   do
368     {
369       namep = name_space;
370       inodep = inode_space;
371
372       errno = 0;
373       dirp = opendir (pathname);
374       if (dirp == NULL)
375         {
376           if (errno != ENOENT || !ignore_missing_files)
377             {
378               error (0, errno, "%s", pathname);
379               err = 1;
380             }
381           free (name_space);
382           free (inode_space);
383           return err;
384         }
385
386       while ((dp = readdir (dirp)) != NULL)
387         {
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')))
392             {
393               unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
394
395               if (size_needed > name_size)
396                 {
397                   char *new_name_space;
398
399                   while (size_needed > name_size)
400                     name_size += 1024;
401
402                   new_name_space = xrealloc (name_space, name_size);
403                   namep += new_name_space - name_space;
404                   name_space = new_name_space;
405                 }
406               namep = stpcpy (namep, dp->d_name) + 1;
407
408               if (inodep == inode_space + n_inodes_allocated)
409                 {
410                   ino_t *new_inode_space;
411
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;
417                 }
418               *inodep++ = D_INO (dp);
419             }
420         }
421       *namep = '\0';
422       if (CLOSEDIR (dirp))
423         {
424           error (0, errno, "%s", pathname);
425           err = 1;
426         }
427
428       pathname_length = strlen (pathname);
429
430       for (namep = name_space, inodep = inode_space; *namep != '\0';
431            namep += name_length, inodep++)
432         {
433           name_length = strlen (namep) + 1;
434
435           /* Satisfy GNU requirement that filenames can be arbitrarily long. */
436           if (pathname_length + 1 + name_length > pnsize)
437             {
438               char *new_pathname;
439
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;
446             }
447
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;
453
454           /* Append '/' and the filename to current pathname, take care of the
455              file (which could result in recursive calls), and take the filename
456              back off. */
457
458           *pathstack->pathp = '/';
459           strcpy (pathstack->pathp + 1, namep);
460
461           /* If the i-number has already appeared, there's an error. */
462           if (duplicate_entry (pathstack->next, pathstack->inum))
463             err++;
464           else if (rm ())
465             err++;
466
467           *pathstack->pathp = '\0';
468           pathstack = pathstack->next;  /* Pop the stack. */
469         }
470     }
471   /* Keep trying while there are still files to remove. */
472   while (namep > name_space && err == 0);
473
474   free (name_space);
475   free (inode_space);
476   return err;
477 }
478
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. */
485
486 static int
487 duplicate_entry (stack, inum)
488      struct pathstack *stack;
489      ino_t inum;
490 {
491 #ifndef _POSIX_SOURCE
492   struct pathstack *p;
493
494   for (p = stack; p != NULL; p = p->next)
495     {
496       if (p->inum == inum)
497         {
498           fprintf (stderr, "\
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\
502 Cycle detected:\n\
503 %s\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. */
508           if (interactive)
509             {
510               fprintf (stderr, "%s: continue? ", program_name);
511               if (!yesno ())
512                 exit (1);
513               return 1;
514             }
515           else
516             exit (1);
517         }
518     }
519 #endif
520   return 0;
521 }
522
523 static void
524 usage (status)
525      int status;
526 {
527   if (status != 0)
528     fprintf (stderr, "Try `%s --help' for more information.\n",
529              program_name);
530   else
531     {
532       printf ("Usage: %s [OPTION]... PATH...\n", program_name);
533       printf ("\
534 \n\
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");
542     }
543   exit (status);
544 }