]> Zhao Yanbai Git Server - minix.git/commitdiff
Implementation of truncate(), ftruncate() and the F_FREESP fcntl().
authorBen Gras <ben@minix3.org>
Wed, 11 Jan 2006 17:14:51 +0000 (17:14 +0000)
committerBen Gras <ben@minix3.org>
Wed, 11 Jan 2006 17:14:51 +0000 (17:14 +0000)
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().

16 files changed:
include/fcntl.h
include/minix/callnr.h
lib/posix/_fcntl.c
lib/posix/_truncate.c
servers/fs/const.h
servers/fs/inode.c
servers/fs/link.c
servers/fs/misc.c
servers/fs/open.c
servers/fs/path.c
servers/fs/proto.h
servers/fs/read.c
servers/fs/select.c
servers/fs/table.c
servers/fs/write.c
servers/pm/table.c

index 2aa7cce6987258f7878a4874fca627a4642a607a..fd3b6fdaf9d3aa2750765397be54ca6201ef7426 100755 (executable)
@@ -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 */
index b4a81effcf2ee5630072094c877d2bdcd420475b..5c5520bdebd68e9e4dd3e51e5be3254ecdd7cea3 100755 (executable)
@@ -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 
index d92610ef391f4d135f21aa6f66222105a6b765d6..5d3ecc94eeaf3f01a02e55e095861b468f8cb9bf 100755 (executable)
@@ -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;
   }
index f939ea0b471d8a75488c40fc27aca0a18db36633..7d2be517a07a3a25cde0716ad10700949247c4c5 100755 (executable)
@@ -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));
 }
index 71d8bad45022f5079cc66ebf6cde7c8022f3cc33..b978ea2a3f12db42133929fe0d2c59114fffce0d 100644 (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 */
index a977796666ab71a455a5bca49c4642a6907024f5..519c8244b20e4785b8a48b56c69bdae0122e6462 100644 (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);
index bf18021c088b28833daa73102227efdbc7940d90..036fce832ce685c532da92a49dae885957a1513c 100644 (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"
 
 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);
-       }
+  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);
 }
 
 /*===========================================================================*
index 4d39e9b5c44b879dcaf1e5cec2db181c74d478e5..59f96d8256cf7c80b076fe924759f278c2ab5767 100644 (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);
   }
index 0f64d3705f20a3f09ca6a060929ee2f0e56895e0..0c97c2a5f23a8e6cda985a90821d693386709270 100644 (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);
   }
 
index 514f627f25701d47ffa6e87a11e153f8f6ef51dd..edf3236baeae4e14d7021cdd8898652cdfe71ac2 100644 (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;
index e3561fc09331ab3f853102cc7a808ccee4cac422..d4e17b139fdf3aa799565397947432c1a2cec393 100644 (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)                                      );
index 77d323634c4af132e8f8c2b57dd08b2318bb845c..9e446528be920671c2c22de53127e97888f4e231 100644 (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 */
index b361ee52d6fbea75f2441b208eade7d2cd020432..34e86335b8b41fd5adfebee77f5bc3056745bf1a 100644 (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++) {
index 59f0c1de6b93bd6ce89238fcc5c1848e1f4ee217..4b1df0811092ca93096c20a0315f6c1621d853a1 100644 (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];
index 23bd3ad6f49a1b1f6976590c2eb96650b40ca1f2..7652ea9d13a4cb3573308804d4afefc0ed959436 100644 (file)
 #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);
index 954b45bc2936fe2970b98a760ad634f47f34fd4d..fb053a93278019838583b5d6569ac244c37598d6 100644 (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];