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