05ad79
From 9f2a32a8fd08bb1b48f29c88bfa398fa4eb5f2a4 Mon Sep 17 00:00:00 2001
05ad79
From: Karel Zak <kzak@redhat.com>
05ad79
Date: Wed, 6 Jun 2018 11:59:16 +0200
05ad79
Subject: [PATCH 159/173] fallocate: backport v2.32-164-g641af90dc
05ad79
05ad79
* add --dig-holes
05ad79
* add --collapse-range
05ad79
* add --insert-range
05ad79
* add --zero-range
05ad79
05ad79
For backward compatibility with previous RHEL7 versions we keep
05ad79
O_CREAT for open(). The current upstream uses O_CREAT only when
05ad79
necessary.
05ad79
05ad79
Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1528567
05ad79
Signed-off-by: Karel Zak <kzak@redhat.com>
05ad79
---
05ad79
 sys-utils/fallocate.1 | 195 ++++++++++++++++++++++------
05ad79
 sys-utils/fallocate.c | 349 +++++++++++++++++++++++++++++++++++++++-----------
05ad79
 2 files changed, 428 insertions(+), 116 deletions(-)
05ad79
05ad79
diff --git a/sys-utils/fallocate.1 b/sys-utils/fallocate.1
05ad79
index 376353013..d4821dcd1 100644
05ad79
--- a/sys-utils/fallocate.1
05ad79
+++ b/sys-utils/fallocate.1
05ad79
@@ -1,72 +1,185 @@
05ad79
-.\" -*- nroff -*-
05ad79
-.TH FALLOCATE 1 "September 2011" "util-linux" "User Commands"
05ad79
+.TH FALLOCATE 1 "April 2014" "util-linux" "User Commands"
05ad79
 .SH NAME
05ad79
-fallocate \- preallocate space to a file
05ad79
+fallocate \- preallocate or deallocate space to a file
05ad79
 .SH SYNOPSIS
05ad79
 .B fallocate
05ad79
-.RB [ \-n ]
05ad79
-.RB [ \-p ]
05ad79
+.RB [ \-c | \-p | \-z ]
05ad79
 .RB [ \-o
05ad79
 .IR offset ]
05ad79
 .B \-l
05ad79
-.IR length
05ad79
+.I length
05ad79
+.RB [ \-n ]
05ad79
+.I filename
05ad79
+.PP
05ad79
+.B fallocate \-d
05ad79
+.RB [ \-o
05ad79
+.IR offset ]
05ad79
+.RB [ \-l
05ad79
+.IR length ]
05ad79
 .I filename
05ad79
 .PP
05ad79
 .B fallocate \-x
05ad79
 .RB [ \-o
05ad79
 .IR offset ]
05ad79
-.RB \-l
05ad79
-.IR length
05ad79
+.B \-l
05ad79
+.I length
05ad79
 .I filename
05ad79
 .SH DESCRIPTION
05ad79
 .B fallocate
05ad79
-is used to preallocate blocks to a file.  For filesystems which support the
05ad79
-fallocate system call, this is done quickly by allocating blocks and marking
05ad79
-them as uninitialized, requiring no IO to the data blocks.  This is much faster
05ad79
-than creating a file by filling it with zeros.
05ad79
-.PP
05ad79
-As of the Linux Kernel v2.6.31, the fallocate system call is supported by the
05ad79
-btrfs, ext4, ocfs2, and xfs filesystems.
05ad79
+is used to manipulate the allocated disk space for a file,
05ad79
+either to deallocate or preallocate it.
05ad79
+For filesystems which support the fallocate system call,
05ad79
+preallocation is done quickly by allocating blocks and marking them as
05ad79
+uninitialized, requiring no IO to the data blocks.
05ad79
+This is much faster than creating a file by filling it with zeroes.
05ad79
 .PP
05ad79
 The exit code returned by
05ad79
 .B fallocate
05ad79
 is 0 on success and 1 on failure.
05ad79
-.PP
05ad79
 .SH OPTIONS
