xref: /minix/minix/fs/mfs/super.c (revision fb9c64b2)
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  *   mounted:         tells if file inode is on mounted (or ROOT) file system
10  *   read_super:      read a superblock
11  */
12 
13 #include "fs.h"
14 #include <string.h>
15 #include <assert.h>
16 #include <minix/com.h>
17 #include <minix/u64.h>
18 #include <minix/bdev.h>
19 #include <machine/param.h>
20 #include <machine/vmparam.h>
21 #include "buf.h"
22 #include "inode.h"
23 #include "super.h"
24 #include "const.h"
25 
26 /*===========================================================================*
27  *				alloc_bit				     *
28  *===========================================================================*/
29 bit_t alloc_bit(sp, map, origin)
30 struct super_block *sp;		/* the filesystem to allocate from */
31 int map;			/* IMAP (inode map) or ZMAP (zone map) */
32 bit_t origin;			/* number of bit to start searching at */
33 {
34 /* Allocate a bit from a bit map and return its bit number. */
35 
36   block_t start_block;		/* first bit block */
37   block_t block;
38   bit_t map_bits;		/* how many bits are there in the bit map? */
39   short bit_blocks;		/* how many blocks are there in the bit map? */
40   unsigned word, bcount;
41   struct buf *bp;
42   bitchunk_t *wptr, *wlim, k;
43   bit_t i, b;
44 
45   if (sp->s_rd_only)
46 	panic("can't allocate bit on read-only filesys");
47 
48   if (map == IMAP) {
49 	start_block = START_BLOCK;
50 	map_bits = (bit_t) (sp->s_ninodes + 1);
51 	bit_blocks = sp->s_imap_blocks;
52   } else {
53 	start_block = START_BLOCK + sp->s_imap_blocks;
54 	map_bits = (bit_t) (sp->s_zones - (sp->s_firstdatazone - 1));
55 	bit_blocks = sp->s_zmap_blocks;
56   }
57 
58   /* Figure out where to start the bit search (depends on 'origin'). */
59   if (origin >= map_bits) origin = 0;	/* for robustness */
60 
61   /* Locate the starting place. */
62   block = (block_t) (origin / FS_BITS_PER_BLOCK(sp->s_block_size));
63   word = (origin % FS_BITS_PER_BLOCK(sp->s_block_size)) / FS_BITCHUNK_BITS;
64 
65   /* Iterate over all blocks plus one, because we start in the middle. */
66   bcount = bit_blocks + 1;
67   do {
68 	bp = get_block(sp->s_dev, start_block + block, NORMAL);
69 	wlim = &b_bitmap(bp)[FS_BITMAP_CHUNKS(sp->s_block_size)];
70 
71 	/* Iterate over the words in block. */
72 	for (wptr = &b_bitmap(bp)[word]; wptr < wlim; wptr++) {
73 
74 		/* Does this word contain a free bit? */
75 		if (*wptr == (bitchunk_t) ~0) continue;
76 
77 		/* Find and allocate the free bit. */
78 		k = (bitchunk_t) conv4(sp->s_native, (int) *wptr);
79 		for (i = 0; (k & (1 << i)) != 0; ++i) {}
80 
81 		/* Bit number from the start of the bit map. */
82 		b = ((bit_t) block * FS_BITS_PER_BLOCK(sp->s_block_size))
83 		    + (wptr - &b_bitmap(bp)[0]) * FS_BITCHUNK_BITS
84 		    + i;
85 
86 		/* Don't allocate bits beyond the end of the map. */
87 		if (b >= map_bits) break;
88 
89 		/* Allocate and return bit number. */
90 		k |= 1 << i;
91 		*wptr = (bitchunk_t) conv4(sp->s_native, (int) k);
92 		MARKDIRTY(bp);
93 		put_block(bp);
94 		if(map == ZMAP) {
95 			used_zones++;
96 			lmfs_change_blockusage(1);
97 		}
98 		return(b);
99 	}
100 	put_block(bp);
101 	if (++block >= (unsigned int) bit_blocks) /* last block, wrap around */
102 		block = 0;
103 	word = 0;
104   } while (--bcount > 0);
105   return(NO_BIT);		/* no bit could be allocated */
106 }
107 
108 /*===========================================================================*
109  *				free_bit				     *
110  *===========================================================================*/
111 void free_bit(sp, map, bit_returned)
112 struct super_block *sp;		/* the filesystem to operate on */
113 int map;			/* IMAP (inode map) or ZMAP (zone map) */
114 bit_t bit_returned;		/* number of bit to insert into the map */
115 {
116 /* Return a zone or inode by turning off its bitmap bit. */
117 
118   unsigned block, word, bit;
119   struct buf *bp;
120   bitchunk_t k, mask;
121   block_t start_block;
122 
123   if (sp->s_rd_only)
124 	panic("can't free bit on read-only filesys");
125 
126   if (map == IMAP) {
127 	start_block = START_BLOCK;
128   } else {
129 	start_block = START_BLOCK + sp->s_imap_blocks;
130   }
131   block = bit_returned / FS_BITS_PER_BLOCK(sp->s_block_size);
132   word = (bit_returned % FS_BITS_PER_BLOCK(sp->s_block_size))
133   	 / FS_BITCHUNK_BITS;
134 
135   bit = bit_returned % FS_BITCHUNK_BITS;
136   mask = 1 << bit;
137 
138   bp = get_block(sp->s_dev, start_block + block, NORMAL);
139 
140   k = (bitchunk_t) conv4(sp->s_native, (int) b_bitmap(bp)[word]);
141   if (!(k & mask)) {
142   	if (map == IMAP) panic("tried to free unused inode");
143   	else panic("tried to free unused block: %u", bit_returned);
144   }
145 
146   k &= ~mask;
147   b_bitmap(bp)[word] = (bitchunk_t) conv4(sp->s_native, (int) k);
148   MARKDIRTY(bp);
149 
150   put_block(bp);
151 
152   if(map == ZMAP) {
153 	used_zones--;
154 	lmfs_change_blockusage(-1);
155   }
156 }
157 
158 /*===========================================================================*
159  *				get_block_size				     *
160  *===========================================================================*/
161 unsigned int get_block_size(dev_t dev)
162 {
163   if (dev == NO_DEV)
164   	panic("request for block size of NO_DEV");
165 
166   return(lmfs_fs_block_size());
167 }
168 
169 
170 /*===========================================================================*
171  *				rw_super				     *
172  *===========================================================================*/
173 static int rw_super(struct super_block *sp, int writing)
174 {
175 /* Read/write a superblock. */
176   dev_t save_dev = sp->s_dev;
177   struct buf *bp;
178   char *sbbuf;
179   int r;
180 
181 /* To keep the 1kb on disk clean, only read/write up to and including
182  * this field.
183  */
184 #define LAST_ONDISK_FIELD s_disk_version
185   int ondisk_bytes = (int) ((char *) &sp->LAST_ONDISK_FIELD - (char *) sp)
186   	+ sizeof(sp->LAST_ONDISK_FIELD);
187 
188   assert(ondisk_bytes > 0);
189   assert(ondisk_bytes < PAGE_SIZE);
190   assert(ondisk_bytes < sizeof(struct super_block));
191 
192   if (sp->s_dev == NO_DEV)
193   	panic("request for super_block of NO_DEV");
194 
195   /* we rely on the cache blocksize, before reading the
196    * superblock, being big enough that our complete superblock
197    * is in block 0.
198    *
199    * copy between the disk block and the superblock buffer (depending
200    * on direction). mark the disk block dirty if the copy is into the
201    * disk block.
202    */
203   assert(lmfs_fs_block_size() >= sizeof(struct super_block) + SUPER_BLOCK_BYTES);
204   assert(SUPER_BLOCK_BYTES >= sizeof(struct super_block));
205   assert(SUPER_BLOCK_BYTES >= ondisk_bytes);
206 
207   /* Unlike accessing any other block, failure to read the superblock is a
208    * somewhat legitimate use case: it may happen when trying to mount a
209    * zero-sized partition.  In that case, we'd rather faily cleanly than
210    * crash the MFS service.
211    */
212   if ((r = lmfs_get_block(&bp, sp->s_dev, 0, NORMAL)) != OK) {
213 	if (writing)
214 		panic("get_block of superblock failed: %d", r);
215 	else
216 		return r;
217   }
218 
219   /* sbbuf points to the disk block at the superblock offset */
220   sbbuf = (char *) b_data(bp) + SUPER_BLOCK_BYTES;
221 
222   if(writing) {
223   	memset(b_data(bp), 0, lmfs_fs_block_size());
224   	memcpy(sbbuf, sp, ondisk_bytes);
225 	lmfs_markdirty(bp);
226   } else {
227 	memset(sp, 0, sizeof(*sp));
228   	memcpy(sp, sbbuf, ondisk_bytes);
229   	sp->s_dev = save_dev;
230   }
231 
232   put_block(bp);
233   lmfs_flushall();
234 
235   return OK;
236 }
237 
238 /*===========================================================================*
239  *				read_super				     *
240  *===========================================================================*/
241 int read_super(struct super_block *sp)
242 {
243   unsigned int magic;
244   block_t offset;
245   int version, native, r;
246 
247   if((r=rw_super(sp, 0)) != OK)
248   	return r;
249 
250   magic = sp->s_magic;		/* determines file system type */
251 
252   if(magic == SUPER_V2 || magic == SUPER_MAGIC) {
253 	printf("MFS: only supports V3 filesystems.\n");
254 	return EINVAL;
255   }
256 
257   /* Get file system version and type - only support v3. */
258   if(magic != SUPER_V3) {
259 	return EINVAL;
260   }
261   version = V3;
262   native = 1;
263 
264   /* If the super block has the wrong byte order, swap the fields; the magic
265    * number doesn't need conversion. */
266   sp->s_ninodes =           (ino_t) conv4(native, (int) sp->s_ninodes);
267   sp->s_nzones =          (zone1_t) conv2(native, (int) sp->s_nzones);
268   sp->s_imap_blocks =       (short) conv2(native, (int) sp->s_imap_blocks);
269   sp->s_zmap_blocks =       (short) conv2(native, (int) sp->s_zmap_blocks);
270   sp->s_firstdatazone_old =(zone1_t)conv2(native,(int)sp->s_firstdatazone_old);
271   sp->s_log_zone_size =     (short) conv2(native, (int) sp->s_log_zone_size);
272   sp->s_max_size =          (off_t) conv4(native, sp->s_max_size);
273   sp->s_zones =             (zone_t)conv4(native, sp->s_zones);
274 
275   /* Zones consisting of multiple blocks are longer supported, so fail as early
276    * as possible. There is still a lot of code cleanup to do here, though.
277    */
278   if (sp->s_log_zone_size != 0) {
279 	printf("MFS: block and zone sizes are different\n");
280 	return EINVAL;
281   }
282 
283   /* Calculate some other numbers that depend on the version here too, to
284    * hide some of the differences.
285    */
286   assert(version == V3);
287   sp->s_block_size = (unsigned short) conv2(native,(int) sp->s_block_size);
288   if (sp->s_block_size < PAGE_SIZE) {
289  	return EINVAL;
290   }
291   sp->s_inodes_per_block = V2_INODES_PER_BLOCK(sp->s_block_size);
292   sp->s_ndzones = V2_NR_DZONES;
293   sp->s_nindirs = V2_INDIRECTS(sp->s_block_size);
294 
295   /* For even larger disks, a similar problem occurs with s_firstdatazone.
296    * If the on-disk field contains zero, we assume that the value was too
297    * large to fit, and compute it on the fly.
298    */
299   if (sp->s_firstdatazone_old == 0) {
300 	offset = START_BLOCK + sp->s_imap_blocks + sp->s_zmap_blocks;
301 	offset += (sp->s_ninodes + sp->s_inodes_per_block - 1) /
302 		sp->s_inodes_per_block;
303 
304 	sp->s_firstdatazone = (offset + (1 << sp->s_log_zone_size) - 1) >>
305 		sp->s_log_zone_size;
306   } else {
307 	sp->s_firstdatazone = (zone_t) sp->s_firstdatazone_old;
308   }
309 
310   if (sp->s_block_size < PAGE_SIZE)
311   	return(EINVAL);
312 
313   if ((sp->s_block_size % 512) != 0)
314   	return(EINVAL);
315 
316   if (SUPER_SIZE > sp->s_block_size)
317   	return(EINVAL);
318 
319   if ((sp->s_block_size % V2_INODE_SIZE) != 0) {
320   	return(EINVAL);
321   }
322 
323   /* Limit s_max_size to LONG_MAX */
324   if ((unsigned long)sp->s_max_size > LONG_MAX)
325 	sp->s_max_size = LONG_MAX;
326 
327   sp->s_isearch = 0;		/* inode searches initially start at 0 */
328   sp->s_zsearch = 0;		/* zone searches initially start at 0 */
329   sp->s_version = version;
330   sp->s_native  = native;
331 
332   /* Make a few basic checks to see if super block looks reasonable. */
333   if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1
334 				|| sp->s_ninodes < 1 || sp->s_zones < 1
335 				|| sp->s_firstdatazone <= 4
336 				|| sp->s_firstdatazone >= sp->s_zones
337 				|| (unsigned) sp->s_log_zone_size > 4) {
338   	printf("not enough imap or zone map blocks, \n");
339   	printf("or not enough inodes, or not enough zones, \n"
340   		"or invalid first data zone, or zone size too large\n");
341 	return(EINVAL);
342   }
343 
344 
345   /* Check any flags we don't understand but are required to. Currently
346    * these don't exist so all such unknown bits are fatal.
347    */
348   if(sp->s_flags & MFSFLAG_MANDATORY_MASK) {
349   	printf("MFS: unsupported feature flags on this FS.\n"
350 		"Please use a newer MFS to mount it.\n");
351 	return(EINVAL);
352   }
353 
354   return(OK);
355 }
356 
357 /*===========================================================================*
358  *				write_super				     *
359  *===========================================================================*/
360 int write_super(struct super_block *sp)
361 {
362   if(sp->s_rd_only)
363   	panic("can't write superblock of readonly filesystem");
364   return rw_super(sp, 1);
365 }
366