/* 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 * load_bit_maps: get the bit maps for the root or a newly mounted device * unload_bit_maps: write the bit maps back to disk after an UMOUNT * 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 * scale_factor: get the zone-to-block conversion factor for a device * rw_super: read or write a superblock */ #include "fs.h" #include <minix/boot.h> #include "buf.h" #include "inode.h" #include "super.h" #define INT_BITS (sizeof(int)<<3) #define BIT_MAP_SHIFT 13 /* (log2 of BLOCK_SIZE) + 3; 13 for 1k blocks */ /*===========================================================================* * load_bit_maps * *===========================================================================*/ PUBLIC int load_bit_maps(dev) dev_t dev; /* which device? */ { /* Load the bit map for some device into the cache and set up superblock. */ register int i; register struct super_block *sp; block_nr zbase; sp = get_super(dev); /* get the superblock pointer */ if (bufs_in_use + sp->s_imap_blocks + sp->s_zmap_blocks >= NR_BUFS - 3) return(ERROR); /* insufficient buffers left for bit maps */ if (sp->s_imap_blocks > I_MAP_SLOTS || sp->s_zmap_blocks > ZMAP_SLOTS) panic("too many map blocks", NO_NUM); /* Load the inode map from the disk. */ for (i = 0; i < sp->s_imap_blocks; i++) sp->s_imap[i] = get_block(dev, SUPER_BLOCK + 1 + i, NORMAL); /* Load the zone map from the disk. */ zbase = SUPER_BLOCK + 1 + sp->s_imap_blocks; for (i = 0; i < sp->s_zmap_blocks; i++) sp->s_zmap[i] = get_block(dev, zbase + i, NORMAL); /* inodes 0 and 1, and zone 0 are never allocated. Mark them as busy. */ sp->s_imap[0]->b_int[0] |= 3; /* inodes 0, 1 busy */ sp->s_zmap[0]->b_int[0] |= 1; /* zone 0 busy */ return(OK); } /*===========================================================================* * unload_bit_maps * *===========================================================================*/ PUBLIC int unload_bit_maps(dev) dev_t dev; /* which device is being unmounted? */ { /* Unload the bit maps so a device can be unmounted. */ register int i; register struct super_block *sp; struct super_block *get_super(); sp = get_super(dev); /* get the superblock pointer */ for (i = 0; i < sp->s_imap_blocks; i++) put_block(sp->s_imap[i], I_MAP_BLOCK); for (i = 0; i < sp->s_zmap_blocks; i++) put_block(sp->s_zmap[i], ZMAP_BLOCK); return(OK); } /*===========================================================================* * alloc_bit * *===========================================================================*/ PUBLIC bit_nr alloc_bit(map_ptr, map_bits, bit_blocks, origin) struct buf *map_ptr[]; /* pointer to array of bit block pointers */ bit_nr map_bits; /* how many bits are there in the bit map? */ unshort bit_blocks; /* how many blocks are there in the bit map? */ bit_nr origin; /* number of bit to start searching at */ { /* Allocate a bit from a bit map and return its bit number. */ register unsigned k; register int *wptr, *wlim; int i, a, b, w, o, block_count; struct buf *bp; /* Figure out where to start the bit search (depends on 'origin'). */ if (origin >= map_bits) origin = 0; /* for robustness */ b = origin >> BIT_MAP_SHIFT; o = origin - (b << BIT_MAP_SHIFT); w = o/INT_BITS; block_count = (w == 0 ? bit_blocks : bit_blocks + 1); /* The outer while loop iterates on the blocks of the map. The inner * while loop iterates on the words of a block. The for loop iterates * on the bits of a word. */ while (block_count--) { /* If need be, loop on all the blocks in the bit map. */ bp = map_ptr[b]; wptr = &bp->b_int[w]; wlim = &bp->b_int[INTS_PER_BLOCK]; while (wptr != wlim) { /* Loop on all the words of one of the bit map blocks. */ if ((k = (unsigned) *wptr) != (unsigned) ~0) { /* This word contains a free bit. Allocate it. */ for (i = 0; i < INT_BITS; i++) if (((k >> i) & 1) == 0) { a = i + (int)(wptr - &bp->b_int[0])*INT_BITS + (b << BIT_MAP_SHIFT); /* If 'a' beyond map check other blks*/ if (a >= map_bits) { wptr = wlim - 1; break; } *wptr |= 1 << i; bp->b_dirt = DIRTY; return( (bit_nr) a); } } wptr++; /* examine next word in this bit map block */ } if (++b == bit_blocks) b = 0; /* we have wrapped around */ w = 0; } return(NO_BIT); /* no bit could be allocated */ } /*===========================================================================* * free_bit * *===========================================================================*/ PUBLIC void free_bit(map_ptr, bit_returned) struct buf *map_ptr[]; /* pointer to array of bit block pointers */ bit_nr bit_returned; /* number of bit to insert into the map */ { /* Return a zone or inode by turning off its bitmap bit. */ int b, r, w, bit; struct buf *bp; b = bit_returned >> BIT_MAP_SHIFT; /* 'b' tells which block it is in */ r = bit_returned - (b << BIT_MAP_SHIFT); w = r/INT_BITS; /* 'w' tells which word it is in */ bit = r % INT_BITS; bp = map_ptr[b]; if (bp == NIL_BUF) return; if (((bp->b_int[w] >> bit)& 1)== 0) { printf("FS freeing unused block of inode. bit = %d\n",bit_returned); /*DEBUG*/ /* panic("freeing unused block or inode--check file sys",(int)bit_returned);*/ } bp->b_int[w] &= ~(1 << bit); /* turn the bit off */ bp->b_dirt = DIRTY; } /*===========================================================================* * 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; 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); } /*===========================================================================* * 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); } /*===========================================================================* * scale_factor * *===========================================================================*/ PUBLIC int scale_factor(ip) struct inode *ip; /* pointer to inode whose superblock needed */ { /* Return the scale factor used for converting blocks to zones. */ register struct super_block *sp; sp = get_super(ip->i_dev); return(sp->s_log_zone_size); } /*===========================================================================* * rw_super * *===========================================================================*/ PUBLIC void rw_super(sp, rw_flag) register struct super_block *sp; /* pointer to a superblock */ int rw_flag; /* READING or WRITING */ { /* Read or write a superblock. */ register struct buf *bp; dev_t dev; /* Check if this is a read or write, and do it. */ if (rw_flag == READING) { dev = sp->s_dev; /* save device; it will be overwritten by copy*/ bp = get_block(sp->s_dev, SUPER_BLOCK, NORMAL); copy( (char *) sp, bp->b_data, SUPER_SIZE); sp->s_dev = dev; /* restore device number */ } else { /* On a write, it is not necessary to go read superblock from disk. */ bp = get_block(sp->s_dev, SUPER_BLOCK, NO_READ); copy(bp->b_data, (char *) sp, SUPER_SIZE); bp->b_dirt = DIRTY; } sp->s_dirt = CLEAN; put_block(bp, ZUPER_BLOCK); }