1 /* mkfs - make the MINIX filesystem Authors: Tanenbaum et al. */
3 /* Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans */
5 #if HAVE_NBTOOL_CONFIG_H
6 #include "nbtool_config.h"
10 #include <sys/types.h>
14 #include <minix/minlib.h>
15 #include <minix/partition.h>
16 #include <sys/ioctl.h>
32 /* Definition of the file system layout: */
38 #define INODE_MAP START_BLOCK
39 /* inode zone indexes pointing to single and double indirect zones */
40 #define S_INDIRECT_IDX (NR_DZONES)
41 #define D_INDIRECT_IDX (NR_DZONES+1)
47 /* some Minix specific types that do not conflict with Posix */
49 typedef uint32_t block_t
; /* block number */
52 typedef uint32_t zone_t
; /* zone number */
55 typedef uint32_t bit_t
; /* bit number in a bit map */
58 typedef uint32_t bitchunk_t
; /* collection of bits in a bitmap */
62 ino_t inocount
; /* amount of inodes */
63 zone_t zonecount
; /* amount of zones */
64 block_t blockcount
; /* amount of blocks */
71 int zone_per_block
, zone_shift
= 0;
72 zone_t next_zone
, zoff
, nr_indirzones
;
73 int inodes_per_block
, indir_per_block
, indir_per_zone
;
74 unsigned int zone_size
;
75 ino_t nrinodes
, inode_offset
, next_inode
;
76 int lct
= 0, fd
, print
= 0;
77 int simple
= 0, dflag
= 0, verbose
= 0;
78 int donttest
; /* skip test if it fits on medium */
80 uint64_t fs_offset_bytes
, fs_offset_blocks
, written_fs_size
= 0;
84 unsigned char *umap_array
; /* bit map tells if block read yet */
85 size_t umap_array_elements
;
86 block_t zone_map
; /* where is zone map? (depends on # inodes) */
87 #ifndef MFS_STATIC_BLOCK_SIZE
90 #define block_size MFS_STATIC_BLOCK_SIZE
95 int main(int argc
, char **argv
);
96 void detect_fs_size(struct fs_size
* fssize
);
97 void sizeup_dir(struct fs_size
* fssize
);
98 block_t
sizeup(char *device
);
99 static int bitmapsize(bit_t nr_bits
, size_t blk_size
);
100 void super(zone_t zones
, ino_t inodes
);
101 void rootdir(ino_t inode
);
102 void enter_symlink(ino_t inode
, char *link
);
103 int dir_try_enter(zone_t z
, ino_t child
, char const *name
);
104 void eat_dir(ino_t parent
);
105 void eat_file(ino_t inode
, int f
);
106 void enter_dir(ino_t parent
, char const *name
, ino_t child
);
107 void add_zone(ino_t n
, zone_t z
, size_t bytes
, time_t cur_time
);
108 void incr_link(ino_t n
);
109 void incr_size(ino_t n
, size_t count
);
110 static ino_t
alloc_inode(int mode
, int usrid
, int grpid
);
111 static zone_t
alloc_zone(void);
112 void insert_bit(block_t block
, bit_t bit
);
113 int mode_con(char *p
);
114 void get_line(char line
[LINE_LEN
], char *parse
[MAX_TOKENS
]);
115 void check_mtab(const char *devname
);
116 time_t file_time(int f
);
117 __dead
void pexit(char const *s
, ...) __printflike(1,2);
118 void *alloc_block(void);
120 int read_and_set(block_t n
);
121 void special(char *string
, int insertmode
);
122 __dead
void usage(void);
123 void get_block(block_t n
, void *buf
);
124 void get_super_block(void *buf
);
125 void put_block(block_t n
, void *buf
);
126 static uint64_t mkfs_seek(uint64_t pos
, int whence
);
127 static ssize_t
mkfs_write(void * buf
, size_t count
);
129 /*================================================================
130 * mkfs - make filesystem
131 *===============================================================*/
133 main(int argc
, char *argv
[])
135 int nread
, mode
, usrid
, grpid
, ch
, extra_space_percent
, Tflag
= 0;
136 block_t blocks
, maxblocks
, bblocks
;
137 ino_t inodes
, root_inum
;
138 char *token
[MAX_TOKENS
], line
[LINE_LEN
], *sfx
;
139 struct fs_size fssize
;
144 /* Process switches. */
148 #ifndef MFS_STATIC_BLOCK_SIZE
152 extra_space_percent
= 0;
153 while ((ch
= getopt(argc
, argv
, "B:b:di:ltvx:z:I:T:")) != EOF
)
155 #ifndef MFS_STATIC_BLOCK_SIZE
157 block_size
= strtoul(optarg
, &sfx
, 0);
159 case 'b': case 'B': /* bytes; NetBSD-compatible */
162 case 'k': block_size
*=1024; break;
163 case 's': block_size
*=SECTOR_SIZE
; break;
169 if (block_size
!= strtoul(optarg
, (char **) NULL
, 0))
170 errx(4, "block size must be exactly %d bytes",
171 MFS_STATIC_BLOCK_SIZE
);
173 (void)sfx
; /* shut up warnings about unused variable...*/
176 fs_offset_bytes
= strtoul(optarg
, (char **) NULL
, 0);
180 blocks
= bblocks
= strtoul(optarg
, (char **) NULL
, 0);
184 current_time
= strtoul(optarg
, (char **) NULL
, 0);
190 inodes
= strtoul(optarg
, (char **) NULL
, 0);
192 case 'l': print
= 1; break;
193 case 't': donttest
= 1; break;
194 case 'v': ++verbose
; break;
195 case 'x': extra_space_percent
= atoi(optarg
); break;
196 case 'z': zone_shift
= atoi(optarg
); break;
200 if (argc
== optind
) usage();
202 /* Get the current time, set it to the mod time of the binary of
203 * mkfs itself when the -d flag is used. The 'current' time is put into
204 * the i_mtimes of all the files. This -d feature is useful when
205 * producing a set of file systems, and one wants all the times to be
206 * identical. First you set the time of the mkfs binary to what you
211 errx(1, "-T and -d both specify a time and so are mutually exclusive");
214 if (stat(progname
, &statbuf
)) {
215 err(1, "stat of itself");
217 current_time
= statbuf
.st_mtime
;
219 current_time
= time((time_t *) 0); /* time mkfs is being run */
222 /* Percentage of extra size must be nonnegative.
223 * It can legitimately be bigger than 100 but has to make some sort of sense.
225 if(extra_space_percent
< 0 || extra_space_percent
> 2000) usage();
227 #ifdef DEFAULT_BLOCK_SIZE
228 if(!block_size
) block_size
= DEFAULT_BLOCK_SIZE
;
230 if (block_size
% SECTOR_SIZE
)
231 errx(4, "block size must be multiple of sector (%d bytes)", SECTOR_SIZE
);
232 #ifdef MIN_BLOCK_SIZE
233 if (block_size
< MIN_BLOCK_SIZE
)
234 errx(4, "block size must be at least %d bytes", MIN_BLOCK_SIZE
);
236 #ifdef MAX_BLOCK_SIZE
237 if (block_size
> MAX_BLOCK_SIZE
)
238 errx(4, "block size must be at most %d bytes", MAX_BLOCK_SIZE
);
240 if(block_size
%INODE_SIZE
)
241 errx(4, "block size must be a multiple of inode size (%d bytes)", INODE_SIZE
);
243 if(zone_shift
< 0 || zone_shift
> 14)
244 errx(4, "zone_shift must be a small non-negative integer");
245 zone_per_block
= 1 << zone_shift
; /* nr of blocks per zone */
247 inodes_per_block
= INODES_PER_BLOCK(block_size
);
248 indir_per_block
= INDIRECTS(block_size
);
249 indir_per_zone
= INDIRECTS(block_size
) << zone_shift
;
250 /* number of file zones we can address directly and with a single indirect*/
251 nr_indirzones
= NR_DZONES
+ indir_per_zone
;
252 zone_size
= block_size
<< zone_shift
;
253 /* Checks for an overflow: only with very big block size */
255 errx(4, "Zones are too big for this program; smaller -B or -z, please!");
257 /* now that the block size is known, do buffer allocations where
260 zero
= alloc_block();
262 fs_offset_blocks
= roundup(fs_offset_bytes
, block_size
) / block_size
;
264 /* Determine the size of the device if not specified as -b or proto. */
265 maxblocks
= sizeup(argv
[optind
]);
266 if (bblocks
!= 0 && bblocks
+ fs_offset_blocks
> maxblocks
&& !insertmode
) {
267 errx(4, "Given size -b %d exceeds device capacity(%d)\n", bblocks
, maxblocks
);
270 if (argc
- optind
== 1 && bblocks
== 0) {
272 /* blocks == 0 is checked later, but leads to a funny way of
273 * reporting a 0-sized device (displays usage).
276 errx(1, "zero size device.");
280 /* The remaining args must be 'special proto', or just 'special' if the
281 * no. of blocks has already been specified.
283 if (argc
- optind
!= 2 && (argc
- optind
!= 1 || blocks
== 0)) usage();
285 if (maxblocks
&& blocks
> maxblocks
&& !insertmode
) {
286 errx(1, "%s: number of blocks too large for device.", argv
[optind
]);
290 check_mtab(argv
[optind
]);
292 /* Check and start processing proto. */
293 optarg
= argv
[++optind
];
294 if (optind
< argc
&& (proto
= fopen(optarg
, "r")) != NULL
) {
295 /* Prototype file is readable. */
297 get_line(line
, token
); /* skip boot block info */
299 /* Read the line with the block and inode counts. */
300 get_line(line
, token
);
302 blocks
= strtol(token
[0], (char **) NULL
, 10);
304 if(bblocks
< strtol(token
[0], (char **) NULL
, 10)) {
305 errx(1, "%s: number of blocks given as parameter(%d)"
306 " is too small for given proto file(%ld).",
307 argv
[optind
], bblocks
,
308 strtol(token
[0], (char **) NULL
, 10));
311 inodes
= strtol(token
[1], (char **) NULL
, 10);
313 /* Process mode line for root directory. */
314 get_line(line
, token
);
315 mode
= mode_con(token
[0]);
316 usrid
= atoi(token
[1]);
317 grpid
= atoi(token
[2]);
319 if(blocks
<= 0 && inodes
<= 0){
320 detect_fs_size(&fssize
);
321 blocks
= fssize
.blockcount
;
322 inodes
= fssize
.inocount
;
323 blocks
+= blocks
*extra_space_percent
/100;
324 inodes
+= inodes
*extra_space_percent
/100;
325 /* XXX is it OK to write on stdout? Use warn() instead? Also consider using verbose */
326 fprintf(stderr
, "dynamically sized filesystem: %u blocks, %u inodes\n",
327 (unsigned int) blocks
, (unsigned int) inodes
);
332 /* Maybe the prototype file is just a size. Check. */
333 blocks
= strtoul(optarg
, (char **) NULL
, 0);
334 if (blocks
== 0) errx(2, "Can't open prototype file");
337 /* Make simple file system of the given size, using defaults. */
345 long long kb
= ((unsigned long long)blocks
*block_size
) / 1024;
348 if (kb
>= 100000) inodes
= kb
/ 4;
349 if (kb
>= 1000000) inodes
= kb
/ 6;
350 if (kb
>= 10000000) inodes
= kb
/ 8;
351 if (kb
>= 100000000) inodes
= kb
/ 10;
352 if (kb
>= 1000000000) inodes
= kb
/ 12;
353 /* XXX check overflow: with very large number of blocks, this results in insanely large number of inodes */
354 /* XXX check underflow (if/when ino_t is signed), else the message below will look strange */
356 /* round up to fill inode block */
357 inodes
+= inodes_per_block
- 1;
358 inodes
= inodes
/ inodes_per_block
* inodes_per_block
;
361 if (blocks
< 5) errx(1, "Block count too small");
362 if (inodes
< 1) errx(1, "Inode count too small");
367 umap_array_elements
= 1 + blocks
/8;
368 if(!(umap_array
= malloc(umap_array_elements
)))
369 err(1, "can't allocate block bitmap (%u bytes).",
370 (unsigned)umap_array_elements
);
373 special(argv
[--optind
], insertmode
);
379 testb
= alloc_block();
381 /* Try writing the last block of partition or diskette. */
382 mkfs_seek((uint64_t)(blocks
- 1) * block_size
, SEEK_SET
);
385 testb
[block_size
/2-1] = 0x1F2F;
386 w
=mkfs_write(testb
, block_size
);
387 sync(); /* flush write, so if error next read fails */
388 mkfs_seek((uint64_t)(blocks
- 1) * block_size
, SEEK_SET
);
391 testb
[block_size
/2-1] = 0;
392 nread
= read(fd
, testb
, block_size
);
393 if (nread
!= block_size
|| testb
[0] != 0x3245 || testb
[1] != 0x11FF ||
394 testb
[block_size
/2-1] != 0x1F2F) {
395 warn("nread = %d\n", nread
);
396 warnx("testb = 0x%x 0x%x 0x%x\n",
397 testb
[0], testb
[1], testb
[block_size
-1]);
398 errx(1, "File system is too big for minor device (read)");
400 mkfs_seek((uint64_t)(blocks
- 1) * block_size
, SEEK_SET
);
403 testb
[block_size
/2-1] = 0;
404 mkfs_write(testb
, block_size
);
405 mkfs_seek(0L, SEEK_SET
);
409 /* Make the file-system */
411 put_block(BOOT_BLOCK
, zero
); /* Write a null boot block. */
412 put_block(BOOT_BLOCK
+1, zero
); /* Write another null block. */
414 super(nrblocks
>> zone_shift
, inodes
);
416 root_inum
= alloc_inode(mode
, usrid
, grpid
);
418 if (simple
== 0) eat_dir(root_inum
);
420 if (print
) print_fs();
421 else if (verbose
> 1) {
423 fprintf(stderr
, "%d inodes used. %u zones (%u blocks) used.\n",
424 (int)next_inode
-1, next_zone
, next_zone
*zone_per_block
);
426 fprintf(stderr
, "%d inodes used. %u zones used.\n",
427 (int)next_inode
-1, next_zone
);
430 if(insertmode
) printf("%"PRIu64
"\n", written_fs_size
);
437 /*================================================================
438 * detect_fs_size - determine image size dynamically
439 *===============================================================*/
441 detect_fs_size(struct fs_size
* fssize
)
444 off_t point
= ftell(proto
);
448 fssize
->inocount
= 1; /* root directory node */
449 fssize
->zonecount
= 0;
450 fssize
->blockcount
= 0;
454 initb
= bitmapsize(1 + fssize
->inocount
, block_size
);
455 initb
+= bitmapsize(fssize
->zonecount
, block_size
);
456 initb
+= START_BLOCK
;
457 initb
+= (fssize
->inocount
+ inodes_per_block
- 1) / inodes_per_block
;
458 initz
= (initb
+ zone_per_block
- 1) >> zone_shift
;
460 fssize
->blockcount
= initb
+ fssize
->zonecount
;
462 fseek(proto
, point
, SEEK_SET
);
466 sizeup_dir(struct fs_size
* fssize
)
468 char *token
[MAX_TOKENS
], *p
;
473 zone_t dir_zones
= 0, fzones
, indirects
;
476 get_line(line
, token
);
479 dir_zones
= (dir_entries
/ (NR_DIR_ENTRIES(block_size
) * zone_per_block
));
480 if(dir_entries
% (NR_DIR_ENTRIES(block_size
) * zone_per_block
))
482 if(dir_zones
> NR_DZONES
)
483 dir_zones
++; /* Max single indir */
484 fssize
->zonecount
+= dir_zones
;
494 } else if (*p
== 'b' || *p
== 'c') {
496 } else if (*p
== 's') {
497 fssize
->zonecount
++; /* Symlink contents is always stored a block */
499 if ((f
= fopen(token
[4], "rb")) == NULL
) {
500 /* on minix natively, allow EACCES and skip the entry.
501 * while crossbuilding, always fail on error.
505 warn("dynamic sizing: can't open %s", token
[4]);
508 err(1, "dynamic sizing: can't open %s", token
[4]);
509 } else if (fseek(f
, 0, SEEK_END
) < 0) {
510 pexit("dynamic size detection failed: seek to end of %s",
512 } else if ( (size
= ftell(f
)) == (off_t
)(-1)) {
513 pexit("dynamic size detection failed: can't tell size of %s",
517 fzones
= roundup(size
, zone_size
) / zone_size
;
519 /* XXX overflow? fzones is u32, size is potentially 64-bit */
520 if (fzones
> NR_DZONES
)
521 indirects
++; /* single indirect needed */
522 if (fzones
> nr_indirzones
) {
523 /* Each further group of 'indir_per_zone'
524 * needs one supplementary indirect zone:
526 indirects
+= roundup(fzones
- nr_indirzones
,
527 indir_per_zone
) / indir_per_zone
;
528 indirects
++; /* + double indirect needed!*/
530 fssize
->zonecount
+= fzones
+ indirects
;
536 /*================================================================
537 * sizeup - determine device size
538 *===============================================================*/
540 sizeup(char * device
)
544 uint64_t bytes
, resize
;
551 if ((fd
= open(device
, O_RDONLY
)) == -1) {
553 perror("sizeup open");
558 if(minix_sizeup(device
, &bytes
) < 0) {
563 d
= (uint32_t)(bytes
/ block_size
);
564 rem
= (uint32_t)(bytes
% block_size
);
566 resize
= ((uint64_t)d
* block_size
) + rem
;
567 if(resize
!= bytes
) {
568 /* Assume block_t is unsigned */
570 fprintf(stderr
, "%s: truncating FS at %lu blocks\n",
571 progname
, (unsigned long)d
);
574 size
= mkfs_seek(0, SEEK_END
);
575 /* Assume block_t is unsigned */
576 if (size
/ block_size
> (block_t
)(-1ul)) {
578 fprintf(stderr
, "%s: truncating FS at %lu blocks\n",
579 progname
, (unsigned long)d
);
581 d
= size
/ block_size
;
591 bitmapsize(bit_t nr_bits
, size_t blk_size
)
595 nr_blocks
= nr_bits
/ FS_BITS_PER_BLOCK(blk_size
);
596 if (nr_blocks
* FS_BITS_PER_BLOCK(blk_size
) < nr_bits
)
601 /*================================================================
602 * super - construct a superblock
603 *===============================================================*/
606 super(zone_t zones
, ino_t inodes
)
608 block_t inodeblks
, initblks
, i
;
610 long long ind_per_zone
, zo
;
612 struct super_block
*sup
;
614 sup
= buf
= alloc_block();
617 /* The assumption is that mkfs will create a clean FS. */
618 sup
->s_flags
= MFSFLAG_CLEAN
;
621 sup
->s_ninodes
= inodes
;
622 /* Check for overflow; cannot happen on V3 file systems */
623 if(inodes
!= sup
->s_ninodes
)
624 errx(1, "Too much inodes for that version of Minix FS.");
625 sup
->s_nzones
= 0; /* not used in V2 - 0 forces errors early */
626 sup
->s_zones
= zones
;
627 /* Check for overflow; can only happen on V1 file systems */
628 if(zones
!= sup
->s_zones
)
629 errx(1, "Too much zones (blocks) for that version of Minix FS.");
631 #ifndef MFS_STATIC_BLOCK_SIZE
632 #define BIGGERBLOCKS "Please try a larger block size for an FS of this size."
634 #define BIGGERBLOCKS "Please use MinixFS V3 for an FS of this size."
636 sup
->s_imap_blocks
= nb
= bitmapsize(1 + inodes
, block_size
);
637 /* Checks for an overflow: nb is uint32_t while s_imap_blocks is of type
639 if(sup
->s_imap_blocks
!= nb
) {
640 errx(1, "too many inode bitmap blocks.\n" BIGGERBLOCKS
);
642 sup
->s_zmap_blocks
= nb
= bitmapsize(zones
, block_size
);
643 /* Idem here check for overflow */
644 if(nb
!= sup
->s_zmap_blocks
) {
645 errx(1, "too many block bitmap blocks.\n" BIGGERBLOCKS
);
647 inode_offset
= START_BLOCK
+ sup
->s_imap_blocks
+ sup
->s_zmap_blocks
;
648 inodeblks
= (inodes
+ inodes_per_block
- 1) / inodes_per_block
;
649 initblks
= inode_offset
+ inodeblks
;
650 sup
->s_firstdatazone_old
= nb
=
651 (initblks
+ (1 << zone_shift
) - 1) >> zone_shift
;
652 if(nb
>= zones
) errx(1, "bit maps too large");
653 if(nb
!= sup
->s_firstdatazone_old
) {
654 /* The field is too small to store the value. Fortunately, the value
655 * can be computed from other fields. We set the on-disk field to zero
656 * to indicate that it must not be used. Eventually, we can always set
657 * the on-disk field to zero, and stop using it.
659 sup
->s_firstdatazone_old
= 0;
661 sup
->s_firstdatazone
= nb
;
662 zoff
= sup
->s_firstdatazone
- 1;
663 sup
->s_log_zone_size
= zone_shift
;
664 sup
->s_magic
= SUPER_MAGIC
;
665 #ifdef MFS_SUPER_BLOCK_SIZE
666 sup
->s_block_size
= block_size
;
667 /* Check for overflow */
668 if(block_size
!= sup
->MFS_SUPER_BLOCK_SIZE
)
669 errx(1, "block_size too large.");
670 sup
->s_disk_version
= 0;
673 ind_per_zone
= (long long) indir_per_zone
;
674 zo
= NR_DZONES
+ ind_per_zone
+ ind_per_zone
*ind_per_zone
;
676 #define MAX_MAX_SIZE (INT32_MAX)
678 if(MAX_MAX_SIZE
/block_size
< zo
) {
679 sup
->s_max_size
= MAX_MAX_SIZE
;
682 sup
->s_max_size
= zo
* block_size
;
686 fprintf(stderr
, "Super block values:\n"
687 "\tnumber of inodes\t%12d\n"
688 "\tnumber of zones \t%12d\n"
689 "\tinode bit map blocks\t%12d\n"
690 "\tzone bit map blocks\t%12d\n"
691 "\tfirst data zone \t%12d\n"
692 "\tblocks per zone shift\t%12d\n"
693 "\tmaximum file size\t%12d\n"
694 "\tmagic number\t\t%#12X\n",
695 sup
->s_ninodes
, sup
->s_zones
,
696 sup
->s_imap_blocks
, sup
->s_zmap_blocks
, sup
->s_firstdatazone
,
697 sup
->s_log_zone_size
, sup
->s_max_size
, sup
->s_magic
);
698 #ifdef MFS_SUPER_BLOCK_SIZE
699 fprintf(stderr
, "\tblock size\t\t%12d\n", sup
->s_block_size
);
703 mkfs_seek((off_t
) SUPER_BLOCK_BYTES
, SEEK_SET
);
704 mkfs_write(buf
, SUPER_BLOCK_BYTES
);
706 /* Clear maps and inodes. */
707 for (i
= START_BLOCK
; i
< initblks
; i
++) put_block((block_t
) i
, zero
);
709 next_zone
= sup
->s_firstdatazone
;
712 zone_map
= INODE_MAP
+ sup
->s_imap_blocks
;
714 insert_bit(zone_map
, 0); /* bit zero must always be allocated */
715 insert_bit((block_t
) INODE_MAP
, 0); /* inode zero not used but
716 * must be allocated */
722 /*================================================================
723 * rootdir - install the root directory
724 *===============================================================*/
731 add_zone(inode
, z
, 2 * sizeof(struct direct
), current_time
);
732 enter_dir(inode
, ".", inode
);
733 enter_dir(inode
, "..", inode
);
739 enter_symlink(ino_t inode
, char *lnk
)
748 if (len
>= block_size
)
749 pexit("symlink too long, max length is %u", (unsigned)block_size
- 1);
751 put_block((z
<< zone_shift
), buf
);
753 add_zone(inode
, z
, len
, current_time
);
759 /*================================================================
760 * eat_dir - recursively install directory
761 *===============================================================*/
763 eat_dir(ino_t parent
)
765 /* Read prototype lines and set up directory. Recurse if need be. */
766 char *token
[MAX_TOKENS
], *p
;
768 int mode
, usrid
, grpid
, maj
, min
, f
;
774 get_line(line
, token
);
776 if (*p
== '$') return;
779 usrid
= atoi(token
[2]);
780 grpid
= atoi(token
[3]);
781 n
= alloc_inode(mode
, usrid
, grpid
);
783 /* Enter name in directory and update directory's size. */
784 enter_dir(parent
, token
[0], n
);
785 incr_size(parent
, sizeof(struct direct
));
787 /* Check to see if file is directory or special. */
790 /* This is a directory. */
791 z
= alloc_zone(); /* zone for new directory */
792 add_zone(n
, z
, 2 * sizeof(struct direct
), current_time
);
793 enter_dir(n
, ".", n
);
794 enter_dir(n
, "..", parent
);
798 } else if (*p
== 'b' || *p
== 'c') {
800 maj
= atoi(token
[4]);
801 min
= atoi(token
[5]);
803 if (token
[6]) size
= atoi(token
[6]);
804 size
= block_size
* size
;
805 add_zone(n
, (zone_t
) (makedev(maj
,min
)), size
, current_time
);
806 } else if (*p
== 's') {
807 enter_symlink(n
, token
[4]);
809 /* Regular file. Go read it. */
810 if ((f
= open(token
[4], O_RDONLY
)) < 0) {
811 /* on minix natively, allow EACCES and skip the entry.
812 * while crossbuilding, always fail on error.
816 warn("Can't open %s", token
[4]);
819 err(1, "Can't open %s", token
[4]);
828 /*================================================================
829 * eat_file - copy file to MINIX
830 *===============================================================*/
831 /* Zonesize >= blocksize */
833 eat_file(ino_t inode
, int f
)
843 for (i
= 0, j
= 0; i
< zone_per_block
; i
++, j
+= ct
) {
844 memset(buf
, 0, block_size
);
845 if ((ct
= read(f
, buf
, block_size
)) > 0) {
846 if (i
== 0) z
= alloc_zone();
847 put_block((z
<< zone_shift
) + i
, buf
);
850 timeval
= (dflag
? current_time
: file_time(f
));
851 if (ct
) add_zone(inode
, z
, (size_t) j
, timeval
);
852 } while (ct
== block_size
);
858 dir_try_enter(zone_t z
, ino_t child
, char const *name
)
860 struct direct
*dir_entry
= alloc_block();
866 for (l
= 0; l
< zone_per_block
; l
++, b
++) {
867 get_block(b
, dir_entry
);
869 for (i
= 0; i
< NR_DIR_ENTRIES(block_size
); i
++)
870 if (!dir_entry
[i
].d_ino
)
873 if(i
< NR_DIR_ENTRIES(block_size
)) {
875 dir_entry
[i
].d_ino
= child
;
876 assert(sizeof(dir_entry
[i
].d_name
) == MFS_DIRSIZ
);
877 if (verbose
&& strlen(name
) > MFS_DIRSIZ
)
878 fprintf(stderr
, "File name %s is too long, truncated\n", name
);
879 strncpy(dir_entry
[i
].d_name
, name
, MFS_DIRSIZ
);
880 put_block(b
, dir_entry
);
890 /*================================================================
891 * directory & inode management assist group
892 *===============================================================*/
894 enter_dir(ino_t parent
, char const *name
, ino_t child
)
896 /* Enter child in parent directory */
897 /* Works for dir > 1 block and zone > block */
903 struct inode
*inoblock
= alloc_block();
904 zone_t
*indirblock
= alloc_block();
906 assert(!(block_size
% sizeof(struct direct
)));
908 /* Obtain the inode structure */
909 b
= ((parent
- 1) / inodes_per_block
) + inode_offset
;
910 off
= (parent
- 1) % inodes_per_block
;
911 get_block(b
, inoblock
);
912 ino
= inoblock
+ off
;
914 for (k
= 0; k
< NR_DZONES
; k
++) {
921 if(dir_try_enter(z
, child
, __UNCONST(name
))) {
922 put_block(b
, inoblock
);
929 /* no space in directory using just direct blocks; try indirect */
930 if (ino
->i_zone
[S_INDIRECT_IDX
] == 0)
931 ino
->i_zone
[S_INDIRECT_IDX
] = alloc_zone();
933 indir
= ino
->i_zone
[S_INDIRECT_IDX
] << zone_shift
;
934 --indir
; /* Compensate for ++indir below */
935 for(k
= 0; k
< (indir_per_zone
); k
++) {
936 if (k
% indir_per_block
== 0)
937 get_block(++indir
, indirblock
);
938 z
= indirblock
[k
% indir_per_block
];
940 z
= indirblock
[k
% indir_per_block
] = alloc_zone();
941 put_block(indir
, indirblock
);
943 if(dir_try_enter(z
, child
, __UNCONST(name
))) {
944 put_block(b
, inoblock
);
951 pexit("Directory-inode %u beyond single indirect blocks. Could not enter %s",
952 (unsigned)parent
, name
);
957 add_zone(ino_t n
, zone_t z
, size_t bytes
, time_t mtime
)
959 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */
963 zone_t indir
, dindir
;
964 struct inode
*p
, *inode
;
967 assert(inodes_per_block
*sizeof(*inode
) == block_size
);
968 if(!(inode
= alloc_block()))
969 err(1, "Couldn't allocate block of inodes");
971 b
= ((n
- 1) / inodes_per_block
) + inode_offset
;
972 off
= (n
- 1) % inodes_per_block
;
977 #ifndef MFS_INODE_ONLY_MTIME /* V1 file systems did not have them... */
978 p
->i_atime
= p
->i_ctime
= current_time
;
980 for (i
= 0; i
< NR_DZONES
; i
++)
981 if (p
->i_zone
[i
] == 0) {
988 assert(indir_per_block
*sizeof(*blk
) == block_size
);
989 if(!(blk
= alloc_block()))
990 err(1, "Couldn't allocate indirect block");
992 /* File has grown beyond a small file. */
993 if (p
->i_zone
[S_INDIRECT_IDX
] == 0)
994 p
->i_zone
[S_INDIRECT_IDX
] = alloc_zone();
995 indir
= p
->i_zone
[S_INDIRECT_IDX
] << zone_shift
;
997 --indir
; /* Compensate for ++indir below */
998 for (i
= 0; i
< (indir_per_zone
); i
++) {
999 if (i
% indir_per_block
== 0)
1000 get_block(++indir
, blk
);
1001 if (blk
[i
% indir_per_block
] == 0) {
1003 put_block(indir
, blk
);
1010 /* File has grown beyond single indirect; we need a double indirect */
1011 assert(indir_per_block
*sizeof(*dblk
) == block_size
);
1012 if(!(dblk
= alloc_block()))
1013 err(1, "Couldn't allocate double indirect block");
1015 if (p
->i_zone
[D_INDIRECT_IDX
] == 0)
1016 p
->i_zone
[D_INDIRECT_IDX
] = alloc_zone();
1017 dindir
= p
->i_zone
[D_INDIRECT_IDX
] << zone_shift
;
1018 put_block(b
, inode
);
1019 --dindir
; /* Compensate for ++indir below */
1020 for (j
= 0; j
< (indir_per_zone
); j
++) {
1021 if (j
% indir_per_block
== 0)
1022 get_block(++dindir
, dblk
);
1023 if (dblk
[j
% indir_per_block
] == 0)
1024 dblk
[j
% indir_per_block
] = alloc_zone();
1025 indir
= dblk
[j
% indir_per_block
] << zone_shift
;
1026 --indir
; /* Compensate for ++indir below */
1027 for (i
= 0; i
< (indir_per_zone
); i
++) {
1028 if (i
% indir_per_block
== 0)
1029 get_block(++indir
, blk
);
1030 if (blk
[i
% indir_per_block
] == 0) {
1032 put_block(dindir
, dblk
);
1033 put_block(indir
, blk
);
1042 pexit("File has grown beyond double indirect");
1046 /* Increment the link count to inode n */
1051 static int enter
= 0;
1052 static struct inode
*inodes
= NULL
;
1055 if (enter
++) pexit("internal error: recursive call to incr_link()");
1057 b
= ((n
- 1) / inodes_per_block
) + inode_offset
;
1058 off
= (n
- 1) % inodes_per_block
;
1060 assert(sizeof(*inodes
) * inodes_per_block
== block_size
);
1061 if(!inodes
&& !(inodes
= alloc_block()))
1062 err(1, "couldn't allocate a block of inodes");
1064 get_block(b
, inodes
);
1065 inodes
[off
].i_nlinks
++;
1066 /* Check overflow (particularly on V1)... */
1067 if (inodes
[off
].i_nlinks
<= 0)
1068 pexit("Too many links to a directory");
1069 put_block(b
, inodes
);
1075 /* Increment the file-size in inode n */
1077 incr_size(ino_t n
, size_t count
)
1082 b
= ((n
- 1) / inodes_per_block
) + inode_offset
;
1083 off
= (n
- 1) % inodes_per_block
;
1085 struct inode
*inodes
;
1087 assert(inodes_per_block
* sizeof(*inodes
) == block_size
);
1088 if(!(inodes
= alloc_block()))
1089 err(1, "couldn't allocate a block of inodes");
1091 get_block(b
, inodes
);
1092 /* Check overflow; avoid compiler spurious warnings */
1093 if (inodes
[off
].i_size
+(int)count
< inodes
[off
].i_size
||
1094 inodes
[off
].i_size
> MAX_MAX_SIZE
-(int)count
)
1095 pexit("File has become too big to be handled by MFS");
1096 inodes
[off
].i_size
+= count
;
1097 put_block(b
, inodes
);
1103 /*================================================================
1104 * allocation assist group
1105 *===============================================================*/
1107 alloc_inode(int mode
, int usrid
, int grpid
)
1112 struct inode
*inodes
;
1115 if (num
> nrinodes
) {
1116 pexit("File system does not have enough inodes (only %llu)", nrinodes
);
1118 b
= ((num
- 1) / inodes_per_block
) + inode_offset
;
1119 off
= (num
- 1) % inodes_per_block
;
1121 assert(inodes_per_block
* sizeof(*inodes
) == block_size
);
1122 if(!(inodes
= alloc_block()))
1123 err(1, "couldn't allocate a block of inodes");
1125 get_block(b
, inodes
);
1126 if (inodes
[off
].i_mode
) {
1127 pexit("allocation new inode %llu with non-zero mode - this cannot happen",
1130 inodes
[off
].i_mode
= mode
;
1131 inodes
[off
].i_uid
= usrid
;
1132 inodes
[off
].i_gid
= grpid
;
1133 if (verbose
&& (inodes
[off
].i_uid
!= usrid
|| inodes
[off
].i_gid
!= grpid
))
1134 fprintf(stderr
, "Uid/gid %d.%d do not fit within inode, truncated\n", usrid
, grpid
);
1135 put_block(b
, inodes
);
1139 /* Set the bit in the bit map. */
1140 insert_bit((block_t
) INODE_MAP
, num
);
1145 /* Allocate a new zone */
1149 /* Works for zone > block */
1155 b
= z
<< zone_shift
;
1156 if (b
> nrblocks
- zone_per_block
)
1157 pexit("File system not big enough for all the files");
1158 for (i
= 0; i
< zone_per_block
; i
++)
1159 put_block(b
+ i
, zero
); /* give an empty zone */
1161 insert_bit(zone_map
, z
- zoff
);
1166 /* Insert one bit into the bitmap */
1168 insert_bit(block_t map
, bit_t bit
)
1171 unsigned int bits_per_block
;
1175 buf
= alloc_block();
1177 bits_per_block
= FS_BITS_PER_BLOCK(block_size
);
1178 map_block
= map
+ bit
/ bits_per_block
;
1179 if (map_block
>= inode_offset
)
1180 pexit("insertbit invades inodes area - this cannot happen");
1181 boff
= bit
% bits_per_block
;
1184 assert(boff
< FS_BITS_PER_BLOCK(block_size
));
1185 get_block(map_block
, buf
);
1186 w
= boff
/ FS_BITCHUNK_BITS
;
1187 s
= boff
% FS_BITCHUNK_BITS
;
1189 put_block(map_block
, buf
);
1195 /*================================================================
1196 * proto-file processing assist group
1197 *===============================================================*/
1198 int mode_con(char *p
)
1200 /* Convert string to mode */
1201 int o1
, o2
, o3
, mode
;
1210 mode
= (o1
<< 6) | (o2
<< 3) | o3
;
1211 if (c1
== 'd') mode
|= S_IFDIR
;
1212 if (c1
== 'b') mode
|= S_IFBLK
;
1213 if (c1
== 'c') mode
|= S_IFCHR
;
1214 if (c1
== 's') mode
|= S_IFLNK
;
1215 if (c1
== 'l') mode
|= S_IFLNK
; /* just to be somewhat ls-compatible*/
1216 /* XXX note: some other mkfs programs consider L to create hardlinks */
1217 if (c1
== '-') mode
|= S_IFREG
;
1218 if (c2
== 'u') mode
|= S_ISUID
;
1219 if (c3
== 'g') mode
|= S_ISGID
;
1220 /* XXX There are no way to encode S_ISVTX */
1225 get_line(char line
[LINE_LEN
], char *parse
[MAX_TOKENS
])
1227 /* Read a line and break it up in tokens */
1232 for (k
= 0; k
< MAX_TOKENS
; k
++) parse
[k
] = 0;
1233 memset(line
, 0, LINE_LEN
);
1237 if (++k
> LINE_LEN
) pexit("Line too long");
1239 if (d
== EOF
) pexit("Unexpected end-of-file");
1241 if (*p
== ' ' || *p
== '\t') *p
= 0;
1255 if (c
== '\n') return;
1256 if (c
== 0) continue;
1260 } while (c
!= 0 && c
!= '\n');
1265 /*================================================================
1267 *===============================================================*/
1270 * Check to see if the special file named 'device' is mounted.
1273 check_mtab(const char *device
) /* /dev/hd1 or whatever */
1275 #if defined(__minix)
1278 char dev
[PATH_MAX
], mount_point
[PATH_MAX
],
1279 type
[MNTNAMELEN
], flags
[MNTFLAGLEN
];
1281 r
= stat(device
, &sb
);
1284 if (errno
== ENOENT
)
1285 return; /* Does not exist, and therefore not mounted. */
1286 err(1, "stat %s failed", device
);
1288 if (!S_ISBLK(sb
.st_mode
))
1290 /* Not a block device and therefore not mounted. */
1294 if (load_mtab(__UNCONST("mkfs")) < 0) return;
1296 n
= get_mtab_entry(dev
, mount_point
, type
, flags
);
1298 if (strcmp(device
, dev
) == 0) {
1299 /* Can't mkfs on top of a mounted file system. */
1300 errx(1, "%s is mounted on %s", device
, mount_point
);
1304 (void) device
; /* shut up warnings about unused variable... */
1312 struct stat statbuf
;
1314 if (!fstat(f
, &statbuf
))
1315 return current_time
;
1316 if (statbuf
.st_mtime
<0 || statbuf
.st_mtime
>(uint32_t)(-1))
1317 return current_time
;
1318 return(statbuf
.st_mtime
);
1323 pexit(char const * s
, ...)
1331 warnx("Line %d being processed when error detected.\n", lct
);
1341 if(!(buf
= malloc(block_size
))) {
1342 err(1, "couldn't allocate filesystem buffer");
1344 memset(buf
, 0, block_size
);
1354 struct inode
*inode2
;
1355 unsigned short *usbuf
;
1359 assert(inodes_per_block
* sizeof(*inode2
) == block_size
);
1360 if(!(inode2
= alloc_block()))
1361 err(1, "couldn't allocate a block of inodes");
1363 assert(NR_DIR_ENTRIES(block_size
)*sizeof(*dir
) == block_size
);
1364 if(!(dir
= alloc_block()))
1365 err(1, "couldn't allocate a block of directory entries");
1367 usbuf
= alloc_block();
1368 get_super_block(usbuf
);
1369 printf("\nSuperblock: ");
1370 for (i
= 0; i
< 8; i
++) printf("%06ho ", usbuf
[i
]);
1372 for (i
= 0; i
< 8; i
++) printf("%#04hX ", usbuf
[i
]);
1374 for (i
= 8; i
< 15; i
++) printf("%06ho ", usbuf
[i
]);
1376 for (i
= 8; i
< 15; i
++) printf("%#04hX ", usbuf
[i
]);
1377 get_block((block_t
) INODE_MAP
, usbuf
);
1378 printf("...\nInode map: ");
1379 for (i
= 0; i
< 9; i
++) printf("%06ho ", usbuf
[i
]);
1380 get_block((block_t
) zone_map
, usbuf
);
1381 printf("...\nZone map: ");
1382 for (i
= 0; i
< 9; i
++) printf("%06ho ", usbuf
[i
]);
1389 for (b
= inode_offset
; k
< nrinodes
; b
++) {
1390 get_block(b
, inode2
);
1391 for (i
= 0; i
< inodes_per_block
; i
++) {
1392 k
= inodes_per_block
* (int) (b
- inode_offset
) + i
+ 1;
1394 if (k
> nrinodes
) break;
1396 if (inode2
[i
].i_mode
!= 0) {
1397 printf("Inode %3u: mode=", (unsigned)k
);
1398 printf("%06o", (unsigned)inode2
[i
].i_mode
);
1399 printf(" uid=%2d gid=%2d size=",
1400 (int)inode2
[i
].i_uid
, (int)inode2
[i
].i_gid
);
1401 printf("%6ld", (long)inode2
[i
].i_size
);
1402 printf(" zone[0]=%u\n", (unsigned)inode2
[i
].i_zone
[0]);
1404 if ((inode2
[i
].i_mode
& S_IFMT
) == S_IFDIR
) {
1405 /* This is a directory */
1406 get_block(inode2
[i
].i_zone
[0] << zone_shift
, dir
);
1407 for (j
= 0; j
< NR_DIR_ENTRIES(block_size
); j
++)
1409 printf("\tInode %2u: %s\n",
1410 (unsigned)dir
[j
].d_ino
,
1418 printf("%d inodes used. %u zones (%u blocks) used.\n",
1419 (int)next_inode
-1, next_zone
, next_zone
*zone_per_block
);
1421 printf("%d inodes used. %u zones used.\n", (int)next_inode
-1, next_zone
);
1428 * The first time a block is read, it returns all 0s, unless there has
1429 * been a write. This routine checks to see if a block has been accessed.
1432 read_and_set(block_t n
)
1438 assert(n
< nrblocks
);
1439 if(w
>= umap_array_elements
) {
1440 errx(1, "umap array too small - this can't happen");
1444 r
= (umap_array
[w
] & mask
? 1 : 0);
1445 umap_array
[w
] |= mask
;
1452 fprintf(stderr
, "Usage: %s [-dltv] [-b blocks] [-i inodes]\n"
1453 "\t[-z zone_shift] [-I offset] [-x extra] [-B blocksize] special [proto]\n",
1459 special(char * string
, int insertmode
)
1461 int openmode
= O_RDWR
;
1462 if(!insertmode
) openmode
|= O_TRUNC
;
1463 fd
= open(string
, O_RDWR
| O_CREAT
, 0644);
1464 if (fd
< 0) err(1, "Can't open special file %s", string
);
1465 mkfs_seek(0, SEEK_SET
);
1472 get_block(block_t n
, void *buf
)
1476 /* First access returns a zero block */
1477 if (read_and_set(n
) == 0) {
1478 memcpy(buf
, zero
, block_size
);
1481 mkfs_seek((uint64_t)(n
) * block_size
, SEEK_SET
);
1482 k
= read(fd
, buf
, block_size
);
1483 if (k
!= block_size
)
1484 pexit("get_block couldn't read block #%u", (unsigned)n
);
1487 /* Read the super block. */
1489 get_super_block(void *buf
)
1493 mkfs_seek((off_t
) SUPER_BLOCK_BYTES
, SEEK_SET
);
1494 k
= read(fd
, buf
, SUPER_BLOCK_BYTES
);
1495 if (k
!= SUPER_BLOCK_BYTES
)
1496 err(1, "get_super_block couldn't read super block");
1499 /* Write a block. */
1501 put_block(block_t n
, void *buf
)
1504 (void) read_and_set(n
);
1506 mkfs_seek((uint64_t)(n
) * block_size
, SEEK_SET
);
1507 mkfs_write(buf
, block_size
);
1511 mkfs_write(void * buf
, size_t count
)
1516 /* Perform & check write */
1517 w
= write(fd
, buf
, count
);
1519 err(1, "mkfs_write: write failed");
1521 errx(1, "mkfs_write: short write: %zd != %zu", w
, count
);
1523 /* Check if this has made the FS any bigger; count bytes after offset */
1524 fssize
= mkfs_seek(0, SEEK_CUR
);
1526 assert(fssize
>= fs_offset_bytes
);
1527 fssize
-= fs_offset_bytes
;
1528 fssize
= roundup(fssize
, block_size
);
1529 if(fssize
> written_fs_size
)
1530 written_fs_size
= fssize
;
1535 /* Seek to position in FS we're creating. */
1537 mkfs_seek(uint64_t pos
, int whence
)
1539 if(whence
== SEEK_SET
) pos
+= fs_offset_bytes
;
1541 if((newpos
=lseek(fd
, pos
, whence
)) == (off_t
) -1)
1542 err(1, "mkfs_seek: lseek failed");