[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [coreutils] Patch FIEMAP support for effieicent sparse file copy v1
From: |
jeff.liu |
Subject: |
Re: [coreutils] Patch FIEMAP support for effieicent sparse file copy v1 |
Date: |
Mon, 22 Mar 2010 22:41:20 +0800 |
User-agent: |
Thunderbird 2.0.0.14 (X11/20080505) |
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 <address@hidden>
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 <address@hidden>
---
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