Fixes for truncate system calls:
- VFS: check for negative sizes in all truncate calls - VFS: update file size after truncating with fcntl(F_FREESP) - VFS: move pos/len checks for F_FREESP with l_len!=0 from FS to VFS - MFS: do not zero data block for small files when fully truncating - MFS: do not write out freed indirect blocks after freeing space - MFS: make truncate work correctly with differing zone/block sizes - tests: add new test50 for truncate call family
This commit is contained in:
parent
f029b0e0b1
commit
bdd4f5857f
8 changed files with 728 additions and 62 deletions
|
@ -11,14 +11,16 @@
|
||||||
#define SAME 1000
|
#define SAME 1000
|
||||||
|
|
||||||
FORWARD _PROTOTYPE( int remove_dir, (struct inode *rldirp,
|
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,
|
FORWARD _PROTOTYPE( int unlink_file, (struct inode *dirp,
|
||||||
struct inode *rip, char file_name[NAME_MAX]) );
|
struct inode *rip, char file_name[NAME_MAX]) );
|
||||||
FORWARD _PROTOTYPE( off_t nextblock, (off_t pos, int zonesize) );
|
FORWARD _PROTOTYPE( off_t nextblock, (off_t pos, int zone_size) );
|
||||||
FORWARD _PROTOTYPE( void zeroblock_half, (struct inode *i, off_t p, int l));
|
FORWARD _PROTOTYPE( void zerozone_half, (struct inode *rip, off_t pos,
|
||||||
FORWARD _PROTOTYPE( void zeroblock_range, (struct inode *i, off_t p, off_t h));
|
int half, int zone_size) );
|
||||||
|
FORWARD _PROTOTYPE( void zerozone_range, (struct inode *rip, off_t pos,
|
||||||
|
off_t len) );
|
||||||
|
|
||||||
/* Args to zeroblock_half() */
|
/* Args to zerozone_half() */
|
||||||
#define FIRST_HALF 0
|
#define FIRST_HALF 0
|
||||||
#define LAST_HALF 1
|
#define LAST_HALF 1
|
||||||
|
|
||||||
|
@ -513,9 +515,12 @@ off_t newsize; /* inode must become this size */
|
||||||
scale = rip->i_sp->s_log_zone_size;
|
scale = rip->i_sp->s_log_zone_size;
|
||||||
zone_size = (zone_t) rip->i_sp->s_block_size << scale;
|
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);
|
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. */
|
/* Next correct the inode size. */
|
||||||
rip->i_size = newsize;
|
rip->i_size = newsize;
|
||||||
rip->i_update |= CTIME | MTIME;
|
rip->i_update |= CTIME | MTIME;
|
||||||
|
@ -545,6 +550,7 @@ off_t start, end; /* range of bytes to free (end uninclusive) */
|
||||||
*/
|
*/
|
||||||
off_t p, e;
|
off_t p, e;
|
||||||
int zone_size, dev;
|
int zone_size, dev;
|
||||||
|
int zero_last, zero_first;
|
||||||
|
|
||||||
if(end > rip->i_size) /* freeing beyond end makes no sense */
|
if(end > rip->i_size) /* freeing beyond end makes no sense */
|
||||||
end = rip->i_size;
|
end = rip->i_size;
|
||||||
|
@ -555,27 +561,29 @@ off_t start, end; /* range of bytes to free (end uninclusive) */
|
||||||
dev = rip->i_dev; /* device on which inode resides */
|
dev = rip->i_dev; /* device on which inode resides */
|
||||||
|
|
||||||
/* If freeing doesn't cross a zone boundary, then we may only zero
|
/* 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) {
|
zero_last = start % zone_size;
|
||||||
zeroblock_range(rip, start, end-start);
|
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 {
|
} else {
|
||||||
/* First zero unused part of partly used blocks. */
|
/* First zero unused part of partly used zones. */
|
||||||
if(start%zone_size)
|
if(zero_last)
|
||||||
zeroblock_half(rip, start, LAST_HALF);
|
zerozone_half(rip, start, LAST_HALF, zone_size);
|
||||||
if(end%zone_size && end < rip->i_size)
|
if(zero_first)
|
||||||
zeroblock_half(rip, end, FIRST_HALF);
|
zerozone_half(rip, end, FIRST_HALF, zone_size);
|
||||||
}
|
|
||||||
|
|
||||||
/* Now completely free the completely unused blocks.
|
/* Now completely free the completely unused zones.
|
||||||
* write_map() will free unused (double) indirect
|
* write_map() will free unused (double) indirect
|
||||||
* blocks too. Converting the range to zone numbers avoids
|
* blocks too. Converting the range to zone numbers avoids
|
||||||
* overflow on p when doing e.g. 'p += zone_size'.
|
* overflow on p when doing e.g. 'p += zone_size'.
|
||||||
*/
|
*/
|
||||||
e = end/zone_size;
|
e = end/zone_size;
|
||||||
if(end == rip->i_size && (end % zone_size)) e++;
|
if(end == rip->i_size && (end % zone_size)) e++;
|
||||||
for(p = nextblock(start, zone_size)/zone_size; p < e; p ++)
|
for(p = nextblock(start, zone_size)/zone_size; p < e; p ++)
|
||||||
write_map(rip, p*zone_size, NO_ZONE, WMAP_FREE);
|
write_map(rip, p*zone_size, NO_ZONE, WMAP_FREE);
|
||||||
|
}
|
||||||
|
|
||||||
rip->i_update |= CTIME | MTIME;
|
rip->i_update |= CTIME | MTIME;
|
||||||
rip->i_dirt = DIRTY;
|
rip->i_dirt = DIRTY;
|
||||||
|
@ -603,62 +611,69 @@ int zone_size;
|
||||||
|
|
||||||
|
|
||||||
/*===========================================================================*
|
/*===========================================================================*
|
||||||
* zeroblock_half *
|
* zerozone_half *
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
PRIVATE void zeroblock_half(rip, pos, half)
|
PRIVATE void zerozone_half(rip, pos, half, zone_size)
|
||||||
struct inode *rip;
|
struct inode *rip;
|
||||||
off_t pos;
|
off_t pos;
|
||||||
int half;
|
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.
|
* half can be FIRST_HALF or LAST_HALF.
|
||||||
*
|
*
|
||||||
* FIRST_HALF: 0..pos-1 will be zeroed
|
* 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;
|
int offset, len;
|
||||||
|
|
||||||
/* Offset of zeroing boundary. */
|
/* Offset of zeroing boundary. */
|
||||||
offset = pos % rip->i_sp->s_block_size;
|
offset = pos % zone_size;
|
||||||
|
|
||||||
if(half == LAST_HALF) {
|
if(half == LAST_HALF) {
|
||||||
len = rip->i_sp->s_block_size - offset;
|
len = zone_size - offset;
|
||||||
} else {
|
} else {
|
||||||
len = offset;
|
len = offset;
|
||||||
pos -= 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;
|
struct inode *rip;
|
||||||
off_t pos;
|
off_t pos;
|
||||||
off_t len;
|
off_t len;
|
||||||
{
|
{
|
||||||
/* Zero a range in a block.
|
/* Zero an arbitrary byte range in a zone, possibly spanning multiple blocks.
|
||||||
* This function is used to zero a segment of a block, either
|
|
||||||
* FIRST_HALF of LAST_HALF.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
block_t b;
|
block_t b;
|
||||||
struct buf *bp;
|
struct buf *bp;
|
||||||
off_t offset;
|
off_t offset;
|
||||||
|
int bytes, block_size;
|
||||||
|
|
||||||
|
block_size = rip->i_sp->s_block_size;
|
||||||
|
|
||||||
if(!len) return; /* no zeroing to be done. */
|
if(!len) return; /* no zeroing to be done. */
|
||||||
if( (b = read_map(rip, pos)) == NO_BLOCK) return;
|
if( (b = read_map(rip, pos)) == NO_BLOCK) return;
|
||||||
if( (bp = get_block(rip->i_dev, b, NORMAL)) == NIL_BUF)
|
while (len > 0) {
|
||||||
panic(__FILE__, "zeroblock_range: no block", NO_NUM);
|
if( (bp = get_block(rip->i_dev, b, NORMAL)) == NIL_BUF)
|
||||||
offset = pos % rip->i_sp->s_block_size;
|
panic(__FILE__, "zerozone_range: no block", NO_NUM);
|
||||||
if(offset + len > rip->i_sp->s_block_size)
|
offset = pos % block_size;
|
||||||
panic(__FILE__, "zeroblock_range: len too long", len);
|
bytes = block_size - offset;
|
||||||
memset(bp->b_data + offset, 0, len);
|
if (bytes > len)
|
||||||
bp->b_dirt = DIRTY;
|
bytes = len;
|
||||||
put_block(bp, FULL_DATA_BLOCK);
|
memset(bp->b_data + offset, 0, bytes);
|
||||||
|
bp->b_dirt = DIRTY;
|
||||||
|
put_block(bp, FULL_DATA_BLOCK);
|
||||||
|
|
||||||
|
pos += bytes;
|
||||||
|
len -= bytes;
|
||||||
|
b++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,6 @@ _PROTOTYPE( struct buf *rahead, (struct inode *rip, block_t baseblock,
|
||||||
u64_t position, unsigned bytes_ahead) );
|
u64_t position, unsigned bytes_ahead) );
|
||||||
_PROTOTYPE( void read_ahead, (void) );
|
_PROTOTYPE( void read_ahead, (void) );
|
||||||
_PROTOTYPE( block_t read_map, (struct inode *rip, off_t pos) );
|
_PROTOTYPE( block_t read_map, (struct inode *rip, off_t pos) );
|
||||||
_PROTOTYPE( int read_write, (int rw_flag) );
|
|
||||||
_PROTOTYPE( zone_t rd_indir, (struct buf *bp, int index) );
|
_PROTOTYPE( zone_t rd_indir, (struct buf *bp, int index) );
|
||||||
|
|
||||||
/* stadir.c */
|
/* stadir.c */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* This file is the counterpart of "read.c". It contains the code for writing
|
/* This file is the counterpart of "read.c". It contains the code for writing
|
||||||
* insofar as this is not contained in read_write().
|
* insofar as this is not contained in fs_readwrite().
|
||||||
*
|
*
|
||||||
* The entry points into this file are
|
* The entry points into this file are
|
||||||
* write_map: write a new zone into an inode
|
* write_map: write a new zone into an inode
|
||||||
|
@ -142,7 +142,7 @@ int op; /* special actions */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Last reference in the indirect block gone? Then
|
/* Last reference in the indirect block gone? Then
|
||||||
* Free the indirect block.
|
* free the indirect block.
|
||||||
*/
|
*/
|
||||||
if(empty_indir(bp, rip->i_sp)) {
|
if(empty_indir(bp, rip->i_sp)) {
|
||||||
free_zone(rip->i_dev, z1);
|
free_zone(rip->i_dev, z1);
|
||||||
|
@ -161,15 +161,18 @@ int op; /* special actions */
|
||||||
} else {
|
} else {
|
||||||
wr_indir(bp, ex, new_zone);
|
wr_indir(bp, ex, new_zone);
|
||||||
}
|
}
|
||||||
bp->b_dirt = DIRTY;
|
/* z1 equals NO_ZONE only when we are freeing up the indirect block. */
|
||||||
|
bp->b_dirt = (z1 == NO_ZONE) ? CLEAN : DIRTY;
|
||||||
put_block(bp, INDIRECT_BLOCK);
|
put_block(bp, INDIRECT_BLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the single indirect block isn't there (or was just freed),
|
/* If the single indirect block isn't there (or was just freed),
|
||||||
* see if we have to keep the double indirect block, if any.
|
* see if we have to keep the double indirect block, if any.
|
||||||
|
* If we don't have to keep it, don't bother writing it out.
|
||||||
*/
|
*/
|
||||||
if(z1 == NO_ZONE && !single && z2 != NO_ZONE &&
|
if(z1 == NO_ZONE && !single && z2 != NO_ZONE &&
|
||||||
empty_indir(bp_dindir, rip->i_sp)) {
|
empty_indir(bp_dindir, rip->i_sp)) {
|
||||||
|
bp_dindir->b_dirt = CLEAN;
|
||||||
free_zone(rip->i_dev, z2);
|
free_zone(rip->i_dev, z2);
|
||||||
rip->i_zone[zones+1] = NO_ZONE;
|
rip->i_zone[zones+1] = NO_ZONE;
|
||||||
}
|
}
|
||||||
|
@ -236,11 +239,11 @@ struct super_block *sb; /* superblock of device block resides on */
|
||||||
PUBLIC void clear_zone(rip, pos, flag)
|
PUBLIC void clear_zone(rip, pos, flag)
|
||||||
register struct inode *rip; /* inode to clear */
|
register struct inode *rip; /* inode to clear */
|
||||||
off_t pos; /* points to block to clear */
|
off_t pos; /* points to block to clear */
|
||||||
int flag; /* 0 if called by read_write, 1 by new_block */
|
int flag; /* 1 if called by new_block, 0 otherwise */
|
||||||
{
|
{
|
||||||
/* Zero a zone, possibly starting in the middle. The parameter 'pos' gives
|
/* Zero a zone, possibly starting in the middle. The parameter 'pos' gives
|
||||||
* a byte in the first block to be zeroed. Clearzone() is called from
|
* a byte in the first block to be zeroed. Clearzone() is called from
|
||||||
* read_write and new_block().
|
* fs_readwrite(), truncate_inode(), and new_block().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
register struct buf *bp;
|
register struct buf *bp;
|
||||||
|
|
|
@ -197,6 +197,8 @@ PUBLIC int do_truncate()
|
||||||
struct vnode *vp;
|
struct vnode *vp;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
if ((off_t) m_in.flength < 0) return(EINVAL);
|
||||||
|
|
||||||
/* Temporarily open file */
|
/* Temporarily open file */
|
||||||
if (fetch_name(m_in.m2_p1, m_in.m2_i1, M1) != OK) return(err_code);
|
if (fetch_name(m_in.m2_p1, m_in.m2_i1, M1) != OK) return(err_code);
|
||||||
if ((vp = eat_path(PATH_NOFLAGS)) == NIL_VNODE) return(err_code);
|
if ((vp = eat_path(PATH_NOFLAGS)) == NIL_VNODE) return(err_code);
|
||||||
|
@ -219,10 +221,12 @@ PUBLIC int do_ftruncate()
|
||||||
int r;
|
int r;
|
||||||
struct filp *rfilp;
|
struct filp *rfilp;
|
||||||
|
|
||||||
|
if ((off_t) m_in.flength < 0) return(EINVAL);
|
||||||
|
|
||||||
/* File is already opened; get a vnode pointer from filp */
|
/* File is already opened; get a vnode pointer from filp */
|
||||||
if ((rfilp = get_filp(m_in.m2_i1)) == NIL_FILP) return(err_code);
|
if ((rfilp = get_filp(m_in.m2_i1)) == NIL_FILP) return(err_code);
|
||||||
if (!(rfilp->filp_mode & W_BIT)) return(EBADF);
|
if (!(rfilp->filp_mode & W_BIT)) return(EBADF);
|
||||||
return truncate_vnode(rfilp->filp_vno, m_in.m2_l1);
|
return truncate_vnode(rfilp->filp_vno, m_in.flength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -220,7 +220,7 @@ PUBLIC int do_fcntl()
|
||||||
|
|
||||||
/* Figure out starting position base. */
|
/* Figure out starting position base. */
|
||||||
switch(flock_arg.l_whence) {
|
switch(flock_arg.l_whence) {
|
||||||
case SEEK_SET: start = 0; if(offset < 0) return EINVAL; break;
|
case SEEK_SET: start = 0; break;
|
||||||
case SEEK_CUR:
|
case SEEK_CUR:
|
||||||
if (ex64hi(f->filp_pos) != 0)
|
if (ex64hi(f->filp_pos) != 0)
|
||||||
panic(__FILE__, "do_fcntl: position in file too high",
|
panic(__FILE__, "do_fcntl: position in file too high",
|
||||||
|
@ -236,15 +236,24 @@ PUBLIC int do_fcntl()
|
||||||
if(offset > 0 && start + offset < start) return EINVAL;
|
if(offset > 0 && start + offset < start) return EINVAL;
|
||||||
if(offset < 0 && start + offset > start) return EINVAL;
|
if(offset < 0 && start + offset > start) return EINVAL;
|
||||||
start += offset;
|
start += offset;
|
||||||
if(flock_arg.l_len > 0) {
|
if(start < 0) return EINVAL;
|
||||||
|
|
||||||
|
if(flock_arg.l_len != 0) {
|
||||||
|
if(start >= f->filp_vno->v_size) return EINVAL;
|
||||||
end = start + flock_arg.l_len;
|
end = start + flock_arg.l_len;
|
||||||
if(end <= start) return EINVAL;
|
if(end <= start) return EINVAL;
|
||||||
|
if(end > f->filp_vno->v_size) end = f->filp_vno->v_size;
|
||||||
} else {
|
} else {
|
||||||
end = 0;
|
end = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return req_ftrunc(f->filp_vno->v_fs_e, f->filp_vno->v_inode_nr, start,
|
r = req_ftrunc(f->filp_vno->v_fs_e, f->filp_vno->v_inode_nr, start,
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
if(r == OK && flock_arg.l_len == 0)
|
||||||
|
f->filp_vno->v_size = start;
|
||||||
|
|
||||||
|
return(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -10,7 +10,7 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \
|
||||||
test21 test22 test23 test25 test26 test27 test28 test29 \
|
test21 test22 test23 test25 test26 test27 test28 test29 \
|
||||||
test30 test31 test32 test34 test35 test36 test37 test38 \
|
test30 test31 test32 test34 test35 test36 test37 test38 \
|
||||||
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \
|
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \
|
||||||
test42 test44 test45 test47 test48 test49
|
test42 test44 test45 test47 test48 test49 test50
|
||||||
|
|
||||||
BIGOBJ= test20 test24
|
BIGOBJ= test20 test24
|
||||||
ROOTOBJ= test11 test33 test43 test46
|
ROOTOBJ= test11 test33 test43 test46
|
||||||
|
@ -98,4 +98,5 @@ test47: test47.c
|
||||||
test48: test48.c
|
test48: test48.c
|
||||||
test49: test49.c
|
test49: test49.c
|
||||||
test49-gcc: test49.c
|
test49-gcc: test49.c
|
||||||
|
test50: test50.c
|
||||||
|
|
||||||
|
|
4
test/run
4
test/run
|
@ -13,13 +13,13 @@ badones= # list of tests that failed
|
||||||
|
|
||||||
# Print test welcome message
|
# Print test welcome message
|
||||||
clr
|
clr
|
||||||
echo "Running POSIX compliance test suite. There are 53 tests in total."
|
echo "Running POSIX compliance test suite. There are 54 tests in total."
|
||||||
echo " "
|
echo " "
|
||||||
|
|
||||||
# Run all the tests, keeping track of who failed.
|
# Run all the tests, keeping track of who failed.
|
||||||
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
|
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
|
||||||
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
|
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
|
||||||
41 42 43 44 45 45-gcc 46 47 48 49 49-gcc sh1.sh sh2.sh
|
41 42 43 44 45 45-gcc 46 47 48 49 49-gcc 50 sh1.sh sh2.sh
|
||||||
do
|
do
|
||||||
if [ -x ./test$i ]
|
if [ -x ./test$i ]
|
||||||
then
|
then
|
||||||
|
|
635
test/test50.c
Normal file
635
test/test50.c
Normal file
|
@ -0,0 +1,635 @@
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue