The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/servers/fs/write.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* This file is the counterpart of "read.c".  It contains the code for writing
    2  * insofar as this is not contained in read_write().
    3  *
    4  * The entry points into this file are
    5  *   do_write:     call read_write to perform the WRITE system call
    6  *   clear_zone:   erase a zone in the middle of a file
    7  *   new_block:    acquire a new block
    8  */
    9 
   10 #include "fs.h"
   11 #include <string.h>
   12 #include "buf.h"
   13 #include "file.h"
   14 #include "fproc.h"
   15 #include "inode.h"
   16 #include "super.h"
   17 
   18 FORWARD _PROTOTYPE( int write_map, (struct inode *rip, off_t position,
   19                         zone_t new_zone)                                );
   20 
   21 FORWARD _PROTOTYPE( void wr_indir, (struct buf *bp, int index, zone_t zone) );
   22 
   23 /*===========================================================================*
   24  *                              do_write                                     *
   25  *===========================================================================*/
   26 PUBLIC int do_write()
   27 {
   28 /* Perform the write(fd, buffer, nbytes) system call. */
   29 
   30   return(read_write(WRITING));
   31 }
   32 
   33 /*===========================================================================*
   34  *                              write_map                                    *
   35  *===========================================================================*/
   36 PRIVATE int write_map(rip, position, new_zone)
   37 register struct inode *rip;     /* pointer to inode to be changed */
   38 off_t position;                 /* file address to be mapped */
   39 zone_t new_zone;                /* zone # to be inserted */
   40 {
   41 /* Write a new zone into an inode. */
   42   int scale, ind_ex, new_ind, new_dbl, zones, nr_indirects, single, zindex, ex;
   43   zone_t z, z1;
   44   register block_t b;
   45   long excess, zone;
   46   struct buf *bp;
   47 
   48   rip->i_dirt = DIRTY;          /* inode will be changed */
   49   bp = NIL_BUF;
   50   scale = rip->i_sp->s_log_zone_size;           /* for zone-block conversion */
   51         /* relative zone # to insert */
   52   zone = (position/rip->i_sp->s_block_size) >> scale;
   53   zones = rip->i_ndzones;       /* # direct zones in the inode */
   54   nr_indirects = rip->i_nindirs;/* # indirect zones per indirect block */
   55 
   56   /* Is 'position' to be found in the inode itself? */
   57   if (zone < zones) {
   58         zindex = (int) zone;    /* we need an integer here */
   59         rip->i_zone[zindex] = new_zone;
   60         return(OK);
   61   }
   62 
   63   /* It is not in the inode, so it must be single or double indirect. */
   64   excess = zone - zones;        /* first Vx_NR_DZONES don't count */
   65   new_ind = FALSE;
   66   new_dbl = FALSE;
   67 
   68   if (excess < nr_indirects) {
   69         /* 'position' can be located via the single indirect block. */
   70         z1 = rip->i_zone[zones];        /* single indirect zone */
   71         single = TRUE;
   72   } else {
   73         /* 'position' can be located via the double indirect block. */
   74         if ( (z = rip->i_zone[zones+1]) == NO_ZONE) {
   75                 /* Create the double indirect block. */
   76                 if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE)
   77                         return(err_code);
   78                 rip->i_zone[zones+1] = z;
   79                 new_dbl = TRUE; /* set flag for later */
   80         }
   81 
   82         /* Either way, 'z' is zone number for double indirect block. */
   83         excess -= nr_indirects; /* single indirect doesn't count */
   84         ind_ex = (int) (excess / nr_indirects);
   85         excess = excess % nr_indirects;
   86         if (ind_ex >= nr_indirects) return(EFBIG);
   87         b = (block_t) z << scale;
   88         bp = get_block(rip->i_dev, b, (new_dbl ? NO_READ : NORMAL));
   89         if (new_dbl) zero_block(bp);
   90         z1 = rd_indir(bp, ind_ex);
   91         single = FALSE;
   92   }
   93 
   94   /* z1 is now single indirect zone; 'excess' is index. */
   95   if (z1 == NO_ZONE) {
   96         /* Create indirect block and store zone # in inode or dbl indir blk. */
   97         z1 = alloc_zone(rip->i_dev, rip->i_zone[0]);
   98         if (single)
   99                 rip->i_zone[zones] = z1;        /* update inode */
  100         else
  101                 wr_indir(bp, ind_ex, z1);       /* update dbl indir */
  102 
  103         new_ind = TRUE;
  104         if (bp != NIL_BUF) bp->b_dirt = DIRTY;  /* if double ind, it is dirty*/
  105         if (z1 == NO_ZONE) {
  106                 put_block(bp, INDIRECT_BLOCK);  /* release dbl indirect blk */
  107                 return(err_code);       /* couldn't create single ind */
  108         }
  109   }
  110   put_block(bp, INDIRECT_BLOCK);        /* release double indirect blk */
  111 
  112   /* z1 is indirect block's zone number. */
  113   b = (block_t) z1 << scale;
  114   bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) );
  115   if (new_ind) zero_block(bp);
  116   ex = (int) excess;                    /* we need an int here */
  117   wr_indir(bp, ex, new_zone);
  118   bp->b_dirt = DIRTY;
  119   put_block(bp, INDIRECT_BLOCK);
  120 
  121   return(OK);
  122 }
  123 
  124 /*===========================================================================*
  125  *                              wr_indir                                     *
  126  *===========================================================================*/
  127 PRIVATE void wr_indir(bp, index, zone)
  128 struct buf *bp;                 /* pointer to indirect block */
  129 int index;                      /* index into *bp */
  130 zone_t zone;                    /* zone to write */
  131 {
  132 /* Given a pointer to an indirect block, write one entry. */
  133 
  134   struct super_block *sp;
  135 
  136   sp = get_super(bp->b_dev);    /* need super block to find file sys type */
  137 
  138   /* write a zone into an indirect block */
  139   if (sp->s_version == V1)
  140         bp->b_v1_ind[index] = (zone1_t) conv2(sp->s_native, (int)  zone);
  141   else
  142         bp->b_v2_ind[index] = (zone_t)  conv4(sp->s_native, (long) zone);
  143 }
  144 
  145 /*===========================================================================*
  146  *                              clear_zone                                   *
  147  *===========================================================================*/
  148 PUBLIC void clear_zone(rip, pos, flag)
  149 register struct inode *rip;     /* inode to clear */
  150 off_t pos;                      /* points to block to clear */
  151 int flag;                       /* 0 if called by read_write, 1 by new_block */
  152 {
  153 /* Zero a zone, possibly starting in the middle.  The parameter 'pos' gives
  154  * a byte in the first block to be zeroed.  Clearzone() is called from 
  155  * read_write and new_block().
  156  */
  157 
  158   register struct buf *bp;
  159   register block_t b, blo, bhi;
  160   register off_t next;
  161   register int scale;
  162   register zone_t zone_size;
  163 
  164   /* If the block size and zone size are the same, clear_zone() not needed. */
  165   scale = rip->i_sp->s_log_zone_size;
  166   if (scale == 0) return;
  167 
  168   zone_size = (zone_t) rip->i_sp->s_block_size << scale;
  169   if (flag == 1) pos = (pos/zone_size) * zone_size;
  170   next = pos + rip->i_sp->s_block_size - 1;
  171 
  172   /* If 'pos' is in the last block of a zone, do not clear the zone. */
  173   if (next/zone_size != pos/zone_size) return;
  174   if ( (blo = read_map(rip, next)) == NO_BLOCK) return;
  175   bhi = (  ((blo>>scale)+1) << scale)   - 1;
  176 
  177   /* Clear all the blocks between 'blo' and 'bhi'. */
  178   for (b = blo; b <= bhi; b++) {
  179         bp = get_block(rip->i_dev, b, NO_READ);
  180         zero_block(bp);
  181         put_block(bp, FULL_DATA_BLOCK);
  182   }
  183 }
  184 
  185 /*===========================================================================*
  186  *                              new_block                                    *
  187  *===========================================================================*/
  188 PUBLIC struct buf *new_block(rip, position)
  189 register struct inode *rip;     /* pointer to inode */
  190 off_t position;                 /* file pointer */
  191 {
  192 /* Acquire a new block and return a pointer to it.  Doing so may require
  193  * allocating a complete zone, and then returning the initial block.
  194  * On the other hand, the current zone may still have some unused blocks.
  195  */
  196 
  197   register struct buf *bp;
  198   block_t b, base_block;
  199   zone_t z;
  200   zone_t zone_size;
  201   int scale, r;
  202   struct super_block *sp;
  203 
  204   /* Is another block available in the current zone? */
  205   if ( (b = read_map(rip, position)) == NO_BLOCK) {
  206         /* Choose first zone if possible. */
  207         /* Lose if the file is nonempty but the first zone number is NO_ZONE
  208          * corresponding to a zone full of zeros.  It would be better to
  209          * search near the last real zone.
  210          */
  211         if (rip->i_zone[0] == NO_ZONE) {
  212                 sp = rip->i_sp;
  213                 z = sp->s_firstdatazone;
  214         } else {
  215                 z = rip->i_zone[0];     /* hunt near first zone */
  216         }
  217         if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NIL_BUF);
  218         if ( (r = write_map(rip, position, z)) != OK) {
  219                 free_zone(rip->i_dev, z);
  220                 err_code = r;
  221                 return(NIL_BUF);
  222         }
  223 
  224         /* If we are not writing at EOF, clear the zone, just to be safe. */
  225         if ( position != rip->i_size) clear_zone(rip, position, 1);
  226         scale = rip->i_sp->s_log_zone_size;
  227         base_block = (block_t) z << scale;
  228         zone_size = (zone_t) rip->i_sp->s_block_size << scale;
  229         b = base_block + (block_t)((position % zone_size)/rip->i_sp->s_block_size);
  230   }
  231 
  232   bp = get_block(rip->i_dev, b, NO_READ);
  233   zero_block(bp);
  234   return(bp);
  235 }
  236 
  237 /*===========================================================================*
  238  *                              zero_block                                   *
  239  *===========================================================================*/
  240 PUBLIC void zero_block(bp)
  241 register struct buf *bp;        /* pointer to buffer to zero */
  242 {
  243 /* Zero a block. */
  244   memset(bp->b_data, 0, MAX_BLOCK_SIZE);
  245   bp->b_dirt = DIRTY;
  246 }

Cache object: 4b86f479755c57eb23b89629c16a1f36


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.