mkfs, mkproto: minor improvements
[minix.git] / usr.sbin / mkfs.mfs / mkfs.c
blob1cbc761b73b75116466914011572db50d0573d85
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 <errno.h>
17 #include <fcntl.h>
18 #include <limits.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <stdint.h>
24 #include "const.h"
25 #include "type.h"
26 #include "mfsdir.h"
27 #if defined(__minix)
28 #include <minix/partition.h>
29 #include <minix/u64.h>
30 #include <sys/ioctl.h>
31 #endif
32 #include <dirent.h>
34 #undef EXTERN
35 #define EXTERN /* get rid of EXTERN by making it null */
36 #include "super.h"
38 #ifndef max
39 #define max(a,b) ((a) > (b) ? (a) : (b))
40 #endif
42 #ifndef DOS
43 #ifndef UNIX
44 #define UNIX
45 #endif
46 #endif
48 #define INODE_MAP 2
49 #define MAX_TOKENS 10
50 #define LINE_LEN 200
51 #define BIN 2
52 #define BINGRP 2
53 #define BIT_MAP_SHIFT 13
54 #define INODE_MAX ((unsigned) 65535)
55 #define SECTOR_SIZE 512
58 #ifdef DOS
59 maybedefine O_RDONLY 4 /* O_RDONLY | BINARY_BIT */
60 maybedefine BWRITE 5 /* O_WRONLY | BINARY_BIT */
61 #endif
63 #if !defined(__minix)
64 #define mul64u(a,b) ((a) * (b))
65 #define lseek64(a,b,c,d) lseek(a,b,c)
66 #ifdef __linux__
67 #include <mntent.h>
68 #endif
69 #endif
71 #if !defined(__minix)
72 typedef uint32_t block_t;
73 typedef uint32_t zone_t;
74 #endif
76 extern char *optarg;
77 extern int optind;
79 int next_zone, next_inode, zone_size, zone_shift = 0, zoff;
80 block_t nrblocks;
81 int inode_offset, lct = 0, disk, fd, print = 0, file = 0;
82 unsigned int nrinodes;
83 int override = 0, simple = 0, dflag;
84 int donttest; /* skip test if it fits on medium */
85 char *progname;
87 uint32_t current_time, bin_time;
88 char *zero, *lastp;
89 char *umap_array; /* bit map tells if block read yet */
90 int umap_array_elements = 0;
91 block_t zone_map; /* where is zone map? (depends on # inodes) */
92 int inodes_per_block;
93 int fs_version;
94 size_t block_size;
95 int extra_space_percent;
97 FILE *proto;
99 #if defined(__NBSD_LIBC) || !defined(__minix)
100 #define getline _mkfs_getline
101 #endif
103 int main(int argc, char **argv);
104 block_t sizeup(char *device);
105 void super(zone_t zones, ino_t inodes);
106 void rootdir(ino_t inode);
107 void eat_dir(ino_t parent);
108 void eat_file(ino_t inode, int f);
109 void enter_dir(ino_t parent, char *name, ino_t child);
110 void incr_size(ino_t n, size_t count);
111 static ino_t alloc_inode(int mode, int usrid, int grpid);
112 static zone_t alloc_zone(void);
113 void add_zone(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
114 void add_z_1(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
115 void add_z_2(ino_t n, zone_t z, size_t bytes, uint32_t cur_time);
116 void incr_link(ino_t n);
117 void insert_bit(block_t block, int bit);
118 int mode_con(char *p);
119 void getline(char line[LINE_LEN], char *parse[MAX_TOKENS]);
120 void check_mtab(char *devname);
121 uint32_t file_time(int f);
122 void pexit(char *s);
123 void copy(char *from, char *to, size_t count);
124 void print_fs(void);
125 int read_and_set(block_t n);
126 void special(char *string);
127 void get_block(block_t n, char *buf);
128 void get_super_block(char *buf);
129 void put_block(block_t n, char *buf);
130 void cache_init(void);
131 void flush(void);
132 void mx_read(int blocknr, char *buf);
133 void mx_write(int blocknr, char *buf);
134 void dexit(char *s, int sectnum, int err);
135 void usage(void);
136 char *alloc_block(void);
138 ino_t inocount;
139 zone_t zonecount;
140 block_t blockcount;
142 void detect_fs_size(void);
143 void sizeup_dir(void);
144 void detect_size(void);
145 void size_dir(void);
146 static int bitmapsize(uint32_t nr_bits, size_t block_size);
148 /*================================================================
149 * mkfs - make filesystem
150 *===============================================================*/
151 int main(argc, argv)
152 int argc;
153 char *argv[];
155 int nread, mode, usrid, grpid, ch;
156 block_t blocks, maxblocks;
157 size_t i;
158 ino_t root_inum;
159 ino_t inodes;
160 zone_t zones;
161 char *token[MAX_TOKENS], line[LINE_LEN];
162 struct stat statbuf;
164 /* Get two times, the current time and the mod time of the binary of
165 * mkfs itself. When the -d flag is used, the later time is put into
166 * the i_mtimes of all the files. This feature is useful when
167 * producing a set of file systems, and one wants all the times to be
168 * identical. First you set the time of the mkfs binary to what you
169 * want, then go.
171 current_time = time((time_t *) 0); /* time mkfs is being run */
172 stat(argv[0], &statbuf);
173 bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */
175 /* Process switches. */
176 progname = argv[0];
177 blocks = 0;
178 i = 0;
179 fs_version = 3;
180 inodes_per_block = 0;
181 block_size = 0;
182 extra_space_percent = 0;
183 while ((ch = getopt(argc, argv, "12b:di:lotB:x:")) != EOF)
184 switch (ch) {
185 case '1':
186 fs_version = 1;
187 inodes_per_block = V1_INODES_PER_BLOCK;
188 break;
189 case '2':
190 fs_version = 2;
191 break;
192 case 'b':
193 blocks = strtoul(optarg, (char **) NULL, 0);
194 break;
195 case 'd':
196 dflag = 1;
197 current_time = bin_time;
198 break;
199 case 'i':
200 i = strtoul(optarg, (char **) NULL, 0);
201 break;
202 case 'l': print = 1; break;
203 case 'o': override = 1; break;
204 case 't': donttest = 1; break;
205 case 'B': block_size = atoi(optarg); break;
206 case 'x': extra_space_percent = atoi(optarg); break;
207 default: usage();
210 if (argc == optind) usage();
212 /* Percentage of extra size must be nonnegative.
213 * It can legitimately be bigger than 100 but has to make some sort of sense.
215 if(extra_space_percent < 0 || extra_space_percent > 2000) usage();
217 if(fs_version == 3) {
218 if(!block_size) block_size = _MAX_BLOCK_SIZE; /* V3 default block size */
219 if(block_size%SECTOR_SIZE || block_size < _MIN_BLOCK_SIZE) {
220 fprintf(stderr, "block size must be multiple of sector (%d) "
221 "and at least %d bytes\n",
222 SECTOR_SIZE, _MIN_BLOCK_SIZE);
223 pexit("specified block size illegal");
225 if(block_size%V2_INODE_SIZE) {
226 fprintf(stderr, "block size must be a multiple of inode size (%d bytes)\n",
227 V2_INODE_SIZE);
228 pexit("specified block size illegal");
230 } else {
231 if(block_size) {
232 pexit("Can't specify a block size if FS version is <3");
234 block_size = _STATIC_BLOCK_SIZE; /* V1/V2 block size */
237 zone_shift = 0; /* for future use */
238 zone_size = 1 << zone_shift; /* nr of blocks per zone */
240 if(!inodes_per_block)
241 inodes_per_block = V2_INODES_PER_BLOCK(block_size);
243 /* now that the block size is known, do buffer allocations where
244 * possible.
246 zero = alloc_block();
247 bzero(zero, block_size);
249 /* Determine the size of the device if not specified as -b or proto. */
250 maxblocks = sizeup(argv[optind]);
251 if (argc - optind == 1 && blocks == 0) {
252 blocks = maxblocks;
253 /* blocks == 0 is checked later, but leads to a funny way of
254 * reporting a 0-sized device (displays usage).
256 if(blocks < 1) {
257 fprintf(stderr, "%s: zero size device.\n", progname);
258 return 1;
262 /* The remaining args must be 'special proto', or just 'special' if the
263 * no. of blocks has already been specified.
265 if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage();
267 if (blocks > maxblocks) {
268 fprintf(stderr, "%s: %s: number of blocks too large for device.\n",
269 progname, argv[optind]);
270 return 1;
273 /* Check special. */
274 check_mtab(argv[optind]);
276 /* Check and start processing proto. */
277 optarg = argv[++optind];
278 if (optind < argc && (proto = fopen(optarg, "r")) != NULL) {
279 /* Prototype file is readable. */
280 lct = 1;
281 getline(line, token); /* skip boot block info */
283 /* Read the line with the block and inode counts. */
284 getline(line, token);
285 blocks = atol(token[0]);
286 inodes = atoi(token[1]);
288 /* Process mode line for root directory. */
289 getline(line, token);
290 mode = mode_con(token[0]);
291 usrid = atoi(token[1]);
292 grpid = atoi(token[2]);
294 if(blocks <= 0 && inodes <= 0){
295 block_t extrablocks = 0;
296 ino_t extrainodes = 0;
297 if(blocks < 0) extrablocks = -blocks;
298 if(inodes < 0) extrainodes = -inodes;
299 detect_fs_size();
300 blocks = blockcount + extrablocks;
301 inodes = inocount + extrainodes;
302 blocks += blocks*extra_space_percent/100;
303 inodes += inodes*extra_space_percent/100;
304 printf("dynamically sized filesystem: %d blocks, %d inodes\n", blocks,
305 (unsigned int) inodes);
307 } else {
308 lct = 0;
309 if (optind < argc) {
310 /* Maybe the prototype file is just a size. Check. */
311 blocks = strtoul(optarg, (char **) NULL, 0);
312 if (blocks == 0) pexit("Can't open prototype file");
314 if (i == 0) {
315 #if defined(__minix)
316 uint32_t kb = div64u(mul64u(blocks, block_size), 1024);
317 #else
318 uint32_t kb = ((unsigned long long) blocks * block_size) / 1024;
319 #endif
320 i = kb / 2;
321 if (kb >= 100000) i = kb / 4;
323 /* round up to fill inode block */
324 i += inodes_per_block - 1;
325 i = i / inodes_per_block * inodes_per_block;
326 if (i > INODE_MAX && fs_version < 3) i = INODE_MAX;
329 if (blocks < 5) pexit("Block count too small");
330 if (i < 1) pexit("Inode count too small");
331 if (i > INODE_MAX && fs_version < 3) pexit("Inode count too large");
332 inodes = (ino_t) i;
334 /* Make simple file system of the given size, using defaults. */
335 mode = 040777;
336 usrid = BIN;
337 grpid = BINGRP;
338 simple = 1;
341 nrblocks = blocks;
342 nrinodes = inodes;
345 size_t bytes;
346 bytes = 1 + blocks/8;
347 if(!(umap_array = malloc(bytes))) {
348 fprintf(stderr, "mkfs: can't allocate block bitmap (%u bytes).\n",
349 bytes);
350 exit(1);
352 umap_array_elements = bytes;
355 /* Open special. */
356 special(argv[--optind]);
358 #ifdef UNIX
359 if (!donttest) {
360 short *testb;
361 ssize_t w;
363 testb = (short *) alloc_block();
365 /* Try writing the last block of partition or diskette. */
366 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
367 pexit("couldn't seek to last block to test size (1)");
369 testb[0] = 0x3245;
370 testb[1] = 0x11FF;
371 testb[block_size/2-1] = 0x1F2F;
372 if ((w=write(fd, (char *) testb, block_size)) != block_size) {
373 if(w < 0) perror("write");
374 printf("%d/%u\n", w, block_size);
375 pexit("File system is too big for minor device (write)");
377 sync(); /* flush write, so if error next read fails */
378 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
379 pexit("couldn't seek to last block to test size (2)");
381 testb[0] = 0;
382 testb[1] = 0;
383 nread = read(fd, (char *) testb, block_size);
384 if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF ||
385 testb[block_size/2-1] != 0x1F2F) {
386 if(nread < 0) perror("read");
387 printf("nread = %d\n", nread);
388 printf("testb = 0x%x 0x%x 0x%x\n", testb[0], testb[1], testb[block_size-1]);
389 pexit("File system is too big for minor device (read)");
391 lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL);
392 testb[0] = 0;
393 testb[1] = 0;
394 if (write(fd, (char *) testb, block_size) != block_size)
395 pexit("File system is too big for minor device (write2)");
396 lseek(fd, 0L, SEEK_SET);
397 free(testb);
399 #endif
401 /* Make the file-system */
403 cache_init();
405 put_block((block_t) 0, zero); /* Write a null boot block. */
407 zones = nrblocks >> zone_shift;
409 super(zones, inodes);
411 root_inum = alloc_inode(mode, usrid, grpid);
412 rootdir(root_inum);
413 if (simple == 0) eat_dir(root_inum);
415 if (print) print_fs();
416 flush();
417 return(0);
419 /* NOTREACHED */
420 } /* end main */
422 /*================================================================
423 * detect_fs_size - determine image size dynamically
424 *===============================================================*/
425 void detect_fs_size()
427 uint32_t point = ftell(proto);
429 inocount = 1; /* root directory node */
430 zonecount = 0;
431 blockcount = 0;
432 sizeup_dir();
434 uint32_t initb;
436 initb = bitmapsize((uint32_t) (1 + inocount), block_size);
437 initb += bitmapsize((uint32_t) zonecount, block_size);
438 initb += START_BLOCK;
439 initb += (inocount + inodes_per_block - 1) / inodes_per_block;
440 initb = (initb + (1 << zone_shift) - 1) >> zone_shift;
442 blockcount = initb+zonecount*zone_size;
443 fseek(proto, point, SEEK_SET);
446 void sizeup_dir()
448 char *token[MAX_TOKENS], *p;
449 char line[LINE_LEN];
450 FILE *f;
451 size_t size;
452 int dir_entries = 2;
453 zone_t dir_zones = 0;
454 zone_t nr_dzones;
456 if (fs_version == 1) {
457 nr_dzones = V1_NR_DZONES;
458 } else {
459 nr_dzones = V2_NR_DZONES;
462 while (1) {
463 getline(line, token);
464 p = token[0];
465 if (*p == '$') {
466 dir_zones = (dir_entries / (NR_DIR_ENTRIES(block_size) * zone_size));
467 if(dir_entries % (NR_DIR_ENTRIES(block_size) * zone_size))
468 dir_zones++;
469 /* Assumes directory fits in direct blocks */
470 zonecount += dir_zones;
471 return;
474 p = token[1];
475 inocount++;
476 dir_entries++;
478 if (*p == 'd') {
479 sizeup_dir();
480 } else if (*p == 'b' || *p == 'c') {
482 } else {
483 if ((f = fopen(token[4], "r")) == NULL) {
484 fprintf(stderr, "%s: Can't open %s: %s\n",
485 progname, token[4], strerror(errno));
486 pexit("dynamic size detection failed");
487 } else {
488 if (fseek(f, 0, SEEK_END) < 0) {
489 fprintf(stderr, "%s: Can't seek to end of %s\n",
490 progname, token[4]);
491 pexit("dynamic size detection failed");
493 size = ftell(f);
494 fclose(f);
495 zone_t fzones= (size / (zone_size * block_size));
496 if (size % (zone_size * block_size))
497 fzones++;
498 if (fzones > nr_dzones)
499 fzones++; /* Assumes files fit within single indirect */
500 zonecount += fzones;
506 /*================================================================
507 * sizeup - determine device size
508 *===============================================================*/
509 block_t sizeup(device)
510 char *device;
512 block_t d;
513 #if defined(__minix)
514 u64_t bytes, resize;
515 u32_t rem;
516 #else
517 off_t size;
518 #endif
521 if ((fd = open(device, O_RDONLY)) == -1) {
522 if (errno != ENOENT)
523 perror("sizeup open");
524 return 0;
527 #if defined(__minix)
528 if(minix_sizeup(device, &bytes) < 0) {
529 perror("sizeup");
530 return 0;
533 d = div64u(bytes, block_size);
534 rem = rem64u(bytes, block_size);
536 resize = add64u(mul64u(d, block_size), rem);
537 if(cmp64(resize, bytes) != 0) {
538 d = ULONG_MAX;
539 fprintf(stderr, "mkfs: truncating FS at %u blocks\n", d);
541 #else
542 size = lseek(fd, 0, SEEK_END);
543 if (size == (off_t) -1) {
544 fprintf(stderr, "Cannot get device size fd=%d\n", fd);
545 exit(-1);
547 d = size / block_size;
548 #endif
550 return d;
554 * copied from fslib
556 static int bitmapsize(nr_bits, block_size)
557 uint32_t nr_bits;
558 size_t block_size;
560 block_t nr_blocks;
562 nr_blocks = (int) (nr_bits / FS_BITS_PER_BLOCK(block_size));
563 if (((uint32_t) nr_blocks * FS_BITS_PER_BLOCK(block_size)) < nr_bits) ++nr_blocks;
564 return(nr_blocks);
567 /*================================================================
568 * super - construct a superblock
569 *===============================================================*/
571 void super(zones, inodes)
572 zone_t zones;
573 ino_t inodes;
575 unsigned int i;
576 int inodeblks;
577 int initblks;
578 uint32_t nb;
579 zone_t v1sq, v2sq;
580 zone_t zo;
581 struct super_block *sup;
582 char *buf, *cp;
584 buf = alloc_block();
586 for (cp = buf; cp < &buf[block_size]; cp++) *cp = 0;
587 sup = (struct super_block *) buf; /* lint - might use a union */
589 /* The assumption is that mkfs will create a clean FS. */
590 sup->s_flags = MFSFLAG_CLEAN;
592 sup->s_ninodes = inodes;
593 if (fs_version == 1) {
594 sup->s_nzones = zones;
595 if (sup->s_nzones != zones) pexit("too many zones");
596 } else {
597 sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */
598 sup->s_zones = zones;
601 #define BIGGERBLOCKS "Please try a larger block size for an FS of this size.\n"
602 sup->s_imap_blocks = nb = bitmapsize((uint32_t) (1 + inodes), block_size);
603 if(sup->s_imap_blocks != nb) {
604 fprintf(stderr, "mkfs: too many inode bitmap blocks.\n" BIGGERBLOCKS);
605 exit(1);
607 sup->s_zmap_blocks = nb = bitmapsize((uint32_t) zones, block_size);
608 if(nb != sup->s_zmap_blocks) {
609 fprintf(stderr, "mkfs: too many block bitmap blocks.\n" BIGGERBLOCKS);
610 exit(1);
612 inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks;
613 inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block;
614 initblks = inode_offset + inodeblks;
615 sup->s_firstdatazone_old = nb =
616 (initblks + (1 << zone_shift) - 1) >> zone_shift;
617 if(nb >= zones) pexit("bit maps too large");
618 if(nb != sup->s_firstdatazone_old) {
619 /* The field is too small to store the value. Fortunately, the value
620 * can be computed from other fields. We set the on-disk field to zero
621 * to indicate that it must not be used. Eventually, we can always set
622 * the on-disk field to zero, and stop using it.
624 sup->s_firstdatazone_old = 0;
626 sup->s_firstdatazone = nb;
627 zoff = sup->s_firstdatazone - 1;
628 sup->s_log_zone_size = zone_shift;
629 if (fs_version == 1) {
630 sup->s_magic = SUPER_MAGIC; /* identify super blocks */
631 v1sq = (zone_t) V1_INDIRECTS * V1_INDIRECTS;
632 zo = V1_NR_DZONES + (int) V1_INDIRECTS + v1sq;
633 sup->s_max_size = zo * block_size;
634 } else {
635 v2sq = (zone_t) V2_INDIRECTS(block_size) * V2_INDIRECTS(block_size);
636 zo = V2_NR_DZONES + (zone_t) V2_INDIRECTS(block_size) + v2sq;
637 if(fs_version == 2) {
638 sup->s_magic = SUPER_V2;/* identify super blocks */
639 sup->s_max_size = zo * block_size;
640 } else {
641 sup->s_magic = SUPER_V3;
642 sup->s_block_size = block_size;
643 sup->s_disk_version = 0;
644 #define MAX_MAX_SIZE (INT_MAX)
645 if(MAX_MAX_SIZE/block_size < zo) {
646 sup->s_max_size = (int32_t) MAX_MAX_SIZE;
648 else {
649 sup->s_max_size = zo * block_size;
654 if (lseek(fd, (off_t) _STATIC_BLOCK_SIZE, SEEK_SET) == (off_t) -1) {
655 pexit("super() couldn't seek");
657 if (write(fd, buf, _STATIC_BLOCK_SIZE) != _STATIC_BLOCK_SIZE) {
658 pexit("super() couldn't write");
661 /* Clear maps and inodes. */
662 for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero);
664 next_zone = sup->s_firstdatazone;
665 next_inode = 1;
667 zone_map = INODE_MAP + sup->s_imap_blocks;
669 insert_bit(zone_map, 0); /* bit zero must always be allocated */
670 insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but
671 * must be allocated */
673 free(buf);
677 /*================================================================
678 * rootdir - install the root directory
679 *===============================================================*/
680 void rootdir(inode)
681 ino_t inode;
683 zone_t z;
685 z = alloc_zone();
686 add_zone(inode, z, 2 * sizeof(struct direct), current_time);
687 enter_dir(inode, ".", inode);
688 enter_dir(inode, "..", inode);
689 incr_link(inode);
690 incr_link(inode);
694 /*================================================================
695 * eat_dir - recursively install directory
696 *===============================================================*/
697 void eat_dir(parent)
698 ino_t parent;
700 /* Read prototype lines and set up directory. Recurse if need be. */
701 char *token[MAX_TOKENS], *p;
702 char line[LINE_LEN];
703 int mode, usrid, grpid, maj, min, f;
704 ino_t n;
705 zone_t z;
706 size_t size;
708 while (1) {
709 getline(line, token);
710 p = token[0];
711 if (*p == '$') return;
712 p = token[1];
713 mode = mode_con(p);
714 usrid = atoi(token[2]);
715 grpid = atoi(token[3]);
716 n = alloc_inode(mode, usrid, grpid);
718 /* Enter name in directory and update directory's size. */
719 enter_dir(parent, token[0], n);
720 incr_size(parent, sizeof(struct direct));
722 /* Check to see if file is directory or special. */
723 incr_link(n);
724 if (*p == 'd') {
725 /* This is a directory. */
726 z = alloc_zone(); /* zone for new directory */
727 add_zone(n, z, 2 * sizeof(struct direct), current_time);
728 enter_dir(n, ".", n);
729 enter_dir(n, "..", parent);
730 incr_link(parent);
731 incr_link(n);
732 eat_dir(n);
733 } else if (*p == 'b' || *p == 'c') {
734 /* Special file. */
735 maj = atoi(token[4]);
736 min = atoi(token[5]);
737 size = 0;
738 if (token[6]) size = atoi(token[6]);
739 size = block_size * size;
740 add_zone(n, (zone_t) (makedev(maj,min)), size, current_time);
741 } else {
742 /* Regular file. Go read it. */
743 if ((f = open(token[4], O_RDONLY)) < 0) {
744 fprintf(stderr, "%s: Can't open %s: %s\n",
745 progname, token[4], strerror(errno));
746 } else {
747 eat_file(n, f);
754 /*================================================================
755 * eat_file - copy file to MINIX
756 *===============================================================*/
757 /* Zonesize >= blocksize */
758 void eat_file(inode, f)
759 ino_t inode;
760 int f;
762 int ct, i, j, k;
763 zone_t z;
764 char *buf;
765 uint32_t timeval;
767 buf = alloc_block();
769 do {
770 for (i = 0, j = 0; i < zone_size; i++, j += ct) {
771 for (k = 0; k < block_size; k++) buf[k] = 0;
772 if ((ct = read(f, buf, block_size)) > 0) {
773 if (i == 0) z = alloc_zone();
774 put_block((z << zone_shift) + i, buf);
777 timeval = (dflag ? current_time : file_time(f));
778 if (ct) add_zone(inode, z, (size_t) j, timeval);
779 } while (ct == block_size);
780 close(f);
781 free(buf);
786 /*================================================================
787 * directory & inode management assist group
788 *===============================================================*/
789 void enter_dir(parent, name, child)
790 ino_t parent, child;
791 char *name;
793 /* Enter child in parent directory */
794 /* Works for dir > 1 block and zone > block */
795 unsigned int i, j, k, l, off;
796 block_t b;
797 zone_t z;
798 char *p1, *p2;
799 struct direct *dir_entry;
800 d1_inode ino1[V1_INODES_PER_BLOCK];
801 d2_inode *ino2;
802 int nr_dzones;
804 b = ((parent - 1) / inodes_per_block) + inode_offset;
805 off = (parent - 1) % inodes_per_block;
807 if(!(dir_entry = malloc(NR_DIR_ENTRIES(block_size) * sizeof(*dir_entry))))
808 pexit("couldn't allocate directory entry");
810 if(!(ino2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*ino2))))
811 pexit("couldn't allocate block of inodes entry");
813 if (fs_version == 1) {
814 get_block(b, (char *) ino1);
815 nr_dzones = V1_NR_DZONES;
816 } else {
817 get_block(b, (char *) ino2);
818 nr_dzones = V2_NR_DZONES;
820 for (k = 0; k < nr_dzones; k++) {
821 if (fs_version == 1) {
822 z = ino1[off].d1_zone[k];
823 if (z == 0) {
824 z = alloc_zone();
825 ino1[off].d1_zone[k] = z;
827 } else {
828 z = ino2[off].d2_zone[k];
829 if (z == 0) {
830 z = alloc_zone();
831 ino2[off].d2_zone[k] = z;
834 for (l = 0; l < zone_size; l++) {
835 get_block((z << zone_shift) + l, (char *) dir_entry);
836 for (i = 0; i < NR_DIR_ENTRIES(block_size); i++) {
837 if (dir_entry[i].mfs_d_ino == 0) {
838 dir_entry[i].mfs_d_ino = child;
839 p1 = name;
840 p2 = dir_entry[i].mfs_d_name;
841 j = sizeof(dir_entry[i].mfs_d_name);
842 j = 60;
843 while (j--) {
844 *p2++ = *p1;
845 if (*p1 != 0) p1++;
847 put_block((z << zone_shift) + l, (char *) dir_entry);
848 if (fs_version == 1) {
849 put_block(b, (char *) ino1);
850 } else {
851 put_block(b, (char *) ino2);
853 free(dir_entry);
854 free(ino2);
855 return;
861 printf("Directory-inode %lu beyond direct blocks. Could not enter %s\n",
862 parent, name);
863 pexit("Halt");
867 void add_zone(ino_t n, zone_t z, size_t bytes, uint32_t cur_time)
869 if (fs_version == 1) {
870 add_z_1(n, z, bytes, cur_time);
871 } else {
872 add_z_2(n, z, bytes, cur_time);
876 void add_z_1(ino_t n, zone_t z, size_t bytes, uint32_t cur_time)
878 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
880 int off, i;
881 block_t b;
882 zone_t indir;
883 uint16_t blk[V1_INDIRECTS];
884 d1_inode *p;
885 d1_inode inode[V1_INODES_PER_BLOCK];
887 b = ((n - 1) / V1_INODES_PER_BLOCK) + inode_offset;
888 off = (n - 1) % V1_INODES_PER_BLOCK;
889 get_block(b, (char *) inode);
890 p = &inode[off];
891 p->d1_size += bytes;
892 p->d1_mtime = cur_time;
893 for (i = 0; i < V1_NR_DZONES; i++)
894 if (p->d1_zone[i] == 0) {
895 p->d1_zone[i] = (uint16_t) z;
896 put_block(b, (char *) inode);
897 return;
899 put_block(b, (char *) inode);
901 /* File has grown beyond a small file. */
902 if (p->d1_zone[V1_NR_DZONES] == 0)
903 p->d1_zone[V1_NR_DZONES] = (uint16_t) alloc_zone();
904 indir = p->d1_zone[V1_NR_DZONES];
905 put_block(b, (char *) inode);
906 b = indir << zone_shift;
907 get_block(b, (char *) blk);
908 for (i = 0; i < V1_INDIRECTS; i++)
909 if (blk[i] == 0) {
910 blk[i] = (uint16_t) z;
911 put_block(b, (char *) blk);
912 return;
914 pexit("File has grown beyond single indirect");
917 void add_z_2(ino_t n, zone_t z, size_t bytes, uint32_t cur_time)
919 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
921 int off, i;
922 block_t b;
923 zone_t indir;
924 zone_t *blk;
925 d2_inode *p;
926 d2_inode *inode;
928 if(!(blk = malloc(V2_INDIRECTS(block_size)*sizeof(*blk))))
929 pexit("Couldn't allocate indirect block");
931 if(!(inode = malloc(V2_INODES_PER_BLOCK(block_size)*sizeof(*inode))))
932 pexit("Couldn't allocate block of inodes");
934 b = ((n - 1) / V2_INODES_PER_BLOCK(block_size)) + inode_offset;
935 off = (n - 1) % V2_INODES_PER_BLOCK(block_size);
936 get_block(b, (char *) inode);
937 p = &inode[off];
938 p->d2_size += bytes;
939 p->d2_mtime = cur_time;
940 for (i = 0; i < V2_NR_DZONES; i++)
941 if (p->d2_zone[i] == 0) {
942 p->d2_zone[i] = z;
943 put_block(b, (char *) inode);
944 free(blk);
945 free(inode);
946 return;
948 put_block(b, (char *) inode);
950 /* File has grown beyond a small file. */
951 if (p->d2_zone[V2_NR_DZONES] == 0) p->d2_zone[V2_NR_DZONES] = alloc_zone();
952 indir = p->d2_zone[V2_NR_DZONES];
953 put_block(b, (char *) inode);
954 b = indir << zone_shift;
955 get_block(b, (char *) blk);
956 for (i = 0; i < V2_INDIRECTS(block_size); i++)
957 if (blk[i] == 0) {
958 blk[i] = z;
959 put_block(b, (char *) blk);
960 free(blk);
961 free(inode);
962 return;
964 pexit("File has grown beyond single indirect");
968 void incr_link(n)
969 ino_t n;
971 /* Increment the link count to inode n */
972 int off;
973 static int enter = 0;
974 block_t b;
976 if(enter) exit(1);
978 b = ((n - 1) / inodes_per_block) + inode_offset;
979 off = (n - 1) % inodes_per_block;
980 if (fs_version == 1) {
981 d1_inode inode1[V1_INODES_PER_BLOCK];
983 get_block(b, (char *) inode1);
984 inode1[off].d1_nlinks++;
985 put_block(b, (char *) inode1);
986 } else {
987 static d2_inode *inode2 = NULL;
988 int n;
990 n = sizeof(*inode2) * V2_INODES_PER_BLOCK(block_size);
991 if(!inode2 && !(inode2 = malloc(n)))
992 pexit("couldn't allocate a block of inodes");
994 get_block(b, (char *) inode2);
995 inode2[off].d2_nlinks++;
996 put_block(b, (char *) inode2);
998 enter = 0;
1002 void incr_size(n, count)
1003 ino_t n;
1004 size_t count;
1006 /* Increment the file-size in inode n */
1007 block_t b;
1008 int off;
1010 b = ((n - 1) / inodes_per_block) + inode_offset;
1011 off = (n - 1) % inodes_per_block;
1012 if (fs_version == 1) {
1013 d1_inode inode1[V1_INODES_PER_BLOCK];
1015 get_block(b, (char *) inode1);
1016 inode1[off].d1_size += count;
1017 put_block(b, (char *) inode1);
1018 } else {
1019 d2_inode *inode2;
1020 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
1021 pexit("couldn't allocate a block of inodes");
1023 get_block(b, (char *) inode2);
1024 inode2[off].d2_size += count;
1025 put_block(b, (char *) inode2);
1026 free(inode2);
1031 /*================================================================
1032 * allocation assist group
1033 *===============================================================*/
1034 static ino_t alloc_inode(mode, usrid, grpid)
1035 int mode, usrid, grpid;
1037 ino_t num;
1038 int off;
1039 block_t b;
1041 num = next_inode++;
1042 if (num > nrinodes) {
1043 fprintf(stderr, "have %d inodoes\n", nrinodes);
1044 pexit("File system does not have enough inodes");
1046 b = ((num - 1) / inodes_per_block) + inode_offset;
1047 off = (num - 1) % inodes_per_block;
1048 if (fs_version == 1) {
1049 d1_inode inode1[V1_INODES_PER_BLOCK];
1051 get_block(b, (char *) inode1);
1052 inode1[off].d1_mode = mode;
1053 inode1[off].d1_uid = usrid;
1054 inode1[off].d1_gid = grpid;
1055 put_block(b, (char *) inode1);
1056 } else {
1057 d2_inode *inode2;
1059 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
1060 pexit("couldn't allocate a block of inodes");
1062 get_block(b, (char *) inode2);
1063 inode2[off].d2_mode = mode;
1064 inode2[off].d2_uid = usrid;
1065 inode2[off].d2_gid = grpid;
1066 put_block(b, (char *) inode2);
1068 free(inode2);
1071 /* Set the bit in the bit map. */
1072 /* DEBUG FIXME. This assumes the bit is in the first inode map block. */
1073 insert_bit((block_t) INODE_MAP, (int) num);
1074 return(num);
1078 static zone_t alloc_zone()
1080 /* Allocate a new zone */
1081 /* Works for zone > block */
1082 block_t b;
1083 int i;
1084 zone_t z;
1086 z = next_zone++;
1087 b = z << zone_shift;
1088 if ((b + zone_size) > nrblocks)
1089 pexit("File system not big enough for all the files");
1090 for (i = 0; i < zone_size; i++)
1091 put_block(b + i, zero); /* give an empty zone */
1092 /* DEBUG FIXME. This assumes the bit is in the first zone map block. */
1093 insert_bit(zone_map, (int) (z - zoff)); /* lint, NOT OK because
1094 * z hasn't been broken
1095 * up into block +
1096 * offset yet. */
1097 return(z);
1101 void insert_bit(block, bit)
1102 block_t block;
1103 int bit;
1105 /* Insert 'count' bits in the bitmap */
1106 int w, s;
1107 #if defined(__minix)
1108 bitchunk_t *buf;
1109 #else
1110 uint32_t *buf;
1111 #endif
1113 #if defined(__minix)
1114 buf = (bitchunk_t *) alloc_block();
1115 #else
1116 buf = (uint32_t *) alloc_block();
1117 #endif
1119 get_block(block, (char *) buf);
1120 #if defined(__minix)
1121 w = bit / (8 * sizeof(bitchunk_t));
1122 s = bit % (8 * sizeof(bitchunk_t));
1123 #else
1124 w = bit / (8 * sizeof(uint32_t));
1125 s = bit % (8 * sizeof(uint32_t));
1126 #endif
1127 buf[w] |= (1 << s);
1128 put_block(block, (char *) buf);
1130 free(buf);
1134 /*================================================================
1135 * proto-file processing assist group
1136 *===============================================================*/
1137 int mode_con(p)
1138 char *p;
1140 /* Convert string to mode */
1141 int o1, o2, o3, mode;
1142 char c1, c2, c3;
1144 c1 = *p++;
1145 c2 = *p++;
1146 c3 = *p++;
1147 o1 = *p++ - '0';
1148 o2 = *p++ - '0';
1149 o3 = *p++ - '0';
1150 mode = (o1 << 6) | (o2 << 3) | o3;
1151 if (c1 == 'd') mode |= S_IFDIR;
1152 if (c1 == 'b') mode |= S_IFBLK;
1153 if (c1 == 'c') mode |= S_IFCHR;
1154 if (c1 == '-') mode |= S_IFREG;
1155 if (c2 == 'u') mode |= S_ISUID;
1156 if (c3 == 'g') mode |= S_ISGID;
1157 return(mode);
1160 void getline(line, parse)
1161 char *parse[MAX_TOKENS];
1162 char line[LINE_LEN];
1164 /* Read a line and break it up in tokens */
1165 int k;
1166 char c, *p;
1167 int d;
1169 for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0;
1170 for (k = 0; k < LINE_LEN; k++) line[k] = 0;
1171 k = 0;
1172 parse[0] = 0;
1173 p = line;
1174 while (1) {
1175 if (++k > LINE_LEN) pexit("Line too long");
1176 d = fgetc(proto);
1177 if (d == EOF) pexit("Unexpected end-of-file");
1178 *p = d;
1179 if (*p == '\n') lct++;
1180 if (*p == ' ' || *p == '\t') *p = 0;
1181 if (*p == '\n') {
1182 *p++ = 0;
1183 *p = '\n';
1184 break;
1186 p++;
1189 k = 0;
1190 p = line;
1191 lastp = line;
1192 while (1) {
1193 c = *p++;
1194 if (c == '\n') return;
1195 if (c == 0) continue;
1196 parse[k++] = p - 1;
1197 do {
1198 c = *p++;
1199 } while (c != 0 && c != '\n');
1204 /*================================================================
1205 * other stuff
1206 *===============================================================*/
1207 void check_mtab(device)
1208 char *device; /* /dev/hd1 or whatever */
1210 /* Check to see if the special file named in s is mounted. */
1211 #if defined(__minix)
1212 int n, r;
1213 struct stat sb;
1214 char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1], version[10], rw_flag[10];
1216 r= stat(device, &sb);
1217 if (r == -1)
1219 if (errno == ENOENT)
1220 return; /* Does not exist, and therefore not mounted. */
1221 fprintf(stderr, "%s: stat %s failed: %s\n",
1222 progname, device, strerror(errno));
1223 exit(1);
1225 if (!S_ISBLK(sb.st_mode))
1227 /* Not a block device and therefore not mounted. */
1228 return;
1231 if (load_mtab("mkfs") < 0) return;
1232 while (1) {
1233 n = get_mtab_entry(special, mounted_on, version, rw_flag);
1234 if (n < 0) return;
1235 if (strcmp(device, special) == 0) {
1236 /* Can't mkfs on top of a mounted file system. */
1237 fprintf(stderr, "%s: %s is mounted on %s\n",
1238 progname, device, mounted_on);
1239 exit(1);
1242 #elif defined(__linux__)
1243 /* XXX: this code is copyright Theodore T'so and distributed under the GPLv2. Rewrite.
1245 struct mntent *mnt;
1246 struct stat st_buf;
1247 dev_t file_dev=0, file_rdev=0;
1248 ino_t file_ino=0;
1249 FILE *f;
1250 int fd;
1251 char *mtab_file = "/proc/mounts";
1253 if ((f = setmntent (mtab_file, "r")) == NULL)
1254 goto error;
1256 if (stat(device, &st_buf) == 0) {
1257 if (S_ISBLK(st_buf.st_mode)) {
1258 file_rdev = st_buf.st_rdev;
1259 } else {
1260 file_dev = st_buf.st_dev;
1261 file_ino = st_buf.st_ino;
1265 while ((mnt = getmntent (f)) != NULL) {
1266 if (strcmp(device, mnt->mnt_fsname) == 0)
1267 break;
1268 if (stat(mnt->mnt_fsname, &st_buf) == 0) {
1269 if (S_ISBLK(st_buf.st_mode)) {
1270 if (file_rdev && (file_rdev == st_buf.st_rdev))
1271 break;
1272 } else {
1273 if (file_dev && ((file_dev == st_buf.st_dev) &&
1274 (file_ino == st_buf.st_ino)))
1275 break;
1280 if (mnt == NULL) {
1282 * Do an extra check to see if this is the root device. We
1283 * can't trust /etc/mtab, and /proc/mounts will only list
1284 * /dev/root for the root filesystem. Argh. Instead we
1285 * check if the given device has the same major/minor number
1286 * as the device that the root directory is on.
1288 if (file_rdev && stat("/", &st_buf) == 0) {
1289 if (st_buf.st_dev == file_rdev) {
1290 goto is_root;
1293 goto test_busy;
1295 /* Validate the entry in case /etc/mtab is out of date */
1297 * We need to be paranoid, because some broken distributions
1298 * (read: Slackware) don't initialize /etc/mtab before checking
1299 * all of the non-root filesystems on the disk.
1301 if (stat(mnt->mnt_dir, &st_buf) < 0) {
1302 if (errno == ENOENT) {
1303 goto test_busy;
1305 goto error;
1307 if (file_rdev && (st_buf.st_dev != file_rdev)) {
1308 goto error;
1311 fprintf(stderr, "Device %s is mounted, exiting\n", device);
1312 exit(-1);
1315 * Check to see if we're referring to the root filesystem.
1316 * If so, do a manual check to see if we can open /etc/mtab
1317 * read/write, since if the root is mounted read/only, the
1318 * contents of /etc/mtab may not be accurate.
1320 if (!strcmp(mnt->mnt_dir, "/")) {
1321 is_root:
1322 fprintf(stderr, "Device %s is mounted as root file system!\n",
1323 device);
1324 exit(-1);
1327 test_busy:
1329 endmntent (f);
1330 if ((stat(device, &st_buf) != 0) ||
1331 !S_ISBLK(st_buf.st_mode))
1332 return;
1333 fd = open(device, O_RDONLY | O_EXCL);
1334 if (fd < 0) {
1335 if (errno == EBUSY) {
1336 fprintf(stderr, "Device %s is used by the system\n", device);
1337 exit(-1);
1339 } else
1340 close(fd);
1342 return;
1344 error:
1345 endmntent (f);
1346 fprintf(stderr, "Error while checking if device %s is mounted\n", device);
1347 exit(-1);
1348 #endif
1352 uint32_t file_time(f)
1353 int f;
1355 #ifdef UNIX
1356 struct stat statbuf;
1357 fstat(f, &statbuf);
1358 return(statbuf.st_mtime);
1359 #else /* fstat not supported by DOS */
1360 return(0L);
1361 #endif
1365 void pexit(s)
1366 char *s;
1368 fprintf(stderr, "%s: %s\n", progname, s);
1369 if (lct != 0)
1370 fprintf(stderr, "Line %d being processed when error detected.\n", lct);
1371 flush();
1372 exit(2);
1376 void copy(from, to, count)
1377 char *from, *to;
1378 size_t count;
1380 while (count--) *to++ = *from++;
1383 char *alloc_block()
1385 char *buf;
1387 if(!(buf = malloc(block_size))) {
1388 pexit("couldn't allocate filesystem buffer");
1390 bzero(buf, block_size);
1392 return buf;
1395 void print_fs()
1397 int i, j;
1398 ino_t k;
1399 d1_inode inode1[V1_INODES_PER_BLOCK];
1400 d2_inode *inode2;
1401 unsigned short *usbuf;
1402 block_t b;
1403 struct direct *dir;
1405 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
1406 pexit("couldn't allocate a block of inodes");
1408 if(!(dir = malloc(NR_DIR_ENTRIES(block_size)*sizeof(*dir))))
1409 pexit("malloc of directory entry failed");
1411 usbuf = (unsigned short *) alloc_block();
1413 get_super_block((char *) usbuf);
1414 printf("\nSuperblock: ");
1415 for (i = 0; i < 8; i++) printf("%06o ", usbuf[i]);
1416 get_block((block_t) 2, (char *) usbuf);
1417 printf("...\nInode map: ");
1418 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1419 get_block((block_t) 3, (char *) usbuf);
1420 printf("...\nZone map: ");
1421 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1422 printf("...\n");
1424 free(usbuf);
1425 usbuf = NULL;
1427 k = 0;
1428 for (b = inode_offset; k < nrinodes; b++) {
1429 if (fs_version == 1) {
1430 get_block(b, (char *) inode1);
1431 } else {
1432 get_block(b, (char *) inode2);
1434 for (i = 0; i < inodes_per_block; i++) {
1435 k = inodes_per_block * (int) (b - inode_offset) + i + 1;
1436 /* Lint but OK */
1437 if (k > nrinodes) break;
1438 if (fs_version == 1) {
1439 if (inode1[i].d1_mode != 0) {
1440 printf("Inode %2lu: mode=", k);
1441 printf("%06o", inode1[i].d1_mode);
1442 printf(" uid=%2d gid=%2d size=",
1443 inode1[i].d1_uid, inode1[i].d1_gid);
1444 printf("%6d", inode1[i].d1_size);
1445 printf(" zone[0]=%d\n", inode1[i].d1_zone[0]);
1447 if ((inode1[i].d1_mode & S_IFMT) == S_IFDIR) {
1448 /* This is a directory */
1449 get_block(inode1[i].d1_zone[0], (char *) dir);
1450 for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1451 if (dir[j].mfs_d_ino)
1452 printf("\tInode %2u: %s\n", dir[j].mfs_d_ino, dir[j].mfs_d_name);
1454 } else {
1455 if (inode2[i].d2_mode != 0) {
1456 printf("Inode %2lu: mode=", k);
1457 printf("%06o", inode2[i].d2_mode);
1458 printf(" uid=%2d gid=%2d size=",
1459 inode2[i].d2_uid, inode2[i].d2_gid);
1460 printf("%6d", inode2[i].d2_size);
1461 printf(" zone[0]=%u\n", inode2[i].d2_zone[0]);
1463 if ((inode2[i].d2_mode & S_IFMT) == S_IFDIR) {
1464 /* This is a directory */
1465 get_block(inode2[i].d2_zone[0], (char *) dir);
1466 for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1467 if (dir[j].mfs_d_ino)
1468 printf("\tInode %2u: %s\n", dir[j].mfs_d_ino, dir[j].mfs_d_name);
1474 printf("%d inodes used. %d zones used.\n", next_inode - 1, next_zone);
1475 free(dir);
1476 free(inode2);
1480 int read_and_set(n)
1481 block_t n;
1483 /* The first time a block is read, it returns all 0s, unless there has
1484 * been a write. This routine checks to see if a block has been accessed.
1487 int w, s, mask, r;
1489 w = n / 8;
1490 if(w >= umap_array_elements) {
1491 pexit("umap array too small - this can't happen");
1493 s = n % 8;
1494 mask = 1 << s;
1495 r = (umap_array[w] & mask ? 1 : 0);
1496 umap_array[w] |= mask;
1497 return(r);
1500 void usage()
1502 fprintf(stderr,
1503 "Usage: %s [-12dlot] [-b blocks] [-i inodes]\n"
1504 "\t[-x extra] [-B blocksize] special [proto]\n",
1505 progname);
1506 exit(1);
1509 /*================================================================
1510 * get_block & put_block for MS-DOS
1511 *===============================================================*/
1512 #ifdef DOS
1515 * These are the get_block and put_block routines
1516 * when compiling & running mkfs.c under MS-DOS.
1518 * It requires the (asembler) routines absread & abswrite
1519 * from the file diskio.asm. Since these routines just do
1520 * as they are told (read & write the sector specified),
1521 * a local cache is used to minimize the i/o-overhead for
1522 * frequently used blocks.
1524 * The global variable "file" determines whether the output
1525 * is to a disk-device or to a binary file.
1529 #define PH_SECTSIZE 512 /* size of a physical disk-sector */
1532 char *derrtab[14] = {
1533 "no error",
1534 "disk is read-only",
1535 "unknown unit",
1536 "device not ready",
1537 "bad command",
1538 "data error",
1539 "internal error: bad request structure length",
1540 "seek error",
1541 "unknown media type",
1542 "sector not found",
1543 "printer out of paper (?)",
1544 "write fault",
1545 "read error",
1546 "general error"
1549 #define CACHE_SIZE 20 /* 20 block-buffers */
1552 struct cache {
1553 char blockbuf[BLOCK_SIZE];
1554 block_t blocknum;
1555 int dirty;
1556 int usecnt;
1557 } cache[CACHE_SIZE];
1560 void special(string)
1561 char *string;
1564 if (string[1] == ':' && string[2] == 0) {
1565 /* Format: d: or d:fname */
1566 disk = (string[0] & ~32) - 'A';
1567 if (disk > 1 && !override) /* safety precaution */
1568 pexit("Bad drive specifier for special");
1569 } else {
1570 file = 1;
1571 if ((fd = creat(string, BWRITE)) == 0)
1572 pexit("Can't open special file");
1576 void get_block(n, buf)
1577 block_t n;
1578 char *buf;
1580 /* Get a block to the user */
1581 struct cache *bp, *fp;
1583 /* First access returns a zero block */
1584 if (read_and_set(n) == 0) {
1585 copy(zero, buf, block_size);
1586 return;
1589 /* Look for block in cache */
1590 fp = 0;
1591 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) {
1592 if (bp->blocknum == n) {
1593 copy(bp, buf, block_size);
1594 bp->usecnt++;
1595 return;
1598 /* Remember clean block */
1599 if (bp->dirty == 0)
1600 if (fp) {
1601 if (fp->usecnt > bp->usecnt) fp = bp;
1602 } else
1603 fp = bp;
1606 /* Block not in cache, get it */
1607 if (!fp) {
1608 /* No clean buf, flush one */
1609 for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++)
1610 if (fp->usecnt > bp->usecnt) fp = bp;
1611 mx_write(fp->blocknum, fp);
1613 mx_read(n, fp);
1614 fp->dirty = 0;
1615 fp->usecnt = 0;
1616 fp->blocknum = n;
1617 copy(fp, buf, block_size);
1620 void put_block(n, buf)
1621 block_t n;
1622 char *buf;
1624 /* Accept block from user */
1625 struct cache *fp, *bp;
1627 (void) read_and_set(n);
1629 /* Look for block in cache */
1630 fp = 0;
1631 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) {
1632 if (bp->blocknum == n) {
1633 copy(buf, bp, block_size);
1634 bp->dirty = 1;
1635 return;
1638 /* Remember clean block */
1639 if (bp->dirty == 0)
1640 if (fp) {
1641 if (fp->usecnt > bp->usecnt) fp = bp;
1642 } else
1643 fp = bp;
1646 /* Block not in cache */
1647 if (!fp) {
1648 /* No clean buf, flush one */
1649 for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++)
1650 if (fp->usecnt > bp->usecnt) fp = bp;
1651 mx_write(fp->blocknum, fp);
1653 fp->dirty = 1;
1654 fp->usecnt = 1;
1655 fp->blocknum = n;
1656 copy(buf, fp, block_size);
1659 void cache_init()
1661 struct cache *bp;
1662 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) bp->blocknum = -1;
1665 void flush()
1667 /* Flush all dirty blocks to disk */
1668 struct cache *bp;
1670 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++)
1671 if (bp->dirty) {
1672 mx_write(bp->blocknum, bp);
1673 bp->dirty = 0;
1677 /*==================================================================
1678 * hard read & write etc.
1679 *=================================================================*/
1680 #define MAX_RETRIES 5
1683 void mx_read(blocknr, buf)
1684 int blocknr;
1685 char *buf;
1688 /* Read the requested MINIX-block in core */
1689 char (*bp)[PH_SECTSIZE];
1690 int sectnum, retries, err;
1692 if (file) {
1693 lseek(fd, (off_t) blocknr * block_size, 0);
1694 if (read(fd, buf, block_size) != block_size)
1695 pexit("mx_read: error reading file");
1696 } else {
1697 sectnum = blocknr * (block_size / PH_SECTSIZE);
1698 for (bp = buf; bp < &buf[block_size]; bp++) {
1699 retries = MAX_RETRIES;
1701 err = absread(disk, sectnum, bp);
1702 while (err && --retries);
1704 if (retries) {
1705 sectnum++;
1706 } else {
1707 dexit("mx_read", sectnum, err);
1713 void mx_write(blocknr, buf)
1714 int blocknr;
1715 char *buf;
1717 /* Write the MINIX-block to disk */
1718 char (*bp)[PH_SECTSIZE];
1719 int retries, sectnum, err;
1721 if (file) {
1722 lseek(fd, blocknr * block_size, 0);
1723 if (write(fd, buf, block_size) != block_size) {
1724 pexit("mx_write: error writing file");
1726 } else {
1727 sectnum = blocknr * (block_size / PH_SECTSIZE);
1728 for (bp = buf; bp < &buf[block_size]; bp++) {
1729 retries = MAX_RETRIES;
1730 do {
1731 err = abswrite(disk, sectnum, bp);
1732 } while (err && --retries);
1734 if (retries) {
1735 sectnum++;
1736 } else {
1737 dexit("mx_write", sectnum, err);
1744 void dexit(s, sectnum, err)
1745 int sectnum, err;
1746 char *s;
1748 printf("Error: %s, sector: %d, code: %d, meaning: %s\n",
1749 s, sectnum, err, derrtab[err]);
1750 exit(2);
1753 #endif
1755 /*================================================================
1756 * get_block & put_block for UNIX
1757 *===============================================================*/
1758 #ifdef UNIX
1760 void special(string)
1761 char *string;
1763 fd = creat(string, 0777);
1764 close(fd);
1765 fd = open(string, O_RDWR);
1766 if (fd < 0) pexit("Can't open special file");
1771 void get_block(n, buf)
1772 block_t n;
1773 char *buf;
1775 /* Read a block. */
1777 int k;
1779 /* First access returns a zero block */
1780 if (read_and_set(n) == 0) {
1781 copy(zero, buf, block_size);
1782 return;
1784 lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL);
1785 k = read(fd, buf, block_size);
1786 if (k != block_size) {
1787 pexit("get_block couldn't read");
1791 void get_super_block(buf)
1792 char *buf;
1794 /* Read a block. */
1796 int k;
1798 if(lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET) < 0) {
1799 perror("lseek");
1800 pexit("seek failed");
1802 k = read(fd, buf, _STATIC_BLOCK_SIZE);
1803 if (k != _STATIC_BLOCK_SIZE) {
1804 pexit("get_super_block couldn't read");
1808 void put_block(n, buf)
1809 block_t n;
1810 char *buf;
1812 /* Write a block. */
1814 (void) read_and_set(n);
1816 /* XXX - check other lseeks too. */
1817 if (lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL) == (off_t) -1) {
1818 pexit("put_block couldn't seek");
1820 if (write(fd, buf, block_size) != block_size) {
1821 pexit("put_block couldn't write");
1826 /* Dummy routines to keep source file clean from #ifdefs */
1828 void flush()
1830 return;
1833 void cache_init()
1835 return;
1838 #endif