05ad79
-The \fIlength\fR and \fIoffset\fR arguments may be followed by the multiplicative
05ad79
-suffixes KiB=1024, MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB
05ad79
-(the "iB" is optional, e.g. "K" has the same meaning as "KiB") or the suffixes
05ad79
-KB=1000, MB=1000*1000, and so on for GB, TB, PB, EB, ZB and YB.
05ad79
-.IP "\fB\-n, \-\-keep-size\fP"
05ad79
+The
05ad79
+.I length
05ad79
+and
05ad79
+.I offset
05ad79
+arguments may be followed by the multiplicative suffixes KiB (=1024),
05ad79
+MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB, and YiB (the "iB" is
05ad79
+optional, e.g., "K" has the same meaning as "KiB") or the suffixes
05ad79
+KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB, and YB.
05ad79
+.PP
05ad79
+The options
05ad79
+.BR \-\-collapse\-range ", " \-\-dig\-holes ", " \-\-punch\-hole ,
05ad79
+and
05ad79
+.B \-\-zero\-range
05ad79
+are mutually exclusive.
05ad79
+.TP
05ad79
+.BR \-c ", " \-\-collapse\-range
05ad79
+Removes a byte range from a file, without leaving a hole.
05ad79
+The byte range to be collapsed starts at
05ad79
+.I offset
05ad79
+and continues for
05ad79
+.I length
05ad79
+bytes.
05ad79
+At the completion of the operation,
05ad79
+the contents of the file starting at the location
05ad79
+.IR offset + length
05ad79
+will be appended at the location
05ad79
+.IR offset ,
05ad79
+and the file will be
05ad79
+.I length
05ad79
+bytes smaller.
05ad79
+The option
05ad79
+.B \-\-keep\-size
05ad79
+may not be specified for the collapse-range operation.
05ad79
+.sp
05ad79
+Available since Linux 3.15 for ext4 (only for extent-based files) and XFS.
05ad79
+.TP
05ad79
+.BR \-d ", " \-\-dig\-holes
05ad79
+Detect and dig holes.
05ad79
+This makes the file sparse in-place, without using extra disk space.
05ad79
+The minimum size of the hole depends on filesystem I/O block size
05ad79
+(usually 4096 bytes).
05ad79
+Also, when using this option,
05ad79
+.B \-\-keep\-size
05ad79
+is implied.  If no range is specified by
05ad79
+.B \-\-offset
05ad79
+and
05ad79
+.BR \-\-length ,
05ad79
+then the entire file is analyzed for holes.
05ad79
+.sp
05ad79
+You can think of this option as doing a
05ad79
+.RB """" "cp \-\-sparse" """"
05ad79
+and then renaming the destination file to the original,
05ad79
+without the need for extra disk space.
05ad79
+.sp
05ad79
+See \fB\-\-punch\-hole\fP for a list of supported filesystems.
05ad79
+.TP
05ad79
+.BR \-i ", " \-\-insert\-range
05ad79
+Insert a hole of
05ad79
+.I length
05ad79
+bytes from
05ad79
+.IR offset ,
05ad79
+shifting existing data.
05ad79
+.TP
05ad79
+.BR \-l ", " "\-\-length " \fIlength
05ad79
+Specifies the length of the range, in bytes.
05ad79
+.TP
05ad79
+.BR \-n ", " \-\-keep\-size
05ad79
 Do not modify the apparent length of the file.  This may effectively allocate
05ad79
 blocks past EOF, which can be removed with a truncate.
