[RFC] fallocate utility

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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;
}

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux