Implementation of truncate(), ftruncate() and the F_FREESP fcntl().

Implemented by changing write_map to accept a WMAP_FREE flag. In that
case, it doesn't update the datablock (creating indirect zones as
necessary) pointer, but it frees the datablock if present. Also it
frees the single and double indirect blocks if unused.

This makes the implementation of truncate_inode() simpler.
truncate_inode() now accepts a truncation length which makes
implementing truncate() and ftruncate() simple.

This also allowed implementing the F_FREESP fcntl().
This commit is contained in:
Ben Gras 2006-01-11 17:14:51 +00:00
parent d9827f56ba
commit 50b06261b6
16 changed files with 417 additions and 98 deletions

View file

@ -23,6 +23,7 @@
#define F_GETLK 5 /* get record locking information */
#define F_SETLK 6 /* set record locking information */
#define F_SETLKW 7 /* set record locking info; wait if blocked */
#define F_FREESP 8 /* free a section of a regular file */
/* File descriptor flags used for fcntl(). POSIX Table 6-2. */
#define FD_CLOEXEC 1 /* close on exec flag for third arg of fcntl */

View file

@ -1,4 +1,4 @@
#define NCALLS 93 /* number of system calls allowed */
#define NCALLS 95 /* number of system calls allowed */
#define EXIT 1
#define FORK 2

View file

@ -32,6 +32,7 @@ int cmd;
case F_GETLK:
case F_SETLK:
case F_SETLKW:
case F_FREESP:
m.m1_p1 = (char *) va_arg(argp, struct flock *);
break;
}

View file

@ -1,4 +1,5 @@
#include <lib.h>
#include <string.h>
#define truncate _truncate
#define ftruncate _ftruncate
#include <unistd.h>
@ -6,8 +7,9 @@
PUBLIC int truncate(const char *_path, off_t _length)
{
message m;
m.m1_p1 = (char *) _path;
m.m1_i1 = _length;
m.m2_p1 = (char *) _path;
m.m2_i1 = strlen(_path)+1;
m.m2_l1 = _length;
return(_syscall(FS, TRUNCATE, &m));
}
@ -15,8 +17,8 @@ PUBLIC int truncate(const char *_path, off_t _length)
PUBLIC int ftruncate(int _fd, off_t _length)
{
message m;
m.m1_i2 = _fd;
m.m1_i1 = _length;
m.m2_l1 = _length;
m.m2_i1 = _fd;
return(_syscall(FS, FTRUNCATE, &m));
}

View file

@ -48,6 +48,9 @@
#define DELETE 2 /* tells search_dir to delete entry */
#define IS_EMPTY 3 /* tells search_dir to ret. OK or ENOTEMPTY */
/* write_map() args */
#define WMAP_FREE (1 << 0)
#define PATH_TRANSPARENT 000 /* parse_path stops at final object */
#define PATH_PENULTIMATE 001 /* parse_path stops at last but one name */
#define PATH_OPAQUE 002 /* parse_path stops at final name */

View file