05ad79
-.IP "\fB\-p, \-\-punch-hole\fP"
05ad79
-Punch holes in the file, the range should not exceed the length of the file.
05ad79
-.IP "\fB\-o, \-\-offset\fP \fIoffset\fP
05ad79
-Specifies the beginning offset of the allocation, in bytes.
05ad79
-.IP "\fB\-l, \-\-length\fP \fIlength\fP
05ad79
-Specifies the length of the allocation, in bytes.
05ad79
-.IP "\fB\-x , \-\-posix\fP
05ad79
-Enable POSIX operation mode. In that mode allocation operation always completes,
05ad79
-but it may take longer time when fast allocation is not supported by the underlying filesystem.
05ad79
-.IP "\fB\-h, \-\-help\fP"
05ad79
-Print help and exit.
05ad79
-.IP "\fB-V, \-\-version"
05ad79
-Print version and exit.
05ad79
+.TP
05ad79
+.BR \-o ", " "\-\-offset " \fIoffset
05ad79
+Specifies the beginning offset of the range, in bytes.
05ad79
+.TP
05ad79
+.BR \-p ", " \-\-punch\-hole
05ad79
+Deallocates space (i.e., creates a hole) in the byte range starting at
05ad79
+.I offset
05ad79
+and continuing for
05ad79
+.I length
05ad79
+bytes.
05ad79
+Within the specified range, partial filesystem blocks are zeroed,
05ad79
+and whole filesystem blocks are removed from the file.
05ad79
+After a successful call,
05ad79
+subsequent reads from this range will return zeroes.
05ad79
+This option may not be specified at the same time as the
05ad79
+.B \-\-zero\-range
05ad79
+option.
05ad79
+Also, when using this option,
05ad79
+.B \-\-keep\-size
05ad79
+is implied.
05ad79
+.sp
05ad79
+Supported for XFS (since Linux 2.6.38), ext4 (since Linux 3.0),
05ad79
+Btrfs (since Linux 3.7) and tmpfs (since Linux 3.5).
05ad79
+.TP
05ad79
+.BR \-v ", " \-\-verbose
05ad79
+Enable verbose mode.
05ad79
+.TP
05ad79
+.BR \-x ", " \-\-posix
05ad79
+Enable POSIX operation mode.
05ad79
+In that mode allocation operation always completes,
05ad79
+but it may take longer time when fast allocation is not supported by
05ad79
+the underlying filesystem.
05ad79
+.TP
05ad79
+.BR \-z ", " \-\-zero\-range
05ad79
+Zeroes space in the byte range starting at
05ad79
+.I offset
05ad79
+and continuing for
05ad79
+.I length
05ad79
+bytes.
05ad79
+Within the specified range, blocks are preallocated for the regions
05ad79
+that span the holes in the file.
05ad79
+After a successful call,
05ad79
+subsequent reads from this range will return zeroes.
05ad79
+.sp
05ad79
+Zeroing is done within the filesystem preferably by converting the
05ad79
+range into unwritten extents.  This approach means that the specified
05ad79
+range will not be physically zeroed out on the device (except for
05ad79
+partial blocks at the either end of the range), and I/O is
05ad79
+(otherwise) required only to update metadata.
05ad79
+.sp
05ad79
+Option \fB\-\-keep\-size\fP can be specified to prevent file length
05ad79
+modification.
05ad79
+.sp
05ad79
+Available since Linux 3.14 for ext4 (only for extent-based files) and XFS.
05ad79
+.TP
05ad79
+.BR \-V ", " \-\-version
05ad79
+Display version information and exit.
05ad79
+.TP
05ad79
+.BR \-h ", " \-\-help
05ad79
+Display help text and exit.
05ad79
 .SH AUTHORS
05ad79
-.UR sandeen@redhat.com
05ad79
+.MT sandeen@redhat.com
05ad79
 Eric Sandeen
05ad79
-.UE
05ad79
+.ME
05ad79
 .br
05ad79
-.UR kzak@redhat.com
05ad79
+.MT kzak@redhat.com
05ad79
 Karel Zak
05ad79
-.UE
05ad79
+.ME
05ad79
 .SH SEE ALSO
05ad79
+.BR truncate (1),
05ad79
 .BR fallocate (2),
05ad79
-.BR posix_fallocate (3),
05ad79
-.BR truncate (1)
05ad79
+.BR posix_fallocate (3)
05ad79
 .SH AVAILABILITY
05ad79
 The fallocate command is part of the util-linux package and is available from
05ad79
-.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
05ad79
+.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
05ad79
 Linux Kernel Archive
05ad79
 .UE .
05ad79
diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c
05ad79
index 17ae5fe69..75d89a7a9 100644
05ad79
--- a/sys-utils/fallocate.c
05ad79
+++ b/sys-utils/fallocate.c
05ad79
@@ -23,6 +23,7 @@
05ad79
  */
05ad79
 #include <sys/stat.h>
05ad79
 #include <sys/types.h>
05ad79
+#include <sys/mman.h>
05ad79
 #include <ctype.h>
05ad79
 #include <errno.h>
05ad79
 #include <fcntl.h>
05ad79
@@ -31,50 +32,110 @@
05ad79
 #include <unistd.h>
05ad79
 #include <getopt.h>
05ad79
 #include <limits.h>
05ad79
+#include <string.h>
05ad79
 
05ad79
 #ifndef HAVE_FALLOCATE
05ad79
 # include <sys/syscall.h>
05ad79
 #endif
05ad79
 
