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
					
				| @ -14,11 +14,13 @@ 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,19 +561,20 @@ 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'. | ||||||
| @ -576,6 +583,7 @@ off_t start, end;		/* range of bytes to free (end uninclusive) */ | |||||||
| 	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; | ||||||
|  |   while (len > 0) { | ||||||
| 	if( (bp = get_block(rip->i_dev, b, NORMAL)) == NIL_BUF) | 	if( (bp = get_block(rip->i_dev, b, NORMAL)) == NIL_BUF) | ||||||
| 	panic(__FILE__, "zeroblock_range: no block", NO_NUM); | 		panic(__FILE__, "zerozone_range: no block", NO_NUM); | ||||||
|   offset = pos % rip->i_sp->s_block_size; | 	offset = pos % block_size; | ||||||
|   if(offset + len > rip->i_sp->s_block_size) | 	bytes = block_size - offset; | ||||||
| 	panic(__FILE__, "zeroblock_range: len too long", len); | 	if (bytes > len) | ||||||
|   memset(bp->b_data + offset, 0, len); | 		bytes = len; | ||||||
|  | 	memset(bp->b_data + offset, 0, bytes); | ||||||
| 	bp->b_dirt = DIRTY; | 	bp->b_dirt = DIRTY; | ||||||
| 	put_block(bp, FULL_DATA_BLOCK); | 	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 David van Moolenbroek
						David van Moolenbroek