diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-01-20 10:55:18 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-01-20 10:55:18 +0000 |
commit | 70e9163c9c18e995515598085cb824e554eb7ae7 (patch) | |
tree | a42dc8b2a6c031354bf31472de888bfc8a060132 /src/mv.c | |
parent | cbf5993c43f49281173f185863577d86bfac6eae (diff) | |
download | coreutils-tarball-70e9163c9c18e995515598085cb824e554eb7ae7.tar.gz |
coreutils-8.25HEADcoreutils-8.25master
Diffstat (limited to 'src/mv.c')
-rw-r--r-- | src/mv.c | 393 |
1 files changed, 209 insertions, 184 deletions
@@ -1,10 +1,10 @@ /* mv -- move or rename files - Copyright (C) 86, 89, 90, 91, 1995-2007 Free Software Foundation, Inc. + Copyright (C) 1986-2016 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -12,8 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Mike Parker, David MacKenzie, and Jim Meyering */ @@ -22,62 +21,44 @@ #include <getopt.h> #include <sys/types.h> #include <assert.h> +#include <selinux/selinux.h> #include "system.h" -#include "argmatch.h" #include "backupfile.h" #include "copy.h" #include "cp-hash.h" #include "error.h" #include "filenamecat.h" -#include "quote.h" #include "remove.h" +#include "root-dev-ino.h" +#include "priv-set.h" -/* The official name of this program (e.g., no `g' prefix). */ +/* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "mv" -#define AUTHORS "Mike Parker", "David MacKenzie", "Jim Meyering" - -/* Initial number of entries in each hash table entry's table of inodes. */ -#define INITIAL_HASH_MODULE 100 - -/* Initial number of entries in the inode hash table. */ -#define INITIAL_ENTRY_TAB_SIZE 70 +#define AUTHORS \ + proper_name ("Mike Parker"), \ + proper_name ("David MacKenzie"), \ + proper_name ("Jim Meyering") /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum { - REPLY_OPTION = CHAR_MAX + 1, - STRIP_TRAILING_SLASHES_OPTION + STRIP_TRAILING_SLASHES_OPTION = CHAR_MAX + 1 }; -/* The name this program was run with. */ -char *program_name; - /* Remove any trailing slashes from each SOURCE argument. */ static bool remove_trailing_slashes; -/* Valid arguments to the `--reply' option. */ -static char const* const reply_args[] = -{ - "yes", "no", "query", NULL -}; - -/* The values that correspond to the above strings. */ -static int const reply_vals[] = -{ - I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER -}; - static struct option const long_options[] = { {"backup", optional_argument, NULL, 'b'}, + {"context", no_argument, NULL, 'Z'}, {"force", no_argument, NULL, 'f'}, {"interactive", no_argument, NULL, 'i'}, + {"no-clobber", no_argument, NULL, 'n'}, {"no-target-directory", no_argument, NULL, 'T'}, - {"reply", required_argument, NULL, REPLY_OPTION}, /* Deprecated 2005-07-03, - remove in 2008. */ {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION}, {"suffix", required_argument, NULL, 'S'}, {"target-directory", required_argument, NULL, 't'}, @@ -92,11 +73,11 @@ static void rm_option_init (struct rm_options *x) { x->ignore_missing_files = false; - x->root_dev_ino = NULL; + x->remove_empty_directories = true; x->recursive = true; x->one_file_system = false; - /* Should we prompt for removal, too? No. Prompting for the `move' + /* Should we prompt for removal, too? No. Prompting for the 'move' part is enough. It implies removal. */ x->interactive = RMI_NEVER; x->stdin_tty = false; @@ -104,29 +85,48 @@ rm_option_init (struct rm_options *x) x->verbose = false; /* Since this program may well have to process additional command - line arguments after any call to `rm', that function must preserve + line arguments after any call to 'rm', that function must preserve the initial working directory, in case one of those is a - `.'-relative name. */ + '.'-relative name. */ x->require_restore_cwd = true; + + { + static struct dev_ino dev_ino_buf; + x->root_dev_ino = get_root_dev_ino (&dev_ino_buf); + if (x->root_dev_ino == NULL) + error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), + quoteaf ("/")); + } } static void cp_option_init (struct cp_options *x) { + bool selinux_enabled = (0 < is_selinux_enabled ()); + + cp_options_default (x); x->copy_as_regular = false; /* FIXME: maybe make this an option */ + x->reflink_mode = REFLINK_AUTO; x->dereference = DEREF_NEVER; x->unlink_dest_before_opening = false; x->unlink_dest_after_failed_open = false; x->hard_link = false; x->interactive = I_UNSPECIFIED; x->move_mode = true; - x->chown_privileges = chown_privileges (); x->one_file_system = false; x->preserve_ownership = true; x->preserve_links = true; x->preserve_mode = true; x->preserve_timestamps = true; + x->explicit_no_preserve_mode= false; + x->preserve_security_context = selinux_enabled; + x->set_security_context = false; + x->reduce_diagnostics = false; + x->data_copy_required = true; x->require_preserve = false; /* FIXME: maybe make this an option */ + x->require_preserve_context = false; + x->preserve_xattr = true; + x->require_preserve_xattr = false; x->recursive = true; x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */ x->symbolic_link = false; @@ -134,6 +134,7 @@ cp_option_init (struct cp_options *x) x->mode = 0; x->stdin_tty = isatty (STDIN_FILENO); + x->open_dangling_dest_symlink = false; x->update = false; x->verbose = false; x->dest_info = NULL; @@ -151,7 +152,7 @@ target_directory_operand (char const *file) int err = (stat (file, &st) == 0 ? 0 : errno); bool is_a_dir = !err && S_ISDIR (st.st_mode); if (err && err != ENOENT) - error (EXIT_FAILURE, err, _("accessing %s"), quote (file)); + error (EXIT_FAILURE, err, _("failed to access %s"), quoteaf (file)); return is_a_dir; } @@ -170,67 +171,70 @@ do_move (const char *source, const char *dest, const struct cp_options *x) { char const *dir_to_remove; if (copy_into_self) - { - /* In general, when copy returns with copy_into_self set, SOURCE is - the same as, or a parent of DEST. In this case we know it's a - parent. It doesn't make sense to move a directory into itself, and - besides in some situations doing so would give highly nonintuitive - results. Run this `mkdir b; touch a c; mv * b' in an empty - directory. Here's the result of running echo `find b -print`: - b b/a b/b b/b/a b/c. Notice that only file `a' was copied - into b/b. Handle this by giving a diagnostic, removing the - copied-into-self directory, DEST (`b/b' in the example), - and failing. */ - - dir_to_remove = NULL; - ok = false; - } + { + /* In general, when copy returns with copy_into_self set, SOURCE is + the same as, or a parent of DEST. In this case we know it's a + parent. It doesn't make sense to move a directory into itself, and + besides in some situations doing so would give highly nonintuitive + results. Run this 'mkdir b; touch a c; mv * b' in an empty + directory. Here's the result of running echo $(find b -print): + b b/a b/b b/b/a b/c. Notice that only file 'a' was copied + into b/b. Handle this by giving a diagnostic, removing the + copied-into-self directory, DEST ('b/b' in the example), + and failing. */ + + dir_to_remove = NULL; + ok = false; + } else if (rename_succeeded) - { - /* No need to remove anything. SOURCE was successfully - renamed to DEST. Or the user declined to rename a file. */ - dir_to_remove = NULL; - } + { + /* No need to remove anything. SOURCE was successfully + renamed to DEST. Or the user declined to rename a file. */ + dir_to_remove = NULL; + } else - { - /* This may mean SOURCE and DEST referred to different devices. - It may also conceivably mean that even though they referred - to the same device, rename wasn't implemented for that device. + { + /* This may mean SOURCE and DEST referred to different devices. + It may also conceivably mean that even though they referred + to the same device, rename wasn't implemented for that device. - E.g., (from Joel N. Weber), - [...] there might someday be cases where you can't rename - but you can copy where the device name is the same, especially - on Hurd. Consider an ftpfs with a primitive ftp server that - supports uploading, downloading and deleting, but not renaming. + E.g., (from Joel N. Weber), + [...] there might someday be cases where you can't rename + but you can copy where the device name is the same, especially + on Hurd. Consider an ftpfs with a primitive ftp server that + supports uploading, downloading and deleting, but not renaming. - Also, note that comparing device numbers is not a reliable - check for `can-rename'. Some systems can be set up so that - files from many different physical devices all have the same - st_dev field. This is a feature of some NFS mounting - configurations. + Also, note that comparing device numbers is not a reliable + check for 'can-rename'. Some systems can be set up so that + files from many different physical devices all have the same + st_dev field. This is a feature of some NFS mounting + configurations. - We reach this point if SOURCE has been successfully copied - to DEST. Now we have to remove SOURCE. + We reach this point if SOURCE has been successfully copied + to DEST. Now we have to remove SOURCE. - This function used to resort to copying only when rename - failed and set errno to EXDEV. */ + This function used to resort to copying only when rename + failed and set errno to EXDEV. */ - dir_to_remove = source; - } + dir_to_remove = source; + } if (dir_to_remove != NULL) - { - struct rm_options rm_options; - enum RM_status status; - - rm_option_init (&rm_options); - rm_options.verbose = x->verbose; - - status = rm (1, &dir_to_remove, &rm_options); - assert (VALID_STATUS (status)); - if (status == RM_ERROR) - ok = false; - } + { + struct rm_options rm_options; + enum RM_status status; + char const *dir[2]; + + rm_option_init (&rm_options); + rm_options.verbose = x->verbose; + dir[0] = dir_to_remove; + dir[1] = NULL; + + status = rm ((void*) dir, &rm_options); + assert (VALID_STATUS (status)); + if (status == RM_ERROR) + ok = false; + } } return ok; @@ -242,15 +246,15 @@ do_move (const char *source, const char *dest, const struct cp_options *x) static bool movefile (char *source, char *dest, bool dest_is_dir, - const struct cp_options *x) + const struct cp_options *x) { bool ok; /* This code was introduced to handle the ambiguity in the semantics of mv that is induced by the varying semantics of the rename function. - Some systems (e.g., Linux) have a rename function that honors a + Some systems (e.g., GNU/Linux) have a rename function that honors a trailing slash, while others (like Solaris 5,6,7) have a rename - function that ignores a trailing slash. I believe the Linux + function that ignores a trailing slash. I believe the GNU/Linux rename semantics are POSIX and susv2 compliant. */ if (remove_trailing_slashes) @@ -277,8 +281,7 @@ void usage (int status) { if (status != EXIT_SUCCESS) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); + emit_try_help (); else { printf (_("\ @@ -286,19 +289,21 @@ Usage: %s [OPTION]... [-T] SOURCE DEST\n\ or: %s [OPTION]... SOURCE... DIRECTORY\n\ or: %s [OPTION]... -t DIRECTORY SOURCE...\n\ "), - program_name, program_name, program_name); + program_name, program_name, program_name); fputs (_("\ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\ -\n\ -"), stdout); - fputs (_("\ -Mandatory arguments to long options are mandatory for short options too.\n\ "), stdout); + + emit_mandatory_arg_note (); + fputs (_("\ - --backup[=CONTROL] make a backup of each existing destination file\n\ + --backup[=CONTROL] make a backup of each existing destination file\ +\n\ -b like --backup but does not accept an argument\n\ -f, --force do not prompt before overwriting\n\ -i, --interactive prompt before overwrite\n\ + -n, --no-clobber do not overwrite an existing file\n\ +If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ "), stdout); fputs (_("\ --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\ @@ -312,12 +317,14 @@ Mandatory arguments to long options are mandatory for short options too.\n\ than the destination file or when the\n\ destination file is missing\n\ -v, --verbose explain what is being done\n\ + -Z, --context set SELinux security context of destination\n\ + file to default type\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\ \n\ -The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\ +The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\ The version control method may be selected via the --backup option or through\n\ the VERSION_CONTROL environment variable. Here are the values:\n\ \n\ @@ -328,7 +335,7 @@ the VERSION_CONTROL environment variable. Here are the values:\n\ existing, nil numbered if numbered backups exist, simple otherwise\n\ simple, never always make simple backups\n\ "), stdout); - printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + emit_ancillary_info (PROGRAM_NAME); } exit (status); } @@ -346,78 +353,89 @@ main (int argc, char **argv) bool no_target_directory = false; int n_files; char **file; + bool selinux_enabled = (0 < is_selinux_enabled ()); initialize_main (&argc, &argv); - program_name = argv[0]; + set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); - atexit (close_stdout); + atexit (close_stdin); cp_option_init (&x); + /* Try to disable the ability to unlink a directory. */ + priv_set_remove_linkdir (); + /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((c = getopt_long (argc, argv, "bfit:uvS:T", long_options, NULL)) - != -1) + while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) + != -1) { switch (c) - { - case 'b': - make_backups = true; - if (optarg) - version_control_string = optarg; - break; - case 'f': - x.interactive = I_ALWAYS_YES; - break; - case 'i': - x.interactive = I_ASK_USER; - break; - case REPLY_OPTION: /* Deprecated */ - x.interactive = XARGMATCH ("--reply", optarg, - reply_args, reply_vals); - error (0, 0, - _("the --reply option is deprecated; use -i or -f instead")); - break; - case STRIP_TRAILING_SLASHES_OPTION: - remove_trailing_slashes = true; - break; - case 't': - if (target_directory) - error (EXIT_FAILURE, 0, _("multiple target directories specified")); - else - { - struct stat st; - if (stat (optarg, &st) != 0) - error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg)); - if (! S_ISDIR (st.st_mode)) - error (EXIT_FAILURE, 0, _("target %s is not a directory"), - quote (optarg)); - } - target_directory = optarg; - break; - case 'T': - no_target_directory = true; - break; - case 'u': - x.update = true; - break; - case 'v': - x.verbose = true; - break; - case 'S': - make_backups = true; - backup_suffix_string = optarg; - break; - case_GETOPT_HELP_CHAR; - case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); - default: - usage (EXIT_FAILURE); - } + { + case 'b': + make_backups = true; + if (optarg) + version_control_string = optarg; + break; + case 'f': + x.interactive = I_ALWAYS_YES; + break; + case 'i': + x.interactive = I_ASK_USER; + break; + case 'n': + x.interactive = I_ALWAYS_NO; + break; + case STRIP_TRAILING_SLASHES_OPTION: + remove_trailing_slashes = true; + break; + case 't': + if (target_directory) + error (EXIT_FAILURE, 0, _("multiple target directories specified")); + else + { + struct stat st; + if (stat (optarg, &st) != 0) + error (EXIT_FAILURE, errno, _("failed to access %s"), + quoteaf (optarg)); + if (! S_ISDIR (st.st_mode)) + error (EXIT_FAILURE, 0, _("target %s is not a directory"), + quoteaf (optarg)); + } + target_directory = optarg; + break; + case 'T': + no_target_directory = true; + break; + case 'u': + x.update = true; + break; + case 'v': + x.verbose = true; + break; + case 'S': + make_backups = true; + backup_suffix_string = optarg; + break; + case 'Z': + /* As a performance enhancement, don't even bother trying + to "restorecon" when not on an selinux-enabled kernel. */ + if (selinux_enabled) + { + x.preserve_security_context = false; + x.set_security_context = true; + } + break; + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: + usage (EXIT_FAILURE); + } } n_files = argc - optind; @@ -426,42 +444,49 @@ main (int argc, char **argv) if (n_files <= !target_directory) { if (n_files <= 0) - error (0, 0, _("missing file operand")); + error (0, 0, _("missing file operand")); else - error (0, 0, _("missing destination file operand after %s"), - quote (file[0])); + error (0, 0, _("missing destination file operand after %s"), + quoteaf (file[0])); usage (EXIT_FAILURE); } if (no_target_directory) { if (target_directory) - error (EXIT_FAILURE, 0, - _("Cannot combine --target-directory (-t) " - "and --no-target-directory (-T)")); + error (EXIT_FAILURE, 0, + _("cannot combine --target-directory (-t) " + "and --no-target-directory (-T)")); if (2 < n_files) - { - error (0, 0, _("extra operand %s"), quote (file[2])); - usage (EXIT_FAILURE); - } + { + error (0, 0, _("extra operand %s"), quoteaf (file[2])); + usage (EXIT_FAILURE); + } } else if (!target_directory) { assert (2 <= n_files); if (target_directory_operand (file[n_files - 1])) - target_directory = file[--n_files]; + target_directory = file[--n_files]; else if (2 < n_files) - error (EXIT_FAILURE, 0, _("target %s is not a directory"), - quote (file[n_files - 1])); + error (EXIT_FAILURE, 0, _("target %s is not a directory"), + quoteaf (file[n_files - 1])); + } + + if (make_backups && x.interactive == I_ALWAYS_NO) + { + error (0, 0, + _("options --backup and --no-clobber are mutually exclusive")); + usage (EXIT_FAILURE); } if (backup_suffix_string) simple_backup_suffix = xstrdup (backup_suffix_string); x.backup_type = (make_backups - ? xget_version (_("backup type"), - version_control_string) - : no_backups); + ? xget_version (_("backup type"), + version_control_string) + : no_backups); hash_init (); @@ -470,17 +495,17 @@ main (int argc, char **argv) int i; /* Initialize the hash table only if we'll need it. - The problem it is used to detect can arise only if there are - two or more files to move. */ + The problem it is used to detect can arise only if there are + two or more files to move. */ if (2 <= n_files) - dest_info_init (&x); + dest_info_init (&x); ok = true; for (i = 0; i < n_files; ++i) - ok &= movefile (file[i], target_directory, true, &x); + ok &= movefile (file[i], target_directory, true, &x); } else ok = movefile (file[0], file[1], false, &x); - exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); + return ok ? EXIT_SUCCESS : EXIT_FAILURE; } |