pci: don't do sanity check for missing pci bus, the check can misfire.
[minix.git] / commands / simple / mkfs.c
blob7e7dd49d43c3f1d390fad8e3ec9629cf03e9a590
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/dir.h>
15 #include <sys/stat.h>
16 #include <stdio.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 <minix/config.h>
25 #include <minix/const.h>
26 #include <minix/type.h>
27 #include <minix/minlib.h>
28 #include "../../servers/mfs/const.h"
29 #if (MACHINE == IBM_PC)
30 #include <minix/partition.h>
31 #include <minix/u64.h>
32 #include <sys/ioctl.h>
33 #endif
34 #include <a.out.h>
35 #include <tools.h>
36 #include <dirent.h>
38 #undef EXTERN
39 #define EXTERN /* get rid of EXTERN by making it null */
40 #include "../../servers/mfs/super.h"
41 #include "../../servers/mfs/type.h"
42 #include "../../servers/mfs/inode.h"
43 #include <minix/fslib.h>
45 #ifndef max
46 #define max(a,b) ((a) > (b) ? (a) : (b))
47 #endif
49 #ifndef DOS
50 #ifndef UNIX
51 #define UNIX
52 #endif
53 #endif
55 #define INODE_MAP 2
56 #define MAX_TOKENS 10
57 #define LINE_LEN 200
58 #define BIN 2
59 #define BINGRP 2
60 #define BIT_MAP_SHIFT 13
61 #define INODE_MAX ((unsigned) 65535)
64 #ifdef DOS
65 maybedefine O_RDONLY 4 /* O_RDONLY | BINARY_BIT */
66 maybedefine BWRITE 5 /* O_WRONLY | BINARY_BIT */
67 #endif
69 extern char *optarg;
70 extern int optind;
72 int next_zone, next_inode, zone_size, zone_shift = 0, zoff;
73 block_t nrblocks;
74 int inode_offset, lct = 0, disk, fd, print = 0, file = 0;
75 unsigned int nrinodes;
76 int override = 0, simple = 0, dflag;
77 int donttest; /* skip test if it fits on medium */
78 char *progname;
80 long current_time, bin_time;
81 char *zero, *lastp;
82 char *umap_array; /* bit map tells if block read yet */
83 int umap_array_elements = 0;
84 block_t zone_map; /* where is zone map? (depends on # inodes) */
85 int inodes_per_block;
86 int fs_version;
87 unsigned int block_size;
89 FILE *proto;
91 _PROTOTYPE(int main, (int argc, char **argv));
92 _PROTOTYPE(block_t sizeup, (char *device));
93 _PROTOTYPE(void super, (zone_t zones, Ino_t inodes));
94 _PROTOTYPE(void rootdir, (Ino_t inode));
95 _PROTOTYPE(void eat_dir, (Ino_t parent));
96 _PROTOTYPE(void eat_file, (Ino_t inode, int f));
97 _PROTOTYPE(void enter_dir, (Ino_t parent, char *name, Ino_t child));
98 _PROTOTYPE(void incr_size, (Ino_t n, long count));
99 _PROTOTYPE(PRIVATE ino_t alloc_inode, (int mode, int usrid, int grpid));
100 _PROTOTYPE(PRIVATE zone_t alloc_zone, (void));
101 _PROTOTYPE(void add_zone, (Ino_t n, zone_t z, long bytes, long cur_time));
102 _PROTOTYPE(void add_z_1, (Ino_t n, zone_t z, long bytes, long cur_time));
103 _PROTOTYPE(void add_z_2, (Ino_t n, zone_t z, long bytes, long cur_time));
104 _PROTOTYPE(void incr_link, (Ino_t n));
105 _PROTOTYPE(void insert_bit, (block_t block, int bit));
106 _PROTOTYPE(int mode_con, (char *p));
107 _PROTOTYPE(void getline, (char line[LINE_LEN], char *parse[MAX_TOKENS]));
108 _PROTOTYPE(void check_mtab, (char *devname));
109 _PROTOTYPE(long file_time, (int f));
110 _PROTOTYPE(void pexit, (char *s));
111 _PROTOTYPE(void copy, (char *from, char *to, int count));
112 _PROTOTYPE(void print_fs, (void));
113 _PROTOTYPE(int read_and_set, (block_t n));
114 _PROTOTYPE(void special, (char *string));
115 _PROTOTYPE(void get_block, (block_t n, char *buf));
116 _PROTOTYPE(void get_super_block, (char *buf));
117 _PROTOTYPE(void put_block, (block_t n, char *buf));
118 _PROTOTYPE(void cache_init, (void));
119 _PROTOTYPE(void flush, (void));
120 _PROTOTYPE(void mx_read, (int blocknr, char *buf));
121 _PROTOTYPE(void mx_write, (int blocknr, char *buf));
122 _PROTOTYPE(void dexit, (char *s, int sectnum, int err));
123 _PROTOTYPE(void usage, (void));
124 _PROTOTYPE(char *alloc_block, (void));
126 /*================================================================
127 * mkfs - make filesystem
128 *===============================================================*/
129 int main(argc, argv)
130 int argc;
131 char *argv[];
133 int nread, mode, usrid, grpid, ch;
134 block_t blocks, maxblocks;
135 block_t i;
136 ino_t root_inum;
137 ino_t inodes;
138 zone_t zones;
139 char *token[MAX_TOKENS], line[LINE_LEN];
140 struct stat statbuf;
142 /* Get two times, the current time and the mod time of the binary of
143 * mkfs itself. When the -d flag is used, the later time is put into
144 * the i_mtimes of all the files. This feature is useful when
145 * producing a set of file systems, and one wants all the times to be
146 * identical. First you set the time of the mkfs binary to what you
147 * want, then go.
149 current_time = time((time_t *) 0); /* time mkfs is being run */
150 stat(argv[0], &statbuf);
151 bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */
153 /* Process switches. */
154 progname = argv[0];
155 blocks = 0;
156 i = 0;
157 fs_version = 3;
158 inodes_per_block = 0;
159 block_size = 0;
160 while ((ch = getopt(argc, argv, "12b:di:lotB:")) != EOF)
161 switch (ch) {
162 case '1':
163 fs_version = 1;
164 inodes_per_block = V1_INODES_PER_BLOCK;
165 break;
166 case '2':
167 fs_version = 2;
168 break;
169 case 'b':
170 blocks = strtoul(optarg, (char **) NULL, 0);
171 break;
172 case 'd':
173 dflag = 1;
174 current_time = bin_time;
175 break;
176 case 'i':
177 i = strtoul(optarg, (char **) NULL, 0);
178 break;
179 case 'l': print = 1; break;
180 case 'o': override = 1; break;
181 case 't': donttest = 1; break;
182 case 'B': block_size = atoi(optarg); break;
183 default: usage();
186 if (argc == optind) usage();
188 if(fs_version == 3) {
189 if(!block_size) block_size = _MAX_BLOCK_SIZE; /* V3 default block size */
190 if(block_size%SECTOR_SIZE || block_size < _MIN_BLOCK_SIZE) {
191 fprintf(stderr, "block size must be multiple of sector (%d) "
192 "and at least %d bytes\n",
193 SECTOR_SIZE, _MIN_BLOCK_SIZE);
194 pexit("specified block size illegal");
196 if(block_size%V2_INODE_SIZE) {
197 fprintf(stderr, "block size must be a multiple of inode size (%d bytes)\n",
198 V2_INODE_SIZE);
199 pexit("specified block size illegal");
201 } else {
202 if(block_size) {
203 pexit("Can't specify a block size if FS version is <3");
205 block_size = _STATIC_BLOCK_SIZE; /* V1/V2 block size */
208 if(!inodes_per_block)
209 inodes_per_block = V2_INODES_PER_BLOCK(block_size);
211 /* now that the block size is known, do buffer allocations where
212 * possible.
214 zero = alloc_block();
215 bzero(zero, block_size);
217 /* Determine the size of the device if not specified as -b or proto. */
218 maxblocks = sizeup(argv[optind]);
219 if (argc - optind == 1 && blocks == 0) {
220 blocks = maxblocks;
221 /* blocks == 0 is checked later, but leads to a funny way of
222 * reporting a 0-sized device (displays usage).
224 if(blocks < 1) {
225 fprintf(stderr, "%s: zero size device.\n", progname);
226 return 1;
230 /* The remaining args must be 'special proto', or just 'special' if the
231 * no. of blocks has already been specified.
233 if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage();
235 if (blocks > maxblocks) {
236 fprintf(stderr, "%s: %s: number of blocks too large for device.\n",
237 progname, argv[optind]);
238 return 1;
241 /* Check special. */
242 check_mtab(argv[optind]);
244 /* Check and start processing proto. */
245 optarg = argv[++optind];
246 if (optind < argc && (proto = fopen(optarg, "r")) != NULL) {
247 /* Prototype file is readable. */
248 lct = 1;
249 getline(line, token); /* skip boot block info */
251 /* Read the line with the block and inode counts. */
252 getline(line, token);
253 blocks = atol(token[0]);
254 inodes = atoi(token[1]);
256 /* Process mode line for root directory. */
257 getline(line, token);
258 mode = mode_con(token[0]);
259 usrid = atoi(token[1]);
260 grpid = atoi(token[2]);
261 } else {
262 lct = 0;
263 if (optind < argc) {
264 /* Maybe the prototype file is just a size. Check. */
265 blocks = strtoul(optarg, (char **) NULL, 0);
266 if (blocks == 0) pexit("Can't open prototype file");
268 if (i == 0) {
269 u32_t kb = div64u(mul64u(blocks, block_size), 1024);
270 i = kb / 2;
271 if (kb >= 100000) i = kb / 4;
273 /* round up to fill inode block */
274 i += inodes_per_block - 1;
275 i = i / inodes_per_block * inodes_per_block;
276 if (i > INODE_MAX && fs_version < 3) i = INODE_MAX;
279 if (blocks < 5) pexit("Block count too small");
280 if (i < 1) pexit("Inode count too small");
281 if (i > INODE_MAX && fs_version < 3) pexit("Inode count too large");
282 inodes = (ino_t) i;
284 /* Make simple file system of the given size, using defaults. */
285 mode = 040777;
286 usrid = BIN;
287 grpid = BINGRP;
288 simple = 1;
291 nrblocks = blocks;
292 nrinodes = inodes;
295 size_t bytes;
296 bytes = 1 + blocks/8;
297 if(!(umap_array = malloc(bytes))) {
298 fprintf(stderr, "mkfs: can't allocate block bitmap (%d bytes).\n",
299 bytes);
300 exit(1);
302 umap_array_elements = bytes;
305 /* Open special. */
306 special(argv[--optind]);
308 #ifdef UNIX
309 if (!donttest) {
310 short *testb;
311 ssize_t w;
313 testb = (short *) alloc_block();
315 /* Try writing the last block of partition or diskette. */
316 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
317 pexit("couldn't seek to last block to test size (1)");
319 testb[0] = 0x3245;
320 testb[1] = 0x11FF;
321 testb[block_size/2-1] = 0x1F2F;
322 if ((w=write(fd, (char *) testb, block_size)) != block_size) {
323 if(w < 0) perror("write");
324 printf("%d/%d\n", w, block_size);
325 pexit("File system is too big for minor device (write)");
327 sync(); /* flush write, so if error next read fails */
328 if(lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL) < 0) {
329 pexit("couldn't seek to last block to test size (2)");
331 testb[0] = 0;
332 testb[1] = 0;
333 nread = read(fd, (char *) testb, block_size);
334 if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF ||
335 testb[block_size/2-1] != 0x1F2F) {
336 if(nread < 0) perror("read");
337 printf("nread = %d\n", nread);
338 printf("testb = 0x%x 0x%x 0x%x\n", testb[0], testb[1], testb[block_size-1]);
339 pexit("File system is too big for minor device (read)");
341 lseek64(fd, mul64u(blocks - 1, block_size), SEEK_SET, NULL);
342 testb[0] = 0;
343 testb[1] = 0;
344 if (write(fd, (char *) testb, block_size) != block_size)
345 pexit("File system is too big for minor device (write2)");
346 lseek(fd, 0L, SEEK_SET);
347 free(testb);
349 #endif
351 /* Make the file-system */
353 cache_init();
355 put_block((block_t) 0, zero); /* Write a null boot block. */
357 zone_shift = 0; /* for future use */
358 zones = nrblocks >> zone_shift;
360 super(zones, inodes);
362 root_inum = alloc_inode(mode, usrid, grpid);
363 rootdir(root_inum);
364 if (simple == 0) eat_dir(root_inum);
366 if (print) print_fs();
367 flush();
368 return(0);
370 /* NOTREACHED */
371 } /* end main */
374 /*================================================================
375 * sizeup - determine device size
376 *===============================================================*/
377 block_t sizeup(device)
378 char *device;
380 int fd;
381 struct partition entry;
382 block_t d;
383 struct stat st;
384 unsigned int rem;
385 u64_t resize;
387 if ((fd = open(device, O_RDONLY)) == -1) {
388 if (errno != ENOENT)
389 perror("sizeup open");
390 return 0;
392 if (ioctl(fd, DIOCGETP, &entry) == -1) {
393 perror("sizeup ioctl");
394 if(fstat(fd, &st) < 0) {
395 perror("fstat");
396 entry.size = cvu64(0);
397 } else {
398 fprintf(stderr, "used fstat instead\n");
399 entry.size = cvu64(st.st_size);
402 close(fd);
403 d = div64u(entry.size, block_size);
404 rem = rem64u(entry.size, block_size);
406 resize = add64u(mul64u(d, block_size), rem);
407 if(cmp64(resize, entry.size) != 0) {
408 d = ULONG_MAX;
409 fprintf(stderr, "mkfs: truncating FS at %lu blocks\n", d);
412 return d;
416 /*================================================================
417 * super - construct a superblock
418 *===============================================================*/
420 void super(zones, inodes)
421 zone_t zones;
422 ino_t inodes;
424 unsigned int i;
425 int inodeblks;
426 int initblks;
427 u32_t nb;
428 zone_t v1sq, v2sq;
429 zone_t zo;
430 struct super_block *sup;
431 char *buf, *cp;
433 buf = alloc_block();
435 for (cp = buf; cp < &buf[block_size]; cp++) *cp = 0;
436 sup = (struct super_block *) buf; /* lint - might use a union */
438 sup->s_ninodes = inodes;
439 if (fs_version == 1) {
440 sup->s_nzones = zones;
441 if (sup->s_nzones != zones) pexit("too many zones");
442 } else {
443 sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */
444 sup->s_zones = zones;
447 #define BIGGERBLOCKS "Please try a larger block size for an FS of this size.\n"
448 sup->s_imap_blocks = nb = bitmapsize((bit_t) (1 + inodes), block_size);
449 if(sup->s_imap_blocks != nb) {
450 fprintf(stderr, "mkfs: too many inode bitmap blocks.\n" BIGGERBLOCKS);
451 exit(1);
453 sup->s_zmap_blocks = nb = bitmapsize((bit_t) zones, block_size);
454 if(nb != sup->s_zmap_blocks) {
455 fprintf(stderr, "mkfs: too many block bitmap blocks.\n" BIGGERBLOCKS);
456 exit(1);
458 inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks;
459 inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block;
460 initblks = inode_offset + inodeblks;
461 sup->s_firstdatazone_old = nb =
462 (initblks + (1 << zone_shift) - 1) >> zone_shift;
463 if(nb >= zones) pexit("bit maps too large");
464 if(nb != sup->s_firstdatazone_old) {
465 /* The field is too small to store the value. Fortunately, the value
466 * can be computed from other fields. We set the on-disk field to zero
467 * to indicate that it must not be used. Eventually, we can always set
468 * the on-disk field to zero, and stop using it.
470 sup->s_firstdatazone_old = 0;
472 sup->s_firstdatazone = nb;
473 zoff = sup->s_firstdatazone - 1;
474 sup->s_log_zone_size = zone_shift;
475 if (fs_version == 1) {
476 sup->s_magic = SUPER_MAGIC; /* identify super blocks */
477 v1sq = (zone_t) V1_INDIRECTS * V1_INDIRECTS;
478 zo = V1_NR_DZONES + (long) V1_INDIRECTS + v1sq;
479 sup->s_max_size = zo * block_size;
480 } else {
481 v2sq = (zone_t) V2_INDIRECTS(block_size) * V2_INDIRECTS(block_size);
482 zo = V2_NR_DZONES + (zone_t) V2_INDIRECTS(block_size) + v2sq;
483 if(fs_version == 2) {
484 sup->s_magic = SUPER_V2;/* identify super blocks */
485 sup->s_max_size = zo * block_size;
486 } else {
487 sup->s_magic = SUPER_V3;
488 sup->s_block_size = block_size;
489 sup->s_disk_version = 0;
490 #define MAX_MAX_SIZE ((unsigned long) LONG_MAX)
491 if(MAX_MAX_SIZE/block_size < zo) {
492 sup->s_max_size = MAX_MAX_SIZE;
494 else {
495 sup->s_max_size = zo * block_size;
500 zone_size = 1 << zone_shift; /* nr of blocks per zone */
502 if (lseek(fd, (off_t) _STATIC_BLOCK_SIZE, SEEK_SET) == (off_t) -1) {
503 pexit("super() couldn't seek");
505 if (write(fd, buf, _STATIC_BLOCK_SIZE) != _STATIC_BLOCK_SIZE) {
506 pexit("super() couldn't write");
509 /* Clear maps and inodes. */
510 for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero);
512 next_zone = sup->s_firstdatazone;
513 next_inode = 1;
515 zone_map = INODE_MAP + sup->s_imap_blocks;
517 insert_bit(zone_map, 0); /* bit zero must always be allocated */
518 insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but
519 * must be allocated */
521 free(buf);
525 /*================================================================
526 * rootdir - install the root directory
527 *===============================================================*/
528 void rootdir(inode)
529 ino_t inode;
531 zone_t z;
533 z = alloc_zone();
534 add_zone(inode, z, 2 * sizeof(struct direct), current_time);
535 enter_dir(inode, ".", inode);
536 enter_dir(inode, "..", inode);
537 incr_link(inode);
538 incr_link(inode);
542 /*================================================================
543 * eat_dir - recursively install directory
544 *===============================================================*/
545 void eat_dir(parent)
546 ino_t parent;
548 /* Read prototype lines and set up directory. Recurse if need be. */
549 char *token[MAX_TOKENS], *p;
550 char line[LINE_LEN];
551 int mode, usrid, grpid, maj, min, f;
552 ino_t n;
553 zone_t z;
554 long size;
556 while (1) {
557 getline(line, token);
558 p = token[0];
559 if (*p == '$') return;
560 p = token[1];
561 mode = mode_con(p);
562 usrid = atoi(token[2]);
563 grpid = atoi(token[3]);
564 if (grpid & 0200) fprintf(stderr, "A.S.Tanenbaum\n");
565 n = alloc_inode(mode, usrid, grpid);
567 /* Enter name in directory and update directory's size. */
568 enter_dir(parent, token[0], n);
569 incr_size(parent, sizeof(struct direct));
571 /* Check to see if file is directory or special. */
572 incr_link(n);
573 if (*p == 'd') {
574 /* This is a directory. */
575 z = alloc_zone(); /* zone for new directory */
576 add_zone(n, z, 2 * sizeof(struct direct), current_time);
577 enter_dir(n, ".", n);
578 enter_dir(n, "..", parent);
579 incr_link(parent);
580 incr_link(n);
581 eat_dir(n);
582 } else if (*p == 'b' || *p == 'c') {
583 /* Special file. */
584 maj = atoi(token[4]);
585 min = atoi(token[5]);
586 size = 0;
587 if (token[6]) size = atoi(token[6]);
588 size = block_size * size;
589 add_zone(n, (zone_t) ((maj << 8) | min), size, current_time);
590 } else {
591 /* Regular file. Go read it. */
592 if ((f = open(token[4], O_RDONLY)) < 0) {
593 fprintf(stderr, "%s: Can't open %s: %s\n",
594 progname, token[4], strerror(errno));
595 } else {
596 eat_file(n, f);
603 /*================================================================
604 * eat_file - copy file to MINIX
605 *===============================================================*/
606 /* Zonesize >= blocksize */
607 void eat_file(inode, f)
608 ino_t inode;
609 int f;
611 int ct, i, j, k;
612 zone_t z;
613 char *buf;
614 long timeval;
616 buf = alloc_block();
618 do {
619 for (i = 0, j = 0; i < zone_size; i++, j += ct) {
620 for (k = 0; k < block_size; k++) buf[k] = 0;
621 if ((ct = read(f, buf, block_size)) > 0) {
622 if (i == 0) z = alloc_zone();
623 put_block((z << zone_shift) + i, buf);
626 timeval = (dflag ? current_time : file_time(f));
627 if (ct) add_zone(inode, z, (long) j, timeval);
628 } while (ct == block_size);
629 close(f);
634 /*================================================================
635 * directory & inode management assist group
636 *===============================================================*/
637 void enter_dir(parent, name, child)
638 ino_t parent, child;
639 char *name;
641 /* Enter child in parent directory */
642 /* Works for dir > 1 block and zone > block */
643 int i, j, k, l, off;
644 block_t b;
645 zone_t z;
646 char *p1, *p2;
647 struct direct *dir_entry;
648 d1_inode ino1[V1_INODES_PER_BLOCK];
649 d2_inode *ino2;
650 int nr_dzones;
652 b = ((parent - 1) / inodes_per_block) + inode_offset;
653 off = (parent - 1) % inodes_per_block;
655 if(!(dir_entry = malloc(NR_DIR_ENTRIES(block_size) * sizeof(*dir_entry))))
656 pexit("couldn't allocate directory entry");
658 if(!(ino2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*ino2))))
659 pexit("couldn't allocate block of inodes entry");
661 if (fs_version == 1) {
662 get_block(b, (char *) ino1);
663 nr_dzones = V1_NR_DZONES;
664 } else {
665 get_block(b, (char *) ino2);
666 nr_dzones = V2_NR_DZONES;
668 for (k = 0; k < nr_dzones; k++) {
669 if (fs_version == 1) {
670 z = ino1[off].d1_zone[k];
671 if (z == 0) {
672 z = alloc_zone();
673 ino1[off].d1_zone[k] = z;
675 } else {
676 z = ino2[off].d2_zone[k];
677 if (z == 0) {
678 z = alloc_zone();
679 ino2[off].d2_zone[k] = z;
682 for (l = 0; l < zone_size; l++) {
683 get_block((z << zone_shift) + l, (char *) dir_entry);
684 for (i = 0; i < NR_DIR_ENTRIES(block_size); i++) {
685 if (dir_entry[i].d_ino == 0) {
686 dir_entry[i].d_ino = child;
687 p1 = name;
688 p2 = dir_entry[i].d_name;
689 j = sizeof(dir_entry[i].d_name);
690 while (j--) {
691 *p2++ = *p1;
692 if (*p1 != 0) p1++;
694 put_block((z << zone_shift) + l, (char *) dir_entry);
695 if (fs_version == 1) {
696 put_block(b, (char *) ino1);
697 } else {
698 put_block(b, (char *) ino2);
700 free(dir_entry);
701 free(ino2);
702 return;
708 printf("Directory-inode %d beyond direct blocks. Could not enter %s\n",
709 parent, name);
710 pexit("Halt");
714 void add_zone(n, z, bytes, cur_time)
715 ino_t n;
716 zone_t z;
717 long bytes, cur_time;
719 if (fs_version == 1) {
720 add_z_1(n, z, bytes, cur_time);
721 } else {
722 add_z_2(n, z, bytes, cur_time);
726 void add_z_1(n, z, bytes, cur_time)
727 ino_t n;
728 zone_t z;
729 long bytes, cur_time;
731 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
733 int off, i;
734 block_t b;
735 zone_t indir;
736 zone1_t blk[V1_INDIRECTS];
737 d1_inode *p;
738 d1_inode inode[V1_INODES_PER_BLOCK];
740 b = ((n - 1) / V1_INODES_PER_BLOCK) + inode_offset;
741 off = (n - 1) % V1_INODES_PER_BLOCK;
742 get_block(b, (char *) inode);
743 p = &inode[off];
744 p->d1_size += bytes;
745 p->d1_mtime = cur_time;
746 for (i = 0; i < V1_NR_DZONES; i++)
747 if (p->d1_zone[i] == 0) {
748 p->d1_zone[i] = (zone1_t) z;
749 put_block(b, (char *) inode);
750 return;
752 put_block(b, (char *) inode);
754 /* File has grown beyond a small file. */
755 if (p->d1_zone[V1_NR_DZONES] == 0)
756 p->d1_zone[V1_NR_DZONES] = (zone1_t) alloc_zone();
757 indir = p->d1_zone[V1_NR_DZONES];
758 put_block(b, (char *) inode);
759 b = indir << zone_shift;
760 get_block(b, (char *) blk);
761 for (i = 0; i < V1_INDIRECTS; i++)
762 if (blk[i] == 0) {
763 blk[i] = (zone1_t) z;
764 put_block(b, (char *) blk);
765 return;
767 pexit("File has grown beyond single indirect");
770 void add_z_2(n, z, bytes, cur_time)
771 ino_t n;
772 zone_t z;
773 long bytes, cur_time;
775 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
777 int off, i;
778 block_t b;
779 zone_t indir;
780 zone_t *blk;
781 d2_inode *p;
782 d2_inode *inode;
784 if(!(blk = malloc(V2_INDIRECTS(block_size)*sizeof(*blk))))
785 pexit("Couldn't allocate indirect block");
787 if(!(inode = malloc(V2_INODES_PER_BLOCK(block_size)*sizeof(*inode))))
788 pexit("Couldn't allocate block of inodes");
790 b = ((n - 1) / V2_INODES_PER_BLOCK(block_size)) + inode_offset;
791 off = (n - 1) % V2_INODES_PER_BLOCK(block_size);
792 get_block(b, (char *) inode);
793 p = &inode[off];
794 p->d2_size += bytes;
795 p->d2_mtime = cur_time;
796 for (i = 0; i < V2_NR_DZONES; i++)
797 if (p->d2_zone[i] == 0) {
798 p->d2_zone[i] = z;
799 put_block(b, (char *) inode);
800 free(blk);
801 free(inode);
802 return;
804 put_block(b, (char *) inode);
806 /* File has grown beyond a small file. */
807 if (p->d2_zone[V2_NR_DZONES] == 0) p->d2_zone[V2_NR_DZONES] = alloc_zone();
808 indir = p->d2_zone[V2_NR_DZONES];
809 put_block(b, (char *) inode);
810 b = indir << zone_shift;
811 get_block(b, (char *) blk);
812 for (i = 0; i < V2_INDIRECTS(block_size); i++)
813 if (blk[i] == 0) {
814 blk[i] = z;
815 put_block(b, (char *) blk);
816 free(blk);
817 free(inode);
818 return;
820 pexit("File has grown beyond single indirect");
824 void incr_link(n)
825 ino_t n;
827 /* Increment the link count to inode n */
828 int off;
829 static int enter = 0;
830 block_t b;
832 if(enter) exit(1);
834 b = ((n - 1) / inodes_per_block) + inode_offset;
835 off = (n - 1) % inodes_per_block;
836 if (fs_version == 1) {
837 d1_inode inode1[V1_INODES_PER_BLOCK];
839 get_block(b, (char *) inode1);
840 inode1[off].d1_nlinks++;
841 put_block(b, (char *) inode1);
842 } else {
843 static d2_inode *inode2 = NULL;
844 int n;
846 n = sizeof(*inode2) * V2_INODES_PER_BLOCK(block_size);
847 if(!inode2 && !(inode2 = malloc(n)))
848 pexit("couldn't allocate a block of inodes");
850 get_block(b, (char *) inode2);
851 inode2[off].d2_nlinks++;
852 put_block(b, (char *) inode2);
854 enter = 0;
858 void incr_size(n, count)
859 ino_t n;
860 long count;
862 /* Increment the file-size in inode n */
863 block_t b;
864 int off;
866 b = ((n - 1) / inodes_per_block) + inode_offset;
867 off = (n - 1) % inodes_per_block;
868 if (fs_version == 1) {
869 d1_inode inode1[V1_INODES_PER_BLOCK];
871 get_block(b, (char *) inode1);
872 inode1[off].d1_size += count;
873 put_block(b, (char *) inode1);
874 } else {
875 d2_inode *inode2;
876 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
877 pexit("couldn't allocate a block of inodes");
879 get_block(b, (char *) inode2);
880 inode2[off].d2_size += count;
881 put_block(b, (char *) inode2);
882 free(inode2);
887 /*================================================================
888 * allocation assist group
889 *===============================================================*/
890 PRIVATE ino_t alloc_inode(mode, usrid, grpid)
891 int mode, usrid, grpid;
893 ino_t num;
894 int off;
895 block_t b;
897 num = next_inode++;
898 if (num > nrinodes) {
899 fprintf(stderr, "have %d inodoes\n", nrinodes);
900 pexit("File system does not have enough inodes");
902 b = ((num - 1) / inodes_per_block) + inode_offset;
903 off = (num - 1) % inodes_per_block;
904 if (fs_version == 1) {
905 d1_inode inode1[V1_INODES_PER_BLOCK];
907 get_block(b, (char *) inode1);
908 inode1[off].d1_mode = mode;
909 inode1[off].d1_uid = usrid;
910 inode1[off].d1_gid = grpid;
911 put_block(b, (char *) inode1);
912 } else {
913 d2_inode *inode2;
915 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
916 pexit("couldn't allocate a block of inodes");
918 get_block(b, (char *) inode2);
919 inode2[off].d2_mode = mode;
920 inode2[off].d2_uid = usrid;
921 inode2[off].d2_gid = grpid;
922 put_block(b, (char *) inode2);
924 free(inode2);
927 /* Set the bit in the bit map. */
928 /* DEBUG FIXME. This assumes the bit is in the first inode map block. */
929 insert_bit((block_t) INODE_MAP, (int) num);
930 return(num);
934 PRIVATE zone_t alloc_zone()
936 /* Allocate a new zone */
937 /* Works for zone > block */
938 block_t b;
939 int i;
940 zone_t z;
942 z = next_zone++;
943 b = z << zone_shift;
944 if ((b + zone_size) > nrblocks)
945 pexit("File system not big enough for all the files");
946 for (i = 0; i < zone_size; i++)
947 put_block(b + i, zero); /* give an empty zone */
948 /* DEBUG FIXME. This assumes the bit is in the first zone map block. */
949 insert_bit(zone_map, (int) (z - zoff)); /* lint, NOT OK because
950 * z hasn't been broken
951 * up into block +
952 * offset yet. */
953 return(z);
957 void insert_bit(block, bit)
958 block_t block;
959 int bit;
961 /* Insert 'count' bits in the bitmap */
962 int w, s;
963 short *buf;
965 buf = (short *) alloc_block();
967 if (block < 0) pexit("insert_bit called with negative argument");
968 get_block(block, (char *) buf);
969 w = bit / (8 * sizeof(short));
970 s = bit % (8 * sizeof(short));
971 buf[w] |= (1 << s);
972 put_block(block, (char *) buf);
974 free(buf);
978 /*================================================================
979 * proto-file processing assist group
980 *===============================================================*/
981 int mode_con(p)
982 char *p;
984 /* Convert string to mode */
985 int o1, o2, o3, mode;
986 char c1, c2, c3;
988 c1 = *p++;
989 c2 = *p++;
990 c3 = *p++;
991 o1 = *p++ - '0';
992 o2 = *p++ - '0';
993 o3 = *p++ - '0';
994 mode = (o1 << 6) | (o2 << 3) | o3;
995 if (c1 == 'd') mode += I_DIRECTORY;
996 if (c1 == 'b') mode += I_BLOCK_SPECIAL;
997 if (c1 == 'c') mode += I_CHAR_SPECIAL;
998 if (c1 == '-') mode += I_REGULAR;
999 if (c2 == 'u') mode += I_SET_UID_BIT;
1000 if (c3 == 'g') mode += I_SET_GID_BIT;
1001 return(mode);
1004 void getline(line, parse)
1005 char *parse[MAX_TOKENS];
1006 char line[LINE_LEN];
1008 /* Read a line and break it up in tokens */
1009 int k;
1010 char c, *p;
1011 int d;
1013 for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0;
1014 for (k = 0; k < LINE_LEN; k++) line[k] = 0;
1015 k = 0;
1016 parse[0] = 0;
1017 p = line;
1018 while (1) {
1019 if (++k > LINE_LEN) pexit("Line too long");
1020 d = fgetc(proto);
1021 if (d == EOF) pexit("Unexpected end-of-file");
1022 *p = d;
1023 if (*p == '\n') lct++;
1024 if (*p == ' ' || *p == '\t') *p = 0;
1025 if (*p == '\n') {
1026 *p++ = 0;
1027 *p = '\n';
1028 break;
1030 p++;
1033 k = 0;
1034 p = line;
1035 lastp = line;
1036 while (1) {
1037 c = *p++;
1038 if (c == '\n') return;
1039 if (c == 0) continue;
1040 parse[k++] = p - 1;
1041 do {
1042 c = *p++;
1043 } while (c != 0 && c != '\n');
1048 /*================================================================
1049 * other stuff
1050 *===============================================================*/
1051 void check_mtab(devname)
1052 char *devname; /* /dev/hd1 or whatever */
1054 /* Check to see if the special file named in s is mounted. */
1056 int n, r;
1057 struct stat sb;
1058 char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1], version[10], rw_flag[10];
1060 r= stat(devname, &sb);
1061 if (r == -1)
1063 if (errno == ENOENT)
1064 return; /* Does not exist, and therefore not mounted. */
1065 fprintf(stderr, "%s: stat %s failed: %s\n",
1066 progname, devname, strerror(errno));
1067 exit(1);
1069 if (!S_ISBLK(sb.st_mode))
1071 /* Not a block device and therefore not mounted. */
1072 return;
1075 if (load_mtab("mkfs") < 0) return;
1076 while (1) {
1077 n = get_mtab_entry(special, mounted_on, version, rw_flag);
1078 if (n < 0) return;
1079 if (strcmp(devname, special) == 0) {
1080 /* Can't mkfs on top of a mounted file system. */
1081 fprintf(stderr, "%s: %s is mounted on %s\n",
1082 progname, devname, mounted_on);
1083 exit(1);
1089 long file_time(f)
1090 int f;
1092 #ifdef UNIX
1093 struct stat statbuf;
1094 fstat(f, &statbuf);
1095 return(statbuf.st_mtime);
1096 #else /* fstat not supported by DOS */
1097 return(0L);
1098 #endif
1102 void pexit(s)
1103 char *s;
1105 fprintf(stderr, "%s: %s\n", progname, s);
1106 if (lct != 0)
1107 fprintf(stderr, "Line %d being processed when error detected.\n", lct);
1108 flush();
1109 exit(2);
1113 void copy(from, to, count)
1114 char *from, *to;
1115 int count;
1117 while (count--) *to++ = *from++;
1120 char *alloc_block()
1122 char *buf;
1124 if(!(buf = malloc(block_size))) {
1125 pexit("couldn't allocate filesystem buffer");
1127 bzero(buf, block_size);
1129 return buf;
1132 void print_fs()
1134 int i, j;
1135 ino_t k;
1136 d1_inode inode1[V1_INODES_PER_BLOCK];
1137 d2_inode *inode2;
1138 unsigned short *usbuf;
1139 block_t b, inode_limit;
1140 struct direct *dir;
1142 if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*inode2))))
1143 pexit("couldn't allocate a block of inodes");
1145 if(!(dir = malloc(NR_DIR_ENTRIES(block_size)*sizeof(*dir))))
1146 pexit("malloc of directory entry failed");
1148 usbuf = (unsigned short *) alloc_block();
1150 get_super_block((char *) usbuf);
1151 printf("\nSuperblock: ");
1152 for (i = 0; i < 8; i++) printf("%06o ", usbuf[i]);
1153 get_block((block_t) 2, (char *) usbuf);
1154 printf("...\nInode map: ");
1155 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1156 get_block((block_t) 3, (char *) usbuf);
1157 printf("...\nZone map: ");
1158 for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]);
1159 printf("...\n");
1161 free(usbuf);
1162 usbuf = NULL;
1164 k = 0;
1165 for (b = inode_offset; k < nrinodes; b++) {
1166 if (fs_version == 1) {
1167 get_block(b, (char *) inode1);
1168 } else {
1169 get_block(b, (char *) inode2);
1171 for (i = 0; i < inodes_per_block; i++) {
1172 k = inodes_per_block * (int) (b - inode_offset) + i + 1;
1173 /* Lint but OK */
1174 if (k > nrinodes) break;
1175 if (fs_version == 1) {
1176 if (inode1[i].d1_mode != 0) {
1177 printf("Inode %2d: mode=", k);
1178 printf("%06o", inode1[i].d1_mode);
1179 printf(" uid=%2d gid=%2d size=",
1180 inode1[i].d1_uid, inode1[i].d1_gid);
1181 printf("%6ld", inode1[i].d1_size);
1182 printf(" zone[0]=%d\n", inode1[i].d1_zone[0]);
1184 if ((inode1[i].d1_mode & I_TYPE) == I_DIRECTORY) {
1185 /* This is a directory */
1186 get_block(inode1[i].d1_zone[0], (char *) dir);
1187 for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1188 if (dir[j].d_ino)
1189 printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name);
1191 } else {
1192 if (inode2[i].d2_mode != 0) {
1193 printf("Inode %2d: mode=", k);
1194 printf("%06o", inode2[i].d2_mode);
1195 printf(" uid=%2d gid=%2d size=",
1196 inode2[i].d2_uid, inode2[i].d2_gid);
1197 printf("%6ld", inode2[i].d2_size);
1198 printf(" zone[0]=%ld\n", inode2[i].d2_zone[0]);
1200 if ((inode2[i].d2_mode & I_TYPE) == I_DIRECTORY) {
1201 /* This is a directory */
1202 get_block(inode2[i].d2_zone[0], (char *) dir);
1203 for (j = 0; j < NR_DIR_ENTRIES(block_size); j++)
1204 if (dir[j].d_ino)
1205 printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name);
1211 printf("%d inodes used. %d zones used.\n", next_inode - 1, next_zone);
1212 free(dir);
1213 free(inode2);
1217 int read_and_set(n)
1218 block_t n;
1220 /* The first time a block is read, it returns all 0s, unless there has
1221 * been a write. This routine checks to see if a block has been accessed.
1224 int w, s, mask, r;
1226 w = n / 8;
1227 if(w >= umap_array_elements) {
1228 pexit("umap array too small - this can't happen");
1230 s = n % 8;
1231 mask = 1 << s;
1232 r = (umap_array[w] & mask ? 1 : 0);
1233 umap_array[w] |= mask;
1234 return(r);
1237 void usage()
1239 fprintf(stderr,
1240 "Usage: %s [-12dlot] [-b blocks] [-i inodes] [-B blocksize] special [proto]\n",
1241 progname);
1242 exit(1);
1245 /*================================================================
1246 * get_block & put_block for MS-DOS
1247 *===============================================================*/
1248 #ifdef DOS
1251 * These are the get_block and put_block routines
1252 * when compiling & running mkfs.c under MS-DOS.
1254 * It requires the (asembler) routines absread & abswrite
1255 * from the file diskio.asm. Since these routines just do
1256 * as they are told (read & write the sector specified),
1257 * a local cache is used to minimize the i/o-overhead for
1258 * frequently used blocks.
1260 * The global variable "file" determines whether the output
1261 * is to a disk-device or to a binary file.
1265 #define PH_SECTSIZE 512 /* size of a physical disk-sector */
1268 char *derrtab[14] = {
1269 "no error",
1270 "disk is read-only",
1271 "unknown unit",
1272 "device not ready",
1273 "bad command",
1274 "data error",
1275 "internal error: bad request structure length",
1276 "seek error",
1277 "unknown media type",
1278 "sector not found",
1279 "printer out of paper (?)",
1280 "write fault",
1281 "read error",
1282 "general error"
1285 #define CACHE_SIZE 20 /* 20 block-buffers */
1288 struct cache {
1289 char blockbuf[BLOCK_SIZE];
1290 block_t blocknum;
1291 int dirty;
1292 int usecnt;
1293 } cache[CACHE_SIZE];
1296 void special(string)
1297 char *string;
1300 if (string[1] == ':' && string[2] == 0) {
1301 /* Format: d: or d:fname */
1302 disk = (string[0] & ~32) - 'A';
1303 if (disk > 1 && !override) /* safety precaution */
1304 pexit("Bad drive specifier for special");
1305 } else {
1306 file = 1;
1307 if ((fd = creat(string, BWRITE)) == 0)
1308 pexit("Can't open special file");
1312 void get_block(n, buf)
1313 block_t n;
1314 char *buf;
1316 /* Get a block to the user */
1317 struct cache *bp, *fp;
1319 /* First access returns a zero block */
1320 if (read_and_set(n) == 0) {
1321 copy(zero, buf, block_size);
1322 return;
1325 /* Look for block in cache */
1326 fp = 0;
1327 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) {
1328 if (bp->blocknum == n) {
1329 copy(bp, buf, block_size);
1330 bp->usecnt++;
1331 return;
1334 /* Remember clean block */
1335 if (bp->dirty == 0)
1336 if (fp) {
1337 if (fp->usecnt > bp->usecnt) fp = bp;
1338 } else
1339 fp = bp;
1342 /* Block not in cache, get it */
1343 if (!fp) {
1344 /* No clean buf, flush one */
1345 for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++)
1346 if (fp->usecnt > bp->usecnt) fp = bp;
1347 mx_write(fp->blocknum, fp);
1349 mx_read(n, fp);
1350 fp->dirty = 0;
1351 fp->usecnt = 0;
1352 fp->blocknum = n;
1353 copy(fp, buf, block_size);
1356 void put_block(n, buf)
1357 block_t n;
1358 char *buf;
1360 /* Accept block from user */
1361 struct cache *fp, *bp;
1363 (void) read_and_set(n);
1365 /* Look for block in cache */
1366 fp = 0;
1367 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) {
1368 if (bp->blocknum == n) {
1369 copy(buf, bp, block_size);
1370 bp->dirty = 1;
1371 return;
1374 /* Remember clean block */
1375 if (bp->dirty == 0)
1376 if (fp) {
1377 if (fp->usecnt > bp->usecnt) fp = bp;
1378 } else
1379 fp = bp;
1382 /* Block not in cache */
1383 if (!fp) {
1384 /* No clean buf, flush one */
1385 for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++)
1386 if (fp->usecnt > bp->usecnt) fp = bp;
1387 mx_write(fp->blocknum, fp);
1389 fp->dirty = 1;
1390 fp->usecnt = 1;
1391 fp->blocknum = n;
1392 copy(buf, fp, block_size);
1395 void cache_init()
1397 struct cache *bp;
1398 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) bp->blocknum = -1;
1401 void flush()
1403 /* Flush all dirty blocks to disk */
1404 struct cache *bp;
1406 for (bp = cache; bp < &cache[CACHE_SIZE]; bp++)
1407 if (bp->dirty) {
1408 mx_write(bp->blocknum, bp);
1409 bp->dirty = 0;
1413 /*==================================================================
1414 * hard read & write etc.
1415 *=================================================================*/
1416 #define MAX_RETRIES 5
1419 void mx_read(blocknr, buf)
1420 int blocknr;
1421 char *buf;
1424 /* Read the requested MINIX-block in core */
1425 char (*bp)[PH_SECTSIZE];
1426 int sectnum, retries, err;
1428 if (file) {
1429 lseek(fd, (off_t) blocknr * block_size, 0);
1430 if (read(fd, buf, block_size) != block_size)
1431 pexit("mx_read: error reading file");
1432 } else {
1433 sectnum = blocknr * (block_size / PH_SECTSIZE);
1434 for (bp = buf; bp < &buf[block_size]; bp++) {
1435 retries = MAX_RETRIES;
1437 err = absread(disk, sectnum, bp);
1438 while (err && --retries);
1440 if (retries) {
1441 sectnum++;
1442 } else {
1443 dexit("mx_read", sectnum, err);
1449 void mx_write(blocknr, buf)
1450 int blocknr;
1451 char *buf;
1453 /* Write the MINIX-block to disk */
1454 char (*bp)[PH_SECTSIZE];
1455 int retries, sectnum, err;
1457 if (file) {
1458 lseek(fd, blocknr * block_size, 0);
1459 if (write(fd, buf, block_size) != block_size) {
1460 pexit("mx_write: error writing file");
1462 } else {
1463 sectnum = blocknr * (block_size / PH_SECTSIZE);
1464 for (bp = buf; bp < &buf[block_size]; bp++) {
1465 retries = MAX_RETRIES;
1466 do {
1467 err = abswrite(disk, sectnum, bp);
1468 } while (err && --retries);
1470 if (retries) {
1471 sectnum++;
1472 } else {
1473 dexit("mx_write", sectnum, err);
1480 void dexit(s, sectnum, err)
1481 int sectnum, err;
1482 char *s;
1484 printf("Error: %s, sector: %d, code: %d, meaning: %s\n",
1485 s, sectnum, err, derrtab[err]);
1486 exit(2);
1489 #endif
1491 /*================================================================
1492 * get_block & put_block for UNIX
1493 *===============================================================*/
1494 #ifdef UNIX
1496 void special(string)
1497 char *string;
1499 fd = creat(string, 0777);
1500 close(fd);
1501 fd = open(string, O_RDWR);
1502 if (fd < 0) pexit("Can't open special file");
1507 void get_block(n, buf)
1508 block_t n;
1509 char *buf;
1511 /* Read a block. */
1513 int k;
1515 /* First access returns a zero block */
1516 if (read_and_set(n) == 0) {
1517 copy(zero, buf, block_size);
1518 return;
1520 lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL);
1521 k = read(fd, buf, block_size);
1522 if (k != block_size) {
1523 pexit("get_block couldn't read");
1527 void get_super_block(buf)
1528 char *buf;
1530 /* Read a block. */
1532 int k;
1534 if(lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET) < 0) {
1535 perror("lseek");
1536 pexit("seek failed");
1538 k = read(fd, buf, _STATIC_BLOCK_SIZE);
1539 if (k != _STATIC_BLOCK_SIZE) {
1540 pexit("get_super_block couldn't read");
1544 void put_block(n, buf)
1545 block_t n;
1546 char *buf;
1548 /* Write a block. */
1550 (void) read_and_set(n);
1552 /* XXX - check other lseeks too. */
1553 if (lseek64(fd, mul64u(n, block_size), SEEK_SET, NULL) == (off_t) -1) {
1554 pexit("put_block couldn't seek");
1556 if (write(fd, buf, block_size) != block_size) {
1557 pexit("put_block couldn't write");
1562 /* Dummy routines to keep source file clean from #ifdefs */
1564 void flush()
1566 return;
1569 void cache_init()
1571 return;
1574 #endif