Subject: Re: RFC: lseek() extension for sparse files version 3 + UFS implementation
To: Reinoud Zandijk <reinoud@netbsd.org>
From: Darrin B.Jewell <dbj@netbsd.org>
List: tech-kern
Date: 09/26/2006 22:42:51
--=-=-=
The following program is something I whipped up to create pathological
sparse files for testing ffs. Perhaps it will be useful.
Darrin
--=-=-=
Content-Disposition: attachment; filename=sparseffs.c
Content-Description: program to create pathological sparse files on ffs
/*
* create a pathological sparse file on ufs
*
* One block is allocated per indirect block, and the allocation
* is performed in a rather pessimal order.
*
* compile with a value for -D BLOCKSIZE to match the layout of the
* filesystem under test. Default is 16384. Note that the
* fragment size does not affect this program.
*
* compile with -D UFS2 to set NINDIR appropriate for ufs2,
* otherwise the default is appropriate for ufs1
*
* compile with -D NOIO to skip doing any actual io
*
* for example, to create maximum sized file named foo
* sparseffs foo
* or to create a file at most 1000 bytes size
* sparseffs foo 1000
* or to create a maximum length file while printing debug info
* sparseffs foo -1 DEBUG
*
* Darrin B. Jewell <dbj@netbsd.org> 2006-09-26T19:51:17-0700
*/
#define HAVE_PWRITE
#define HAVE_ERR
#define HAVE_GETPROGNAME
//#define HAVE_LLSEEK
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_ERR
#include <err.h>
#else
#include <errno.h>
#define err(x, s, ...) (printf(s ": %s\n", ##__VA_ARGS__, strerror(errno)), exit(x))
#define errx(x, s, ...) (printf(s "\n" , ##__VA_ARGS__), exit(x))
#define warnx(s, ...) (printf(s "\n" , ##__VA_ARGS__))
#endif
#ifndef HAVE_GETPROGNAME
#define getprogname() (argv[0])
#endif
#ifndef BLOCKSIZE
#define BLOCKSIZE 16384 /* default if filesystem is >= 2048000 bytes */
#endif
/* 4 for ufs1, 8 for ufs2 */
#ifndef DADDRSIZE
#define DADDRSIZE 4 /* for ufs1 */
#endif
#ifndef NINDIR
#ifdef UFS2
#define NINDIR (BLOCKSIZE/8)
#else
#define NINDIR (BLOCKSIZE/4)
#endif
#endif
#define NDADDR 12
#define NIADDR 3
int debug;
void
writebyte(int fd, int64_t off)
{
#ifdef HAVE_PWRITE
{
ssize_t w;
w = pwrite(fd, "\0", 1, off);
if (w != 1)
err(EXIT_FAILURE, "pwrite 1 byte at 0x%"PRIxMAX" returned %"PRIdMAX,
(intmax_t)off, (intmax_t)w);
}
#else
{
ssize_t w;
#ifdef HAVE_LLSEEK
offset_t l = off;
offset_t r;
r = lseek(fd, l, SEEK_SET);
if (r != l)
err(EXIT_FAILURE, "llseek(0x%"PRIxMAX") returned 0x%" PRIxMAX,
(intmax_t)l, (intmax_t)r);
#else
off_t l = off;
off_t r;
r = lseek(fd, l, SEEK_SET);
if (r != l)
err(EXIT_FAILURE, "lseek(0x%"PRIxMAX") returned 0x%" PRIxMAX,
(intmax_t)l, (intmax_t)r);
#endif
w = write(fd, "\0", 1);
if (w != 1)
err(EXIT_FAILURE, "write 1 byte at 0x%"PRIxMAX" returned %"PRIdMAX,
(intmax_t)off, (intmax_t)w);
}
#endif
}
int
main(int argc, char *argv[])
{
int64_t len;
const char *name;
int i, j, k;
#ifndef NOIO
int fd;
int error;
#endif
if (argc < 2)
errx(EXIT_FAILURE, "usage: %s filename maxlen DEBUG", getprogname());
debug = argc > 3;
name = argv[1];
len = -1;
if (argc > 2)
len = strtoll(argv[2], NULL, 0);
#ifdef HAVE_LLSEEK
if (sizeof(offset_t) < sizeof(int64_t))
warnx("Warning: sizeof(offset_t) == %d < sizeof(int64_t) == %d",
sizeof(offset_t), sizeof(int64_t));
#else
if (sizeof(off_t) < sizeof(int64_t))
warnx("Warning: sizeof(off_t) == %d < sizeof(int64_t) == %d",
sizeof(off_t), sizeof(int64_t));
#endif
fprintf(stderr, "BLOCKSIZE = %d\n", BLOCKSIZE);
fprintf(stderr, "NINDIR = %d\n", NINDIR);
int64_t sizes[NIADDR+1]; /* number of blocks indirect block level */
sizes[0] = 1;
for (i=1; i < sizeof(sizes)/sizeof(sizes[0]); i++) {
sizes[i] = sizes[i-1]*NINDIR;
}
int64_t maxsizes[NIADDR+1]; /* total number of blocks at indirect block level */
maxsizes[0] = NDADDR * sizes[0];
for (i=1; i < sizeof(maxsizes)/sizeof(maxsizes[0]); i++) {
maxsizes[i] = maxsizes[i-1] + sizes[i];
}
int64_t indirs[NIADDR+1]; /* number of indirect blocks for each level */
indirs[0] = 0;
for (i=1; i < sizeof(indirs)/sizeof(indirs[0]); i++) {
indirs[i] = (indirs[i-1]*NINDIR)+1;
}
int64_t maxindirs[NIADDR+1]; /* total number of indirect blocks for each level */
maxindirs[0] = 0;
for (i=1; i < sizeof(maxindirs)/sizeof(maxindirs[0]); i++) {
maxindirs[i] = maxindirs[i-1] + indirs[i];
}
for (i=0; i < sizeof(sizes)/sizeof(sizes[0]); i++) {
fprintf(stderr, "sizes[%d] = %"PRIu64" blocks, %"PRIu64" bytes\n", i, sizes[i], sizes[i]*BLOCKSIZE);
}
for (i=0; i < sizeof(maxsizes)/sizeof(maxsizes[0]); i++) {
fprintf(stderr, "maxsizes[%d] = %"PRIu64" blocks, %"PRIu64" bytes\n", i, maxsizes[i], maxsizes[i]*BLOCKSIZE);
}
for (i=0; i < sizeof(indirs)/sizeof(indirs[0]); i++) {
fprintf(stderr, "indirs[%d] = %"PRIu64" indirs, %"PRIu64" bytes\n", i, indirs[i], indirs[i]*BLOCKSIZE);
}
for (i=0; i < sizeof(maxindirs)/sizeof(maxindirs[0]); i++) {
fprintf(stderr, "maxindirs[%d] = %"PRIu64" maxindirs, %"PRIu64" bytes\n", i, maxindirs[i], maxindirs[i]*BLOCKSIZE);
}
if ((len < 0) || (len > maxsizes[NIADDR]*BLOCKSIZE-1))
len = maxsizes[NIADDR]*BLOCKSIZE-1; /* ffs sets maxfilesize to this */
if (debug)
fprintf(stdout, "creating file at most = 0x%"PRIxMAX" = %"PRIdMAX" bytes\n",
(intmax_t)len, (intmax_t)len);
#ifndef NOIO
fd = open(name, O_CREAT | O_WRONLY, 0666);
if (!fd)
err(EXIT_FAILURE, "open %s", name);
#endif
if (len <= 0)
goto close;
/* If sparse file would have no indirect blocks, allocate it now */
if (len <= maxsizes[0]*BLOCKSIZE) {
if (debug) {
fprintf(stdout, "off = %16"PRIdMAX" direct\n", (intmax_t)len-1);
fflush(stdout);
}
#ifndef NOIO
writebyte(fd, len-1);
#endif
goto close;
}
/* Allocate indirect blocks in particularly insideous order
* Order as follows:
* each 1 level indirect pointer on inode indirect pointer 3 step by sizes[3]
* each 1 level indirect pointer on inode indirect pointer 2 step by sizes[2]
* each 1 level indirect pointer on inode indirect pointer 1 step by sizes[1]
* each 2 level indirect pointer on inode indirect pointer 3 step by sizes[2]
* each 2 level indirect pointer on inode indirect pointer 2 step by sizes[1]
* each 3 level indirect pointer on inode indirect pointer 3 step by sizes[1]
* i j k
* notice that k=j-i+1
*/
for (i = 1; i <= NIADDR; i++) {
for (j = NIADDR; j >= i; j--) {
int64_t boff;
k=j-i+1;
/* allocate a block in the file, but at most one per indirect */
for (boff = maxsizes[j]-sizes[k]; boff >= maxsizes[j-1]; boff -= sizes[k]) {
int64_t off;
/* First possible byte */
off = boff*BLOCKSIZE;
/* If start of offset is past len, then ignore this pass */
if (off > len-1)
continue;
/* Last possible byte */
off = ((boff + sizes[k])*BLOCKSIZE)-1;
/* If end of offset is past len, truncate offset */
if (off > len-1)
off = len-1;
if (debug) {
fprintf(stdout, "off = %16"PRId64" step = %16"PRId64"; lvl[ %d ] ib[ %d ] sizes[ %d ]\n",
off, (sizes[k]*BLOCKSIZE), i, j, k);
fflush(stdout);
}
#ifndef NOIO
writebyte(fd, off);
#endif
}
}
}
close:
#ifndef NOIO
error = close(fd);
if (error == -1)
err(EXIT_FAILURE, "close");
#endif
return EXIT_SUCCESS;
}
--=-=-=--