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.
717 lines
20 KiB
717 lines
20 KiB
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 |
|
|
|
|