8cd9e6dc3a15470a6401dbffef505953bd681c90
[platform/upstream/coreutils.git] / src / rm.c
1 /* `rm' file deletion utility for GNU.
2    Copyright (C) 88, 90, 91, 94, 95, 1996 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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "error.h"
27
28 #ifdef D_INO_IN_DIRENT
29 # define D_INO(dp) ((dp)->d_ino)
30 #else
31 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
32 # define D_INO(dp) 1
33 #endif
34
35 /* An element in a stack of pointers into `pathname'.
36    `pathp' points to where in `pathname' the terminating '\0' goes
37    for this level's directory name.  */
38 struct pathstack
39 {
40   struct pathstack *next;
41   char *pathp;
42   ino_t inum;
43 };
44
45 char *basename ();
46 char *stpcpy ();
47 char *xmalloc ();
48 char *xrealloc ();
49 int euidaccess ();
50 int yesno ();
51 void strip_trailing_slashes ();
52
53 static int clear_directory __P ((struct stat *statp));
54 static int duplicate_entry __P ((struct pathstack *stack, ino_t inum));
55 static int remove_dir __P ((struct stat *statp));
56 static int remove_file __P ((struct stat *statp));
57 static int rm __P ((void));
58 static void usage __P ((int status));
59
60 /* Name this program was run with.  */
61 char *program_name;
62
63 /* Linked list of pathnames of directories in progress in recursive rm.
64    The entries actually contain pointers into `pathname'.
65    `pathstack' is the current deepest level.  */
66 static struct pathstack *pathstack = NULL;
67
68 /* Path of file now being processed; extended as necessary.  */
69 static char *pathname;
70
71 /* Number of bytes currently allocated for `pathname';
72    made larger when necessary, but never smaller.  */
73 static int pnsize;
74
75 /* If nonzero, display the name of each file removed.  */
76 static int verbose;
77
78 /* If nonzero, ignore nonexistant files.  */
79 static int ignore_missing_files;
80
81 /* If nonzero, recursively remove directories.  */
82 static int recursive;
83
84 /* If nonzero, query the user about whether to remove each file.  */
85 static int interactive;
86
87 /* If nonzero, remove directories with unlink instead of rmdir, and don't
88    require a directory to be empty before trying to unlink it.
89    Only works for the super-user.  */
90 static int unlink_dirs;
91
92 /* If nonzero, stdin is a tty.  */
93 static int stdin_tty;
94
95 /* If nonzero, display usage information and exit.  */
96 static int show_help;
97
98 /* If nonzero, print the version on standard output and exit.  */
99 static int show_version;
100
101 static struct option const long_opts[] =
102 {
103   {"directory", no_argument, &unlink_dirs, 1},
104   {"force", no_argument, NULL, 'f'},
105   {"interactive", no_argument, NULL, 'i'},
106   {"recursive", no_argument, &recursive, 1},
107   {"verbose", no_argument, &verbose, 1},
108   {"help", no_argument, &show_help, 1},
109   {"version", no_argument, &show_version, 1},
110   {NULL, 0, NULL, 0}
111 };
112
113 int
114 main (int argc, char **argv)
115 {
116   int err = 0;
117   int c;
118
119   program_name = argv[0];
120   setlocale (LC_ALL, "");
121   bindtextdomain (PACKAGE, LOCALEDIR);
122   textdomain (PACKAGE);
123
124   verbose = ignore_missing_files = recursive = interactive
125     = unlink_dirs = 0;
126   pnsize = 256;
127   pathname = xmalloc (pnsize);
128
129   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF)
130     {
131       switch (c)
132         {
133         case 0:         /* Long option.  */
134           break;
135         case 'd':
136           unlink_dirs = 1;
137           break;
138         case 'f':
139           interactive = 0;
140           ignore_missing_files = 1;
141           break;
142         case 'i':
143           interactive = 1;
144           ignore_missing_files = 0;
145           break;
146         case 'r':
147         case 'R':
148           recursive = 1;
149           break;
150         case 'v':
151           verbose = 1;
152           break;
153         default:
154           usage (1);
155         }
156     }
157
158   if (show_version)
159     {
160       printf ("rm - %s\n", PACKAGE_VERSION);
161       exit (0);
162     }
163
164   if (show_help)
165     usage (0);
166
167   if (optind == argc)
168     {
169       if (ignore_missing_files)
170         exit (0);
171       else
172         {
173           error (0, 0, _("too few arguments"));
174           usage (1);
175         }
176     }
177
178   stdin_tty = isatty (STDIN_FILENO);
179
180   for (; optind < argc; optind++)
181     {
182       int len;
183
184       /* Stripping slashes is harmless for rmdir;
185          if the arg is not a directory, it will fail with ENOTDIR.  */
186       strip_trailing_slashes (argv[optind]);
187       len = strlen (argv[optind]);
188       if (len + 1 > pnsize)
189         {
190           free (pathname);
191           pnsize = 2 * (len + 1);
192           pathname = xmalloc (pnsize);
193         }
194       strcpy (pathname, argv[optind]);
195       err += rm ();
196     }
197
198   exit (err > 0);
199 }
200
201 /* Remove file or directory `pathname' after checking appropriate things.
202    Return 0 if `pathname' is removed, 1 if not.  */
203
204 static int
205 rm (void)
206 {
207   struct stat path_stats;
208   char *base = basename (pathname);
209
210   if (base[0] == '.' && (base[1] == '\0'
211                              || (base[1] == '.' && base[2] == '\0')))
212     {
213       error (0, 0, _("cannot remove `.' or `..'"));
214       return 1;
215     }
216
217   if (lstat (pathname, &path_stats)
218       /* The following or-clause is solely for systems like SunOS 4.1.3
219          with (broken) lstat that interpret a zero-length file name
220          argument as something meaningful.  For such systems, manually
221          set errno to ENOENT.  */
222       || (pathname[0] == '\0' && (errno = ENOENT)))
223     {
224       if (errno == ENOENT && ignore_missing_files)
225         return 0;
226       error (0, errno, "%s", pathname);
227       return 1;
228     }
229
230   if (S_ISDIR (path_stats.st_mode) && !unlink_dirs)
231     return remove_dir (&path_stats);
232   else
233     return remove_file (&path_stats);
234 }
235
236 /* Query the user if appropriate, and if ok try to remove the
237    non-directory `pathname', which STATP contains info about.
238    Return 0 if `pathname' is removed, 1 if not.  */
239
240 static int
241 remove_file (struct stat *statp)
242 {
243   if (!ignore_missing_files && (interactive || stdin_tty)
244       && euidaccess (pathname, W_OK)
245 #ifdef S_ISLNK
246       && !S_ISLNK (statp->st_mode)
247 #endif
248       )
249     {
250       fprintf (stderr, _("%s: remove %s`%s', overriding mode %04o? "),
251                program_name,
252                S_ISDIR (statp->st_mode) ? _("directory ") : "",
253                pathname,
254                (unsigned int) (statp->st_mode & 07777));
255       if (!yesno ())
256         return 1;
257     }
258   else if (interactive)
259     {
260       fprintf (stderr, _("%s: remove %s`%s'? "), program_name,
261                S_ISDIR (statp->st_mode) ? _("directory ") : "",
262                pathname);
263       if (!yesno ())
264         return 1;
265     }
266
267   if (verbose)
268     printf ("%s\n", pathname);
269
270   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
271     {
272       error (0, errno, "%s", pathname);
273       return 1;
274     }
275   return 0;
276 }
277
278 /* If not in recursive mode, print an error message and return 1.
279    Otherwise, query the user if appropriate, then try to recursively
280    remove directory `pathname', which STATP contains info about.
281    Return 0 if `pathname' is removed, 1 if not.  */
282
283 static int
284 remove_dir (struct stat *statp)
285 {
286   int err;
287
288   if (!recursive)
289     {
290       error (0, 0, _("%s: is a directory"), pathname);
291       return 1;
292     }
293
294   if (!ignore_missing_files && (interactive || stdin_tty)
295       && euidaccess (pathname, W_OK))
296     {
297       fprintf (stderr,
298                _("%s: descend directory `%s', overriding mode %04o? "),
299                program_name, pathname,
300                (unsigned int) (statp->st_mode & 07777));
301       if (!yesno ())
302         return 1;
303     }
304   else if (interactive)
305     {
306       fprintf (stderr, _("%s: descend directory `%s'? "),
307                program_name, pathname);
308       if (!yesno ())
309         return 1;
310     }
311
312   if (verbose)
313     printf ("%s\n", pathname);
314
315   err = clear_directory (statp);
316
317   if (interactive)
318     {
319       if (err)
320         fprintf (stderr, _("%s: remove directory `%s' (might be nonempty)? "),
321                  program_name, pathname);
322       else
323         fprintf (stderr, _("%s: remove directory `%s'? "),
324                  program_name, pathname);
325       if (!yesno ())
326         return 1;
327     }
328
329   if (rmdir (pathname) && (errno != ENOENT || !ignore_missing_files))
330     {
331       error (0, errno, "%s", pathname);
332       return 1;
333     }
334   return 0;
335 }
336
337 /* Read directory `pathname' and remove all of its entries,
338    avoiding use of chdir.
339    On entry, STATP points to the results of stat on `pathname'.
340    Return 0 for success, error count for failure.
341    Upon return, `pathname' will have the same contents as before,
342    but its address might be different; in that case, `pnsize' will
343    be larger, as well.  */
344
345 static int
346 clear_directory (struct stat *statp)
347 {
348   DIR *dirp;
349   struct dirent *dp;
350   char *name_space;             /* Copy of directory's filenames.  */
351   char *namep;                  /* Current entry in `name_space'.  */
352   unsigned name_size;           /* Bytes allocated for `name_space'.  */
353   int name_length;              /* Length of filename in `namep' plus '\0'.  */
354   int pathname_length;          /* Length of `pathname'.  */
355   ino_t *inode_space;           /* Copy of directory's inodes.  */
356   ino_t *inodep;                /* Current entry in `inode_space'.  */
357   unsigned n_inodes_allocated;  /* There is space for this many inodes
358                                           in `inode_space'.  */
359   int err = 0;                  /* Return status.  */
360   struct pathstack pathframe;   /* New top of stack.  */
361   struct pathstack *pp;         /* Temporary.  */
362
363   name_size = statp->st_size;
364   name_space = (char *) xmalloc (name_size);
365
366   n_inodes_allocated = (statp->st_size + sizeof (ino_t) - 1) / sizeof (ino_t);
367   inode_space = (ino_t *) xmalloc (n_inodes_allocated * sizeof (ino_t));
368
369   do
370     {
371       namep = name_space;
372       inodep = inode_space;
373
374       errno = 0;
375       dirp = opendir (pathname);
376       if (dirp == NULL)
377         {
378           if (errno != ENOENT || !ignore_missing_files)
379             {
380               error (0, errno, "%s", pathname);
381               err = 1;
382             }
383           free (name_space);
384           free (inode_space);
385           return err;
386         }
387
388       while ((dp = readdir (dirp)) != NULL)
389         {
390           /* Skip "." and "..".  */
391           if (dp->d_name[0] != '.'
392               || (dp->d_name[1] != '\0'
393                   && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
394             {
395               unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
396
397               if (size_needed > name_size)
398                 {
399                   char *new_name_space;
400
401                   while (size_needed > name_size)
402                     name_size += 1024;
403
404                   new_name_space = xrealloc (name_space, name_size);
405                   namep += new_name_space - name_space;
406                   name_space = new_name_space;
407                 }
408               namep = stpcpy (namep, dp->d_name) + 1;
409
410               if (inodep == inode_space + n_inodes_allocated)
411                 {
412                   ino_t *new_inode_space;
413
414                   n_inodes_allocated += 1024;
415                   new_inode_space = (ino_t *) xrealloc (inode_space,
416                                         n_inodes_allocated * sizeof (ino_t));
417                   inodep += new_inode_space - inode_space;
418                   inode_space = new_inode_space;
419                 }
420               *inodep++ = D_INO (dp);
421             }
422         }
423       *namep = '\0';
424       if (CLOSEDIR (dirp))
425         {
426           error (0, errno, "%s", pathname);
427           err = 1;
428         }
429
430       pathname_length = strlen (pathname);
431
432       for (namep = name_space, inodep = inode_space; *namep != '\0';
433            namep += name_length, inodep++)
434         {
435           name_length = strlen (namep) + 1;
436
437           /* Handle arbitrarily long filenames.  */
438           if (pathname_length + 1 + name_length > pnsize)
439             {
440               char *new_pathname;
441
442               pnsize = (pathname_length + 1 + name_length) * 2;
443               new_pathname = xrealloc (pathname, pnsize);
444               /* Update all pointers in the stack to use the new area.  */
445               for (pp = pathstack; pp != NULL; pp = pp->next)
446                 pp->pathp += new_pathname - pathname;
447               pathname = new_pathname;
448             }
449
450           /* Add a new frame to the top of the path stack.  */
451           pathframe.pathp = pathname + pathname_length;
452           pathframe.inum = *inodep;
453           pathframe.next = pathstack;
454           pathstack = &pathframe;
455
456           /* Append '/' and the filename to current pathname, take care of
457              the file (which could result in recursive calls), and take
458              the filename back off.  */
459
460           *pathstack->pathp = '/';
461           strcpy (pathstack->pathp + 1, namep);
462
463           /* If the i-number has already appeared, there's an error.  */
464           if (duplicate_entry (pathstack->next, pathstack->inum))
465             err++;
466           else if (rm ())
467             err++;
468
469           *pathstack->pathp = '\0';
470           pathstack = pathstack->next;  /* Pop the stack.  */
471         }
472     }
473   /* Keep trying while there are still files to remove.  */
474   while (namep > name_space && err == 0);
475
476   free (name_space);
477   free (inode_space);
478   return err;
479 }
480
481 /* If STACK does not already have an entry with the same i-number as INUM,
482    return 0. Otherwise, ask the user whether to continue;
483    if yes, return 1, and if no, exit.
484    This assumes that no one tries to remove filesystem mount points;
485    doing so could cause duplication of i-numbers that would not indicate
486    a corrupted file system.  */
487
488 static int
489 duplicate_entry (struct pathstack *stack, ino_t inum)
490 {
491 #ifdef D_INO_IN_DIRENT
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 /* D_INO_IN_DIRENT */
520   return 0;
521 }
522
523 static void
524 usage (int status)
525 {
526   if (status != 0)
527     fprintf (stderr, _("Try `%s --help' for more information.\n"),
528              program_name);
529   else
530     {
531       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
532       printf (_("\
533 Remove (unlink) the FILE(s).\n\
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       puts (_("\nReport bugs to bug-gnu-utils@gnu.ai.mit.edu"));
544     }
545   exit (status);
546 }