super.c

Go to the documentation of this file.
00001 /* This file manages the super block table and the related data structures,
00002  * namely, the bit maps that keep track of which zones and which inodes are
00003  * allocated and which are free.  When a new inode or zone is needed, the
00004  * appropriate bit map is searched for a free entry.
00005  *
00006  * The entry points into this file are
00007  *   alloc_bit:       somebody wants to allocate a zone or inode; find one
00008  *   free_bit:        indicate that a zone or inode is available for allocation
00009  *   get_super:       search the 'superblock' table for a device
00010  *   mounted:         tells if file inode is on mounted (or ROOT) file system
00011  *   read_super:      read a superblock
00012  */
00013 
00014 #include "fs.h"
00015 #include <string.h>
00016 #include <minix/com.h>
00017 #include "buf.h"
00018 #include "inode.h"
00019 #include "super.h"
00020 #include "const.h"
00021 
00022 /*===========================================================================*
00023  *                              alloc_bit                                    *
00024  *===========================================================================*/
00025 PUBLIC bit_t alloc_bit(sp, map, origin)
00026 struct super_block *sp;         /* the filesystem to allocate from */
00027 int map;                        /* IMAP (inode map) or ZMAP (zone map) */
00028 bit_t origin;                   /* number of bit to start searching at */
00029 {
00030 /* Allocate a bit from a bit map and return its bit number. */
00031 
00032   block_t start_block;          /* first bit block */
00033   bit_t map_bits;               /* how many bits are there in the bit map? */
00034   unsigned bit_blocks;          /* how many blocks are there in the bit map? */
00035   unsigned block, word, bcount;
00036   struct buf *bp;
00037   bitchunk_t *wptr, *wlim, k;
00038   bit_t i, b;
00039 
00040   if (sp->s_rd_only)
00041         panic(__FILE__,"can't allocate bit on read-only filesys.", NO_NUM);
00042 
00043   if (map == IMAP) {
00044         start_block = START_BLOCK;
00045         map_bits = sp->s_ninodes + 1;
00046         bit_blocks = sp->s_imap_blocks;
00047   } else {
00048         start_block = START_BLOCK + sp->s_imap_blocks;
00049         map_bits = sp->s_zones - (sp->s_firstdatazone - 1);
00050         bit_blocks = sp->s_zmap_blocks;
00051   }
00052 
00053   /* Figure out where to start the bit search (depends on 'origin'). */
00054   if (origin >= map_bits) origin = 0;   /* for robustness */
00055 
00056   /* Locate the starting place. */
00057   block = origin / FS_BITS_PER_BLOCK(sp->s_block_size);
00058   word = (origin % FS_BITS_PER_BLOCK(sp->s_block_size)) / FS_BITCHUNK_BITS;
00059 
00060   /* Iterate over all blocks plus one, because we start in the middle. */
00061   bcount = bit_blocks + 1;
00062   do {
00063         bp = get_block(sp->s_dev, start_block + block, NORMAL);
00064         wlim = &bp->b_bitmap[FS_BITMAP_CHUNKS(sp->s_block_size)];
00065 
00066         /* Iterate over the words in block. */
00067         for (wptr = &bp->b_bitmap[word]; wptr < wlim; wptr++) {
00068 
00069                 /* Does this word contain a free bit? */
00070                 if (*wptr == (bitchunk_t) ~0) continue;
00071 
00072                 /* Find and allocate the free bit. */
00073                 k = conv2(sp->s_native, (int) *wptr);
00074                 for (i = 0; (k & (1 << i)) != 0; ++i) {}
00075 
00076                 /* Bit number from the start of the bit map. */
00077                 b = ((bit_t) block * FS_BITS_PER_BLOCK(sp->s_block_size))
00078                     + (wptr - &bp->b_bitmap[0]) * FS_BITCHUNK_BITS
00079                     + i;
00080 
00081                 /* Don't allocate bits beyond the end of the map. */
00082                 if (b >= map_bits) break;
00083 
00084                 /* Allocate and return bit number. */
00085                 k |= 1 << i;
00086                 *wptr = conv2(sp->s_native, (int) k);
00087                 bp->b_dirt = DIRTY;
00088                 put_block(bp, MAP_BLOCK);
00089                 return(b);
00090         }
00091         put_block(bp, MAP_BLOCK);
00092         if (++block >= bit_blocks) block = 0;   /* last block, wrap around */
00093         word = 0;
00094   } while (--bcount > 0);
00095   return(NO_BIT);               /* no bit could be allocated */
00096 }
00097 
00098 /*===========================================================================*
00099  *                              free_bit                                     *
00100  *===========================================================================*/
00101 PUBLIC void free_bit(sp, map, bit_returned)
00102 struct super_block *sp;         /* the filesystem to operate on */
00103 int map;                        /* IMAP (inode map) or ZMAP (zone map) */
00104 bit_t bit_returned;             /* number of bit to insert into the map */
00105 {
00106 /* Return a zone or inode by turning off its bitmap bit. */
00107 
00108   unsigned block, word, bit;
00109   struct buf *bp;
00110   bitchunk_t k, mask;
00111   block_t start_block;
00112 
00113   if (sp->s_rd_only)
00114         panic(__FILE__,"can't free bit on read-only filesys.", NO_NUM);
00115 
00116   if (map == IMAP) {
00117         start_block = START_BLOCK;
00118   } else {
00119         start_block = START_BLOCK + sp->s_imap_blocks;
00120   }
00121   block = bit_returned / FS_BITS_PER_BLOCK(sp->s_block_size);
00122   word = (bit_returned % FS_BITS_PER_BLOCK(sp->s_block_size))
00123          / FS_BITCHUNK_BITS;
00124 
00125   bit = bit_returned % FS_BITCHUNK_BITS;
00126   mask = 1 << bit;
00127 
00128   bp = get_block(sp->s_dev, start_block + block, NORMAL);
00129 
00130   k = conv2(sp->s_native, (int) bp->b_bitmap[word]);
00131   if (!(k & mask)) {
00132         panic(__FILE__,map == IMAP ? "tried to free unused inode" :
00133               "tried to free unused block", bit_returned);
00134   }
00135 
00136   k &= ~mask;
00137   bp->b_bitmap[word] = conv2(sp->s_native, (int) k);
00138   bp->b_dirt = DIRTY;
00139 
00140   put_block(bp, MAP_BLOCK);
00141 }
00142 
00143 /*===========================================================================*
00144  *                              get_super                                    *
00145  *===========================================================================*/
00146 PUBLIC struct super_block *get_super(dev)
00147 dev_t dev;                      /* device number whose super_block is sought */
00148 {
00149 /* Search the superblock table for this device.  It is supposed to be there. */
00150 
00151   register struct super_block *sp;
00152 
00153   if (dev == NO_DEV)
00154         panic(__FILE__,"request for super_block of NO_DEV", NO_NUM);
00155 
00156   for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
00157         if (sp->s_dev == dev) return(sp);
00158 
00159   /* Search failed.  Something wrong. */
00160   panic(__FILE__,"can't find superblock for device (in decimal)", (int) dev);
00161 
00162   return(NIL_SUPER);            /* to keep the compiler and lint quiet */
00163 }
00164 
00165 /*===========================================================================*
00166  *                              get_block_size                               *
00167  *===========================================================================*/
00168 PUBLIC int get_block_size(dev_t dev)
00169 {
00170 /* Search the superblock table for this device. */
00171 
00172   register struct super_block *sp;
00173 
00174   if (dev == NO_DEV)
00175         panic(__FILE__,"request for block size of NO_DEV", NO_NUM);
00176 
00177   for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) {
00178         if (sp->s_dev == dev) {
00179                 return(sp->s_block_size);
00180         }
00181   }
00182 
00183   /* no mounted filesystem? use this block size then. */
00184   return _MIN_BLOCK_SIZE;
00185 }
00186 
00187 /*===========================================================================*
00188  *                              mounted                                      *
00189  *===========================================================================*/
00190 PUBLIC int mounted(rip)
00191 register struct inode *rip;     /* pointer to inode */
00192 {
00193 /* Report on whether the given inode is on a mounted (or ROOT) file system. */
00194 
00195   register struct super_block *sp;
00196   register dev_t dev;
00197 
00198   dev = (dev_t) rip->i_zone[0];
00199   if (dev == root_dev) return(TRUE);    /* inode is on root file system */
00200 
00201   for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
00202         if (sp->s_dev == dev) return(TRUE);
00203 
00204   return(FALSE);
00205 }
00206 
00207 /*===========================================================================*
00208  *                              read_super                                   *
00209  *===========================================================================*/
00210 PUBLIC int read_super(sp)
00211 register struct super_block *sp; /* pointer to a superblock */
00212 {
00213 /* Read a superblock. */
00214   dev_t dev;
00215   int magic;
00216   int version, native, r;
00217   static char sbbuf[_MIN_BLOCK_SIZE];
00218 
00219   dev = sp->s_dev;              /* save device (will be overwritten by copy) */
00220   if (dev == NO_DEV)
00221         panic(__FILE__,"request for super_block of NO_DEV", NO_NUM);
00222   r = dev_io(DEV_READ, dev, FS_PROC_NR,
00223         sbbuf, SUPER_BLOCK_BYTES, _MIN_BLOCK_SIZE, 0);
00224   if (r != _MIN_BLOCK_SIZE) {
00225         return EINVAL;
00226   }
00227   memcpy(sp, sbbuf, sizeof(*sp));
00228   sp->s_dev = NO_DEV;           /* restore later */
00229   magic = sp->s_magic;          /* determines file system type */
00230 
00231   /* Get file system version and type. */
00232   if (magic == SUPER_MAGIC || magic == conv2(BYTE_SWAP, SUPER_MAGIC)) {
00233         version = V1;
00234         native  = (magic == SUPER_MAGIC);
00235   } else if (magic == SUPER_V2 || magic == conv2(BYTE_SWAP, SUPER_V2)) {
00236         version = V2;
00237         native  = (magic == SUPER_V2);
00238   } else if (magic == SUPER_V3) {
00239         version = V3;
00240         native = 1;
00241   } else {
00242         return(EINVAL);
00243   }
00244 
00245   /* If the super block has the wrong byte order, swap the fields; the magic
00246    * number doesn't need conversion. */
00247   sp->s_ninodes =       conv4(native, sp->s_ninodes);
00248   sp->s_nzones =        conv2(native, (int) sp->s_nzones);
00249   sp->s_imap_blocks =   conv2(native, (int) sp->s_imap_blocks);
00250   sp->s_zmap_blocks =   conv2(native, (int) sp->s_zmap_blocks);
00251   sp->s_firstdatazone = conv2(native, (int) sp->s_firstdatazone);
00252   sp->s_log_zone_size = conv2(native, (int) sp->s_log_zone_size);
00253   sp->s_max_size =      conv4(native, sp->s_max_size);
00254   sp->s_zones =         conv4(native, sp->s_zones);
00255 
00256   /* In V1, the device size was kept in a short, s_nzones, which limited
00257    * devices to 32K zones.  For V2, it was decided to keep the size as a
00258    * long.  However, just changing s_nzones to a long would not work, since
00259    * then the position of s_magic in the super block would not be the same
00260    * in V1 and V2 file systems, and there would be no way to tell whether
00261    * a newly mounted file system was V1 or V2.  The solution was to introduce
00262    * a new variable, s_zones, and copy the size there.
00263    *
00264    * Calculate some other numbers that depend on the version here too, to
00265    * hide some of the differences.
00266    */
00267   if (version == V1) {
00268         sp->s_block_size = _STATIC_BLOCK_SIZE;
00269         sp->s_zones = sp->s_nzones;     /* only V1 needs this copy */
00270         sp->s_inodes_per_block = V1_INODES_PER_BLOCK;
00271         sp->s_ndzones = V1_NR_DZONES;
00272         sp->s_nindirs = V1_INDIRECTS;
00273   } else {
00274         if (version == V2)
00275                 sp->s_block_size = _STATIC_BLOCK_SIZE;
00276         if (sp->s_block_size < _MIN_BLOCK_SIZE)
00277                 return EINVAL;
00278         sp->s_inodes_per_block = V2_INODES_PER_BLOCK(sp->s_block_size);
00279         sp->s_ndzones = V2_NR_DZONES;
00280         sp->s_nindirs = V2_INDIRECTS(sp->s_block_size);
00281   }
00282 
00283   if (sp->s_block_size < _MIN_BLOCK_SIZE) {
00284         return EINVAL;
00285   }
00286   if (sp->s_block_size > _MAX_BLOCK_SIZE) {
00287         printf("Filesystem block size is %d kB; maximum filesystem\n"
00288         "block size is %d kB. This limit can be increased by recompiling.\n",
00289         sp->s_block_size/1024, _MAX_BLOCK_SIZE/1024);
00290         return EINVAL;
00291   }
00292   if ((sp->s_block_size % 512) != 0) {
00293         return EINVAL;
00294   }
00295   if (SUPER_SIZE > sp->s_block_size) {
00296         return EINVAL;
00297   }
00298   if ((sp->s_block_size % V2_INODE_SIZE) != 0 ||
00299      (sp->s_block_size % V1_INODE_SIZE) != 0) {
00300         return EINVAL;
00301   }
00302 
00303   sp->s_isearch = 0;            /* inode searches initially start at 0 */
00304   sp->s_zsearch = 0;            /* zone searches initially start at 0 */
00305   sp->s_version = version;
00306   sp->s_native  = native;
00307 
00308   /* Make a few basic checks to see if super block looks reasonable. */
00309   if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1
00310                                 || sp->s_ninodes < 1 || sp->s_zones < 1
00311                                 || (unsigned) sp->s_log_zone_size > 4) {
00312         printf("not enough imap or zone map blocks, \n");
00313         printf("or not enough inodes, or not enough zones, "
00314                 "or zone size too large\n");
00315         return(EINVAL);
00316   }
00317   sp->s_dev = dev;              /* restore device number */
00318   return(OK);
00319 }

Generated on Fri Apr 14 22:57:30 2006 for minix by  doxygen 1.4.6