xref: /minix3/minix/usr.sbin/mkfs.mfs/mkfs.c (revision 03ac74ede908465cc64c671bbd209e761dc765dc)
1 /* mkfs  -  make the MINIX filesystem	Authors: Tanenbaum et al. */
2 
3 /*	Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans */
4 
5 #if HAVE_NBTOOL_CONFIG_H
6 #include "nbtool_config.h"
7 #endif
8 
9 #include <sys/cdefs.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 
13 #if defined(__minix)
14 #include <minix/minlib.h>
15 #include <minix/partition.h>
16 #include <sys/ioctl.h>
17 #endif
18 
19 #include <assert.h>
20 #include <err.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 
32 /* Definition of the file system layout: */
33 #include "const.h"
34 #include "type.h"
35 #include "mfsdir.h"
36 #include "super.h"
37 
38 #define INODE_MAP	START_BLOCK
39 /* inode zone indexes pointing to single and double indirect zones */
40 #define S_INDIRECT_IDX	(NR_DZONES)
41 #define D_INDIRECT_IDX	(NR_DZONES+1)
42 
43 
44 #define MAX_TOKENS          10
45 #define LINE_LEN           300
46 
47 /* some Minix specific types that do not conflict with Posix */
48 #ifndef block_t
49 typedef uint32_t block_t;	/* block number */
50 #endif
51 #ifndef zone_t
52 typedef uint32_t zone_t;	/* zone number */
53 #endif
54 #ifndef bit_t
55 typedef uint32_t bit_t;		/* bit number in a bit map */
56 #endif
57 #ifndef bitchunk_t
58 typedef uint32_t bitchunk_t;	/* collection of bits in a bitmap */
59 #endif
60 
61 struct fs_size {
62   ino_t inocount; /* amount of inodes */
63   zone_t zonecount; /* amount of zones */
64   block_t blockcount; /* amount of blocks */
65 };
66 
67 extern char *optarg;
68 extern int optind;
69 
70 block_t nrblocks;
71 int zone_per_block, zone_shift = 0;
72 zone_t next_zone, zoff, nr_indirzones;
73 int inodes_per_block, indir_per_block, indir_per_zone;
74 unsigned int zone_size;
75 ino_t nrinodes, inode_offset, next_inode;
76 int lct = 0, fd, print = 0;
77 int simple = 0, dflag = 0, verbose = 0;
78 int donttest;			/* skip test if it fits on medium */
79 char *progname;
80 uint64_t fs_offset_bytes, fs_offset_blocks, written_fs_size = 0;
81 
82 time_t current_time;
83 char *zero;
84 unsigned char *umap_array;	/* bit map tells if block read yet */
85 size_t umap_array_elements;
86 block_t zone_map;		/* where is zone map? (depends on # inodes) */
87 #ifndef MFS_STATIC_BLOCK_SIZE
88 size_t block_size;
89 #else
90 #define block_size	MFS_STATIC_BLOCK_SIZE
91 #endif
92 
93 FILE *proto;
94 
95 int main(int argc, char **argv);
96 void detect_fs_size(struct fs_size * fssize);
97 void sizeup_dir(struct fs_size * fssize);
98 block_t sizeup(char *device);
99 static int bitmapsize(bit_t nr_bits, size_t blk_size);
100 void super(zone_t zones, ino_t inodes);
101 void rootdir(ino_t inode);
102 void enter_symlink(ino_t inode, char *link);
103 int dir_try_enter(zone_t z, ino_t child, char const *name);
104 void eat_dir(ino_t parent);
105 void eat_file(ino_t inode, int f);
106 void enter_dir(ino_t parent, char const *name, ino_t child);
107 void add_zone(ino_t n, zone_t z, size_t bytes, time_t cur_time);
108 void incr_link(ino_t n);
109 void incr_size(ino_t n, size_t count);
110 static ino_t alloc_inode(int mode, int usrid, int grpid);
111 static zone_t alloc_zone(void);
112 void insert_bit(block_t block, bit_t bit);
113 int mode_con(char *p);
114 void get_line(char line[LINE_LEN], char *parse[MAX_TOKENS]);
115 void check_mtab(const char *devname);
116 time_t file_time(int f);
117 __dead void pexit(char const *s, ...) __printflike(1,2);
118 void *alloc_block(void);
119 void print_fs(void);
120 int read_and_set(block_t n);
121 void special(char *string, int insertmode);
122 __dead void usage(void);
123 void get_block(block_t n, void *buf);
124 void get_super_block(void *buf);
125 void put_block(block_t n, void *buf);
126 static uint64_t mkfs_seek(uint64_t pos, int whence);
127 static ssize_t mkfs_write(void * buf, size_t count);
128 
129 /*================================================================
130  *                    mkfs  -  make filesystem
131  *===============================================================*/
132 int
133 main(int argc, char *argv[])
134 {
135   int nread, mode, usrid, grpid, ch, extra_space_percent, Tflag = 0;
136   block_t blocks, maxblocks, bblocks;
137   ino_t inodes, root_inum;
138   char *token[MAX_TOKENS], line[LINE_LEN], *sfx;
139   struct fs_size fssize;
140   int insertmode = 0;
141 
142   progname = argv[0];
143 
144   /* Process switches. */
145   blocks = 0;
146   inodes = 0;
147   bblocks = 0;
148 #ifndef MFS_STATIC_BLOCK_SIZE
149   block_size = 0;
150 #endif
151   zone_shift = 0;
152   extra_space_percent = 0;
153   while ((ch = getopt(argc, argv, "B:b:di:ltvx:z:I:T:")) != EOF)
154 	switch (ch) {
155 #ifndef MFS_STATIC_BLOCK_SIZE
156 	    case 'B':
157 		block_size = strtoul(optarg, &sfx, 0);
158 		switch(*sfx) {
159 		case 'b': case 'B': /* bytes; NetBSD-compatible */
160 		case '\0':	break;
161 		case 'K':
162 		case 'k':	block_size*=1024; break;
163 		case 's': 	block_size*=SECTOR_SIZE; break;
164 		default:	usage();
165 		}
166 		break;
167 #else
168 	    case 'B':
169 		if (block_size != strtoul(optarg, (char **) NULL, 0))
170 			errx(4, "block size must be exactly %d bytes",
171 			    MFS_STATIC_BLOCK_SIZE);
172 		break;
173 		(void)sfx;	/* shut up warnings about unused variable...*/
174 #endif
175 	    case 'I':
176 		fs_offset_bytes = strtoul(optarg, (char **) NULL, 0);
177 		insertmode = 1;
178 		break;
179 	    case 'b':
180 		blocks = bblocks = strtoul(optarg, (char **) NULL, 0);
181 		break;
182 	    case 'T':
183 		Tflag = 1;
184 		current_time = strtoul(optarg, (char **) NULL, 0);
185 		break;
186 	    case 'd':
187 		dflag = 1;
188 		break;
189 	    case 'i':
190 		inodes = strtoul(optarg, (char **) NULL, 0);
191 		break;
192 	    case 'l':	print = 1;	break;
193 	    case 't':	donttest = 1;	break;
194 	    case 'v':	++verbose;	break;
195 	    case 'x':	extra_space_percent = atoi(optarg); break;
196 	    case 'z':	zone_shift = atoi(optarg);	break;
197 	    default:	usage();
198 	}
199 
200   if (argc == optind) usage();
201 
202   /* Get the current time, set it to the mod time of the binary of
203    * mkfs itself when the -d flag is used. The 'current' time is put into
204    * the i_mtimes of all the files.  This -d feature is useful when
205    * producing a set of file systems, and one wants all the times to be
206    * identical. First you set the time of the mkfs binary to what you
207    * want, then go.
208    */
209   if(Tflag) {
210     if(dflag)
211 	errx(1, "-T and -d both specify a time and so are mutually exclusive");
212   } else if(dflag) {
213 	struct stat statbuf;
214 	if (stat(progname, &statbuf)) {
215 		err(1, "stat of itself");
216 	}
217 	current_time = statbuf.st_mtime;
218   } else {
219 	  current_time = time((time_t *) 0);	/* time mkfs is being run */
220   }
221 
222   /* Percentage of extra size must be nonnegative.
223    * It can legitimately be bigger than 100 but has to make some sort of sense.
224    */
225   if(extra_space_percent < 0 || extra_space_percent > 2000) usage();
226 
227 #ifdef DEFAULT_BLOCK_SIZE
228   if(!block_size) block_size = DEFAULT_BLOCK_SIZE;
229 #endif
230   if (block_size % SECTOR_SIZE)
231 	errx(4, "block size must be multiple of sector (%d bytes)", SECTOR_SIZE);
232 #ifdef MIN_BLOCK_SIZE
233   if (block_size < MIN_BLOCK_SIZE)
234 	errx(4, "block size must be at least %d bytes", MIN_BLOCK_SIZE);
235 #endif
236 #ifdef MAX_BLOCK_SIZE
237   if (block_size > MAX_BLOCK_SIZE)
238 	errx(4, "block size must be at most %d bytes", MAX_BLOCK_SIZE);
239 #endif
240   if(block_size%INODE_SIZE)
241 	errx(4, "block size must be a multiple of inode size (%d bytes)", INODE_SIZE);
242 
243   if(zone_shift < 0 || zone_shift > 14)
244 	errx(4, "zone_shift must be a small non-negative integer");
245   zone_per_block = 1 << zone_shift;	/* nr of blocks per zone */
246 
247   inodes_per_block = INODES_PER_BLOCK(block_size);
248   indir_per_block = INDIRECTS(block_size);
249   indir_per_zone = INDIRECTS(block_size) << zone_shift;
250   /* number of file zones we can address directly and with a single indirect*/
251   nr_indirzones = NR_DZONES + indir_per_zone;
252   zone_size = block_size << zone_shift;
253   /* Checks for an overflow: only with very big block size */
254   if (zone_size <= 0)
255 	errx(4, "Zones are too big for this program; smaller -B or -z, please!");
256 
257   /* now that the block size is known, do buffer allocations where
258    * possible.
259    */
260   zero = alloc_block();
261 
262   fs_offset_blocks = roundup(fs_offset_bytes, block_size) / block_size;
263 
264   /* Determine the size of the device if not specified as -b or proto. */
265   maxblocks = sizeup(argv[optind]);
266   if (bblocks != 0 && bblocks + fs_offset_blocks > maxblocks && !insertmode) {
267 	errx(4, "Given size -b %d exceeds device capacity(%d)\n", bblocks, maxblocks);
268   }
269 
270   if (argc - optind == 1 && bblocks == 0) {
271   	blocks = maxblocks;
272   	/* blocks == 0 is checked later, but leads to a funny way of
273   	 * reporting a 0-sized device (displays usage).
274   	 */
275   	if(blocks < 1) {
276   		errx(1, "zero size device.");
277 	}
278   }
279 
280   /* The remaining args must be 'special proto', or just 'special' if the
281    * no. of blocks has already been specified.
282    */
283   if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage();
284 
285   if (maxblocks && blocks > maxblocks && !insertmode) {
286  	errx(1, "%s: number of blocks too large for device.", argv[optind]);
287   }
288 
289   /* Check special. */
290   check_mtab(argv[optind]);
291 
292   /* Check and start processing proto. */
293   optarg = argv[++optind];
294   if (optind < argc && (proto = fopen(optarg, "r")) != NULL) {
295 	/* Prototype file is readable. */
296 	lct = 1;
297 	get_line(line, token);	/* skip boot block info */
298 
299 	/* Read the line with the block and inode counts. */
300 	get_line(line, token);
301 	if (bblocks == 0){
302 		blocks = strtol(token[0], (char **) NULL, 10);
303 	} else {
304 		if(bblocks < strtol(token[0], (char **) NULL, 10)) {
305  			errx(1, "%s: number of blocks given as parameter(%d)"
306 				" is too small for given proto file(%ld).",
307 				argv[optind], bblocks,
308 				strtol(token[0], (char **) NULL, 10));
309 		};
310 	}
311 	inodes = strtol(token[1], (char **) NULL, 10);
312 
313 	/* Process mode line for root directory. */
314 	get_line(line, token);
315 	mode = mode_con(token[0]);
316 	usrid = atoi(token[1]);
317 	grpid = atoi(token[2]);
318 
319 	if(blocks <= 0 && inodes <= 0){
320 		detect_fs_size(&fssize);
321 		blocks = fssize.blockcount;
322 		inodes = fssize.inocount;
323 		blocks += blocks*extra_space_percent/100;
324 		inodes += inodes*extra_space_percent/100;
325 /* XXX is it OK to write on stdout? Use warn() instead? Also consider using verbose */
326 		fprintf(stderr, "dynamically sized filesystem: %u blocks, %u inodes\n",
327 		    (unsigned int) blocks, (unsigned int) inodes);
328 	}
329   } else {
330 	lct = 0;
331 	if (optind < argc) {
332 		/* Maybe the prototype file is just a size.  Check. */
333 		blocks = strtoul(optarg, (char **) NULL, 0);
334 		if (blocks == 0) errx(2, "Can't open prototype file");
335 	}
336 
337 	/* Make simple file system of the given size, using defaults. */
338 	mode = 040777;
339 	usrid = 0;
340 	grpid = 0;
341 	simple = 1;
342   }
343 
344   if (inodes == 0) {
345 	long long kb = ((unsigned long long)blocks*block_size) / 1024;
346 
347 	inodes = kb / 2;
348 	if (kb >= 100000) inodes = kb / 4;
349 	if (kb >= 1000000) inodes = kb / 6;
350 	if (kb >= 10000000) inodes = kb / 8;
351 	if (kb >= 100000000) inodes = kb / 10;
352 	if (kb >= 1000000000) inodes = kb / 12;
353 /* XXX check overflow: with very large number of blocks, this results in insanely large number of inodes */
354 /* XXX check underflow (if/when ino_t is signed), else the message below will look strange */
355 
356 	/* round up to fill inode block */
357 	inodes += inodes_per_block - 1;
358 	inodes = inodes / inodes_per_block * inodes_per_block;
359   }
360 
361   if (blocks < 5) errx(1, "Block count too small");
362   if (inodes < 1) errx(1, "Inode count too small");
363 
364   nrblocks = blocks;
365   nrinodes = inodes;
366 
367   umap_array_elements = 1 + blocks/8;
368   if(!(umap_array = malloc(umap_array_elements)))
369 	err(1, "can't allocate block bitmap (%u bytes).",
370 		(unsigned)umap_array_elements);
371 
372   /* Open special. */
373   special(argv[--optind], insertmode);
374 
375   if (!donttest) {
376 	uint16_t *testb;
377 	ssize_t w;
378 
379 	testb = alloc_block();
380 
381 	/* Try writing the last block of partition or diskette. */
382 	mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET);
383 	testb[0] = 0x3245;
384 	testb[1] = 0x11FF;
385 	testb[block_size/2-1] = 0x1F2F;
386 	w=mkfs_write(testb, block_size);
387 	sync();			/* flush write, so if error next read fails */
388 	mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET);
389 	testb[0] = 0;
390 	testb[1] = 0;
391 	testb[block_size/2-1] = 0;
392 	nread = read(fd, testb, block_size);
393 	if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF ||
394 	    testb[block_size/2-1] != 0x1F2F) {
395 		warn("nread = %d\n", nread);
396 		warnx("testb = 0x%x 0x%x 0x%x\n",
397 		    testb[0], testb[1], testb[block_size-1]);
398 		errx(1, "File system is too big for minor device (read)");
399 	}
400 	mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET);
401 	testb[0] = 0;
402 	testb[1] = 0;
403 	testb[block_size/2-1] = 0;
404 	mkfs_write(testb, block_size);
405 	mkfs_seek(0L, SEEK_SET);
406 	free(testb);
407   }
408 
409   /* Make the file-system */
410 
411   put_block(BOOT_BLOCK, zero);		/* Write a null boot block. */
412   put_block(BOOT_BLOCK+1, zero);	/* Write another null block. */
413 
414   super(nrblocks >> zone_shift, inodes);
415 
416   root_inum = alloc_inode(mode, usrid, grpid);
417   rootdir(root_inum);
418   if (simple == 0) eat_dir(root_inum);
419 
420   if (print) print_fs();
421   else if (verbose > 1) {
422 	  if (zone_shift)
423 		fprintf(stderr, "%d inodes used.     %u zones (%u blocks) used.\n",
424 		    (int)next_inode-1, next_zone, next_zone*zone_per_block);
425 	  else
426 		fprintf(stderr, "%d inodes used.     %u zones used.\n",
427 		    (int)next_inode-1, next_zone);
428   }
429 
430   if(insertmode) printf("%"PRIu64"\n", written_fs_size);
431 
432   return(0);
433 
434   /* NOTREACHED */
435 }				/* end main */
436 
437 /*================================================================
438  *        detect_fs_size  -  determine image size dynamically
439  *===============================================================*/
440 void
441 detect_fs_size(struct fs_size * fssize)
442 {
443   int prev_lct = lct;
444   off_t point = ftell(proto);
445   block_t initb;
446   zone_t initz;
447 
448   fssize->inocount = 1;	/* root directory node */
449   fssize->zonecount = 0;
450   fssize->blockcount = 0;
451 
452   sizeup_dir(fssize);
453 
454   initb = bitmapsize(1 + fssize->inocount, block_size);
455   initb += bitmapsize(fssize->zonecount, block_size);
456   initb += START_BLOCK;
457   initb += (fssize->inocount + inodes_per_block - 1) / inodes_per_block;
458   initz = (initb + zone_per_block - 1) >> zone_shift;
459 
460   fssize->blockcount = initb+ fssize->zonecount;
461   lct = prev_lct;
462   fseek(proto, point, SEEK_SET);
463 }
464 
465 void
466 sizeup_dir(struct fs_size * fssize)
467 {
468   char *token[MAX_TOKENS], *p;
469   char line[LINE_LEN];
470   FILE *f;
471   off_t size;
472   int dir_entries = 2;
473   zone_t dir_zones = 0, fzones, indirects;
474 
475   while (1) {
476 	get_line(line, token);
477 	p = token[0];
478 	if (*p == '$') {
479 		dir_zones = (dir_entries / (NR_DIR_ENTRIES(block_size) * zone_per_block));
480 		if(dir_entries % (NR_DIR_ENTRIES(block_size) * zone_per_block))
481 			dir_zones++;
482 		if(dir_zones > NR_DZONES)
483 			dir_zones++;	/* Max single indir */
484 		fssize->zonecount += dir_zones;
485 		return;
486 	}
487 
488 	p = token[1];
489 	fssize->inocount++;
490 	dir_entries++;
491 
492 	if (*p == 'd') {
493 		sizeup_dir(fssize);
494 	} else if (*p == 'b' || *p == 'c') {
495 
496 	} else if (*p == 's') {
497 		fssize->zonecount++; /* Symlink contents is always stored a block */
498 	} else {
499 		if ((f = fopen(token[4], "rb")) == NULL) {
500 /* on minix natively, allow EACCES and skip the entry.
501  * while crossbuilding, always fail on error.
502  */
503 #ifdef __minix
504 			if(errno == EACCES)
505 				warn("dynamic sizing: can't open %s", token[4]);
506 			else
507 #endif
508 				err(1, "dynamic sizing: can't open %s", token[4]);
509 		} else if (fseek(f, 0, SEEK_END) < 0) {
510 			pexit("dynamic size detection failed: seek to end of %s",
511 			    token[4]);
512 		} else if ( (size = ftell(f)) == (off_t)(-1)) {
513 			pexit("dynamic size detection failed: can't tell size of %s",
514 			    token[4]);
515 		} else {
516 			fclose(f);
517 			fzones = roundup(size, zone_size) / zone_size;
518 			indirects = 0;
519 			/* XXX overflow? fzones is u32, size is potentially 64-bit */
520 			if (fzones > NR_DZONES)
521 				indirects++; /* single indirect needed */
522 			if (fzones > nr_indirzones) {
523 				/* Each further group of 'indir_per_zone'
524 				 * needs one supplementary indirect zone:
525 				 */
526 				indirects += roundup(fzones - nr_indirzones,
527 				    indir_per_zone) / indir_per_zone;
528 				indirects++;	/* + double indirect needed!*/
529 			}
530 			fssize->zonecount += fzones + indirects;
531 		}
532 	}
533   }
534 }
535 
536 /*================================================================
537  *                    sizeup  -  determine device size
538  *===============================================================*/
539 block_t
540 sizeup(char * device)
541 {
542   block_t d;
543 #if defined(__minix)
544   uint64_t bytes, resize;
545   uint32_t rem;
546 #else
547   off_t size;
548 #endif
549 
550 
551   if ((fd = open(device, O_RDONLY)) == -1) {
552 	if (errno != ENOENT)
553 		perror("sizeup open");
554 	return 0;
555   }
556 
557 #if defined(__minix)
558   if(minix_sizeup(device, &bytes) < 0) {
559        perror("sizeup");
560        return 0;
561   }
562 
563   d = (uint32_t)(bytes / block_size);
564   rem = (uint32_t)(bytes % block_size);
565 
566   resize = ((uint64_t)d * block_size) + rem;
567   if(resize != bytes) {
568 	/* Assume block_t is unsigned */
569 	d = (block_t)(-1ul);
570 	fprintf(stderr, "%s: truncating FS at %lu blocks\n",
571 		progname, (unsigned long)d);
572   }
573 #else
574   size = mkfs_seek(0, SEEK_END);
575   /* Assume block_t is unsigned */
576   if (size / block_size > (block_t)(-1ul)) {
577 	d = (block_t)(-1ul);
578 	fprintf(stderr, "%s: truncating FS at %lu blocks\n",
579 		progname, (unsigned long)d);
580   } else
581 	d = size / block_size;
582 #endif
583 
584   return d;
585 }
586 
587 /*
588  * copied from fslib
589  */
590 static int
591 bitmapsize(bit_t nr_bits, size_t blk_size)
592 {
593   block_t nr_blocks;
594 
595   nr_blocks = nr_bits / FS_BITS_PER_BLOCK(blk_size);
596   if (nr_blocks * FS_BITS_PER_BLOCK(blk_size) < nr_bits)
597 	++nr_blocks;
598   return(nr_blocks);
599 }
600 
601 /*================================================================
602  *                 super  -  construct a superblock
603  *===============================================================*/
604 
605 void
606 super(zone_t zones, ino_t inodes)
607 {
608   block_t inodeblks, initblks, i;
609   unsigned long nb;
610   long long ind_per_zone, zo;
611   void *buf;
612   struct super_block *sup;
613 
614   sup = buf = alloc_block();
615 
616 #ifdef MFSFLAG_CLEAN
617   /* The assumption is that mkfs will create a clean FS. */
618   sup->s_flags = MFSFLAG_CLEAN;
619 #endif
620 
621   sup->s_ninodes = inodes;
622   /* Check for overflow; cannot happen on V3 file systems */
623   if(inodes != sup->s_ninodes)
624 	errx(1, "Too much inodes for that version of Minix FS.");
625   sup->s_nzones = 0;	/* not used in V2 - 0 forces errors early */
626   sup->s_zones = zones;
627   /* Check for overflow; can only happen on V1 file systems */
628   if(zones != sup->s_zones)
629 	errx(1, "Too much zones (blocks) for that version of Minix FS.");
630 
631 #ifndef MFS_STATIC_BLOCK_SIZE
632 #define BIGGERBLOCKS "Please try a larger block size for an FS of this size."
633 #else
634 #define BIGGERBLOCKS "Please use MinixFS V3 for an FS of this size."
635 #endif
636   sup->s_imap_blocks = nb = bitmapsize(1 + inodes, block_size);
637   /* Checks for an overflow: nb is uint32_t while s_imap_blocks is of type
638    * int16_t */
639   if(sup->s_imap_blocks != nb) {
640 	errx(1, "too many inode bitmap blocks.\n" BIGGERBLOCKS);
641   }
642   sup->s_zmap_blocks = nb = bitmapsize(zones, block_size);
643   /* Idem here check for overflow */
644   if(nb != sup->s_zmap_blocks) {
645 	errx(1, "too many block bitmap blocks.\n" BIGGERBLOCKS);
646   }
647   inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks;
648   inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block;
649   initblks = inode_offset + inodeblks;
650   sup->s_firstdatazone_old = nb =
651 	(initblks + (1 << zone_shift) - 1) >> zone_shift;
652   if(nb >= zones) errx(1, "bit maps too large");
653   if(nb != sup->s_firstdatazone_old) {
654 	/* The field is too small to store the value. Fortunately, the value
655 	 * can be computed from other fields. We set the on-disk field to zero
656 	 * to indicate that it must not be used. Eventually, we can always set
657 	 * the on-disk field to zero, and stop using it.
658 	 */
659 	sup->s_firstdatazone_old = 0;
660   }
661   sup->s_firstdatazone = nb;
662   zoff = sup->s_firstdatazone - 1;
663   sup->s_log_zone_size = zone_shift;
664   sup->s_magic = SUPER_MAGIC;
665 #ifdef MFS_SUPER_BLOCK_SIZE
666   sup->s_block_size = block_size;
667   /* Check for overflow */
668   if(block_size != sup->MFS_SUPER_BLOCK_SIZE)
669 	errx(1, "block_size too large.");
670   sup->s_disk_version = 0;
671 #endif
672 
673   ind_per_zone = (long long) indir_per_zone;
674   zo = NR_DZONES + ind_per_zone + ind_per_zone*ind_per_zone;
675 #ifndef MAX_MAX_SIZE
676 #define MAX_MAX_SIZE 	(INT32_MAX)
677 #endif
678   if(MAX_MAX_SIZE/block_size < zo) {
679 	sup->s_max_size = MAX_MAX_SIZE;
680   }
681   else {
682 	sup->s_max_size = zo * block_size;
683   }
684 
685   if (verbose>1) {
686 	fprintf(stderr, "Super block values:\n"
687 	    "\tnumber of inodes\t%12d\n"
688 	    "\tnumber of zones \t%12d\n"
689 	    "\tinode bit map blocks\t%12d\n"
690 	    "\tzone bit map blocks\t%12d\n"
691 	    "\tfirst data zone \t%12d\n"
692 	    "\tblocks per zone shift\t%12d\n"
693 	    "\tmaximum file size\t%12d\n"
694 	    "\tmagic number\t\t%#12X\n",
695 	    sup->s_ninodes, sup->s_zones,
696 	    sup->s_imap_blocks, sup->s_zmap_blocks, sup->s_firstdatazone,
697 	    sup->s_log_zone_size, sup->s_max_size, sup->s_magic);
698 #ifdef MFS_SUPER_BLOCK_SIZE
699 	fprintf(stderr, "\tblock size\t\t%12d\n", sup->s_block_size);
700 #endif
701   }
702 
703   mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET);
704   mkfs_write(buf, SUPER_BLOCK_BYTES);
705 
706   /* Clear maps and inodes. */
707   for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero);
708 
709   next_zone = sup->s_firstdatazone;
710   next_inode = 1;
711 
712   zone_map = INODE_MAP + sup->s_imap_blocks;
713 
714   insert_bit(zone_map, 0);	/* bit zero must always be allocated */
715   insert_bit((block_t) INODE_MAP, 0);	/* inode zero not used but
716 					 * must be allocated */
717 
718   free(buf);
719 }
720 
721 
722 /*================================================================
723  *              rootdir  -  install the root directory
724  *===============================================================*/
725 void
726 rootdir(ino_t inode)
727 {
728   zone_t z;
729 
730   z = alloc_zone();
731   add_zone(inode, z, 2 * sizeof(struct direct), current_time);
732   enter_dir(inode, ".", inode);
733   enter_dir(inode, "..", inode);
734   incr_link(inode);
735   incr_link(inode);
736 }
737 
738 void
739 enter_symlink(ino_t inode, char *lnk)
740 {
741   zone_t z;
742   size_t len;
743   char *buf;
744 
745   buf = alloc_block();
746   z = alloc_zone();
747   len = strlen(lnk);
748   if (len >= block_size)
749 	pexit("symlink too long, max length is %u", (unsigned)block_size - 1);
750   strcpy(buf, lnk);
751   put_block((z << zone_shift), buf);
752 
753   add_zone(inode, z, len, current_time);
754 
755   free(buf);
756 }
757 
758 
759 /*================================================================
760  *	    eat_dir  -  recursively install directory
761  *===============================================================*/
762 void
763 eat_dir(ino_t parent)
764 {
765   /* Read prototype lines and set up directory. Recurse if need be. */
766   char *token[MAX_TOKENS], *p;
767   char line[LINE_LEN];
768   int mode, usrid, grpid, maj, min, f;
769   ino_t n;
770   zone_t z;
771   size_t size;
772 
773   while (1) {
774 	get_line(line, token);
775 	p = token[0];
776 	if (*p == '$') return;
777 	p = token[1];
778 	mode = mode_con(p);
779 	usrid = atoi(token[2]);
780 	grpid = atoi(token[3]);
781 	n = alloc_inode(mode, usrid, grpid);
782 
783 	/* Enter name in directory and update directory's size. */
784 	enter_dir(parent, token[0], n);
785 	incr_size(parent, sizeof(struct direct));
786 
787 	/* Check to see if file is directory or special. */
788 	incr_link(n);
789 	if (*p == 'd') {
790 		/* This is a directory. */
791 		z = alloc_zone();	/* zone for new directory */
792 		add_zone(n, z, 2 * sizeof(struct direct), current_time);
793 		enter_dir(n, ".", n);
794 		enter_dir(n, "..", parent);
795 		incr_link(parent);
796 		incr_link(n);
797 		eat_dir(n);
798 	} else if (*p == 'b' || *p == 'c') {
799 		/* Special file. */
800 		maj = atoi(token[4]);
801 		min = atoi(token[5]);
802 		size = 0;
803 		if (token[6]) size = atoi(token[6]);
804 		size = block_size * size;
805 		add_zone(n, (zone_t) (makedev(maj,min)), size, current_time);
806 	} else if (*p == 's') {
807 		enter_symlink(n, token[4]);
808 	} else {
809 		/* Regular file. Go read it. */
810 		if ((f = open(token[4], O_RDONLY)) < 0) {
811 /* on minix natively, allow EACCES and skip the entry.
812  * while crossbuilding, always fail on error.
813  */
814 #ifdef __minix
815 			if(errno == EACCES)
816 				warn("Can't open %s", token[4]);
817 			else
818 #endif
819 				err(1, "Can't open %s", token[4]);
820 		} else {
821 			eat_file(n, f);
822 		}
823 	}
824   }
825 
826 }
827 
828 /*================================================================
829  * 		eat_file  -  copy file to MINIX
830  *===============================================================*/
831 /* Zonesize >= blocksize */
832 void
833 eat_file(ino_t inode, int f)
834 {
835   int ct = 0, i, j;
836   zone_t z = 0;
837   char *buf;
838   time_t timeval;
839 
840   buf = alloc_block();
841 
842   do {
843 	for (i = 0, j = 0; i < zone_per_block; i++, j += ct) {
844 		memset(buf, 0, block_size);
845 		if ((ct = read(f, buf, block_size)) > 0) {
846 			if (i == 0) z = alloc_zone();
847 			put_block((z << zone_shift) + i, buf);
848 		}
849 	}
850 	timeval = (dflag ? current_time : file_time(f));
851 	if (ct) add_zone(inode, z, (size_t) j, timeval);
852   } while (ct == block_size);
853   close(f);
854   free(buf);
855 }
856 
857 int
858 dir_try_enter(zone_t z, ino_t child, char const *name)
859 {
860   struct direct *dir_entry = alloc_block();
861   int r = 0;
862   block_t b;
863   int i, l;
864 
865   b = z << zone_shift;
866   for (l = 0; l < zone_per_block; l++, b++) {
867 	get_block(b, dir_entry);
868 
869 	for (i = 0; i < NR_DIR_ENTRIES(block_size); i++)
870 		if (!dir_entry[i].d_ino)
871 			break;
872 
873 	if(i < NR_DIR_ENTRIES(block_size)) {
874 		r = 1;
875 		dir_entry[i].d_ino = child;
876 		assert(sizeof(dir_entry[i].d_name) == MFS_DIRSIZ);
877 		if (verbose && strlen(name) > MFS_DIRSIZ)
878 			fprintf(stderr, "File name %s is too long, truncated\n", name);
879 		strncpy(dir_entry[i].d_name, name, MFS_DIRSIZ);
880 		put_block(b, dir_entry);
881 		break;
882 	}
883   }
884 
885   free(dir_entry);
886 
887   return r;
888 }
889 
890 /*================================================================
891  *	    directory & inode management assist group
892  *===============================================================*/
893 void
894 enter_dir(ino_t parent, char const *name, ino_t child)
895 {
896   /* Enter child in parent directory */
897   /* Works for dir > 1 block and zone > block */
898   unsigned int k;
899   block_t b, indir;
900   zone_t z;
901   int off;
902   struct inode *ino;
903   struct inode *inoblock = alloc_block();
904   zone_t *indirblock = alloc_block();
905 
906   assert(!(block_size % sizeof(struct direct)));
907 
908   /* Obtain the inode structure */
909   b = ((parent - 1) / inodes_per_block) + inode_offset;
910   off = (parent - 1) % inodes_per_block;
911   get_block(b, inoblock);
912   ino = inoblock + off;
913 
914   for (k = 0; k < NR_DZONES; k++) {
915 	z = ino->i_zone[k];
916 	if (z == 0) {
917 		z = alloc_zone();
918 		ino->i_zone[k] = z;
919 	}
920 
921 	if(dir_try_enter(z, child, __UNCONST(name))) {
922 		put_block(b, inoblock);
923 		free(inoblock);
924 		free(indirblock);
925 		return;
926 	}
927   }
928 
929   /* no space in directory using just direct blocks; try indirect */
930   if (ino->i_zone[S_INDIRECT_IDX] == 0)
931   	ino->i_zone[S_INDIRECT_IDX] = alloc_zone();
932 
933   indir = ino->i_zone[S_INDIRECT_IDX] << zone_shift;
934   --indir; /* Compensate for ++indir below */
935   for(k = 0; k < (indir_per_zone); k++) {
936 	if (k % indir_per_block == 0)
937 		get_block(++indir, indirblock);
938   	z = indirblock[k % indir_per_block];
939 	if(!z) {
940 		z = indirblock[k % indir_per_block] = alloc_zone();
941 		put_block(indir, indirblock);
942 	}
943 	if(dir_try_enter(z, child, __UNCONST(name))) {
944 		put_block(b, inoblock);
945 		free(inoblock);
946 		free(indirblock);
947 		return;
948 	}
949   }
950 
951   pexit("Directory-inode %u beyond single indirect blocks.  Could not enter %s",
952          (unsigned)parent, name);
953 }
954 
955 
956 void
957 add_zone(ino_t n, zone_t z, size_t bytes, time_t mtime)
958 {
959   /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
960 
961   int off, i, j;
962   block_t b;
963   zone_t indir, dindir;
964   struct inode *p, *inode;
965   zone_t *blk, *dblk;
966 
967   assert(inodes_per_block*sizeof(*inode) == block_size);
968   if(!(inode = alloc_block()))
969   	err(1, "Couldn't allocate block of inodes");
970 
971   b = ((n - 1) / inodes_per_block) + inode_offset;
972   off = (n - 1) % inodes_per_block;
973   get_block(b, inode);
974   p = &inode[off];
975   p->i_size += bytes;
976   p->i_mtime = mtime;
977 #ifndef MFS_INODE_ONLY_MTIME /* V1 file systems did not have them... */
978   p->i_atime = p->i_ctime = current_time;
979 #endif
980   for (i = 0; i < NR_DZONES; i++)
981 	if (p->i_zone[i] == 0) {
982 		p->i_zone[i] = z;
983 		put_block(b, inode);
984   		free(inode);
985 		return;
986 	}
987 
988   assert(indir_per_block*sizeof(*blk) == block_size);
989   if(!(blk = alloc_block()))
990   	err(1, "Couldn't allocate indirect block");
991 
992   /* File has grown beyond a small file. */
993   if (p->i_zone[S_INDIRECT_IDX] == 0)
994 	p->i_zone[S_INDIRECT_IDX] = alloc_zone();
995   indir = p->i_zone[S_INDIRECT_IDX] << zone_shift;
996   put_block(b, inode);
997   --indir; /* Compensate for ++indir below */
998   for (i = 0; i < (indir_per_zone); i++) {
999 	if (i % indir_per_block == 0)
1000 		get_block(++indir, blk);
1001 	if (blk[i % indir_per_block] == 0) {
1002 		blk[i] = z;
1003 		put_block(indir, blk);
1004   		free(blk);
1005   		free(inode);
1006 		return;
1007 	}
1008   }
1009 
1010   /* File has grown beyond single indirect; we need a double indirect */
1011   assert(indir_per_block*sizeof(*dblk) == block_size);
1012   if(!(dblk = alloc_block()))
1013   	err(1, "Couldn't allocate double indirect block");
1014 
1015   if (p->i_zone[D_INDIRECT_IDX] == 0)
1016 	p->i_zone[D_INDIRECT_IDX] = alloc_zone();
1017   dindir = p->i_zone[D_INDIRECT_IDX] << zone_shift;
1018   put_block(b, inode);
1019   --dindir; /* Compensate for ++indir below */
1020   for (j = 0; j < (indir_per_zone); j++) {
1021 	if (j % indir_per_block == 0)
1022 		get_block(++dindir, dblk);
1023 	if (dblk[j % indir_per_block] == 0)
1024 		dblk[j % indir_per_block] = alloc_zone();
1025 	indir = dblk[j % indir_per_block] << zone_shift;
1026 	--indir; /* Compensate for ++indir below */
1027 	for (i = 0; i < (indir_per_zone); i++) {
1028 		if (i % indir_per_block == 0)
1029 			get_block(++indir, blk);
1030 		if (blk[i % indir_per_block] == 0) {
1031 			blk[i] = z;
1032 			put_block(dindir, dblk);
1033 			put_block(indir, blk);
1034 	  		free(dblk);
1035 	  		free(blk);
1036 	  		free(inode);
1037 			return;
1038 		}
1039 	}
1040   }
1041 
1042   pexit("File has grown beyond double indirect");
1043 }
1044 
1045 
1046 /* Increment the link count to inode n */
1047 void
1048 incr_link(ino_t n)
1049 {
1050   int off;
1051   static int enter = 0;
1052   static struct inode *inodes = NULL;
1053   block_t b;
1054 
1055   if (enter++) pexit("internal error: recursive call to incr_link()");
1056 
1057   b = ((n - 1) / inodes_per_block) + inode_offset;
1058   off = (n - 1) % inodes_per_block;
1059   {
1060 	assert(sizeof(*inodes) * inodes_per_block == block_size);
1061 	if(!inodes && !(inodes = alloc_block()))
1062 		err(1, "couldn't allocate a block of inodes");
1063 
1064 	get_block(b, inodes);
1065 	inodes[off].i_nlinks++;
1066 	/* Check overflow (particularly on V1)... */
1067 	if (inodes[off].i_nlinks <= 0)
1068 		pexit("Too many links to a directory");
1069 	put_block(b, inodes);
1070   }
1071   enter = 0;
1072 }
1073 
1074 
1075 /* Increment the file-size in inode n */
1076 void
1077 incr_size(ino_t n, size_t count)
1078 {
1079   block_t b;
1080   int off;
1081 
1082   b = ((n - 1) / inodes_per_block) + inode_offset;
1083   off = (n - 1) % inodes_per_block;
1084   {
1085 	struct inode *inodes;
1086 
1087 	assert(inodes_per_block * sizeof(*inodes) == block_size);
1088 	if(!(inodes = alloc_block()))
1089 		err(1, "couldn't allocate a block of inodes");
1090 
1091 	get_block(b, inodes);
1092 	/* Check overflow; avoid compiler spurious warnings */
1093 	if (inodes[off].i_size+(int)count < inodes[off].i_size ||
1094 	    inodes[off].i_size > MAX_MAX_SIZE-(int)count)
1095 		pexit("File has become too big to be handled by MFS");
1096 	inodes[off].i_size += count;
1097 	put_block(b, inodes);
1098 	free(inodes);
1099   }
1100 }
1101 
1102 
1103 /*================================================================
1104  * 	 	     allocation assist group
1105  *===============================================================*/
1106 static ino_t
1107 alloc_inode(int mode, int usrid, int grpid)
1108 {
1109   ino_t num;
1110   int off;
1111   block_t b;
1112   struct inode *inodes;
1113 
1114   num = next_inode++;
1115   if (num > nrinodes) {
1116   	pexit("File system does not have enough inodes (only %llu)", nrinodes);
1117   }
1118   b = ((num - 1) / inodes_per_block) + inode_offset;
1119   off = (num - 1) % inodes_per_block;
1120 
1121   assert(inodes_per_block * sizeof(*inodes) == block_size);
1122   if(!(inodes = alloc_block()))
1123 	err(1, "couldn't allocate a block of inodes");
1124 
1125   get_block(b, inodes);
1126   if (inodes[off].i_mode) {
1127 	pexit("allocation new inode %llu with non-zero mode - this cannot happen",
1128 		num);
1129   }
1130   inodes[off].i_mode = mode;
1131   inodes[off].i_uid = usrid;
1132   inodes[off].i_gid = grpid;
1133   if (verbose && (inodes[off].i_uid != usrid || inodes[off].i_gid != grpid))
1134 	fprintf(stderr, "Uid/gid %d.%d do not fit within inode, truncated\n", usrid, grpid);
1135   put_block(b, inodes);
1136 
1137   free(inodes);
1138 
1139   /* Set the bit in the bit map. */
1140   insert_bit((block_t) INODE_MAP, num);
1141   return(num);
1142 }
1143 
1144 
1145 /* Allocate a new zone */
1146 static zone_t
1147 alloc_zone(void)
1148 {
1149   /* Works for zone > block */
1150   block_t b;
1151   int i;
1152   zone_t z;
1153 
1154   z = next_zone++;
1155   b = z << zone_shift;
1156   if (b > nrblocks - zone_per_block)
1157 	pexit("File system not big enough for all the files");
1158   for (i = 0; i < zone_per_block; i++)
1159 	put_block(b + i, zero);	/* give an empty zone */
1160 
1161   insert_bit(zone_map, z - zoff);
1162   return z;
1163 }
1164 
1165 
1166 /* Insert one bit into the bitmap */
1167 void
1168 insert_bit(block_t map, bit_t bit)
1169 {
1170   int boff, w, s;
1171   unsigned int bits_per_block;
1172   block_t map_block;
1173   bitchunk_t *buf;
1174 
1175   buf = alloc_block();
1176 
1177   bits_per_block = FS_BITS_PER_BLOCK(block_size);
1178   map_block = map + bit / bits_per_block;
1179   if (map_block >= inode_offset)
1180 	pexit("insertbit invades inodes area - this cannot happen");
1181   boff = bit % bits_per_block;
1182 
1183   assert(boff >=0);
1184   assert(boff < FS_BITS_PER_BLOCK(block_size));
1185   get_block(map_block, buf);
1186   w = boff / FS_BITCHUNK_BITS;
1187   s = boff % FS_BITCHUNK_BITS;
1188   buf[w] |= (1 << s);
1189   put_block(map_block, buf);
1190 
1191   free(buf);
1192 }
1193 
1194 
1195 /*================================================================
1196  * 		proto-file processing assist group
1197  *===============================================================*/
1198 int mode_con(char *p)
1199 {
1200   /* Convert string to mode */
1201   int o1, o2, o3, mode;
1202   char c1, c2, c3;
1203 
1204   c1 = *p++;
1205   c2 = *p++;
1206   c3 = *p++;
1207   o1 = *p++ - '0';
1208   o2 = *p++ - '0';
1209   o3 = *p++ - '0';
1210   mode = (o1 << 6) | (o2 << 3) | o3;
1211   if (c1 == 'd') mode |= S_IFDIR;
1212   if (c1 == 'b') mode |= S_IFBLK;
1213   if (c1 == 'c') mode |= S_IFCHR;
1214   if (c1 == 's') mode |= S_IFLNK;
1215   if (c1 == 'l') mode |= S_IFLNK;	/* just to be somewhat ls-compatible*/
1216 /* XXX note: some other mkfs programs consider L to create hardlinks */
1217   if (c1 == '-') mode |= S_IFREG;
1218   if (c2 == 'u') mode |= S_ISUID;
1219   if (c3 == 'g') mode |= S_ISGID;
1220 /* XXX There are no way to encode S_ISVTX */
1221   return(mode);
1222 }
1223 
1224 void
1225 get_line(char line[LINE_LEN], char *parse[MAX_TOKENS])
1226 {
1227   /* Read a line and break it up in tokens */
1228   int k;
1229   char c, *p;
1230   int d;
1231 
1232   for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0;
1233   memset(line, 0, LINE_LEN);
1234   k = 0;
1235   p = line;
1236   while (1) {
1237 	if (++k > LINE_LEN) pexit("Line too long");
1238 	d = fgetc(proto);
1239 	if (d == EOF) pexit("Unexpected end-of-file");
1240 	*p = d;
1241 	if (*p == ' ' || *p == '\t') *p = 0;
1242 	if (*p == '\n') {
1243 		lct++;
1244 		*p++ = 0;
1245 		*p = '\n';
1246 		break;
1247 	}
1248 	p++;
1249   }
1250 
1251   k = 0;
1252   p = line;
1253   while (1) {
1254 	c = *p++;
1255 	if (c == '\n') return;
1256 	if (c == 0) continue;
1257 	parse[k++] = p - 1;
1258 	do {
1259 		c = *p++;
1260 	} while (c != 0 && c != '\n');
1261   }
1262 }
1263 
1264 
1265 /*================================================================
1266  *			other stuff
1267  *===============================================================*/
1268 
1269 /*
1270  * Check to see if the special file named 'device' is mounted.
1271  */
1272 void
1273 check_mtab(const char *device)		/* /dev/hd1 or whatever */
1274 {
1275 #if defined(__minix)
1276   int n, r;
1277   struct stat sb;
1278   char dev[PATH_MAX], mount_point[PATH_MAX],
1279 	type[MNTNAMELEN], flags[MNTFLAGLEN];
1280 
1281   r= stat(device, &sb);
1282   if (r == -1)
1283   {
1284 	if (errno == ENOENT)
1285 		return;	/* Does not exist, and therefore not mounted. */
1286 	err(1, "stat %s failed", device);
1287   }
1288   if (!S_ISBLK(sb.st_mode))
1289   {
1290 	/* Not a block device and therefore not mounted. */
1291 	return;
1292   }
1293 
1294   if (load_mtab(__UNCONST("mkfs")) < 0) return;
1295   while (1) {
1296 	n = get_mtab_entry(dev, mount_point, type, flags);
1297 	if (n < 0) return;
1298 	if (strcmp(device, dev) == 0) {
1299 		/* Can't mkfs on top of a mounted file system. */
1300 		errx(1, "%s is mounted on %s", device, mount_point);
1301 	}
1302   }
1303 #else
1304 	(void) device;	/* shut up warnings about unused variable... */
1305 #endif
1306 }
1307 
1308 
1309 time_t
1310 file_time(int f)
1311 {
1312   struct stat statbuf;
1313 
1314   if (!fstat(f, &statbuf))
1315 	return current_time;
1316   if (statbuf.st_mtime<0 || statbuf.st_mtime>(uint32_t)(-1))
1317 	return current_time;
1318   return(statbuf.st_mtime);
1319 }
1320 
1321 
1322 __dead void
1323 pexit(char const * s, ...)
1324 {
1325   va_list va;
1326 
1327   va_start(va, s);
1328   vwarn(s, va);
1329   va_end(va);
1330   if (lct != 0)
1331 	warnx("Line %d being processed when error detected.\n", lct);
1332   exit(2);
1333 }
1334 
1335 
1336 void *
1337 alloc_block(void)
1338 {
1339 	void *buf;
1340 
1341 	if(!(buf = malloc(block_size))) {
1342 		err(1, "couldn't allocate filesystem buffer");
1343 	}
1344 	memset(buf, 0, block_size);
1345 
1346 	return buf;
1347 }
1348 
1349 void
1350 print_fs(void)
1351 {
1352   int i, j;
1353   ino_t k;
1354   struct inode *inode2;
1355   unsigned short *usbuf;
1356   block_t b;
1357   struct direct *dir;
1358 
1359   assert(inodes_per_block * sizeof(*inode2) == block_size);
1360   if(!(inode2 = alloc_block()))
1361 	err(1, "couldn't allocate a block of inodes");
1362 
1363   assert(NR_DIR_ENTRIES(block_size)*sizeof(*dir) == block_size);
1364   if(!(dir = alloc_block()))
1365 	err(1, "couldn't allocate a block of directory entries");
1366 
1367   usbuf = alloc_block();
1368   get_super_block(usbuf);
1369   printf("\nSuperblock: ");
1370   for (i = 0; i < 8; i++) printf("%06ho ", usbuf[i]);
1371   printf("\n            ");
1372   for (i = 0; i < 8; i++) printf("%#04hX ", usbuf[i]);
1373   printf("\n            ");
1374   for (i = 8; i < 15; i++) printf("%06ho ", usbuf[i]);
1375   printf("\n            ");
1376   for (i = 8; i < 15; i++) printf("%#04hX ", usbuf[i]);
1377   get_block((block_t) INODE_MAP, usbuf);
1378   printf("...\nInode map:  ");
1379   for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]);
1380   get_block((block_t) zone_map, usbuf);
1381   printf("...\nZone  map:  ");
1382   for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]);
1383   printf("...\n");
1384 
1385   free(usbuf);
1386   usbuf = NULL;
1387 
1388   k = 0;
1389   for (b = inode_offset; k < nrinodes; b++) {
1390 	get_block(b, inode2);
1391 	for (i = 0; i < inodes_per_block; i++) {
1392 		k = inodes_per_block * (int) (b - inode_offset) + i + 1;
1393 		/* Lint but OK */
1394 		if (k > nrinodes) break;
1395 		{
1396 			if (inode2[i].i_mode != 0) {
1397 				printf("Inode %3u:  mode=", (unsigned)k);
1398 				printf("%06o", (unsigned)inode2[i].i_mode);
1399 				printf("  uid=%2d  gid=%2d  size=",
1400 					(int)inode2[i].i_uid, (int)inode2[i].i_gid);
1401 				printf("%6ld", (long)inode2[i].i_size);
1402 				printf("  zone[0]=%u\n", (unsigned)inode2[i].i_zone[0]);
1403 			}
1404 			if ((inode2[i].i_mode & S_IFMT) == S_IFDIR) {
1405 				/* This is a directory */
1406 				get_block(inode2[i].i_zone[0] << zone_shift, dir);
1407 				for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1408 					if (dir[j].d_ino)
1409 						printf("\tInode %2u: %s\n",
1410 							(unsigned)dir[j].d_ino,
1411 							dir[j].d_name);
1412 			}
1413 		}
1414 	}
1415   }
1416 
1417   if (zone_shift)
1418 	printf("%d inodes used.     %u zones (%u blocks) used.\n",
1419 		(int)next_inode-1, next_zone, next_zone*zone_per_block);
1420   else
1421 	printf("%d inodes used.     %u zones used.\n", (int)next_inode-1, next_zone);
1422   free(dir);
1423   free(inode2);
1424 }
1425 
1426 
1427 /*
1428  * The first time a block is read, it returns all 0s, unless there has
1429  * been a write.  This routine checks to see if a block has been accessed.
1430  */
1431 int
1432 read_and_set(block_t n)
1433 {
1434   int w, s, mask, r;
1435 
1436   w = n / 8;
1437 
1438   assert(n < nrblocks);
1439   if(w >= umap_array_elements) {
1440 	errx(1, "umap array too small - this can't happen");
1441   }
1442   s = n % 8;
1443   mask = 1 << s;
1444   r = (umap_array[w] & mask ? 1 : 0);
1445   umap_array[w] |= mask;
1446   return(r);
1447 }
1448 
1449 __dead void
1450 usage(void)
1451 {
1452   fprintf(stderr, "Usage: %s [-dltv] [-b blocks] [-i inodes]\n"
1453 	"\t[-z zone_shift] [-I offset] [-x extra] [-B blocksize] special [proto]\n",
1454       progname);
1455   exit(4);
1456 }
1457 
1458 void
1459 special(char * string, int insertmode)
1460 {
1461   int openmode = O_RDWR;
1462   if(!insertmode) openmode |= O_TRUNC;
1463   fd = open(string, O_RDWR | O_CREAT, 0644);
1464   if (fd < 0) err(1, "Can't open special file %s", string);
1465   mkfs_seek(0, SEEK_SET);
1466 }
1467 
1468 
1469 
1470 /* Read a block. */
1471 void
1472 get_block(block_t n, void *buf)
1473 {
1474   ssize_t k;
1475 
1476   /* First access returns a zero block */
1477   if (read_and_set(n) == 0) {
1478 	memcpy(buf, zero, block_size);
1479 	return;
1480   }
1481   mkfs_seek((uint64_t)(n) * block_size, SEEK_SET);
1482   k = read(fd, buf, block_size);
1483   if (k != block_size)
1484 	pexit("get_block couldn't read block #%u", (unsigned)n);
1485 }
1486 
1487 /* Read the super block. */
1488 void
1489 get_super_block(void *buf)
1490 {
1491   ssize_t k;
1492 
1493   mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET);
1494   k = read(fd, buf, SUPER_BLOCK_BYTES);
1495   if (k != SUPER_BLOCK_BYTES)
1496 	err(1, "get_super_block couldn't read super block");
1497 }
1498 
1499 /* Write a block. */
1500 void
1501 put_block(block_t n, void *buf)
1502 {
1503 
1504   (void) read_and_set(n);
1505 
1506   mkfs_seek((uint64_t)(n) * block_size, SEEK_SET);
1507   mkfs_write(buf, block_size);
1508 }
1509 
1510 static ssize_t
1511 mkfs_write(void * buf, size_t count)
1512 {
1513 	uint64_t fssize;
1514 	ssize_t w;
1515 
1516 	/* Perform & check write */
1517 	w = write(fd, buf, count);
1518 	if(w < 0)
1519 		err(1, "mkfs_write: write failed");
1520 	if(w != count)
1521 		errx(1, "mkfs_write: short write: %zd != %zu", w, count);
1522 
1523 	/* Check if this has made the FS any bigger; count bytes after offset */
1524 	fssize = mkfs_seek(0, SEEK_CUR);
1525 
1526 	assert(fssize >= fs_offset_bytes);
1527 	fssize -= fs_offset_bytes;
1528 	fssize = roundup(fssize, block_size);
1529 	if(fssize > written_fs_size)
1530 		written_fs_size = fssize;
1531 
1532 	return w;
1533 }
1534 
1535 /* Seek to position in FS we're creating. */
1536 static uint64_t
1537 mkfs_seek(uint64_t pos, int whence)
1538 {
1539 	if(whence == SEEK_SET) pos += fs_offset_bytes;
1540 	off_t newpos;
1541 	if((newpos=lseek(fd, pos, whence)) == (off_t) -1)
1542 		err(1, "mkfs_seek: lseek failed");
1543 	return newpos;
1544 }
1545