05ad79
-#ifdef HAVE_LINUX_FALLOC_H
05ad79
-# include <linux/falloc.h>	/* for FALLOC_FL_* flags */
05ad79
+#if defined(HAVE_LINUX_FALLOC_H) && \
05ad79
+    (!defined(FALLOC_FL_KEEP_SIZE) || !defined(FALLOC_FL_PUNCH_HOLE) || \
05ad79
+     !defined(FALLOC_FL_COLLAPSE_RANGE) || !defined(FALLOC_FL_ZERO_RANGE) || \
05ad79
+     !defined(FALLOC_FL_INSERT_RANGE))
05ad79
+# include <linux/falloc.h>	/* non-libc fallback for FALLOC_FL_* flags */
05ad79
 #endif
05ad79
 
05ad79
+
05ad79
 #ifndef FALLOC_FL_KEEP_SIZE
05ad79
-# define FALLOC_FL_KEEP_SIZE 1
05ad79
+# define FALLOC_FL_KEEP_SIZE		0x1
05ad79
 #endif
05ad79
 
05ad79
 #ifndef FALLOC_FL_PUNCH_HOLE
05ad79
-# define FALLOC_FL_PUNCH_HOLE 2
05ad79
+# define FALLOC_FL_PUNCH_HOLE		0x2
05ad79
+#endif
05ad79
+
05ad79
+#ifndef FALLOC_FL_COLLAPSE_RANGE
05ad79
+# define FALLOC_FL_COLLAPSE_RANGE	0x8
05ad79
+#endif
05ad79
+
05ad79
+#ifndef FALLOC_FL_ZERO_RANGE
05ad79
+# define FALLOC_FL_ZERO_RANGE		0x10
05ad79
+#endif
05ad79
+
05ad79
+#ifndef FALLOC_FL_INSERT_RANGE
05ad79
+# define FALLOC_FL_INSERT_RANGE		0x20
05ad79
 #endif
05ad79
 
05ad79
 #include "nls.h"
05ad79
 #include "strutils.h"
05ad79
 #include "c.h"
05ad79
 #include "closestream.h"
05ad79
+#include "xalloc.h"
05ad79
 #include "optutils.h"
05ad79
 
05ad79
-static void __attribute__((__noreturn__)) usage(FILE *out)
05ad79
+static int verbose;
05ad79
+static char *filename;
05ad79
+
05ad79
+static void __attribute__((__noreturn__)) usage(void)
05ad79
 {
05ad79
+	FILE *out = stdout;
05ad79
 	fputs(USAGE_HEADER, out);
05ad79
 	fprintf(out,
05ad79
 	      _(" %s [options] <filename>\n"), program_invocation_short_name);
05ad79
+
05ad79
+	fputs(USAGE_SEPARATOR, out);
05ad79
+	fputs(_("Preallocate space to, or deallocate space from a file.\n"), out);
05ad79
+
05ad79
 	fputs(USAGE_OPTIONS, out);
05ad79
-	fputs(_(" -n, --keep-size     don't modify the length of the file\n"
05ad79
-		" -p, --punch-hole    punch holes in the file\n"
05ad79
-		" -o, --offset <num>  offset of the allocation, in bytes\n"
05ad79
-		" -l, --length <num>  length of the allocation, in bytes\n"), out);
05ad79
+	fputs(_(" -c, --collapse-range remove a range from the file\n"), out);
05ad79
+	fputs(_(" -d, --dig-holes      detect zeroes and replace with holes\n"), out);
05ad79
+	fputs(_(" -i, --insert-range   insert a hole at range, shifting existing data\n"), out);
05ad79
+	fputs(_(" -l, --length <num>   length for range operations, in bytes\n"), out);
05ad79
+	fputs(_(" -n, --keep-size      maintain the apparent size of the file\n"), out);
05ad79
+	fputs(_(" -o, --offset <num>   offset for range operations, in bytes\n"), out);
05ad79
+	fputs(_(" -p, --punch-hole     replace a range with a hole (implies -n)\n"), out);
05ad79
+	fputs(_(" -z, --zero-range     zero and ensure allocation of a range\n"), out);
05ad79
 #ifdef HAVE_POSIX_FALLOCATE
05ad79
-	fputs(_(" -x, --posix         use posix_fallocate(3) instead of fallocate(2)\n"), out);
05ad79
+	fputs(_(" -x, --posix          use posix_fallocate(3) instead of fallocate(2)\n"), out);
05ad79
 #endif
05ad79
+	fputs(_(" -v, --verbose        verbose mode\n"), out);
05ad79
+
05ad79
 	fputs(USAGE_SEPARATOR, out);
05ad79
-	fputs(USAGE_HELP, out);
05ad79
-	fputs(USAGE_VERSION, out);
05ad79
-	fprintf(out, USAGE_MAN_TAIL("fallocate(1)"));
05ad79
+	printf(USAGE_HELP_OPTIONS(22));
05ad79
+
05ad79
+	printf(USAGE_MAN_TAIL("fallocate(1)"));
05ad79
 
05ad79
-	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
05ad79
+	exit(EXIT_SUCCESS);
05ad79
 }
05ad79
 
05ad79
+static loff_t cvtnum(char *s)
05ad79
+{
05ad79
+	uintmax_t x;
05ad79
+
05ad79
+	if (strtosize(s, &x))
05ad79
+		return -1LL;
05ad79
+
05ad79
+	return x;
05ad79
+}
05ad79
+
05ad79
+static void xfallocate(int fd, int mode, off_t offset, off_t length)
05ad79
+{
05ad79
+	int error;
05ad79
+#ifdef HAVE_FALLOCATE
05ad79
+	error = fallocate(fd, mode, offset, length);
05ad79
+#else
05ad79
+	error = syscall(SYS_fallocate, fd, mode, offset, length);
05ad79
+#endif
05ad79
+	/*
05ad79
+	 * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported
05ad79
+	 * ENOSYS: The filesystem does not support sys_fallocate
05ad79
+	 */
05ad79
+	if (error < 0) {
05ad79
+		if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP)
05ad79
+			errx(EXIT_FAILURE, _("fallocate failed: keep size mode is unsupported"));
05ad79
+		err(EXIT_FAILURE, _("fallocate failed"));
05ad79
+	}
05ad79
+}
05ad79
 
05ad79
 #ifdef HAVE_POSIX_FALLOCATE
05ad79
 static void xposix_fallocate(int fd, off_t offset, off_t length)
05ad79
@@ -86,41 +147,163 @@ static void xposix_fallocate(int fd, off_t offset, off_t length)
05ad79
 }
05ad79
 #endif
05ad79
 
