cdown / rpms / util-linux

Forked from rpms/util-linux 2 years ago
Clone

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

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