mirror of
https://github.com/coreutils/coreutils.git
synced 2024-12-18 06:18:25 +08:00
cp --reflink: add an "auto" parameter to fall back to a normal copy
* doc/coreutils.texi (cp invocation): Document the new "auto" and "always" options to --reflink. * src/copy.c (copy_reg): Fall back to a standard copy when reflink() is not supported and --reflink=auto specified. * src/copy.h [struct cp_options] (reflink): Change type s/bool/enum/. * src/cp.c (usage): Describe the --reflink={always,auto} options and expand a little on what --reflink does. (main): parse the new parameters to --reflink and allow all --sparse options with --reflink=auto. * src/install.c (cp_option_init): Init the enum instead of bool. * src/mv.c (cp_option_init): Likewise. * tests/cp/reflink-auto: A new test for falling back to normal copy. * tests/Makefile.am: Reference the new test. * NEWS: Mention the new feature.
This commit is contained in:
parent
f296cf4052
commit
72f98388c3
4
NEWS
4
NEWS
@ -13,6 +13,10 @@ GNU coreutils NEWS -*- outline -*-
|
||||
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]
|
||||
|
||||
|
@ -7543,15 +7543,31 @@ Also, it is not portable to use @option{-R} to copy symbolic links
|
||||
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
|
||||
|
19
src/copy.c
19
src/copy.c
@ -635,14 +635,18 @@ copy_reg (char const *src_name, char const *dst_name,
|
||||
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;
|
||||
}
|
||||
|
||||
{
|
||||
@ -2248,8 +2252,11 @@ valid_options (const struct cp_options *co)
|
||||
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;
|
||||
}
|
||||
|
||||
|
22
src/copy.h
22
src/copy.h
@ -43,6 +43,19 @@ enum Sparse_type
|
||||
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
|
||||
{
|
||||
@ -73,6 +86,11 @@ enum Dereference_symlink
|
||||
|| (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
|
||||
@ -219,8 +237,8 @@ struct cp_options
|
||||
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
|
||||
|
30
src/cp.c
30
src/cp.c
@ -102,6 +102,16 @@ static enum Sparse_type const sparse_type[] =
|
||||
};
|
||||
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'},
|
||||
@ -122,7 +132,7 @@ static struct option const long_opts[] =
|
||||
{"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'},
|
||||
@ -192,12 +202,12 @@ Mandatory arguments to long options are mandatory for short options too.\n\
|
||||
"), 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);
|
||||
@ -223,6 +233,10 @@ corresponding DEST file is made sparse as well. That is the behavior\n\
|
||||
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\
|
||||
@ -755,7 +769,7 @@ cp_option_init (struct cp_options *x)
|
||||
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;
|
||||
@ -921,7 +935,11 @@ main (int argc, char **argv)
|
||||
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. */
|
||||
@ -1084,7 +1102,7 @@ main (int argc, char **argv)
|
||||
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);
|
||||
|
@ -269,7 +269,7 @@ cp_option_init (struct cp_options *x)
|
||||
{
|
||||
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;
|
||||
|
2
src/mv.c
2
src/mv.c
@ -105,7 +105,7 @@ cp_option_init (struct cp_options *x)
|
||||
|
||||
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;
|
||||
|
@ -291,6 +291,7 @@ TESTS = \
|
||||
cp/proc-short-read \
|
||||
cp/proc-zero-len \
|
||||
cp/r-vs-symlink \
|
||||
cp/reflink-auto \
|
||||
cp/same-file \
|
||||
cp/slink-2-slink \
|
||||
cp/sparse \
|
||||
|
45
tests/cp/reflink-auto
Executable file
45
tests/cp/reflink-auto
Executable file
@ -0,0 +1,45 @@
|
||||
#!/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
|
Loading…
Reference in New Issue
Block a user