05ad79
+/* The real buffer size has to be bufsize + sizeof(uintptr_t) */
05ad79
+static int is_nul(void *buf, size_t bufsize)
05ad79
+{
05ad79
+	typedef uintptr_t word;
05ad79
+	void const *vp;
05ad79
+	char const *cbuf = buf, *cp;
05ad79
+	word const *wp = buf;
05ad79
 
05ad79
-static loff_t cvtnum(char *s)
05ad79
+	/* set sentinel */
05ad79
+	memset((char *) buf + bufsize, '\1', sizeof(word));
05ad79
+
05ad79
+	/* Find first nonzero *word*, or the word with the sentinel.  */
05ad79
+	while (*wp++ == 0)
05ad79
+		continue;
05ad79
+
05ad79
+	/* Find the first nonzero *byte*, or the sentinel.  */
05ad79
+	vp = wp - 1;
05ad79
+	cp = vp;
05ad79
+
05ad79
+	while (*cp++ == 0)
05ad79
+		continue;
05ad79
+
05ad79
+	return cbuf + bufsize < cp;
05ad79
+}
05ad79
+
05ad79
+static void dig_holes(int fd, off_t file_off, off_t len)
05ad79
 {
05ad79
-	uintmax_t x;
05ad79
+	off_t file_end = len ? file_off + len : 0;
05ad79
+	off_t hole_start = 0, hole_sz = 0;
05ad79
+	uintmax_t ct = 0;
05ad79
+	size_t  bufsz;
05ad79
+	char *buf;
05ad79
+	struct stat st;
05ad79
+#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
05ad79
+	off_t cache_start = file_off;
05ad79
+	/*
05ad79
+	 * We don't want to call POSIX_FADV_DONTNEED to discard cached
05ad79
+	 * data in PAGE_SIZE steps. IMHO it's overkill (too many syscalls).
05ad79
+	 *
05ad79
+	 * Let's assume that 1MiB (on system with 4K page size) is just
05ad79
+	 * a good compromise.
05ad79
+	 *					    -- kzak Feb-2014
05ad79
+	 */
05ad79
+	const size_t cachesz = getpagesize() * 256;
05ad79
+#endif
05ad79
 
05ad79
-	if (strtosize(s, &x))
05ad79
-		return -1LL;
05ad79
+	if (fstat(fd, &st) != 0)
05ad79
+		err(EXIT_FAILURE, _("stat of %s failed"), filename);
05ad79
 
05ad79
-	return x;
05ad79
+	bufsz = st.st_blksize;
05ad79
+
05ad79
+	if (lseek(fd, file_off, SEEK_SET) < 0)
05ad79
+		err(EXIT_FAILURE, _("seek on %s failed"), filename);
05ad79
+
05ad79
+	/* buffer + extra space for is_nul() sentinel */
05ad79
+	buf = xmalloc(bufsz + sizeof(uintptr_t));
05ad79
+	while (file_end == 0 || file_off < file_end) {
05ad79
+		/*
05ad79
+		 * Detect data area (skip holes)
05ad79
+		 */
05ad79
+		off_t end, off;
05ad79
+
05ad79
+		off = lseek(fd, file_off, SEEK_DATA);
05ad79
+		if ((off == -1 && errno == ENXIO) ||
05ad79
+		    (file_end && off >= file_end))
05ad79
+			break;
05ad79
+
05ad79
+		end = lseek(fd, off, SEEK_HOLE);
05ad79
+		if (file_end && end > file_end)
05ad79
+			end = file_end;
05ad79
+
05ad79
+#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
05ad79
+		posix_fadvise(fd, off, end, POSIX_FADV_SEQUENTIAL);
05ad79
+#endif
05ad79
+		/*
05ad79
+		 * Dig holes in the area
05ad79
+		 */
05ad79
+		while (off < end) {
05ad79
+			ssize_t rsz = pread(fd, buf, bufsz, off);
05ad79
+			if (rsz < 0 && errno)
05ad79
+				err(EXIT_FAILURE, _("%s: read failed"), filename);
05ad79
+			if (end && rsz > 0 && off > end - rsz)
05ad79
+				rsz = end - off;
05ad79
+			if (rsz <= 0)
05ad79
+				break;
05ad79
+
05ad79
+			if (is_nul(buf, rsz)) {
05ad79
+				if (!hole_sz)				/* new hole detected */
05ad79
+					hole_start = off;
05ad79
+				hole_sz += rsz;
05ad79
+			 } else if (hole_sz) {
05ad79
+				xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
05ad79
+					   hole_start, hole_sz);
05ad79
+				ct += hole_sz;
05ad79
+				hole_sz = hole_start = 0;
05ad79
+			}
05ad79
+
05ad79
+#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE)
05ad79
+			/* discard cached data */
05ad79
+			if (off - cache_start > (off_t) cachesz) {
05ad79
+				size_t clen = off - cache_start;
05ad79
+
05ad79
+				clen = (clen / cachesz) * cachesz;
05ad79
+				posix_fadvise(fd, cache_start, clen, POSIX_FADV_DONTNEED);
05ad79
+				cache_start = cache_start + clen;
05ad79
+			}
05ad79
+#endif
05ad79
+			off += rsz;
05ad79
+		}
05ad79
+		if (hole_sz) {
05ad79
+			xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
05ad79
+					hole_start, hole_sz);
05ad79
+			ct += hole_sz;
05ad79
+		}
05ad79
+		file_off = off;
05ad79
+	}
05ad79
+
05ad79
+	free(buf);
05ad79
+
05ad79
+	if (verbose) {
05ad79
+		char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, ct);
05ad79
+		fprintf(stdout, _("%s: %s (%ju bytes) converted to sparse holes.\n"),
05ad79
+				filename, str, ct);
05ad79
+		free(str);
05ad79
+	}
05ad79
 }
05ad79
 
05ad79
 int main(int argc, char **argv)
