summaryrefslogtreecommitdiffstats
path: root/sys-utils/fallocate.c
diff options
context:
space:
mode:
authorKarel Zak2017-11-29 14:26:42 +0100
committerKarel Zak2017-11-29 15:01:39 +0100
commit173ef882376d5384ba5fc6a52d2a6000b4fc4bf1 (patch)
tree2a159a8d762a282940337c326dfe954db183ea50 /sys-utils/fallocate.c
parentlogin-utils: use free_getlogindefs_data() (diff)
downloadkernel-qcow2-util-linux-173ef882376d5384ba5fc6a52d2a6000b4fc4bf1.tar.gz
kernel-qcow2-util-linux-173ef882376d5384ba5fc6a52d2a6000b4fc4bf1.tar.xz
kernel-qcow2-util-linux-173ef882376d5384ba5fc6a52d2a6000b4fc4bf1.zip
fallocate: dig holes only in data extents
Based on patch from Vaclav Dolezal <vdolezal@redhat.com>, this implementation is less invasive. The patch adds a new while() for pread() call (so diff is mostly code indention). The pread() is called for a real data only (addressed by 'off' and 'end') and we use SEEK_{DATA,HOLE} before the pread() to skip already existing holes. The variables 'file_off' and 'file_end' addresses area in the file as specified on fallocate command line. Test: $ truncate -s 10G testfile $ dd if=/dev/zero of=testfile count=10 bs=1M conv=notrunc old version: $ time /usr/bin/fallocate --dig-holes --verbose testfile testfile: 10 GiB (10737418240 bytes) converted to sparse holes. real 0m3.013s user 0m0.700s sys 0m2.304s new version: $ time ./fallocate --dig-holes --verbose testfile testfile: 10 MiB (10485760 bytes) converted to sparse holes. real 0m0.026s user 0m0.002s sys 0m0.004s The old version scans all file. The change has minimal overhead for files without holes. Addresses: https://github.com/karelzak/util-linux/issues/421 Co-Author: Vaclav Dolezal <vdolezal@redhat.com> Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils/fallocate.c')
-rw-r--r--sys-utils/fallocate.c119
1 files changed, 55 insertions, 64 deletions
diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c
index c8b2b2fcc..4c53f8535 100644
--- a/sys-utils/fallocate.c
+++ b/sys-utils/fallocate.c
@@ -147,25 +147,6 @@ static void xposix_fallocate(int fd, off_t offset, off_t length)
}
#endif
-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 */
-}
-
/* The real buffer size has to be bufsize + sizeof(uintptr_t) */
static int is_nul(void *buf, size_t bufsize)
{
@@ -191,16 +172,16 @@ static int is_nul(void *buf, size_t bufsize)
return cbuf + bufsize < cp;
}
-static void dig_holes(int fd, off_t off, off_t len)
+static void dig_holes(int fd, off_t file_off, off_t len)
{
- off_t end = len ? off + len : 0;
+ off_t file_end = len ? file_off + len : 0;
off_t hole_start = 0, hole_sz = 0;
uintmax_t ct = 0;
size_t bufsz;
char *buf;
struct stat st;
#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
- off_t cache_start = off;
+ off_t cache_start = file_off;
/*
* We don't want to call POSIX_FADV_DONTNEED to discard cached
* data in PAGE_SIZE steps. IMHO it's overkill (too many syscalls).
@@ -217,60 +198,70 @@ static void dig_holes(int fd, off_t off, off_t len)
bufsz = st.st_blksize;
- if (lseek(fd, off, SEEK_SET) < 0)
+ if (lseek(fd, file_off, SEEK_SET) < 0)
err(EXIT_FAILURE, _("seek on %s failed"), filename);
/* buffer + extra space for is_nul() sentinel */
buf = xmalloc(bufsz + sizeof(uintptr_t));
+ while (file_end == 0 || file_off < file_end) {
+ /*
+ * Detect data area (skip exiting holes)
+ */
+ off_t end, off;
+
+ off = lseek(fd, file_off, SEEK_DATA);
+ if ((off == -1 && errno == ENXIO) ||
+ (file_end && off >= file_end))
+ break;
+
+ end = lseek(fd, off, SEEK_HOLE);
+ if (file_end && end > file_end)
+ end = file_end;
+
#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
- posix_fadvise(fd, off, 0, POSIX_FADV_SEQUENTIAL);
+ posix_fadvise(fd, off, end, POSIX_FADV_SEQUENTIAL);
#endif
+ /*
+ * Dig holes in the area
+ */
+ while (off < end) {
+ ssize_t rsz = pread(fd, buf, bufsz, off);
+ if (rsz < 0 && errno)
+ err(EXIT_FAILURE, _("%s: read failed"), filename);
+ if (end && rsz > 0 && off > end - rsz)
+ rsz = end - off;
+ if (rsz <= 0)
+ break;
+
+ if (is_nul(buf, rsz)) {
+ if (!hole_sz) /* new hole detected */
+ 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;
+ }
- while (end == 0 || off < end) {
- ssize_t rsz;
-
- rsz = pread(fd, buf, bufsz, off);
- if (rsz < 0 && errno)
- err(EXIT_FAILURE, _("%s: read failed"), filename);
- if (end && rsz > 0 && off > end - rsz)
- rsz = end - off;
- if (rsz <= 0)
- break;
+#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE)
+ /* discard cached data */
+ if (off - cache_start > (off_t) cachesz) {
+ size_t clen = off - cache_start;
- if (is_nul(buf, rsz)) {
- if (!hole_sz) { /* new hole detected */
- int rc = skip_hole(fd, &off);
- if (rc == 0)
- continue; /* hole skipped */
- else if (rc == 1)
- break; /* end of file */
- hole_start = off;
+ clen = (clen / cachesz) * cachesz;
+ posix_fadvise(fd, cache_start, clen, POSIX_FADV_DONTNEED);
+ cache_start = cache_start + clen;
}
- hole_sz += rsz;
- } else if (hole_sz) {
+#endif
+ off += rsz;
+ }
+ if (hole_sz) {
xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
- hole_start, hole_sz);
+ hole_start, hole_sz);
ct += hole_sz;
- hole_sz = hole_start = 0;
}
-
-#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE)
- /* discard cached data */
- if (off - cache_start > (off_t) cachesz) {
- size_t clen = off - cache_start;
-
- clen = (clen / cachesz) * cachesz;
- posix_fadvise(fd, cache_start, clen, POSIX_FADV_DONTNEED);
- cache_start = cache_start + clen;
- }
-#endif
- off += rsz;
- }
-
- if (hole_sz) {
- xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
- hole_start, hole_sz);
- ct += hole_sz;
+ file_off = off;
}
free(buf);