Hello, > + if (x.fiemap_mode == FIEMAP_ALWAYS && x.sparse_mode != SPARSE_NEVER) I just found an mistake as above-mentioned.
The revised version was shown as following: >From 7171878eb24317d2b773665d9d8c0500743e64c9 Mon Sep 17 00:00:00 2001 From: Jie Liu <[email protected]> Date: Mon, 22 Mar 2010 22:32:48 +0800 Subject: [PATCH] add fiemap_copy() function to perform file copy in fiemap way, which is more efficient for the sparse file backup. Signed-off-by: Jie Liu <[email protected]> --- src/copy.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/copy.h | 27 ++++++++++++ src/cp.c | 53 +++++++++++++++++++++++- 3 files changed, 212 insertions(+), 3 deletions(-) diff --git a/src/copy.c b/src/copy.c index 29f37c9..d16ea13 100644 --- a/src/copy.c +++ b/src/copy.c @@ -65,6 +65,10 @@ # include <sys/ioctl.h> #endif +#ifndef HAVE_FIEMAP +# include "fiemap.h" +#endif + #ifndef HAVE_FCHOWN # define HAVE_FCHOWN false # define fchown(fd, uid, gid) (-1) @@ -151,6 +155,104 @@ clone_file (int dest_fd, int src_fd) #endif } +#ifdef __linux__ +# undef FS_IOC_FIEMAP +# define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) +/* Perform FIEMAP(available in mainline 2.6.27) copy for sparse file backup. + Call ioctl(2) with FS_IOC_FIEMAP will return the number of extents and the + extent information mapped to a file, it does not includes the holes. so the + overhead deal with holes with lseek(2) could be saved. + This would result in much faster backups for any kind of sparse file. */ +static bool +fiemap_copy (int src_fd, int dest_fd, size_t optimal_buf_size, + uint32_t fiemap_flags, char const *src_name, + char const *dst_name) +{ + int last = 0; + unsigned int i; + bool return_val = true; + char fiemap_buf[4096] = ""; + struct fiemap *fiemap = (struct fiemap *)fiemap_buf; + struct fiemap_extent *fm_ext = &fiemap->fm_extents[0]; + uint32_t count = (sizeof(fiemap_buf) - sizeof(*fiemap)) / + sizeof(struct fiemap_extent); + + memset (fiemap, 0, sizeof (*fiemap)); + + do + { + fiemap->fm_start = 0ULL; + fiemap->fm_length = FIEMAP_MAX_OFFSET; + fiemap->fm_flags |= fiemap_flags; + fiemap->fm_extent_count = count; + + /* If the underlaying filesystem does not support FIEMAP or + the flags specified, fall back to do normal copy if the + fiemap_mod == FIEMAP_AUTO. */ + if (ioctl (src_fd, FS_IOC_FIEMAP, (unsigned long) fiemap) < 0) + return false; + + /* If 0 extents are returned, then more ioctls are not needed. */ + if (fiemap->fm_mapped_extents == 0) + { + return true; + } + + for (i = 0; i < fiemap->fm_mapped_extents; i++) + { + uint64_t ext_logical = fm_ext[i].fe_logical; + uint64_t ext_len = fm_ext[i].fe_length; + + /* FIXME: do we need to deal with the FIEMAP_EXTENT_UNKNOWN/FIEMAP_EXTENT_ENCODED? */ + + if (lseek (src_fd, ext_logical, SEEK_CUR) < 0LL) + { + error (0, errno, _("cannot lseek %s"), quote (src_name)); + return_val = false; + } + + uint64_t tot_read; + char buf[optimal_buf_size]; + while (tot_read < ext_len) + { + memset (buf, 0, sizeof(buf)); + ssize_t n_read = read (src_fd, buf, optimal_buf_size); + if (n_read < 0) + { +#ifdef EINTR + if (errno == EINTR) + continue; +#endif + error (0, errno, _("reading %s"), quote (src_name)); + return_val = false; + } + + if (n_read == 0) + break; + + if (full_write (dest_fd, buf, n_read) != n_read) + { + error (0, errno, _("writing %s"), quote (dst_name)); + return_val = false; + } + + tot_read += n_read; + } + + if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST) + last = 1; + + fiemap->fm_start = (fm_ext[i-1].fe_logical + fm_ext[i-1].fe_length); + } + + } while (last == 0); + + return return_val; +} +#else +static bool fiemap_copy (ignored) { errno == ENOTSUP; return false; } +#endif + /* FIXME: describe */ /* FIXME: rewrite this to use a hash table so we avoid the quadratic performance hit that's probably noticeable only on trees deeper @@ -703,6 +805,31 @@ copy_reg (char const *src_name, char const *dst_name, buf_size = blcm; } + if (x->fiemap_mode) + { + uint32_t fiemap_flags = 0; + + if (x->fiemap_sync) + fiemap_flags |= FIEMAP_FLAG_SYNC; + if (x->fiemap_xattr) + fiemap_flags |= FIEMAP_FLAG_XATTR; + + bool fiemap_copy_ok = fiemap_copy (source_desc, dest_desc, buf_size, + fiemap_flags, src_name, dst_name); + if (fiemap_copy_ok) + goto preserve_extra_info; + else + { +#ifdef EBADR + if ((errno == EBADR) && (x->fiemap_mode == FIEMAP_AUTO)) + goto normal_copy; +#endif + error (0, errno, _("FIEMAP copy failed %s"), quote (src_name)); + goto close_src_and_dst_desc; + } + } + +normal_copy: /* Make a buffer with space for a sentinel at the end. */ buf_alloc = xmalloc (buf_size + buf_alignment_slop); buf = ptr_align (buf_alloc, buf_alignment); @@ -813,6 +940,7 @@ copy_reg (char const *src_name, char const *dst_name, } } +preserve_extra_info: if (x->preserve_timestamps) { struct timespec timespec[2]; @@ -901,8 +1029,11 @@ close_src_desc: return_val = false; } - free (buf_alloc); - free (name_alloc); + if (buf_alloc) + free (buf_alloc); + if (name_alloc) + free (name_alloc); + return return_val; } diff --git a/src/copy.h b/src/copy.h index bd7359f..0bd407d 100644 --- a/src/copy.h +++ b/src/copy.h @@ -56,6 +56,19 @@ enum Reflink_type REFLINK_ALWAYS }; +/* Control of FIEMAP copy. */ +enum Fiemap_type +{ + /* Default to a standard copy. */ + FIEMAP_NEVER, + + /* Try a FIEMAP copy and fall back to a standard copy. */ + FIEMAP_AUTO, + + /* Require a FIEMAP copy and fail if not available. */ + FIEMAP_ALWAYS +}; + /* This type is used to help mv (via copy.c) distinguish these cases. */ enum Interactive { @@ -91,6 +104,11 @@ enum Dereference_symlink || (Mode) == REFLINK_AUTO \ || (Mode) == REFLINK_ALWAYS) +# define VALID_FIEMAP_MODE(Mode) \ + ((Mode) == FIEMAP_NEVER \ + || (Mode) == FIEMAP_AUTO \ + || (Mode) == FIEMAP_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 @@ -237,9 +255,18 @@ struct cp_options such a symlink) and returns false. */ bool open_dangling_dest_symlink; + /* If true, set fiemap ioctl flags with FIEMAP_FLAG_SYNC. */ + bool fiemap_sync; + + /* If true, set fiemap ioctl flags with FIEMAP_FLAG_XATTR. */ + bool fiemap_xattr; + /* Control creation of COW files. */ enum Reflink_type reflink_mode; + /* Control of FIEMAP type file copy. */ + enum Fiemap_type fiemap_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 that was specified on the command line. Use it to avoid clobbering diff --git a/src/cp.c b/src/cp.c index cc958d1..ab4c75e 100644 --- a/src/cp.c +++ b/src/cp.c @@ -78,8 +78,11 @@ enum PRESERVE_ATTRIBUTES_OPTION, REFLINK_OPTION, SPARSE_OPTION, + FIEMAP_OPTION, STRIP_TRAILING_SLASHES_OPTION, - UNLINK_DEST_BEFORE_OPENING + UNLINK_DEST_BEFORE_OPENING, + FIEMAP_FLAG_SYNC_OPTION, + FIEMAP_FLAG_XATTR_OPTION }; /* True if the kernel is SELinux enabled. */ @@ -112,6 +115,16 @@ static enum Reflink_type const reflink_type[] = }; ARGMATCH_VERIFY (reflink_type_string, reflink_type); +static char const *const fiemap_type_string[] = +{ + "auto", "always", NULL +}; +static enum Fiemap_type const fiemap_type[] = +{ + FIEMAP_AUTO, FIEMAP_ALWAYS +}; +ARGMATCH_VERIFY (fiemap_type_string, fiemap_type); + static struct option const long_opts[] = { {"archive", no_argument, NULL, 'a'}, @@ -133,6 +146,9 @@ static struct option const long_opts[] = {"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING}, {"sparse", required_argument, NULL, SPARSE_OPTION}, {"reflink", optional_argument, NULL, REFLINK_OPTION}, + {"fiemap", optional_argument, NULL, FIEMAP_OPTION}, + {"fiemap-sync", optional_argument, NULL, FIEMAP_FLAG_SYNC_OPTION}, + {"fiemap-xattr", optional_argument, NULL, FIEMAP_FLAG_XATTR_OPTION}, {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION}, {"suffix", required_argument, NULL, 'S'}, {"symbolic-link", no_argument, NULL, 's'}, @@ -212,6 +228,11 @@ Mandatory arguments to long options are mandatory for short options too.\n\ argument\n\ "), stdout); fputs (_("\ + --fiemap=[=WHEN] control creation of sparse files. See below\n\ + --fiemap-sync sync file data before fiemap\n\ + --fiemap-xattr map extended attribute tree\n\ +"), stdout); + fputs (_("\ -s, --symbolic-link make symbolic links instead of copying\n\ -S, --suffix=SUFFIX override the usual backup suffix\n\ -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\ @@ -237,6 +258,10 @@ Use --sparse=never to inhibit creation of sparse files.\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\ +\n\ +When --fiemap[=always] is specified, perform a fiemap copy, where the\n\ +allocated data blocks are copied except holes. If this is not possible the\n\ +copy fails, or if --fiemap=auto is specified, fall back to a standard copy.\n\ "), stdout); fputs (_("\ \n\ @@ -770,6 +795,10 @@ cp_option_init (struct cp_options *x) x->move_mode = false; x->one_file_system = false; x->reflink_mode = REFLINK_NEVER; + x->fiemap_mode = FIEMAP_NEVER; + + x->fiemap_sync = false; + x->fiemap_xattr = false; x->preserve_ownership = false; x->preserve_links = false; @@ -942,6 +971,22 @@ main (int argc, char **argv) reflink_type_string, reflink_type); break; + case FIEMAP_OPTION: + if (optarg == NULL) + x.fiemap_mode = FIEMAP_ALWAYS; + else + x.fiemap_mode = XARGMATCH ("--fiemap", optarg, + fiemap_type_string, fiemap_type); + break; + + case FIEMAP_FLAG_SYNC_OPTION: + x.fiemap_sync = true; + break; + + case FIEMAP_FLAG_XATTR_OPTION: + x.fiemap_xattr = true; + break; + case 'a': /* Like -dR --preserve=all with reduced failure diagnostics. */ x.dereference = DEREF_NEVER; x.preserve_links = true; @@ -1108,6 +1153,12 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + if (x.fiemap_mode == FIEMAP_ALWAYS && x.sparse_mode == SPARSE_NEVER) + { + error (0, 0, _("--fiemap can not be used with --sparse=never")); + usage (EXIT_FAILURE); + } + if (backup_suffix_string) simple_backup_suffix = xstrdup (backup_suffix_string); -- 1.5.4.3 Thanks, -Jeff
