port of netbsd's tr
[minix.git] / commands / simple / origmkfs.c
blob7d70f74432b73834be829f5f5890391bb2391c06
1 /* mkfs - make the MINIX filesystem Authors: Tanenbaum et al. */
3 /* Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans
5 * This program can make both version 1 and version 2 file systems, as follows:
6 * mkfs /dev/fd0 1200 # Version 2 (default)
7 * mkfs -1 /dev/fd0 360 # Version 1
9 */
11 #include <sys/types.h>
12 #include <sys/dir.h>
13 #include <sys/stat.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <dirent.h>
20 #include <string.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <minix/config.h>
24 #include <minix/const.h>
25 #include <minix/type.h>
26 #include <minix/minlib.h>
27 #include "../../servers/fs/const.h"
28 #if (MACHINE == IBM_PC)
29 #include <minix/partition.h>
30 #include <minix/u64.h>
31 #include <sys/ioctl.h>
32 #endif
34 #undef EXTERN
35 #define EXTERN /* get rid of EXTERN by making it null */
36 #include "../../servers/fs/type.h"
37 #include "../../servers/fs/super.h"
38 #include <minix/fslib.h>
40 #ifndef DOS
41 #ifndef UNIX
42 #define UNIX
43 #endif
44 #endif
46 #undef BLOCK_SIZE
47 #define BLOCK_SIZE 1024
49 #define INODE_MAP 2
50 #define MAX_TOKENS 10
51 #define LINE_LEN 200
52 #define BIN 2
53 #define BINGRP 2
54 #define BIT_MAP_SHIFT 13
55 #define N_BLOCKS (1024L * 1024)
56 #define N_BLOCKS16 (128L * 1024)
57 #define INODE_MAX ((unsigned) 65535)
59 /* You can make a really large file system on a 16-bit system, but the array
60 * of bits that get_block()/putblock() needs gets a bit big, so we can only
61 * prefill MAX_INIT blocks. (16-bit fsck can't check a file system larger
62 * than N_BLOCKS16 anyway.)
64 #define MAX_INIT (sizeof(char *) == 2 ? N_BLOCKS16 : N_BLOCKS)
67 #ifdef DOS
68 maybedefine O_RDONLY 4 /* O_RDONLY | BINARY_BIT */
69 maybedefine BWRITE 5 /* O_WRONLY | BINARY_BIT */
70 #endif
72 extern char *optarg;
73 extern int optind;
75 int next_zone, next_inode, zone_size, zone_shift = 0, zoff;
76 block_t nrblocks;
77 int inode_offset, lct = 0, disk, fd, print = 0, file = 0;
78 unsigned int nrinodes;
79 int override = 0, simple = 0, dflag;
80 int donttest; /* skip test if it fits on medium */
81 char *progname;
83 long current_time, bin_time;
84 char zero[BLOCK_SIZE], *lastp;
85 char umap[MAX_INIT / 8]; /* bit map tells if block read yet */
86 block_t zone_map; /* where is zone map? (depends on # inodes) */
87 int inodes_per_block;
88 int fs_version;
89 block_t max_nrblocks;
91 FILE *proto;
93 _PROTOTYPE(int main, (int argc, char **argv));
94 _PROTOTYPE(block_t sizeup, (char *device));
95 _PROTOTYPE(void super, (zone_t zones, Ino_t inodes));
96 _PROTOTYPE(void rootdir, (Ino_t inode));
97 _PROTOTYPE(void eat_dir, (Ino_t parent));
98 _PROTOTYPE(void eat_file, (Ino_t inode, int f));
99 _PROTOTYPE(void enter_dir, (Ino_t parent, char *name, Ino_t child));
100 _PROTOTYPE(void incr_size, (Ino_t n, long count));
101 _PROTOTYPE(PRIVATE ino_t alloc_inode, (int mode, int usrid, int grpid));
102 _PROTOTYPE(PRIVATE zone_t alloc_zone, (void));
103 _PROTOTYPE(void add_zone, (Ino_t n, zone_t z, long bytes, long cur_time));
104 _PROTOTYPE(void add_z_1, (Ino_t n, zone_t z, long bytes, long cur_time));
105 _PROTOTYPE(void add_z_2, (Ino_t n, zone_t z, long bytes, long cur_time));
106 _PROTOTYPE(void incr_link, (Ino_t n));
107 _PROTOTYPE(void insert_bit, (block_t block, int bit));
108 _PROTOTYPE(int mode_con, (char *p));
109 _PROTOTYPE(void getline, (char line[LINE_LEN], char *parse[MAX_TOKENS]));
110 _PROTOTYPE(void check_mtab, (char *devname));
111 _PROTOTYPE(long file_time, (int f));
112 _PROTOTYPE(void pexit, (char *s));
113 _PROTOTYPE(void copy, (char *from, char *to, int count));
114 _PROTOTYPE(void print_fs, (void));
115 _PROTOTYPE(int read_and_set, (block_t n));
116 _PROTOTYPE(void special, (char *string));
117 _PROTOTYPE(void get_block, (block_t n, char buf[BLOCK_SIZE]));
118 _PROTOTYPE(void put_block, (block_t n, char buf[BLOCK_SIZE]));
119 _PROTOTYPE(void cache_init, (void));
120 _PROTOTYPE(void flush, (void));
121 _PROTOTYPE(void mx_read, (int blocknr, char buf[BLOCK_SIZE]));
122 _PROTOTYPE(void mx_write, (int blocknr, char buf[BLOCK_SIZE]));
123 _PROTOTYPE(void dexit, (char *s, int sectnum, int err));
124 _PROTOTYPE(void usage, (void));
126 /*================================================================
127 * mkfs - make filesystem
128 *===============================================================*/
129 int main(argc, argv)
130 int argc;
131 char *argv[];
133 int nread, mode, usrid, grpid, ch;
134 block_t blocks;
135 block_t i;
136 ino_t root_inum;
137 ino_t inodes;
138 zone_t zones;
139 char *token[MAX_TOKENS], line[LINE_LEN];
140 struct stat statbuf;
142 /* Get two times, the current time and the mod time of the binary of
143 * mkfs itself. When the -d flag is used, the later time is put into
144 * the i_mtimes of all the files. This feature is useful when
145 * producing a set of file systems, and one wants all the times to be
146 * identical. First you set the time of the mkfs binary to what you
147 * want, then go.
149 current_time = time((time_t *) 0); /* time mkfs is being run */
150 stat(argv[0], &statbuf);
151 bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */
153 /* Process switches. */
154 progname = argv[0];
155 blocks = 0;
156 i = 0;
157 fs_version = 2;
158 inodes_per_block = V2_INODES_PER_BLOCK(BLOCK_SIZE);
159 max_nrblocks = N_BLOCKS;
160 while ((ch = getopt(argc, argv, "1b:di:lot")) != EOF)
161 switch (ch) {
162 case '1':
163 fs_version = 1;
164 inodes_per_block = V1_INODES_PER_BLOCK;
165 max_nrblocks = 0xFFFF;
166 break;
167 case 'b':
168 blocks = strtoul(optarg, (char **) NULL, 0);
169 break;
170 case 'd':
171 dflag = 1;
172 current_time = bin_time;
173 break;
174 case 'i':
175 i = strtoul(optarg, (char **) NULL, 0);
176 break;
177 case 'l': print = 1; break;
178 case 'o': override = 1; break;
179 case 't': donttest = 1; break;
180 default: usage();
183 /* Determine the size of the device if not specified as -b or proto. */
184 if (argc - optind == 1 && blocks == 0) blocks = sizeup(argv[optind]);
185 printf("%lu blocks\n", blocks);
187 /* The remaining args must be 'special proto', or just 'special' if the
188 * block size has already been specified.
190 if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage();
192 /* Check special. */
193 check_mtab(argv[optind]);
195 /* Check and start processing proto. */
196 optarg = argv[++optind];
197 if (optind < argc && (proto = fopen(optarg, "r")) != NULL) {
198 /* Prototype file is readable. */
199 lct = 1;
200 getline(line, token); /* skip boot block info */
202 /* Read the line with the block and inode counts. */
203 getline(line, token);
204 blocks = atol(token[0]);
205 if (blocks > max_nrblocks) {
206 printf("%d > %d\n", blocks, max_nrblocks);
207 pexit("Block count too large");
209 if (sizeof(char *) == 2 && blocks > N_BLOCKS16) {
210 fprintf(stderr,
211 "%s: warning: FS is larger than the %dM that fsck can check!\n",
212 progname, (int) (N_BLOCKS16 / (1024L * 1024)));
214 inodes = atoi(token[1]);
216 /* Process mode line for root directory. */
217 getline(line, token);
218 mode = mode_con(token[0]);
219 usrid = atoi(token[1]);
220 grpid = atoi(token[2]);
221 } else {
222 lct = 0;
223 if (optind < argc) {
224 /* Maybe the prototype file is just a size. Check. */
225 blocks = strtoul(optarg, (char **) NULL, 0);
226 if (blocks == 0) pexit("Can't open prototype file");
228 if (i == 0) {
229 /* The default for inodes is 3 blocks per inode, rounded up
230 * to fill an inode block. Above 20M, the average files are
231 * sure to be larger because it is hard to fill up 20M with
232 * tiny files, so reduce the default number of inodes. This
233 * default can always be overridden by using the -i option.
235 i = blocks / 3;
236 if (blocks >= 20000) i = blocks / 4;
237 if (blocks >= 40000) i = blocks / 5;
238 if (blocks >= 60000) i = blocks / 6;
239 if (blocks >= 80000) i = blocks / 7;
240 if (blocks >= 100000) i = blocks / 8;
241 i += inodes_per_block - 1;
242 i = i / inodes_per_block * inodes_per_block;
243 if (i > INODE_MAX) i = INODE_MAX;
245 if (blocks < 5) pexit("Block count too small");
246 if (blocks > max_nrblocks) {
247 printf("%d > %d\n", blocks, max_nrblocks);
248 pexit("Block count too large");
250 if (i < 1) pexit("Inode count too small");
251 if (i > INODE_MAX) pexit("Inode count too large");
252 inodes = (ino_t) i;
254 /* Make simple file system of the given size, using defaults. */
255 mode = 040777;
256 usrid = BIN;
257 grpid = BINGRP;
258 simple = 1;
260 nrblocks = blocks;
261 nrinodes = inodes;
263 /* Open special. */
264 special(argv[--optind]);
266 #ifdef UNIX
267 if (!donttest) {
268 static short testb[BLOCK_SIZE / sizeof(short)];
270 /* Try writing the last block of partition or diskette. */
271 lseek(fd, (off_t) (blocks - 1) * BLOCK_SIZE, SEEK_SET);
272 testb[0] = 0x3245;
273 testb[1] = 0x11FF;
274 if (write(fd, (char *) testb, BLOCK_SIZE) != BLOCK_SIZE)
275 pexit("File system is too big for minor device");
276 sync(); /* flush write, so if error next read fails */
277 lseek(fd, (off_t) (blocks - 1) * BLOCK_SIZE, SEEK_SET);
278 testb[0] = 0;
279 testb[1] = 0;
280 nread = read(fd, (char *) testb, BLOCK_SIZE);
281 if (nread != BLOCK_SIZE || testb[0] != 0x3245 || testb[1] != 0x11FF)
282 pexit("File system is too big for minor device");
283 lseek(fd, (off_t) (blocks - 1) * BLOCK_SIZE, SEEK_SET);
284 testb[0] = 0;
285 testb[1] = 0;
286 if (write(fd, (char *) testb, BLOCK_SIZE) != BLOCK_SIZE)
287 pexit("File system is too big for minor device");
288 lseek(fd, 0L, SEEK_SET);
290 #endif
292 /* Make the file-system */
294 cache_init();
296 put_block((block_t) 0, zero); /* Write a null boot block. */
298 zone_shift = 0; /* for future use */
299 zones = nrblocks >> zone_shift;
301 super(zones, inodes);
303 root_inum = alloc_inode(mode, usrid, grpid);
304 rootdir(root_inum);
305 if (simple == 0) eat_dir(root_inum);
307 if (print) print_fs();
308 flush();
309 return(0);
311 /* NOTREACHED */
312 } /* end main */
315 /*================================================================
316 * sizeup - determine device size
317 *===============================================================*/
318 block_t sizeup(device)
319 char *device;
321 int fd;
322 struct partition entry;
324 if ((fd = open(device, O_RDONLY)) == -1) return 0;
325 if (ioctl(fd, DIOCGETP, &entry) == -1) entry.size = cvu64(0);
326 close(fd);
327 return div64u(entry.size, BLOCK_SIZE);
331 /*================================================================
332 * super - construct a superblock
333 *===============================================================*/
335 void super(zones, inodes)
336 zone_t zones;
337 ino_t inodes;
339 unsigned int i;
340 int inodeblks;
341 int initblks;
343 zone_t initzones, nrzones, v1sq, v2sq;
344 zone_t zo;
345 struct super_block *sup;
346 char buf[BLOCK_SIZE], *cp;
348 for (cp = buf; cp < &buf[BLOCK_SIZE]; cp++) *cp = 0;
349 sup = (struct super_block *) buf; /* lint - might use a union */
351 sup->s_ninodes = inodes;
352 if (fs_version == 1) {
353 sup->s_nzones = zones;
354 } else {
355 sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */
356 sup->s_zones = zones;
358 sup->s_imap_blocks = bitmapsize((bit_t) (1 + inodes), BLOCK_SIZE);
359 sup->s_zmap_blocks = bitmapsize((bit_t) zones, BLOCK_SIZE);
360 inode_offset = sup->s_imap_blocks + sup->s_zmap_blocks + 2;
361 inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block;
362 initblks = inode_offset + inodeblks;
363 initzones = (initblks + (1 << zone_shift) - 1) >> zone_shift;
364 nrzones = nrblocks >> zone_shift;
365 sup->s_firstdatazone = (initblks + (1 << zone_shift) - 1) >> zone_shift;
366 zoff = sup->s_firstdatazone - 1;
367 sup->s_log_zone_size = zone_shift;
368 if (fs_version == 1) {
369 sup->s_magic = SUPER_MAGIC; /* identify super blocks */
370 v1sq = (zone_t) V1_INDIRECTS * V1_INDIRECTS;
371 zo = V1_NR_DZONES + (long) V1_INDIRECTS + v1sq;
372 } else {
373 sup->s_magic = SUPER_V2;/* identify super blocks */
374 v2sq = (zone_t) V2_INDIRECTS(BLOCK_SIZE) * V2_INDIRECTS(BLOCK_SIZE);
375 zo = V2_NR_DZONES + (zone_t) V2_INDIRECTS(BLOCK_SIZE) + v2sq;
377 sup->s_max_size = zo * BLOCK_SIZE;
378 zone_size = 1 << zone_shift; /* nr of blocks per zone */
380 put_block((block_t) 1, buf);
382 /* Clear maps and inodes. */
383 for (i = 2; i < initblks; i++) put_block((block_t) i, zero);
385 next_zone = sup->s_firstdatazone;
386 next_inode = 1;
388 zone_map = INODE_MAP + sup->s_imap_blocks;
390 insert_bit(zone_map, 0); /* bit zero must always be allocated */
391 insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but
392 * must be allocated */
396 /*================================================================
397 * rootdir - install the root directory
398 *===============================================================*/
399 void rootdir(inode)
400 ino_t inode;
402 zone_t z;
404 z = alloc_zone();
405 add_zone(inode, z, 32L, current_time);
406 enter_dir(inode, ".", inode);
407 enter_dir(inode, "..", inode);
408 incr_link(inode);
409 incr_link(inode);
413 /*================================================================
414 * eat_dir - recursively install directory
415 *===============================================================*/
416 void eat_dir(parent)
417 ino_t parent;
419 /* Read prototype lines and set up directory. Recurse if need be. */
420 char *token[MAX_TOKENS], *p;
421 char line[LINE_LEN];
422 int mode, usrid, grpid, maj, min, f;
423 ino_t n;
424 zone_t z;
425 long size;
427 while (1) {
428 getline(line, token);
429 p = token[0];
430 if (*p == '$') return;
431 p = token[1];
432 mode = mode_con(p);
433 usrid = atoi(token[2]);
434 grpid = atoi(token[3]);
435 if (grpid & 0200) fprintf(stderr, "A.S.Tanenbaum\n");
436 n = alloc_inode(mode, usrid, grpid);
438 /* Enter name in directory and update directory's size. */
439 enter_dir(parent, token[0], n);
440 incr_size(parent, 16L);
442 /* Check to see if file is directory or special. */
443 incr_link(n);
444 if (*p == 'd') {
445 /* This is a directory. */
446 z = alloc_zone(); /* zone for new directory */
447 add_zone(n, z, 32L, current_time);
448 enter_dir(n, ".", n);
449 enter_dir(n, "..", parent);
450 incr_link(parent);
451 incr_link(n);
452 eat_dir(n);
453 } else if (*p == 'b' || *p == 'c') {
454 /* Special file. */
455 maj = atoi(token[4]);
456 min = atoi(token[5]);
457 size = 0;
458 if (token[6]) size = atoi(token[6]);
459 size = BLOCK_SIZE * size;
460 add_zone(n, (zone_t) ((maj << 8) | min), size, current_time);
461 } else {
462 /* Regular file. Go read it. */
463 if ((f = open(token[4], O_RDONLY)) < 0) {
464 fprintf(stderr, "%s: Can't open %s: %s\n",
465 progname, token[4], strerror(errno));
466 } else
467 eat_file(n, f);
473 /*================================================================
474 * eat_file - copy file to MINIX
475 *===============================================================*/
476 /* Zonesize >= blocksize */
477 void eat_file(inode, f)
478 ino_t inode;
479 int f;
481 int ct, i, j, k;
482 zone_t z;
483 char buf[BLOCK_SIZE];
484 long timeval;
486 do {
487 for (i = 0, j = 0; i < zone_size; i++, j += ct) {
488 for (k = 0; k < BLOCK_SIZE; k++) buf[k] = 0;
489 if ((ct = read(f, buf, BLOCK_SIZE)) > 0) {
490 if (i == 0) z = alloc_zone();
491 put_block((z << zone_shift) + i, buf);
494 timeval = (dflag ? current_time : file_time(f));
495 if (ct) add_zone(inode, z, (long) j, timeval);
496 } while (ct == BLOCK_SIZE);
497 close(f);
502 /*================================================================
503 * directory & inode management assist group
504 *===============================================================*/
505 void enter_dir(parent, name, child)
506 ino_t parent, child;
507 char *name;
509 /* Enter child in parent directory */
510 /* Works for dir > 1 block and zone > block */
511 int i, j, k, l, off;
512 block_t b;
513 zone_t z;
514 char *p1, *p2;
515 struct direct dir_entry[NR_DIR_ENTRIES(BLOCK_SIZE)];
516 d1_inode ino1[V1_INODES_PER_BLOCK];
517 d2_inode ino2[V2_INODES_PER_BLOCK(BLOCK_SIZE)];
518 int nr_dzones;
520 b = ((parent - 1) / inodes_per_block) + inode_offset;
521 off = (parent - 1) % inodes_per_block;
523 if (fs_version == 1) {
524 get_block(b, (char *) ino1);
525 nr_dzones = V1_NR_DZONES;
526 } else {
527 get_block(b, (char *) ino2);
528 nr_dzones = V2_NR_DZONES;
530 for (k = 0; k < nr_dzones; k++) {
531 if (fs_version == 1) {
532 z = ino1[off].d1_zone[k];
533 if (z == 0) {
534 z = alloc_zone();
535 ino1[off].d1_zone[k] = z;
537 } else {
538 z = ino2[off].d2_zone[k];
539 if (z == 0) {
540 z = alloc_zone();
541 ino2[off].d2_zone[k] = z;
544 for (l = 0; l < zone_size; l++) {
545 get_block((z << zone_shift) + l, (char *) dir_entry);
546 for (i = 0; i < NR_DIR_ENTRIES(BLOCK_SIZE); i++) {
547 if (dir_entry[i].d_ino == 0) {
548 dir_entry[i].d_ino = child;
549 p1 = name;
550 p2 = dir_entry[i].d_name;
551 j = 14;
552 while (j--) {
553 *p2++ = *p1;
554 if (*p1 != 0) p1++;
556 put_block((z << zone_shift) + l, (char *) dir_entry);
557 if (fs_version == 1) {
558 put_block(b, (char *) ino1);
559 } else {
560 put_block(b, (char *) ino2);
562 return;
568 printf("Directory-inode %d beyond direct blocks. Could not enter %s\n",
569 parent, name);
570 pexit("Halt");
574 void add_zone(n, z, bytes, cur_time)
575 ino_t n;
576 zone_t z;
577 long bytes, cur_time;
579 if (fs_version == 1) {
580 add_z_1(n, z, bytes, cur_time);
581 } else {
582 add_z_2(n, z, bytes, cur_time);
586 void add_z_1(n, z, bytes, cur_time)
587 ino_t n;
588 zone_t z;
589 long bytes, cur_time;
591 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
593 int off, i;
594 block_t b;
595 zone_t indir;
596 zone1_t blk[V1_INDIRECTS];
597 d1_inode *p;
598 d1_inode inode[V1_INODES_PER_BLOCK];
600 b = ((n - 1) / V1_INODES_PER_BLOCK) + inode_offset;
601 off = (n - 1) % V1_INODES_PER_BLOCK;
602 get_block(b, (char *) inode);
603 p = &inode[off];
604 p->d1_size += bytes;
605 p->d1_mtime = cur_time;
606 for (i = 0; i < V1_NR_DZONES; i++)
607 if (p->d1_zone[i] == 0) {
608 p->d1_zone[i] = (zone1_t) z;
609 put_block(b, (char *) inode);
610 return;
612 put_block(b, (char *) inode);
614 /* File has grown beyond a small file. */
615 if (p->d1_zone[V1_NR_DZONES] == 0)
616 p->d1_zone[V1_NR_DZONES] = (zone1_t) alloc_zone();
617 indir = p->d1_zone[V1_NR_DZONES];
618 put_block(b, (char *) inode);
619 b = indir << zone_shift;
620 get_block(b, (char *) blk);
621 for (i = 0; i < V1_INDIRECTS; i++)
622 if (blk[i] == 0) {
623 blk[i] = (zone1_t) z;
624 put_block(b, (char *) blk);
625 return;
627 pexit("File has grown beyond single indirect");
630 void add_z_2(n, z, bytes, cur_time)
631 ino_t n;
632 zone_t z;
633 long bytes, cur_time;
635 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
637 int off, i;
638 block_t b;
639 zone_t indir;
640 zone_t blk[V2_INDIRECTS(BLOCK_SIZE)];
641 d2_inode *p;
642 d2_inode inode[V2_INODES_PER_BLOCK(BLOCK_SIZE)];
644 b = ((n - 1) / V2_INODES_PER_BLOCK(BLOCK_SIZE)) + inode_offset;
645 off = (n - 1) % V2_INODES_PER_BLOCK(BLOCK_SIZE);
646 get_block(b, (char *) inode);
647 p = &inode[off];
648 p->d2_size += bytes;
649 p->d2_mtime = cur_time;
650 for (i = 0; i < V2_NR_DZONES; i++)
651 if (p->d2_zone[i] == 0) {
652 p->d2_zone[i] = z;
653 put_block(b, (char *) inode);
654 return;
656 put_block(b, (char *) inode);
658 /* File has grown beyond a small file. */
659 if (p->d2_zone[V2_NR_DZONES] == 0) p->d2_zone[V2_NR_DZONES] = alloc_zone();
660 indir = p->d2_zone[V2_NR_DZONES];
661 put_block(b, (char *) inode);
662 b = indir << zone_shift;
663 get_block(b, (char *) blk);
664 for (i = 0; i < V2_INDIRECTS(BLOCK_SIZE); i++)
665 if (blk[i] == 0) {
666 blk[i] = z;
667 put_block(b, (char *) blk);
668 return;
670 pexit("File has grown beyond single indirect");
674 void incr_link(n)
675 ino_t n;
677 /* Increment the link count to inode n */
678 int off;
679 block_t b;
681 b = ((n - 1) / inodes_per_block) + inode_offset;
682 off = (n - 1) % inodes_per_block;
683 if (fs_version == 1) {
684 d1_inode inode1[V1_INODES_PER_BLOCK];
686 get_block(b, (char *) inode1);
687 inode1[off].d1_nlinks++;
688 put_block(b, (char *) inode1);
689 } else {
690 d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)];
692 get_block(b, (char *) inode2);
693 inode2[off].d2_nlinks++;
694 put_block(b, (char *) inode2);
699 void incr_size(n, count)
700 ino_t n;
701 long count;
703 /* Increment the file-size in inode n */
704 block_t b;
705 int off;
707 b = ((n - 1) / inodes_per_block) + inode_offset;
708 off = (n - 1) % inodes_per_block;
709 if (fs_version == 1) {
710 d1_inode inode1[V1_INODES_PER_BLOCK];
712 get_block(b, (char *) inode1);
713 inode1[off].d1_size += count;
714 put_block(b, (char *) inode1);
715 } else {
716 d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)];
718 get_block(b, (char *) inode2);
719 inode2[off].d2_size += count;
720 put_block(b, (char *) inode2);
725 /*================================================================
726 * allocation assist group
727 *===============================================================*/
728 PRIVATE ino_t alloc_inode(mode, usrid, grpid)
729 int mode, usrid, grpid;
731 ino_t num;
732 int off;
733 block_t b;
735 num = next_inode++;
736 if (num > nrinodes) pexit("File system does not have enough inodes");
737 b = ((num - 1) / inodes_per_block) + inode_offset;
738 off = (num - 1) % inodes_per_block;
739 if (fs_version == 1) {
740 d1_inode inode1[V1_INODES_PER_BLOCK];
742 get_block(b, (char *) inode1);
743 inode1[off].d1_mode = mode;
744 inode1[off].d1_uid = usrid;
745 inode1[off].d1_gid = grpid;
746 put_block(b, (char *) inode1);
747 } else {
748 d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)];
750 get_block(b, (char *) inode2);
751 inode2[off].d2_mode = mode;
752 inode2[off].d2_uid = usrid;
753 inode2[off].d2_gid = grpid;
754 put_block(b, (char *) inode2);
757 /* Set the bit in the bit map. */
758 /* DEBUG FIXME. This assumes the bit is in the first inode map block. */
759 insert_bit((block_t) INODE_MAP, (int) num);
760 return(num);
764 PRIVATE zone_t alloc_zone()
766 /* Allocate a new zone */
767 /* Works for zone > block */
768 block_t b;
769 int i;
770 zone_t z;
772 z = next_zone++;
773 b = z << zone_shift;
774 if ((b + zone_size) > nrblocks)
775 pexit("File system not big enough for all the files");
776 for (i = 0; i < zone_size; i++)
777 put_block(b + i, zero); /* give an empty zone */
778 /* DEBUG FIXME. This assumes the bit is in the first zone map block. */
779 insert_bit(zone_map, (int) (z - zoff)); /* lint, NOT OK because
780 * z hasn't been broken
781 * up into block +
782 * offset yet. */
783 return(z);
787 void insert_bit(block, bit)
788 block_t block;
789 int bit;
791 /* Insert 'count' bits in the bitmap */
792 int w, s;
793 short buf[BLOCK_SIZE / sizeof(short)];
795 if (block < 0) pexit("insert_bit called with negative argument");
796 get_block(block, (char *) buf);
797 w = bit / (8 * sizeof(short));
798 s = bit % (8 * sizeof(short));
799 buf[w] |= (1 << s);
800 put_block(block, (char *) buf);
804 /*================================================================
805 * proto-file processing assist group
806 *===============================================================*/
807 int mode_con(p)
808 char *p;
810 /* Convert string to mode */
811 int o1, o2, o3, mode;
812 char c1, c2, c3;
814 c1 = *p++;
815 c2 = *p++;
816 c3 = *p++;
817 o1 = *p++ - '0';
818 o2 = *p++ - '0';
819 o3 = *p++ - '0';
820 mode = (o1 << 6) | (o2 << 3) | o3;
821 if (c1 == 'd') mode += I_DIRECTORY;
822 if (c1 == 'b') mode += I_BLOCK_SPECIAL;
823 if (c1 == 'c') mode += I_CHAR_SPECIAL;
824 if (c1 == '-') mode += I_REGULAR;
825 if (c2 == 'u') mode += I_SET_UID_BIT;
826 if (c3 == 'g') mode += I_SET_GID_BIT;
827 return(mode);
830 void getline(line, parse)
831 char *parse[MAX_TOKENS];
832 char line[LINE_LEN];
834 /* Read a line and break it up in tokens */
835 int k;
836 char c, *p;
837 int d;
839 for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0;
840 for (k = 0; k < LINE_LEN; k++) line[k] = 0;
841 k = 0;
842 parse[0] = 0;
843 p = line;
844 while (1) {
845 if (++k > LINE_LEN) pexit("Line too long");
846 d = fgetc(proto);
847 if (d == EOF) pexit("Unexpected end-of-file");
848 *p = d;
849 if (*p == '\n') lct++;
850 if (*p == ' ' || *p == '\t') *p = 0;
851 if (*p == '\n') {
852 *p++ = 0;
853 *p = '\n';
854 break;
856 p++;
859 k = 0;
860 p = line;
861 lastp = line;
862 while (1) {
863 c = *p++;
864 if (c == '\n') return;
865 if (c == 0) continue;
866 parse[k++] = p - 1;
867 do {
868 c = *p++;
869 } while (c != 0 && c != '\n');
874 /*================================================================
875 * other stuff
876 *===============================================================*/
877 void check_mtab(devname)
878 char *devname; /* /dev/hd1 or whatever */
880 /* Check to see if the special file named in s is mounted. */
882 int n;
883 char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1], version[10], rw_flag[10];
885 if (load_mtab("mkfs") < 0) return;
886 while (1) {
887 n = get_mtab_entry(special, mounted_on, version, rw_flag);
888 if (n < 0) return;
889 if (strcmp(devname, special) == 0) {
890 /* Can't mkfs on top of a mounted file system. */
891 fprintf(stderr, "%s: %s is mounted on %s\n",
892 progname, devname, mounted_on);
893 exit(1);
899 long file_time(f)
900 int f;
902 #ifdef UNIX
903 struct stat statbuf;
904 fstat(f, &statbuf);
905 return(statbuf.st_mtime);
906 #else /* fstat not supported by DOS */
907 return(0L);
908 #endif
912 void pexit(s)
913 char *s;
915 fprintf(stderr, "%s: %s\n", progname, s);
916 if (lct != 0)
917 fprintf(stderr, "Line %d being processed when error detected.\n", lct);
918 flush();
919 exit(2);
923 void copy(from, to, count)
924 char *from, *to;
925 int count;
927 while (count--) *to++ = *from++;
931 void print_fs()
933 int i, j;
934 ino_t k;
935 d1_inode inode1[V1_INODES_PER_BLOCK];
936 d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)];
937 unsigned short usbuf[BLOCK_SIZE / sizeof(unsigned short)];
938 block_t b, inode_limit;
939 struct direct dir[NR_DIR_ENTRIES(BLOCK_SIZE)];
941 get_block((block_t) 1, (char *) usbuf);
942 printf("\nSuperblock: ");
943 for (i = 0; i < 8; i++) printf("%06o ", usbuf[i]);
944 get_block((block_t) 2, (char *) usbuf);
945 printf("...\nInode map: ");
946 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
947 get_block((block_t) 3, (char *) usbuf);
948 printf("...\nZone map: ");
949 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
950 printf("...\n");
952 k = 0;
953 for (b = inode_offset; k < nrinodes; b++) {
954 if (fs_version == 1) {
955 get_block(b, (char *) inode1);
956 } else {
957 get_block(b, (char *) inode2);
959 for (i = 0; i < inodes_per_block; i++) {
960 k = inodes_per_block * (int) (b - inode_offset) + i + 1;
961 /* Lint but OK */
962 if (k > nrinodes) break;
963 if (fs_version == 1) {
964 if (inode1[i].d1_mode != 0) {
965 printf("Inode %2d: mode=", k);
966 printf("%06o", inode1[i].d1_mode);
967 printf(" uid=%2d gid=%2d size=",
968 inode1[i].d1_uid, inode1[i].d1_gid);
969 printf("%6ld", inode1[i].d1_size);
970 printf(" zone[0]=%d\n", inode1[i].d1_zone[0]);
972 if ((inode1[i].d1_mode & I_TYPE) == I_DIRECTORY) {
973 /* This is a directory */
974 get_block(inode1[i].d1_zone[0], (char *) dir);
975 for (j = 0; j < NR_DIR_ENTRIES(BLOCK_SIZE); j++)
976 if (dir[j].d_ino)
977 printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name);
979 } else {
980 if (inode2[i].d2_mode != 0) {
981 printf("Inode %2d: mode=", k);
982 printf("%06o", inode2[i].d2_mode);
983 printf(" uid=%2d gid=%2d size=",
984 inode2[i].d2_uid, inode2[i].d2_gid);
985 printf("%6ld", inode2[i].d2_size);
986 printf(" zone[0]=%ld\n", inode2[i].d2_zone[0]);
988 if ((inode2[i].d2_mode & I_TYPE) == I_DIRECTORY) {
989 /* This is a directory */
990 get_block(inode2[i].d2_zone[0], (char *) dir);
991 for (j = 0; j < NR_DIR_ENTRIES(BLOCK_SIZE); j++)
992 if (dir[j].d_ino)
993 printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name);
999 printf("%d inodes used. %d zones used.\n", next_inode - 1, next_zone);
1003 int read_and_set(n)
1004 block_t n;
1006 /* The first time a block is read, it returns all 0s, unless there has
1007 * been a write. This routine checks to see if a block has been accessed.
1010 int w, s, mask, r;
1012 if (sizeof(char *) == 2 && n >= MAX_INIT) pexit("can't initialize past 128M");
1013 w = n / 8;
1014 s = n % 8;
1015 mask = 1 << s;
1016 r = (umap[w] & mask ? 1 : 0);
1017 umap[w] |= mask;
1018 return(r);
1021 void usage()
1023 fprintf(stderr,
1024 "Usage: %s [-1dlot] [-b blocks] [-i inodes] special [proto]\n",
1025 progname);
1026 exit(1);
1029 /*================================================================
1030 * get_block & put_block for MS-DOS
1031 *===============================================================*/
1032 #ifdef DOS
1035 * These are the get_block and put_block routines
1036 * when compiling & running mkfs.c under MS-DOS.
1038 * It requires the (asembler) routines absread & abswrite
1039 * from the file diskio.asm. Since these routines just do
1040 * as they are told (read & write the sector specified),
1041 * a local cache is used to minimize the i/o-overhead for
1042 * frequently used blocks.
1044 * The global variable "file" determines whether the output
1045 * is to a disk-device or to a binary file.
1049 #define PH_SECTSIZE 512 /* size of a physical disk-sector */
1052 char *derrtab[14] = {
1053 "no error",
1054 "disk is read-only",
1055 "unknown unit",
1056 "device not ready",
1057 "bad command",
1058 "data error",
1059 "internal error: bad request structure length",
1060 "seek error",
1061 "unknown media type",
1062 "sector not found",
1063 "printer out of paper (?)",
1064 "write fault",
1065 "read error",
1066 "general error"
1069 #define CACHE_SIZE 20 /* 20 block-buffers */
1072 struct cache {
1073 char blockbuf[BLOCK_SIZE];
1074 block_t blocknum;
1075 int dirty;
1076 int usecnt;
1077 } cache[CACHE_SIZE];
1080 void special(string)
1081 char *string;
1084 if (string[1] == ':' && string[2] == 0) {
1085 /* Format: d: or d:fname */
1086 disk = (string[0] & ~32) - 'A';
1087 if (disk > 1 && !override) /* safety precaution */
1088 pexit("Bad drive specifier for special");
1089 } else {
1090 file = 1;
1091 if ((fd = creat(string, BWRITE)) == 0)
1092 pexit("Can't open special file");
1096 void get_block(n, buf)
1097 block_t n;
1098 char buf[BLOCK_SIZE];
1100 /* Get a block to the user */
1101 struct cache *bp, *fp;
1103 /* First access returns a zero block */
1104 if (read_and_set(n) == 0) {
1105 copy(zero, buf, BLOCK_SIZE);
1106 return;
1109 /* Look for block in cache */
1110 fp = 0;
1111 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) {
1112 if (bp->blocknum == n) {
1113 copy(bp, buf, BLOCK_SIZE);
1114 bp->usecnt++;
1115 return;
1118 /* Remember clean block */
1119 if (bp->dirty == 0)
1120 if (fp) {
1121 if (fp->usecnt > bp->usecnt) fp = bp;
1122 } else
1123 fp = bp;
1126 /* Block not in cache, get it */
1127 if (!fp) {
1128 /* No clean buf, flush one */
1129 for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++)
1130 if (fp->usecnt > bp->usecnt) fp = bp;
1131 mx_write(fp->blocknum, fp);
1133 mx_read(n, fp);
1134 fp->dirty = 0;
1135 fp->usecnt = 0;
1136 fp->blocknum = n;
1137 copy(fp, buf, BLOCK_SIZE);
1140 void put_block(n, buf)
1141 block_t n;
1142 char buf[BLOCK_SIZE];
1144 /* Accept block from user */
1145 struct cache *fp, *bp;
1147 (void) read_and_set(n);
1149 /* Look for block in cache */
1150 fp = 0;
1151 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) {
1152 if (bp->blocknum == n) {
1153 copy(buf, bp, BLOCK_SIZE);
1154 bp->dirty = 1;
1155 return;
1158 /* Remember clean block */
1159 if (bp->dirty == 0)
1160 if (fp) {
1161 if (fp->usecnt > bp->usecnt) fp = bp;
1162 } else
1163 fp = bp;
1166 /* Block not in cache */
1167 if (!fp) {
1168 /* No clean buf, flush one */
1169 for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++)
1170 if (fp->usecnt > bp->usecnt) fp = bp;
1171 mx_write(fp->blocknum, fp);
1173 fp->dirty = 1;
1174 fp->usecnt = 1;
1175 fp->blocknum = n;
1176 copy(buf, fp, BLOCK_SIZE);
1179 void cache_init()
1181 struct cache *bp;
1182 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) bp->blocknum = -1;
1185 void flush()
1187 /* Flush all dirty blocks to disk */
1188 struct cache *bp;
1190 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++)
1191 if (bp->dirty) {
1192 mx_write(bp->blocknum, bp);
1193 bp->dirty = 0;
1197 /*==================================================================
1198 * hard read & write etc.
1199 *=================================================================*/
1200 #define MAX_RETRIES 5
1203 void mx_read(blocknr, buf)
1204 int blocknr;
1205 char buf[BLOCK_SIZE];
1208 /* Read the requested MINIX-block in core */
1209 char (*bp)[PH_SECTSIZE];
1210 int sectnum, retries, err;
1212 if (file) {
1213 lseek(fd, (off_t) blocknr * BLOCK_SIZE, 0);
1214 if (read(fd, buf, BLOCK_SIZE) != BLOCK_SIZE)
1215 pexit("mx_read: error reading file");
1216 } else {
1217 sectnum = blocknr * (BLOCK_SIZE / PH_SECTSIZE);
1218 for (bp = buf; bp < &buf[BLOCK_SIZE]; bp++) {
1219 retries = MAX_RETRIES;
1221 err = absread(disk, sectnum, bp);
1222 while (err && --retries);
1224 if (retries) {
1225 sectnum++;
1226 } else {
1227 dexit("mx_read", sectnum, err);
1233 void mx_write(blocknr, buf)
1234 int blocknr;
1235 char buf[BLOCK_SIZE];
1237 /* Write the MINIX-block to disk */
1238 char (*bp)[PH_SECTSIZE];
1239 int retries, sectnum, err;
1241 if (file) {
1242 lseek(fd, blocknr * BLOCK_SIZE, 0);
1243 if (write(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) {
1244 pexit("mx_write: error writing file");
1246 } else {
1247 sectnum = blocknr * (BLOCK_SIZE / PH_SECTSIZE);
1248 for (bp = buf; bp < &buf[BLOCK_SIZE]; bp++) {
1249 retries = MAX_RETRIES;
1250 do {
1251 err = abswrite(disk, sectnum, bp);
1252 } while (err && --retries);
1254 if (retries) {
1255 sectnum++;
1256 } else {
1257 dexit("mx_write", sectnum, err);
1264 void dexit(s, sectnum, err)
1265 int sectnum, err;
1266 char *s;
1268 printf("Error: %s, sector: %d, code: %d, meaning: %s\n",
1269 s, sectnum, err, derrtab[err]);
1270 exit(2);
1273 #endif
1275 /*================================================================
1276 * get_block & put_block for UNIX
1277 *===============================================================*/
1278 #ifdef UNIX
1280 void special(string)
1281 char *string;
1283 fd = creat(string, 0777);
1284 close(fd);
1285 fd = open(string, O_RDWR);
1286 if (fd < 0) pexit("Can't open special file");
1291 void get_block(n, buf)
1292 block_t n;
1293 char buf[BLOCK_SIZE];
1295 /* Read a block. */
1297 int k;
1299 /* First access returns a zero block */
1300 if (read_and_set(n) == 0) {
1301 copy(zero, buf, BLOCK_SIZE);
1302 return;
1304 lseek(fd, (off_t) n * BLOCK_SIZE, SEEK_SET);
1305 k = read(fd, buf, BLOCK_SIZE);
1306 if (k != BLOCK_SIZE) {
1307 pexit("get_block couldn't read");
1311 void put_block(n, buf)
1312 block_t n;
1313 char buf[BLOCK_SIZE];
1315 /* Write a block. */
1317 (void) read_and_set(n);
1319 /* XXX - check other lseeks too. */
1320 if (lseek(fd, (off_t) n * BLOCK_SIZE, SEEK_SET) == (off_t) -1) {
1321 pexit("put_block couldn't seek");
1323 if (write(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) {
1324 pexit("put_block couldn't write");
1329 /* Dummy routines to keep source file clean from #ifdefs */
1331 void flush()
1333 return;
1336 void cache_init()
1338 return;
1341 #endif