@ -86,12 +86,12 @@ register struct inode *rip; /* pointer to inode to be released */
if (--rip->i_count == 0) { /* i_count == 0 means no one is using it now */
if (rip->i_nlinks == 0) {
/* i_nlinks == 0 means free the inode. */
truncate_inode(rip, 0, 0); /* return all the disk blocks */
truncate_inode(rip, 0); /* return all the disk blocks */
rip->i_mode = I_NOT_ALLOC; /* clear I_TYPE field */
rip->i_dirt = DIRTY;
free_inode(rip->i_dev, rip->i_num);
} else {
if (rip->i_pipe == I_PIPE) truncate_inode(rip, 0, 0);
if (rip->i_pipe == I_PIPE) truncate_inode(rip, 0);
}
rip->i_pipe = NO_PIPE; /* should always be cleared */
if (rip->i_dirt == DIRTY) rw_inode(rip, WRITING);

View file

@ -3,10 +3,13 @@
* file and the blocks must be returned to the free block pool.
*
* The entry points into this file are
* do_link: perform the LINK system call
* do_unlink: perform the UNLINK and RMDIR system calls
* do_rename: perform the RENAME system call
* truncate: release all the blocks associated with an inode
* do_link: perform the LINK system call
* do_unlink: perform the UNLINK and RMDIR system calls
* do_rename: perform the RENAME system call
* do_truncate: perform the TRUNCATE system call
* do_ftruncate: perform the FTRUNCATE system call
* truncate_inode: release the blocks associated with an inode up to a size
* freesp_inode: release a range of blocks without setting the size
*/
#include "fs.h"
@ -25,9 +28,15 @@
FORWARD _PROTOTYPE( int remove_dir, (struct inode *rldirp, 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() */
#define FIRST_HALF 0
#define LAST_HALF 1
/*===========================================================================*
* do_link *
@ -311,65 +320,223 @@ PUBLIC int do_rename()
return(r == SAME ? OK : r);
}
/*===========================================================================*
* do_truncate *
*===========================================================================*/
PUBLIC int do_truncate()
{
/* truncate_inode() does the actual work of do_truncate() and do_ftruncate().
* do_truncate() and do_ftruncate() have to get hold of the inode, either
* by name or fd, do checks on it, and call truncate_inode() to do the
* work.
*/
int r;
struct inode *rip; /* pointer to inode to be truncated */
if (fetch_name(m_in.m2_p1, m_in.m2_i1, M1) != OK)
return err_code;
if( (rip = eat_path(user_path)) == NIL_INODE)
return err_code;
if ( (rip->i_mode & I_TYPE) != I_REGULAR)
r = EINVAL;
else
r = truncate_inode(rip, m_in.m2_l1);
put_inode(rip);
return r;
}
/*===========================================================================*
* do_ftruncate *
*===========================================================================*/
PUBLIC int do_ftruncate()
{
/* As with do_truncate(), truncate_inode() does the actual work. */
struct filp *rfilp;
if ( (rfilp = get_filp(m_in.m2_i1)) == NIL_FILP)
return err_code;
if ( (rfilp->filp_ino->i_mode & I_TYPE) != I_REGULAR)
return EINVAL;
return truncate_inode(rfilp->filp_ino, m_in.m2_l1);
}
/*===========================================================================*
* truncate_inode *
*===========================================================================*/
PUBLIC void truncate_inode(rip, newsize, resetzones)
PUBLIC int truncate_inode(rip, newsize)
register struct inode *rip; /* pointer to inode to be truncated */
off_t newsize; /* inode must become this size (ignored) */
int resetzones; /* zone references cleared on disk (ignored) */
off_t newsize; /* inode must become this size */
{
/* Remove all the zones from the inode 'rip' and mark it dirty. */
register block_t b;
zone_t z, zone_size, z1;
off_t position;
int i, scale, file_type, waspipe, single, nr_indirects;
struct buf *bp;
/* Set inode to a certain size, freeing any zones no longer referenced
* and updating the size in the inode. If the inode is extended, the
* extra space is a hole that reads as zeroes.
*
* Nothing special has to happen to file pointers if inode is opened in
* O_APPEND mode, as this is different per fd and is checked when
* writing is done.
*/
zone_t zone_size;
off_t p;
int scale, file_type, waspipe;
dev_t dev;
file_type = rip->i_mode & I_TYPE; /* check to see if file is special */
if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL) return;
if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL)
return EINVAL;
if(newsize > rip->i_sp->s_max_size) /* don't let inode grow too big */
return EFBIG;
dev = rip->i_dev; /* device on which inode resides */
scale = rip->i_sp->s_log_zone_size;
zone_size = (zone_t) rip->i_sp->s_block_size << scale;
nr_indirects = rip->i_nindirs;
/* Pipes can shrink, so adjust size to make sure all zones are removed. */
waspipe = rip->i_pipe == I_PIPE; /* TRUE is this was a pipe */
if (waspipe) rip->i_size = PIPE_SIZE(rip->i_sp->s_block_size);
/* Step through the file a zone at a time, finding and freeing the zones. */
for (position = 0; position < rip->i_size; position += zone_size) {
if ( (b = read_map(rip, position)) != NO_BLOCK) {
z = (zone_t) b >> scale;
free_zone(dev, z);
}
}
/* All the data zones have been freed. Now free the indirect zones. */
rip->i_dirt = DIRTY;
waspipe = rip->i_pipe == I_PIPE; /* TRUE if this was a pipe */
if (waspipe) {
wipe_inode(rip); /* clear out inode for pipes */
return; /* indirect slots contain file positions */
if(newsize != 0)
return EINVAL; /* Only truncate pipes to 0. */
rip->i_size = PIPE_SIZE(rip->i_sp->s_block_size);
}
single = rip->i_ndzones;
free_zone(dev, rip->i_zone[single]); /* single indirect zone */
if ( (z = rip->i_zone[single+1]) != NO_ZONE) {
/* Free all the single indirect zones pointed to by the double. */
b = (block_t) z << scale;
bp = get_block(dev, b, NORMAL); /* get double indirect zone */
for (i = 0; i < nr_indirects; i++) {
z1 = rd_indir(bp, i);
free_zone(dev, z1);
/* Free the actual space if relevant. */
if(newsize < rip->i_size)
freesp_inode(rip, newsize, rip->i_size);
/* Next correct the inode size. */
if(!waspipe) rip->i_size = newsize;
rip->i_dirt = DIRTY;
return OK;
}
/*===========================================================================*
* freesp_inode *
*===========================================================================*/
PUBLIC int freesp_inode(rip, start, end)
register struct inode *rip; /* pointer to inode to be partly freed */
off_t start, end; /* range of bytes to free (end uninclusive) */
{
/* Cut an arbitrary hole in an inode. The caller is responsible for checking
* the reasonableness of the inode type of rip. The reason is this is that
* this function can be called for different reasons, for which different
* sets of inode types are reasonable. Adjusting the final size of the inode
* is to be done by the caller too, if wished.
*
* Consumers of this function currently are truncate_inode() (used to
* free indirect and data blocks for any type of inode, but also to
* implement the ftruncate() and truncate() system calls) and the F_FREESP
* fcntl().
*/
off_t p, e;
int zone_size, dev;
if(end > rip->i_size) /* freeing beyond end makes no sense */
end = rip->i_size;
if(end <= start) /* end is uninclusive, so start<end */
return EINVAL;
zone_size = rip->i_sp->s_block_size << rip->i_sp->s_log_zone_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.
*/
if(start/zone_size == (end-1)/zone_size) {
zeroblock_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);
}
/* Now free the double indirect zone itself. */
put_block(bp, INDIRECT_BLOCK);
free_zone(dev, z);
}
/* 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);
/* Leave zone numbers for de(1) to recover file after an unlink(2). */
return OK;
}
/*===========================================================================*
* nextblock *
*===========================================================================*/
PRIVATE off_t nextblock(pos, zone_size)
off_t pos;
int zone_size;
{
/* Return the first position in the next block after position 'pos'
* (unless this is the first position in the current block).
* This can be done in one expression, but that can overflow pos.
*/
off_t p;
p = (pos/zone_size)*zone_size;
if((pos % zone_size)) p += zone_size; /* Round up. */
return p;
}
/*===========================================================================*
* zeroblock_half *
*===========================================================================*/
PRIVATE void zeroblock_half(rip, pos, half)
struct inode *rip;
off_t pos;
int half;
{
/* Zero the upper or lower 'half' of a block 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
*/
int offset, len;
/* Offset of zeroing boundary. */
offset = pos % rip->i_sp->s_block_size;
if(half == LAST_HALF) {
len = rip->i_sp->s_block_size - offset;
} else {
len = offset;
pos -= offset;
offset = 0;
}
zeroblock_range(rip, pos, len);
}
/*===========================================================================*
* zeroblock_range *
*===========================================================================*/
PRIVATE void zeroblock_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.
*
*/
block_t b;
struct buf *bp;
off_t offset;
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);
}
/*===========================================================================*

View file

@ -156,6 +156,54 @@ PUBLIC int do_fcntl()
/* Set or clear a file lock. */
r = lock_op(f, m_in.request);
return(r);
case F_FREESP:
{
/* Free a section of a file. Preparation is done here,
* actual freeing in freesp_inode().
*/
off_t start, end;
struct flock flock_arg;
signed long offset;
/* Check if it's a regular file. */
if((f->filp_ino->i_mode & I_TYPE) != I_REGULAR) {
return EINVAL;
}
/* Copy flock data from userspace. */
if((r = sys_datacopy(who, (vir_bytes) m_in.name1,
SELF, (vir_bytes) &flock_arg,
(phys_bytes) sizeof(flock_arg))) != OK)
return r;
/* Convert starting offset to signed. */
offset = (signed long) flock_arg.l_start;
/* Figure out starting position base. */
switch(flock_arg.l_whence) {
case SEEK_SET: start = 0; if(offset < 0) return EINVAL; break;
case SEEK_CUR: start = f->filp_pos; break;
case SEEK_END: start = f->filp_ino->i_size; break;
default: return EINVAL;
}
/* Check for overflow or underflow. */
if(offset > 0 && start + offset < start) { return EINVAL; }
if(offset < 0 && start + offset > start) { return EINVAL; }
start += offset;
if(flock_arg.l_len > 0) {
end = start + flock_arg.l_len;
if(end <= start) {
return EINVAL;
}
r = freesp_inode(f->filp_ino, start, end);
} else {
r = truncate_inode(f->filp_ino, start);
}
return r;
}
default:
return(EINVAL);
}

