remove extra mkfs.1
[minix.git] / usr.sbin / mkfs.mfs / mkfs.c
blobd8c220338a63105bac32cfeac47ef8e1ce0b6262
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 version 1, 2 and 3 file systems, as follows:
6 * mkfs /dev/fd0 1200 # Version 3 (default)
7 * mkfs -1 /dev/fd0 360 # Version 1
9 * Note that the version 1 and 2 file systems produced by this program are not
10 * compatible with the original version 1 and 2 file system layout.
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <stdio.h>
16 #include <assert.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <limits.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <stdint.h>
25 #include "const.h"
26 #include "type.h"
27 #include "mfsdir.h"
28 #if defined(__minix)
29 #include <minix/partition.h>
30 #include <minix/u64.h>
31 #include <minix/minlib.h>
32 #include <sys/ioctl.h>
33 #endif
34 #include <dirent.h>
36 #undef EXTERN
37 #define EXTERN /* get rid of EXTERN by making it null */
38 #include "super.h"
40 #ifndef max
41 #define max(a,b) ((a) > (b) ? (a) : (b))
42 #endif
44 #define INODE_MAP 2
45 #define MAX_TOKENS 10
46 #define LINE_LEN 200
47 #define BIN 2
48 #define BINGRP 2
49 #define BIT_MAP_SHIFT 13
50 #define INODE_MAX ((unsigned) 65535)
51 #define SECTOR_SIZE 512
54 #if !defined(__minix)
55 #define mul64u(a,b) ((a) * (b))
56 #define lseek64(a,b,c,d) lseek(a,b,c)
57 #ifdef __linux__
58 #include <mntent.h>
59 #endif
60 #endif
62 #if !defined(__minix)
63 typedef uint32_t block_t;
64 typedef uint32_t zone_t;
65 #endif
67 extern char *optarg;
68 extern int optind;
70 int next_zone, next_inode, zoff;
71 block_t nrblocks;
72 int inode_offset, lct = 0, disk, fd, print = 0, file = 0;
73 unsigned int nrinodes;
74 int override = 0, simple = 0, dflag;
75 int donttest; /* skip test if it fits on medium */
76 char *progname;
78 uint32_t current_time, bin_time;
79 char *zero, *lastp;
80 char *umap_array; /* bit map tells if block read yet */
81 int umap_array_elements = 0;
82 block_t zone_map; /* where is zone map? (depends on # inodes) */
83 int inodes_per_block;
84 size_t block_size;
85 int extra_space_percent;
87 FILE *proto;
89 #if defined(__NBSD_LIBC) || !defined(__minix)
90 #define getline _mkfs_getline
91 #endif
93 int main(int argc, char **argv);
94 block_t sizeup(char *device);
95 void super(zone_t zones, ino_t inodes);
96 void rootdir(ino_t inode);
97 void eat_dir(ino_t parent);
98 void eat_file(ino_t inode, int f);
99 void enter_dir(ino_t parent, char *name, ino_t child);
100 void incr_size(ino_t n, size_t count);
101 static ino_t alloc_inode(int mode, int usrid, int grpid);
102 static zone_t alloc_zone(void);
103 void add_zone(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
104 void add_z_1(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
105 void add_z_2(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
106 void incr_link(ino_t n);
107 void insert_bit(block_t block, int bit);
108 int mode_con(char *p);
109 void getline(char line[LINE_LEN], char *parse[MAX_TOKENS]);
110 void check_mtab(char *devname);
111 uint32_t file_time(int f);
112 void pexit(char *s);
113 void copy(char *from, char *to, size_t count);
114 void print_fs(void);
115 int read_and_set(block_t n);
116 void special(char *string);
117 void get_block(block_t n, char *buf);
118 void get_super_block(char *buf);
119 void put_block(block_t n, char *buf);
120 void mx_read(int blocknr, char *buf);
121 void mx_write(int blocknr, char *buf);
122 void dexit(char *s, int sectnum, int err);
123 void usage(void);
124 void *alloc_block(void);
126 ino_t inocount;
127 zone_t zonecount;
128 block_t blockcount;
130 void detect_fs_size(void);
131 void sizeup_dir(void);
132 void detect_size(void);
133 void size_dir(void);
134 static int bitmapsize(uint32_t nr_bits, size_t block_size);
136 /*================================================================
137 * mkfs - make filesystem
138 *===============================================================*/
139 int main(argc, argv)
140 int argc;
141 char *argv[];
143 int nread, mode, usrid, grpid, ch;
144 block_t blocks, maxblocks;
145 size_t i;
146 ino_t root_inum;
147 ino_t inodes;
148 zone_t zones;
149 char *token[MAX_TOKENS], line[LINE_LEN];
150 struct stat statbuf;
152 /* Get two times, the current time and the mod time of the binary of
153 * mkfs itself. When the -d flag is used, the later time is put into
154 * the i_mtimes of all the files. This feature is useful when
155 * producing a set of file systems, and one wants all the times to be
156 * identical. First you set the time of the mkfs binary to what you
157 * want, then go.
159 current_time = time((time_t *) 0); /* time mkfs is being run */
160 stat(argv[0], &statbuf);
161 bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */
163 /* Process switches. */
164 progname = argv[0];
165 blocks = 0;
166 i = 0;
167 inodes_per_block = 0;
168 block_size = 0;
169 extra_space_percent = 0;
170 while ((ch = getopt(argc, argv, "b:di:lotB:x:")) != EOF)
171 switch (ch) {
172 case 'b':
173 blocks = strtoul(optarg, (char **) NULL, 0);
174 break;
175 case 'd':
176 dflag = 1;
177 current_time = bin_time;
178 break;
179 case 'i':
180 i = strtoul(optarg, (char **) NULL, 0);
181 break;
182 case 'l': print = 1; break;
183 case 'o': override = 1; break;
184 case 't': donttest = 1; break;
185 case 'B': block_size = atoi(optarg); break;
186 case 'x': extra_space_percent = atoi(optarg); break;
187 default: usage();
190 if (argc == optind) usage();
192 /* Percentage of extra size must be nonnegative.
193 * It can legitimately be bigger than 100 but has to make some sort of sense.
195 if(extra_space_percent < 0 || extra_space_percent > 2000) usage();
198 if(!block_size) block_size = _MAX_BLOCK_SIZE; /* V3 default block size */
199 if(block_size%SECTOR_SIZE || block_size < _MIN_BLOCK_SIZE) {
200 fprintf(stderr, "block size must be multiple of sector (%d) "
201 "and at least %d bytes\n",
202 SECTOR_SIZE, _MIN_BLOCK_SIZE);
203 pexit("specified block size illegal");
205 if(block_size%V2_INODE_SIZE) {
206 fprintf(stderr, "block size must be a multiple of inode size (%d bytes)\n",
207 V2_INODE_SIZE);
208 pexit("specified block size illegal");
212 if(!inodes_per_block)
213 inodes_per_block = V2_INODES_PER_BLOCK(block_size);
215 /* now that the block size is known, do buffer allocations where
216 * possible.
218 zero = alloc_block();
219 bzero(zero, block_size);
221 /* Determine the size of the device if not specified as -b or proto. */
222 maxblocks = sizeup(argv[optind]);
223 if (argc - optind == 1 && blocks == 0) {
224 blocks = maxblocks;
225 /* blocks == 0 is checked later, but leads to a funny way of
226 * reporting a 0-sized device (displays usage).
228 if(blocks < 1) {
229 fprintf(stderr, "%s: zero size device.\n", progname);
230 return 1;
234 /* The remaining args must be 'special proto', or just 'special' if the
235 * no. of blocks has already been specified.
237 if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage();
239 if (blocks > maxblocks) {
240 fprintf(stderr, "%s: %s: number of blocks too large for device.\n",
241 progname, argv[optind]);
242 return 1;
245 /* Check special. */
246 check_mtab(argv[optind]);
248 /* Check and start processing proto. */
249 optarg = argv[++optind];
250 if (optind < argc && (proto = fopen(optarg, "r")) != NULL) {
251 /* Prototype file is readable. */
252 lct = 1;
253 getline(line, token); /* skip boot block info */
255 /* Read the line with the block and inode counts. */
256 getline(line, token);
257 blocks = atol(token[0]);
258 inodes = atoi(token[1]);
260 /* Process mode line for root directory. */
261 getline(line, token);
262 mode = mode_con(token[0]);
263 usrid = atoi(token[1]);
264 grpid = atoi(token[2]);
266 if(blocks <= 0 && inodes <= 0){
267 detect_fs_size();
268 blocks = blockcount;
269 inodes = inocount;
270 blocks += blocks*extra_space_percent/100;
271 inodes += inodes*extra_space_percent/100;
272 printf("dynamically sized filesystem: %d blocks, %d inodes\n", blocks,
273 (unsigned int) inodes);
275 } else {
276 lct = 0;
277 if (optind < argc) {
278 /* Maybe the prototype file is just a size. Check. */
279 blocks = strtoul(optarg, (char **) NULL, 0);
280 if (blocks == 0) pexit("Can't open prototype file");
282 if (i == 0) {
283 #if defined(__minix)
284 uint32_t kb = div64u(mul64u(blocks, block_size), 1024);
285 #else
286 uint32_t kb = ((unsigned long long) blocks * block_size) / 1024;
287 #endif
288 i = kb / 2;
289 if (kb >= 100000) i = kb / 4;
291 /* round up to fill inode block */
292 i += inodes_per_block - 1;
293 i = i / inodes_per_block * inodes_per_block;
295 if (blocks < 5) pexit("Block count too small");
296 if (i < 1) pexit("Inode count too small");
297 inodes = (ino_t) i;
299 /* Make simple file system of the given size, using defaults. */
300 mode = 040777;
301 usrid = BIN;
302 grpid = BINGRP;
303 simple = 1;
306 nrblocks = blocks;
307 nrinodes = inodes;
310 size_t bytes;
311 bytes = 1 + blocks/8;
312 if(!(umap_array = malloc(bytes))) {
313 fprintf(stderr, "mkfs: can't allocate block bitmap (%u bytes).\n",
314 bytes);
315 exit(1);
317 umap_array_elements = bytes;
320 /* Open special. */
321 special(argv[--optind]);
323 if (!donttest) {
324 short *testb;
325 ssize_t w;
327 testb = (short *) alloc_block();
329 /* Try writing the last block of partition or diskette. */
330 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
331 pexit("couldn't seek to last block to test size (1)");
333 testb[0] = 0x3245;
334 testb[1] = 0x11FF;
335 testb[block_size/2-1] = 0x1F2F;
336 if ((w=write(fd, (char *) testb, block_size)) != block_size) {
337 if(w < 0) perror("write");
338 printf("%d/%u\n", w, block_size);
339 pexit("File system is too big for minor device (write)");
341 sync(); /* flush write, so if error next read fails */
342 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
343 pexit("couldn't seek to last block to test size (2)");
345 testb[0] = 0;
346 testb[1] = 0;
347 nread = read(fd, (char *) testb, block_size);
348 if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF ||
349 testb[block_size/2-1] != 0x1F2F) {
350 if(nread < 0) perror("read");
351 printf("nread = %d\n", nread);
352 printf("testb = 0x%x 0x%x 0x%x\n", testb[0], testb[1], testb[block_size-1]);
353 pexit("File system is too big for minor device (read)");
355 lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL);
356 testb[0] = 0;
357 testb[1] = 0;
358 if (write(fd, (char *) testb, block_size) != block_size)
359 pexit("File system is too big for minor device (write2)");
360 lseek(fd, 0L, SEEK_SET);
361 free(testb);
364 /* Make the file-system */
366 put_block((block_t) 0, zero); /* Write a null boot block. */
368 zones = nrblocks;
370 super(zones, inodes);
372 root_inum = alloc_inode(mode, usrid, grpid);
373 rootdir(root_inum);
374 if (simple == 0) eat_dir(root_inum);
376 if (print) print_fs();
377 return(0);
379 /* NOTREACHED */
380 } /* end main */
382 /*================================================================
383 * detect_fs_size - determine image size dynamically
384 *===============================================================*/
385 void detect_fs_size()
387 uint32_t point = ftell(proto);
389 inocount = 1; /* root directory node */
390 zonecount = 0;
391 blockcount = 0;
392 sizeup_dir();
394 uint32_t initb;
396 initb = bitmapsize((uint32_t) (1 + inocount), block_size);
397 initb += bitmapsize((uint32_t) zonecount, block_size);
398 initb += START_BLOCK;
399 initb += (inocount + inodes_per_block - 1) / inodes_per_block;
401 blockcount = initb+zonecount;
402 fseek(proto, point, SEEK_SET);
405 void sizeup_dir()
407 char *token[MAX_TOKENS], *p;
408 char line[LINE_LEN];
409 FILE *f;
410 size_t size;
411 int dir_entries = 2;
412 zone_t dir_zones = 0;
413 zone_t nr_dzones;
415 nr_dzones = V2_NR_DZONES;
417 while (1) {
418 getline(line, token);
419 p = token[0];
420 if (*p == '$') {
421 dir_zones = (dir_entries / (NR_DIR_ENTRIES(block_size)));
422 if(dir_entries % (NR_DIR_ENTRIES(block_size)))
423 dir_zones++;
424 if(dir_zones > nr_dzones)
425 dir_zones++; /* Max single indir */
426 zonecount += dir_zones;
427 return;
430 p = token[1];
431 inocount++;
432 dir_entries++;
434 if (*p == 'd') {
435 sizeup_dir();
436 } else if (*p == 'b' || *p == 'c') {
438 } else if (*p == 's') {
439 zonecount++; /* Symlink contents is always stored a block */
440 } else {
441 if ((f = fopen(token[4], "r")) == NULL) {
442 fprintf(stderr, "%s: Can't open %s: %s\n",
443 progname, token[4], strerror(errno));
444 pexit("dynamic size detection failed");
445 } else {
446 if (fseek(f, 0, SEEK_END) < 0) {
447 fprintf(stderr, "%s: Can't seek to end of %s\n",
448 progname, token[4]);
449 pexit("dynamic size detection failed");
451 size = ftell(f);
452 fclose(f);
453 zone_t fzones= (size / block_size);
454 if (size % block_size)
455 fzones++;
456 if (fzones > nr_dzones)
457 fzones++; /* Assumes files fit within single indirect */
458 zonecount += fzones;
464 /*================================================================
465 * sizeup - determine device size
466 *===============================================================*/
467 block_t sizeup(device)
468 char *device;
470 block_t d;
471 #if defined(__minix)
472 u64_t bytes, resize;
473 u32_t rem;
474 #else
475 off_t size;
476 #endif
479 if ((fd = open(device, O_RDONLY)) == -1) {
480 if (errno != ENOENT)
481 perror("sizeup open");
482 return 0;
485 #if defined(__minix)
486 if(minix_sizeup(device, &bytes) < 0) {
487 perror("sizeup");
488 return 0;
491 d = div64u(bytes, block_size);
492 rem = rem64u(bytes, block_size);
494 resize = add64u(mul64u(d, block_size), rem);
495 if(cmp64(resize, bytes) != 0) {
496 d = ULONG_MAX;
497 fprintf(stderr, "mkfs: truncating FS at %u blocks\n", d);
499 #else
500 size = lseek(fd, 0, SEEK_END);
501 if (size == (off_t) -1) {
502 fprintf(stderr, "Cannot get device size fd=%d\n", fd);
503 exit(-1);
505 d = size / block_size;
506 #endif
508 return d;
512 * copied from fslib
514 static int bitmapsize(nr_bits, block_size)
515 uint32_t nr_bits;
516 size_t block_size;
518 block_t nr_blocks;
520 nr_blocks = (int) (nr_bits / FS_BITS_PER_BLOCK(block_size));
521 if (((uint32_t) nr_blocks * FS_BITS_PER_BLOCK(block_size)) < nr_bits) ++nr_blocks;
522 return(nr_blocks);
525 /*================================================================
526 * super - construct a superblock
527 *===============================================================*/
529 void super(zones, inodes)
530 zone_t zones;
531 ino_t inodes;
533 unsigned int i;
534 int inodeblks;
535 int initblks;
536 uint32_t nb;
537 zone_t v2sq;
538 zone_t zo;
539 struct super_block *sup;
540 char *buf, *cp;
542 buf = alloc_block();
544 for (cp = buf; cp < &buf[block_size]; cp++) *cp = 0;
545 sup = (struct super_block *) buf; /* lint - might use a union */
547 /* The assumption is that mkfs will create a clean FS. */
548 sup->s_flags = MFSFLAG_CLEAN;
550 sup->s_ninodes = inodes;
551 sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */
552 sup->s_zones = zones;
554 #define BIGGERBLOCKS "Please try a larger block size for an FS of this size.\n"
555 sup->s_imap_blocks = nb = bitmapsize((uint32_t) (1 + inodes), block_size);
556 if(sup->s_imap_blocks != nb) {
557 fprintf(stderr, "mkfs: too many inode bitmap blocks.\n" BIGGERBLOCKS);
558 exit(1);
560 sup->s_zmap_blocks = nb = bitmapsize((uint32_t) zones, block_size);
561 if(nb != sup->s_zmap_blocks) {
562 fprintf(stderr, "mkfs: too many block bitmap blocks.\n" BIGGERBLOCKS);
563 exit(1);
565 inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks;
566 inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block;
567 initblks = inode_offset + inodeblks;
568 sup->s_firstdatazone_old = nb = initblks;
569 if(nb >= zones) pexit("bit maps too large");
570 if(nb != sup->s_firstdatazone_old) {
571 /* The field is too small to store the value. Fortunately, the value
572 * can be computed from other fields. We set the on-disk field to zero
573 * to indicate that it must not be used. Eventually, we can always set
574 * the on-disk field to zero, and stop using it.
576 sup->s_firstdatazone_old = 0;
578 sup->s_firstdatazone = nb;
579 zoff = sup->s_firstdatazone - 1;
580 sup->s_log_zone_size = 0;
582 v2sq = (zone_t) V2_INDIRECTS(block_size) * V2_INDIRECTS(block_size);
583 zo = V2_NR_DZONES + (zone_t) V2_INDIRECTS(block_size) + v2sq;
585 sup->s_magic = SUPER_V3;
586 sup->s_block_size = block_size;
587 sup->s_disk_version = 0;
588 #define MAX_MAX_SIZE (INT_MAX)
589 if(MAX_MAX_SIZE/block_size < zo) {
590 sup->s_max_size = (int32_t) MAX_MAX_SIZE;
592 else {
593 sup->s_max_size = zo * block_size;
598 if (lseek(fd, (off_t) _STATIC_BLOCK_SIZE, SEEK_SET) == (off_t) -1) {
599 pexit("super() couldn't seek");
601 if (write(fd, buf, _STATIC_BLOCK_SIZE) != _STATIC_BLOCK_SIZE) {
602 pexit("super() couldn't write");
605 /* Clear maps and inodes. */
606 for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero);
608 next_zone = sup->s_firstdatazone;
609 next_inode = 1;
611 zone_map = INODE_MAP + sup->s_imap_blocks;
613 insert_bit(zone_map, 0); /* bit zero must always be allocated */
614 insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but
615 * must be allocated */
617 free(buf);
621 /*================================================================
622 * rootdir - install the root directory
623 *===============================================================*/
624 void rootdir(inode)
625 ino_t inode;
627 zone_t z;
629 z = alloc_zone();
630 add_zone(inode, z, 2 * sizeof(struct direct), current_time);
631 enter_dir(inode, ".", inode);
632 enter_dir(inode, "..", inode);
633 incr_link(inode);
634 incr_link(inode);
637 void enter_symlink(ino_t inode, char *link)
639 zone_t z;
640 char *buf;
642 buf = alloc_block();
643 z = alloc_zone();
644 strcpy(buf, link);
645 put_block(z, buf);
647 add_zone(inode, z, (size_t) strlen(link), current_time);
649 free(buf);
653 /*================================================================
654 * eat_dir - recursively install directory
655 *===============================================================*/
656 void eat_dir(parent)
657 ino_t parent;
659 /* Read prototype lines and set up directory. Recurse if need be. */
660 char *token[MAX_TOKENS], *p;
661 char line[LINE_LEN];
662 int mode, usrid, grpid, maj, min, f;
663 ino_t n;
664 zone_t z;
665 size_t size;
667 while (1) {
668 getline(line, token);
669 p = token[0];
670 if (*p == '$') return;
671 p = token[1];
672 mode = mode_con(p);
673 usrid = atoi(token[2]);
674 grpid = atoi(token[3]);
675 n = alloc_inode(mode, usrid, grpid);
677 /* Enter name in directory and update directory's size. */
678 enter_dir(parent, token[0], n);
679 incr_size(parent, sizeof(struct direct));
681 /* Check to see if file is directory or special. */
682 incr_link(n);
683 if (*p == 'd') {
684 /* This is a directory. */
685 z = alloc_zone(); /* zone for new directory */
686 add_zone(n, z, 2 * sizeof(struct direct), current_time);
687 enter_dir(n, ".", n);
688 enter_dir(n, "..", parent);
689 incr_link(parent);
690 incr_link(n);
691 eat_dir(n);
692 } else if (*p == 'b' || *p == 'c') {
693 /* Special file. */
694 maj = atoi(token[4]);
695 min = atoi(token[5]);
696 size = 0;
697 if (token[6]) size = atoi(token[6]);
698 size = block_size * size;
699 add_zone(n, (zone_t) (makedev(maj,min)), size, current_time);
700 } else if (*p == 's') {
701 enter_symlink(n, token[4]);
702 } else {
703 /* Regular file. Go read it. */
704 if ((f = open(token[4], O_RDONLY)) < 0) {
705 fprintf(stderr, "%s: Can't open %s: %s\n",
706 progname, token[4], strerror(errno));
707 } else {
708 eat_file(n, f);
715 /*================================================================
716 * eat_file - copy file to MINIX
717 *===============================================================*/
718 /* Zonesize >= blocksize */
719 void eat_file(inode, f)
720 ino_t inode;
721 int f;
723 int ct, k;
724 zone_t z;
725 char *buf;
726 uint32_t timeval;
728 buf = alloc_block();
730 do {
731 for (k = 0; k < block_size; k++) buf[k] = 0;
732 if ((ct = read(f, buf, block_size)) > 0) {
733 z = alloc_zone();
734 put_block(z, buf);
736 timeval = (dflag ? current_time : file_time(f));
737 if (ct) add_zone(inode, z, (size_t) ct, timeval);
738 } while (ct == block_size);
739 close(f);
740 free(buf);
743 d2_inode *get_inoblock(ino_t i, block_t *blockno, d2_inode **ino)
745 int off;
746 d2_inode *inoblock = alloc_block();
747 *blockno = ((i - 1) / inodes_per_block) + inode_offset;
748 off = (i - 1) % inodes_per_block;
749 get_block(*blockno, (char *) inoblock);
750 *ino = inoblock + off;
751 return inoblock;
754 int dir_try_enter(zone_t z, ino_t child, char *name)
756 char *p1, *p2;
757 struct direct *dir_entry = alloc_block();
758 int r = 0;
759 int i;
761 get_block(z, (char *) dir_entry);
763 for (i = 0; i < NR_DIR_ENTRIES(block_size); i++)
764 if (!dir_entry[i].mfs_d_ino)
765 break;
767 if(i < NR_DIR_ENTRIES(block_size)) {
768 int j;
770 r = 1;
771 dir_entry[i].mfs_d_ino = child;
772 p1 = name;
773 p2 = dir_entry[i].mfs_d_name;
774 j = sizeof(dir_entry[i].mfs_d_name);
775 assert(j == 60);
776 while (j--) {
777 *p2++ = *p1;
778 if (*p1 != 0) p1++;
782 put_block(z, (char *) dir_entry);
783 free(dir_entry);
785 return r;
788 /*================================================================
789 * directory & inode management assist group
790 *===============================================================*/
791 void enter_dir(parent, name, child)
792 ino_t parent, child;
793 char *name;
795 /* Enter child in parent directory */
796 /* Works for dir > 1 block and zone > block */
797 unsigned int k;
798 block_t b;
799 zone_t z;
800 zone_t *indirblock = alloc_block();
801 d2_inode *ino;
802 d2_inode *inoblock = get_inoblock(parent, &b, &ino);
804 assert(!(block_size % sizeof(struct direct)));
806 for (k = 0; k < V2_NR_DZONES; k++) {
807 z = ino->d2_zone[k];
808 if (z == 0) {
809 z = alloc_zone();
810 ino->d2_zone[k] = z;
813 if(dir_try_enter(z, child, name)) {
814 put_block(b, (char *) inoblock);
815 free(inoblock);
816 free(indirblock);
817 return;
821 /* no space in directory using just direct blocks; try indirect */
822 if (ino->d2_zone[V2_NR_DZONES] == 0)
823 ino->d2_zone[V2_NR_DZONES] = alloc_zone();
825 get_block(ino->d2_zone[V2_NR_DZONES], (char *) indirblock);
827 for(k = 0; k < V2_INDIRECTS(block_size); k++) {
828 z = indirblock[k];
829 if(!z) z = indirblock[k] = alloc_zone();
831 if(dir_try_enter(z, child, name)) {
832 put_block(b, (char *) inoblock);
833 put_block(ino->d2_zone[V2_NR_DZONES], (char *) indirblock);
834 free(inoblock);
835 free(indirblock);
836 return;
840 printf("Directory-inode %lu beyond direct blocks. Could not enter %s\n",
841 parent, name);
842 pexit("Halt");
846 void add_zone(ino_t n, zone_t z, size_t bytes, uint32_t cur_time)
848 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
850 int off, i;
851 block_t b;
852 zone_t indir;
853 zone_t *blk;
854 d2_inode *p;
855 d2_inode *inode;
857 if(!(blk = malloc(V2_INDIRECTS(block_size)*sizeof(*blk))))
858 pexit("Couldn't allocate indirect block");
860 if(!(inode = malloc(V2_INODES_PER_BLOCK(block_size)*sizeof(*inode))))
861 pexit("Couldn't allocate block of inodes");
863 b = ((n - 1) / V2_INODES_PER_BLOCK(block_size)) + inode_offset;
864 off = (n - 1) % V2_INODES_PER_BLOCK(block_size);
865 get_block(b, (char *) inode);
866 p = &inode[off];
867 p->d2_size += bytes;
868 p->d2_mtime = cur_time;
869 for (i = 0; i < V2_NR_DZONES; i++)
870 if (p->d2_zone[i] == 0) {
871 p->d2_zone[i] = z;
872 put_block(b, (char *) inode);
873 free(blk);
874 free(inode);
875 return;
877 put_block(b, (char *) inode);
879 /* File has grown beyond a small file. */
880 if (p->d2_zone[V2_NR_DZONES] == 0) p->d2_zone[V2_NR_DZONES] = alloc_zone();
881 indir = p->d2_zone[V2_NR_DZONES];
882 put_block(b, (char *) inode);
883 b = indir;
884 get_block(b, (char *) blk);
885 for (i = 0; i < V2_INDIRECTS(block_size); i++)
886 if (blk[i] == 0) {
887 blk[i] = z;
888 put_block(b, (char *) blk);
889 free(blk);
890 free(inode);
891 return;
893 pexit("File has grown beyond single indirect");
897 void incr_link(n)
898 ino_t n;
900 /* Increment the link count to inode n */
901 int off;
902 static int enter = 0;
903 block_t b;
905 if(enter) exit(1);
907 b = ((n - 1) / inodes_per_block) + inode_offset;
908 off = (n - 1) % inodes_per_block;
910 static d2_inode *inode2 = NULL;
911 int n;
913 n = sizeof(*inode2) * V2_INODES_PER_BLOCK(block_size);
914 if(!inode2 && !(inode2 = malloc(n)))
915 pexit("couldn't allocate a block of inodes");
917 get_block(b, (char *) inode2);
918 inode2[off].d2_nlinks++;
919 put_block(b, (char *) inode2);
921 enter = 0;
925 void incr_size(n, count)
926 ino_t n;
927 size_t count;
929 /* Increment the file-size in inode n */
930 block_t b;
931 int off;
933 b = ((n - 1) / inodes_per_block) + inode_offset;
934 off = (n - 1) % inodes_per_block;
936 d2_inode *inode2;
937 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
938 pexit("couldn't allocate a block of inodes");
940 get_block(b, (char *) inode2);
941 inode2[off].d2_size += count;
942 put_block(b, (char *) inode2);
943 free(inode2);
948 /*================================================================
949 * allocation assist group
950 *===============================================================*/
951 static ino_t alloc_inode(mode, usrid, grpid)
952 int mode, usrid, grpid;
954 ino_t num;
955 int off;
956 block_t b;
958 num = next_inode++;
959 if (num > nrinodes) {
960 fprintf(stderr, "have %d inodoes\n", nrinodes);
961 pexit("File system does not have enough inodes");
963 b = ((num - 1) / inodes_per_block) + inode_offset;
964 off = (num - 1) % inodes_per_block;
966 d2_inode *inode2;
968 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
969 pexit("couldn't allocate a block of inodes");
971 get_block(b, (char *) inode2);
972 inode2[off].d2_mode = mode;
973 inode2[off].d2_uid = usrid;
974 inode2[off].d2_gid = grpid;
975 put_block(b, (char *) inode2);
977 free(inode2);
980 /* Set the bit in the bit map. */
981 /* DEBUG FIXME. This assumes the bit is in the first inode map block. */
982 insert_bit((block_t) INODE_MAP, (int) num);
983 return(num);
987 static zone_t alloc_zone()
989 /* Allocate a new zone */
990 /* Works for zone > block */
991 block_t b;
992 zone_t z;
994 z = next_zone++;
995 b = z;
996 if ((b + 1) > nrblocks)
997 pexit("File system not big enough for all the files");
998 put_block(b, zero); /* give an empty zone */
999 /* DEBUG FIXME. This assumes the bit is in the first zone map block. */
1000 insert_bit(zone_map, (int) (z - zoff)); /* lint, NOT OK because
1001 * z hasn't been broken
1002 * up into block +
1003 * offset yet. */
1004 return(z);
1008 void insert_bit(block, bit)
1009 block_t block;
1010 int bit;
1012 /* Insert 'count' bits in the bitmap */
1013 int w, s;
1014 #if defined(__minix)
1015 bitchunk_t *buf;
1016 #else
1017 uint32_t *buf;
1018 #endif
1020 #if defined(__minix)
1021 buf = (bitchunk_t *) alloc_block();
1022 #else
1023 buf = (uint32_t *) alloc_block();
1024 #endif
1026 get_block(block, (char *) buf);
1027 #if defined(__minix)
1028 w = bit / (8 * sizeof(bitchunk_t));
1029 s = bit % (8 * sizeof(bitchunk_t));
1030 #else
1031 w = bit / (8 * sizeof(uint32_t));
1032 s = bit % (8 * sizeof(uint32_t));
1033 #endif
1034 buf[w] |= (1 << s);
1035 put_block(block, (char *) buf);
1037 free(buf);
1041 /*================================================================
1042 * proto-file processing assist group
1043 *===============================================================*/
1044 int mode_con(p)
1045 char *p;
1047 /* Convert string to mode */
1048 int o1, o2, o3, mode;
1049 char c1, c2, c3;
1051 c1 = *p++;
1052 c2 = *p++;
1053 c3 = *p++;
1054 o1 = *p++ - '0';
1055 o2 = *p++ - '0';
1056 o3 = *p++ - '0';
1057 mode = (o1 << 6) | (o2 << 3) | o3;
1058 if (c1 == 'd') mode |= S_IFDIR;
1059 if (c1 == 'b') mode |= S_IFBLK;
1060 if (c1 == 'c') mode |= S_IFCHR;
1061 if (c1 == 's') mode |= S_IFLNK;
1062 if (c1 == '-') mode |= S_IFREG;
1063 if (c2 == 'u') mode |= S_ISUID;
1064 if (c3 == 'g') mode |= S_ISGID;
1065 return(mode);
1068 void getline(line, parse)
1069 char *parse[MAX_TOKENS];
1070 char line[LINE_LEN];
1072 /* Read a line and break it up in tokens */
1073 int k;
1074 char c, *p;
1075 int d;
1077 for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0;
1078 for (k = 0; k < LINE_LEN; k++) line[k] = 0;
1079 k = 0;
1080 parse[0] = 0;
1081 p = line;
1082 while (1) {
1083 if (++k > LINE_LEN) pexit("Line too long");
1084 d = fgetc(proto);
1085 if (d == EOF) pexit("Unexpected end-of-file");
1086 *p = d;
1087 if (*p == '\n') lct++;
1088 if (*p == ' ' || *p == '\t') *p = 0;
1089 if (*p == '\n') {
1090 *p++ = 0;
1091 *p = '\n';
1092 break;
1094 p++;
1097 k = 0;
1098 p = line;
1099 lastp = line;
1100 while (1) {
1101 c = *p++;
1102 if (c == '\n') return;
1103 if (c == 0) continue;
1104 parse[k++] = p - 1;
1105 do {
1106 c = *p++;
1107 } while (c != 0 && c != '\n');
1112 /*================================================================
1113 * other stuff
1114 *===============================================================*/
1115 void check_mtab(device)
1116 char *device; /* /dev/hd1 or whatever */
1118 /* Check to see if the special file named in s is mounted. */
1119 #if defined(__minix)
1120 int n, r;
1121 struct stat sb;
1122 char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1], version[10], rw_flag[10];
1124 r= stat(device, &sb);
1125 if (r == -1)
1127 if (errno == ENOENT)
1128 return; /* Does not exist, and therefore not mounted. */
1129 fprintf(stderr, "%s: stat %s failed: %s\n",
1130 progname, device, strerror(errno));
1131 exit(1);
1133 if (!S_ISBLK(sb.st_mode))
1135 /* Not a block device and therefore not mounted. */
1136 return;
1139 if (load_mtab("mkfs") < 0) return;
1140 while (1) {
1141 n = get_mtab_entry(special, mounted_on, version, rw_flag);
1142 if (n < 0) return;
1143 if (strcmp(device, special) == 0) {
1144 /* Can't mkfs on top of a mounted file system. */
1145 fprintf(stderr, "%s: %s is mounted on %s\n",
1146 progname, device, mounted_on);
1147 exit(1);
1150 #elif defined(__linux__)
1151 /* XXX: this code is copyright Theodore T'so and distributed under the GPLv2. Rewrite.
1153 struct mntent *mnt;
1154 struct stat st_buf;
1155 dev_t file_dev=0, file_rdev=0;
1156 ino_t file_ino=0;
1157 FILE *f;
1158 int fd;
1159 char *mtab_file = "/proc/mounts";
1161 if ((f = setmntent (mtab_file, "r")) == NULL)
1162 goto error;
1164 if (stat(device, &st_buf) == 0) {
1165 if (S_ISBLK(st_buf.st_mode)) {
1166 file_rdev = st_buf.st_rdev;
1167 } else {
1168 file_dev = st_buf.st_dev;
1169 file_ino = st_buf.st_ino;
1173 while ((mnt = getmntent (f)) != NULL) {
1174 if (strcmp(device, mnt->mnt_fsname) == 0)
1175 break;
1176 if (stat(mnt->mnt_fsname, &st_buf) == 0) {
1177 if (S_ISBLK(st_buf.st_mode)) {
1178 if (file_rdev && (file_rdev == st_buf.st_rdev))
1179 break;
1180 } else {
1181 if (file_dev && ((file_dev == st_buf.st_dev) &&
1182 (file_ino == st_buf.st_ino)))
1183 break;
1188 if (mnt == NULL) {
1190 * Do an extra check to see if this is the root device. We
1191 * can't trust /etc/mtab, and /proc/mounts will only list
1192 * /dev/root for the root filesystem. Argh. Instead we
1193 * check if the given device has the same major/minor number
1194 * as the device that the root directory is on.
1196 if (file_rdev && stat("/", &st_buf) == 0) {
1197 if (st_buf.st_dev == file_rdev) {
1198 goto is_root;
1201 goto test_busy;
1203 /* Validate the entry in case /etc/mtab is out of date */
1205 * We need to be paranoid, because some broken distributions
1206 * (read: Slackware) don't initialize /etc/mtab before checking
1207 * all of the non-root filesystems on the disk.
1209 if (stat(mnt->mnt_dir, &st_buf) < 0) {
1210 if (errno == ENOENT) {
1211 goto test_busy;
1213 goto error;
1215 if (file_rdev && (st_buf.st_dev != file_rdev)) {
1216 goto error;
1219 fprintf(stderr, "Device %s is mounted, exiting\n", device);
1220 exit(-1);
1223 * Check to see if we're referring to the root filesystem.
1224 * If so, do a manual check to see if we can open /etc/mtab
1225 * read/write, since if the root is mounted read/only, the
1226 * contents of /etc/mtab may not be accurate.
1228 if (!strcmp(mnt->mnt_dir, "/")) {
1229 is_root:
1230 fprintf(stderr, "Device %s is mounted as root file system!\n",
1231 device);
1232 exit(-1);
1235 test_busy:
1237 endmntent (f);
1238 if ((stat(device, &st_buf) != 0) ||
1239 !S_ISBLK(st_buf.st_mode))
1240 return;
1241 fd = open(device, O_RDONLY | O_EXCL);
1242 if (fd < 0) {
1243 if (errno == EBUSY) {
1244 fprintf(stderr, "Device %s is used by the system\n", device);
1245 exit(-1);
1247 } else
1248 close(fd);
1250 return;
1252 error:
1253 endmntent (f);
1254 fprintf(stderr, "Error while checking if device %s is mounted\n", device);
1255 exit(-1);
1256 #endif
1260 uint32_t file_time(f)
1261 int f;
1263 struct stat statbuf;
1264 fstat(f, &statbuf);
1265 return(statbuf.st_mtime);
1269 void pexit(s)
1270 char *s;
1272 fprintf(stderr, "%s: %s\n", progname, s);
1273 if (lct != 0)
1274 fprintf(stderr, "Line %d being processed when error detected.\n", lct);
1275 exit(2);
1279 void copy(from, to, count)
1280 char *from, *to;
1281 size_t count;
1283 while (count--) *to++ = *from++;
1286 void *alloc_block()
1288 char *buf;
1290 if(!(buf = malloc(block_size))) {
1291 pexit("couldn't allocate filesystem buffer");
1293 bzero(buf, block_size);
1295 return buf;
1298 void print_fs()
1300 int i, j;
1301 ino_t k;
1302 d2_inode *inode2;
1303 unsigned short *usbuf;
1304 block_t b;
1305 struct direct *dir;
1307 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
1308 pexit("couldn't allocate a block of inodes");
1310 if(!(dir = malloc(NR_DIR_ENTRIES(block_size)*sizeof(*dir))))
1311 pexit("malloc of directory entry failed");
1313 usbuf = (unsigned short *) alloc_block();
1315 get_super_block((char *) usbuf);
1316 printf("\nSuperblock: ");
1317 for (i = 0; i < 8; i++) printf("%06o ", usbuf[i]);
1318 get_block((block_t) 2, (char *) usbuf);
1319 printf("...\nInode map: ");
1320 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1321 get_block((block_t) 3, (char *) usbuf);
1322 printf("...\nZone map: ");
1323 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1324 printf("...\n");
1326 free(usbuf);
1327 usbuf = NULL;
1329 k = 0;
1330 for (b = inode_offset; k < nrinodes; b++) {
1331 get_block(b, (char *) inode2);
1332 for (i = 0; i < inodes_per_block; i++) {
1333 k = inodes_per_block * (int) (b - inode_offset) + i + 1;
1334 /* Lint but OK */
1335 if (k > nrinodes) break;
1337 if (inode2[i].d2_mode != 0) {
1338 printf("Inode %2lu: mode=", k);
1339 printf("%06o", inode2[i].d2_mode);
1340 printf(" uid=%2d gid=%2d size=",
1341 inode2[i].d2_uid, inode2[i].d2_gid);
1342 printf("%6d", inode2[i].d2_size);
1343 printf(" zone[0]=%u\n", inode2[i].d2_zone[0]);
1345 if ((inode2[i].d2_mode & S_IFMT) == S_IFDIR) {
1346 /* This is a directory */
1347 get_block(inode2[i].d2_zone[0], (char *) dir);
1348 for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1349 if (dir[j].mfs_d_ino)
1350 printf("\tInode %2u: %s\n", dir[j].mfs_d_ino, dir[j].mfs_d_name);
1356 printf("%d inodes used. %d zones used.\n", next_inode - 1, next_zone);
1357 free(dir);
1358 free(inode2);
1362 int read_and_set(n)
1363 block_t n;
1365 /* The first time a block is read, it returns all 0s, unless there has
1366 * been a write. This routine checks to see if a block has been accessed.
1369 int w, s, mask, r;
1371 w = n / 8;
1372 if(w >= umap_array_elements) {
1373 pexit("umap array too small - this can't happen");
1375 s = n % 8;
1376 mask = 1 << s;
1377 r = (umap_array[w] & mask ? 1 : 0);
1378 umap_array[w] |= mask;
1379 return(r);
1382 void usage()
1384 fprintf(stderr,
1385 "Usage: %s [-12dlot] [-b blocks] [-i inodes]\n"
1386 "\t[-x extra] [-B blocksize] special [proto]\n",
1387 progname);
1388 exit(1);
1391 void special(string)
1392 char *string;
1394 fd = creat(string, 0777);
1395 close(fd);
1396 fd = open(string, O_RDWR);
1397 if (fd < 0) pexit("Can't open special file");
1402 void get_block(n, buf)
1403 block_t n;
1404 char *buf;
1406 /* Read a block. */
1408 int k;
1410 /* First access returns a zero block */
1411 if (read_and_set(n) == 0) {
1412 copy(zero, buf, block_size);
1413 return;
1415 lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL);
1416 k = read(fd, buf, block_size);
1417 if (k != block_size) {
1418 pexit("get_block couldn't read");
1422 void get_super_block(buf)
1423 char *buf;
1425 /* Read a block. */
1427 int k;
1429 if(lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET) < 0) {
1430 perror("lseek");
1431 pexit("seek failed");
1433 k = read(fd, buf, _STATIC_BLOCK_SIZE);
1434 if (k != _STATIC_BLOCK_SIZE) {
1435 pexit("get_super_block couldn't read");
1439 void put_block(n, buf)
1440 block_t n;
1441 char *buf;
1443 /* Write a block. */
1445 (void) read_and_set(n);
1447 /* XXX - check other lseeks too. */
1448 if (lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL) == (off_t) -1) {
1449 pexit("put_block couldn't seek");
1451 if (write(fd, buf, block_size) != block_size) {
1452 pexit("put_block couldn't write");