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
16 #include <minix/com.h>
17 #include <minix/u64.h>
24 /*===========================================================================*
26 *===========================================================================*/
27 PUBLIC bit_t
alloc_bit(sp
, map
, origin
)
28 struct super_block
*sp
; /* the filesystem to allocate from */
29 int map
; /* IMAP (inode map) or ZMAP (zone map) */
30 bit_t origin
; /* number of bit to start searching at */
32 /* Allocate a bit from a bit map and return its bit number. */
34 block_t start_block
; /* first bit block */
35 bit_t map_bits
; /* how many bits are there in the bit map? */
36 unsigned bit_blocks
; /* how many blocks are there in the bit map? */
37 unsigned block
, word
, bcount
;
39 bitchunk_t
*wptr
, *wlim
, k
;
43 panic(__FILE__
,"can't allocate bit on read-only filesys.", NO_NUM
);
46 start_block
= START_BLOCK
;
47 map_bits
= sp
->s_ninodes
+ 1;
48 bit_blocks
= sp
->s_imap_blocks
;
50 start_block
= START_BLOCK
+ sp
->s_imap_blocks
;
51 map_bits
= sp
->s_zones
- (sp
->s_firstdatazone
- 1);
52 bit_blocks
= sp
->s_zmap_blocks
;
55 /* Figure out where to start the bit search (depends on 'origin'). */
56 if (origin
>= map_bits
) origin
= 0; /* for robustness */
58 /* Locate the starting place. */
59 block
= origin
/ FS_BITS_PER_BLOCK(sp
->s_block_size
);
60 word
= (origin
% FS_BITS_PER_BLOCK(sp
->s_block_size
)) / FS_BITCHUNK_BITS
;
62 /* Iterate over all blocks plus one, because we start in the middle. */
63 bcount
= bit_blocks
+ 1;
65 bp
= get_block(sp
->s_dev
, start_block
+ block
, NORMAL
);
66 wlim
= &bp
->b_bitmap
[FS_BITMAP_CHUNKS(sp
->s_block_size
)];
68 /* Iterate over the words in block. */
69 for (wptr
= &bp
->b_bitmap
[word
]; wptr
< wlim
; wptr
++) {
71 /* Does this word contain a free bit? */
72 if (*wptr
== (bitchunk_t
) ~0) continue;
74 /* Find and allocate the free bit. */
75 k
= conv2(sp
->s_native
, (int) *wptr
);
76 for (i
= 0; (k
& (1 << i
)) != 0; ++i
) {}
78 /* Bit number from the start of the bit map. */
79 b
= ((bit_t
) block
* FS_BITS_PER_BLOCK(sp
->s_block_size
))
80 + (wptr
- &bp
->b_bitmap
[0]) * FS_BITCHUNK_BITS
83 /* Don't allocate bits beyond the end of the map. */
84 if (b
>= map_bits
) break;
86 /* Allocate and return bit number. */
88 *wptr
= conv2(sp
->s_native
, (int) k
);
90 put_block(bp
, MAP_BLOCK
);
93 put_block(bp
, MAP_BLOCK
);
94 if (++block
>= bit_blocks
) block
= 0; /* last block, wrap around */
96 } while (--bcount
> 0);
97 return(NO_BIT
); /* no bit could be allocated */
100 /*===========================================================================*
102 *===========================================================================*/
103 PUBLIC
void free_bit(sp
, map
, bit_returned
)
104 struct super_block
*sp
; /* the filesystem to operate on */
105 int map
; /* IMAP (inode map) or ZMAP (zone map) */
106 bit_t bit_returned
; /* number of bit to insert into the map */
108 /* Return a zone or inode by turning off its bitmap bit. */
110 unsigned block
, word
, bit
;
116 panic(__FILE__
,"can't free bit on read-only filesys.", NO_NUM
);
119 start_block
= START_BLOCK
;
121 start_block
= START_BLOCK
+ sp
->s_imap_blocks
;
123 block
= bit_returned
/ FS_BITS_PER_BLOCK(sp
->s_block_size
);
124 word
= (bit_returned
% FS_BITS_PER_BLOCK(sp
->s_block_size
))
127 bit
= bit_returned
% FS_BITCHUNK_BITS
;
130 bp
= get_block(sp
->s_dev
, start_block
+ block
, NORMAL
);
132 k
= conv2(sp
->s_native
, (int) bp
->b_bitmap
[word
]);
134 panic(__FILE__
,map
== IMAP
? "tried to free unused inode" :
135 "tried to free unused block", bit_returned
);
139 bp
->b_bitmap
[word
] = conv2(sp
->s_native
, (int) k
);
142 put_block(bp
, MAP_BLOCK
);
145 /*===========================================================================*
147 *===========================================================================*/
148 PUBLIC
struct super_block
*get_super(dev
)
149 dev_t dev
; /* device number whose super_block is sought */
151 /* Search the superblock table for this device. It is supposed to be there. */
153 register struct super_block
*sp
;
156 panic(__FILE__
,"request for super_block of NO_DEV", NO_NUM
);
158 for (sp
= &super_block
[0]; sp
< &super_block
[NR_SUPERS
]; sp
++)
159 if (sp
->s_dev
== dev
) return(sp
);
160 printf("MFS(%d)get_super: sp->s_dev: %d, dev: %d\n", SELF_E
, sp
->s_dev
, dev
);
162 /* Search failed. Something wrong. */
163 panic(__FILE__
,"can't find superblock for device (in decimal)", (int) dev
);
165 return(NIL_SUPER
); /* to keep the compiler and lint quiet */
168 /*===========================================================================*
170 *===========================================================================*/
171 PUBLIC
int get_block_size(dev_t dev
)
173 /* Search the superblock table for this device. */
175 register struct super_block
*sp
;
178 panic(__FILE__
,"request for block size of NO_DEV", NO_NUM
);
180 for (sp
= &super_block
[0]; sp
< &super_block
[NR_SUPERS
]; sp
++) {
181 if (sp
->s_dev
== dev
) {
182 return(sp
->s_block_size
);
186 /* no mounted filesystem? use this block size then. */
187 return _MIN_BLOCK_SIZE
;
190 /*===========================================================================*
192 *===========================================================================*/
193 /* Report on whether the given inode is on a mounted (or ROOT) file system. */
195 PUBLIC int mounted(rip)
196 register struct inode *rip;
199 register struct super_block *sp;
202 dev = (dev_t) rip->i_zone[0];
203 if (dev == root_dev) return(TRUE);
205 for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++)
206 if (sp->s_dev == dev) return(TRUE);
211 /*===========================================================================*
213 *===========================================================================*/
214 PUBLIC
int read_super(sp
)
215 register struct super_block
*sp
; /* pointer to a superblock */
217 /* Read a superblock. */
220 int version
, native
, r
;
221 static char sbbuf
[_MIN_BLOCK_SIZE
];
223 dev
= sp
->s_dev
; /* save device (will be overwritten by copy) */
225 panic(__FILE__
,"request for super_block of NO_DEV", NO_NUM
);
227 r
= block_dev_io(MFS_DEV_READ
, dev
, SELF_E
,
228 sbbuf
, cvu64(SUPER_BLOCK_BYTES
), _MIN_BLOCK_SIZE
, 0);
229 if (r
!= _MIN_BLOCK_SIZE
) {
232 memcpy(sp
, sbbuf
, sizeof(*sp
));
233 sp
->s_dev
= NO_DEV
; /* restore later */
234 magic
= sp
->s_magic
; /* determines file system type */
236 /* Get file system version and type. */
237 if (magic
== SUPER_MAGIC
|| magic
== conv2(BYTE_SWAP
, SUPER_MAGIC
)) {
239 native
= (magic
== SUPER_MAGIC
);
240 } else if (magic
== SUPER_V2
|| magic
== conv2(BYTE_SWAP
, SUPER_V2
)) {
242 native
= (magic
== SUPER_V2
);
243 } else if (magic
== SUPER_V3
) {
250 /* If the super block has the wrong byte order, swap the fields; the magic
251 * number doesn't need conversion. */
252 sp
->s_ninodes
= conv4(native
, sp
->s_ninodes
);
253 sp
->s_nzones
= conv2(native
, (int) sp
->s_nzones
);
254 sp
->s_imap_blocks
= conv2(native
, (int) sp
->s_imap_blocks
);
255 sp
->s_zmap_blocks
= conv2(native
, (int) sp
->s_zmap_blocks
);
256 sp
->s_firstdatazone
= conv2(native
, (int) sp
->s_firstdatazone
);
257 sp
->s_log_zone_size
= conv2(native
, (int) sp
->s_log_zone_size
);
258 sp
->s_max_size
= conv4(native
, sp
->s_max_size
);
259 sp
->s_zones
= conv4(native
, sp
->s_zones
);
261 /* In V1, the device size was kept in a short, s_nzones, which limited
262 * devices to 32K zones. For V2, it was decided to keep the size as a
263 * long. However, just changing s_nzones to a long would not work, since
264 * then the position of s_magic in the super block would not be the same
265 * in V1 and V2 file systems, and there would be no way to tell whether
266 * a newly mounted file system was V1 or V2. The solution was to introduce
267 * a new variable, s_zones, and copy the size there.
269 * Calculate some other numbers that depend on the version here too, to
270 * hide some of the differences.
273 sp
->s_block_size
= _STATIC_BLOCK_SIZE
;
274 sp
->s_zones
= sp
->s_nzones
; /* only V1 needs this copy */
275 sp
->s_inodes_per_block
= V1_INODES_PER_BLOCK
;
276 sp
->s_ndzones
= V1_NR_DZONES
;
277 sp
->s_nindirs
= V1_INDIRECTS
;
280 sp
->s_block_size
= _STATIC_BLOCK_SIZE
;
281 if (sp
->s_block_size
< _MIN_BLOCK_SIZE
) {
284 sp
->s_inodes_per_block
= V2_INODES_PER_BLOCK(sp
->s_block_size
);
285 sp
->s_ndzones
= V2_NR_DZONES
;
286 sp
->s_nindirs
= V2_INDIRECTS(sp
->s_block_size
);
289 if (sp
->s_block_size
< _MIN_BLOCK_SIZE
) {
292 if (sp
->s_block_size
> _MAX_BLOCK_SIZE
) {
293 printf("Filesystem block size is %d kB; maximum filesystem\n"
294 "block size is %d kB. This limit can be increased by recompiling.\n",
295 sp
->s_block_size
/1024, _MAX_BLOCK_SIZE
/1024);
298 if ((sp
->s_block_size
% 512) != 0) {
301 if (SUPER_SIZE
> sp
->s_block_size
) {
304 if ((sp
->s_block_size
% V2_INODE_SIZE
) != 0 ||
305 (sp
->s_block_size
% V1_INODE_SIZE
) != 0) {
309 /* Limit s_max_size to LONG_MAX */
310 if ((unsigned long)sp
->s_max_size
> LONG_MAX
)
312 printf("read_super: reducing s_max_size to LONG_MAX\n");
313 sp
->s_max_size
= LONG_MAX
;
316 sp
->s_isearch
= 0; /* inode searches initially start at 0 */
317 sp
->s_zsearch
= 0; /* zone searches initially start at 0 */
318 sp
->s_version
= version
;
319 sp
->s_native
= native
;
321 /* Make a few basic checks to see if super block looks reasonable. */
322 if (sp
->s_imap_blocks
< 1 || sp
->s_zmap_blocks
< 1
323 || sp
->s_ninodes
< 1 || sp
->s_zones
< 1
324 || (unsigned) sp
->s_log_zone_size
> 4) {
325 printf("not enough imap or zone map blocks, \n");
326 printf("or not enough inodes, or not enough zones, "
327 "or zone size too large\n");
330 sp
->s_dev
= dev
; /* restore device number */