Blame SOURCES/0159-fallocate-backport-v2.32-164-g641af90dc.patch

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