323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* This file manages the super block table and the related data structures,
 | |
|  * namely, the bit maps that keep track of which zones and which inodes are
 | |
|  * allocated and which are free.  When a new inode or zone is needed, the
 | |
|  * appropriate bit map is searched for a free entry.
 | |
|  *
 | |
|  * The entry points into this file are
 | |
|  *   alloc_bit:       somebody wants to allocate a zone or inode; find one
 | |
|  *   free_bit:        indicate that a zone or inode is available for allocation
 | |
|  *   get_super:       search the 'superblock' table for a device
 | |
|  *   mounted:         tells if file inode is on mounted (or ROOT) file system
 | |
|  *   read_super:      read a superblock
 | |
|  */
 | |
| 
 | |
| #include "fs.h"
 | |
| #include <string.h>
 | |
| #include <minix/com.h>
 | |
| #include "buf.h"
 | |
| #include "inode.h"
 | |
| #include "super.h"
 | |
| #include "const.h"
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				alloc_bit				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC bit_t alloc_bit(sp, map, origin)
 | |
| struct super_block *sp;		/* the filesystem to allocate from */
 | |
| int map;			/* IMAP (inode map) or ZMAP (zone map) */
 | |
| bit_t origin;			/* number of bit to start searching at */
 | |
| {
 | |
| /* Allocate a bit from a bit map and return its bit number. */
 | |
| 
 | |
|   block_t start_block;		/* first bit block */
 | |
|   bit_t map_bits;		/* how many bits are there in the bit map? */
 | |
|   unsigned bit_blocks;		/* how many blocks are there in the bit map? */
 | |
|   unsigned block, word, bcount, wcount;
 | |
|   struct buf *bp;
 | |
|   bitchunk_t *wptr, *wlim, k;
 | |
|   bit_t i, b;
 | |
| 
 | |
|   if (sp->s_rd_only)
 | |
| 	panic("can't allocate bit on read-only filesys.", NO_NUM);
 | |
| 
 | |
|   if (map == IMAP) {
 | |
| 	start_block = START_BLOCK;
 | |
| 	map_bits = sp->s_ninodes + 1;
 | |
| 	bit_blocks = sp->s_imap_blocks;
 | |
|   } else {
 | |
| 	start_block = START_BLOCK + sp->s_imap_blocks;
 | |
| 	map_bits = sp->s_zones - (sp->s_firstdatazone - 1);
 | |
| 	bit_blocks = sp->s_zmap_blocks;
 | |
|   }
 | |
| 
 | |
|   /* Figure out where to start the bit search (depends on 'origin'). */
 | |
|   if (origin >= map_bits) origin = 0;	/* for robustness */
 | |
| 
 | |
|   /* Locate the starting place. */
 | |
|   block = origin / BITS_PER_BLOCK(sp->s_block_size);
 | |
|   word = (origin % BITS_PER_BLOCK(sp->s_block_size)) / BITCHUNK_BITS;
 | |
| 
 | |
|   /* Iterate over all blocks plus one, because we start in the middle. */
 | |
|   bcount = bit_blocks + 1;
 | |
|   do {
 | |
| 	bp = get_block(sp->s_dev, start_block + block, NORMAL);
 | |
| 	wlim = &bp->b_bitmap[BITMAP_CHUNKS(sp->s_block_size)];
 | |
| 
 | |
| 	/* Iterate over the words in block. */
 | |
| 	for (wptr = &bp->b_bitmap[word]; wptr < wlim; wptr++) {
 | |
| 
 | |
| 		/* Does this word contain a free bit? */
 | |
| 		if (*wptr == (bitchunk_t) ~0) continue;
 | |
| 
 | |
| 		/* Find and allocate the free bit. */
 | |
| 		k = conv2(sp->s_native, (int) *wptr);
 | |
| 		for (i = 0; (k & (1 << i)) != 0; ++i) {}
 | |
| 
 | |
| 		/* Bit number from the start of the bit map. */
 | |
| 		b = ((bit_t) block * BITS_PER_BLOCK(sp->s_block_size))
 | |
| 		    + (wptr - &bp->b_bitmap[0]) * BITCHUNK_BITS
 | |
| 		    + i;
 | |
| 
 | |
| 		/* Don't allocate bits beyond the end of the map. */
 | |
| 		if (b >= map_bits) break;
 | |
| 
 | |
| 		/* Allocate and return bit number. */
 | |
| 		k |= 1 << i;
 | |
| 		*wptr = conv2(sp->s_native, (int) k);
 | |
| 		bp->b_dirt = DIRTY;
 | |
| 		put_block(bp, MAP_BLOCK);
 | |
| 		return(b);
 | |
| 	}
 | |
| 	put_block(bp, MAP_BLOCK);
 | |
| 	if (++block >= bit_blocks) block = 0;	/* last block, wrap around */
 | |
| 	word = 0;
 | |
|   } while (--bcount > 0);
 | |
|   return(NO_BIT);		/* no bit could be allocated */
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				free_bit				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC void free_bit(sp, map, bit_returned)
 | |
| struct super_block *sp;		/* the filesystem to operate on */
 | |
| int map;			/* IMAP (inode map) or ZMAP (zone map) */
 | |
| bit_t bit_returned;		/* number of bit to insert into the map */
 | |
| {
 | |
| /* Return a zone or inode by turning off its bitmap bit. */
 | |
| 
 | |
|   unsigned block, word, bit;
 | |
|   struct buf *bp;
 | |
|   bitchunk_t k, mask;
 | |
|   block_t start_block;
 | |
| 
 | |
|   if (sp->s_rd_only)
 | |
| 	panic("can't free bit on read-only filesys.", NO_NUM);
 | |
| 
 | |
|   if (map == IMAP) {
 | |
| 	start_block = START_BLOCK;
 | |
|   } else {
 | |
| 	start_block = START_BLOCK + sp->s_imap_blocks;
 | |
|   }
 | |
|   block = bit_returned / BITS_PER_BLOCK(sp->s_block_size);
 | |
|   word = (bit_returned % BITS_PER_BLOCK(sp->s_block_size)) / BITCHUNK_BITS;
 | |
|   bit = bit_returned % BITCHUNK_BITS;
 | |
|   mask = 1 << bit;
 | |
| 
 | |
|   bp = get_block(sp->s_dev, start_block + block, NORMAL);
 | |
| 
 | |
|   k = conv2(sp->s_native, (int) bp->b_bitmap[word]);
 | |
|   if (!(k & mask)) {
 | |
| 	panic(map == IMAP ? "tried to free unused inode" :
 | |
| 	      "tried to free unused block", NO_NUM);
 | |
|   }
 | |
| 
 | |
|   k &= ~mask;
 | |
|   bp->b_bitmap[word] = conv2(sp->s_native, (int) k);
 | |
|   bp->b_dirt = DIRTY;
 | |
| 
 | |
|   put_block(bp, MAP_BLOCK);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				get_super				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC struct super_block *get_super(dev)
 | |
| dev_t dev;			/* device number whose super_block is sought */
 | |
| {
 | |
| /* Search the superblock table for this device.  It is supposed to be there. */
 | |
| 
 | |
|   register struct super_block *sp;
 | |
| 
 | |
|   if(dev == NO_DEV)
 | |
|   	panic("request for super_block of NO_DEV in get_super()", NO_NUM);
 | |
| 
 | |
|   for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
 | |
| 	if (sp->s_dev == dev) return(sp);
 | |
| 
 | |
|   /* Search failed.  Something wrong. */
 | |
|   panic("can't find superblock for device (in decimal)", (int) dev);
 | |
| 
 | |
|   return(NIL_SUPER);		/* to keep the compiler and lint quiet */
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				get_block_size				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int get_block_size(dev_t dev)
 | |
| {
 | |
| /* Search the superblock table for this device. */
 | |
| 
 | |
|   register struct super_block *sp;
 | |
| 
 | |
|   if(dev == NO_DEV)
 | |
|   	panic("request for block size of NO_DEV in get_block_size()", NO_NUM);
 | |
| 
 | |
|   for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
 | |
| 	if (sp->s_dev == dev)
 | |
| 		return(sp->s_block_size);
 | |
| 
 | |
|   /* no mounted filesystem? use this block size then. */
 | |
|   return MIN_BLOCK_SIZE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				mounted					     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int mounted(rip)
 | |
| register struct inode *rip;	/* pointer to inode */
 | |
| {
 | |
| /* Report on whether the given inode is on a mounted (or ROOT) file system. */
 | |
| 
 | |
|   register struct super_block *sp;
 | |
|   register dev_t dev;
 | |
| 
 | |
|   dev = (dev_t) rip->i_zone[0];
 | |
|   if (dev == root_dev) return(TRUE);	/* inode is on root file system */
 | |
| 
 | |
|   for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
 | |
| 	if (sp->s_dev == dev) return(TRUE);
 | |
| 
 | |
|   return(FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				read_super				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int read_super(sp)
 | |
| register struct super_block *sp; /* pointer to a superblock */
 | |
| {
 | |
| /* Read a superblock. */
 | |
|   dev_t dev;
 | |
|   int magic;
 | |
|   int version, native, sb_block, r;
 | |
|   off_t sb_bytes_offset, sb_io_offset;
 | |
|   static char sbbuf[MIN_BLOCK_SIZE];
 | |
| 
 | |
|   dev = sp->s_dev;		/* save device (will be overwritten by copy) */
 | |
|   if(dev == NO_DEV)
 | |
|   	panic("request for super_block of NO_DEV in read_super()", NO_NUM);
 | |
|   r = dev_io(DEV_READ, dev, FS_PROC_NR,
 | |
|   	sbbuf, SUPER_BLOCK_BYTES, MIN_BLOCK_SIZE, 0);
 | |
|   if(r != MIN_BLOCK_SIZE) {
 | |
|   	printf("dev_io failed for super block (%d)\n", r);
 | |
|   	return EINVAL;
 | |
|   }
 | |
|   memcpy(sp, sbbuf, sizeof(*sp));
 | |
|   sp->s_dev = NO_DEV;		/* restore later */
 | |
|   magic = sp->s_magic;		/* determines file system type */
 | |
| 
 | |
|   /* Get file system version and type. */
 | |
|   if (magic == SUPER_MAGIC || magic == conv2(BYTE_SWAP, SUPER_MAGIC)) {
 | |
| 	version = V1;
 | |
| 	native  = (magic == SUPER_MAGIC);
 | |
|   } else if (magic == SUPER_V2 || magic == conv2(BYTE_SWAP, SUPER_V2)) {
 | |
| 	version = V2;
 | |
| 	native  = (magic == SUPER_V2);
 | |
|   } else if (magic == SUPER_V3) {
 | |
| 	version = V3;
 | |
|   	native = 1;
 | |
|   } else {
 | |
|   	printf("unrecognized magic number\n");
 | |
| 	return(EINVAL);
 | |
|   }
 | |
| 
 | |
|   /* If the super block has the wrong byte order, swap the fields; the magic
 | |
|    * number doesn't need conversion. */
 | |
|   sp->s_ninodes =       conv4(native, sp->s_ninodes);
 | |
|   sp->s_nzones =        conv2(native, (int) sp->s_nzones);
 | |
|   sp->s_imap_blocks =   conv2(native, (int) sp->s_imap_blocks);
 | |
|   sp->s_zmap_blocks =   conv2(native, (int) sp->s_zmap_blocks);
 | |
|   sp->s_firstdatazone = conv2(native, (int) sp->s_firstdatazone);
 | |
|   sp->s_log_zone_size = conv2(native, (int) sp->s_log_zone_size);
 | |
|   sp->s_max_size =      conv4(native, sp->s_max_size);
 | |
|   sp->s_zones =         conv4(native, sp->s_zones);
 | |
| 
 | |
|   /* In V1, the device size was kept in a short, s_nzones, which limited
 | |
|    * devices to 32K zones.  For V2, it was decided to keep the size as a
 | |
|    * long.  However, just changing s_nzones to a long would not work, since
 | |
|    * then the position of s_magic in the super block would not be the same
 | |
|    * in V1 and V2 file systems, and there would be no way to tell whether
 | |
|    * a newly mounted file system was V1 or V2.  The solution was to introduce
 | |
|    * a new variable, s_zones, and copy the size there.
 | |
|    *
 | |
|    * Calculate some other numbers that depend on the version here too, to
 | |
|    * hide some of the differences.
 | |
|    */
 | |
|   if (version == V1) {
 | |
|   	sp->s_block_size = STATIC_BLOCK_SIZE;
 | |
| 	sp->s_zones = sp->s_nzones;	/* only V1 needs this copy */
 | |
| 	sp->s_inodes_per_block = V1_INODES_PER_BLOCK;
 | |
| 	sp->s_ndzones = V1_NR_DZONES;
 | |
| 	sp->s_nindirs = V1_INDIRECTS;
 | |
|   } else {
 | |
|   	if(version == V2)
 | |
|   		sp->s_block_size = STATIC_BLOCK_SIZE;
 | |
|   	if(sp->s_block_size < MIN_BLOCK_SIZE)
 | |
|   		return EINVAL;
 | |
| 	sp->s_inodes_per_block = V2_INODES_PER_BLOCK(sp->s_block_size);
 | |
| 	sp->s_ndzones = V2_NR_DZONES;
 | |
| 	sp->s_nindirs = V2_INDIRECTS(sp->s_block_size);
 | |
|   }
 | |
| 
 | |
|   if(sp->s_block_size < MIN_BLOCK_SIZE || sp->s_block_size > MAX_BLOCK_SIZE) {
 | |
|   	printf("block size (%d) out of range\n", sp->s_block_size);
 | |
|   	return EINVAL;
 | |
|   }
 | |
|   if((sp->s_block_size % 512) != 0) {
 | |
|   	printf("block size (%d) not multiple of sector size\n",
 | |
|   		sp->s_block_size);
 | |
|   	return EINVAL;
 | |
|   }
 | |
|   if(SUPER_SIZE > sp->s_block_size) {
 | |
|   	printf("super block size size (%d) larger than block size (%d)\n",
 | |
|   		SUPER_SIZE, sp->s_block_size);
 | |
|   	return EINVAL;
 | |
|   }
 | |
|   if((sp->s_block_size % V2_INODE_SIZE) != 0 ||
 | |
|      (sp->s_block_size % V1_INODE_SIZE) != 0) {
 | |
|   	printf("super block size size (%d) not multiple of inode size\n",
 | |
|   		sp->s_block_size);
 | |
|   	return EINVAL;
 | |
|   }
 | |
| 
 | |
|   sp->s_isearch = 0;		/* inode searches initially start at 0 */
 | |
|   sp->s_zsearch = 0;		/* zone searches initially start at 0 */
 | |
|   sp->s_version = version;
 | |
|   sp->s_native  = native;
 | |
| 
 | |
|   /* Make a few basic checks to see if super block looks reasonable. */
 | |
|   if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1
 | |
| 				|| sp->s_ninodes < 1 || sp->s_zones < 1
 | |
| 				|| (unsigned) sp->s_log_zone_size > 4) {
 | |
|   	printf("not enough imap or zone map blocks, \n");
 | |
|   	printf("or not enough inodes, or not enough zones, or zone size too large\n");
 | |
| 	return(EINVAL);
 | |
|   }
 | |
|   sp->s_dev = dev;		/* restore device number */
 | |
|   return(OK);
 | |
| }
 | 
