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", NO_NUM);
|
---|
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 | }
|
---|