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