tar: use utime() to restore timestamps
[minix.git] / servers / mfs / super.c
blob1268253340bd049f39425a68c1a026cd5ae7212b
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.
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
14 #include "fs.h"
15 #include <string.h>
16 #include <assert.h>
17 #include <minix/com.h>
18 #include <minix/u64.h>
19 #include <minix/bdev.h>
20 #include "buf.h"
21 #include "inode.h"
22 #include "super.h"
23 #include "const.h"
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 */
34 /* Allocate a bit from a bit map and return its bit number. */
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;
45 if (sp->s_rd_only)
46 panic("can't allocate bit on read-only filesys");
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;
58 /* Figure out where to start the bit search (depends on 'origin'). */
59 if (origin >= map_bits) origin = 0; /* for robustness */
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;
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)];
71 /* Iterate over the words in block. */
72 for (wptr = &b_bitmap(bp)[word]; wptr < wlim; wptr++) {
74 /* Does this word contain a free bit? */
75 if (*wptr == (bitchunk_t) ~0) continue;
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) {}
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;
86 /* Don't allocate bits beyond the end of the map. */
87 if (b >= map_bits) break;
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, MAP_BLOCK);
94 return(b);
96 put_block(bp, MAP_BLOCK);
97 if (++block >= (unsigned int) bit_blocks) /* last block, wrap around */
98 block = 0;
99 word = 0;
100 } while (--bcount > 0);
101 return(NO_BIT); /* no bit could be allocated */
104 /*===========================================================================*
105 * free_bit *
106 *===========================================================================*/
107 void free_bit(sp, map, bit_returned)
108 struct super_block *sp; /* the filesystem to operate on */
109 int map; /* IMAP (inode map) or ZMAP (zone map) */
110 bit_t bit_returned; /* number of bit to insert into the map */
112 /* Return a zone or inode by turning off its bitmap bit. */
114 unsigned block, word, bit;
115 struct buf *bp;
116 bitchunk_t k, mask;
117 block_t start_block;
119 if (sp->s_rd_only)
120 panic("can't free bit on read-only filesys");
122 if (map == IMAP) {
123 start_block = START_BLOCK;
124 } else {
125 start_block = START_BLOCK + sp->s_imap_blocks;
127 block = bit_returned / FS_BITS_PER_BLOCK(sp->s_block_size);
128 word = (bit_returned % FS_BITS_PER_BLOCK(sp->s_block_size))
129 / FS_BITCHUNK_BITS;
131 bit = bit_returned % FS_BITCHUNK_BITS;
132 mask = 1 << bit;
134 bp = get_block(sp->s_dev, start_block + block, NORMAL);
136 k = (bitchunk_t) conv4(sp->s_native, (int) b_bitmap(bp)[word]);
137 if (!(k & mask)) {
138 if (map == IMAP) panic("tried to free unused inode");
139 else panic("tried to free unused block: %u", bit_returned);
142 k &= ~mask;
143 b_bitmap(bp)[word] = (bitchunk_t) conv4(sp->s_native, (int) k);
144 MARKDIRTY(bp);
146 put_block(bp, MAP_BLOCK);
150 /*===========================================================================*
151 * get_super *
152 *===========================================================================*/
153 struct super_block *get_super(
154 dev_t dev /* device number whose super_block is sought */
157 if (dev == NO_DEV)
158 panic("request for super_block of NO_DEV");
160 if(superblock.s_dev != dev)
161 panic("wrong superblock: 0x%x", (int) dev);
163 return(&superblock);
167 /*===========================================================================*
168 * get_block_size *
169 *===========================================================================*/
170 unsigned int get_block_size(dev_t dev)
172 if (dev == NO_DEV)
173 panic("request for block size of NO_DEV");
175 return(lmfs_fs_block_size());
179 /*===========================================================================*
180 * rw_super *
181 *===========================================================================*/
182 static int rw_super(struct super_block *sp, int writing)
184 /* Read/write a superblock. */
185 int r;
186 static char *sbbuf;
187 dev_t save_dev = sp->s_dev;
189 /* To keep the 1kb on disk clean, only read/write up to and including
190 * this field.
192 #define LAST_ONDISK_FIELD s_disk_version
193 int ondisk_bytes = (int) ((char *) &sp->LAST_ONDISK_FIELD - (char *) sp)
194 + sizeof(sp->LAST_ONDISK_FIELD);
196 STATICINIT(sbbuf, _MIN_BLOCK_SIZE);
198 assert(ondisk_bytes > 0);
199 assert(ondisk_bytes < _MIN_BLOCK_SIZE);
200 assert(ondisk_bytes < sizeof(struct super_block));
202 if (sp->s_dev == NO_DEV)
203 panic("request for super_block of NO_DEV");
205 if(writing) {
206 memset(sbbuf, 0, _MIN_BLOCK_SIZE);
207 memcpy(sbbuf, sp, ondisk_bytes);
208 r = bdev_write(sp->s_dev, cvu64(SUPER_BLOCK_BYTES), sbbuf, _MIN_BLOCK_SIZE,
209 BDEV_NOFLAGS);
210 } else {
211 r = bdev_read(sp->s_dev, cvu64(SUPER_BLOCK_BYTES), sbbuf, _MIN_BLOCK_SIZE,
212 BDEV_NOFLAGS);
213 memset(sp, 0, sizeof(*sp));
214 memcpy(sp, sbbuf, ondisk_bytes);
215 sp->s_dev = save_dev;
218 if (r != _MIN_BLOCK_SIZE)
219 return(EINVAL);
221 return OK;
224 /*===========================================================================*
225 * read_super *
226 *===========================================================================*/
227 int read_super(struct super_block *sp)
229 unsigned int magic;
230 block_t offset;
231 int version, native, r;
233 if((r=rw_super(sp, 0)) != OK)
234 return r;
236 magic = sp->s_magic; /* determines file system type */
238 /* Get file system version and type. */
239 if (magic == SUPER_MAGIC || magic == conv2(BYTE_SWAP, SUPER_MAGIC)) {
240 version = V1;
241 native = (magic == SUPER_MAGIC);
242 } else if (magic == SUPER_V2 || magic == conv2(BYTE_SWAP, SUPER_V2)) {
243 version = V2;
244 native = (magic == SUPER_V2);
245 } else if (magic == SUPER_V3) {
246 version = V3;
247 native = 1;
248 } else {
249 return(EINVAL);
252 /* If the super block has the wrong byte order, swap the fields; the magic
253 * number doesn't need conversion. */
254 sp->s_ninodes = (ino_t) conv4(native, (int) sp->s_ninodes);
255 sp->s_nzones = (zone1_t) conv2(native, (int) sp->s_nzones);
256 sp->s_imap_blocks = (short) conv2(native, (int) sp->s_imap_blocks);
257 sp->s_zmap_blocks = (short) conv2(native, (int) sp->s_zmap_blocks);
258 sp->s_firstdatazone_old =(zone1_t)conv2(native,(int)sp->s_firstdatazone_old);
259 sp->s_log_zone_size = (short) conv2(native, (int) sp->s_log_zone_size);
260 sp->s_max_size = (off_t) conv4(native, sp->s_max_size);
261 sp->s_zones = (zone_t)conv4(native, sp->s_zones);
263 /* In V1, the device size was kept in a short, s_nzones, which limited
264 * devices to 32K zones. For V2, it was decided to keep the size as a
265 * long. However, just changing s_nzones to a long would not work, since
266 * then the position of s_magic in the super block would not be the same
267 * in V1 and V2 file systems, and there would be no way to tell whether
268 * a newly mounted file system was V1 or V2. The solution was to introduce
269 * a new variable, s_zones, and copy the size there.
271 * Calculate some other numbers that depend on the version here too, to
272 * hide some of the differences.
274 if (version == V1) {
275 sp->s_block_size = _STATIC_BLOCK_SIZE;
276 sp->s_zones = (zone_t) sp->s_nzones; /* only V1 needs this copy */
277 sp->s_inodes_per_block = V1_INODES_PER_BLOCK;
278 sp->s_ndzones = V1_NR_DZONES;
279 sp->s_nindirs = V1_INDIRECTS;
280 } else {
281 if (version == V2) {
282 sp->s_block_size = _STATIC_BLOCK_SIZE;
283 } else {
284 sp->s_block_size = (unsigned short)
285 conv2(native,(int) sp->s_block_size);
287 if (sp->s_block_size < _MIN_BLOCK_SIZE) {
288 return EINVAL;
290 sp->s_inodes_per_block = V2_INODES_PER_BLOCK(sp->s_block_size);
291 sp->s_ndzones = V2_NR_DZONES;
292 sp->s_nindirs = V2_INDIRECTS(sp->s_block_size);
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.
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;
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;
310 if (sp->s_block_size < _MIN_BLOCK_SIZE)
311 return(EINVAL);
313 if ((sp->s_block_size % 512) != 0)
314 return(EINVAL);
316 if (SUPER_SIZE > sp->s_block_size)
317 return(EINVAL);
319 if ((sp->s_block_size % V2_INODE_SIZE) != 0 ||
320 (sp->s_block_size % V1_INODE_SIZE) != 0) {
321 return(EINVAL);
324 /* Limit s_max_size to LONG_MAX */
325 if ((unsigned long)sp->s_max_size > LONG_MAX)
326 sp->s_max_size = LONG_MAX;
328 sp->s_isearch = 0; /* inode searches initially start at 0 */
329 sp->s_zsearch = 0; /* zone searches initially start at 0 */
330 sp->s_version = version;
331 sp->s_native = native;
333 /* Make a few basic checks to see if super block looks reasonable. */
334 if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1
335 || sp->s_ninodes < 1 || sp->s_zones < 1
336 || sp->s_firstdatazone <= 4
337 || sp->s_firstdatazone >= sp->s_zones
338 || (unsigned) sp->s_log_zone_size > 4) {
339 printf("not enough imap or zone map blocks, \n");
340 printf("or not enough inodes, or not enough zones, \n"
341 "or invalid first data zone, or zone size too large\n");
342 return(EINVAL);
346 /* Check any flags we don't understand but are required to. Currently
347 * these don't exist so all such unknown bits are fatal.
349 if(sp->s_flags & MFSFLAG_MANDATORY_MASK) {
350 printf("MFS: unsupported feature flags on this FS.\n"
351 "Please use a newer MFS to mount it.\n");
352 return(EINVAL);
355 return(OK);
358 /*===========================================================================*
359 * write_super *
360 *===========================================================================*/
361 int write_super(struct super_block *sp)
363 if(sp->s_rd_only)
364 panic("can't write superblock of readonly filesystem");
365 return rw_super(sp, 1);