summaryrefslogtreecommitdiffstats
path: root/sys-utils/fallocate.c
diff options
context:
space:
mode:
authorKarel Zak2014-02-18 11:11:56 +0100
committerKarel Zak2014-02-18 11:38:47 +0100
commitdb5f00bc880200e5fc83257db7ea6b06853708c5 (patch)
tree33817d69a490166da97e4af27c057d9ee55f2666 /sys-utils/fallocate.c
parentfallocate: fix man page synopsis (diff)
downloadkernel-qcow2-util-linux-db5f00bc880200e5fc83257db7ea6b06853708c5.tar.gz
kernel-qcow2-util-linux-db5f00bc880200e5fc83257db7ea6b06853708c5.tar.xz
kernel-qcow2-util-linux-db5f00bc880200e5fc83257db7ea6b06853708c5.zip
fallocate: use SEEK_DATA on already sparse files
It's more efficient to skip already known holes by SEEK_DATA (seek to the next area with data). Thanks to Pádraig Brady. Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils/fallocate.c')
-rw-r--r--sys-utils/fallocate.c38
1 files changed, 35 insertions, 3 deletions
diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c
index d18ba1878..6c2b1ea34 100644
--- a/sys-utils/fallocate.c
+++ b/sys-utils/fallocate.c
@@ -110,6 +110,26 @@ static void xfallocate(int fd, int mode, off_t offset, off_t length)
}
}
+
+static int skip_hole(int fd, off_t *off)
+{
+ off_t newoff;
+
+ errno = 0;
+ newoff = lseek(fd, *off, SEEK_DATA);
+
+ /* ENXIO means that there is no more data -- probably sparse hole at
+ * the end of the file */
+ if (newoff < 0 && errno == ENXIO)
+ return 1;
+
+ if (newoff > *off) {
+ *off = newoff;
+ return 0; /* success */
+ }
+ return -1; /* no hole */
+}
+
static void dig_holes(int fd, off_t off, off_t len)
{
off_t end = len ? off + len : 0;
@@ -118,13 +138,17 @@ static void dig_holes(int fd, off_t off, off_t len)
size_t bufsz;
char *buf, *empty;
struct stat st;
+ int sparse = 0;
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 (st.st_blocks * 512 < st.st_size) {
+ if (verbose)
+ fprintf(stdout, _("%s: already has holes.\n"), filename);
+ sparse = 1;
+ }
if (lseek(fd, off, SEEK_SET) < 0)
err(EXIT_FAILURE, _("seek on %s failed"), filename);
@@ -147,8 +171,16 @@ static void dig_holes(int fd, off_t off, off_t len)
break;
if (memcmp(buf, empty, rsz) == 0) {
- if (hole_sz == 0)
+ if (!hole_sz) { /* new hole detected */
+ if (sparse) {
+ int rc = skip_hole(fd, &off);
+ if (rc == 0)
+ continue; /* hole skipped */
+ else if (rc == 1)
+ break; /* end of file */
+ }
hole_start = off;
+ }
hole_sz += rsz;
} else if (hole_sz) {
xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,