summaryrefslogtreecommitdiffstats
path: root/sys-utils/fallocate.c
diff options
context:
space:
mode:
authorKarel Zak2014-02-17 10:55:56 +0100
committerKarel Zak2014-02-17 11:13:54 +0100
commitd6cecc3fbab62e5ccd850a41273b6ced24bb29d2 (patch)
treed17640838ae98386d6ccd16799d8f388858bbef3 /sys-utils/fallocate.c
parentfallocate: add --verbose, clean up usage() (diff)
downloadkernel-qcow2-util-linux-d6cecc3fbab62e5ccd850a41273b6ced24bb29d2.tar.gz
kernel-qcow2-util-linux-d6cecc3fbab62e5ccd850a41273b6ced24bb29d2.tar.xz
kernel-qcow2-util-linux-d6cecc3fbab62e5ccd850a41273b6ced24bb29d2.zip
fallocate: improve --dig-holes
* don't use --length to specify hole size, always use stat.st_blksize * use --offset and --length to specify offset within the file (like another fallocate operations) Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils/fallocate.c')
-rw-r--r--sys-utils/fallocate.c126
1 files changed, 71 insertions, 55 deletions
diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c
index 64be20de8..d18ba1878 100644
--- a/sys-utils/fallocate.c
+++ b/sys-utils/fallocate.c
@@ -94,7 +94,6 @@ static loff_t cvtnum(char *s)
static void xfallocate(int fd, int mode, off_t offset, off_t length)
{
int error;
-
#ifdef HAVE_FALLOCATE
error = fallocate(fd, mode, offset, length);
#else
@@ -111,51 +110,70 @@ static void xfallocate(int fd, int mode, off_t offset, off_t length)
}
}
-/*
- * Look for chunks of '\0's with size hole_size and when we find them, dig a
- * hole on that offset with that size
- */
-static void detect_holes(int fd, size_t hole_size)
+static void dig_holes(int fd, off_t off, off_t len)
{
- void *zeros;
- ssize_t bufsz = hole_size;
- void *buf;
- off_t offset, end;
-
- /* Create a buffer of '\0's to compare against */
- /* XXX: Use mmap() with MAP_PRIVATE so Linux can avoid this allocation */
- zeros = mmap(NULL, hole_size, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- if (zeros == MAP_FAILED)
- err(EXIT_FAILURE, _("mmap failed"));
-
- /* buffer to read the file */
+ off_t end = len ? off + len : 0;
+ off_t hole_start = 0, hole_sz = 0;
+ uintmax_t ct = 0;
+ size_t bufsz;
+ char *buf, *empty;
+ struct stat st;
+
+ if (fstat(fd, &st) != 0)
+ err(EXIT_FAILURE, _("stat failed %s"), filename);
+ bufsz = st.st_blksize;
+
+ if (verbose && st.st_blocks * 512 < st.st_size)
+ fprintf(stdout, _("%s: already has holes!\n"), filename);
+ if (lseek(fd, off, SEEK_SET) < 0)
+ err(EXIT_FAILURE, _("seek on %s failed"), filename);
+
buf = xmalloc(bufsz);
+ empty = xcalloc(1, bufsz);
- end = lseek(fd, 0, SEEK_END);
- if (end < 0)
- err(EXIT_FAILURE, _("seek failed"));
+#if defined(POSIX_FADV_SEQUENTIAL) && defined(POSIX_FADV_NOREUSE) && defined(HAVE_POSIX_FADVISE)
+ posix_fadvise(fd, off, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+#endif
- for (offset = 0; offset + (ssize_t) hole_size <= end; offset += bufsz) {
- /* Try to read hole_size bytes */
- bufsz = pread(fd, buf, hole_size, offset);
- if (bufsz == -1)
- err(EXIT_FAILURE, _("read failed"));
+ while (end == 0 || off < end) {
+ ssize_t rsz;
- /* Always use bufsz, as we may read less than hole_size bytes */
- if (memcmp(buf, zeros, bufsz))
- continue;
+ rsz = pread(fd, buf, bufsz, off);
+ if (rsz < 0 && errno)
+ err(EXIT_FAILURE, _("%s: read failed"), filename);
+ if (end && rsz > 0 && off + rsz > end)
+ rsz = end - off;
+ if (rsz <= 0)
+ break;
- if (verbose)
- fprintf(stdout, "%s: detected hole at offset %ju (size %ju)\n",
- filename, (uintmax_t) offset, (uintmax_t) bufsz);
+ if (memcmp(buf, empty, rsz) == 0) {
+ if (hole_sz == 0)
+ hole_start = off;
+ hole_sz += rsz;
+ } else if (hole_sz) {
+ xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
+ hole_start, hole_sz);
+ ct += hole_sz;
+ hole_sz = hole_start = 0;
+ }
+ off += rsz;
+ }
+ if (hole_sz) {
xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
- offset, bufsz);
+ hole_start, hole_sz);
+ ct += hole_sz;
}
- if (munmap(zeros, hole_size))
- err(EXIT_FAILURE, _("munmap failed"));
free(buf);
+ free(empty);
+
+ if (verbose) {
+ char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, ct);
+ fprintf(stdout, _("%s: %s (%ju bytes) converted to sparse holes.\n"),
+ filename, str, ct);
+ free(str);
+ }
}
int main(int argc, char **argv)
@@ -163,7 +181,7 @@ int main(int argc, char **argv)
int c;
int fd;
int mode = 0;
- int dig_holes = 0;
+ int dig = 0;
loff_t length = -2LL;
loff_t offset = 0;
@@ -199,7 +217,7 @@ int main(int argc, char **argv)
mode |= FALLOC_FL_KEEP_SIZE;
break;
case 'd':
- dig_holes = 1;
+ dig = 1;
break;
case 'l':
length = cvtnum(optarg);
@@ -215,16 +233,19 @@ int main(int argc, char **argv)
break;
}
}
- if (dig_holes && mode != 0)
- errx(EXIT_FAILURE, _("Can't use -p or -n with --dig-holes"));
- if (dig_holes && offset != 0)
- errx(EXIT_FAILURE, _("Can't use -o with --dig-holes"));
- if (length == -2LL && dig_holes)
- length = 32 * 1024;
- if (length == -2LL && !dig_holes)
- errx(EXIT_FAILURE, _("no length argument specified"));
- if (length <= 0)
- errx(EXIT_FAILURE, _("invalid length value specified"));
+ if (dig) {
+ if (mode != 0)
+ errx(EXIT_FAILURE, _("Can't use -p or -n with --dig-holes"));
+ if (length == -2LL)
+ length = 0;
+ if (length < 0)
+ errx(EXIT_FAILURE, _("invalid length value specified"));
+ } else {
+ if (length == -2LL)
+ errx(EXIT_FAILURE, _("no length argument specified"));
+ if (length <= 0)
+ errx(EXIT_FAILURE, _("invalid length value specified"));
+ }
if (offset < 0)
errx(EXIT_FAILURE, _("invalid offset value specified"));
if (optind == argc)
@@ -241,15 +262,10 @@ int main(int argc, char **argv)
if (fd < 0)
err(EXIT_FAILURE, _("cannot open %s"), filename);
- if (dig_holes)
- detect_holes(fd, length);
- else {
- if (verbose)
- fprintf(stdout, "%s: fallocate offset=%ju, length=%ju\n",
- filename, (uintmax_t) offset,
- (uintmax_t) length);
+ if (dig)
+ dig_holes(fd, offset, length);
+ else
xfallocate(fd, mode, offset, length);
- }
if (close_fd(fd) != 0)
err(EXIT_FAILURE, _("write failed: %s"), filename);