[9] | 1 | /* This file manages the super block table and the related data structures,
|
---|
| 2 | * namely, the bit maps that keep track of which zones and which inodes are
|
---|
| 3 | * allocated and which are free. When a new inode or zone is needed, the
|
---|
| 4 | * appropriate bit map is searched for a free entry.
|
---|
| 5 | *
|
---|
| 6 | * The entry points into this file are
|
---|
| 7 | * alloc_bit: somebody wants to allocate a zone or inode; find one
|
---|
| 8 | * free_bit: indicate that a zone or inode is available for allocation
|
---|
| 9 | * get_super: search the 'superblock' table for a device
|
---|
| 10 | * mounted: tells if file inode is on mounted (or ROOT) file system
|
---|
| 11 | * read_super: read a superblock
|
---|
| 12 | */
|
---|
| 13 |
|
---|
| 14 | #include "fs.h"
|
---|
| 15 | #include <string.h>
|
---|
| 16 | #include <minix/com.h>
|
---|
| 17 | #include "buf.h"
|
---|
| 18 | #include "inode.h"
|
---|
| 19 | #include "super.h"
|
---|
| 20 | #include "const.h"
|
---|
| 21 |
|
---|
| 22 | /*===========================================================================*
|
---|
| 23 | * alloc_bit *
|
---|
| 24 | *===========================================================================*/
|
---|
| 25 | PUBLIC bit_t alloc_bit(sp, map, origin)
|
---|
| 26 | struct super_block *sp; /* the filesystem to allocate from */
|
---|
| 27 | int map; /* IMAP (inode map) or ZMAP (zone map) */
|
---|
| 28 | bit_t origin; /* number of bit to start searching at */
|
---|
| 29 | {
|
---|
| 30 | /* Allocate a bit from a bit map and return its bit number. */
|
---|
| 31 |
|
---|
| 32 | block_t start_block; /* first bit block */
|
---|
| 33 | bit_t map_bits; /* how many bits are there in the bit map? */
|
---|
| 34 | unsigned bit_blocks; /* how many blocks are there in the bit map? */
|
---|
| 35 | unsigned block, word, bcount;
|
---|
| 36 | struct buf *bp;
|
---|
| 37 | bitchunk_t *wptr, *wlim, k;
|
---|
| 38 | bit_t i, b;
|
---|
| 39 |
|
---|
| 40 | if (sp->s_rd_only)
|
---|
| 41 | panic(__FILE__,"can't allocate bit on read-only filesys.", NO_NUM);
|
---|
| 42 |
|
---|
| 43 | if (map == IMAP) {
|
---|
| 44 | start_block = START_BLOCK;
|
---|
| 45 | map_bits = sp->s_ninodes + 1;
|
---|
| 46 | bit_blocks = sp->s_imap_blocks;
|
---|
| 47 | } else {
|
---|
| 48 | start_block = START_BLOCK + sp->s_imap_blocks;
|
---|
| 49 | map_bits = sp->s_zones - (sp->s_firstdatazone - 1);
|
---|
| 50 | bit_blocks = sp->s_zmap_blocks;
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 | /* Figure out where to start the bit search (depends on 'origin'). */
|
---|
| 54 | if (origin >= map_bits) origin = 0; /* for robustness */
|
---|
| 55 |
|
---|
| 56 | /* Locate the starting place. */
|
---|
| 57 | block = origin / FS_BITS_PER_BLOCK(sp->s_block_size);
|
---|
| 58 | word = (origin % FS_BITS_PER_BLOCK(sp->s_block_size)) / FS_BITCHUNK_BITS;
|
---|
| 59 |
|
---|
| 60 | /* Iterate over all blocks plus one, because we start in the middle. */
|
---|
| 61 | bcount = bit_blocks + 1;
|
---|
| 62 | do {
|
---|
| 63 | bp = get_block(sp->s_dev, start_block + block, NORMAL);
|
---|
| 64 | wlim = &bp->b_bitmap[FS_BITMAP_CHUNKS(sp->s_block_size)];
|
---|
| 65 |
|
---|
| 66 | /* Iterate over the words in block. */
|
---|
| 67 | for (wptr = &bp->b_bitmap[word]; wptr < wlim; wptr++) {
|
---|
| 68 |
|
---|
| 69 | /* Does this word contain a free bit? */
|
---|
| 70 | if (*wptr == (bitchunk_t) ~0) continue;
|
---|
| 71 |
|
---|
| 72 | /* Find and allocate the free bit. */
|
---|
| 73 | k = conv2(sp->s_native, (int) *wptr);
|
---|
| 74 | for (i = 0; (k & (1 << i)) != 0; ++i) {}
|
---|
| 75 |
|
---|
| 76 | /* Bit number from the start of the bit map. */
|
---|
| 77 | b = ((bit_t) block * FS_BITS_PER_BLOCK(sp->s_block_size))
|
---|
| 78 | + (wptr - &bp->b_bitmap[0]) * FS_BITCHUNK_BITS
|
---|
| 79 | + i;
|
---|
| 80 |
|
---|
| 81 | /* Don't allocate bits beyond the end of the map. */
|
---|
| 82 | if (b >= map_bits) break;
|
---|
| 83 |
|
---|
| 84 | /* Allocate and return bit number. */
|
---|
| 85 | k |= 1 << i;
|
---|
| 86 | *wptr = conv2(sp->s_native, (int) k);
|
---|
| 87 | bp->b_dirt = DIRTY;
|
---|
| 88 | put_block(bp, MAP_BLOCK);
|
---|
| 89 | return(b);
|
---|
| 90 | }
|
---|
| 91 | put_block(bp, MAP_BLOCK);
|
---|
| 92 | if (++block >= bit_blocks) block = 0; /* last block, wrap around */
|
---|
| 93 | word = 0;
|
---|
| 94 | } while (--bcount > 0);
|
---|
| 95 | return(NO_BIT); /* no bit could be allocated */
|
---|
| 96 | }
|
---|
| 97 |
|
---|
| 98 | /*===========================================================================*
|
---|
| 99 | * free_bit *
|
---|
| 100 | *===========================================================================*/
|
---|
| 101 | PUBLIC void free_bit(sp, map, bit_returned)
|
---|
| 102 | struct super_block *sp; /* the filesystem to operate on */
|
---|
| 103 | int map; /* IMAP (inode map) or ZMAP (zone map) */
|
---|
| 104 | bit_t bit_returned; /* number of bit to insert into the map */
|
---|
| 105 | {
|
---|
| 106 | /* Return a zone or inode by turning off its bitmap bit. */
|
---|
| 107 |
|
---|
| 108 | unsigned block, word, bit;
|
---|
| 109 | struct buf *bp;
|
---|
| 110 | bitchunk_t k, mask;
|
---|
| 111 | block_t start_block;
|
---|
| 112 |
|
---|
| 113 | if (sp->s_rd_only)
|
---|
| 114 | panic(__FILE__,"can't free bit on read-only filesys.", NO_NUM);
|
---|
| 115 |
|
---|
| 116 | if (map == IMAP) {
|
---|
| 117 | start_block = START_BLOCK;
|
---|
| 118 | } else {
|
---|
| 119 | start_block = START_BLOCK + sp->s_imap_blocks;
|
---|
| 120 | }
|
---|
| 121 | block = bit_returned / FS_BITS_PER_BLOCK(sp->s_block_size);
|
---|
| 122 | word = (bit_returned % FS_BITS_PER_BLOCK(sp->s_block_size))
|
---|
| 123 | / FS_BITCHUNK_BITS;
|
---|
| 124 |
|
---|
| 125 | bit = bit_returned % FS_BITCHUNK_BITS;
|
---|
| 126 | mask = 1 << bit;
|
---|
| 127 |
|
---|
| 128 | bp = get_block(sp->s_dev, start_block + block, NORMAL);
|
---|
| 129 |
|
---|
| 130 | k = conv2(sp->s_native, (int) bp->b_bitmap[word]);
|
---|
| 131 | if (!(k & mask)) {
|
---|
| 132 | panic(__FILE__,map == IMAP ? "tried to free unused inode" :
|
---|
| 133 | "tried to free unused block", bit_returned);
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | k &= ~mask;
|
---|
| 137 | bp->b_bitmap[word] = conv2(sp->s_native, (int) k);
|
---|
| 138 | bp->b_dirt = DIRTY;
|
---|
| 139 |
|
---|
| 140 | put_block(bp, MAP_BLOCK);
|
---|
| 141 | }
|
---|
| 142 |
|
---|
| 143 | /*===========================================================================*
|
---|
| 144 | * get_super *
|
---|
| 145 | *===========================================================================*/
|
---|
| 146 | PUBLIC struct super_block *get_super(dev)
|
---|
| 147 | dev_t dev; /* device number whose super_block is sought */
|
---|
| 148 | {
|
---|
| 149 | /* Search the superblock table for this device. It is supposed to be there. */
|
---|
| 150 |
|
---|
| 151 | register struct super_block *sp;
|
---|
| 152 |
|
---|
| 153 | if (dev == NO_DEV)
|
---|
| 154 | panic(__FILE__,"request for super_block of NO_DEV", NO_NUM);
|
---|
| 155 |
|
---|
| 156 | for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
|
---|
| 157 | if (sp->s_dev == dev) return(sp);
|
---|
| 158 |
|
---|
| 159 | /* Search failed. Something wrong. */
|
---|
| 160 | panic(__FILE__,"can't find superblock for device (in decimal)", (int) dev);
|
---|
| 161 |
|
---|
| 162 | return(NIL_SUPER); /* to keep the compiler and lint quiet */
|
---|
| 163 | }
|
---|
| 164 |
|
---|
| 165 | /*===========================================================================*
|
---|
| 166 | * get_block_size *
|
---|
| 167 | *===========================================================================*/
|
---|
| 168 | PUBLIC int get_block_size(dev_t dev)
|
---|
| 169 | {
|
---|
| 170 | /* Search the superblock table for this device. */
|
---|
| 171 |
|
---|
| 172 | register struct super_block *sp;
|
---|
| 173 |
|
---|
| 174 | if (dev == NO_DEV)
|
---|
| 175 | panic(__FILE__,"request for block size of NO_DEV", NO_NUM);
|
---|
| 176 |
|
---|
| 177 | for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) {
|
---|
| 178 | if (sp->s_dev == dev) {
|
---|
| 179 | return(sp->s_block_size);
|
---|
| 180 | }
|
---|
| 181 | }
|
---|
| 182 |
|
---|
| 183 | /* no mounted filesystem? use this block size then. */
|
---|
| 184 | return _MIN_BLOCK_SIZE;
|
---|
| 185 | }
|
---|
| 186 |
|
---|
| 187 | /*===========================================================================*
|
---|
| 188 | * mounted *
|
---|
| 189 | *===========================================================================*/
|
---|
| 190 | PUBLIC int mounted(rip)
|
---|
| 191 | register struct inode *rip; /* pointer to inode */
|
---|
| 192 | {
|
---|
| 193 | /* Report on whether the given inode is on a mounted (or ROOT) file system. */
|
---|
| 194 |
|
---|
| 195 | register struct super_block *sp;
|
---|
| 196 | register dev_t dev;
|
---|
| 197 |
|
---|
| 198 | dev = (dev_t) rip->i_zone[0];
|
---|
| 199 | if (dev == root_dev) return(TRUE); /* inode is on root file system */
|
---|
| 200 |
|
---|
| 201 | for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
|
---|
| 202 | if (sp->s_dev == dev) return(TRUE);
|
---|
| 203 |
|
---|
| 204 | return(FALSE);
|
---|
| 205 | }
|
---|
| 206 |
|
---|
| 207 | /*===========================================================================*
|
---|
| 208 | * read_super *
|
---|
| 209 | *===========================================================================*/
|
---|
| 210 | PUBLIC int read_super(sp)
|
---|
| 211 | register struct super_block *sp; /* pointer to a superblock */
|
---|
| 212 | {
|
---|
| 213 | /* Read a superblock. */
|
---|
| 214 | dev_t dev;
|
---|
| 215 | int magic;
|
---|
| 216 | int version, native, r;
|
---|
| 217 | static char sbbuf[_MIN_BLOCK_SIZE];
|
---|
| 218 |
|
---|
| 219 | dev = sp->s_dev; /* save device (will be overwritten by copy) */
|
---|
| 220 | if (dev == NO_DEV)
|
---|
| 221 | panic(__FILE__,"request for super_block of NO_DEV", NO_NUM);
|
---|
| 222 | r = dev_io(DEV_READ, dev, FS_PROC_NR,
|
---|
| 223 | sbbuf, SUPER_BLOCK_BYTES, _MIN_BLOCK_SIZE, 0);
|
---|
| 224 | if (r != _MIN_BLOCK_SIZE) {
|
---|
| 225 | return EINVAL;
|
---|
| 226 | }
|
---|
| 227 | memcpy(sp, sbbuf, sizeof(*sp));
|
---|
| 228 | sp->s_dev = NO_DEV; /* restore later */
|
---|
| 229 | magic = sp->s_magic; /* determines file system type */
|
---|
| 230 |
|
---|
| 231 | /* Get file system version and type. */
|
---|
| 232 | if (magic == SUPER_MAGIC || magic == conv2(BYTE_SWAP, SUPER_MAGIC)) {
|
---|
| 233 | version = V1;
|
---|
| 234 | native = (magic == SUPER_MAGIC);
|
---|
| 235 | } else if (magic == SUPER_V2 || magic == conv2(BYTE_SWAP, SUPER_V2)) {
|
---|
| 236 | version = V2;
|
---|
| 237 | native = (magic == SUPER_V2);
|
---|
| 238 | } else if (magic == SUPER_V3) {
|
---|
| 239 | version = V3;
|
---|
| 240 | native = 1;
|
---|
| 241 | } else {
|
---|
| 242 | return(EINVAL);
|
---|
| 243 | }
|
---|
| 244 |
|
---|
| 245 | /* If the super block has the wrong byte order, swap the fields; the magic
|
---|
| 246 | * number doesn't need conversion. */
|
---|
| 247 | sp->s_ninodes = conv4(native, sp->s_ninodes);
|
---|
| 248 | sp->s_nzones = conv2(native, (int) sp->s_nzones);
|
---|
| 249 | sp->s_imap_blocks = conv2(native, (int) sp->s_imap_blocks);
|
---|
| 250 | sp->s_zmap_blocks = conv2(native, (int) sp->s_zmap_blocks);
|
---|
| 251 | sp->s_firstdatazone = conv2(native, (int) sp->s_firstdatazone);
|
---|
| 252 | sp->s_log_zone_size = conv2(native, (int) sp->s_log_zone_size);
|
---|
| 253 | sp->s_max_size = conv4(native, sp->s_max_size);
|
---|
| 254 | sp->s_zones = conv4(native, sp->s_zones);
|
---|
| 255 |
|
---|
| 256 | /* In V1, the device size was kept in a short, s_nzones, which limited
|
---|
| 257 | * devices to 32K zones. For V2, it was decided to keep the size as a
|
---|
| 258 | * long. However, just changing s_nzones to a long would not work, since
|
---|
| 259 | * then the position of s_magic in the super block would not be the same
|
---|
| 260 | * in V1 and V2 file systems, and there would be no way to tell whether
|
---|
| 261 | * a newly mounted file system was V1 or V2. The solution was to introduce
|
---|
| 262 | * a new variable, s_zones, and copy the size there.
|
---|
| 263 | *
|
---|
| 264 | * Calculate some other numbers that depend on the version here too, to
|
---|
| 265 | * hide some of the differences.
|
---|
| 266 | */
|
---|
| 267 | if (version == V1) {
|
---|
| 268 | sp->s_block_size = _STATIC_BLOCK_SIZE;
|
---|
| 269 | sp->s_zones = sp->s_nzones; /* only V1 needs this copy */
|
---|
| 270 | sp->s_inodes_per_block = V1_INODES_PER_BLOCK;
|
---|
| 271 | sp->s_ndzones = V1_NR_DZONES;
|
---|
| 272 | sp->s_nindirs = V1_INDIRECTS;
|
---|
| 273 | } else {
|
---|
| 274 | if (version == V2)
|
---|
| 275 | sp->s_block_size = _STATIC_BLOCK_SIZE;
|
---|
| 276 | if (sp->s_block_size < _MIN_BLOCK_SIZE)
|
---|
| 277 | return EINVAL;
|
---|
| 278 | sp->s_inodes_per_block = V2_INODES_PER_BLOCK(sp->s_block_size);
|
---|
| 279 | sp->s_ndzones = V2_NR_DZONES;
|
---|
| 280 | sp->s_nindirs = V2_INDIRECTS(sp->s_block_size);
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | if (sp->s_block_size < _MIN_BLOCK_SIZE) {
|
---|
| 284 | return EINVAL;
|
---|
| 285 | }
|
---|
| 286 | if (sp->s_block_size > _MAX_BLOCK_SIZE) {
|
---|
| 287 | printf("Filesystem block size is %d kB; maximum filesystem\n"
|
---|
| 288 | "block size is %d kB. This limit can be increased by recompiling.\n",
|
---|
| 289 | sp->s_block_size/1024, _MAX_BLOCK_SIZE/1024);
|
---|
| 290 | return EINVAL;
|
---|
| 291 | }
|
---|
| 292 | if ((sp->s_block_size % 512) != 0) {
|
---|
| 293 | return EINVAL;
|
---|
| 294 | }
|
---|
| 295 | if (SUPER_SIZE > sp->s_block_size) {
|
---|
| 296 | return EINVAL;
|
---|
| 297 | }
|
---|
| 298 | if ((sp->s_block_size % V2_INODE_SIZE) != 0 ||
|
---|
| 299 | (sp->s_block_size % V1_INODE_SIZE) != 0) {
|
---|
| 300 | return EINVAL;
|
---|
| 301 | }
|
---|
| 302 |
|
---|
| 303 | sp->s_isearch = 0; /* inode searches initially start at 0 */
|
---|
| 304 | sp->s_zsearch = 0; /* zone searches initially start at 0 */
|
---|
| 305 | sp->s_version = version;
|
---|
| 306 | sp->s_native = native;
|
---|
| 307 |
|
---|
| 308 | /* Make a few basic checks to see if super block looks reasonable. */
|
---|
| 309 | if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1
|
---|
| 310 | || sp->s_ninodes < 1 || sp->s_zones < 1
|
---|
| 311 | || (unsigned) sp->s_log_zone_size > 4) {
|
---|
| 312 | printf("not enough imap or zone map blocks, \n");
|
---|
| 313 | printf("or not enough inodes, or not enough zones, "
|
---|
| 314 | "or zone size too large\n");
|
---|
| 315 | return(EINVAL);
|
---|
| 316 | }
|
---|
| 317 | sp->s_dev = dev; /* restore device number */
|
---|
| 318 | return(OK);
|
---|
| 319 | }
|
---|