bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] cp: add --reflink=auto option to fall back to standard copy


From: Pádraig Brady
Subject: [PATCH] cp: add --reflink=auto option to fall back to standard copy
Date: Wed, 26 Aug 2009 00:46:58 +0100
User-agent: Thunderbird 2.0.0.6 (X11/20071008)

Attached is a patch to add a new parameter to the --reflink option,
which will perform a standard copy if COW is not supported.

comments appreciated.
cheers,
Pádraig.
>From b1ef11949b95446e6df08ce128d38554715e500b Mon Sep 17 00:00:00 2001
From: =?utf-8?q?P=C3=A1draig=20Brady?= <address@hidden>
Date: Wed, 26 Aug 2009 00:32:43 +0100
Subject: [PATCH] cp: add --reflink=auto option to fall back to standard 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: Change the reflink option from bool to an 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.
* src/install.c (cp_option_init): Init the enum instead of bool
* src/mv.c (cp_option_init): Likewise.
---
 doc/coreutils.texi |   22 +++++++++++++++++++---
 src/copy.c         |   17 +++++++++++------
 src/copy.h         |   22 ++++++++++++++++++++--
 src/cp.c           |   30 ++++++++++++++++++++++++------
 src/install.c      |    2 +-
 src/mv.c           |    2 +-
 6 files changed, 76 insertions(+), 19 deletions(-)

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 8e1b73d..f4be2c2 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -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.
 
address@hidden --reflink
address@hidden --reflink
address@hidden address@hidden
address@hidden address@hidden
address@hidden COW
address@hidden clone
address@hidden 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:
+
address@hidden @samp
address@hidden 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.
+
address@hidden auto
+If the copy-on-write operation is not supported then fall back
+to the standard copy behaviour.
address@hidden table
+
+
 @item --remove-destination
 @opindex --remove-destination
 Remove each existing destination file before attempting to open it
diff --git a/src/copy.c b/src/copy.c
index 238764a..ed8ae88 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -629,14 +629,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;
     }
 
   {
@@ -2242,8 +2246,9 @@ 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 && co->sparse_mode != SPARSE_AUTO));
   return true;
 }
 
diff --git a/src/copy.h b/src/copy.h
index cdef64a..745533a 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 7424f9b..9b915f0 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 && 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 a4e0cab..fafa21a 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 814e55d..29fdde3 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;
-- 
1.6.2.5


reply via email to

[Prev in Thread] Current Thread [Next in Thread]