Hi All, I'd like to introduce OCFS2 reflink feature to cp(1).
Currently, we could run cp(1) with '--reflink=[WHEN]' to perform Btrfs clone operation. We can share the same trigger option to do OCFS2 reflink operation without any conflicts. Here is the different between Btrfs clone and OCFS2 reflink, refers to Joel's comments before. "ocfs2's reflink (where the term was coined) is an atomic creation of a new target inode duplicating the attributes of and sharing cow data extents with the source. btrfs's ioctl(2) links data extents from one existing inode to another existing inode. It is not atomic regarding the target inode creation, and it doesn't inherently set the attributes of the target inode." >From 43cadc7bf905492e38d0c259adf0f00e7b5ab7ac Mon Sep 17 00:00:00 2001 From: Jie Liu <[email protected]> Date: Sun, 4 Apr 2010 23:29:33 +0800 Subject: [PATCH] Add OCFS2 reflink feature to cp(1). Currently, cp(1) does Btrfs's ioctl(2) reflinks via "--reflink=[WHEN]". This patch add OCFS2 reflink which share the same trigger option, it perform ocfs2 ioctl(2) reflink only if the source file is resides on OCFS2 filesystem and link destination file system is OCFS2 as well. Signed-off-by: Jie Liu <[email protected]> --- src/copy.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/copy.c b/src/copy.c index 29f37c9..cb515ed 100644 --- a/src/copy.c +++ b/src/copy.c @@ -53,6 +53,13 @@ #include "write-any-file.h" #include "areadlink.h" #include "yesno.h" +#include "fs.h" + +#if HAVE_SYS_VFS_H +# include <sys/vfs.h> +#else +# include <sys/statfs.h> +#endif #if USE_XATTR # include <attr/error_context.h> @@ -132,6 +139,74 @@ utimens_symlink (char const *file, struct timespec const *timespec) return err; } +/* This function detects whether the source and destination files + are all resides on OCFS2. If true, then perform the OCFS2 reflink + operation. Otherwise, performing btrfs clone operation. */ +static inline bool +is_ocfs2_file (int dst_fd, int src_fd) +{ +#if HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE && defined __linux__ + struct statfs statfsbuf1; + struct statfs statfsbuf2; + + return (fstatfs (src_fd, &statfsbuf1) == 0 + && statfsbuf1.f_type == S_MAGIC_OCFS2 + && fstatfs (dst_fd, &statfsbuf2) == 0 + && statfsbuf2.f_type == S_MAGIC_OCFS2); +#else + (void) dst_fd; + (void) src_fd; + return false; +#endif +} + +/* Perform the OCFS2 CoW reflink operation if possible. + We do not attempt to preserve security attributes for a reference + counted link. Instead, let 'x->preserve_xxxx' to process them if + they are the user expected. + 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 *new_dst) +{ +#ifdef __linux__ +# ifndef REFLINK_ATTR_NONE +# define REFLINK_ATTR_NONE 0 +# 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 + /* Check if the destination does not exist but created by cp(1) before. + In this case, we have to unlink(2) it before performing reflink. + Reflink can not create a file that already exists. */ + if (*new_dst) + { + if (unlink (dst_name) != 0) + return -1; + } + + struct reflink_arguments args = { + .old_path = (unsigned long) src_name, + .new_path = (unsigned long) dst_name, + .preserve = REFLINK_ATTR_NONE, + }; + + return ioctl (src_fd, OCFS2_IOC_REFLINK, &args); +#else + (void) src_name; + (void) dst_name; + (void) src_fd; + (void) new_dst; + 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 @@ -634,12 +709,15 @@ copy_reg (char const *src_name, char const *dst_name, if (x->reflink_mode) { - bool clone_ok = clone_file (dest_desc, source_desc) == 0; - if (clone_ok || x->reflink_mode == REFLINK_ALWAYS) + bool reflink_ok = is_ocfs2_file (dest_desc, source_desc) + ? reflink_file (src_name, dst_name, source_desc, new_dst) == 0 + : clone_file (dest_desc, source_desc) == 0; + + if (reflink_ok || x->reflink_mode == REFLINK_ALWAYS) { - if (!clone_ok) + if (!reflink_ok) { - error (0, errno, _("failed to clone %s"), quote (dst_name)); + error (0, errno, _("failed to reflink %s"), quote (dst_name)); return_val = false; goto close_src_and_dst_desc; } -- 1.5.4.3 Best Regards, -Jef
