LCOV - code coverage report
Current view: top level - src - rm.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 119 120 99.2 %
Date: 2018-01-30 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /* `rm' file deletion utility for GNU.
       2             :    Copyright (C) 88, 90, 91, 1994-2007 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 3 of the License, or
       7             :    (at your option) 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, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : /* Written by Paul Rubin, David MacKenzie, and Richard Stallman.
      18             :    Reworked to use chdir and avoid recursion by Jim Meyering.  */
      19             : 
      20             : /* Implementation overview:
      21             : 
      22             :    In the `usual' case, RM saves no state for directories it is processing.
      23             :    When a removal fails (either due to an error or to an interactive `no'
      24             :    reply), the failure is noted (see description of `ht' in remove.c's
      25             :    remove_cwd_entries function) so that when/if the containing directory
      26             :    is reopened, RM doesn't try to remove the entry again.
      27             : 
      28             :    RM may delete arbitrarily deep hierarchies -- even ones in which file
      29             :    names (from root to leaf) are longer than the system-imposed maximum.
      30             :    It does this by using chdir to change to each directory in turn before
      31             :    removing the entries in that directory.
      32             : 
      33             :    RM detects directory cycles lazily.  See lib/cycle-check.c.
      34             : 
      35             :    RM is careful to avoid forming full file names whenever possible.
      36             :    A full file name is formed only when it is about to be used -- e.g.
      37             :    in a diagnostic or in an interactive-mode prompt.
      38             : 
      39             :    RM minimizes the number of lstat system calls it makes.  On systems
      40             :    that have valid d_type data in directory entries, RM makes only one
      41             :    lstat call per command line argument -- regardless of the depth of
      42             :    the hierarchy.  */
      43             : 
      44             : #include <config.h>
      45             : #include <stdio.h>
      46             : #include <getopt.h>
      47             : #include <sys/types.h>
      48             : #include <assert.h>
      49             : 
      50             : #include "system.h"
      51             : #include "argmatch.h"
      52             : #include "error.h"
      53             : #include "lstat.h"
      54             : #include "quote.h"
      55             : #include "quotearg.h"
      56             : #include "remove.h"
      57             : #include "root-dev-ino.h"
      58             : #include "yesno.h"
      59             : 
      60             : /* The official name of this program (e.g., no `g' prefix).  */
      61             : #define PROGRAM_NAME "rm"
      62             : 
      63             : #define AUTHORS \
      64             :   "Paul Rubin", "David MacKenzie, Richard Stallman", "Jim Meyering"
      65             : 
      66             : /* Name this program was run with.  */
      67             : char *program_name;
      68             : 
      69             : /* For long options that have no equivalent short option, use a
      70             :    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
      71             : enum
      72             : {
      73             :   INTERACTIVE_OPTION = CHAR_MAX + 1,
      74             :   ONE_FILE_SYSTEM,
      75             :   NO_PRESERVE_ROOT,
      76             :   PRESERVE_ROOT,
      77             :   PRESUME_INPUT_TTY_OPTION
      78             : };
      79             : 
      80             : enum interactive_type
      81             :   {
      82             :     interactive_never,          /* 0: no option or --interactive=never */
      83             :     interactive_once,           /* 1: -I or --interactive=once */
      84             :     interactive_always          /* 2: default, -i or --interactive=always */
      85             :   };
      86             : 
      87             : static struct option const long_opts[] =
      88             : {
      89             :   {"directory", no_argument, NULL, 'd'},
      90             :   {"force", no_argument, NULL, 'f'},
      91             :   {"interactive", optional_argument, NULL, INTERACTIVE_OPTION},
      92             : 
      93             :   {"one-file-system", no_argument, NULL, ONE_FILE_SYSTEM},
      94             :   {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
      95             :   {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
      96             : 
      97             :   /* This is solely for testing.  Do not document.  */
      98             :   /* It is relatively difficult to ensure that there is a tty on stdin.
      99             :      Since rm acts differently depending on that, without this option,
     100             :      it'd be harder to test the parts of rm that depend on that setting.  */
     101             :   {"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION},
     102             : 
     103             :   {"recursive", no_argument, NULL, 'r'},
     104             :   {"verbose", no_argument, NULL, 'v'},
     105             :   {GETOPT_HELP_OPTION_DECL},
     106             :   {GETOPT_VERSION_OPTION_DECL},
     107             :   {NULL, 0, NULL, 0}
     108             : };
     109             : 
     110             : static char const *const interactive_args[] =
     111             : {
     112             :   "never", "no", "none",
     113             :   "once",
     114             :   "always", "yes", NULL
     115             : };
     116             : static enum interactive_type const interactive_types[] =
     117             : {
     118             :   interactive_never, interactive_never, interactive_never,
     119             :   interactive_once,
     120             :   interactive_always, interactive_always
     121             : };
     122             : ARGMATCH_VERIFY (interactive_args, interactive_types);
     123             : 
     124             : /* Advise the user about invalid usages like "rm -foo" if the file
     125             :    "-foo" exists, assuming ARGC and ARGV are as with `main'.  */
     126             : 
     127             : static void
     128           7 : diagnose_leading_hyphen (int argc, char **argv)
     129             : {
     130             :   /* OPTIND is unreliable, so iterate through the arguments looking
     131             :      for a file name that looks like an option.  */
     132             :   int i;
     133             : 
     134          16 :   for (i = 1; i < argc; i++)
     135             :     {
     136          11 :       char const *arg = argv[i];
     137             :       struct stat st;
     138             : 
     139          11 :       if (arg[0] == '-' && arg[1] && lstat (arg, &st) == 0)
     140             :         {
     141           2 :           fprintf (stderr,
     142             :                    _("Try `%s ./%s' to remove the file %s.\n"),
     143             :                    argv[0],
     144             :                    quotearg_n_style (1, shell_quoting_style, arg),
     145             :                    quote (arg));
     146           2 :           break;
     147             :         }
     148             :     }
     149           7 : }
     150             : 
     151             : void
     152          24 : usage (int status)
     153             : {
     154          24 :   if (status != EXIT_SUCCESS)
     155          23 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
     156             :              program_name);
     157             :   else
     158             :     {
     159           1 :       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
     160           1 :       fputs (_("\
     161             : Remove (unlink) the FILE(s).\n\
     162             : \n\
     163             :   -f, --force           ignore nonexistent files, never prompt\n\
     164             :   -i                    prompt before every removal\n\
     165             : "), stdout);
     166           1 :       fputs (_("\
     167             :   -I                    prompt once before removing more than three files, or\n\
     168             :                           when removing recursively.  Less intrusive than -i,\n\
     169             :                           while still giving protection against most mistakes\n\
     170             :       --interactive[=WHEN]  prompt according to WHEN: never, once (-I), or\n\
     171             :                           always (-i).  Without WHEN, prompt always\n\
     172             : "), stdout);
     173           1 :       fputs (_("\
     174             :       --one-file-system  when removing a hierarchy recursively, skip any\n\
     175             :                           directory that is on a file system different from\n\
     176             :                           that of the corresponding command line argument\n\
     177             : "), stdout);
     178           1 :       fputs (_("\
     179             :       --no-preserve-root  do not treat `/' specially\n\
     180             :       --preserve-root   do not remove `/' (default)\n\
     181             :   -r, -R, --recursive   remove directories and their contents recursively\n\
     182             :   -v, --verbose         explain what is being done\n\
     183             : "), stdout);
     184           1 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     185           1 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     186           1 :       fputs (_("\
     187             : \n\
     188             : By default, rm does not remove directories.  Use the --recursive (-r or -R)\n\
     189             : option to remove each listed directory, too, along with all of its contents.\n\
     190             : "), stdout);
     191           1 :       printf (_("\
     192             : \n\
     193             : To remove a file whose name starts with a `-', for example `-foo',\n\
     194             : use one of these commands:\n\
     195             :   %s -- -foo\n\
     196             : \n\
     197             :   %s ./-foo\n\
     198             : "),
     199             :               program_name, program_name);
     200           1 :       fputs (_("\
     201             : \n\
     202             : Note that if you use rm to remove a file, it is usually possible to recover\n\
     203             : the contents of that file.  If you want more assurance that the contents are\n\
     204             : truly unrecoverable, consider using shred.\n\
     205             : "), stdout);
     206           1 :       emit_bug_reporting_address ();
     207             :     }
     208          24 :   exit (status);
     209             : }
     210             : 
     211             : static void
     212          88 : rm_option_init (struct rm_options *x)
     213             : {
     214          88 :   x->ignore_missing_files = false;
     215          88 :   x->interactive = RMI_SOMETIMES;
     216          88 :   x->one_file_system = false;
     217          88 :   x->recursive = false;
     218          88 :   x->root_dev_ino = NULL;
     219          88 :   x->stdin_tty = isatty (STDIN_FILENO);
     220          88 :   x->verbose = false;
     221             : 
     222             :   /* Since this program exits immediately after calling `rm', rm need not
     223             :      expend unnecessary effort to preserve the initial working directory.  */
     224          88 :   x->require_restore_cwd = false;
     225          88 : }
     226             : 
     227             : int
     228          88 : main (int argc, char **argv)
     229             : {
     230          88 :   bool preserve_root = true;
     231             :   struct rm_options x;
     232          88 :   bool prompt_once = false;
     233             :   int c;
     234             : 
     235             :   initialize_main (&argc, &argv);
     236          88 :   program_name = argv[0];
     237          88 :   setlocale (LC_ALL, "");
     238             :   bindtextdomain (PACKAGE, LOCALEDIR);
     239             :   textdomain (PACKAGE);
     240             : 
     241          88 :   atexit (close_stdin);
     242             : 
     243          88 :   rm_option_init (&x);
     244             : 
     245         222 :   while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
     246             :     {
     247          56 :       switch (c)
     248             :         {
     249           6 :         case 'd':
     250             :           /* Ignore this option, for backward compatibility with
     251             :              coreutils 5.92.  FIXME: Some time after 2005, change this
     252             :              to report an error (or perhaps behave like FreeBSD does)
     253             :              instead of ignoring the option.  */
     254           6 :           break;
     255             : 
     256           7 :         case 'f':
     257           7 :           x.interactive = RMI_NEVER;
     258           7 :           x.ignore_missing_files = true;
     259           7 :           prompt_once = false;
     260           7 :           break;
     261             : 
     262           7 :         case 'i':
     263           7 :           x.interactive = RMI_ALWAYS;
     264           7 :           x.ignore_missing_files = false;
     265           7 :           prompt_once = false;
     266           7 :           break;
     267             : 
     268           4 :         case 'I':
     269           4 :           x.interactive = RMI_NEVER;
     270           4 :           x.ignore_missing_files = false;
     271           4 :           prompt_once = true;
     272           4 :           break;
     273             : 
     274          11 :         case 'r':
     275             :         case 'R':
     276          11 :           x.recursive = true;
     277          11 :           break;
     278             : 
     279           5 :         case INTERACTIVE_OPTION:
     280             :           {
     281             :             int i;
     282           5 :             if (optarg)
     283           4 :               i = XARGMATCH ("--interactive", optarg, interactive_args,
     284             :                              interactive_types);
     285             :             else
     286           1 :               i = interactive_always;
     287           4 :             switch (i)
     288             :               {
     289           1 :               case interactive_never:
     290           1 :                 x.interactive = RMI_NEVER;
     291           1 :                 prompt_once = false;
     292           1 :                 break;
     293             : 
     294           2 :               case interactive_once:
     295           2 :                 x.interactive = RMI_SOMETIMES;
     296           2 :                 x.ignore_missing_files = false;
     297           2 :                 prompt_once = true;
     298           2 :                 break;
     299             : 
     300           1 :               case interactive_always:
     301           1 :                 x.interactive = RMI_ALWAYS;
     302           1 :                 x.ignore_missing_files = false;
     303           1 :                 prompt_once = false;
     304           1 :                 break;
     305             :               }
     306           4 :             break;
     307             :           }
     308             : 
     309           1 :         case ONE_FILE_SYSTEM:
     310           1 :           x.one_file_system = true;
     311           1 :           break;
     312             : 
     313           2 :         case NO_PRESERVE_ROOT:
     314           2 :           preserve_root = false;
     315           2 :           break;
     316             : 
     317           1 :         case PRESERVE_ROOT:
     318           1 :           preserve_root = true;
     319           1 :           break;
     320             : 
     321           1 :         case PRESUME_INPUT_TTY_OPTION:
     322           1 :           x.stdin_tty = true;
     323           1 :           break;
     324             : 
     325           2 :         case 'v':
     326           2 :           x.verbose = true;
     327           2 :           break;
     328             : 
     329           1 :         case_GETOPT_HELP_CHAR;
     330           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     331           7 :         default:
     332           7 :           diagnose_leading_hyphen (argc, argv);
     333           7 :           usage (EXIT_FAILURE);
     334             :         }
     335             :     }
     336             : 
     337          78 :   if (argc <= optind)
     338             :     {
     339          18 :       if (x.ignore_missing_files)
     340           3 :         exit (EXIT_SUCCESS);
     341             :       else
     342             :         {
     343          15 :           error (0, 0, _("missing operand"));
     344          15 :           usage (EXIT_FAILURE);
     345             :         }
     346             :     }
     347             : 
     348          60 :   if (x.recursive & preserve_root)
     349             :     {
     350             :       static struct dev_ino dev_ino_buf;
     351           9 :       x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
     352           9 :       if (x.root_dev_ino == NULL)
     353           0 :         error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
     354             :                quote ("/"));
     355             :     }
     356             : 
     357          60 :   size_t n_files = argc - optind;
     358          60 :   char const *const *file = (char const *const *) argv + optind;
     359             : 
     360          60 :   if (prompt_once && (x.recursive || 3 < n_files))
     361             :     {
     362           4 :       fprintf (stderr,
     363           2 :                (x.recursive
     364             :                 ? _("%s: remove all arguments recursively? ")
     365             :                 : _("%s: remove all arguments? ")),
     366             :                program_name);
     367           2 :       if (!yesno ())
     368           1 :         exit (EXIT_SUCCESS);
     369             :     }
     370          59 :   enum RM_status status = rm (n_files, file, &x);
     371          59 :   assert (VALID_STATUS (status));
     372          59 :   exit (status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS);
     373             : }

Generated by: LCOV version 1.10