ldivmod, uldivmod: fix qdivrem calls
[minix.git] / usr.sbin / mkfs.mfs / mkfs.c
blobc21d50d9d5adf067376c3eb907db55c66fe94156
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 #if HAVE_NBTOOL_CONFIG_H
14 #include "nbtool_config.h"
15 #endif
17 #include <sys/cdefs.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <stdio.h>
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <stdint.h>
30 #include "const.h"
31 #include "type.h"
32 #include "mfsdir.h"
33 #if defined(__minix)
34 #include <minix/partition.h>
35 #include <minix/u64.h>
36 #include <minix/minlib.h>
37 #include <sys/ioctl.h>
38 #endif
39 #include <dirent.h>
41 #undef EXTERN
42 #define EXTERN /* get rid of EXTERN by making it null */
43 #include "super.h"
45 #ifndef max
46 #define max(a,b) ((a) > (b) ? (a) : (b))
47 #endif
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 INODE_MAX ((unsigned) 65535)
56 #define SECTOR_SIZE 512
59 #if !defined(__minix)
60 #define mul64u(a,b) ((a) * (b))
61 #define lseek64(a,b,c,d) lseek(a,b,c)
62 #ifdef __linux__
63 #include <mntent.h>
64 #endif
65 #endif
67 #if !defined(__minix)
68 typedef uint32_t block_t;
69 typedef uint32_t zone_t;
70 #endif
72 extern char *optarg;
73 extern int optind;
75 int next_zone, next_inode, 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 uint32_t current_time, bin_time;
84 char *zero, *lastp;
85 char *umap_array; /* bit map tells if block read yet */
86 int umap_array_elements = 0;
87 block_t zone_map; /* where is zone map? (depends on # inodes) */
88 int inodes_per_block;
89 size_t block_size;
90 int extra_space_percent;
92 FILE *proto;
94 #if defined(__NBSD_LIBC) || !defined(__minix)
95 #define getline _mkfs_getline
96 #endif
98 int main(int argc, char **argv);
99 block_t sizeup(char *device);
100 void super(zone_t zones, ino_t inodes);
101 void rootdir(ino_t inode);
102 int dir_try_enter(zone_t z, ino_t child, char *name);
103 void eat_dir(ino_t parent);
104 void eat_file(ino_t inode, int f);
105 void enter_dir(ino_t parent, char const *name, ino_t child);
106 void enter_symlink(ino_t inode, char *link);
107 d2_inode *get_inoblock(ino_t i, block_t *blockno, d2_inode **ino);
108 void incr_size(ino_t n, size_t count);
109 static ino_t alloc_inode(int mode, int usrid, int grpid);
110 static zone_t alloc_zone(void);
111 void add_zone(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
112 void add_z_1(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
113 void add_z_2(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
114 void incr_link(ino_t n);
115 void insert_bit(block_t block, int bit);
116 int mode_con(char *p);
117 void getline(char line[LINE_LEN], char *parse[MAX_TOKENS]);
118 void check_mtab(char *devname);
119 uint32_t file_time(int f);
120 __dead void pexit(char const *s);
121 void copy(char *from, char *to, size_t count);
122 void print_fs(void);
123 int read_and_set(block_t n);
124 void special(char *string);
125 void get_block(block_t n, char *buf);
126 void get_super_block(char *buf);
127 void put_block(block_t n, char *buf);
128 void mx_read(int blocknr, char *buf);
129 void mx_write(int blocknr, char *buf);
130 void dexit(char *s, int sectnum, int err);
131 __dead void usage(void);
132 void *alloc_block(void);
134 ino_t inocount;
135 zone_t zonecount;
136 block_t blockcount;
138 void detect_fs_size(void);
139 void sizeup_dir(void);
140 void detect_size(void);
141 void size_dir(void);
142 static int bitmapsize(uint32_t nr_bits, size_t block_size);
144 /*================================================================
145 * mkfs - make filesystem
146 *===============================================================*/
147 int main(argc, argv)
148 int argc;
149 char *argv[];
151 int nread, mode, usrid, grpid, ch;
152 block_t blocks, maxblocks;
153 size_t i;
154 ino_t root_inum;
155 ino_t inodes;
156 zone_t zones;
157 char *token[MAX_TOKENS], line[LINE_LEN];
158 struct stat statbuf;
160 /* Get two times, the current time and the mod time of the binary of
161 * mkfs itself. When the -d flag is used, the later time is put into
162 * the i_mtimes of all the files. This feature is useful when
163 * producing a set of file systems, and one wants all the times to be
164 * identical. First you set the time of the mkfs binary to what you
165 * want, then go.
167 current_time = time((time_t *) 0); /* time mkfs is being run */
168 stat(argv[0], &statbuf);
169 bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */
171 /* Process switches. */
172 progname = argv[0];
173 blocks = 0;
174 i = 0;
175 inodes_per_block = 0;
176 block_size = 0;
177 extra_space_percent = 0;
178 while ((ch = getopt(argc, argv, "b:di:lotB:x:")) != EOF)
179 switch (ch) {
180 case 'b':
181 blocks = strtoul(optarg, (char **) NULL, 0);
182 break;
183 case 'd':
184 dflag = 1;
185 current_time = bin_time;
186 break;
187 case 'i':
188 i = strtoul(optarg, (char **) NULL, 0);
189 break;
190 case 'l': print = 1; break;
191 case 'o': override = 1; break;
192 case 't': donttest = 1; break;
193 case 'B': block_size = atoi(optarg); break;
194 case 'x': extra_space_percent = atoi(optarg); break;
195 default: usage();
198 if (argc == optind) usage();
200 /* Percentage of extra size must be nonnegative.
201 * It can legitimately be bigger than 100 but has to make some sort of sense.
203 if(extra_space_percent < 0 || extra_space_percent > 2000) usage();
206 if(!block_size) block_size = _MAX_BLOCK_SIZE; /* V3 default block size */
207 if(block_size%SECTOR_SIZE || block_size < _MIN_BLOCK_SIZE) {
208 fprintf(stderr, "block size must be multiple of sector (%d) "
209 "and at least %d bytes\n",
210 SECTOR_SIZE, _MIN_BLOCK_SIZE);
211 pexit("specified block size illegal");
213 if(block_size%V2_INODE_SIZE) {
214 fprintf(stderr, "block size must be a multiple of inode size (%d bytes)\n",
215 V2_INODE_SIZE);
216 pexit("specified block size illegal");
220 if(!inodes_per_block)
221 inodes_per_block = V2_INODES_PER_BLOCK(block_size);
223 /* now that the block size is known, do buffer allocations where
224 * possible.
226 zero = alloc_block();
227 bzero(zero, block_size);
229 /* Determine the size of the device if not specified as -b or proto. */
230 maxblocks = sizeup(argv[optind]);
231 if (argc - optind == 1 && blocks == 0) {
232 blocks = maxblocks;
233 /* blocks == 0 is checked later, but leads to a funny way of
234 * reporting a 0-sized device (displays usage).
236 if(blocks < 1) {
237 fprintf(stderr, "%s: zero size device.\n", progname);
238 return 1;
242 /* The remaining args must be 'special proto', or just 'special' if the
243 * no. of blocks has already been specified.
245 if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage();
247 if (blocks > maxblocks) {
248 fprintf(stderr, "%s: %s: number of blocks too large for device.\n",
249 progname, argv[optind]);
250 return 1;
253 /* Check special. */
254 check_mtab(argv[optind]);
256 /* Check and start processing proto. */
257 optarg = argv[++optind];
258 if (optind < argc && (proto = fopen(optarg, "r")) != NULL) {
259 /* Prototype file is readable. */
260 lct = 1;
261 getline(line, token); /* skip boot block info */
263 /* Read the line with the block and inode counts. */
264 getline(line, token);
265 blocks = atol(token[0]);
266 inodes = atoi(token[1]);
268 /* Process mode line for root directory. */
269 getline(line, token);
270 mode = mode_con(token[0]);
271 usrid = atoi(token[1]);
272 grpid = atoi(token[2]);
274 if(blocks <= 0 && inodes <= 0){
275 detect_fs_size();
276 blocks = blockcount;
277 inodes = inocount;
278 blocks += blocks*extra_space_percent/100;
279 inodes += inodes*extra_space_percent/100;
280 printf("dynamically sized filesystem: %d blocks, %d inodes\n", blocks,
281 (unsigned int) inodes);
283 } else {
284 lct = 0;
285 if (optind < argc) {
286 /* Maybe the prototype file is just a size. Check. */
287 blocks = strtoul(optarg, (char **) NULL, 0);
288 if (blocks == 0) pexit("Can't open prototype file");
290 if (i == 0) {
291 #if defined(__minix)
292 uint32_t kb = div64u(mul64u(blocks, block_size), 1024);
293 #else
294 uint32_t kb = ((unsigned long long) blocks * block_size) / 1024;
295 #endif
296 i = kb / 2;
297 if (kb >= 100000) i = kb / 4;
299 /* round up to fill inode block */
300 i += inodes_per_block - 1;
301 i = i / inodes_per_block * inodes_per_block;
303 if (blocks < 5) pexit("Block count too small");
304 if (i < 1) pexit("Inode count too small");
305 inodes = (ino_t) i;
307 /* Make simple file system of the given size, using defaults. */
308 mode = 040777;
309 usrid = BIN;
310 grpid = BINGRP;
311 simple = 1;
314 nrblocks = blocks;
315 nrinodes = inodes;
318 size_t bytes;
319 bytes = 1 + blocks/8;
320 if(!(umap_array = malloc(bytes))) {
321 fprintf(stderr, "mkfs: can't allocate block bitmap (%u bytes).\n",
322 bytes);
323 exit(1);
325 umap_array_elements = bytes;
328 /* Open special. */
329 special(argv[--optind]);
331 if (!donttest) {
332 short *testb;
333 ssize_t w;
335 testb = (short *) alloc_block();
337 /* Try writing the last block of partition or diskette. */
338 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
339 pexit("couldn't seek to last block to test size (1)");
341 testb[0] = 0x3245;
342 testb[1] = 0x11FF;
343 testb[block_size/2-1] = 0x1F2F;
344 if ((w=write(fd, (char *) testb, block_size)) != block_size) {
345 if(w < 0) perror("write");
346 printf("%d/%u\n", w, block_size);
347 pexit("File system is too big for minor device (write)");
349 sync(); /* flush write, so if error next read fails */
350 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
351 pexit("couldn't seek to last block to test size (2)");
353 testb[0] = 0;
354 testb[1] = 0;
355 nread = read(fd, (char *) testb, block_size);
356 if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF ||
357 testb[block_size/2-1] != 0x1F2F) {
358 if(nread < 0) perror("read");
359 printf("nread = %d\n", nread);
360 printf("testb = 0x%x 0x%x 0x%x\n", testb[0], testb[1], testb[block_size-1]);
361 pexit("File system is too big for minor device (read)");
363 lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL);
364 testb[0] = 0;
365 testb[1] = 0;
366 if (write(fd, (char *) testb, block_size) != block_size)
367 pexit("File system is too big for minor device (write2)");
368 lseek(fd, 0L, SEEK_SET);
369 free(testb);
372 /* Make the file-system */
374 put_block((block_t) 0, zero); /* Write a null boot block. */
376 zones = nrblocks;
378 super(zones, inodes);
380 root_inum = alloc_inode(mode, usrid, grpid);
381 rootdir(root_inum);
382 if (simple == 0) eat_dir(root_inum);
384 if (print) print_fs();
385 return(0);
387 /* NOTREACHED */
388 } /* end main */
390 /*================================================================
391 * detect_fs_size - determine image size dynamically
392 *===============================================================*/
393 void detect_fs_size()
395 uint32_t point = ftell(proto);
397 inocount = 1; /* root directory node */
398 zonecount = 0;
399 blockcount = 0;
400 sizeup_dir();
402 uint32_t initb;
404 initb = bitmapsize((uint32_t) (1 + inocount), block_size);
405 initb += bitmapsize((uint32_t) zonecount, block_size);
406 initb += START_BLOCK;
407 initb += (inocount + inodes_per_block - 1) / inodes_per_block;
409 blockcount = initb+zonecount;
410 fseek(proto, point, SEEK_SET);
413 void sizeup_dir()
415 char *token[MAX_TOKENS], *p;
416 char line[LINE_LEN];
417 FILE *f;
418 size_t size;
419 int dir_entries = 2;
420 zone_t dir_zones = 0;
421 zone_t nr_dzones;
423 nr_dzones = V2_NR_DZONES;
425 while (1) {
426 getline(line, token);
427 p = token[0];
428 if (*p == '$') {
429 dir_zones = (dir_entries / (NR_DIR_ENTRIES(block_size)));
430 if(dir_entries % (NR_DIR_ENTRIES(block_size)))
431 dir_zones++;
432 if(dir_zones > nr_dzones)
433 dir_zones++; /* Max single indir */
434 zonecount += dir_zones;
435 return;
438 p = token[1];
439 inocount++;
440 dir_entries++;
442 if (*p == 'd') {
443 sizeup_dir();
444 } else if (*p == 'b' || *p == 'c') {
446 } else if (*p == 's') {
447 zonecount++; /* Symlink contents is always stored a block */
448 } else {
449 if ((f = fopen(token[4], "r")) == NULL) {
450 fprintf(stderr, "%s: Can't open %s: %s\n",
451 progname, token[4], strerror(errno));
452 pexit("dynamic size detection failed");
453 } else {
454 if (fseek(f, 0, SEEK_END) < 0) {
455 fprintf(stderr, "%s: Can't seek to end of %s\n",
456 progname, token[4]);
457 pexit("dynamic size detection failed");
459 size = ftell(f);
460 fclose(f);
461 zone_t fzones= (size / block_size);
462 if (size % block_size)
463 fzones++;
464 if (fzones > nr_dzones)
465 fzones++; /* Assumes files fit within single indirect */
466 zonecount += fzones;
472 /*================================================================
473 * sizeup - determine device size
474 *===============================================================*/
475 block_t sizeup(device)
476 char *device;
478 block_t d;
479 #if defined(__minix)
480 u64_t bytes, resize;
481 u32_t rem;
482 #else
483 off_t size;
484 #endif
487 if ((fd = open(device, O_RDONLY)) == -1) {
488 if (errno != ENOENT)
489 perror("sizeup open");
490 return 0;
493 #if defined(__minix)
494 if(minix_sizeup(device, &bytes) < 0) {
495 perror("sizeup");
496 return 0;
499 d = div64u(bytes, block_size);
500 rem = rem64u(bytes, block_size);
502 resize = add64u(mul64u(d, block_size), rem);
503 if(cmp64(resize, bytes) != 0) {
504 d = ULONG_MAX;
505 fprintf(stderr, "mkfs: truncating FS at %u blocks\n", d);
507 #else
508 size = lseek(fd, 0, SEEK_END);
509 if (size == (off_t) -1) {
510 fprintf(stderr, "Cannot get device size fd=%d\n", fd);
511 exit(-1);
513 d = size / block_size;
514 #endif
516 return d;
520 * copied from fslib
522 static int bitmapsize(uint32_t nr_bits, size_t blk_size)
524 block_t nr_blocks;
526 nr_blocks = (int) (nr_bits / FS_BITS_PER_BLOCK(blk_size));
527 if (((uint32_t) nr_blocks * FS_BITS_PER_BLOCK(blk_size)) < nr_bits)
528 ++nr_blocks;
529 return(nr_blocks);
532 /*================================================================
533 * super - construct a superblock
534 *===============================================================*/
536 void super(zones, inodes)
537 zone_t zones;
538 ino_t inodes;
540 unsigned int i;
541 int inodeblks;
542 int initblks;
543 uint32_t nb;
544 zone_t v2sq;
545 zone_t zo;
546 struct super_block *sup;
547 char *buf, *cp;
549 buf = alloc_block();
551 for (cp = buf; cp < &buf[block_size]; cp++) *cp = 0;
552 sup = (struct super_block *) buf; /* lint - might use a union */
554 /* The assumption is that mkfs will create a clean FS. */
555 sup->s_flags = MFSFLAG_CLEAN;
557 sup->s_ninodes = inodes;
558 sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */
559 sup->s_zones = zones;
561 #define BIGGERBLOCKS "Please try a larger block size for an FS of this size.\n"
562 sup->s_imap_blocks = nb = bitmapsize((uint32_t) (1 + inodes), block_size);
563 if(sup->s_imap_blocks != nb) {
564 fprintf(stderr, "mkfs: too many inode bitmap blocks.\n" BIGGERBLOCKS);
565 exit(1);
567 sup->s_zmap_blocks = nb = bitmapsize((uint32_t) zones, block_size);
568 if(nb != sup->s_zmap_blocks) {
569 fprintf(stderr, "mkfs: too many block bitmap blocks.\n" BIGGERBLOCKS);
570 exit(1);
572 inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks;
573 inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block;
574 initblks = inode_offset + inodeblks;
575 sup->s_firstdatazone_old = nb = initblks;
576 if(nb >= zones) pexit("bit maps too large");
577 if(nb != sup->s_firstdatazone_old) {
578 /* The field is too small to store the value. Fortunately, the value
579 * can be computed from other fields. We set the on-disk field to zero
580 * to indicate that it must not be used. Eventually, we can always set
581 * the on-disk field to zero, and stop using it.
583 sup->s_firstdatazone_old = 0;
585 sup->s_firstdatazone = nb;
586 zoff = sup->s_firstdatazone - 1;
587 sup->s_log_zone_size = 0;
589 v2sq = (zone_t) V2_INDIRECTS(block_size) * V2_INDIRECTS(block_size);
590 zo = V2_NR_DZONES + (zone_t) V2_INDIRECTS(block_size) + v2sq;
592 sup->s_magic = SUPER_V3;
593 sup->s_block_size = block_size;
594 sup->s_disk_version = 0;
595 #define MAX_MAX_SIZE (INT_MAX)
596 if(MAX_MAX_SIZE/block_size < zo) {
597 sup->s_max_size = (int32_t) MAX_MAX_SIZE;
599 else {
600 sup->s_max_size = zo * block_size;
605 if (lseek(fd, (off_t) _STATIC_BLOCK_SIZE, SEEK_SET) == (off_t) -1) {
606 pexit("super() couldn't seek");
608 if (write(fd, buf, _STATIC_BLOCK_SIZE) != _STATIC_BLOCK_SIZE) {
609 pexit("super() couldn't write");
612 /* Clear maps and inodes. */
613 for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero);
615 next_zone = sup->s_firstdatazone;
616 next_inode = 1;
618 zone_map = INODE_MAP + sup->s_imap_blocks;
620 insert_bit(zone_map, 0); /* bit zero must always be allocated */
621 insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but
622 * must be allocated */
624 free(buf);
628 /*================================================================
629 * rootdir - install the root directory
630 *===============================================================*/
631 void rootdir(inode)
632 ino_t inode;
634 zone_t z;
636 z = alloc_zone();
637 add_zone(inode, z, 2 * sizeof(struct direct), current_time);
638 enter_dir(inode, ".", inode);
639 enter_dir(inode, "..", inode);
640 incr_link(inode);
641 incr_link(inode);
644 void enter_symlink(ino_t inode, char *lnk)
646 zone_t z;
647 char *buf;
649 buf = alloc_block();
650 z = alloc_zone();
651 strcpy(buf, lnk);
652 put_block(z, buf);
654 add_zone(inode, z, (size_t) strlen(lnk), current_time);
656 free(buf);
660 /*================================================================
661 * eat_dir - recursively install directory
662 *===============================================================*/
663 void eat_dir(parent)
664 ino_t parent;
666 /* Read prototype lines and set up directory. Recurse if need be. */
667 char *token[MAX_TOKENS], *p;
668 char line[LINE_LEN];
669 int mode, usrid, grpid, maj, min, f;
670 ino_t n;
671 zone_t z;
672 size_t size;
674 while (1) {
675 getline(line, token);
676 p = token[0];
677 if (*p == '$') return;
678 p = token[1];
679 mode = mode_con(p);
680 usrid = atoi(token[2]);
681 grpid = atoi(token[3]);
682 n = alloc_inode(mode, usrid, grpid);
684 /* Enter name in directory and update directory's size. */
685 enter_dir(parent, token[0], n);
686 incr_size(parent, sizeof(struct direct));
688 /* Check to see if file is directory or special. */
689 incr_link(n);
690 if (*p == 'd') {
691 /* This is a directory. */
692 z = alloc_zone(); /* zone for new directory */
693 add_zone(n, z, 2 * sizeof(struct direct), current_time);
694 enter_dir(n, ".", n);
695 enter_dir(n, "..", parent);
696 incr_link(parent);
697 incr_link(n);
698 eat_dir(n);
699 } else if (*p == 'b' || *p == 'c') {
700 /* Special file. */
701 maj = atoi(token[4]);
702 min = atoi(token[5]);
703 size = 0;
704 if (token[6]) size = atoi(token[6]);
705 size = block_size * size;
706 add_zone(n, (zone_t) (makedev(maj,min)), size, current_time);
707 } else if (*p == 's') {
708 enter_symlink(n, token[4]);
709 } else {
710 /* Regular file. Go read it. */
711 if ((f = open(token[4], O_RDONLY)) < 0) {
712 fprintf(stderr, "%s: Can't open %s: %s\n",
713 progname, token[4], strerror(errno));
714 } else {
715 eat_file(n, f);
722 /*================================================================
723 * eat_file - copy file to MINIX
724 *===============================================================*/
725 /* Zonesize >= blocksize */
726 void eat_file(inode, f)
727 ino_t inode;
728 int f;
730 int ct, k;
731 zone_t z = 0;
732 char *buf;
733 uint32_t timeval;
735 buf = alloc_block();
737 do {
738 for (k = 0; k < block_size; k++) buf[k] = 0;
739 if ((ct = read(f, buf, block_size)) > 0) {
740 z = alloc_zone();
741 put_block(z, buf);
743 timeval = (dflag ? current_time : file_time(f));
744 if (ct) add_zone(inode, z, (size_t) ct, timeval);
745 } while (ct == block_size);
746 close(f);
747 free(buf);
750 d2_inode *get_inoblock(ino_t i, block_t *blockno, d2_inode **ino)
752 int off;
753 d2_inode *inoblock = alloc_block();
754 *blockno = ((i - 1) / inodes_per_block) + inode_offset;
755 off = (i - 1) % inodes_per_block;
756 get_block(*blockno, (char *) inoblock);
757 *ino = inoblock + off;
758 return inoblock;
761 int dir_try_enter(zone_t z, ino_t child, char *name)
763 char *p1, *p2;
764 struct direct *dir_entry = alloc_block();
765 int r = 0;
766 int i;
768 get_block(z, (char *) dir_entry);
770 for (i = 0; i < NR_DIR_ENTRIES(block_size); i++)
771 if (!dir_entry[i].mfs_d_ino)
772 break;
774 if(i < NR_DIR_ENTRIES(block_size)) {
775 int j;
777 r = 1;
778 dir_entry[i].mfs_d_ino = child;
779 p1 = name;
780 p2 = dir_entry[i].mfs_d_name;
781 j = sizeof(dir_entry[i].mfs_d_name);
782 assert(j == 60);
783 while (j--) {
784 *p2++ = *p1;
785 if (*p1 != 0) p1++;
789 put_block(z, (char *) dir_entry);
790 free(dir_entry);
792 return r;
795 /*================================================================
796 * directory & inode management assist group
797 *===============================================================*/
798 void enter_dir(ino_t parent, char const *name, ino_t child)
800 /* Enter child in parent directory */
801 /* Works for dir > 1 block and zone > block */
802 unsigned int k;
803 block_t b;
804 zone_t z;
805 zone_t *indirblock = alloc_block();
806 d2_inode *ino;
807 d2_inode *inoblock = get_inoblock(parent, &b, &ino);
809 assert(!(block_size % sizeof(struct direct)));
811 for (k = 0; k < V2_NR_DZONES; k++) {
812 z = ino->d2_zone[k];
813 if (z == 0) {
814 z = alloc_zone();
815 ino->d2_zone[k] = z;
818 if(dir_try_enter(z, child, __UNCONST(name))) {
819 put_block(b, (char *) inoblock);
820 free(inoblock);
821 free(indirblock);
822 return;
826 /* no space in directory using just direct blocks; try indirect */
827 if (ino->d2_zone[V2_NR_DZONES] == 0)
828 ino->d2_zone[V2_NR_DZONES] = alloc_zone();
830 get_block(ino->d2_zone[V2_NR_DZONES], (char *) indirblock);
832 for(k = 0; k < V2_INDIRECTS(block_size); k++) {
833 z = indirblock[k];
834 if(!z) z = indirblock[k] = alloc_zone();
836 if(dir_try_enter(z, child, __UNCONST(name))) {
837 put_block(b, (char *) inoblock);
838 put_block(ino->d2_zone[V2_NR_DZONES], (char *) indirblock);
839 free(inoblock);
840 free(indirblock);
841 return;
845 printf("Directory-inode %u beyond direct blocks. Could not enter %s\n",
846 parent, name);
847 pexit("Halt");
851 void add_zone(ino_t n, zone_t z, size_t bytes, uint32_t cur_time)
853 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
855 int off, i;
856 block_t b;
857 zone_t indir;
858 zone_t *blk;
859 d2_inode *p;
860 d2_inode *inode;
862 if(!(blk = malloc(V2_INDIRECTS(block_size)*sizeof(*blk))))
863 pexit("Couldn't allocate indirect block");
865 if(!(inode = malloc(V2_INODES_PER_BLOCK(block_size)*sizeof(*inode))))
866 pexit("Couldn't allocate block of inodes");
868 b = ((n - 1) / V2_INODES_PER_BLOCK(block_size)) + inode_offset;
869 off = (n - 1) % V2_INODES_PER_BLOCK(block_size);
870 get_block(b, (char *) inode);
871 p = &inode[off];
872 p->d2_size += bytes;
873 p->d2_mtime = cur_time;
874 for (i = 0; i < V2_NR_DZONES; i++)
875 if (p->d2_zone[i] == 0) {
876 p->d2_zone[i] = z;
877 put_block(b, (char *) inode);
878 free(blk);
879 free(inode);
880 return;
882 put_block(b, (char *) inode);
884 /* File has grown beyond a small file. */
885 if (p->d2_zone[V2_NR_DZONES] == 0) p->d2_zone[V2_NR_DZONES] = alloc_zone();
886 indir = p->d2_zone[V2_NR_DZONES];
887 put_block(b, (char *) inode);
888 b = indir;
889 get_block(b, (char *) blk);
890 for (i = 0; i < V2_INDIRECTS(block_size); i++)
891 if (blk[i] == 0) {
892 blk[i] = z;
893 put_block(b, (char *) blk);
894 free(blk);
895 free(inode);
896 return;
898 pexit("File has grown beyond single indirect");
902 void incr_link(n)
903 ino_t n;
905 /* Increment the link count to inode n */
906 int off;
907 static int enter = 0;
908 block_t b;
910 if(enter) exit(1);
912 b = ((n - 1) / inodes_per_block) + inode_offset;
913 off = (n - 1) % inodes_per_block;
915 static d2_inode *inode2 = NULL;
916 int s;
918 s = sizeof(*inode2) * V2_INODES_PER_BLOCK(block_size);
919 if(!inode2 && !(inode2 = malloc(s)))
920 pexit("couldn't allocate a block of inodes");
922 get_block(b, (char *) inode2);
923 inode2[off].d2_nlinks++;
924 put_block(b, (char *) inode2);
926 enter = 0;
930 void incr_size(n, count)
931 ino_t n;
932 size_t count;
934 /* Increment the file-size in inode n */
935 block_t b;
936 int off;
938 b = ((n - 1) / inodes_per_block) + inode_offset;
939 off = (n - 1) % inodes_per_block;
941 d2_inode *inode2;
942 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
943 pexit("couldn't allocate a block of inodes");
945 get_block(b, (char *) inode2);
946 inode2[off].d2_size += count;
947 put_block(b, (char *) inode2);
948 free(inode2);
953 /*================================================================
954 * allocation assist group
955 *===============================================================*/
956 static ino_t alloc_inode(mode, usrid, grpid)
957 int mode, usrid, grpid;
959 ino_t num;
960 int off;
961 block_t b;
963 num = next_inode++;
964 if (num > nrinodes) {
965 fprintf(stderr, "have %d inodoes\n", nrinodes);
966 pexit("File system does not have enough inodes");
968 b = ((num - 1) / inodes_per_block) + inode_offset;
969 off = (num - 1) % inodes_per_block;
971 d2_inode *inode2;
973 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
974 pexit("couldn't allocate a block of inodes");
976 get_block(b, (char *) inode2);
977 inode2[off].d2_mode = mode;
978 inode2[off].d2_uid = usrid;
979 inode2[off].d2_gid = grpid;
980 put_block(b, (char *) inode2);
982 free(inode2);
985 /* Set the bit in the bit map. */
986 /* DEBUG FIXME. This assumes the bit is in the first inode map block. */
987 insert_bit((block_t) INODE_MAP, (int) num);
988 return(num);
992 static zone_t alloc_zone()
994 /* Allocate a new zone */
995 /* Works for zone > block */
996 block_t b;
997 zone_t z;
999 z = next_zone++;
1000 b = z;
1001 if ((b + 1) > nrblocks)
1002 pexit("File system not big enough for all the files");
1003 put_block(b, zero); /* give an empty zone */
1004 /* DEBUG FIXME. This assumes the bit is in the first zone map block. */
1005 insert_bit(zone_map, (int) (z - zoff)); /* lint, NOT OK because
1006 * z hasn't been broken
1007 * up into block +
1008 * offset yet. */
1009 return(z);
1013 void insert_bit(block, bit)
1014 block_t block;
1015 int bit;
1017 /* Insert 'count' bits in the bitmap */
1018 int w, s;
1019 #if defined(__minix)
1020 bitchunk_t *buf;
1021 #else
1022 uint32_t *buf;
1023 #endif
1025 #if defined(__minix)
1026 buf = (bitchunk_t *) alloc_block();
1027 #else
1028 buf = (uint32_t *) alloc_block();
1029 #endif
1031 get_block(block, (char *) buf);
1032 #if defined(__minix)
1033 w = bit / (8 * sizeof(bitchunk_t));
1034 s = bit % (8 * sizeof(bitchunk_t));
1035 #else
1036 w = bit / (8 * sizeof(uint32_t));
1037 s = bit % (8 * sizeof(uint32_t));
1038 #endif
1039 buf[w] |= (1 << s);
1040 put_block(block, (char *) buf);
1042 free(buf);
1046 /*================================================================
1047 * proto-file processing assist group
1048 *===============================================================*/
1049 int mode_con(p)
1050 char *p;
1052 /* Convert string to mode */
1053 int o1, o2, o3, mode;
1054 char c1, c2, c3;
1056 c1 = *p++;
1057 c2 = *p++;
1058 c3 = *p++;
1059 o1 = *p++ - '0';
1060 o2 = *p++ - '0';
1061 o3 = *p++ - '0';
1062 mode = (o1 << 6) | (o2 << 3) | o3;
1063 if (c1 == 'd') mode |= S_IFDIR;
1064 if (c1 == 'b') mode |= S_IFBLK;
1065 if (c1 == 'c') mode |= S_IFCHR;
1066 if (c1 == 's') mode |= S_IFLNK;
1067 if (c1 == '-') mode |= S_IFREG;
1068 if (c2 == 'u') mode |= S_ISUID;
1069 if (c3 == 'g') mode |= S_ISGID;
1070 return(mode);
1073 void getline(line, parse)
1074 char *parse[MAX_TOKENS];
1075 char line[LINE_LEN];
1077 /* Read a line and break it up in tokens */
1078 int k;
1079 char c, *p;
1080 int d;
1082 for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0;
1083 for (k = 0; k < LINE_LEN; k++) line[k] = 0;
1084 k = 0;
1085 parse[0] = 0;
1086 p = line;
1087 while (1) {
1088 if (++k > LINE_LEN) pexit("Line too long");
1089 d = fgetc(proto);
1090 if (d == EOF) pexit("Unexpected end-of-file");
1091 *p = d;
1092 if (*p == '\n') lct++;
1093 if (*p == ' ' || *p == '\t') *p = 0;
1094 if (*p == '\n') {
1095 *p++ = 0;
1096 *p = '\n';
1097 break;
1099 p++;
1102 k = 0;
1103 p = line;
1104 lastp = line;
1105 while (1) {
1106 c = *p++;
1107 if (c == '\n') return;
1108 if (c == 0) continue;
1109 parse[k++] = p - 1;
1110 do {
1111 c = *p++;
1112 } while (c != 0 && c != '\n');
1117 /*================================================================
1118 * other stuff
1119 *===============================================================*/
1120 void check_mtab(device)
1121 char *device; /* /dev/hd1 or whatever */
1123 /* Check to see if the special file named in s is mounted. */
1124 #if defined(__minix)
1125 int n, r;
1126 struct stat sb;
1127 char dev[PATH_MAX], mount_point[PATH_MAX],
1128 type[MNTNAMELEN], flags[MNTFLAGLEN];
1130 r= stat(device, &sb);
1131 if (r == -1)
1133 if (errno == ENOENT)
1134 return; /* Does not exist, and therefore not mounted. */
1135 fprintf(stderr, "%s: stat %s failed: %s\n",
1136 progname, device, strerror(errno));
1137 exit(1);
1139 if (!S_ISBLK(sb.st_mode))
1141 /* Not a block device and therefore not mounted. */
1142 return;
1145 if (load_mtab(__UNCONST("mkfs")) < 0) return;
1146 while (1) {
1147 n = get_mtab_entry(dev, mount_point, type, flags);
1148 if (n < 0) return;
1149 if (strcmp(device, dev) == 0) {
1150 /* Can't mkfs on top of a mounted file system. */
1151 fprintf(stderr, "%s: %s is mounted on %s\n",
1152 progname, device, mount_point);
1153 exit(1);
1156 #elif defined(__linux__)
1157 /* XXX: this code is copyright Theodore T'so and distributed under the GPLv2. Rewrite.
1159 struct mntent *mnt;
1160 struct stat st_buf;
1161 dev_t file_dev=0, file_rdev=0;
1162 ino_t file_ino=0;
1163 FILE *f;
1164 int fd;
1165 char *mtab_file = "/proc/mounts";
1167 if ((f = setmntent (mtab_file, "r")) == NULL)
1168 goto error;
1170 if (stat(device, &st_buf) == 0) {
1171 if (S_ISBLK(st_buf.st_mode)) {
1172 file_rdev = st_buf.st_rdev;
1173 } else {
1174 file_dev = st_buf.st_dev;
1175 file_ino = st_buf.st_ino;
1179 while ((mnt = getmntent (f)) != NULL) {
1180 if (strcmp(device, mnt->mnt_fsname) == 0)
1181 break;
1182 if (stat(mnt->mnt_fsname, &st_buf) == 0) {
1183 if (S_ISBLK(st_buf.st_mode)) {
1184 if (file_rdev && (file_rdev == st_buf.st_rdev))
1185 break;
1186 } else {
1187 if (file_dev && ((file_dev == st_buf.st_dev) &&
1188 (file_ino == st_buf.st_ino)))
1189 break;
1194 if (mnt == NULL) {
1196 * Do an extra check to see if this is the root device. We
1197 * can't trust /etc/mtab, and /proc/mounts will only list
1198 * /dev/root for the root filesystem. Argh. Instead we
1199 * check if the given device has the same major/minor number
1200 * as the device that the root directory is on.
1202 if (file_rdev && stat("/", &st_buf) == 0) {
1203 if (st_buf.st_dev == file_rdev) {
1204 goto is_root;
1207 goto test_busy;
1209 /* Validate the entry in case /etc/mtab is out of date */
1211 * We need to be paranoid, because some broken distributions
1212 * (read: Slackware) don't initialize /etc/mtab before checking
1213 * all of the non-root filesystems on the disk.
1215 if (stat(mnt->mnt_dir, &st_buf) < 0) {
1216 if (errno == ENOENT) {
1217 goto test_busy;
1219 goto error;
1221 if (file_rdev && (st_buf.st_dev != file_rdev)) {
1222 goto error;
1225 fprintf(stderr, "Device %s is mounted, exiting\n", device);
1226 exit(-1);
1229 * Check to see if we're referring to the root filesystem.
1230 * If so, do a manual check to see if we can open /etc/mtab
1231 * read/write, since if the root is mounted read/only, the
1232 * contents of /etc/mtab may not be accurate.
1234 if (!strcmp(mnt->mnt_dir, "/")) {
1235 is_root:
1236 fprintf(stderr, "Device %s is mounted as root file system!\n",
1237 device);
1238 exit(-1);
1241 test_busy:
1243 endmntent (f);
1244 if ((stat(device, &st_buf) != 0) ||
1245 !S_ISBLK(st_buf.st_mode))
1246 return;
1247 fd = open(device, O_RDONLY | O_EXCL);
1248 if (fd < 0) {
1249 if (errno == EBUSY) {
1250 fprintf(stderr, "Device %s is used by the system\n", device);
1251 exit(-1);
1253 } else
1254 close(fd);
1256 return;
1258 error:
1259 endmntent (f);
1260 fprintf(stderr, "Error while checking if device %s is mounted\n", device);
1261 exit(-1);
1262 #endif
1266 uint32_t file_time(f)
1267 int f;
1269 struct stat statbuf;
1270 fstat(f, &statbuf);
1271 return(statbuf.st_mtime);
1275 __dead void pexit(char const * s)
1277 fprintf(stderr, "%s: %s\n", progname, s);
1278 if (lct != 0)
1279 fprintf(stderr, "Line %d being processed when error detected.\n", lct);
1280 exit(2);
1284 void copy(from, to, count)
1285 char *from, *to;
1286 size_t count;
1288 while (count--) *to++ = *from++;
1291 void *alloc_block()
1293 char *buf;
1295 if(!(buf = malloc(block_size))) {
1296 pexit("couldn't allocate filesystem buffer");
1298 bzero(buf, block_size);
1300 return buf;
1303 void print_fs()
1305 int i, j;
1306 ino_t k;
1307 d2_inode *inode2;
1308 unsigned short *usbuf;
1309 block_t b;
1310 struct direct *dir;
1312 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
1313 pexit("couldn't allocate a block of inodes");
1315 if(!(dir = malloc(NR_DIR_ENTRIES(block_size)*sizeof(*dir))))
1316 pexit("malloc of directory entry failed");
1318 usbuf = (unsigned short *) alloc_block();
1320 get_super_block((char *) usbuf);
1321 printf("\nSuperblock: ");
1322 for (i = 0; i < 8; i++) printf("%06o ", usbuf[i]);
1323 get_block((block_t) 2, (char *) usbuf);
1324 printf("...\nInode map: ");
1325 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1326 get_block((block_t) 3, (char *) usbuf);
1327 printf("...\nZone map: ");
1328 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1329 printf("...\n");
1331 free(usbuf);
1332 usbuf = NULL;
1334 k = 0;
1335 for (b = inode_offset; k < nrinodes; b++) {
1336 get_block(b, (char *) inode2);
1337 for (i = 0; i < inodes_per_block; i++) {
1338 k = inodes_per_block * (int) (b - inode_offset) + i + 1;
1339 /* Lint but OK */
1340 if (k > nrinodes) break;
1342 if (inode2[i].d2_mode != 0) {
1343 printf("Inode %2u: mode=", k);
1344 printf("%06o", inode2[i].d2_mode);
1345 printf(" uid=%2d gid=%2d size=",
1346 inode2[i].d2_uid, inode2[i].d2_gid);
1347 printf("%6d", inode2[i].d2_size);
1348 printf(" zone[0]=%u\n", inode2[i].d2_zone[0]);
1350 if ((inode2[i].d2_mode & S_IFMT) == S_IFDIR) {
1351 /* This is a directory */
1352 get_block(inode2[i].d2_zone[0], (char *) dir);
1353 for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1354 if (dir[j].mfs_d_ino)
1355 printf("\tInode %2u: %s\n", dir[j].mfs_d_ino, dir[j].mfs_d_name);
1361 printf("%d inodes used. %d zones used.\n", next_inode - 1, next_zone);
1362 free(dir);
1363 free(inode2);
1367 int read_and_set(n)
1368 block_t n;
1370 /* The first time a block is read, it returns all 0s, unless there has
1371 * been a write. This routine checks to see if a block has been accessed.
1374 int w, s, mask, r;
1376 w = n / 8;
1377 if(w >= umap_array_elements) {
1378 pexit("umap array too small - this can't happen");
1380 s = n % 8;
1381 mask = 1 << s;
1382 r = (umap_array[w] & mask ? 1 : 0);
1383 umap_array[w] |= mask;
1384 return(r);
1387 __dead void usage()
1389 fprintf(stderr,
1390 "Usage: %s [-12dlot] [-b blocks] [-i inodes]\n"
1391 "\t[-x extra] [-B blocksize] special [proto]\n",
1392 progname);
1393 exit(1);
1396 void special(string)
1397 char *string;
1399 fd = creat(string, 0777);
1400 close(fd);
1401 fd = open(string, O_RDWR);
1402 if (fd < 0) pexit("Can't open special file");
1407 void get_block(n, buf)
1408 block_t n;
1409 char *buf;
1411 /* Read a block. */
1413 int k;
1415 /* First access returns a zero block */
1416 if (read_and_set(n) == 0) {
1417 copy(zero, buf, block_size);
1418 return;
1420 lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL);
1421 k = read(fd, buf, block_size);
1422 if (k != block_size) {
1423 pexit("get_block couldn't read");
1427 void get_super_block(buf)
1428 char *buf;
1430 /* Read a block. */
1432 int k;
1434 if(lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET) < 0) {
1435 perror("lseek");
1436 pexit("seek failed");
1438 k = read(fd, buf, _STATIC_BLOCK_SIZE);
1439 if (k != _STATIC_BLOCK_SIZE) {
1440 pexit("get_super_block couldn't read");
1444 void put_block(n, buf)
1445 block_t n;
1446 char *buf;
1448 /* Write a block. */
1450 (void) read_and_set(n);
1452 /* XXX - check other lseeks too. */
1453 if (lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL) == (off_t) -1) {
1454 pexit("put_block couldn't seek");
1456 if (write(fd, buf, block_size) != block_size) {
1457 pexit("put_block couldn't write");