LCOV - code coverage report
Current view: top level - src - mv.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 153 180 85.0 %
Date: 2018-01-30 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /* mv -- move or rename files
       2             :    Copyright (C) 86, 89, 90, 91, 1995-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 Mike Parker, David MacKenzie, and Jim Meyering */
      18             : 
      19             : #include <config.h>
      20             : #include <stdio.h>
      21             : #include <getopt.h>
      22             : #include <sys/types.h>
      23             : #include <assert.h>
      24             : #include <selinux/selinux.h>
      25             : 
      26             : #include "system.h"
      27             : #include "argmatch.h"
      28             : #include "backupfile.h"
      29             : #include "copy.h"
      30             : #include "cp-hash.h"
      31             : #include "error.h"
      32             : #include "filenamecat.h"
      33             : #include "quote.h"
      34             : #include "remove.h"
      35             : #include "root-dev-ino.h"
      36             : 
      37             : /* The official name of this program (e.g., no `g' prefix).  */
      38             : #define PROGRAM_NAME "mv"
      39             : 
      40             : #define AUTHORS "Mike Parker", "David MacKenzie", "Jim Meyering"
      41             : 
      42             : /* Initial number of entries in each hash table entry's table of inodes.  */
      43             : #define INITIAL_HASH_MODULE 100
      44             : 
      45             : /* Initial number of entries in the inode hash table.  */
      46             : #define INITIAL_ENTRY_TAB_SIZE 70
      47             : 
      48             : /* For long options that have no equivalent short option, use a
      49             :    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
      50             : enum
      51             : {
      52             :   REPLY_OPTION = CHAR_MAX + 1,
      53             :   STRIP_TRAILING_SLASHES_OPTION
      54             : };
      55             : 
      56             : /* The name this program was run with. */
      57             : char *program_name;
      58             : 
      59             : /* Remove any trailing slashes from each SOURCE argument.  */
      60             : static bool remove_trailing_slashes;
      61             : 
      62             : /* Valid arguments to the `--reply' option. */
      63             : static char const* const reply_args[] =
      64             : {
      65             :   "yes", "no", "query", NULL
      66             : };
      67             : 
      68             : /* The values that correspond to the above strings. */
      69             : static int const reply_vals[] =
      70             : {
      71             :   I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER
      72             : };
      73             : 
      74             : static struct option const long_options[] =
      75             : {
      76             :   {"backup", optional_argument, NULL, 'b'},
      77             :   {"force", no_argument, NULL, 'f'},
      78             :   {"interactive", no_argument, NULL, 'i'},
      79             :   {"no-target-directory", no_argument, NULL, 'T'},
      80             :   {"reply", required_argument, NULL, REPLY_OPTION}, /* Deprecated 2005-07-03,
      81             :                                                        remove in 2008. */
      82             :   {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
      83             :   {"suffix", required_argument, NULL, 'S'},
      84             :   {"target-directory", required_argument, NULL, 't'},
      85             :   {"update", no_argument, NULL, 'u'},
      86             :   {"verbose", no_argument, NULL, 'v'},
      87             :   {GETOPT_HELP_OPTION_DECL},
      88             :   {GETOPT_VERSION_OPTION_DECL},
      89             :   {NULL, 0, NULL, 0}
      90             : };
      91             : 
      92             : static void
      93           0 : rm_option_init (struct rm_options *x)
      94             : {
      95           0 :   x->ignore_missing_files = false;
      96           0 :   x->recursive = true;
      97           0 :   x->one_file_system = false;
      98             : 
      99             :   /* Should we prompt for removal, too?  No.  Prompting for the `move'
     100             :      part is enough.  It implies removal.  */
     101           0 :   x->interactive = RMI_NEVER;
     102           0 :   x->stdin_tty = false;
     103             : 
     104           0 :   x->verbose = false;
     105             : 
     106             :   /* Since this program may well have to process additional command
     107             :      line arguments after any call to `rm', that function must preserve
     108             :      the initial working directory, in case one of those is a
     109             :      `.'-relative name.  */
     110           0 :   x->require_restore_cwd = true;
     111             : 
     112             :   {
     113             :     static struct dev_ino dev_ino_buf;
     114           0 :     x->root_dev_ino = get_root_dev_ino (&dev_ino_buf);
     115           0 :     if (x->root_dev_ino == NULL)
     116           0 :       error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
     117             :              quote ("/"));
     118             :   }
     119           0 : }
     120             : 
     121             : static void
     122         127 : cp_option_init (struct cp_options *x)
     123             : {
     124         127 :   bool selinux_enabled = (0 < is_selinux_enabled ());
     125             : 
     126         127 :   cp_options_default (x);
     127         127 :   x->copy_as_regular = false;  /* FIXME: maybe make this an option */
     128         127 :   x->dereference = DEREF_NEVER;
     129         127 :   x->unlink_dest_before_opening = false;
     130         127 :   x->unlink_dest_after_failed_open = false;
     131         127 :   x->hard_link = false;
     132         127 :   x->interactive = I_UNSPECIFIED;
     133         127 :   x->move_mode = true;
     134         127 :   x->one_file_system = false;
     135         127 :   x->preserve_ownership = true;
     136         127 :   x->preserve_links = true;
     137         127 :   x->preserve_mode = true;
     138         127 :   x->preserve_timestamps = true;
     139         127 :   x->preserve_security_context = selinux_enabled;
     140         127 :   x->require_preserve = false;  /* FIXME: maybe make this an option */
     141         127 :   x->require_preserve_context = false;
     142         127 :   x->recursive = true;
     143         127 :   x->sparse_mode = SPARSE_AUTO;  /* FIXME: maybe make this an option */
     144         127 :   x->symbolic_link = false;
     145         127 :   x->set_mode = false;
     146         127 :   x->mode = 0;
     147         127 :   x->stdin_tty = isatty (STDIN_FILENO);
     148             : 
     149         127 :   x->open_dangling_dest_symlink = false;
     150         127 :   x->update = false;
     151         127 :   x->verbose = false;
     152         127 :   x->dest_info = NULL;
     153         127 :   x->src_info = NULL;
     154         127 : }
     155             : 
     156             : /* FILE is the last operand of this command.  Return true if FILE is a
     157             :    directory.  But report an error if there is a problem accessing FILE, other
     158             :    than nonexistence (errno == ENOENT).  */
     159             : 
     160             : static bool
     161          78 : target_directory_operand (char const *file)
     162             : {
     163             :   struct stat st;
     164          78 :   int err = (stat (file, &st) == 0 ? 0 : errno);
     165          78 :   bool is_a_dir = !err && S_ISDIR (st.st_mode);
     166          78 :   if (err && err != ENOENT)
     167          14 :     error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
     168          64 :   return is_a_dir;
     169             : }
     170             : 
     171             : /* Move SOURCE onto DEST.  Handles cross-file-system moves.
     172             :    If SOURCE is a directory, DEST must not exist.
     173             :    Return true if successful.  */
     174             : 
     175             : static bool
     176          71 : do_move (const char *source, const char *dest, const struct cp_options *x)
     177             : {
     178             :   bool copy_into_self;
     179             :   bool rename_succeeded;
     180          71 :   bool ok = copy (source, dest, false, x, &copy_into_self, &rename_succeeded);
     181             : 
     182          71 :   if (ok)
     183             :     {
     184             :       char const *dir_to_remove;
     185           6 :       if (copy_into_self)
     186             :         {
     187             :           /* In general, when copy returns with copy_into_self set, SOURCE is
     188             :              the same as, or a parent of DEST.  In this case we know it's a
     189             :              parent.  It doesn't make sense to move a directory into itself, and
     190             :              besides in some situations doing so would give highly nonintuitive
     191             :              results.  Run this `mkdir b; touch a c; mv * b' in an empty
     192             :              directory.  Here's the result of running echo `find b -print`:
     193             :              b b/a b/b b/b/a b/c.  Notice that only file `a' was copied
     194             :              into b/b.  Handle this by giving a diagnostic, removing the
     195             :              copied-into-self directory, DEST (`b/b' in the example),
     196             :              and failing.  */
     197             : 
     198           0 :           dir_to_remove = NULL;
     199           0 :           ok = false;
     200             :         }
     201           6 :       else if (rename_succeeded)
     202             :         {
     203             :           /* No need to remove anything.  SOURCE was successfully
     204             :              renamed to DEST.  Or the user declined to rename a file.  */
     205           6 :           dir_to_remove = NULL;
     206             :         }
     207             :       else
     208             :         {
     209             :           /* This may mean SOURCE and DEST referred to different devices.
     210             :              It may also conceivably mean that even though they referred
     211             :              to the same device, rename wasn't implemented for that device.
     212             : 
     213             :              E.g., (from Joel N. Weber),
     214             :              [...] there might someday be cases where you can't rename
     215             :              but you can copy where the device name is the same, especially
     216             :              on Hurd.  Consider an ftpfs with a primitive ftp server that
     217             :              supports uploading, downloading and deleting, but not renaming.
     218             : 
     219             :              Also, note that comparing device numbers is not a reliable
     220             :              check for `can-rename'.  Some systems can be set up so that
     221             :              files from many different physical devices all have the same
     222             :              st_dev field.  This is a feature of some NFS mounting
     223             :              configurations.
     224             : 
     225             :              We reach this point if SOURCE has been successfully copied
     226             :              to DEST.  Now we have to remove SOURCE.
     227             : 
     228             :              This function used to resort to copying only when rename
     229             :              failed and set errno to EXDEV.  */
     230             : 
     231           0 :           dir_to_remove = source;
     232             :         }
     233             : 
     234           6 :       if (dir_to_remove != NULL)
     235             :         {
     236             :           struct rm_options rm_options;
     237             :           enum RM_status status;
     238             : 
     239           0 :           rm_option_init (&rm_options);
     240           0 :           rm_options.verbose = x->verbose;
     241             : 
     242           0 :           status = rm (1, &dir_to_remove, &rm_options);
     243           0 :           assert (VALID_STATUS (status));
     244           0 :           if (status == RM_ERROR)
     245           0 :             ok = false;
     246             :         }
     247             :     }
     248             : 
     249          71 :   return ok;
     250             : }
     251             : 
     252             : /* Move file SOURCE onto DEST.  Handles the case when DEST is a directory.
     253             :    Treat DEST as a directory if DEST_IS_DIR.
     254             :    Return true if successful.  */
     255             : 
     256             : static bool
     257          71 : movefile (char *source, char *dest, bool dest_is_dir,
     258             :           const struct cp_options *x)
     259             : {
     260             :   bool ok;
     261             : 
     262             :   /* This code was introduced to handle the ambiguity in the semantics
     263             :      of mv that is induced by the varying semantics of the rename function.
     264             :      Some systems (e.g., Linux) have a rename function that honors a
     265             :      trailing slash, while others (like Solaris 5,6,7) have a rename
     266             :      function that ignores a trailing slash.  I believe the Linux
     267             :      rename semantics are POSIX and susv2 compliant.  */
     268             : 
     269          71 :   if (remove_trailing_slashes)
     270           1 :     strip_trailing_slashes (source);
     271             : 
     272          71 :   if (dest_is_dir)
     273             :     {
     274             :       /* Treat DEST as a directory; build the full filename.  */
     275          23 :       char const *src_basename = last_component (source);
     276          23 :       char *new_dest = file_name_concat (dest, src_basename, NULL);
     277          23 :       strip_trailing_slashes (new_dest);
     278          23 :       ok = do_move (source, new_dest, x);
     279          23 :       free (new_dest);
     280             :     }
     281             :   else
     282             :     {
     283          48 :       ok = do_move (source, dest, x);
     284             :     }
     285             : 
     286          71 :   return ok;
     287             : }
     288             : 
     289             : void
     290          44 : usage (int status)
     291             : {
     292          44 :   if (status != EXIT_SUCCESS)
     293          43 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
     294             :              program_name);
     295             :   else
     296             :     {
     297           1 :       printf (_("\
     298             : Usage: %s [OPTION]... [-T] SOURCE DEST\n\
     299             :   or:  %s [OPTION]... SOURCE... DIRECTORY\n\
     300             :   or:  %s [OPTION]... -t DIRECTORY SOURCE...\n\
     301             : "),
     302             :               program_name, program_name, program_name);
     303           1 :       fputs (_("\
     304             : Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
     305             : \n\
     306             : "), stdout);
     307           1 :       fputs (_("\
     308             : Mandatory arguments to long options are mandatory for short options too.\n\
     309             : "), stdout);
     310           1 :       fputs (_("\
     311             :       --backup[=CONTROL]       make a backup of each existing destination file\n\
     312             :   -b                           like --backup but does not accept an argument\n\
     313             :   -f, --force                  do not prompt before overwriting\n\
     314             :   -i, --interactive            prompt before overwrite\n\
     315             : "), stdout);
     316           1 :       fputs (_("\
     317             :       --strip-trailing-slashes  remove any trailing slashes from each SOURCE\n\
     318             :                                  argument\n\
     319             :   -S, --suffix=SUFFIX          override the usual backup suffix\n\
     320             : "), stdout);
     321           1 :       fputs (_("\
     322             :   -t, --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
     323             :   -T, --no-target-directory    treat DEST as a normal file\n\
     324             :   -u, --update                 move only when the SOURCE file is newer\n\
     325             :                                  than the destination file or when the\n\
     326             :                                  destination file is missing\n\
     327             :   -v, --verbose                explain what is being done\n\
     328             : "), stdout);
     329           1 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     330           1 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     331           1 :       fputs (_("\
     332             : \n\
     333             : The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
     334             : The version control method may be selected via the --backup option or through\n\
     335             : the VERSION_CONTROL environment variable.  Here are the values:\n\
     336             : \n\
     337             : "), stdout);
     338           1 :       fputs (_("\
     339             :   none, off       never make backups (even if --backup is given)\n\
     340             :   numbered, t     make numbered backups\n\
     341             :   existing, nil   numbered if numbered backups exist, simple otherwise\n\
     342             :   simple, never   always make simple backups\n\
     343             : "), stdout);
     344           1 :       emit_bug_reporting_address ();
     345             :     }
     346          44 :   exit (status);
     347             : }
     348             : 
     349             : int
     350         127 : main (int argc, char **argv)
     351             : {
     352             :   int c;
     353             :   bool ok;
     354         127 :   bool make_backups = false;
     355             :   char *backup_suffix_string;
     356         127 :   char *version_control_string = NULL;
     357             :   struct cp_options x;
     358         127 :   char *target_directory = NULL;
     359         127 :   bool no_target_directory = false;
     360             :   int n_files;
     361             :   char **file;
     362             : 
     363             :   initialize_main (&argc, &argv);
     364         127 :   program_name = argv[0];
     365         127 :   setlocale (LC_ALL, "");
     366             :   bindtextdomain (PACKAGE, LOCALEDIR);
     367             :   textdomain (PACKAGE);
     368             : 
     369         127 :   atexit (close_stdin);
     370             : 
     371         127 :   cp_option_init (&x);
     372             : 
     373             :   /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
     374             :      we'll actually use backup_suffix_string.  */
     375         127 :   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
     376             : 
     377         325 :   while ((c = getopt_long (argc, argv, "bfit:uvS:T", long_options, NULL))
     378             :          != -1)
     379             :     {
     380          85 :       switch (c)
     381             :         {
     382          29 :         case 'b':
     383          29 :           make_backups = true;
     384          29 :           if (optarg)
     385           8 :             version_control_string = optarg;
     386          29 :           break;
     387           4 :         case 'f':
     388           4 :           x.interactive = I_ALWAYS_YES;
     389           4 :           break;
     390           2 :         case 'i':
     391           2 :           x.interactive = I_ASK_USER;
     392           2 :           break;
     393           4 :         case REPLY_OPTION: /* Deprecated */
     394           4 :           x.interactive = XARGMATCH ("--reply", optarg,
     395             :                                      reply_args, reply_vals);
     396           2 :           error (0, 0,
     397             :                  _("the --reply option is deprecated; use -i or -f instead"));
     398           2 :           break;
     399           2 :         case STRIP_TRAILING_SLASHES_OPTION:
     400           2 :           remove_trailing_slashes = true;
     401           2 :           break;
     402           5 :         case 't':
     403           5 :           if (target_directory)
     404           0 :             error (EXIT_FAILURE, 0, _("multiple target directories specified"));
     405             :           else
     406             :             {
     407             :               struct stat st;
     408           5 :               if (stat (optarg, &st) != 0)
     409           1 :                 error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
     410           4 :               if (! S_ISDIR (st.st_mode))
     411           1 :                 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
     412             :                        quote (optarg));
     413             :             }
     414           3 :           target_directory = optarg;
     415           3 :           break;
     416           4 :         case 'T':
     417           4 :           no_target_directory = true;
     418           4 :           break;
     419          21 :         case 'u':
     420          21 :           x.update = true;
     421          21 :           break;
     422           2 :         case 'v':
     423           2 :           x.verbose = true;
     424           2 :           break;
     425           2 :         case 'S':
     426           2 :           make_backups = true;
     427           2 :           backup_suffix_string = optarg;
     428           2 :           break;
     429           1 :         case_GETOPT_HELP_CHAR;
     430           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     431           8 :         default:
     432           8 :           usage (EXIT_FAILURE);
     433             :         }
     434             :     }
     435             : 
     436         113 :   n_files = argc - optind;
     437         113 :   file = argv + optind;
     438             : 
     439         113 :   if (n_files <= !target_directory)
     440             :     {
     441          29 :       if (n_files <= 0)
     442          14 :         error (0, 0, _("missing file operand"));
     443             :       else
     444          15 :         error (0, 0, _("missing destination file operand after %s"),
     445             :                quote (file[0]));
     446          29 :       usage (EXIT_FAILURE);
     447             :     }
     448             : 
     449          84 :   if (no_target_directory)
     450             :     {
     451           4 :       if (target_directory)
     452           0 :         error (EXIT_FAILURE, 0,
     453             :                _("Cannot combine --target-directory (-t) "
     454             :                  "and --no-target-directory (-T)"));
     455           4 :       if (2 < n_files)
     456             :         {
     457           0 :           error (0, 0, _("extra operand %s"), quote (file[2]));
     458           0 :           usage (EXIT_FAILURE);
     459             :         }
     460             :     }
     461          80 :   else if (!target_directory)
     462             :     {
     463          78 :       assert (2 <= n_files);
     464          78 :       if (target_directory_operand (file[n_files - 1]))
     465          18 :         target_directory = file[--n_files];
     466          46 :       else if (2 < n_files)
     467           0 :         error (EXIT_FAILURE, 0, _("target %s is not a directory"),
     468           0 :                quote (file[n_files - 1]));
     469             :     }
     470             : 
     471          70 :   if (backup_suffix_string)
     472           1 :     simple_backup_suffix = xstrdup (backup_suffix_string);
     473             : 
     474          66 :   x.backup_type = (make_backups
     475             :                    ? xget_version (_("backup type"),
     476             :                                    version_control_string)
     477          70 :                    : no_backups);
     478             : 
     479          66 :   hash_init ();
     480             : 
     481          66 :   if (target_directory)
     482             :     {
     483             :       int i;
     484             : 
     485             :       /* Initialize the hash table only if we'll need it.
     486             :          The problem it is used to detect can arise only if there are
     487             :          two or more files to move.  */
     488          18 :       if (2 <= n_files)
     489           5 :         dest_info_init (&x);
     490             : 
     491          18 :       ok = true;
     492          41 :       for (i = 0; i < n_files; ++i)
     493          23 :         ok &= movefile (file[i], target_directory, true, &x);
     494             :     }
     495             :   else
     496          48 :     ok = movefile (file[0], file[1], false, &x);
     497             : 
     498          66 :   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
     499             : }

Generated by: LCOV version 1.10