printing a summary to stderr.
[bug introduced in coreutils-6.11]
+** New features
+
+ cp --reflink accepts a new "auto" parameter which falls back to
+ a standard copy if creating a copy-on-write clone is not possible.
* Noteworthy changes in release 7.5 (2009-08-20) [stable]
unless you also specify @option{-P}, as @acronym{POSIX} allows
implementations that dereference symbolic links by default.
-@item --reflink
-@opindex --reflink
+@item --reflink[=@var{when}]
+@opindex --reflink[=@var{when}]
+@cindex COW
+@cindex clone
+@cindex copy on write
Perform a lightweight, copy-on-write (COW) copy.
-Copying with this option can succeed only on some relatively new file systems.
+Copying with this option can succeed only on some file systems.
Once it has succeeded, beware that the source and destination files
share the same disk data blocks as long as they remain unmodified.
Thus, if a disk I/O error affects data blocks of one of the files,
the other suffers the exact same fate.
+The @var{when} value can be one of the following:
+
+@table @samp
+@item always
+The default behavior: if the copy-on-write operation is not supported
+then report the failure for each file and exit with a failure status.
+
+@item auto
+If the copy-on-write operation is not supported then fall back
+to the standard copy behaviour.
+@end table
+
+
@item --remove-destination
@opindex --remove-destination
Remove each existing destination file before attempting to open it
goto close_src_and_dst_desc;
}
- if (x->reflink)
+ if (x->reflink_mode)
{
- if (clone_file (dest_desc, source_desc))
+ bool clone_ok = clone_file (dest_desc, source_desc) == 0;
+ if (clone_ok || x->reflink_mode == REFLINK_ALWAYS)
{
- error (0, errno, _("failed to clone %s"), quote (dst_name));
- return_val = false;
+ if (!clone_ok)
+ {
+ error (0, errno, _("failed to clone %s"), quote (dst_name));
+ return_val = false;
+ }
+ goto close_src_and_dst_desc;
}
- goto close_src_and_dst_desc;
}
{
assert (co != NULL);
assert (VALID_BACKUP_TYPE (co->backup_type));
assert (VALID_SPARSE_MODE (co->sparse_mode));
+ assert (VALID_REFLINK_MODE (co->reflink_mode));
assert (!(co->hard_link && co->symbolic_link));
- assert (!(co->reflink && co->sparse_mode != SPARSE_AUTO));
+ assert (!
+ (co->reflink_mode == REFLINK_ALWAYS
+ && co->sparse_mode != SPARSE_AUTO));
return true;
}
SPARSE_ALWAYS
};
+/* Control creation of COW files. */
+enum Reflink_type
+{
+ /* Default to a standard copy. */
+ REFLINK_NEVER,
+
+ /* Try a COW copy and fall back to a standard copy. */
+ REFLINK_AUTO,
+
+ /* Require a COW copy and fail if not available. */
+ REFLINK_ALWAYS
+};
+
/* This type is used to help mv (via copy.c) distinguish these cases. */
enum Interactive
{
|| (Mode) == SPARSE_AUTO \
|| (Mode) == SPARSE_ALWAYS)
+# define VALID_REFLINK_MODE(Mode) \
+ ((Mode) == REFLINK_NEVER \
+ || (Mode) == REFLINK_AUTO \
+ || (Mode) == REFLINK_ALWAYS)
+
/* These options control how files are copied by at least the
following programs: mv (when rename doesn't work), cp, install.
So, if you add a new member, be sure to initialize it in
such a symlink) and returns false. */
bool open_dangling_dest_symlink;
- /* If true, attempt to clone the file instead of copying it. */
- bool reflink;
+ /* Control creation of COW files. */
+ enum Reflink_type reflink_mode;
/* This is a set of destination name/inode/dev triples. Each such triple
represents a file we have created corresponding to a source file name
};
ARGMATCH_VERIFY (sparse_type_string, sparse_type);
+static char const *const reflink_type_string[] =
+{
+ "auto", "always", NULL
+};
+static enum Reflink_type const reflink_type[] =
+{
+ REFLINK_AUTO, REFLINK_ALWAYS
+};
+ARGMATCH_VERIFY (reflink_type_string, reflink_type);
+
static struct option const long_opts[] =
{
{"archive", no_argument, NULL, 'a'},
{"recursive", no_argument, NULL, 'R'},
{"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING},
{"sparse", required_argument, NULL, SPARSE_OPTION},
- {"reflink", no_argument, NULL, REFLINK_OPTION},
+ {"reflink", optional_argument, NULL, REFLINK_OPTION},
{"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
{"suffix", required_argument, NULL, 'S'},
{"symbolic-link", no_argument, NULL, 's'},
"), stdout);
fputs (_("\
-R, -r, --recursive copy directories recursively\n\
- --reflink perform a lightweight (CoW/clone) copy\n\
+ --reflink[=WHEN] control clone/CoW copies. See below.\n\
--remove-destination remove each existing destination file before\n\
attempting to open it (contrast with --force)\n\
"), stdout);
fputs (_("\
- --sparse=WHEN control creation of sparse files\n\
+ --sparse=WHEN control creation of sparse files. See below.\n\
--strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
argument\n\
"), stdout);
selected by --sparse=auto. Specify --sparse=always to create a sparse DEST\n\
file whenever the SOURCE file contains a long enough sequence of zero bytes.\n\
Use --sparse=never to inhibit creation of sparse files.\n\
+\n\
+When --reflink[=always] is specified, perform a lightweight copy, where the\n\
+data blocks are copied only when modified. If this is not possible the copy\n\
+fails, or if --reflink=auto is specified, fall back to a standard copy.\n\
"), stdout);
fputs (_("\
\n\
x->interactive = I_UNSPECIFIED;
x->move_mode = false;
x->one_file_system = false;
- x->reflink = false;
+ x->reflink_mode = REFLINK_NEVER;
x->preserve_ownership = false;
x->preserve_links = false;
break;
case REFLINK_OPTION:
- x.reflink = true;
+ if (optarg == NULL)
+ x.reflink_mode = REFLINK_ALWAYS;
+ else
+ x.reflink_mode = XARGMATCH ("--reflink", optarg,
+ reflink_type_string, reflink_type);
break;
case 'a': /* Like -dR --preserve=all with reduced failure diagnostics. */
usage (EXIT_FAILURE);
}
- if (x.reflink && x.sparse_mode != SPARSE_AUTO)
+ if (x.reflink_mode == REFLINK_ALWAYS && x.sparse_mode != SPARSE_AUTO)
{
error (0, 0, _("--reflink can be used only with --sparse=auto"));
usage (EXIT_FAILURE);
{
cp_options_default (x);
x->copy_as_regular = true;
- x->reflink = false;
+ x->reflink_mode = REFLINK_NEVER;
x->dereference = DEREF_ALWAYS;
x->unlink_dest_before_opening = true;
x->unlink_dest_after_failed_open = false;
cp_options_default (x);
x->copy_as_regular = false; /* FIXME: maybe make this an option */
- x->reflink = false;
+ x->reflink_mode = REFLINK_NEVER;
x->dereference = DEREF_NEVER;
x->unlink_dest_before_opening = false;
x->unlink_dest_after_failed_open = false;
cp/proc-short-read \
cp/proc-zero-len \
cp/r-vs-symlink \
+ cp/reflink-auto \
cp/same-file \
cp/slink-2-slink \
cp/sparse \
--- /dev/null
+#!/bin/sh
+# Test cp --reflink=auto
+
+# Copyright (C) 2009 Free Software Foundation, Inc.
+
+# 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 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
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# 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, see <http://www.gnu.org/licenses/>.
+
+if test "$VERBOSE" = yes; then
+ set -x
+ cp --version
+fi
+
+. $srcdir/test-lib.sh
+
+cleanup_() { rm -rf "$other_partition_tmpdir"; }
+. "$abs_srcdir/other-fs-tmpdir"
+a_other="$other_partition_tmpdir/a"
+rm -f $a_other || framework_failure
+
+echo non_zero_size > "$a_other"
+
+# we shouldn't be able to reflink() files on separate partitions
+cp --reflink "$a_other" b && fail=1
+
+# --reflink=auto should fall back to a normal copy
+cp --reflink=auto "$a_other" b || fail=1
+test -s b || fail=1
+
+# --reflink=auto should allow --sparse for fallback copies.
+# This command can be used to create minimal sized copies.
+cp --reflink=auto --sparse=always "$a_other" b || fail=1
+test -s b || fail=1
+
+Exit $fail