write.c

Go to the documentation of this file.
00001 /* This file is the counterpart of "read.c".  It contains the code for writing
00002  * insofar as this is not contained in read_write().
00003  *
00004  * The entry points into this file are
00005  *   do_write:     call read_write to perform the WRITE system call
00006  *   clear_zone:   erase a zone in the middle of a file
00007  *   new_block:    acquire a new block
00008  */
00009 
00010 #include "fs.h"
00011 #include <string.h>
00012 #include "buf.h"
00013 #include "file.h"
00014 #include "fproc.h"
00015 #include "inode.h"
00016 #include "super.h"
00017 
00018 FORWARD _PROTOTYPE( void wr_indir, (struct buf *bp, int index, zone_t zone) );
00019 FORWARD _PROTOTYPE( int empty_indir, (struct buf *, struct super_block *) );
00020 
00021 /*===========================================================================*
00022  *                              do_write                                     *
00023  *===========================================================================*/
00024 PUBLIC int do_write()
00025 {
00026 /* Perform the write(fd, buffer, nbytes) system call. */
00027 
00028   return(read_write(WRITING));
00029 }
00030 
00031 /*===========================================================================*
00032  *                              write_map                                    *
00033  *===========================================================================*/
00034 PUBLIC int write_map(rip, position, new_zone, op)
00035 struct inode *rip;              /* pointer to inode to be changed */
00036 off_t position;                 /* file address to be mapped */
00037 zone_t new_zone;                /* zone # to be inserted */
00038 int op;                         /* special actions */
00039 {
00040 /* Write a new zone into an inode.
00041  *
00042  * If op includes WMAP_FREE, free the data zone corresponding to that position
00043  * in the inode ('new_zone' is ignored then). Also free the indirect block
00044  * if that was the last entry in the indirect block.
00045  * Also free the double indirect block if that was the last entry in the
00046  * double indirect block.
00047  */
00048   int scale, ind_ex, new_ind, new_dbl, zones, nr_indirects, single, zindex, ex;
00049   zone_t z, z1, z2 = NO_ZONE, old_zone;
00050   register block_t b;
00051   long excess, zone;
00052   struct buf *bp_dindir = NIL_BUF, *bp = NIL_BUF;
00053 
00054   rip->i_dirt = DIRTY;          /* inode will be changed */
00055   scale = rip->i_sp->s_log_zone_size;           /* for zone-block conversion */
00056         /* relative zone # to insert */
00057   zone = (position/rip->i_sp->s_block_size) >> scale;
00058   zones = rip->i_ndzones;       /* # direct zones in the inode */
00059   nr_indirects = rip->i_nindirs;/* # indirect zones per indirect block */
00060 
00061   /* Is 'position' to be found in the inode itself? */
00062   if (zone < zones) {
00063         zindex = (int) zone;    /* we need an integer here */
00064         if(rip->i_zone[zindex] != NO_ZONE && (op & WMAP_FREE)) {
00065                 free_zone(rip->i_dev, rip->i_zone[zindex]);
00066                 rip->i_zone[zindex] = NO_ZONE;
00067         } else {
00068                 rip->i_zone[zindex] = new_zone;
00069         }
00070         return(OK);
00071   }
00072 
00073   /* It is not in the inode, so it must be single or double indirect. */
00074   excess = zone - zones;        /* first Vx_NR_DZONES don't count */
00075   new_ind = FALSE;
00076   new_dbl = FALSE;
00077 
00078   if (excess < nr_indirects) {
00079         /* 'position' can be located via the single indirect block. */
00080         z1 = rip->i_zone[zones];        /* single indirect zone */
00081         single = TRUE;
00082   } else {
00083         /* 'position' can be located via the double indirect block. */
00084         if ( (z2 = z = rip->i_zone[zones+1]) == NO_ZONE &&
00085             !(op & WMAP_FREE)) {
00086                 /* Create the double indirect block. */
00087                 if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE)
00088                         return(err_code);
00089                 rip->i_zone[zones+1] = z;
00090                 new_dbl = TRUE; /* set flag for later */
00091         }
00092 
00093         /* 'z' is zone number for double indirect block, either old
00094          * or newly created.
00095          * If there wasn't one and WMAP_FREE is set, 'z' is NO_ZONE.
00096          */
00097         excess -= nr_indirects; /* single indirect doesn't count */
00098         ind_ex = (int) (excess / nr_indirects);
00099         excess = excess % nr_indirects;
00100         if (ind_ex >= nr_indirects) return(EFBIG);
00101 
00102         if(z == NO_ZONE) {
00103                 /* WMAP_FREE and no double indirect block - then no
00104                  * single indirect block either.
00105                  */
00106                 z1 = NO_ZONE;
00107         } else {
00108                 b = (block_t) z << scale;
00109                 bp_dindir = get_block(rip->i_dev, b, (new_dbl?NO_READ:NORMAL));
00110                 if (new_dbl) zero_block(bp_dindir);
00111                 z1 = rd_indir(bp_dindir, ind_ex);
00112         }
00113         single = FALSE;
00114   }
00115 
00116   /* z1 is now single indirect zone, or NO_ZONE; 'excess' is index.
00117    * We have to create the indirect zone if it's NO_ZONE. Unless
00118    * we're freeing (WMAP_FREE).
00119    */
00120   if (z1 == NO_ZONE && !(op & WMAP_FREE)) {
00121         z1 = alloc_zone(rip->i_dev, rip->i_zone[0]);
00122         if (single)
00123                 rip->i_zone[zones] = z1; /* update inode w. single indirect */
00124         else
00125                 wr_indir(bp_dindir, ind_ex, z1);        /* update dbl indir */
00126 
00127         new_ind = TRUE;
00128         /* If double ind, it is dirty. */
00129         if (bp_dindir != NIL_BUF) bp_dindir->b_dirt = DIRTY;
00130         if (z1 == NO_ZONE) {
00131                 /* Release dbl indirect blk. */
00132                 put_block(bp_dindir, INDIRECT_BLOCK);
00133                 return(err_code);       /* couldn't create single ind */
00134         }
00135   }
00136 
00137   /* z1 is indirect block's zone number (unless it's NO_ZONE when we're
00138    * freeing).
00139    */
00140   if(z1 != NO_ZONE) {
00141         ex = (int) excess;                      /* we need an int here */
00142         b = (block_t) z1 << scale;
00143         bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) );
00144         if (new_ind) zero_block(bp);
00145         if(op & WMAP_FREE) {
00146                 if((old_zone = rd_indir(bp, ex)) != NO_ZONE) {
00147                         free_zone(rip->i_dev, old_zone);
00148                         wr_indir(bp, ex, NO_ZONE);
00149                 }
00150 
00151                 /* Last reference in the indirect block gone? Then
00152                  * Free the indirect block.
00153                  */
00154                 if(empty_indir(bp, rip->i_sp)) {
00155                         free_zone(rip->i_dev, z1);
00156                         z1 = NO_ZONE;
00157                         /* Update the reference to the indirect block to
00158                          * NO_ZONE - in the double indirect block if there
00159                          * is one, otherwise in the inode directly.
00160                          */
00161                         if(single) {
00162                                 rip->i_zone[zones] = z1;
00163                         } else {
00164                                 wr_indir(bp_dindir, ind_ex, z1);
00165                                 bp_dindir->b_dirt = DIRTY;
00166                         }
00167                 }
00168         } else {
00169                 wr_indir(bp, ex, new_zone);
00170         }
00171         bp->b_dirt = DIRTY;
00172         put_block(bp, INDIRECT_BLOCK);
00173   }
00174 
00175   /* If the single indirect block isn't there (or was just freed),
00176    * see if we have to keep the double indirect block.
00177    */
00178   if(z1 == NO_ZONE && !single && empty_indir(bp_dindir, rip->i_sp) &&
00179      z2 != NO_ZONE) {
00180         free_zone(rip->i_dev, z2);
00181         rip->i_zone[zones+1] = NO_ZONE;
00182   }
00183 
00184   put_block(bp_dindir, INDIRECT_BLOCK); /* release double indirect blk */
00185 
00186   return(OK);
00187 }
00188 
00189 /*===========================================================================*
00190  *                              wr_indir                                     *
00191  *===========================================================================*/
00192 PRIVATE void wr_indir(bp, index, zone)
00193 struct buf *bp;                 /* pointer to indirect block */
00194 int index;                      /* index into *bp */
00195 zone_t zone;                    /* zone to write */
00196 {
00197 /* Given a pointer to an indirect block, write one entry. */
00198 
00199   struct super_block *sp;
00200 
00201   if(bp == NIL_BUF)
00202         panic(__FILE__, "wr_indir() on NIL_BUF", NO_NUM);
00203 
00204   sp = get_super(bp->b_dev);    /* need super block to find file sys type */
00205 
00206   /* write a zone into an indirect block */
00207   if (sp->s_version == V1)
00208         bp->b_v1_ind[index] = (zone1_t) conv2(sp->s_native, (int)  zone);
00209   else
00210         bp->b_v2_ind[index] = (zone_t)  conv4(sp->s_native, (long) zone);
00211 }
00212 
00213 /*===========================================================================*
00214  *                              empty_indir                                  *
00215  *===========================================================================*/
00216 PRIVATE int empty_indir(bp, sb)
00217 struct buf *bp;                 /* pointer to indirect block */
00218 struct super_block *sb;         /* superblock of device block resides on */
00219 {
00220 /* Return nonzero if the indirect block pointed to by bp contains
00221  * only NO_ZONE entries.
00222  */
00223         int i;
00224         if(sb->s_version == V1) {
00225                 for(i = 0; i < V1_INDIRECTS; i++)
00226                         if(bp->b_v1_ind[i] != NO_ZONE)
00227                                 return 0;
00228         } else {
00229                 for(i = 0; i < V2_INDIRECTS(sb->s_block_size); i++)
00230                         if(bp->b_v2_ind[i] != NO_ZONE)
00231                                 return 0;
00232         }
00233 
00234         return 1;
00235 }
00236 
00237 /*===========================================================================*
00238  *                              clear_zone                                   *
00239  *===========================================================================*/
00240 PUBLIC void clear_zone(rip, pos, flag)
00241 register struct inode *rip;     /* inode to clear */
00242 off_t pos;                      /* points to block to clear */
00243 int flag;                       /* 0 if called by read_write, 1 by new_block */
00244 {
00245 /* Zero a zone, possibly starting in the middle.  The parameter 'pos' gives
00246  * a byte in the first block to be zeroed.  Clearzone() is called from 
00247  * read_write and new_block().
00248  */
00249 
00250   register struct buf *bp;
00251   register block_t b, blo, bhi;
00252   register off_t next;
00253   register int scale;
00254   register zone_t zone_size;
00255 
00256   /* If the block size and zone size are the same, clear_zone() not needed. */
00257   scale = rip->i_sp->s_log_zone_size;
00258   if (scale == 0) return;
00259 
00260   zone_size = (zone_t) rip->i_sp->s_block_size << scale;
00261   if (flag == 1) pos = (pos/zone_size) * zone_size;
00262   next = pos + rip->i_sp->s_block_size - 1;
00263 
00264   /* If 'pos' is in the last block of a zone, do not clear the zone. */
00265   if (next/zone_size != pos/zone_size) return;
00266   if ( (blo = read_map(rip, next)) == NO_BLOCK) return;
00267   bhi = (  ((blo>>scale)+1) << scale)   - 1;
00268 
00269   /* Clear all the blocks between 'blo' and 'bhi'. */
00270   for (b = blo; b <= bhi; b++) {
00271         bp = get_block(rip->i_dev, b, NO_READ);
00272         zero_block(bp);
00273         put_block(bp, FULL_DATA_BLOCK);
00274   }
00275 }
00276 
00277 /*===========================================================================*
00278  *                              new_block                                    *
00279  *===========================================================================*/
00280 PUBLIC struct buf *new_block(rip, position)
00281 register struct inode *rip;     /* pointer to inode */
00282 off_t position;                 /* file pointer */
00283 {
00284 /* Acquire a new block and return a pointer to it.  Doing so may require
00285  * allocating a complete zone, and then returning the initial block.
00286  * On the other hand, the current zone may still have some unused blocks.
00287  */
00288 
00289   register struct buf *bp;
00290   block_t b, base_block;
00291   zone_t z;
00292   zone_t zone_size;
00293   int scale, r;
00294   struct super_block *sp;
00295 
00296   /* Is another block available in the current zone? */
00297   if ( (b = read_map(rip, position)) == NO_BLOCK) {
00298         /* Choose first zone if possible. */
00299         /* Lose if the file is nonempty but the first zone number is NO_ZONE
00300          * corresponding to a zone full of zeros.  It would be better to
00301          * search near the last real zone.
00302          */
00303         if (rip->i_zone[0] == NO_ZONE) {
00304                 sp = rip->i_sp;
00305                 z = sp->s_firstdatazone;
00306         } else {
00307                 z = rip->i_zone[0];     /* hunt near first zone */
00308         }
00309         if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NIL_BUF);
00310         if ( (r = write_map(rip, position, z, 0)) != OK) {
00311                 free_zone(rip->i_dev, z);
00312                 err_code = r;
00313                 return(NIL_BUF);
00314         }
00315 
00316         /* If we are not writing at EOF, clear the zone, just to be safe. */
00317         if ( position != rip->i_size) clear_zone(rip, position, 1);
00318         scale = rip->i_sp->s_log_zone_size;
00319         base_block = (block_t) z << scale;
00320         zone_size = (zone_t) rip->i_sp->s_block_size << scale;
00321         b = base_block + (block_t)((position % zone_size)/rip->i_sp->s_block_size);
00322   }
00323 
00324   bp = get_block(rip->i_dev, b, NO_READ);
00325   zero_block(bp);
00326   return(bp);
00327 }
00328 
00329 /*===========================================================================*
00330  *                              zero_block                                   *
00331  *===========================================================================*/
00332 PUBLIC void zero_block(bp)
00333 register struct buf *bp;        /* pointer to buffer to zero */
00334 {
00335 /* Zero a block. */
00336   memset(bp->b_data, 0, _MAX_BLOCK_SIZE);
00337   bp->b_dirt = DIRTY;
00338 }

Generated on Fri Apr 14 22:56:38 2006 for minix by  doxygen 1.4.6