TODO: add an item for a chmod optimization
[platform/upstream/coreutils.git] / src / mv.c
1 /* mv -- move or rename files
2    Copyright (C) 86, 89, 90, 91, 1995-2008 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 \
41   proper_name ("Mike Parker"), \
42   proper_name ("David MacKenzie"), \
43   proper_name ("Jim Meyering")
44
45 /* Initial number of entries in each hash table entry's table of inodes.  */
46 #define INITIAL_HASH_MODULE 100
47
48 /* Initial number of entries in the inode hash table.  */
49 #define INITIAL_ENTRY_TAB_SIZE 70
50
51 /* For long options that have no equivalent short option, use a
52    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
53 enum
54 {
55   REPLY_OPTION = CHAR_MAX + 1,
56   STRIP_TRAILING_SLASHES_OPTION
57 };
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 rm_option_init (struct rm_options *x)
94 {
95   x->ignore_missing_files = false;
96   x->recursive = true;
97   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   x->interactive = RMI_NEVER;
102   x->stdin_tty = false;
103
104   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   x->require_restore_cwd = true;
111
112   {
113     static struct dev_ino dev_ino_buf;
114     x->root_dev_ino = get_root_dev_ino (&dev_ino_buf);
115     if (x->root_dev_ino == NULL)
116       error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
117              quote ("/"));
118   }
119 }
120
121 static void
122 cp_option_init (struct cp_options *x)
123 {
124   bool selinux_enabled = (0 < is_selinux_enabled ());
125
126   cp_options_default (x);
127   x->copy_as_regular = false;  /* FIXME: maybe make this an option */
128   x->dereference = DEREF_NEVER;
129   x->unlink_dest_before_opening = false;
130   x->unlink_dest_after_failed_open = false;
131   x->hard_link = false;
132   x->interactive = I_UNSPECIFIED;
133   x->move_mode = true;
134   x->one_file_system = false;
135   x->preserve_ownership = true;
136   x->preserve_links = true;
137   x->preserve_mode = true;
138   x->preserve_timestamps = true;
139   x->preserve_security_context = selinux_enabled;
140   x->require_preserve = false;  /* FIXME: maybe make this an option */
141   x->require_preserve_context = false;
142   x->recursive = true;
143   x->sparse_mode = SPARSE_AUTO;  /* FIXME: maybe make this an option */
144   x->symbolic_link = false;
145   x->set_mode = false;
146   x->mode = 0;
147   x->stdin_tty = isatty (STDIN_FILENO);
148
149   x->open_dangling_dest_symlink = false;
150   x->update = false;
151   x->verbose = false;
152   x->dest_info = NULL;
153   x->src_info = NULL;
154 }
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 target_directory_operand (char const *file)
162 {
163   struct stat st;
164   int err = (stat (file, &st) == 0 ? 0 : errno);
165   bool is_a_dir = !err && S_ISDIR (st.st_mode);
166   if (err && err != ENOENT)
167     error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
168   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 do_move (const char *source, const char *dest, const struct cp_options *x)
177 {
178   bool copy_into_self;
179   bool rename_succeeded;
180   bool ok = copy (source, dest, false, x, &copy_into_self, &rename_succeeded);
181
182   if (ok)
183     {
184       char const *dir_to_remove;
185       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           dir_to_remove = NULL;
199           ok = false;
200         }
201       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           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           dir_to_remove = source;
232         }
233
234       if (dir_to_remove != NULL)
235         {
236           struct rm_options rm_options;
237           enum RM_status status;
238
239           rm_option_init (&rm_options);
240           rm_options.verbose = x->verbose;
241
242           status = rm (1, &dir_to_remove, &rm_options);
243           assert (VALID_STATUS (status));
244           if (status == RM_ERROR)
245             ok = false;
246         }
247     }
248
249   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 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   if (remove_trailing_slashes)
270     strip_trailing_slashes (source);
271
272   if (dest_is_dir)
273     {
274       /* Treat DEST as a directory; build the full filename.  */
275       char const *src_basename = last_component (source);
276       char *new_dest = file_name_concat (dest, src_basename, NULL);
277       strip_trailing_slashes (new_dest);
278       ok = do_move (source, new_dest, x);
279       free (new_dest);
280     }
281   else
282     {
283       ok = do_move (source, dest, x);
284     }
285
286   return ok;
287 }
288
289 void
290 usage (int status)
291 {
292   if (status != EXIT_SUCCESS)
293     fprintf (stderr, _("Try `%s --help' for more information.\n"),
294              program_name);
295   else
296     {
297       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       fputs (_("\
304 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
305 \n\
306 "), stdout);
307       fputs (_("\
308 Mandatory arguments to long options are mandatory for short options too.\n\
309 "), stdout);
310       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       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       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       fputs (HELP_OPTION_DESCRIPTION, stdout);
330       fputs (VERSION_OPTION_DESCRIPTION, stdout);
331       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       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       emit_bug_reporting_address ();
345     }
346   exit (status);
347 }
348
349 int
350 main (int argc, char **argv)
351 {
352   int c;
353   bool ok;
354   bool make_backups = false;
355   char *backup_suffix_string;
356   char *version_control_string = NULL;
357   struct cp_options x;
358   char *target_directory = NULL;
359   bool no_target_directory = false;
360   int n_files;
361   char **file;
362
363   initialize_main (&argc, &argv);
364   set_program_name (argv[0]);
365   setlocale (LC_ALL, "");
366   bindtextdomain (PACKAGE, LOCALEDIR);
367   textdomain (PACKAGE);
368
369   atexit (close_stdin);
370
371   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   backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
376
377   while ((c = getopt_long (argc, argv, "bfit:uvS:T", long_options, NULL))
378          != -1)
379     {
380       switch (c)
381         {
382         case 'b':
383           make_backups = true;
384           if (optarg)
385             version_control_string = optarg;
386           break;
387         case 'f':
388           x.interactive = I_ALWAYS_YES;
389           break;
390         case 'i':
391           x.interactive = I_ASK_USER;
392           break;
393         case REPLY_OPTION: /* Deprecated */
394           x.interactive = XARGMATCH ("--reply", optarg,
395                                      reply_args, reply_vals);
396           error (0, 0,
397                  _("the --reply option is deprecated; use -i or -f instead"));
398           break;
399         case STRIP_TRAILING_SLASHES_OPTION:
400           remove_trailing_slashes = true;
401           break;
402         case 't':
403           if (target_directory)
404             error (EXIT_FAILURE, 0, _("multiple target directories specified"));
405           else
406             {
407               struct stat st;
408               if (stat (optarg, &st) != 0)
409                 error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
410               if (! S_ISDIR (st.st_mode))
411                 error (EXIT_FAILURE, 0, _("target %s is not a directory"),
412                        quote (optarg));
413             }
414           target_directory = optarg;
415           break;
416         case 'T':
417           no_target_directory = true;
418           break;
419         case 'u':
420           x.update = true;
421           break;
422         case 'v':
423           x.verbose = true;
424           break;
425         case 'S':
426           make_backups = true;
427           backup_suffix_string = optarg;
428           break;
429         case_GETOPT_HELP_CHAR;
430         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
431         default:
432           usage (EXIT_FAILURE);
433         }
434     }
435
436   n_files = argc - optind;
437   file = argv + optind;
438
439   if (n_files <= !target_directory)
440     {
441       if (n_files <= 0)
442         error (0, 0, _("missing file operand"));
443       else
444         error (0, 0, _("missing destination file operand after %s"),
445                quote (file[0]));
446       usage (EXIT_FAILURE);
447     }
448
449   if (no_target_directory)
450     {
451       if (target_directory)
452         error (EXIT_FAILURE, 0,
453                _("cannot combine --target-directory (-t) "
454                  "and --no-target-directory (-T)"));
455       if (2 < n_files)
456         {
457           error (0, 0, _("extra operand %s"), quote (file[2]));
458           usage (EXIT_FAILURE);
459         }
460     }
461   else if (!target_directory)
462     {
463       assert (2 <= n_files);
464       if (target_directory_operand (file[n_files - 1]))
465         target_directory = file[--n_files];
466       else if (2 < n_files)
467         error (EXIT_FAILURE, 0, _("target %s is not a directory"),
468                quote (file[n_files - 1]));
469     }
470
471   if (backup_suffix_string)
472     simple_backup_suffix = xstrdup (backup_suffix_string);
473
474   x.backup_type = (make_backups
475                    ? xget_version (_("backup type"),
476                                    version_control_string)
477                    : no_backups);
478
479   hash_init ();
480
481   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       if (2 <= n_files)
489         dest_info_init (&x);
490
491       ok = true;
492       for (i = 0; i < n_files; ++i)
493         ok &= movefile (file[i], target_directory, true, &x);
494     }
495   else
496     ok = movefile (file[0], file[1], false, &x);
497
498   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
499 }