View file

@ -14,6 +14,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include "buf.h"
@ -121,7 +122,7 @@ PRIVATE int common_open(register int oflags, mode_t omode)
/* Truncate regular file if O_TRUNC. */
if (oflags & O_TRUNC) {
if ((r = forbidden(rip, W_BIT)) !=OK) break;
truncate_inode(rip, 0, 0);
truncate_inode(rip, 0);
wipe_inode(rip);
/* Send the inode from the inode cache to the
* block cache, so it gets written on the next
@ -467,9 +468,9 @@ PUBLIC int do_lseek()
/* The value of 'whence' determines the start position to use. */
switch(m_in.whence) {
case 0: pos = 0; break;
case 1: pos = rfilp->filp_pos; break;
case 2: pos = rfilp->filp_ino->i_size; break;
case SEEK_SET: pos = 0; break;
case SEEK_CUR: pos = rfilp->filp_pos; break;
case SEEK_END: pos = rfilp->filp_ino->i_size; break;
default: return(EINVAL);
}

View file

@ -45,7 +45,6 @@ int action; /* action on last part of path */
struct inode *rip, *dir_ip;
char *new_name;
struct inode *new_ip;
int symloop;
char lstring[NAME_MAX];
@ -177,7 +176,6 @@ register struct inode *ldip; /* directory containing link */
size_t sl; /* length of link */
size_t tl; /* length of suffix */
char *sp; /* start of link text */
char *ep; /* end of conditional segment */
bip = NIL_INODE;
bp = NIL_BUF;

View file

@ -74,7 +74,10 @@ _PROTOTYPE( void wipe_inode, (struct inode *rip) );
_PROTOTYPE( int do_link, (void) );
_PROTOTYPE( int do_unlink, (void) );
_PROTOTYPE( int do_rename, (void) );
_PROTOTYPE( void truncate_inode, (struct inode *rip, off_t len, int cz) );
_PROTOTYPE( int do_truncate, (void) );
_PROTOTYPE( int do_ftruncate, (void) );
_PROTOTYPE( int truncate_inode, (struct inode *rip, off_t len) );
_PROTOTYPE( int freesp_inode, (struct inode *rip, off_t st, off_t end) );
/* lock.c */
_PROTOTYPE( int lock_op, (struct filp *f, int req) );
@ -147,7 +150,7 @@ _PROTOTYPE( int do_read, (void) );
_PROTOTYPE( struct buf *rahead, (struct inode *rip, block_t baseblock,
off_t position, unsigned bytes_ahead) );
_PROTOTYPE( void read_ahead, (void) );
_PROTOTYPE( block_t read_map, (struct inode *rip, off_t position) );
_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) );
@ -187,6 +190,7 @@ _PROTOTYPE( void clear_zone, (struct inode *rip, off_t pos, int flag) );
_PROTOTYPE( int do_write, (void) );
_PROTOTYPE( struct buf *new_block, (struct inode *rip, off_t position) );
_PROTOTYPE( void zero_block, (struct buf *bp) );
_PROTOTYPE( int write_map, (struct inode *, off_t, zone_t, int) );
/* select.c */
_PROTOTYPE( int do_select, (void) );

