Attached is a first stab at an fallocate utility, which will preallocate blocks to a file. This can be useful for virtual images, database files, testing, etc. Normally we'd hope that various tools will start using preallocation internally, but until then such a utility may be useful, and could be scripted as well. There are some bits of trickiness here .... We need a perfect storm for this all to work smoothly. :) * we need a kernel with sys_fallocate * we need to operate on a filesystem that supports fallocate * we need a glibc that maps fallocate() to sys_fallocate fallocate(2) isn't supported by anything but bleeding-edge glibc, and has a few problems even then (https://bugzilla.redhat.com/show_bug.cgi?id=500487) I put a fallback in the attached tool so that if fallocate() fails, it goes to posix_fallocate. On recent (not bleeding) glibc, posix_fallocate tries to call sys_fallocate, and resorts to writing 0s if that fails. On even older glibc, it goes straight to 0-writing. The user won't know which one happened, unless they allocate a few gigabytes and notice the delay. As it stands, the tool won't built at all unless glibc provides fallocate. I suppose that could be fixed with some autoconf-fu, and just go straight to posix_fallocate. Anyway, it's a rough cut. What do folks think? Thanks, -Eric
.\" -*- nroff -*- .TH FALLOCATE 1 "Jul 2009" "Version 1.0" .SH NAME fallocate \- preallocate space to a file. .SH SYNOPSIS .B fallocate [ .B \-nv ] [ .B \-o .I offset ] .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. .PP If the .BR fallocate (2) system call is not supported, this tool falls back to using .BR posix_fallocate (3), which simply writes 0s into the file. In this case the .B \-n option is not supported and if specified, the command will return with an error. .PP The exit code returned by .B fallocate is 0 on success and 1 on failure. .PP .SH OPTIONS .TP .B -n Do not modify the apparent length of the file. This may effectively allocate blocks past EOF, which can be removed with a truncate. .TP .B -v Be more verbose. Specifically, warn if falling back to .BR posix_fallocate (3). .TP .BI -o \ offset Specifies the beginning offset of the allocation, in bytes. Suffixes of k, m, g, t, p, e may be specified to denote KiB, MiB, GiB, etc. .TP .BI -l \ length Specifies the length of the allocation, in bytes. Suffixes of k, m, g, t, p, e may be specified to denote KiB, MiB, GiB, etc. .SH BUGS All generic options must precede and not be combined with file system-specific options. Some file system-specific programs do not support the .I -v (verbose) option, nor return meaningful exit codes. Also, some file system-specific programs do not automatically detect the device size and require the .I blocks parameter to be specified. .SH AUTHORS Eric Sandeen (sandeen@xxxxxxxxxx) .SH SEE ALSO .BR fallocate (2), .BR posix_fallocate (3) .SH AVAILABILITY The fallocate command is part of the util-linux-ng package and is available from ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/.
/* * fallocate - utility to use the fallocate system call * * Copyright (C) 2008 Red Hat, Inc. All rights reserved. * Written by Eric Sandeen <sandeen@xxxxxxxxxx> * * cvtnum routine taken from xfsprogs, * Copyright (c) 2003-2005 Silicon Graphics, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <sys/stat.h> #include <sys/types.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/falloc.h> void usage(void) { printf("Usage: fallocate [-nv] [-o offset] -l length filename\n"); exit(EXIT_FAILURE); } #define EXABYTES(x) ((long long)(x) << 60) #define PETABYTES(x) ((long long)(x) << 50) #define TERABYTES(x) ((long long)(x) << 40) #define GIGABYTES(x) ((long long)(x) << 30) #define MEGABYTES(x) ((long long)(x) << 20) #define KILOBYTES(x) ((long long)(x) << 10) long long cvtnum(char *s) { long long i; char *sp; int c; i = strtoll(s, &sp, 0); if (i == 0 && sp == s) return -1LL; if (*sp == '\0') return i; if (sp[1] != '\0') return -1LL; c = tolower(*sp); switch (c) { case 'k': return KILOBYTES(i); case 'm': return MEGABYTES(i); case 'g': return GIGABYTES(i); case 't': return TERABYTES(i); case 'p': return PETABYTES(i); case 'e': return EXABYTES(i); } return -1LL; } int main(int argc, char **argv) { int fd; char *fname; int opt; loff_t length = -2LL; loff_t offset = 0; int falloc_mode = 0; int verbose = 0; int error; while ((opt = getopt(argc, argv, "nl:o:v")) != -1) { switch(opt) { case 'n': /* do not change filesize */ falloc_mode = FALLOC_FL_KEEP_SIZE; break; case 'l': length = cvtnum(optarg); break; case 'o': offset = cvtnum(optarg); break; case 'v': verbose++; break; default: usage(); } } if (length == -2LL) { printf("Error: no length argument specified\n"); usage(); } if (length <= 0) { printf("Error: invalid length value specified\n"); usage(); } if (offset < 0) { printf("Error: invalid offset value specified\n"); usage(); } if (optind == argc) { printf("Error: no filename specified\n"); usage(); } fname = argv[optind++]; fd = open(fname, O_WRONLY|O_CREAT, 0644); if (fd < 0) { perror("Error opening file"); exit(EXIT_FAILURE); } error = fallocate(fd, falloc_mode, offset, length); /* * EOPNOTSUPP: The falloc_mode is unsupported * ENOSYS: The filesystem does not support sys_fallocate */ if (error < 0 && (errno == EOPNOTSUPP || errno == ENOSYS)) { if (verbose || falloc_mode) printf("Falling back to posix_fallocate\n"); if (falloc_mode) { printf("-n option unsupported\n"); exit(EXIT_FAILURE); } error = posix_fallocate(fd, offset, length); } if (error < 0) { perror("fallocate failed"); exit(EXIT_FAILURE); } close(fd); return 0; }