1433d6423SLionel Sambuc /* This file manages the super block table and the related data structures,
2433d6423SLionel Sambuc * namely, the bit maps that keep track of which zones and which inodes are
3433d6423SLionel Sambuc * allocated and which are free. When a new inode or zone is needed, the
4433d6423SLionel Sambuc * appropriate bit map is searched for a free entry.
5433d6423SLionel Sambuc *
6433d6423SLionel Sambuc * The entry points into this file are
7433d6423SLionel Sambuc * alloc_bit: somebody wants to allocate a zone or inode; find one
8433d6423SLionel Sambuc * free_bit: indicate that a zone or inode is available for allocation
9433d6423SLionel Sambuc * mounted: tells if file inode is on mounted (or ROOT) file system
10433d6423SLionel Sambuc * read_super: read a superblock
11433d6423SLionel Sambuc */
12433d6423SLionel Sambuc
13433d6423SLionel Sambuc #include "fs.h"
14433d6423SLionel Sambuc #include <string.h>
15433d6423SLionel Sambuc #include <assert.h>
16433d6423SLionel Sambuc #include <minix/com.h>
17433d6423SLionel Sambuc #include <minix/u64.h>
18433d6423SLionel Sambuc #include <minix/bdev.h>
19433d6423SLionel Sambuc #include <machine/param.h>
20433d6423SLionel Sambuc #include <machine/vmparam.h>
21433d6423SLionel Sambuc #include "buf.h"
22433d6423SLionel Sambuc #include "inode.h"
23433d6423SLionel Sambuc #include "super.h"
24433d6423SLionel Sambuc #include "const.h"
25433d6423SLionel Sambuc
26433d6423SLionel Sambuc /*===========================================================================*
27433d6423SLionel Sambuc * alloc_bit *
28433d6423SLionel Sambuc *===========================================================================*/
alloc_bit(sp,map,origin)29433d6423SLionel Sambuc bit_t alloc_bit(sp, map, origin)
30433d6423SLionel Sambuc struct super_block *sp; /* the filesystem to allocate from */
31433d6423SLionel Sambuc int map; /* IMAP (inode map) or ZMAP (zone map) */
32433d6423SLionel Sambuc bit_t origin; /* number of bit to start searching at */
33433d6423SLionel Sambuc {
34433d6423SLionel Sambuc /* Allocate a bit from a bit map and return its bit number. */
35433d6423SLionel Sambuc
36433d6423SLionel Sambuc block_t start_block; /* first bit block */
37433d6423SLionel Sambuc block_t block;
38433d6423SLionel Sambuc bit_t map_bits; /* how many bits are there in the bit map? */
39433d6423SLionel Sambuc short bit_blocks; /* how many blocks are there in the bit map? */
40433d6423SLionel Sambuc unsigned word, bcount;
41433d6423SLionel Sambuc struct buf *bp;
42433d6423SLionel Sambuc bitchunk_t *wptr, *wlim, k;
43433d6423SLionel Sambuc bit_t i, b;
44433d6423SLionel Sambuc
45433d6423SLionel Sambuc if (sp->s_rd_only)
46433d6423SLionel Sambuc panic("can't allocate bit on read-only filesys");
47433d6423SLionel Sambuc
48433d6423SLionel Sambuc if (map == IMAP) {
49433d6423SLionel Sambuc start_block = START_BLOCK;
50433d6423SLionel Sambuc map_bits = (bit_t) (sp->s_ninodes + 1);
51433d6423SLionel Sambuc bit_blocks = sp->s_imap_blocks;
52433d6423SLionel Sambuc } else {
53433d6423SLionel Sambuc start_block = START_BLOCK + sp->s_imap_blocks;
54433d6423SLionel Sambuc map_bits = (bit_t) (sp->s_zones - (sp->s_firstdatazone - 1));
55433d6423SLionel Sambuc bit_blocks = sp->s_zmap_blocks;
56433d6423SLionel Sambuc }
57433d6423SLionel Sambuc
58433d6423SLionel Sambuc /* Figure out where to start the bit search (depends on 'origin'). */
59433d6423SLionel Sambuc if (origin >= map_bits) origin = 0; /* for robustness */
60433d6423SLionel Sambuc
61433d6423SLionel Sambuc /* Locate the starting place. */
62433d6423SLionel Sambuc block = (block_t) (origin / FS_BITS_PER_BLOCK(sp->s_block_size));
63433d6423SLionel Sambuc word = (origin % FS_BITS_PER_BLOCK(sp->s_block_size)) / FS_BITCHUNK_BITS;
64433d6423SLionel Sambuc
65433d6423SLionel Sambuc /* Iterate over all blocks plus one, because we start in the middle. */
66433d6423SLionel Sambuc bcount = bit_blocks + 1;
67433d6423SLionel Sambuc do {
68433d6423SLionel Sambuc bp = get_block(sp->s_dev, start_block + block, NORMAL);
69433d6423SLionel Sambuc wlim = &b_bitmap(bp)[FS_BITMAP_CHUNKS(sp->s_block_size)];
70433d6423SLionel Sambuc
71433d6423SLionel Sambuc /* Iterate over the words in block. */
72433d6423SLionel Sambuc for (wptr = &b_bitmap(bp)[word]; wptr < wlim; wptr++) {
73433d6423SLionel Sambuc
74433d6423SLionel Sambuc /* Does this word contain a free bit? */
75433d6423SLionel Sambuc if (*wptr == (bitchunk_t) ~0) continue;
76433d6423SLionel Sambuc
77433d6423SLionel Sambuc /* Find and allocate the free bit. */
78433d6423SLionel Sambuc k = (bitchunk_t) conv4(sp->s_native, (int) *wptr);
79433d6423SLionel Sambuc for (i = 0; (k & (1 << i)) != 0; ++i) {}
80433d6423SLionel Sambuc
81433d6423SLionel Sambuc /* Bit number from the start of the bit map. */
82433d6423SLionel Sambuc b = ((bit_t) block * FS_BITS_PER_BLOCK(sp->s_block_size))
83433d6423SLionel Sambuc + (wptr - &b_bitmap(bp)[0]) * FS_BITCHUNK_BITS
84433d6423SLionel Sambuc + i;
85433d6423SLionel Sambuc
86433d6423SLionel Sambuc /* Don't allocate bits beyond the end of the map. */
87433d6423SLionel Sambuc if (b >= map_bits) break;
88433d6423SLionel Sambuc
89433d6423SLionel Sambuc /* Allocate and return bit number. */
90433d6423SLionel Sambuc k |= 1 << i;
91433d6423SLionel Sambuc *wptr = (bitchunk_t) conv4(sp->s_native, (int) k);
92433d6423SLionel Sambuc MARKDIRTY(bp);
930314acfbSDavid van Moolenbroek put_block(bp);
94433d6423SLionel Sambuc if(map == ZMAP) {
951311233cSDavid van Moolenbroek used_zones++;
961311233cSDavid van Moolenbroek lmfs_change_blockusage(1);
97433d6423SLionel Sambuc }
98433d6423SLionel Sambuc return(b);
99433d6423SLionel Sambuc }
1000314acfbSDavid van Moolenbroek put_block(bp);
101433d6423SLionel Sambuc if (++block >= (unsigned int) bit_blocks) /* last block, wrap around */
102433d6423SLionel Sambuc block = 0;
103433d6423SLionel Sambuc word = 0;
104433d6423SLionel Sambuc } while (--bcount > 0);
105433d6423SLionel Sambuc return(NO_BIT); /* no bit could be allocated */
106433d6423SLionel Sambuc }
107433d6423SLionel Sambuc
108433d6423SLionel Sambuc /*===========================================================================*
109433d6423SLionel Sambuc * free_bit *
110433d6423SLionel Sambuc *===========================================================================*/
free_bit(sp,map,bit_returned)111433d6423SLionel Sambuc void free_bit(sp, map, bit_returned)
112433d6423SLionel Sambuc struct super_block *sp; /* the filesystem to operate on */
113433d6423SLionel Sambuc int map; /* IMAP (inode map) or ZMAP (zone map) */
114433d6423SLionel Sambuc bit_t bit_returned; /* number of bit to insert into the map */
115433d6423SLionel Sambuc {
116433d6423SLionel Sambuc /* Return a zone or inode by turning off its bitmap bit. */
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc unsigned block, word, bit;
119433d6423SLionel Sambuc struct buf *bp;
120433d6423SLionel Sambuc bitchunk_t k, mask;
121433d6423SLionel Sambuc block_t start_block;
122433d6423SLionel Sambuc
123433d6423SLionel Sambuc if (sp->s_rd_only)
124433d6423SLionel Sambuc panic("can't free bit on read-only filesys");
125433d6423SLionel Sambuc
126433d6423SLionel Sambuc if (map == IMAP) {
127433d6423SLionel Sambuc start_block = START_BLOCK;
128433d6423SLionel Sambuc } else {
129433d6423SLionel Sambuc start_block = START_BLOCK + sp->s_imap_blocks;
130433d6423SLionel Sambuc }
131433d6423SLionel Sambuc block = bit_returned / FS_BITS_PER_BLOCK(sp->s_block_size);
132433d6423SLionel Sambuc word = (bit_returned % FS_BITS_PER_BLOCK(sp->s_block_size))
133433d6423SLionel Sambuc / FS_BITCHUNK_BITS;
134433d6423SLionel Sambuc
135433d6423SLionel Sambuc bit = bit_returned % FS_BITCHUNK_BITS;
136433d6423SLionel Sambuc mask = 1 << bit;
137433d6423SLionel Sambuc
138433d6423SLionel Sambuc bp = get_block(sp->s_dev, start_block + block, NORMAL);
139433d6423SLionel Sambuc
140433d6423SLionel Sambuc k = (bitchunk_t) conv4(sp->s_native, (int) b_bitmap(bp)[word]);
141433d6423SLionel Sambuc if (!(k & mask)) {
142433d6423SLionel Sambuc if (map == IMAP) panic("tried to free unused inode");
143433d6423SLionel Sambuc else panic("tried to free unused block: %u", bit_returned);
144433d6423SLionel Sambuc }
145433d6423SLionel Sambuc
146433d6423SLionel Sambuc k &= ~mask;
147433d6423SLionel Sambuc b_bitmap(bp)[word] = (bitchunk_t) conv4(sp->s_native, (int) k);
148433d6423SLionel Sambuc MARKDIRTY(bp);
149433d6423SLionel Sambuc
1500314acfbSDavid van Moolenbroek put_block(bp);
151433d6423SLionel Sambuc
152433d6423SLionel Sambuc if(map == ZMAP) {
1531311233cSDavid van Moolenbroek used_zones--;
1541311233cSDavid van Moolenbroek lmfs_change_blockusage(-1);
155433d6423SLionel Sambuc }
156433d6423SLionel Sambuc }
157433d6423SLionel Sambuc
158433d6423SLionel Sambuc /*===========================================================================*
159433d6423SLionel Sambuc * get_block_size *
160433d6423SLionel Sambuc *===========================================================================*/
get_block_size(dev_t dev)161433d6423SLionel Sambuc unsigned int get_block_size(dev_t dev)
162433d6423SLionel Sambuc {
163433d6423SLionel Sambuc if (dev == NO_DEV)
164433d6423SLionel Sambuc panic("request for block size of NO_DEV");
165433d6423SLionel Sambuc
166433d6423SLionel Sambuc return(lmfs_fs_block_size());
167433d6423SLionel Sambuc }
168433d6423SLionel Sambuc
169433d6423SLionel Sambuc
170433d6423SLionel Sambuc /*===========================================================================*
171433d6423SLionel Sambuc * rw_super *
172433d6423SLionel Sambuc *===========================================================================*/
rw_super(struct super_block * sp,int writing)173433d6423SLionel Sambuc static int rw_super(struct super_block *sp, int writing)
174433d6423SLionel Sambuc {
175433d6423SLionel Sambuc /* Read/write a superblock. */
176433d6423SLionel Sambuc dev_t save_dev = sp->s_dev;
177433d6423SLionel Sambuc struct buf *bp;
178433d6423SLionel Sambuc char *sbbuf;
179*ee038489SDavid van Moolenbroek int r;
180433d6423SLionel Sambuc
181433d6423SLionel Sambuc /* To keep the 1kb on disk clean, only read/write up to and including
182433d6423SLionel Sambuc * this field.
183433d6423SLionel Sambuc */
184433d6423SLionel Sambuc #define LAST_ONDISK_FIELD s_disk_version
185433d6423SLionel Sambuc int ondisk_bytes = (int) ((char *) &sp->LAST_ONDISK_FIELD - (char *) sp)
186433d6423SLionel Sambuc + sizeof(sp->LAST_ONDISK_FIELD);
187433d6423SLionel Sambuc
188433d6423SLionel Sambuc assert(ondisk_bytes > 0);
189433d6423SLionel Sambuc assert(ondisk_bytes < PAGE_SIZE);
190433d6423SLionel Sambuc assert(ondisk_bytes < sizeof(struct super_block));
191433d6423SLionel Sambuc
192433d6423SLionel Sambuc if (sp->s_dev == NO_DEV)
193433d6423SLionel Sambuc panic("request for super_block of NO_DEV");
194433d6423SLionel Sambuc
195433d6423SLionel Sambuc /* we rely on the cache blocksize, before reading the
196433d6423SLionel Sambuc * superblock, being big enough that our complete superblock
197433d6423SLionel Sambuc * is in block 0.
198433d6423SLionel Sambuc *
199433d6423SLionel Sambuc * copy between the disk block and the superblock buffer (depending
200433d6423SLionel Sambuc * on direction). mark the disk block dirty if the copy is into the
201433d6423SLionel Sambuc * disk block.
202433d6423SLionel Sambuc */
203433d6423SLionel Sambuc assert(lmfs_fs_block_size() >= sizeof(struct super_block) + SUPER_BLOCK_BYTES);
204433d6423SLionel Sambuc assert(SUPER_BLOCK_BYTES >= sizeof(struct super_block));
205433d6423SLionel Sambuc assert(SUPER_BLOCK_BYTES >= ondisk_bytes);
206*ee038489SDavid van Moolenbroek
207*ee038489SDavid van Moolenbroek /* Unlike accessing any other block, failure to read the superblock is a
208*ee038489SDavid van Moolenbroek * somewhat legitimate use case: it may happen when trying to mount a
209*ee038489SDavid van Moolenbroek * zero-sized partition. In that case, we'd rather faily cleanly than
210*ee038489SDavid van Moolenbroek * crash the MFS service.
211*ee038489SDavid van Moolenbroek */
212*ee038489SDavid van Moolenbroek if ((r = lmfs_get_block(&bp, sp->s_dev, 0, NORMAL)) != OK) {
213*ee038489SDavid van Moolenbroek if (writing)
214*ee038489SDavid van Moolenbroek panic("get_block of superblock failed: %d", r);
215*ee038489SDavid van Moolenbroek else
216*ee038489SDavid van Moolenbroek return r;
217*ee038489SDavid van Moolenbroek }
218433d6423SLionel Sambuc
219433d6423SLionel Sambuc /* sbbuf points to the disk block at the superblock offset */
220433d6423SLionel Sambuc sbbuf = (char *) b_data(bp) + SUPER_BLOCK_BYTES;
221433d6423SLionel Sambuc
222433d6423SLionel Sambuc if(writing) {
223433d6423SLionel Sambuc memset(b_data(bp), 0, lmfs_fs_block_size());
224433d6423SLionel Sambuc memcpy(sbbuf, sp, ondisk_bytes);
225433d6423SLionel Sambuc lmfs_markdirty(bp);
226433d6423SLionel Sambuc } else {
227433d6423SLionel Sambuc memset(sp, 0, sizeof(*sp));
228433d6423SLionel Sambuc memcpy(sp, sbbuf, ondisk_bytes);
229433d6423SLionel Sambuc sp->s_dev = save_dev;
230433d6423SLionel Sambuc }
231433d6423SLionel Sambuc
2320314acfbSDavid van Moolenbroek put_block(bp);
233433d6423SLionel Sambuc lmfs_flushall();
234433d6423SLionel Sambuc
235433d6423SLionel Sambuc return OK;
236433d6423SLionel Sambuc }
237433d6423SLionel Sambuc
238433d6423SLionel Sambuc /*===========================================================================*
239433d6423SLionel Sambuc * read_super *
240433d6423SLionel Sambuc *===========================================================================*/
read_super(struct super_block * sp)241433d6423SLionel Sambuc int read_super(struct super_block *sp)
242433d6423SLionel Sambuc {
243433d6423SLionel Sambuc unsigned int magic;
244433d6423SLionel Sambuc block_t offset;
245433d6423SLionel Sambuc int version, native, r;
246433d6423SLionel Sambuc
247433d6423SLionel Sambuc if((r=rw_super(sp, 0)) != OK)
248433d6423SLionel Sambuc return r;
249433d6423SLionel Sambuc
250433d6423SLionel Sambuc magic = sp->s_magic; /* determines file system type */
251433d6423SLionel Sambuc
252433d6423SLionel Sambuc if(magic == SUPER_V2 || magic == SUPER_MAGIC) {
253433d6423SLionel Sambuc printf("MFS: only supports V3 filesystems.\n");
254433d6423SLionel Sambuc return EINVAL;
255433d6423SLionel Sambuc }
256433d6423SLionel Sambuc
257433d6423SLionel Sambuc /* Get file system version and type - only support v3. */
258433d6423SLionel Sambuc if(magic != SUPER_V3) {
259433d6423SLionel Sambuc return EINVAL;
260433d6423SLionel Sambuc }
261433d6423SLionel Sambuc version = V3;
262433d6423SLionel Sambuc native = 1;
263433d6423SLionel Sambuc
264433d6423SLionel Sambuc /* If the super block has the wrong byte order, swap the fields; the magic
265433d6423SLionel Sambuc * number doesn't need conversion. */
266433d6423SLionel Sambuc sp->s_ninodes = (ino_t) conv4(native, (int) sp->s_ninodes);
267433d6423SLionel Sambuc sp->s_nzones = (zone1_t) conv2(native, (int) sp->s_nzones);
268433d6423SLionel Sambuc sp->s_imap_blocks = (short) conv2(native, (int) sp->s_imap_blocks);
269433d6423SLionel Sambuc sp->s_zmap_blocks = (short) conv2(native, (int) sp->s_zmap_blocks);
270433d6423SLionel Sambuc sp->s_firstdatazone_old =(zone1_t)conv2(native,(int)sp->s_firstdatazone_old);
271433d6423SLionel Sambuc sp->s_log_zone_size = (short) conv2(native, (int) sp->s_log_zone_size);
272433d6423SLionel Sambuc sp->s_max_size = (off_t) conv4(native, sp->s_max_size);
273433d6423SLionel Sambuc sp->s_zones = (zone_t)conv4(native, sp->s_zones);
274433d6423SLionel Sambuc
2751311233cSDavid van Moolenbroek /* Zones consisting of multiple blocks are longer supported, so fail as early
2761311233cSDavid van Moolenbroek * as possible. There is still a lot of code cleanup to do here, though.
2771311233cSDavid van Moolenbroek */
2781311233cSDavid van Moolenbroek if (sp->s_log_zone_size != 0) {
2791311233cSDavid van Moolenbroek printf("MFS: block and zone sizes are different\n");
2801311233cSDavid van Moolenbroek return EINVAL;
2811311233cSDavid van Moolenbroek }
2821311233cSDavid van Moolenbroek
283433d6423SLionel Sambuc /* Calculate some other numbers that depend on the version here too, to
284433d6423SLionel Sambuc * hide some of the differences.
285433d6423SLionel Sambuc */
286433d6423SLionel Sambuc assert(version == V3);
287433d6423SLionel Sambuc sp->s_block_size = (unsigned short) conv2(native,(int) sp->s_block_size);
288433d6423SLionel Sambuc if (sp->s_block_size < PAGE_SIZE) {
289433d6423SLionel Sambuc return EINVAL;
290433d6423SLionel Sambuc }
291433d6423SLionel Sambuc sp->s_inodes_per_block = V2_INODES_PER_BLOCK(sp->s_block_size);
292433d6423SLionel Sambuc sp->s_ndzones = V2_NR_DZONES;
293433d6423SLionel Sambuc sp->s_nindirs = V2_INDIRECTS(sp->s_block_size);
294433d6423SLionel Sambuc
295433d6423SLionel Sambuc /* For even larger disks, a similar problem occurs with s_firstdatazone.
296433d6423SLionel Sambuc * If the on-disk field contains zero, we assume that the value was too
297433d6423SLionel Sambuc * large to fit, and compute it on the fly.
298433d6423SLionel Sambuc */
299433d6423SLionel Sambuc if (sp->s_firstdatazone_old == 0) {
300433d6423SLionel Sambuc offset = START_BLOCK + sp->s_imap_blocks + sp->s_zmap_blocks;
301433d6423SLionel Sambuc offset += (sp->s_ninodes + sp->s_inodes_per_block - 1) /
302433d6423SLionel Sambuc sp->s_inodes_per_block;
303433d6423SLionel Sambuc
304433d6423SLionel Sambuc sp->s_firstdatazone = (offset + (1 << sp->s_log_zone_size) - 1) >>
305433d6423SLionel Sambuc sp->s_log_zone_size;
306433d6423SLionel Sambuc } else {
307433d6423SLionel Sambuc sp->s_firstdatazone = (zone_t) sp->s_firstdatazone_old;
308433d6423SLionel Sambuc }
309433d6423SLionel Sambuc
310433d6423SLionel Sambuc if (sp->s_block_size < PAGE_SIZE)
311433d6423SLionel Sambuc return(EINVAL);
312433d6423SLionel Sambuc
313433d6423SLionel Sambuc if ((sp->s_block_size % 512) != 0)
314433d6423SLionel Sambuc return(EINVAL);
315433d6423SLionel Sambuc
316433d6423SLionel Sambuc if (SUPER_SIZE > sp->s_block_size)
317433d6423SLionel Sambuc return(EINVAL);
318433d6423SLionel Sambuc
319433d6423SLionel Sambuc if ((sp->s_block_size % V2_INODE_SIZE) != 0) {
320433d6423SLionel Sambuc return(EINVAL);
321433d6423SLionel Sambuc }
322433d6423SLionel Sambuc
323433d6423SLionel Sambuc /* Limit s_max_size to LONG_MAX */
324433d6423SLionel Sambuc if ((unsigned long)sp->s_max_size > LONG_MAX)
325433d6423SLionel Sambuc sp->s_max_size = LONG_MAX;
326433d6423SLionel Sambuc
327433d6423SLionel Sambuc sp->s_isearch = 0; /* inode searches initially start at 0 */
328433d6423SLionel Sambuc sp->s_zsearch = 0; /* zone searches initially start at 0 */
329433d6423SLionel Sambuc sp->s_version = version;
330433d6423SLionel Sambuc sp->s_native = native;
331433d6423SLionel Sambuc
332433d6423SLionel Sambuc /* Make a few basic checks to see if super block looks reasonable. */
333433d6423SLionel Sambuc if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1
334433d6423SLionel Sambuc || sp->s_ninodes < 1 || sp->s_zones < 1
335433d6423SLionel Sambuc || sp->s_firstdatazone <= 4
336433d6423SLionel Sambuc || sp->s_firstdatazone >= sp->s_zones
337433d6423SLionel Sambuc || (unsigned) sp->s_log_zone_size > 4) {
338433d6423SLionel Sambuc printf("not enough imap or zone map blocks, \n");
339433d6423SLionel Sambuc printf("or not enough inodes, or not enough zones, \n"
340433d6423SLionel Sambuc "or invalid first data zone, or zone size too large\n");
341433d6423SLionel Sambuc return(EINVAL);
342433d6423SLionel Sambuc }
343433d6423SLionel Sambuc
344433d6423SLionel Sambuc
345433d6423SLionel Sambuc /* Check any flags we don't understand but are required to. Currently
346433d6423SLionel Sambuc * these don't exist so all such unknown bits are fatal.
347433d6423SLionel Sambuc */
348433d6423SLionel Sambuc if(sp->s_flags & MFSFLAG_MANDATORY_MASK) {
349433d6423SLionel Sambuc printf("MFS: unsupported feature flags on this FS.\n"
350433d6423SLionel Sambuc "Please use a newer MFS to mount it.\n");
351433d6423SLionel Sambuc return(EINVAL);
352433d6423SLionel Sambuc }
353433d6423SLionel Sambuc
354433d6423SLionel Sambuc return(OK);
355433d6423SLionel Sambuc }
356433d6423SLionel Sambuc
357433d6423SLionel Sambuc /*===========================================================================*
358433d6423SLionel Sambuc * write_super *
359433d6423SLionel Sambuc *===========================================================================*/
write_super(struct super_block * sp)360433d6423SLionel Sambuc int write_super(struct super_block *sp)
361433d6423SLionel Sambuc {
362433d6423SLionel Sambuc if(sp->s_rd_only)
363433d6423SLionel Sambuc panic("can't write superblock of readonly filesystem");
364433d6423SLionel Sambuc return rw_super(sp, 1);
365433d6423SLionel Sambuc }
366