diff --git a/include/fcntl.h b/include/fcntl.h index 2aa7cce69..fd3b6fdaf 100755 --- a/include/fcntl.h +++ b/include/fcntl.h @@ -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 */ diff --git a/include/minix/callnr.h b/include/minix/callnr.h index b4a81effc..5c5520bde 100755 --- a/include/minix/callnr.h +++ b/include/minix/callnr.h @@ -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 diff --git a/lib/posix/_fcntl.c b/lib/posix/_fcntl.c index d92610ef3..5d3ecc94e 100755 --- a/lib/posix/_fcntl.c +++ b/lib/posix/_fcntl.c @@ -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; } diff --git a/lib/posix/_truncate.c b/lib/posix/_truncate.c index f939ea0b4..7d2be517a 100755 --- a/lib/posix/_truncate.c +++ b/lib/posix/_truncate.c @@ -1,4 +1,5 @@ #include +#include #define truncate _truncate #define ftruncate _ftruncate #include @@ -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)); } diff --git a/servers/fs/const.h b/servers/fs/const.h index 71d8bad45..b978ea2a3 100644 --- a/servers/fs/const.h +++ b/servers/fs/const.h @@ -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 */ diff --git a/servers/fs/inode.c b/servers/fs/inode.c index a97779666..519c8244b 100644 --- a/servers/fs/inode.c +++ b/servers/fs/inode.c @@ -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); diff --git a/servers/fs/link.c b/servers/fs/link.c index bf18021c0..036fce832 100644 --- a/servers/fs/link.c +++ b/servers/fs/link.c @@ -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 starti_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); } /*===========================================================================* diff --git a/servers/fs/misc.c b/servers/fs/misc.c index 4d39e9b5c..59f96d825 100644 --- a/servers/fs/misc.c +++ b/servers/fs/misc.c @@ -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); } diff --git a/servers/fs/open.c b/servers/fs/open.c index 0f64d3705..0c97c2a5f 100644 --- a/servers/fs/open.c +++ b/servers/fs/open.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #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); } diff --git a/servers/fs/path.c b/servers/fs/path.c index 514f627f2..edf3236ba 100644 --- a/servers/fs/path.c +++ b/servers/fs/path.c @@ -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; diff --git a/servers/fs/proto.h b/servers/fs/proto.h index e3561fc09..d4e17b139 100644 --- a/servers/fs/proto.h +++ b/servers/fs/proto.h @@ -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) ); diff --git a/servers/fs/read.c b/servers/fs/read.c index 77d323634..9e446528b 100644 --- a/servers/fs/read.c +++ b/servers/fs/read.c @@ -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 */ diff --git a/servers/fs/select.c b/servers/fs/select.c index b361ee52d..34e86335b 100644 --- a/servers/fs/select.c +++ b/servers/fs/select.c @@ -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++) { diff --git a/servers/fs/table.c b/servers/fs/table.c index 59f0c1de6..4b1df0811 100644 --- a/servers/fs/table.c +++ b/servers/fs/table.c @@ -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]; diff --git a/servers/fs/write.c b/servers/fs/write.c index 23bd3ad6f..7652ea9d1 100644 --- a/servers/fs/write.c +++ b/servers/fs/write.c @@ -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); diff --git a/servers/pm/table.c b/servers/pm/table.c index 954b45bc2..fb053a932 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -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];