05ad79
 {
05ad79
-	char	*fname;
05ad79
 	int	c;
05ad79
-	int	error = 0;
05ad79
 	int	fd;
05ad79
 	int	mode = 0;
05ad79
-	int	posix = 0;
05ad79
+	int	dig = 0;
05ad79
+	int posix = 0;
05ad79
 	loff_t	length = -2LL;
05ad79
 	loff_t	offset = 0;
05ad79
 
05ad79
 	static const struct option longopts[] = {
05ad79
-	    { "help",      0, 0, 'h' },
05ad79
-	    { "version",   0, 0, 'V' },
05ad79
-	    { "keep-size", 0, 0, 'n' },
05ad79
-	    { "punch-hole", 0, 0, 'p' },
05ad79
-	    { "offset",    1, 0, 'o' },
05ad79
-	    { "length",    1, 0, 'l' },
05ad79
-	    { "posix",     0, 0, 'x' },
05ad79
-	    { NULL,        0, 0, 0 }
05ad79
+	    { "help",           no_argument,       NULL, 'h' },
05ad79
+	    { "version",        no_argument,       NULL, 'V' },
05ad79
+	    { "keep-size",      no_argument,       NULL, 'n' },
05ad79
+	    { "punch-hole",     no_argument,       NULL, 'p' },
05ad79
+	    { "collapse-range", no_argument,       NULL, 'c' },
05ad79
+	    { "dig-holes",      no_argument,       NULL, 'd' },
05ad79
+	    { "insert-range",   no_argument,       NULL, 'i' },
05ad79
+	    { "zero-range",     no_argument,       NULL, 'z' },
05ad79
+	    { "offset",         required_argument, NULL, 'o' },
05ad79
+	    { "length",         required_argument, NULL, 'l' },
05ad79
+	    { "posix",          no_argument,       NULL, 'x' },
05ad79
+	    { "verbose",        no_argument,       NULL, 'v' },
05ad79
+	    { NULL, 0, NULL, 0 }
05ad79
 	};
05ad79
 
05ad79
-	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
05ad79
-		{ 'x', 'n', 'p' },
05ad79
+	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
05ad79
+		{ 'c', 'd', 'p', 'z' },
05ad79
+		{ 'c', 'n' },
05ad79
+		{ 'x', 'c', 'd', 'i', 'n', 'p', 'z'},
05ad79
 		{ 0 }
05ad79
 	};
05ad79
 	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
05ad79
@@ -130,29 +313,39 @@ int main(int argc, char **argv)
05ad79
 	textdomain(PACKAGE);
05ad79
 	atexit(close_stdout);
05ad79
 
