#define SAME 1000
FORWARD _PROTOTYPE( int remove_dir, (struct inode *rldirp,
- struct inode *rip, char dir_name[NAME_MAX]) );
+ struct inode *rip, char dir_name[NAME_MAX]) );
FORWARD _PROTOTYPE( int unlink_file, (struct inode *dirp,
- struct inode *rip, char file_name[NAME_MAX]) );
-FORWARD _PROTOTYPE( off_t nextblock, (off_t pos, int zonesize) );
-FORWARD _PROTOTYPE( void zeroblock_half, (struct inode *i, off_t p, int l));
-FORWARD _PROTOTYPE( void zeroblock_range, (struct inode *i, off_t p, off_t h));
-
-/* Args to zeroblock_half() */
+ struct inode *rip, char file_name[NAME_MAX]) );
+FORWARD _PROTOTYPE( off_t nextblock, (off_t pos, int zone_size) );
+FORWARD _PROTOTYPE( void zerozone_half, (struct inode *rip, off_t pos,
+ int half, int zone_size) );
+FORWARD _PROTOTYPE( void zerozone_range, (struct inode *rip, off_t pos,
+ off_t len) );
+
+/* Args to zerozone_half() */
#define FIRST_HALF 0
#define LAST_HALF 1
scale = rip->i_sp->s_log_zone_size;
zone_size = (zone_t) rip->i_sp->s_block_size << scale;
- /* Free the actual space if relevant. */
+ /* Free the actual space if truncating. */
if(newsize < rip->i_size) freesp_inode(rip, newsize, rip->i_size);
+ /* Clear the rest of the last zone if expanding. */
+ if(newsize > rip->i_size) clear_zone(rip, rip->i_size, 0);
+
/* Next correct the inode size. */
rip->i_size = newsize;
rip->i_update |= CTIME | MTIME;
*/
off_t p, e;
int zone_size, dev;
+ int zero_last, zero_first;
if(end > rip->i_size) /* freeing beyond end makes no sense */
end = rip->i_size;
dev = rip->i_dev; /* device on which inode resides */
/* If freeing doesn't cross a zone boundary, then we may only zero
- * a range of the block.
+ * a range of the zone, unless we are freeing up that entire zone.
*/
- if(start/zone_size == (end-1)/zone_size) {
- zeroblock_range(rip, start, end-start);
+ zero_last = start % zone_size;
+ zero_first = end % zone_size && end < rip->i_size;
+ if(start/zone_size == (end-1)/zone_size && (zero_last || zero_first)) {
+ zerozone_range(rip, start, end-start);
} else {
- /* First zero unused part of partly used blocks. */
- if(start%zone_size)
- zeroblock_half(rip, start, LAST_HALF);
- if(end%zone_size && end < rip->i_size)
- zeroblock_half(rip, end, FIRST_HALF);
+ /* First zero unused part of partly used zones. */
+ if(zero_last)
+ zerozone_half(rip, start, LAST_HALF, zone_size);
+ if(zero_first)
+ zerozone_half(rip, end, FIRST_HALF, zone_size);
+
+ /* Now completely free the completely unused zones.
+ * write_map() will free unused (double) indirect
+ * blocks too. Converting the range to zone numbers avoids
+ * overflow on p when doing e.g. 'p += zone_size'.
+ */
+ e = end/zone_size;
+ if(end == rip->i_size && (end % zone_size)) e++;
+ for(p = nextblock(start, zone_size)/zone_size; p < e; p ++)
+ write_map(rip, p*zone_size, NO_ZONE, WMAP_FREE);
}
- /* Now completely free the completely unused blocks.
- * write_map() will free unused (double) indirect
- * blocks too. Converting the range to zone numbers avoids
- * overflow on p when doing e.g. 'p += zone_size'.
- */
- e = end/zone_size;
- if(end == rip->i_size && (end % zone_size)) e++;
- for(p = nextblock(start, zone_size)/zone_size; p < e; p ++)
- write_map(rip, p*zone_size, NO_ZONE, WMAP_FREE);
-
rip->i_update |= CTIME | MTIME;
rip->i_dirt = DIRTY;
/*===========================================================================*
- * zeroblock_half *
+ * zerozone_half *
*===========================================================================*/
-PRIVATE void zeroblock_half(rip, pos, half)
+PRIVATE void zerozone_half(rip, pos, half, zone_size)
struct inode *rip;
off_t pos;
int half;
+int zone_size;
{
-/* Zero the upper or lower 'half' of a block that holds position 'pos'.
+/* Zero the upper or lower 'half' of a zone that holds position 'pos'.
* half can be FIRST_HALF or LAST_HALF.
*
* FIRST_HALF: 0..pos-1 will be zeroed
- * LAST_HALF: pos..blocksize-1 will be zeroed
+ * LAST_HALF: pos..zone_size-1 will be zeroed
*/
int offset, len;
/* Offset of zeroing boundary. */
- offset = pos % rip->i_sp->s_block_size;
+ offset = pos % zone_size;
if(half == LAST_HALF) {
- len = rip->i_sp->s_block_size - offset;
+ len = zone_size - offset;
} else {
len = offset;
pos -= offset;
- offset = 0;
}
- zeroblock_range(rip, pos, len);
+ zerozone_range(rip, pos, len);
}
/*===========================================================================*
- * zeroblock_range *
+ * zerozone_range *
*===========================================================================*/
-PRIVATE void zeroblock_range(rip, pos, len)
+PRIVATE void zerozone_range(rip, pos, len)
struct inode *rip;
off_t pos;
off_t len;
{
-/* Zero a range in a block.
- * This function is used to zero a segment of a block, either
- * FIRST_HALF of LAST_HALF.
- *
+/* Zero an arbitrary byte range in a zone, possibly spanning multiple blocks.
*/
block_t b;
struct buf *bp;
off_t offset;
+ int bytes, block_size;
+
+ block_size = rip->i_sp->s_block_size;
if(!len) return; /* no zeroing to be done. */
if( (b = read_map(rip, pos)) == NO_BLOCK) return;
- if( (bp = get_block(rip->i_dev, b, NORMAL)) == NIL_BUF)
- panic(__FILE__, "zeroblock_range: no block", NO_NUM);
- offset = pos % rip->i_sp->s_block_size;
- if(offset + len > rip->i_sp->s_block_size)
- panic(__FILE__, "zeroblock_range: len too long", len);
- memset(bp->b_data + offset, 0, len);
- bp->b_dirt = DIRTY;
- put_block(bp, FULL_DATA_BLOCK);
+ while (len > 0) {
+ if( (bp = get_block(rip->i_dev, b, NORMAL)) == NIL_BUF)
+ panic(__FILE__, "zerozone_range: no block", NO_NUM);
+ offset = pos % block_size;
+ bytes = block_size - offset;
+ if (bytes > len)
+ bytes = len;
+ memset(bp->b_data + offset, 0, bytes);
+ bp->b_dirt = DIRTY;
+ put_block(bp, FULL_DATA_BLOCK);
+
+ pos += bytes;
+ len -= bytes;
+ b++;
+ }
}
--- /dev/null
+/* Tests for truncate(2) call family - by D.C. van Moolenbroek */
+#define _POSIX_SOURCE 1
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#define ITERATIONS 1
+#define MAX_ERROR 4
+
+#define TESTFILE "testfile"
+#define TESTSIZE 4096
+#define THRESHOLD 1048576
+
+#ifndef MIN
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#endif
+
+#include "common.c"
+
+_PROTOTYPE(int main, (int argc, char *argv[]));
+_PROTOTYPE(void prepare, (void));
+_PROTOTYPE(int make_file, (off_t size));
+_PROTOTYPE(void check_file, (int fd, off_t size, off_t hole_start,
+ off_t hole_end));
+_PROTOTYPE(void all_sizes,
+ (_PROTOTYPE(void (*call), (off_t osize, off_t nsize))));
+_PROTOTYPE(void test50a, (void));
+_PROTOTYPE(void test50b, (void));
+_PROTOTYPE(void test50c, (void));
+_PROTOTYPE(void test50d, (void));
+_PROTOTYPE(void sub50e, (off_t osize, off_t nsize));
+_PROTOTYPE(void test50e, (void));
+_PROTOTYPE(void sub50f, (off_t osize, off_t nsize));
+_PROTOTYPE(void test50f, (void));
+_PROTOTYPE(void sub50g, (off_t osize, off_t nsize));
+_PROTOTYPE(void test50g, (void));
+_PROTOTYPE(void sub50h, (off_t osize, off_t nsize));
+_PROTOTYPE(void test50h, (void));
+_PROTOTYPE(void sub50i, (off_t size, off_t off, size_t len, int type));
+_PROTOTYPE(void test50i, (void));
+
+/* Some of the sizes have been chosen in such a way that they should be on the
+ * edge of direct/single indirect/double indirect switchovers for a MINIX
+ * file system with 4K block size.
+ */
+static off_t sizes[] = {
+ 0L, 1L, 511L, 512L, 513L, 1023L, 1024L, 1025L, 2047L, 2048L, 2049L, 3071L,
+ 3072L, 3073L, 4095L, 4096L, 4097L, 16383L, 16384L, 16385L, 28671L, 28672L,
+ 28673L, 65535L, 65536L, 65537L, 4222975L, 4222976L, 4222977L
+};
+
+static unsigned char *data;
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+ int i, j, m = 0xFFFF;
+
+ start(50);
+ prepare();
+ if (argc == 2) m = atoi(argv[1]);
+ for (j = 0; j < ITERATIONS; j++) {
+ if (m & 00001) test50a();
+ if (m & 00002) test50b();
+ if (m & 00004) test50c();
+ if (m & 00010) test50d();
+ if (m & 00020) test50e();
+ if (m & 00040) test50f();
+ if (m & 00100) test50g();
+ if (m & 00200) test50h();
+ if (m & 00400) test50i();
+ }
+
+ quit();
+ return(-1); /* impossible */
+}
+
+void prepare()
+{
+ size_t largest;
+ int i;
+
+ largest = 0;
+ for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
+ if (largest < sizes[i]) largest = sizes[i];
+
+ /* internal integrity check: this is needed for early tests */
+ assert(largest >= TESTSIZE);
+
+ data = malloc(largest);
+ if (data == NULL) e(1000);
+
+ srand(1);
+
+ for (i = 0; i < largest; i++)
+ data[i] = (unsigned char) (rand() % 255 + 1);
+}
+
+void all_sizes(call)
+_PROTOTYPE(void (*call), (off_t osize, off_t nsize));
+{
+ int i, j;
+
+ for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++)
+ for (j = 0; j < sizeof(sizes) / sizeof(sizes[0]); j++)
+ call(sizes[i], sizes[j]);
+}
+
+int make_file(size)
+off_t size;
+{
+ off_t off;
+ int i, fd, r;
+
+ if ((fd = open(TESTFILE, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) e(1001);
+
+ off = 0;
+ while (off < size) {
+ r = write(fd, data + off, size - off);
+
+ if (r != size - off) e(1002);
+
+ off += r;
+ }
+
+ return fd;
+}
+
+void check_file(fd, hole_start, hole_end, size)
+int fd;
+off_t hole_start;
+off_t hole_end;
+off_t size;
+{
+ static unsigned char buf[16384];
+ struct stat statbuf;
+ off_t off;
+ int i, chunk;
+
+ /* The size must match. */
+ if (fstat(fd, &statbuf) != 0) e(1003);
+ if (statbuf.st_size != size) e(1004);
+
+ if (lseek(fd, 0L, SEEK_SET) != 0L) e(1005);
+
+ /* All bytes in the file must be equal to what we wrote, except for the bytes
+ * in the hole, which must be zero.
+ */
+ for (off = 0; off < size; off += chunk) {
+ chunk = MIN(sizeof(buf), size - off);
+
+ if (read(fd, buf, chunk) != chunk) e(1006);
+
+ for (i = 0; i < chunk; i++) {
+ if (off + i >= hole_start && off + i < hole_end) {
+ if (buf[i] != 0) e(1007);
+ }
+ else {
+ if (buf[i] != data[off+i]) e(1008);
+ }
+ }
+ }
+
+ /* We must get back EOF at the end. */
+ if (read(fd, buf, sizeof(buf)) != 0) e(1009);
+}
+
+void test50a()
+{
+ struct stat statbuf;
+ int fd;
+
+ subtest = 1;
+
+ if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
+
+ if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
+
+ /* Negative sizes should result in EINVAL. */
+ if (truncate(TESTFILE, -1) != -1) e(3);
+ if (errno != EINVAL) e(4);
+
+ /* Make sure the file size did not change. */
+ if (fstat(fd, &statbuf) != 0) e(5);
+ if (statbuf.st_size != TESTSIZE) e(6);
+
+ close(fd);
+ if (unlink(TESTFILE) != 0) e(7);
+
+ /* An empty path should result in ENOENT. */
+ if (truncate("", 0) != -1) e(8);
+ if (errno != ENOENT) e(9);
+
+ /* A non-existing file name should result in ENOENT. */
+ if (truncate(TESTFILE"2", 0) != -1) e(10);
+ if (errno != ENOENT) e(11);
+}
+
+void test50b()
+{
+ struct stat statbuf;
+ int fd;
+
+ subtest = 2;
+
+ if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
+
+ if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
+
+ /* Negative sizes should result in EINVAL. */
+ if (ftruncate(fd, -1) != -1) e(3);
+ if (errno != EINVAL) e(4);
+
+ /* Make sure the file size did not change. */
+ if (fstat(fd, &statbuf) != 0) e(5);
+ if (statbuf.st_size != TESTSIZE) e(6);
+
+ close(fd);
+
+ /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
+ if (ftruncate(fd, 0) != -1) e(7);
+ if (errno != EBADF && errno != EINVAL) e(8);
+
+ if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(9);
+
+ /* Calls on a file opened read-only should return EBADF or EINVAL. */
+ if (ftruncate(fd, 0) != -1) e(10);
+ if (errno != EBADF && errno != EINVAL) e(11);
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(12);
+}
+
+void test50c()
+{
+ struct stat statbuf;
+ struct flock flock;
+ off_t off;
+ int fd;
+
+ subtest = 3;
+
+ if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
+
+ if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
+
+ off = TESTSIZE / 2;
+ if (lseek(fd, off, SEEK_SET) != off) e(3);
+
+ flock.l_len = 0;
+
+ /* Negative sizes should result in EINVAL. */
+ flock.l_whence = SEEK_SET;
+ flock.l_start = -1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(4);
+ if (errno != EINVAL) e(5);
+
+ flock.l_whence = SEEK_CUR;
+ flock.l_start = -off - 1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(6);
+ if (errno != EINVAL) e(7);
+
+ flock.l_whence = SEEK_END;
+ flock.l_start = -TESTSIZE - 1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(8);
+ if (errno != EINVAL) e(9);
+
+ /* Make sure the file size did not change. */
+ if (fstat(fd, &statbuf) != 0) e(10);
+ if (statbuf.st_size != TESTSIZE) e(11);
+
+ /* Proper negative values should work, however. */
+ flock.l_whence = SEEK_CUR;
+ flock.l_start = -1;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(12);
+
+ if (fstat(fd, &statbuf) != 0) e(13);
+ if (statbuf.st_size != off - 1) e(14);
+
+ flock.l_whence = SEEK_END;
+ flock.l_start = -off + 1;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(15);
+
+ if (fstat(fd, &statbuf) != 0) e(16);
+ if (statbuf.st_size != 0L) e(17);
+
+ close(fd);
+
+ /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
+ flock.l_whence = SEEK_SET;
+ flock.l_start = 0;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(18);
+ if (errno != EBADF && errno != EINVAL) e(19);
+
+ if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(20);
+
+ /* Calls on a file opened read-only should return EBADF or EINVAL. */
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(21);
+ if (errno != EBADF && errno != EINVAL) e(22);
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(23);
+}
+
+void test50d()
+{
+ struct stat statbuf;
+ struct flock flock;
+ off_t off;
+ int fd;
+
+ subtest = 4;
+
+ if ((fd = open(TESTFILE, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) e(1);
+
+ if (write(fd, data, TESTSIZE) != TESTSIZE) e(2);
+
+ off = TESTSIZE / 2;
+ if (lseek(fd, off, SEEK_SET) != off) e(3);
+
+ /* The given length must be positive. */
+ flock.l_whence = SEEK_CUR;
+ flock.l_start = 0;
+ flock.l_len = -1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(4);
+ if (errno != EINVAL) e(5);
+
+ /* Negative start positions are not allowed. */
+ flock.l_whence = SEEK_SET;
+ flock.l_start = -1;
+ flock.l_len = 1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(6);
+ if (errno != EINVAL) e(7);
+
+ flock.l_whence = SEEK_CUR;
+ flock.l_start = -off - 1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(8);
+ if (errno != EINVAL) e(9);
+
+ flock.l_whence = SEEK_END;
+ flock.l_start = -TESTSIZE - 1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(10);
+ if (errno != EINVAL) e(11);
+
+ /* Start positions at or beyond the end of the file are no good, either. */
+ flock.l_whence = SEEK_SET;
+ flock.l_start = TESTSIZE;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(12);
+ if (errno != EINVAL) e(13);
+
+ flock.l_start = TESTSIZE + 1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(13);
+ if (errno != EINVAL) e(14);
+
+ flock.l_whence = SEEK_CUR;
+ flock.l_start = TESTSIZE - off;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(15);
+ if (errno != EINVAL) e(16);
+
+ flock.l_whence = SEEK_END;
+ flock.l_start = 1;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(17);
+ if (errno != EINVAL) e(18);
+
+ /* End positions beyond the end of the file may be silently bounded. */
+ flock.l_whence = SEEK_SET;
+ flock.l_start = 0;
+ flock.l_len = TESTSIZE + 1;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(19);
+
+ flock.l_whence = SEEK_CUR;
+ flock.l_len = TESTSIZE - off + 1;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(20);
+
+ flock.l_whence = SEEK_END;
+ flock.l_start = -1;
+ flock.l_len = 2;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(21);
+
+ /* However, this must never cause the file size to change. */
+ if (fstat(fd, &statbuf) != 0) e(22);
+ if (statbuf.st_size != TESTSIZE) e(23);
+
+ close(fd);
+
+ /* Calls on an invalid file descriptor should return EBADF or EINVAL. */
+ flock.l_whence = SEEK_SET;
+ flock.l_start = 0;
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(24);
+ if (errno != EBADF && errno != EINVAL) e(25);
+
+ if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(26);
+
+ /* Calls on a file opened read-only should return EBADF or EINVAL. */
+ if (fcntl(fd, F_FREESP, &flock) != -1) e(27);
+ if (errno != EBADF && errno != EINVAL) e(28);
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(29);
+}
+
+void sub50e(osize, nsize)
+off_t osize;
+off_t nsize;
+{
+ int fd;
+
+ fd = make_file(osize);
+
+ if (truncate(TESTFILE, nsize) != 0) e(1);
+
+ check_file(fd, osize, nsize, nsize);
+
+ if (nsize < osize) {
+ if (truncate(TESTFILE, osize) != 0) e(2);
+
+ check_file(fd, nsize, osize, osize);
+ }
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(3);
+
+}
+
+void test50e()
+{
+ subtest = 5;
+
+ /* truncate(2) on a file that is open. */
+ all_sizes(sub50e);
+}
+
+void sub50f(osize, nsize)
+off_t osize;
+off_t nsize;
+{
+ int fd;
+
+ fd = make_file(osize);
+
+ close(fd);
+
+ if (truncate(TESTFILE, nsize) != 0) e(1);
+
+ if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(2);
+
+ check_file(fd, osize, nsize, nsize);
+
+ if (nsize < osize) {
+ close(fd);
+
+ if (truncate(TESTFILE, osize) != 0) e(3);
+
+ if ((fd = open(TESTFILE, O_RDONLY)) < 0) e(4);
+
+ check_file(fd, nsize, osize, osize);
+ }
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(5);
+}
+
+void test50f()
+{
+ subtest = 6;
+
+ /* truncate(2) on a file that is not open. */
+ all_sizes(sub50f);
+}
+
+void sub50g(osize, nsize)
+off_t osize;
+off_t nsize;
+{
+ int fd, r;
+
+ fd = make_file(osize);
+
+ if (ftruncate(fd, nsize) != 0) e(1);
+
+ check_file(fd, osize, nsize, nsize);
+
+ if (nsize < osize) {
+ if (ftruncate(fd, osize) != 0) e(2);
+
+ check_file(fd, nsize, osize, osize);
+ }
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(3);
+}
+
+void test50g()
+{
+ subtest = 7;
+
+ /* ftruncate(2) on an open file. */
+ all_sizes(sub50g);
+}
+
+void sub50h(osize, nsize)
+off_t osize;
+off_t nsize;
+{
+ struct flock flock;
+ int fd;
+
+ fd = make_file(osize);
+
+ flock.l_whence = SEEK_SET;
+ flock.l_start = nsize;
+ flock.l_len = 0;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(1);
+
+ check_file(fd, osize, nsize, nsize);
+
+ if (nsize < osize) {
+ flock.l_whence = SEEK_SET;
+ flock.l_start = osize;
+ flock.l_len = 0;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(2);
+
+ check_file(fd, nsize, osize, osize);
+ }
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(3);
+}
+
+void test50h()
+{
+ subtest = 8;
+
+ /* fcntl(2) with F_FREESP and l_len=0. */
+ all_sizes(sub50h);
+}
+
+void sub50i(size, off, len, type)
+off_t size;
+off_t off;
+size_t len;
+int type;
+{
+ struct flock flock;
+ int fd;
+
+ fd = make_file(size);
+
+ switch (type) {
+ case 0:
+ flock.l_whence = SEEK_SET;
+ flock.l_start = off;
+ break;
+ case 1:
+ if (lseek(fd, off, SEEK_SET) != off) e(1);
+ flock.l_whence = SEEK_CUR;
+ flock.l_start = 0;
+ break;
+ case 2:
+ flock.l_whence = SEEK_END;
+ flock.l_start = off - size;
+ break;
+ default:
+ e(1);
+ }
+
+ flock.l_len = len;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(2);
+
+ check_file(fd, off, off + len, size);
+
+ /* Repeat the call in order to see whether the file system can handle holes
+ * while freeing up. If not, the server would typically crash; we need not
+ * check the results again.
+ */
+ flock.l_whence = SEEK_SET;
+ flock.l_start = off;
+ if (fcntl(fd, F_FREESP, &flock) != 0) e(3);
+
+ close(fd);
+
+ if (unlink(TESTFILE) != 0) e(4);
+}
+
+void test50i()
+{
+ off_t off;
+ int i, j, k, l;
+
+ subtest = 9;
+
+ /* fcntl(2) with F_FREESP and l_len>0. */
+
+ /* This loop determines the size of the test file. */
+ for (i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
+ /* Big files simply take too long. We have to compromise here. */
+ if (sizes[i] >= THRESHOLD) continue;
+
+ /* This loop determines one of the two values for the offset. */
+ for (j = 0; j < sizeof(sizes) / sizeof(sizes[0]); j++) {
+ if (sizes[j] >= sizes[i]) continue;
+
+ /* This loop determines the other. */
+ for (k = 0; k < sizeof(sizes) / sizeof(sizes[0]); k++) {
+ if (sizes[k] > sizes[j]) continue;
+
+ /* Construct an offset by adding the two sizes. */
+ off = sizes[j] + sizes[k];
+
+ if (j + 1 < sizeof(sizes) / sizeof(sizes[0]) &&
+ off >= sizes[j + 1]) continue;
+
+ /* This loop determines the length of the hole. */
+ for (l = 0; l < sizeof(sizes) / sizeof(sizes[0]); l++) {
+ if (sizes[l] == 0 || off + sizes[l] > sizes[i])
+ continue;
+
+ /* This could have been a loop, too! */
+ sub50i(sizes[i], off, sizes[l], 0);
+ sub50i(sizes[i], off, sizes[l], 1);
+ sub50i(sizes[i], off, sizes[l], 2);
+ }
+ }
+ }
+ }
+}