summaryrefslogtreecommitdiff
path: root/src/extent-scan.c
diff options
context:
space:
mode:
authorjeff.liu <jeff.liu@oracle.com>2010-09-29 16:11:41 +0800
committerJim Meyering <meyering@redhat.com>2011-01-30 20:44:11 +0100
commit2db1433eabc847d9ad43e059764222e9b33233aa (patch)
tree52004fa39c3225022cbb77d501be0f3ffd309add /src/extent-scan.c
parentf3e78eeff292fcc79814e62d015a271688e0212a (diff)
downloadcoreutils-2db1433eabc847d9ad43e059764222e9b33233aa.tar.xz
fiemap copy: add extent-scan.[ch], avoid a double-free and reorganize
Changes: ======== 1. fix write_zeros() per Jim's comments. 2. remove char const *fname from struct extent_scan. 3. change the signature of open_extent_scan() from "void open_extent_scan(struct extent_scan **scan)" to "void open_extent_scan(struct extent_scan *scan)" to avoid having to malloc the extent_scan variable; instead save it on the stack. 4. move close_extent_scan() from a function defined in extent-scan.c to extent-scan.h as a macro definition, but it does nothing for now, since initial extent scan defined at stack. 5. add a macro "free_extents_info()" defined at extent-scan.h to release the memory allocated to extent info which should be called combine with get_extents_info(), it just one line, so IMHO, define it as macro should be ok. * src/extent-scan.c: New file; functions to read "extents". * src/extent-scan.h: Header file of extent-scan.c. * src/Makefile.am: Reference it and link it to copy_source. * src/copy.c: Use the new functions and avoid double-free.
Diffstat (limited to 'src/extent-scan.c')
-rw-r--r--src/extent-scan.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/src/extent-scan.c b/src/extent-scan.c
new file mode 100644
index 000000000..f371b8718
--- /dev/null
+++ b/src/extent-scan.c
@@ -0,0 +1,113 @@
+/* extent-scan.c -- core functions for scanning extents
+ Copyright (C) 2010 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Written by Jie Liu (jeff.liu@oracle.com). */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <assert.h>
+
+#include "system.h"
+#include "extent-scan.h"
+#include "error.h"
+#include "quote.h"
+
+#ifndef HAVE_FIEMAP
+# include "fiemap.h"
+#endif
+
+/* Allocate space for struct extent_scan, initialize the entries if
+ necessary and return it as the input argument of get_extents_info(). */
+extern void
+open_extent_scan (int src_fd, struct extent_scan *scan)
+{
+ scan->fd = src_fd;
+ scan->ei_count = 0;
+ scan->scan_start = 0;
+ scan->initial_scan_failed = false;
+ scan->hit_last_extent = false;
+}
+
+#ifdef __linux__
+# ifndef FS_IOC_FIEMAP
+# define FS_IOC_FIEMAP _IOWR ('f', 11, struct fiemap)
+# endif
+/* Call ioctl(2) with FS_IOC_FIEMAP (available in linux 2.6.27) to
+ obtain a map of file extents excluding holes. */
+extern bool
+get_extents_info (struct extent_scan *scan)
+{
+ union { struct fiemap f; char c[4096]; } fiemap_buf;
+ struct fiemap *fiemap = &fiemap_buf.f;
+ struct fiemap_extent *fm_extents = &fiemap->fm_extents[0];
+ enum { count = (sizeof fiemap_buf - sizeof *fiemap) / sizeof *fm_extents };
+ verify (count != 0);
+ unsigned int i;
+
+ /* This is required at least to initialize fiemap->fm_start,
+ but also serves (in mid 2010) to appease valgrind, which
+ appears not to know the semantics of the FIEMAP ioctl. */
+ memset (&fiemap_buf, 0, sizeof fiemap_buf);
+
+ fiemap->fm_start = scan->scan_start;
+ fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+ fiemap->fm_extent_count = count;
+ fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
+
+ /* Fall back to the standard copy if call ioctl(2) failed for the
+ the first time. */
+ if (ioctl (scan->fd, FS_IOC_FIEMAP, fiemap) < 0)
+ {
+ if (scan->scan_start == 0)
+ scan->initial_scan_failed = true;
+ return false;
+ }
+
+ /* If 0 extents are returned, then more get_extent_table() are not needed. */
+ if (fiemap->fm_mapped_extents == 0)
+ {
+ scan->hit_last_extent = true;
+ return false;
+ }
+
+ scan->ei_count = fiemap->fm_mapped_extents;
+ scan->ext_info = xnmalloc (scan->ei_count, sizeof (struct extent_info));
+
+ for (i = 0; i < scan->ei_count; i++)
+ {
+ assert (fm_extents[i].fe_logical <= OFF_T_MAX);
+
+ scan->ext_info[i].ext_logical = fm_extents[i].fe_logical;
+ scan->ext_info[i].ext_length = fm_extents[i].fe_length;
+ scan->ext_info[i].ext_flags = fm_extents[i].fe_flags;
+ }
+
+ i--;
+ if (scan->ext_info[i].ext_flags & FIEMAP_EXTENT_LAST)
+ {
+ scan->hit_last_extent = true;
+ return true;
+ }
+
+ scan->scan_start = fm_extents[i].fe_logical + fm_extents[i].fe_length;
+
+ return true;
+}
+#else
+extern bool get_extents_info (ignored) { errno = ENOTSUP; return false; }
+#endif