05ad79
-	while ((c = getopt_long(argc, argv, "hVnpl:o:x", longopts, NULL)) != -1) {
05ad79
+	while ((c = getopt_long(argc, argv, "hvVncpdizxl:o:", longopts, NULL))
05ad79
+			!= -1) {
05ad79
 
05ad79
 		err_exclusive_options(c, longopts, excl, excl_st);
05ad79
 
05ad79
 		switch(c) {
05ad79
 		case 'h':
05ad79
-			usage(stdout);
05ad79
+			usage();
05ad79
 			break;
05ad79
-		case 'V':
05ad79
-			printf(UTIL_LINUX_VERSION);
05ad79
-			return EXIT_SUCCESS;
05ad79
-		case 'p':
05ad79
-			mode |= FALLOC_FL_PUNCH_HOLE;
05ad79
-			/* fall through */
05ad79
-		case 'n':
05ad79
-			mode |= FALLOC_FL_KEEP_SIZE;
05ad79
+		case 'c':
05ad79
+			mode |= FALLOC_FL_COLLAPSE_RANGE;
05ad79
+			break;
05ad79
+		case 'd':
05ad79
+			dig = 1;
05ad79
+			break;
05ad79
+		case 'i':
05ad79
+			mode |= FALLOC_FL_INSERT_RANGE;
05ad79
 			break;
05ad79
 		case 'l':
05ad79
 			length = cvtnum(optarg);
05ad79
 			break;
05ad79
+		case 'n':
05ad79
+			mode |= FALLOC_FL_KEEP_SIZE;
05ad79
+			break;
05ad79
 		case 'o':
05ad79
 			offset = cvtnum(optarg);
05ad79
 			break;
05ad79
+		case 'p':
05ad79
+			mode |= FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
05ad79
+			break;
05ad79
+		case 'z':
05ad79
+			mode |= FALLOC_FL_ZERO_RANGE;
05ad79
+			break;
05ad79
 		case 'x':
05ad79
 #ifdef HAVE_POSIX_FALLOCATE
05ad79
 			posix = 1;
05ad79
@@ -160,53 +353,59 @@ int main(int argc, char **argv)
05ad79
 #else
05ad79
 			errx(EXIT_FAILURE, _("posix_fallocate support is not compiled"))
05ad79
 #endif
05ad79
-		default:
05ad79
-			usage(stderr);
05ad79
+		case 'v':
05ad79
+			verbose++;
05ad79
 			break;
05ad79
+		case 'V':
05ad79
+			printf(UTIL_LINUX_VERSION);
05ad79
+			return EXIT_SUCCESS;
05ad79
+		default:
05ad79
+			errtryhelp(EXIT_FAILURE);
05ad79
 		}
05ad79
 	}
05ad79
 
05ad79
-	if (length == -2LL)
05ad79
-		errx(EXIT_FAILURE, _("no length argument specified"));
05ad79
-	if (length <= 0)
05ad79
-		errx(EXIT_FAILURE, _("invalid length value specified"));
05ad79
-	if (offset < 0)
05ad79
-		errx(EXIT_FAILURE, _("invalid offset value specified"));
05ad79
 	if (optind == argc)
05ad79
-		errx(EXIT_FAILURE, _("no filename specified."));
05ad79
+		errx(EXIT_FAILURE, _("no filename specified"));
05ad79
+
05ad79
+	filename = argv[optind++];
05ad79
 
05ad79
-	fname = argv[optind++];
05ad79
+	if (optind != argc)
05ad79
+		errx(EXIT_FAILURE, _("unexpected number of arguments"));
05ad79
 
05ad79
-	if (optind != argc) {
05ad79
-		warnx(_("unexpected number of arguments"));
05ad79
-		usage(stderr);
05ad79
+	if (dig) {
05ad79
+		/* for --dig-holes the default is analyze all file */
05ad79
+		if (length == -2LL)
05ad79
+			length = 0;
05ad79
+		if (length < 0)
05ad79
+			errx(EXIT_FAILURE, _("invalid length value specified"));
05ad79
+	} else {
05ad79
+		/* it's safer to require the range specification (--length --offset) */
05ad79
+		if (length == -2LL)
05ad79
+			errx(EXIT_FAILURE, _("no length argument specified"));
05ad79
+		if (length <= 0)
05ad79
+			errx(EXIT_FAILURE, _("invalid length value specified"));
05ad79
 	}
05ad79
+	if (offset < 0)
05ad79
+		errx(EXIT_FAILURE, _("invalid offset value specified"));
05ad79
 
05ad79
-	fd = open(fname, O_WRONLY|O_CREAT, 0644);
05ad79
+	/* O_CREAT makes sense only for the default fallocate(2) behavior
05ad79
+	 * when mode is no specified and new space is allocated
05ad79
+	 *
05ad79
+	 * RHEL7.6: for backward compatibility in RHEL7 we keep O_CREAT there.
05ad79
+	 */
05ad79
+	fd = open(filename, O_RDWR | O_CREAT,
05ad79
+		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
05ad79
 	if (fd < 0)
05ad79
-		err(EXIT_FAILURE, _("cannot open %s"), fname);
05ad79
+		err(EXIT_FAILURE, _("cannot open %s"), filename);
05ad79
 
05ad79
+	if (dig)
05ad79
+		dig_holes(fd, offset, length);
05ad79
 #ifdef HAVE_POSIX_FALLOCATE
05ad79
-	if (posix)
05ad79
+	else if (posix)
05ad79
 		xposix_fallocate(fd, offset, length);
05ad79
-	else
05ad79
 #endif
05ad79
-
05ad79
-#ifdef HAVE_FALLOCATE
05ad79
-	error = fallocate(fd, mode, offset, length);
05ad79
-#else
05ad79
-	error = syscall(SYS_fallocate, fd, mode, offset, length);
05ad79
-#endif
05ad79
-	/*
05ad79
-	 * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported
05ad79
-	 * ENOSYS: The filesystem does not support sys_fallocate
05ad79
-	 */
05ad79
-	if (error < 0) {
05ad79
-		if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP)
05ad79
-			errx(EXIT_FAILURE,
05ad79
-				_("keep size mode (-n option) unsupported"));
05ad79
-		err(EXIT_FAILURE, _("%s: fallocate failed"), fname);
05ad79
-	}
05ad79
+	else
05ad79
+		xfallocate(fd, mode, offset, length);
05ad79
 
05ad79
 	close(fd);
05ad79
 	return EXIT_SUCCESS;
05ad79
-- 
05ad79
2.14.4
05ad79