View file

@ -410,6 +410,9 @@ int index; /* index into *bp */
struct super_block *sp;
zone_t zone; /* V2 zones are longs (shorts in V1) */
if(bp == NIL_BUF)
panic(__FILE__, "rd_indir() on NIL_BUF", NO_NUM);
sp = get_super(bp->b_dev); /* need super block to find file sys type */
/* read a zone from an indirect block */

View file

@ -681,7 +681,6 @@ PUBLIC void select_timeout_check(timer_t *timer)
*===========================================================================*/
PUBLIC void select_unsuspend_by_proc(int proc)
{
struct filp *fp;
int fd, s;
for(s = 0; s < MAXSELECTS; s++) {

View file

@ -110,6 +110,8 @@ PUBLIC _PROTOTYPE (int (*call_vec[]), (void) ) = {
no_sys, /* 90 = gettimeofday */
no_sys, /* 91 = seteuid */
no_sys, /* 92 = setegid */
do_truncate, /* 93 = truncate */
do_ftruncate, /* 94 = truncate */
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];

View file

@ -15,10 +15,8 @@
#include "inode.h"
#include "super.h"
FORWARD _PROTOTYPE( int write_map, (struct inode *rip, off_t position,
zone_t new_zone) );
FORWARD _PROTOTYPE( void wr_indir, (struct buf *bp, int index, zone_t zone) );
FORWARD _PROTOTYPE( int empty_indir, (struct buf *, struct super_block *) );
/*===========================================================================*
* do_write *
@ -33,20 +31,27 @@ PUBLIC int do_write()
/*===========================================================================*
* write_map *
*===========================================================================*/
PRIVATE int write_map(rip, position, new_zone)
register struct inode *rip; /* pointer to inode to be changed */
PUBLIC int write_map(rip, position, new_zone, op)
struct inode *rip; /* pointer to inode to be changed */
off_t position; /* file address to be mapped */
zone_t new_zone; /* zone # to be inserted */
int op; /* special actions */
{
/* Write a new zone into an inode. */
/* Write a new zone into an inode.
*
* If op includes WMAP_FREE, free the data zone corresponding to that position
* in the inode ('new_zone' is ignored then). Also free the indirect block
* if that was the last entry in the indirect block.
* Also free the double indirect block if that was the last entry in the
* double indirect block.
*/
int scale, ind_ex, new_ind, new_dbl, zones, nr_indirects, single, zindex, ex;
zone_t z, z1;
zone_t z, z1, z2 = NO_ZONE, old_zone;
register block_t b;
long excess, zone;
struct buf *bp;
struct buf *bp_dindir = NIL_BUF, *bp = NIL_BUF;
rip->i_dirt = DIRTY; /* inode will be changed */
bp = NIL_BUF;
scale = rip->i_sp->s_log_zone_size; /* for zone-block conversion */
/* relative zone # to insert */
zone = (position/rip->i_sp->s_block_size) >> scale;
@ -56,7 +61,12 @@ zone_t new_zone; /* zone # to be inserted */
/* Is 'position' to be found in the inode itself? */
if (zone < zones) {
zindex = (int) zone; /* we need an integer here */
rip->i_zone[zindex] = new_zone;
if(rip->i_zone[zindex] != NO_ZONE && (op & WMAP_FREE)) {
free_zone(rip->i_dev, rip->i_zone[zindex]);
rip->i_zone[zindex] = NO_ZONE;
} else {
rip->i_zone[zindex] = new_zone;
}
return(OK);
}
@ -71,7 +81,8 @@ zone_t new_zone; /* zone # to be inserted */
single = TRUE;
} else {
/* 'position' can be located via the double indirect block. */
if ( (z = rip->i_zone[zones+1]) == NO_ZONE) {
if ( (z2 = z = rip->i_zone[zones+1]) == NO_ZONE &&
!(op & WMAP_FREE)) {
/* Create the double indirect block. */
if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE)
return(err_code);
@ -79,44 +90,98 @@ zone_t new_zone; /* zone # to be inserted */
new_dbl = TRUE; /* set flag for later */
}
/* Either way, 'z' is zone number for double indirect block. */
/* 'z' is zone number for double indirect block, either old
* or newly created.
* If there wasn't one and WMAP_FREE is set, 'z' is NO_ZONE.
*/
excess -= nr_indirects; /* single indirect doesn't count */
ind_ex = (int) (excess / nr_indirects);
excess = excess % nr_indirects;
if (ind_ex >= nr_indirects) return(EFBIG);
b = (block_t) z << scale;
bp = get_block(rip->i_dev, b, (new_dbl ? NO_READ : NORMAL));
if (new_dbl) zero_block(bp);
z1 = rd_indir(bp, ind_ex);
if(z == NO_ZONE) {
/* WMAP_FREE and no double indirect block - then no
* single indirect block either.
*/
z1 = NO_ZONE;
} else {
b = (block_t) z << scale;
bp_dindir = get_block(rip->i_dev, b, (new_dbl?NO_READ:NORMAL));
if (new_dbl) zero_block(bp_dindir);
z1 = rd_indir(bp_dindir, ind_ex);
}
single = FALSE;
}
/* z1 is now single indirect zone; 'excess' is index. */
if (z1 == NO_ZONE) {
/* Create indirect block and store zone # in inode or dbl indir blk. */
/* z1 is now single indirect zone, or NO_ZONE; 'excess' is index.
* We have to create the indirect zone if it's NO_ZONE. Unless
* we're freeing (WMAP_FREE).
*/
if (z1 == NO_ZONE && !(op & WMAP_FREE)) {
z1 = alloc_zone(rip->i_dev, rip->i_zone[0]);
if (single)
rip->i_zone[zones] = z1; /* update inode */
rip->i_zone[zones] = z1; /* update inode w. single indirect */
else
wr_indir(bp, ind_ex, z1); /* update dbl indir */
wr_indir(bp_dindir, ind_ex, z1); /* update dbl indir */
new_ind = TRUE;
if (bp != NIL_BUF) bp->b_dirt = DIRTY; /* if double ind, it is dirty*/
/* If double ind, it is dirty. */
if (bp_dindir != NIL_BUF) bp_dindir->b_dirt = DIRTY;
if (z1 == NO_ZONE) {
put_block(bp, INDIRECT_BLOCK); /* release dbl indirect blk */
/* Release dbl indirect blk. */
put_block(bp_dindir, INDIRECT_BLOCK);
return(err_code); /* couldn't create single ind */
}
}
put_block(bp, INDIRECT_BLOCK); /* release double indirect blk */
/* z1 is indirect block's zone number. */
b = (block_t) z1 << scale;
bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) );
if (new_ind) zero_block(bp);
ex = (int) excess; /* we need an int here */
wr_indir(bp, ex, new_zone);
bp->b_dirt = DIRTY;
put_block(bp, INDIRECT_BLOCK);
/* z1 is indirect block's zone number (unless it's NO_ZONE when we're
* freeing).
*/
if(z1 != NO_ZONE) {
ex = (int) excess; /* we need an int here */
b = (block_t) z1 << scale;
bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) );
if (new_ind) zero_block(bp);
if(op & WMAP_FREE) {
if((old_zone = rd_indir(bp, ex)) != NO_ZONE) {
free_zone(rip->i_dev, old_zone);
wr_indir(bp, ex, NO_ZONE);
}
/* Last reference in the indirect block gone? Then
* Free the indirect block.
*/
if(empty_indir(bp, rip->i_sp)) {
free_zone(rip->i_dev, z1);
z1 = NO_ZONE;
/* Update the reference to the indirect block to
* NO_ZONE - in the double indirect block if there
* is one, otherwise in the inode directly.
*/
if(single) {
rip->i_zone[zones] = z1;
} else {
wr_indir(bp_dindir, ind_ex, z1);
bp_dindir->b_dirt = DIRTY;
}
}
} else {
wr_indir(bp, ex, new_zone);
}
bp->b_dirt = DIRTY;
put_block(bp, INDIRECT_BLOCK);
}
/* If the single indirect block isn't there (or was just freed),
* see if we have to keep the double indirect block.
*/
if(z1 == NO_ZONE && !single && empty_indir(bp_dindir, rip->i_sp) &&
z2 != NO_ZONE) {
free_zone(rip->i_dev, z2);
rip->i_zone[zones+1] = NO_ZONE;
}
put_block(bp_dindir, INDIRECT_BLOCK); /* release double indirect blk */
return(OK);
}
@ -133,6 +198,9 @@ zone_t zone; /* zone to write */
struct super_block *sp;
if(bp == NIL_BUF)
panic(__FILE__, "wr_indir() on NIL_BUF", NO_NUM);
sp = get_super(bp->b_dev); /* need super block to find file sys type */
/* write a zone into an indirect block */
@ -142,6 +210,30 @@ zone_t zone; /* zone to write */
bp->b_v2_ind[index] = (zone_t) conv4(sp->s_native, (long) zone);
}
/*===========================================================================*
* empty_indir *
*===========================================================================*/
PRIVATE int empty_indir(bp, sb)
struct buf *bp; /* pointer to indirect block */
struct super_block *sb; /* superblock of device block resides on */
{
/* Return nonzero if the indirect block pointed to by bp contains
* only NO_ZONE entries.
*/
int i;
if(sb->s_version == V1) {
for(i = 0; i < V1_INDIRECTS; i++)
if(bp->b_v1_ind[i] != NO_ZONE)
return 0;
} else {
for(i = 0; i < V2_INDIRECTS(sb->s_block_size); i++)
if(bp->b_v2_ind[i] != NO_ZONE)
return 0;
}
return 1;
}
/*===========================================================================*
* clear_zone *
*===========================================================================*/
@ -215,7 +307,7 @@ off_t position; /* file pointer */
z = rip->i_zone[0]; /* hunt near first zone */
}
if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NIL_BUF);
if ( (r = write_map(rip, position, z)) != OK) {
if ( (r = write_map(rip, position, z, 0)) != OK) {
free_zone(rip->i_dev, z);
err_code = r;
return(NIL_BUF);

View file

@ -108,11 +108,9 @@ _PROTOTYPE (int (*call_vec[NCALLS]), (void) ) = {
do_getsetpriority, /* 89 = setpriority */
do_time, /* 90 = gettimeofday */
do_getset, /* 91 = seteuid */
do_getset /* 92 = setegid */
#if 0
do_getset, /* 92 = setegid */
no_sys, /* 93 = truncate */
no_sys, /* 94 = ftruncate */
#endif
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1];