Skip to content

Commit

Permalink
cp: accept the --reflink option
Browse files Browse the repository at this point in the history
* NEWS: Mention it.
* doc/coreutils.texi (cp invocation): Describe it.
* src/copy.h (struct cp_options) [reflink]: New member.
* src/copy.c (usage): Describe it.
(copy_reg): If reflink is true try to clone the file.
(main): Check for --reflink.
(cp_option_init): Initialize the new member.
* src/install.c (cp_option_init): Initialize the new member.
* src/mv.c (cp_option_init): Likewise.
* tests/cp/sparse: Add a new test case.
  • Loading branch information
giuseppe authored and Jim Meyering committed Aug 7, 2009
1 parent ff159a6 commit a1d7469
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 6 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ GNU coreutils NEWS -*- outline -*-

chroot now accepts the options --userspec and --groups.

cp accepts a new option, --reflink: create a lightweight copy
using copy-on-write (COW). This is currently supported only on
btrfs file systems.

cp now preserves time stamps on symbolic links, when possible

cp, install, mv: take advantage of btrfs' O(1) copy-on-write feature
Expand Down
9 changes: 9 additions & 0 deletions doc/coreutils.texi
Original file line number Diff line number Diff line change
Expand Up @@ -7543,6 +7543,15 @@ 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
Perform a lightweight, copy-on-write (COW) copy.
Copying with this option can succeed only on some relatively new 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.

@item --remove-destination
@opindex --remove-destination
Remove each existing destination file before attempting to open it
Expand Down
16 changes: 10 additions & 6 deletions src/copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -624,13 +624,16 @@ copy_reg (char const *src_name, char const *dst_name,
goto close_src_and_dst_desc;
}

/* If --sparse=auto is in effect, attempt a btrfs clone operation.
If the operation is not supported or it fails then copy the file
in the usual way. */
bool copied = (x->sparse_mode == SPARSE_AUTO
&& clone_file (dest_desc, source_desc) == 0);
if (x->reflink)
{
if (clone_file (dest_desc, source_desc))
{
error (0, errno, _("failed to clone %s"), quote (dst_name));
return_val = false;
}
goto close_src_and_dst_desc;
}

if (!copied)
{
typedef uintptr_t word;
off_t n_read_total = 0;
Expand Down Expand Up @@ -2232,6 +2235,7 @@ valid_options (const struct cp_options *co)
assert (VALID_BACKUP_TYPE (co->backup_type));
assert (VALID_SPARSE_MODE (co->sparse_mode));
assert (!(co->hard_link && co->symbolic_link));
assert (!(co->reflink && co->sparse_mode != SPARSE_AUTO));
return true;
}

Expand Down
3 changes: 3 additions & 0 deletions src/copy.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ 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;

/* 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
that was specified on the command line. Use it to avoid clobbering
Expand Down
14 changes: 14 additions & 0 deletions src/cp.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ enum
NO_PRESERVE_ATTRIBUTES_OPTION,
PARENTS_OPTION,
PRESERVE_ATTRIBUTES_OPTION,
REFLINK_OPTION,
SPARSE_OPTION,
STRIP_TRAILING_SLASHES_OPTION,
UNLINK_DEST_BEFORE_OPENING
Expand Down Expand Up @@ -121,6 +122,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},
{"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
{"suffix", required_argument, NULL, 'S'},
{"symbolic-link", no_argument, NULL, 's'},
Expand Down Expand Up @@ -190,6 +192,7 @@ 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\
--remove-destination remove each existing destination file before\n\
attempting to open it (contrast with --force)\n\
"), stdout);
Expand Down Expand Up @@ -752,6 +755,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->preserve_ownership = false;
x->preserve_links = false;
Expand Down Expand Up @@ -916,6 +920,10 @@ main (int argc, char **argv)
sparse_type_string, sparse_type);
break;

case REFLINK_OPTION:
x.reflink = true;
break;

case 'a': /* Like -dR --preserve=all with reduced failure diagnostics. */
x.dereference = DEREF_NEVER;
x.preserve_links = true;
Expand Down Expand Up @@ -1076,6 +1084,12 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}

if (x.reflink && x.sparse_mode != SPARSE_AUTO)
{
error (0, 0, _("--reflink can be used only with --sparse=auto"));
usage (EXIT_FAILURE);
}

if (backup_suffix_string)
simple_backup_suffix = xstrdup (backup_suffix_string);

Expand Down
1 change: 1 addition & 0 deletions src/install.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ cp_option_init (struct cp_options *x)
{
cp_options_default (x);
x->copy_as_regular = true;
x->reflink = false;
x->dereference = DEREF_ALWAYS;
x->unlink_dest_before_opening = true;
x->unlink_dest_after_failed_open = false;
Expand Down
1 change: 1 addition & 0 deletions src/mv.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +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->dereference = DEREF_NEVER;
x->unlink_dest_before_opening = false;
x->unlink_dest_after_failed_open = false;
Expand Down
4 changes: 4 additions & 0 deletions tests/cp/sparse
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ cp --sparse=always sparse copy || fail=1
# Ensure that the copy has the same block count as the original.
test `stat --printf %b copy` -le `stat --printf %b sparse` || fail=1

# Ensure that --sparse={always,never} with --reflink fail.
cp --sparse=always --reflink sparse copy && fail=1
cp --sparse=never --reflink sparse copy && fail=1

Exit $fail

0 comments on commit a1d7469

Please sign in to comment.