summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/copy.c19
-rw-r--r--src/copy.h22
-rw-r--r--src/cp.c30
-rw-r--r--src/install.c2
-rw-r--r--src/mv.c2
5 files changed, 59 insertions, 16 deletions
diff --git a/src/copy.c b/src/copy.c
index 5f84f7e43..17be63e56 100644
--- a/src/copy.c
+++ b/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;
}
diff --git a/src/copy.h b/src/copy.h
index cdef64a7f..745533aec 100644
--- a/src/copy.h
+++ b/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
diff --git a/src/cp.c b/src/cp.c
index 7424f9b3c..2ba1dbf09 100644
--- a/src/cp.c
+++ b/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);
diff --git a/src/install.c b/src/install.c
index a4e0cab32..fafa21a3c 100644
--- a/src/install.c
+++ b/src/install.c
@@ -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;
diff --git a/src/mv.c b/src/mv.c
index 814e55dfe..29fdde308 100644
--- a/src/mv.c
+++ b/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;