cp: optional --reflink support
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 13 Jul 2018 18:30:02 +0000 (20:30 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 13 Jul 2018 18:30:02 +0000 (20:30 +0200)
function                                             old     new   delta
cp_main                                              428     512     +84
copy_file                                           1676    1742     +66

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
coreutils/cp.c
include/libbb.h
libbb/copy_file.c

index 455bffbba1299f03aca7fc63e1337be2a6310626..b623aaf3381339763f6bb9f6eb3a2a80b6a15295 100644 (file)
 //config:      help
 //config:      Enable long options.
 //config:      Also add support for --parents option.
+//config:
+//config:config FEATURE_CP_REFLINK
+//config:      bool "Enable --reflink[=auto]
+//config:      default y
+//config:      depends on FEATURE_CP_LONG_OPTIONS
 
 //applet:IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp))
 /* NOEXEC despite cases when it can be a "runner" (cp -r LARGE_DIR NEW_DIR) */
@@ -72,10 +77,14 @@ int cp_main(int argc, char **argv)
 #if ENABLE_FEATURE_CP_LONG_OPTIONS
                /*OPT_rmdest  = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */
                OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1),
+               OPT_reflink = 1 << (FILEUTILS_CP_OPTNUM+2),
 #endif
        };
 
 #if ENABLE_FEATURE_CP_LONG_OPTIONS
+# if ENABLE_FEATURE_CP_REFLINK
+       char *reflink = NULL;
+# endif
        flags = getopt32long(argv, "^"
                FILEUTILS_CP_OPTSTR
                "\0"
@@ -99,7 +108,22 @@ int cp_main(int argc, char **argv)
                "update\0"         No_argument "u"
                "remove-destination\0" No_argument "\xff"
                "parents\0"        No_argument "\xfe"
+# if ENABLE_FEATURE_CP_REFLINK
+               "reflink\0"        Optional_argument "\xfd"
+               , &reflink
+# endif
        );
+# if ENABLE_FEATURE_CP_REFLINK
+       BUILD_BUG_ON(OPT_reflink != FILEUTILS_REFLINK);
+       if (flags & FILEUTILS_REFLINK) {
+               if (!reflink)
+                       flags |= FILEUTILS_REFLINK_ALWAYS;
+               else if (strcmp(reflink, "always") == 0)
+                       flags |= FILEUTILS_REFLINK_ALWAYS;
+               else if (strcmp(reflink, "auto") != 0)
+                       bb_show_usage();
+       }
+# endif
 #else
        flags = getopt32(argv, "^"
                FILEUTILS_CP_OPTSTR
index d4ba031dfe129dcd6f877235a82ca185e0066547..94caba2bb2a81e16d823daa7c84153f8379f55c5 100644 (file)
@@ -410,6 +410,9 @@ enum {      /* cp.c, mv.c, install.c depend on these values. CAREFUL when changing th
        FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 15, /* -c */
 #endif
        FILEUTILS_RMDEST          = 1 << (16 - !ENABLE_SELINUX), /* --remove-destination */
+       /* bit 17 skipped for "cp --parents" */
+       FILEUTILS_REFLINK         = 1 << (18 - !ENABLE_SELINUX), /* cp --reflink=auto */
+       FILEUTILS_REFLINK_ALWAYS  = 1 << (19 - !ENABLE_SELINUX), /* cp --reflink[=always] */
        /*
         * Hole. cp may have some bits set here,
         * they should not affect remove_file()/copy_file()
index 1b8befd65440e394dd0e0be119926aac3b6b6975..98bd4fe72cadfa55460a3af4f1ed095872b05e6c 100644 (file)
@@ -339,9 +339,28 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
                                freecon(con);
                        }
                }
+#endif
+#if ENABLE_FEATURE_CP_REFLINK
+# undef BTRFS_IOCTL_MAGIC
+# define BTRFS_IOCTL_MAGIC 0x94
+# undef BTRFS_IOC_CLONE
+# define BTRFS_IOC_CLONE _IOW (BTRFS_IOCTL_MAGIC, 9, int)
+               if (flags & FILEUTILS_REFLINK) {
+                       retval = ioctl(dst_fd, BTRFS_IOC_CLONE, src_fd);
+                       if (retval == 0)
+                               goto do_close;
+                       /* reflink did not work */
+                       if (flags & FILEUTILS_REFLINK_ALWAYS) {
+                               bb_perror_msg("failed to clone '%s' from '%s'", dest, source);
+                               goto do_close;
+                       }
+                       /* fall through to standard copy */
+                       retval = 0;
+               }
 #endif
                if (bb_copyfd_eof(src_fd, dst_fd) == -1)
                        retval = -1;
+ IF_FEATURE_CP_REFLINK(do_close:)
                /* Careful with writing... */
                if (close(dst_fd) < 0) {
                        bb_perror_msg("error writing to '%s'", dest);