doc: remove 'proposed' in regard to $'' descriptions
[coreutils.git] / src / mv.c
blob692943a70da8ae5ef8867f47bf4c85eba9df89f7
1 /* mv -- move or rename files
2 Copyright (C) 1986-2024 Free Software Foundation, Inc.
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Mike Parker, David MacKenzie, and Jim Meyering */
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <selinux/label.h>
25 #include "system.h"
26 #include "argmatch.h"
27 #include "assure.h"
28 #include "backupfile.h"
29 #include "copy.h"
30 #include "cp-hash.h"
31 #include "filenamecat.h"
32 #include "remove.h"
33 #include "renameatu.h"
34 #include "root-dev-ino.h"
35 #include "targetdir.h"
36 #include "priv-set.h"
38 /* The official name of this program (e.g., no 'g' prefix). */
39 #define PROGRAM_NAME "mv"
41 #define AUTHORS \
42 proper_name ("Mike Parker"), \
43 proper_name ("David MacKenzie"), \
44 proper_name ("Jim Meyering")
46 /* For long options that have no equivalent short option, use a
47 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
48 enum
50 DEBUG_OPTION = CHAR_MAX + 1,
51 EXCHANGE_OPTION,
52 NO_COPY_OPTION,
53 STRIP_TRAILING_SLASHES_OPTION
56 static char const *const update_type_string[] =
58 "all", "none", "none-fail", "older", nullptr
60 static enum Update_type const update_type[] =
62 UPDATE_ALL, UPDATE_NONE, UPDATE_NONE_FAIL, UPDATE_OLDER,
64 ARGMATCH_VERIFY (update_type_string, update_type);
66 static struct option const long_options[] =
68 {"backup", optional_argument, nullptr, 'b'},
69 {"context", no_argument, nullptr, 'Z'},
70 {"debug", no_argument, nullptr, DEBUG_OPTION},
71 {"exchange", no_argument, nullptr, EXCHANGE_OPTION},
72 {"force", no_argument, nullptr, 'f'},
73 {"interactive", no_argument, nullptr, 'i'},
74 {"no-clobber", no_argument, nullptr, 'n'}, /* Deprecated. */
75 {"no-copy", no_argument, nullptr, NO_COPY_OPTION},
76 {"no-target-directory", no_argument, nullptr, 'T'},
77 {"strip-trailing-slashes", no_argument, nullptr,
78 STRIP_TRAILING_SLASHES_OPTION},
79 {"suffix", required_argument, nullptr, 'S'},
80 {"target-directory", required_argument, nullptr, 't'},
81 {"update", optional_argument, nullptr, 'u'},
82 {"verbose", no_argument, nullptr, 'v'},
83 {GETOPT_HELP_OPTION_DECL},
84 {GETOPT_VERSION_OPTION_DECL},
85 {nullptr, 0, nullptr, 0}
88 static void
89 rm_option_init (struct rm_options *x)
91 x->ignore_missing_files = false;
92 x->remove_empty_directories = true;
93 x->recursive = true;
94 x->one_file_system = false;
96 /* Should we prompt for removal, too? No. Prompting for the 'move'
97 part is enough. It implies removal. */
98 x->interactive = RMI_NEVER;
99 x->stdin_tty = false;
101 x->verbose = false;
103 /* Since this program may well have to process additional command
104 line arguments after any call to 'rm', that function must preserve
105 the initial working directory, in case one of those is a
106 '.'-relative name. */
107 x->require_restore_cwd = true;
110 static struct dev_ino dev_ino_buf;
111 x->root_dev_ino = get_root_dev_ino (&dev_ino_buf);
112 if (x->root_dev_ino == nullptr)
113 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
114 quoteaf ("/"));
117 x->preserve_all_root = false;
120 static void
121 cp_option_init (struct cp_options *x)
123 bool selinux_enabled = (0 < is_selinux_enabled ());
125 cp_options_default (x);
126 x->copy_as_regular = false; /* FIXME: maybe make this an option */
127 x->reflink_mode = REFLINK_AUTO;
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->install_mode = false;
135 x->one_file_system = false;
136 x->preserve_ownership = true;
137 x->preserve_links = true;
138 x->preserve_mode = true;
139 x->preserve_timestamps = true;
140 x->explicit_no_preserve_mode= false;
141 x->preserve_security_context = selinux_enabled;
142 x->set_security_context = nullptr;
143 x->reduce_diagnostics = false;
144 x->data_copy_required = true;
145 x->require_preserve = false; /* FIXME: maybe make this an option */
146 x->require_preserve_context = false;
147 x->preserve_xattr = true;
148 x->require_preserve_xattr = false;
149 x->recursive = true;
150 x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */
151 x->symbolic_link = false;
152 x->set_mode = false;
153 x->mode = 0;
154 x->stdin_tty = isatty (STDIN_FILENO);
156 x->open_dangling_dest_symlink = false;
157 x->update = false;
158 x->verbose = false;
159 x->dest_info = nullptr;
160 x->src_info = nullptr;
163 /* Move SOURCE onto DEST aka DEST_DIRFD+DEST_RELNAME.
164 Handle cross-file-system moves.
165 If SOURCE is a directory, DEST must not exist.
166 Return true if successful. */
168 static bool
169 do_move (char const *source, char const *dest,
170 int dest_dirfd, char const *dest_relname, const struct cp_options *x)
172 bool copy_into_self;
173 bool rename_succeeded;
174 bool ok = copy (source, dest, dest_dirfd, dest_relname, 0, x,
175 &copy_into_self, &rename_succeeded);
177 if (ok)
179 char const *dir_to_remove;
180 if (copy_into_self)
182 /* In general, when copy returns with copy_into_self set, SOURCE is
183 the same as, or a parent of DEST. In this case we know it's a
184 parent. It doesn't make sense to move a directory into itself, and
185 besides in some situations doing so would give highly unintuitive
186 results. Run this 'mkdir b; touch a c; mv * b' in an empty
187 directory. Here's the result of running echo $(find b -print):
188 b b/a b/b b/b/a b/c. Notice that only file 'a' was copied
189 into b/b. Handle this by giving a diagnostic, removing the
190 copied-into-self directory, DEST ('b/b' in the example),
191 and failing. */
193 dir_to_remove = nullptr;
194 ok = false;
196 else if (rename_succeeded)
198 /* No need to remove anything. SOURCE was successfully
199 renamed to DEST. Or the user declined to rename a file. */
200 dir_to_remove = nullptr;
202 else
204 /* This may mean SOURCE and DEST referred to different devices.
205 It may also conceivably mean that even though they referred
206 to the same device, rename wasn't implemented for that device.
208 E.g., (from Joel N. Weber),
209 [...] there might someday be cases where you can't rename
210 but you can copy where the device name is the same, especially
211 on Hurd. Consider an ftpfs with a primitive ftp server that
212 supports uploading, downloading and deleting, but not renaming.
214 Also, note that comparing device numbers is not a reliable
215 check for 'can-rename'. Some systems can be set up so that
216 files from many different physical devices all have the same
217 st_dev field. This is a feature of some NFS mounting
218 configurations.
220 We reach this point if SOURCE has been successfully copied
221 to DEST. Now we have to remove SOURCE.
223 This function used to resort to copying only when rename
224 failed and set errno to EXDEV. */
226 dir_to_remove = source;
229 if (dir_to_remove != nullptr)
231 struct rm_options rm_options;
232 enum RM_status status;
233 char const *dir[2];
235 rm_option_init (&rm_options);
236 rm_options.verbose = x->verbose;
237 dir[0] = dir_to_remove;
238 dir[1] = nullptr;
240 status = rm ((void *) dir, &rm_options);
241 affirm (VALID_STATUS (status));
242 if (status == RM_ERROR)
243 ok = false;
247 return ok;
250 void
251 usage (int status)
253 if (status != EXIT_SUCCESS)
254 emit_try_help ();
255 else
257 printf (_("\
258 Usage: %s [OPTION]... [-T] SOURCE DEST\n\
259 or: %s [OPTION]... SOURCE... DIRECTORY\n\
260 or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
262 program_name, program_name, program_name);
263 fputs (_("\
264 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
265 "), stdout);
267 emit_mandatory_arg_note ();
269 fputs (_("\
270 --backup[=CONTROL] make a backup of each existing destination file\
272 -b like --backup but does not accept an argument\n\
273 "), stdout);
274 fputs (_("\
275 --debug explain how a file is copied. Implies -v\n\
276 "), stdout);
277 fputs (_("\
278 --exchange exchange source and destination\n\
279 "), stdout);
280 fputs (_("\
281 -f, --force do not prompt before overwriting\n\
282 -i, --interactive prompt before overwrite\n\
283 -n, --no-clobber do not overwrite an existing file\n\
284 If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
285 "), stdout);
286 fputs (_("\
287 --no-copy do not copy if renaming fails\n\
288 --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
289 argument\n\
290 -S, --suffix=SUFFIX override the usual backup suffix\n\
291 "), stdout);
292 fputs (_("\
293 -t, --target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY\n\
294 -T, --no-target-directory treat DEST as a normal file\n\
295 "), stdout);
296 fputs (_("\
297 --update[=UPDATE] control which existing files are updated;\n\
298 UPDATE={all,none,none-fail,older(default)}.\n\
299 -u equivalent to --update[=older]. See below\n\
300 "), stdout);
301 fputs (_("\
302 -v, --verbose explain what is being done\n\
303 -Z, --context set SELinux security context of destination\n\
304 file to default type\n\
305 "), stdout);
306 fputs (HELP_OPTION_DESCRIPTION, stdout);
307 fputs (VERSION_OPTION_DESCRIPTION, stdout);
308 emit_update_parameters_note ();
309 emit_backup_suffix_note ();
310 emit_ancillary_info (PROGRAM_NAME);
312 exit (status);
316 main (int argc, char **argv)
318 int c;
319 bool ok;
320 bool make_backups = false;
321 char const *backup_suffix = nullptr;
322 char *version_control_string = nullptr;
323 struct cp_options x;
324 bool remove_trailing_slashes = false;
325 char const *target_directory = nullptr;
326 bool no_target_directory = false;
327 int n_files;
328 char **file;
329 bool selinux_enabled = (0 < is_selinux_enabled ());
330 bool no_clobber = false;
332 initialize_main (&argc, &argv);
333 set_program_name (argv[0]);
334 setlocale (LC_ALL, "");
335 bindtextdomain (PACKAGE, LOCALEDIR);
336 textdomain (PACKAGE);
338 atexit (close_stdin);
340 cp_option_init (&x);
342 /* Try to disable the ability to unlink a directory. */
343 priv_set_remove_linkdir ();
345 while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr))
346 != -1)
348 switch (c)
350 case 'b':
351 make_backups = true;
352 if (optarg)
353 version_control_string = optarg;
354 break;
355 case 'f':
356 x.interactive = I_ALWAYS_YES;
357 break;
358 case 'i':
359 x.interactive = I_ASK_USER;
360 break;
361 case 'n':
362 x.interactive = I_ALWAYS_SKIP;
363 no_clobber = true;
364 x.update = false;
365 break;
366 case DEBUG_OPTION:
367 x.debug = x.verbose = true;
368 break;
369 case EXCHANGE_OPTION:
370 x.exchange = true;
371 break;
372 case NO_COPY_OPTION:
373 x.no_copy = true;
374 break;
375 case STRIP_TRAILING_SLASHES_OPTION:
376 remove_trailing_slashes = true;
377 break;
378 case 't':
379 if (target_directory)
380 error (EXIT_FAILURE, 0, _("multiple target directories specified"));
381 target_directory = optarg;
382 break;
383 case 'T':
384 no_target_directory = true;
385 break;
386 case 'u':
387 if (! no_clobber)
389 enum Update_type update_opt = UPDATE_OLDER;
390 if (optarg)
391 update_opt = XARGMATCH ("--update", optarg,
392 update_type_string, update_type);
393 if (update_opt == UPDATE_ALL)
395 /* Default mv operation. */
396 x.update = false;
397 x.interactive = I_UNSPECIFIED;
399 else if (update_opt == UPDATE_NONE)
401 x.update = false;
402 x.interactive = I_ALWAYS_SKIP;
404 else if (update_opt == UPDATE_NONE_FAIL)
406 x.update = false;
407 x.interactive = I_ALWAYS_NO;
409 else if (update_opt == UPDATE_OLDER)
411 x.update = true;
412 x.interactive = I_UNSPECIFIED;
415 break;
416 case 'v':
417 x.verbose = true;
418 break;
419 case 'S':
420 make_backups = true;
421 backup_suffix = optarg;
422 break;
423 case 'Z':
424 /* As a performance enhancement, don't even bother trying
425 to "restorecon" when not on an selinux-enabled kernel. */
426 if (selinux_enabled)
428 x.preserve_security_context = false;
429 x.set_security_context = selabel_open (SELABEL_CTX_FILE,
430 nullptr, 0);
431 if (! x.set_security_context)
432 error (0, errno, _("warning: ignoring --context"));
434 break;
435 case_GETOPT_HELP_CHAR;
436 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
437 default:
438 usage (EXIT_FAILURE);
442 n_files = argc - optind;
443 file = argv + optind;
445 if (n_files <= !target_directory)
447 if (n_files <= 0)
448 error (0, 0, _("missing file operand"));
449 else
450 error (0, 0, _("missing destination file operand after %s"),
451 quoteaf (file[0]));
452 usage (EXIT_FAILURE);
455 struct stat sb;
456 sb.st_mode = 0;
457 int target_dirfd = AT_FDCWD;
458 if (no_target_directory)
460 if (target_directory)
461 error (EXIT_FAILURE, 0,
462 _("cannot combine --target-directory (-t) "
463 "and --no-target-directory (-T)"));
464 if (2 < n_files)
466 error (0, 0, _("extra operand %s"), quoteaf (file[2]));
467 usage (EXIT_FAILURE);
470 else if (target_directory)
472 target_dirfd = target_directory_operand (target_directory, &sb);
473 if (! target_dirfd_valid (target_dirfd))
474 error (EXIT_FAILURE, errno, _("target directory %s"),
475 quoteaf (target_directory));
477 else
479 char const *lastfile = file[n_files - 1];
480 if (n_files == 2 && !x.exchange)
481 x.rename_errno = (renameatu (AT_FDCWD, file[0], AT_FDCWD, lastfile,
482 RENAME_NOREPLACE)
483 ? errno : 0);
484 if (x.rename_errno != 0)
486 int fd = target_directory_operand (lastfile, &sb);
487 if (target_dirfd_valid (fd))
489 x.rename_errno = -1;
490 target_dirfd = fd;
491 target_directory = lastfile;
492 n_files--;
494 else
496 /* The last operand LASTFILE cannot be opened as a directory.
497 If there are more than two operands, report an error.
499 Also, report an error if LASTFILE is known to be a directory
500 even though it could not be opened, which can happen if
501 opening failed with EACCES on a platform lacking O_PATH.
502 In this case use stat to test whether LASTFILE is a
503 directory, in case opening a non-directory with (O_SEARCH
504 | O_DIRECTORY) failed with EACCES not ENOTDIR. */
505 int err = errno;
506 if (2 < n_files
507 || (O_PATHSEARCH == O_SEARCH && err == EACCES
508 && (sb.st_mode != 0 || stat (lastfile, &sb) == 0)
509 && S_ISDIR (sb.st_mode)))
510 error (EXIT_FAILURE, err, _("target %s"), quoteaf (lastfile));
515 /* Handle the ambiguity in the semantics of mv induced by the
516 varying semantics of the rename function. POSIX-compatible
517 systems (e.g., GNU/Linux) have a rename function that honors a
518 trailing slash in the source, while others (Solaris 9, FreeBSD
519 7.2) have a rename function that ignores it. */
520 if (remove_trailing_slashes)
521 for (int i = 0; i < n_files; i++)
522 strip_trailing_slashes (file[i]);
524 if (make_backups
525 && (x.exchange
526 || x.interactive == I_ALWAYS_SKIP
527 || x.interactive == I_ALWAYS_NO))
529 error (0, 0,
530 _("cannot combine --backup with "
531 "--exchange, -n, or --update=none-fail"));
532 usage (EXIT_FAILURE);
535 x.backup_type = (make_backups
536 ? xget_version (_("backup type"),
537 version_control_string)
538 : no_backups);
539 set_simple_backup_suffix (backup_suffix);
541 hash_init ();
543 if (target_directory)
545 /* Initialize the hash table only if we'll need it.
546 The problem it is used to detect can arise only if there are
547 two or more files to move. */
548 if (2 <= n_files)
549 dest_info_init (&x);
551 ok = true;
552 for (int i = 0; i < n_files; ++i)
554 x.last_file = i + 1 == n_files;
555 char const *source = file[i];
556 char const *source_basename = last_component (source);
557 char *dest_relname;
558 char *dest = file_name_concat (target_directory, source_basename,
559 &dest_relname);
560 strip_trailing_slashes (dest_relname);
561 ok &= do_move (source, dest, target_dirfd, dest_relname, &x);
562 free (dest);
565 else
567 x.last_file = true;
568 ok = do_move (file[0], file[1], AT_FDCWD, file[1], &x);
571 main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);