>From 34556c4e36effc165032724cd37e6a4d2682f880 Mon Sep 17 00:00:00 2001 From: Jie Liu Date: Tue, 3 May 2011 22:21:41 +0800 Subject: [PATCH 1/1] copy: add OCFS2 reflink support * src/copy.c (copy_reg): When cp is invoked with '--reflink=[WHEN]', try OCFS2 reflink file first, if it fails, try btrfs clone file later if possible. The reflink will fail with EEXIST if the destination file already exists on OCFS2, in this case, it means the user intended to do OCFS2 reflink rather than btrfs clone, so we need not try the latter. Signed-off-by: Jie Liu --- src/copy.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 8 deletions(-) Index: src/copy.c =================================================================== --- src/copy.c.orig 2014-02-20 17:50:19.791236017 +0100 +++ src/copy.c 2014-02-20 17:54:06.068824055 +0100 @@ -221,6 +221,46 @@ sparse_copy (int src_fd, int dest_fd, ch return true; } +/* Perform the OCFS2 CoW reflink ioctl(2) operation if possible. + When using '-p' option, the file's default attributes(i.e. mode,timestamp, + ownership and security context if possbile) are reflinked to the destination + file as well, we will then skip over the standard preserve process for such + attributes. Upon success, return 0, Otherwise, return -1 and set errno. */ +static inline int +reflink_file (char const *src_name, char const *dst_name, + int src_fd, bool preserve_attrs) +{ +#ifdef __linux__ +# ifndef REFLINK_ATTR_NONE +# define REFLINK_ATTR_NONE 0 +# endif +# ifndef REFLINK_ATTR_PRESERVE +# define REFLINK_ATTR_PRESERVE 1 +# endif +# ifndef OCFS2_IOC_REFLINK + struct reflink_arguments { + uint64_t old_path; + uint64_t new_path; + uint64_t preserve; + }; +# define OCFS2_IOC_REFLINK _IOW ('o', 4, struct reflink_arguments) +# endif + struct reflink_arguments args = { + .old_path = (unsigned long) src_name, + .new_path = (unsigned long) dst_name, + .preserve = preserve_attrs ? REFLINK_ATTR_PRESERVE : REFLINK_ATTR_NONE, + }; + return ioctl (src_fd, OCFS2_IOC_REFLINK, &args); +#else + (void) src_name; + (void) dst_name; + (void) src_fd; + (void) preserve_attrs; + errno = ENOTSUP; + return -1; +#endif +} + /* Perform the O(1) btrfs clone operation, if possible. Upon success, return 0. Otherwise, return -1 and set errno. */ static inline int @@ -930,6 +970,45 @@ copy_reg (char const *src_name, char con goto close_src_desc; } + /* When cp is invoked with '--reflink=[WHEN]', try to do OCFS2 reflink + ioctl(2) first, if it fails, try btrfs clone file later if possible. + The reason why perform those operations separately is because `cp' + will create the destination file if it is a '*new_dst'. In this case, + we have to unlink(2) it firstly, otherwise, OCFS2 reflink will fail with + 'EEXIST'. */ + bool reflink_ok = false; + if (data_copy_required && x->reflink_mode) + { + bool preserve_attributes = (x->preserve_ownership + && x->preserve_mode + && x->preserve_timestamps); + reflink_ok = reflink_file (src_name, dst_name, source_desc, + preserve_attributes) == 0; + if (reflink_ok) + { + *new_dst = false; + + /* Skip over the standard attributes preserve process + if reflink succeeds and they are already reflinked. */ + if (preserve_attributes) + goto close_src_desc; + } + else + { + /* When the destination file is aready exists on OCFS2, the + above reflink should fails with EEXIST. If that happens, + we need not try btrfs clone again since it means the user + is definitely want a OCFS2 reflink. */ + if (errno == EEXIST) + { + error (0, errno, _("failed to reflink %s from %s"), + quote_n (0, dst_name), quote_n (1, src_name)); + return_val = false; + goto close_src_desc; + } + } + } + /* The semantics of the following open calls are mandated by the specs for both cp and mv. */ if (! *new_dst) @@ -1075,17 +1154,20 @@ copy_reg (char const *src_name, char con /* --attributes-only overrides --reflink. */ if (data_copy_required && x->reflink_mode) { - bool clone_ok = clone_file (dest_desc, source_desc) == 0; - if (clone_ok || x->reflink_mode == REFLINK_ALWAYS) + if (! reflink_ok) { - if (!clone_ok) + 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 from %s"), - quote_n (0, dst_name), quote_n (1, src_name)); - return_val = false; - goto close_src_and_dst_desc; + if (!clone_ok) + { + error (0, errno, _("failed to clone %s from %s"), + quote_n (0, dst_name), quote_n (1, src_name)); + return_val = false; + goto close_src_and_dst_desc; + } + data_copy_required = false; } - data_copy_required = false; } }