#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 */
-#define NCALLS 93 /* number of system calls allowed */
+#define NCALLS 95 /* number of system calls allowed */
#define EXIT 1
#define FORK 2
case F_GETLK:
case F_SETLK:
case F_SETLKW:
+ case F_FREESP:
m.m1_p1 = (char *) va_arg(argp, struct flock *);
break;
}
#include <lib.h>
+#include <string.h>
#define truncate _truncate
#define ftruncate _ftruncate
#include <unistd.h>
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));
}
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));
}
#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 */
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);
* 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"
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 *
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);
- }
+ waspipe = rip->i_pipe == I_PIPE; /* TRUE if this was a pipe */
+ if (waspipe) {
+ if(newsize != 0)
+ return EINVAL; /* Only truncate pipes to 0. */
+ rip->i_size = PIPE_SIZE(rip->i_sp->s_block_size);
}
- /* All the data zones have been freed. Now free the indirect zones. */
+ /* 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;
- if (waspipe) {
- wipe_inode(rip); /* clear out inode for pipes */
- return; /* indirect slots contain file positions */
- }
- 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);
+
+ 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);
+
+ 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;
+}
- /* Leave zone numbers for de(1) to recover file after an unlink(2). */
+/*===========================================================================*
+ * 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);
}
/*===========================================================================*
/* 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);
}
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
+#include <unistd.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include "buf.h"
/* 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
/* 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);
}
struct inode *rip, *dir_ip;
char *new_name;
- struct inode *new_ip;
int symloop;
char lstring[NAME_MAX];
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;
_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) );
_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) );
_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) );
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 */
*===========================================================================*/
PUBLIC void select_unsuspend_by_proc(int proc)
{
- struct filp *fp;
int fd, s;
for(s = 0; s < MAXSELECTS; s++) {
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];
#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 *
/*===========================================================================*
* 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;
/* 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);
}
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);
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);
}
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 */
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 *
*===========================================================================*/
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);
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];