1 /* mkfs - make the MINIX filesystem Authors: Tanenbaum et al. */ 2 3 /* Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans */ 4 5 #if HAVE_NBTOOL_CONFIG_H 6 #include "nbtool_config.h" 7 #endif 8 9 #include <sys/cdefs.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 13 #if defined(__minix) 14 #include <minix/minlib.h> 15 #include <minix/partition.h> 16 #include <sys/ioctl.h> 17 #endif 18 19 #include <assert.h> 20 #include <err.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <limits.h> 24 #include <stdarg.h> 25 #include <stdint.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <time.h> 30 #include <unistd.h> 31 32 /* Definition of the file system layout: */ 33 #include "const.h" 34 #include "type.h" 35 #include "mfsdir.h" 36 #include "super.h" 37 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) 42 43 44 #define MAX_TOKENS 10 45 #define LINE_LEN 300 46 /* XXX why do we not use 0 / SU_ID ? */ 47 #define BIN 2 48 #define BINGRP 2 49 50 /* some Minix specific types that do not conflict with Posix */ 51 #ifndef block_t 52 typedef uint32_t block_t; /* block number */ 53 #endif 54 #ifndef zone_t 55 typedef uint32_t zone_t; /* zone number */ 56 #endif 57 #ifndef bit_t 58 typedef uint32_t bit_t; /* bit number in a bit map */ 59 #endif 60 #ifndef bitchunk_t 61 typedef uint32_t bitchunk_t; /* collection of bits in a bitmap */ 62 #endif 63 64 struct fs_size { 65 ino_t inocount; /* amount of inodes */ 66 zone_t zonecount; /* amount of zones */ 67 block_t blockcount; /* amount of blocks */ 68 }; 69 70 extern char *optarg; 71 extern int optind; 72 73 block_t nrblocks; 74 int zone_per_block, zone_shift = 0; 75 zone_t next_zone, zoff, nr_indirzones; 76 int inodes_per_block, indir_per_block, indir_per_zone; 77 unsigned int zone_size; 78 ino_t nrinodes, inode_offset, next_inode; 79 int lct = 0, fd, print = 0; 80 int simple = 0, dflag = 0, verbose = 0; 81 int donttest; /* skip test if it fits on medium */ 82 char *progname; 83 uint64_t fs_offset_bytes, fs_offset_blocks, written_fs_size = 0; 84 85 time_t current_time; 86 char *zero; 87 unsigned char *umap_array; /* bit map tells if block read yet */ 88 size_t umap_array_elements; 89 block_t zone_map; /* where is zone map? (depends on # inodes) */ 90 #ifndef MFS_STATIC_BLOCK_SIZE 91 size_t block_size; 92 #else 93 #define block_size MFS_STATIC_BLOCK_SIZE 94 #endif 95 96 FILE *proto; 97 98 int main(int argc, char **argv); 99 void detect_fs_size(struct fs_size * fssize); 100 void sizeup_dir(struct fs_size * fssize); 101 block_t sizeup(char *device); 102 static int bitmapsize(bit_t nr_bits, size_t blk_size); 103 void super(zone_t zones, ino_t inodes); 104 void rootdir(ino_t inode); 105 void enter_symlink(ino_t inode, char *link); 106 int dir_try_enter(zone_t z, ino_t child, char const *name); 107 void eat_dir(ino_t parent); 108 void eat_file(ino_t inode, int f); 109 void enter_dir(ino_t parent, char const *name, ino_t child); 110 void add_zone(ino_t n, zone_t z, size_t bytes, time_t cur_time); 111 void incr_link(ino_t n); 112 void incr_size(ino_t n, size_t count); 113 static ino_t alloc_inode(int mode, int usrid, int grpid); 114 static zone_t alloc_zone(void); 115 void insert_bit(block_t block, bit_t bit); 116 int mode_con(char *p); 117 void get_line(char line[LINE_LEN], char *parse[MAX_TOKENS]); 118 void check_mtab(const char *devname); 119 time_t file_time(int f); 120 __dead void pexit(char const *s, ...) __printflike(1,2); 121 void *alloc_block(void); 122 void print_fs(void); 123 int read_and_set(block_t n); 124 void special(char *string, int insertmode); 125 __dead void usage(void); 126 void get_block(block_t n, void *buf); 127 void get_super_block(void *buf); 128 void put_block(block_t n, void *buf); 129 static uint64_t mkfs_seek(uint64_t pos, int whence); 130 static ssize_t mkfs_write(void * buf, size_t count); 131 132 /*================================================================ 133 * mkfs - make filesystem 134 *===============================================================*/ 135 int 136 main(int argc, char *argv[]) 137 { 138 int nread, mode, usrid, grpid, ch, extra_space_percent, Tflag = 0; 139 block_t blocks, maxblocks, bblocks; 140 ino_t inodes, root_inum; 141 char *token[MAX_TOKENS], line[LINE_LEN], *sfx; 142 struct fs_size fssize; 143 int insertmode = 0; 144 145 progname = argv[0]; 146 147 /* Process switches. */ 148 blocks = 0; 149 inodes = 0; 150 bblocks = 0; 151 #ifndef MFS_STATIC_BLOCK_SIZE 152 block_size = 0; 153 #endif 154 zone_shift = 0; 155 extra_space_percent = 0; 156 while ((ch = getopt(argc, argv, "B:b:di:ltvx:z:I:T:")) != EOF) 157 switch (ch) { 158 #ifndef MFS_STATIC_BLOCK_SIZE 159 case 'B': 160 block_size = strtoul(optarg, &sfx, 0); 161 switch(*sfx) { 162 case 'b': case 'B': /* bytes; NetBSD-compatible */ 163 case '\0': break; 164 case 'K': 165 case 'k': block_size*=1024; break; 166 case 's': block_size*=SECTOR_SIZE; break; 167 default: usage(); 168 } 169 break; 170 #else 171 case 'B': 172 if (block_size != strtoul(optarg, (char **) NULL, 0)) 173 errx(4, "block size must be exactly %d bytes", 174 MFS_STATIC_BLOCK_SIZE); 175 break; 176 (void)sfx; /* shut up warnings about unused variable...*/ 177 #endif 178 case 'I': 179 fs_offset_bytes = strtoul(optarg, (char **) NULL, 0); 180 insertmode = 1; 181 break; 182 case 'b': 183 blocks = bblocks = strtoul(optarg, (char **) NULL, 0); 184 break; 185 case 'T': 186 Tflag = 1; 187 current_time = strtoul(optarg, (char **) NULL, 0); 188 break; 189 case 'd': 190 dflag = 1; 191 break; 192 case 'i': 193 inodes = strtoul(optarg, (char **) NULL, 0); 194 break; 195 case 'l': print = 1; break; 196 case 't': donttest = 1; break; 197 case 'v': ++verbose; break; 198 case 'x': extra_space_percent = atoi(optarg); break; 199 case 'z': zone_shift = atoi(optarg); break; 200 default: usage(); 201 } 202 203 if (argc == optind) usage(); 204 205 /* Get the current time, set it to the mod time of the binary of 206 * mkfs itself when the -d flag is used. The 'current' time is put into 207 * the i_mtimes of all the files. This -d feature is useful when 208 * producing a set of file systems, and one wants all the times to be 209 * identical. First you set the time of the mkfs binary to what you 210 * want, then go. 211 */ 212 if(Tflag) { 213 if(dflag) 214 errx(1, "-T and -d both specify a time and so are mutually exclusive"); 215 } else if(dflag) { 216 struct stat statbuf; 217 if (stat(progname, &statbuf)) { 218 err(1, "stat of itself"); 219 } 220 current_time = statbuf.st_mtime; 221 } else { 222 current_time = time((time_t *) 0); /* time mkfs is being run */ 223 } 224 225 /* Percentage of extra size must be nonnegative. 226 * It can legitimately be bigger than 100 but has to make some sort of sense. 227 */ 228 if(extra_space_percent < 0 || extra_space_percent > 2000) usage(); 229 230 #ifdef DEFAULT_BLOCK_SIZE 231 if(!block_size) block_size = DEFAULT_BLOCK_SIZE; 232 #endif 233 if (block_size % SECTOR_SIZE) 234 errx(4, "block size must be multiple of sector (%d bytes)", SECTOR_SIZE); 235 #ifdef MIN_BLOCK_SIZE 236 if (block_size < MIN_BLOCK_SIZE) 237 errx(4, "block size must be at least %d bytes", MIN_BLOCK_SIZE); 238 #endif 239 #ifdef MAX_BLOCK_SIZE 240 if (block_size > MAX_BLOCK_SIZE) 241 errx(4, "block size must be at most %d bytes", MAX_BLOCK_SIZE); 242 #endif 243 if(block_size%INODE_SIZE) 244 errx(4, "block size must be a multiple of inode size (%d bytes)", INODE_SIZE); 245 246 if(zone_shift < 0 || zone_shift > 14) 247 errx(4, "zone_shift must be a small non-negative integer"); 248 zone_per_block = 1 << zone_shift; /* nr of blocks per zone */ 249 250 inodes_per_block = INODES_PER_BLOCK(block_size); 251 indir_per_block = INDIRECTS(block_size); 252 indir_per_zone = INDIRECTS(block_size) << zone_shift; 253 /* number of file zones we can address directly and with a single indirect*/ 254 nr_indirzones = NR_DZONES + indir_per_zone; 255 zone_size = block_size << zone_shift; 256 /* Checks for an overflow: only with very big block size */ 257 if (zone_size <= 0) 258 errx(4, "Zones are too big for this program; smaller -B or -z, please!"); 259 260 /* now that the block size is known, do buffer allocations where 261 * possible. 262 */ 263 zero = alloc_block(); 264 265 fs_offset_blocks = roundup(fs_offset_bytes, block_size) / block_size; 266 267 /* Determine the size of the device if not specified as -b or proto. */ 268 maxblocks = sizeup(argv[optind]); 269 if (bblocks != 0 && bblocks + fs_offset_blocks > maxblocks && !insertmode) { 270 errx(4, "Given size -b %d exeeds device capacity(%d)\n", bblocks, maxblocks); 271 } 272 273 if (argc - optind == 1 && bblocks == 0) { 274 blocks = maxblocks; 275 /* blocks == 0 is checked later, but leads to a funny way of 276 * reporting a 0-sized device (displays usage). 277 */ 278 if(blocks < 1) { 279 errx(1, "zero size device."); 280 } 281 } 282 283 /* The remaining args must be 'special proto', or just 'special' if the 284 * no. of blocks has already been specified. 285 */ 286 if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage(); 287 288 if (maxblocks && blocks > maxblocks && !insertmode) { 289 errx(1, "%s: number of blocks too large for device.", argv[optind]); 290 } 291 292 /* Check special. */ 293 check_mtab(argv[optind]); 294 295 /* Check and start processing proto. */ 296 optarg = argv[++optind]; 297 if (optind < argc && (proto = fopen(optarg, "r")) != NULL) { 298 /* Prototype file is readable. */ 299 lct = 1; 300 get_line(line, token); /* skip boot block info */ 301 302 /* Read the line with the block and inode counts. */ 303 get_line(line, token); 304 if (bblocks == 0){ 305 blocks = strtol(token[0], (char **) NULL, 10); 306 } else { 307 if(bblocks < strtol(token[0], (char **) NULL, 10)) { 308 errx(1, "%s: number of blocks given as parameter(%d)" 309 " is too small for given proto file(%ld).", 310 argv[optind], bblocks, 311 strtol(token[0], (char **) NULL, 10)); 312 }; 313 } 314 inodes = strtol(token[1], (char **) NULL, 10); 315 316 /* Process mode line for root directory. */ 317 get_line(line, token); 318 mode = mode_con(token[0]); 319 usrid = atoi(token[1]); 320 grpid = atoi(token[2]); 321 322 if(blocks <= 0 && inodes <= 0){ 323 detect_fs_size(&fssize); 324 blocks = fssize.blockcount; 325 inodes = fssize.inocount; 326 blocks += blocks*extra_space_percent/100; 327 inodes += inodes*extra_space_percent/100; 328 /* XXX is it OK to write on stdout? Use warn() instead? Also consider using verbose */ 329 fprintf(stderr, "dynamically sized filesystem: %u blocks, %u inodes\n", 330 (unsigned int) blocks, (unsigned int) inodes); 331 } 332 } else { 333 lct = 0; 334 if (optind < argc) { 335 /* Maybe the prototype file is just a size. Check. */ 336 blocks = strtoul(optarg, (char **) NULL, 0); 337 if (blocks == 0) errx(2, "Can't open prototype file"); 338 } 339 340 /* Make simple file system of the given size, using defaults. */ 341 mode = 040777; 342 usrid = BIN; 343 grpid = BINGRP; 344 simple = 1; 345 } 346 347 if (inodes == 0) { 348 long long kb = ((unsigned long long)blocks*block_size) / 1024; 349 350 inodes = kb / 2; 351 if (kb >= 100000) inodes = kb / 4; 352 if (kb >= 1000000) inodes = kb / 6; 353 if (kb >= 10000000) inodes = kb / 8; 354 if (kb >= 100000000) inodes = kb / 10; 355 if (kb >= 1000000000) inodes = kb / 12; 356 /* XXX check overflow: with very large number of blocks, this results in insanely large number of inodes */ 357 /* XXX check underflow (if/when ino_t is signed), else the message below will look strange */ 358 359 /* round up to fill inode block */ 360 inodes += inodes_per_block - 1; 361 inodes = inodes / inodes_per_block * inodes_per_block; 362 } 363 364 if (blocks < 5) errx(1, "Block count too small"); 365 if (inodes < 1) errx(1, "Inode count too small"); 366 367 nrblocks = blocks; 368 nrinodes = inodes; 369 370 umap_array_elements = 1 + blocks/8; 371 if(!(umap_array = malloc(umap_array_elements))) 372 err(1, "can't allocate block bitmap (%u bytes).", 373 (unsigned)umap_array_elements); 374 375 /* Open special. */ 376 special(argv[--optind], insertmode); 377 378 if (!donttest) { 379 uint16_t *testb; 380 ssize_t w; 381 382 testb = alloc_block(); 383 384 /* Try writing the last block of partition or diskette. */ 385 mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET); 386 testb[0] = 0x3245; 387 testb[1] = 0x11FF; 388 testb[block_size/2-1] = 0x1F2F; 389 w=mkfs_write(testb, block_size); 390 sync(); /* flush write, so if error next read fails */ 391 mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET); 392 testb[0] = 0; 393 testb[1] = 0; 394 testb[block_size/2-1] = 0; 395 nread = read(fd, testb, block_size); 396 if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF || 397 testb[block_size/2-1] != 0x1F2F) { 398 warn("nread = %d\n", nread); 399 warnx("testb = 0x%x 0x%x 0x%x\n", 400 testb[0], testb[1], testb[block_size-1]); 401 errx(1, "File system is too big for minor device (read)"); 402 } 403 mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET); 404 testb[0] = 0; 405 testb[1] = 0; 406 testb[block_size/2-1] = 0; 407 mkfs_write(testb, block_size); 408 mkfs_seek(0L, SEEK_SET); 409 free(testb); 410 } 411 412 /* Make the file-system */ 413 414 put_block(BOOT_BLOCK, zero); /* Write a null boot block. */ 415 put_block(BOOT_BLOCK+1, zero); /* Write another null block. */ 416 417 super(nrblocks >> zone_shift, inodes); 418 419 root_inum = alloc_inode(mode, usrid, grpid); 420 rootdir(root_inum); 421 if (simple == 0) eat_dir(root_inum); 422 423 if (print) print_fs(); 424 else if (verbose > 1) { 425 if (zone_shift) 426 fprintf(stderr, "%d inodes used. %u zones (%u blocks) used.\n", 427 (int)next_inode-1, next_zone, next_zone*zone_per_block); 428 else 429 fprintf(stderr, "%d inodes used. %u zones used.\n", 430 (int)next_inode-1, next_zone); 431 } 432 433 if(insertmode) printf("%"PRIu64"\n", written_fs_size); 434 435 return(0); 436 437 /* NOTREACHED */ 438 } /* end main */ 439 440 /*================================================================ 441 * detect_fs_size - determine image size dynamically 442 *===============================================================*/ 443 void 444 detect_fs_size(struct fs_size * fssize) 445 { 446 int prev_lct = lct; 447 off_t point = ftell(proto); 448 block_t initb; 449 zone_t initz; 450 451 fssize->inocount = 1; /* root directory node */ 452 fssize->zonecount = 0; 453 fssize->blockcount = 0; 454 455 sizeup_dir(fssize); 456 457 initb = bitmapsize(1 + fssize->inocount, block_size); 458 initb += bitmapsize(fssize->zonecount, block_size); 459 initb += START_BLOCK; 460 initb += (fssize->inocount + inodes_per_block - 1) / inodes_per_block; 461 initz = (initb + zone_per_block - 1) >> zone_shift; 462 463 fssize->blockcount = initb+ fssize->zonecount; 464 lct = prev_lct; 465 fseek(proto, point, SEEK_SET); 466 } 467 468 void 469 sizeup_dir(struct fs_size * fssize) 470 { 471 char *token[MAX_TOKENS], *p; 472 char line[LINE_LEN]; 473 FILE *f; 474 off_t size; 475 int dir_entries = 2; 476 zone_t dir_zones = 0, fzones, indirects; 477 478 while (1) { 479 get_line(line, token); 480 p = token[0]; 481 if (*p == '$') { 482 dir_zones = (dir_entries / (NR_DIR_ENTRIES(block_size) * zone_per_block)); 483 if(dir_entries % (NR_DIR_ENTRIES(block_size) * zone_per_block)) 484 dir_zones++; 485 if(dir_zones > NR_DZONES) 486 dir_zones++; /* Max single indir */ 487 fssize->zonecount += dir_zones; 488 return; 489 } 490 491 p = token[1]; 492 fssize->inocount++; 493 dir_entries++; 494 495 if (*p == 'd') { 496 sizeup_dir(fssize); 497 } else if (*p == 'b' || *p == 'c') { 498 499 } else if (*p == 's') { 500 fssize->zonecount++; /* Symlink contents is always stored a block */ 501 } else { 502 if ((f = fopen(token[4], "rb")) == NULL) { 503 /* on minix natively, allow EACCES and skip the entry. 504 * while crossbuilding, always fail on error. 505 */ 506 #ifdef __minix 507 if(errno == EACCES) 508 warn("dynamic sizing: can't open %s", token[4]); 509 else 510 #endif 511 err(1, "dynamic sizing: can't open %s", token[4]); 512 } else if (fseek(f, 0, SEEK_END) < 0) { 513 pexit("dynamic size detection failed: seek to end of %s", 514 token[4]); 515 } else if ( (size = ftell(f)) == (off_t)(-1)) { 516 pexit("dynamic size detection failed: can't tell size of %s", 517 token[4]); 518 } else { 519 fclose(f); 520 fzones = roundup(size, zone_size) / zone_size; 521 indirects = 0; 522 /* XXX overflow? fzones is u32, size is potentially 64-bit */ 523 if (fzones > NR_DZONES) 524 indirects++; /* single indirect needed */ 525 if (fzones > nr_indirzones) { 526 /* Each further group of 'indir_per_zone' 527 * needs one supplementary indirect zone: 528 */ 529 indirects += roundup(fzones - nr_indirzones, 530 indir_per_zone) / indir_per_zone; 531 indirects++; /* + double indirect needed!*/ 532 } 533 fssize->zonecount += fzones + indirects; 534 } 535 } 536 } 537 } 538 539 /*================================================================ 540 * sizeup - determine device size 541 *===============================================================*/ 542 block_t 543 sizeup(char * device) 544 { 545 block_t d; 546 #if defined(__minix) 547 uint64_t bytes, resize; 548 uint32_t rem; 549 #else 550 off_t size; 551 #endif 552 553 554 if ((fd = open(device, O_RDONLY)) == -1) { 555 if (errno != ENOENT) 556 perror("sizeup open"); 557 return 0; 558 } 559 560 #if defined(__minix) 561 if(minix_sizeup(device, &bytes) < 0) { 562 perror("sizeup"); 563 return 0; 564 } 565 566 d = (uint32_t)(bytes / block_size); 567 rem = (uint32_t)(bytes % block_size); 568 569 resize = ((uint64_t)d * block_size) + rem; 570 if(resize != bytes) { 571 /* Assume block_t is unsigned */ 572 d = (block_t)(-1ul); 573 fprintf(stderr, "%s: truncating FS at %lu blocks\n", 574 progname, (unsigned long)d); 575 } 576 #else 577 size = mkfs_seek(0, SEEK_END); 578 /* Assume block_t is unsigned */ 579 if (size / block_size > (block_t)(-1ul)) { 580 d = (block_t)(-1ul); 581 fprintf(stderr, "%s: truncating FS at %lu blocks\n", 582 progname, (unsigned long)d); 583 } else 584 d = size / block_size; 585 #endif 586 587 return d; 588 } 589 590 /* 591 * copied from fslib 592 */ 593 static int 594 bitmapsize(bit_t nr_bits, size_t blk_size) 595 { 596 block_t nr_blocks; 597 598 nr_blocks = nr_bits / FS_BITS_PER_BLOCK(blk_size); 599 if (nr_blocks * FS_BITS_PER_BLOCK(blk_size) < nr_bits) 600 ++nr_blocks; 601 return(nr_blocks); 602 } 603 604 /*================================================================ 605 * super - construct a superblock 606 *===============================================================*/ 607 608 void 609 super(zone_t zones, ino_t inodes) 610 { 611 block_t inodeblks, initblks, i; 612 unsigned long nb; 613 long long ind_per_zone, zo; 614 void *buf; 615 struct super_block *sup; 616 617 sup = buf = alloc_block(); 618 619 #ifdef MFSFLAG_CLEAN 620 /* The assumption is that mkfs will create a clean FS. */ 621 sup->s_flags = MFSFLAG_CLEAN; 622 #endif 623 624 sup->s_ninodes = inodes; 625 /* Check for overflow; cannot happen on V3 file systems */ 626 if(inodes != sup->s_ninodes) 627 errx(1, "Too much inodes for that version of Minix FS."); 628 sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */ 629 sup->s_zones = zones; 630 /* Check for overflow; can only happen on V1 file systems */ 631 if(zones != sup->s_zones) 632 errx(1, "Too much zones (blocks) for that version of Minix FS."); 633 634 #ifndef MFS_STATIC_BLOCK_SIZE 635 #define BIGGERBLOCKS "Please try a larger block size for an FS of this size." 636 #else 637 #define BIGGERBLOCKS "Please use MinixFS V3 for an FS of this size." 638 #endif 639 sup->s_imap_blocks = nb = bitmapsize(1 + inodes, block_size); 640 /* Checks for an overflow: nb is uint32_t while s_imap_blocks is of type 641 * int16_t */ 642 if(sup->s_imap_blocks != nb) { 643 errx(1, "too many inode bitmap blocks.\n" BIGGERBLOCKS); 644 } 645 sup->s_zmap_blocks = nb = bitmapsize(zones, block_size); 646 /* Idem here check for overflow */ 647 if(nb != sup->s_zmap_blocks) { 648 errx(1, "too many block bitmap blocks.\n" BIGGERBLOCKS); 649 } 650 inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks; 651 inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block; 652 initblks = inode_offset + inodeblks; 653 sup->s_firstdatazone_old = nb = 654 (initblks + (1 << zone_shift) - 1) >> zone_shift; 655 if(nb >= zones) errx(1, "bit maps too large"); 656 if(nb != sup->s_firstdatazone_old) { 657 /* The field is too small to store the value. Fortunately, the value 658 * can be computed from other fields. We set the on-disk field to zero 659 * to indicate that it must not be used. Eventually, we can always set 660 * the on-disk field to zero, and stop using it. 661 */ 662 sup->s_firstdatazone_old = 0; 663 } 664 sup->s_firstdatazone = nb; 665 zoff = sup->s_firstdatazone - 1; 666 sup->s_log_zone_size = zone_shift; 667 sup->s_magic = SUPER_MAGIC; 668 #ifdef MFS_SUPER_BLOCK_SIZE 669 sup->s_block_size = block_size; 670 /* Check for overflow */ 671 if(block_size != sup->MFS_SUPER_BLOCK_SIZE) 672 errx(1, "block_size too large."); 673 sup->s_disk_version = 0; 674 #endif 675 676 ind_per_zone = (long long) indir_per_zone; 677 zo = NR_DZONES + ind_per_zone + ind_per_zone*ind_per_zone; 678 #ifndef MAX_MAX_SIZE 679 #define MAX_MAX_SIZE (INT32_MAX) 680 #endif 681 if(MAX_MAX_SIZE/block_size < zo) { 682 sup->s_max_size = MAX_MAX_SIZE; 683 } 684 else { 685 sup->s_max_size = zo * block_size; 686 } 687 688 if (verbose>1) { 689 fprintf(stderr, "Super block values:\n" 690 "\tnumber of inodes\t%12d\n" 691 "\tnumber of zones \t%12d\n" 692 "\tinode bit map blocks\t%12d\n" 693 "\tzone bit map blocks\t%12d\n" 694 "\tfirst data zone \t%12d\n" 695 "\tblocks per zone shift\t%12d\n" 696 "\tmaximum file size\t%12d\n" 697 "\tmagic number\t\t%#12X\n", 698 sup->s_ninodes, sup->s_zones, 699 sup->s_imap_blocks, sup->s_zmap_blocks, sup->s_firstdatazone, 700 sup->s_log_zone_size, sup->s_max_size, sup->s_magic); 701 #ifdef MFS_SUPER_BLOCK_SIZE 702 fprintf(stderr, "\tblock size\t\t%12d\n", sup->s_block_size); 703 #endif 704 } 705 706 mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET); 707 mkfs_write(buf, SUPER_BLOCK_BYTES); 708 709 /* Clear maps and inodes. */ 710 for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero); 711 712 next_zone = sup->s_firstdatazone; 713 next_inode = 1; 714 715 zone_map = INODE_MAP + sup->s_imap_blocks; 716 717 insert_bit(zone_map, 0); /* bit zero must always be allocated */ 718 insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but 719 * must be allocated */ 720 721 free(buf); 722 } 723 724 725 /*================================================================ 726 * rootdir - install the root directory 727 *===============================================================*/ 728 void 729 rootdir(ino_t inode) 730 { 731 zone_t z; 732 733 z = alloc_zone(); 734 add_zone(inode, z, 2 * sizeof(struct direct), current_time); 735 enter_dir(inode, ".", inode); 736 enter_dir(inode, "..", inode); 737 incr_link(inode); 738 incr_link(inode); 739 } 740 741 void 742 enter_symlink(ino_t inode, char *lnk) 743 { 744 zone_t z; 745 size_t len; 746 char *buf; 747 748 buf = alloc_block(); 749 z = alloc_zone(); 750 len = strlen(lnk); 751 if (len >= block_size) 752 pexit("symlink too long, max length is %u", (unsigned)block_size - 1); 753 strcpy(buf, lnk); 754 put_block((z << zone_shift), buf); 755 756 add_zone(inode, z, len, current_time); 757 758 free(buf); 759 } 760 761 762 /*================================================================ 763 * eat_dir - recursively install directory 764 *===============================================================*/ 765 void 766 eat_dir(ino_t parent) 767 { 768 /* Read prototype lines and set up directory. Recurse if need be. */ 769 char *token[MAX_TOKENS], *p; 770 char line[LINE_LEN]; 771 int mode, usrid, grpid, maj, min, f; 772 ino_t n; 773 zone_t z; 774 size_t size; 775 776 while (1) { 777 get_line(line, token); 778 p = token[0]; 779 if (*p == '$') return; 780 p = token[1]; 781 mode = mode_con(p); 782 usrid = atoi(token[2]); 783 grpid = atoi(token[3]); 784 n = alloc_inode(mode, usrid, grpid); 785 786 /* Enter name in directory and update directory's size. */ 787 enter_dir(parent, token[0], n); 788 incr_size(parent, sizeof(struct direct)); 789 790 /* Check to see if file is directory or special. */ 791 incr_link(n); 792 if (*p == 'd') { 793 /* This is a directory. */ 794 z = alloc_zone(); /* zone for new directory */ 795 add_zone(n, z, 2 * sizeof(struct direct), current_time); 796 enter_dir(n, ".", n); 797 enter_dir(n, "..", parent); 798 incr_link(parent); 799 incr_link(n); 800 eat_dir(n); 801 } else if (*p == 'b' || *p == 'c') { 802 /* Special file. */ 803 maj = atoi(token[4]); 804 min = atoi(token[5]); 805 size = 0; 806 if (token[6]) size = atoi(token[6]); 807 size = block_size * size; 808 add_zone(n, (zone_t) (makedev(maj,min)), size, current_time); 809 } else if (*p == 's') { 810 enter_symlink(n, token[4]); 811 } else { 812 /* Regular file. Go read it. */ 813 if ((f = open(token[4], O_RDONLY)) < 0) { 814 /* on minix natively, allow EACCES and skip the entry. 815 * while crossbuilding, always fail on error. 816 */ 817 #ifdef __minix 818 if(errno == EACCES) 819 warn("Can't open %s", token[4]); 820 else 821 #endif 822 err(1, "Can't open %s", token[4]); 823 } else { 824 eat_file(n, f); 825 } 826 } 827 } 828 829 } 830 831 /*================================================================ 832 * eat_file - copy file to MINIX 833 *===============================================================*/ 834 /* Zonesize >= blocksize */ 835 void 836 eat_file(ino_t inode, int f) 837 { 838 int ct = 0, i, j; 839 zone_t z = 0; 840 char *buf; 841 time_t timeval; 842 843 buf = alloc_block(); 844 845 do { 846 for (i = 0, j = 0; i < zone_per_block; i++, j += ct) { 847 memset(buf, 0, block_size); 848 if ((ct = read(f, buf, block_size)) > 0) { 849 if (i == 0) z = alloc_zone(); 850 put_block((z << zone_shift) + i, buf); 851 } 852 } 853 timeval = (dflag ? current_time : file_time(f)); 854 if (ct) add_zone(inode, z, (size_t) j, timeval); 855 } while (ct == block_size); 856 close(f); 857 free(buf); 858 } 859 860 int 861 dir_try_enter(zone_t z, ino_t child, char const *name) 862 { 863 struct direct *dir_entry = alloc_block(); 864 int r = 0; 865 block_t b; 866 int i, l; 867 868 b = z << zone_shift; 869 for (l = 0; l < zone_per_block; l++, b++) { 870 get_block(b, dir_entry); 871 872 for (i = 0; i < NR_DIR_ENTRIES(block_size); i++) 873 if (!dir_entry[i].d_ino) 874 break; 875 876 if(i < NR_DIR_ENTRIES(block_size)) { 877 r = 1; 878 dir_entry[i].d_ino = child; 879 assert(sizeof(dir_entry[i].d_name) == MFS_DIRSIZ); 880 if (verbose && strlen(name) > MFS_DIRSIZ) 881 fprintf(stderr, "File name %s is too long, truncated\n", name); 882 strncpy(dir_entry[i].d_name, name, MFS_DIRSIZ); 883 put_block(b, dir_entry); 884 break; 885 } 886 } 887 888 free(dir_entry); 889 890 return r; 891 } 892 893 /*================================================================ 894 * directory & inode management assist group 895 *===============================================================*/ 896 void 897 enter_dir(ino_t parent, char const *name, ino_t child) 898 { 899 /* Enter child in parent directory */ 900 /* Works for dir > 1 block and zone > block */ 901 unsigned int k; 902 block_t b, indir; 903 zone_t z; 904 int off; 905 struct inode *ino; 906 struct inode *inoblock = alloc_block(); 907 zone_t *indirblock = alloc_block(); 908 909 assert(!(block_size % sizeof(struct direct))); 910 911 /* Obtain the inode structure */ 912 b = ((parent - 1) / inodes_per_block) + inode_offset; 913 off = (parent - 1) % inodes_per_block; 914 get_block(b, inoblock); 915 ino = inoblock + off; 916 917 for (k = 0; k < NR_DZONES; k++) { 918 z = ino->i_zone[k]; 919 if (z == 0) { 920 z = alloc_zone(); 921 ino->i_zone[k] = z; 922 } 923 924 if(dir_try_enter(z, child, __UNCONST(name))) { 925 put_block(b, inoblock); 926 free(inoblock); 927 free(indirblock); 928 return; 929 } 930 } 931 932 /* no space in directory using just direct blocks; try indirect */ 933 if (ino->i_zone[S_INDIRECT_IDX] == 0) 934 ino->i_zone[S_INDIRECT_IDX] = alloc_zone(); 935 936 indir = ino->i_zone[S_INDIRECT_IDX] << zone_shift; 937 --indir; /* Compensate for ++indir below */ 938 for(k = 0; k < (indir_per_zone); k++) { 939 if (k % indir_per_block == 0) 940 get_block(++indir, indirblock); 941 z = indirblock[k % indir_per_block]; 942 if(!z) { 943 z = indirblock[k % indir_per_block] = alloc_zone(); 944 put_block(indir, indirblock); 945 } 946 if(dir_try_enter(z, child, __UNCONST(name))) { 947 put_block(b, inoblock); 948 free(inoblock); 949 free(indirblock); 950 return; 951 } 952 } 953 954 pexit("Directory-inode %u beyond single indirect blocks. Could not enter %s", 955 (unsigned)parent, name); 956 } 957 958 959 void 960 add_zone(ino_t n, zone_t z, size_t bytes, time_t mtime) 961 { 962 /* Add zone z to inode n. The file has grown by 'bytes' bytes. */ 963 964 int off, i, j; 965 block_t b; 966 zone_t indir, dindir; 967 struct inode *p, *inode; 968 zone_t *blk, *dblk; 969 970 assert(inodes_per_block*sizeof(*inode) == block_size); 971 if(!(inode = alloc_block())) 972 err(1, "Couldn't allocate block of inodes"); 973 974 b = ((n - 1) / inodes_per_block) + inode_offset; 975 off = (n - 1) % inodes_per_block; 976 get_block(b, inode); 977 p = &inode[off]; 978 p->i_size += bytes; 979 p->i_mtime = mtime; 980 #ifndef MFS_INODE_ONLY_MTIME /* V1 file systems did not have them... */ 981 p->i_atime = p->i_ctime = current_time; 982 #endif 983 for (i = 0; i < NR_DZONES; i++) 984 if (p->i_zone[i] == 0) { 985 p->i_zone[i] = z; 986 put_block(b, inode); 987 free(inode); 988 return; 989 } 990 991 assert(indir_per_block*sizeof(*blk) == block_size); 992 if(!(blk = alloc_block())) 993 err(1, "Couldn't allocate indirect block"); 994 995 /* File has grown beyond a small file. */ 996 if (p->i_zone[S_INDIRECT_IDX] == 0) 997 p->i_zone[S_INDIRECT_IDX] = alloc_zone(); 998 indir = p->i_zone[S_INDIRECT_IDX] << zone_shift; 999 put_block(b, inode); 1000 --indir; /* Compensate for ++indir below */ 1001 for (i = 0; i < (indir_per_zone); i++) { 1002 if (i % indir_per_block == 0) 1003 get_block(++indir, blk); 1004 if (blk[i % indir_per_block] == 0) { 1005 blk[i] = z; 1006 put_block(indir, blk); 1007 free(blk); 1008 free(inode); 1009 return; 1010 } 1011 } 1012 1013 /* File has grown beyond single indirect; we need a double indirect */ 1014 assert(indir_per_block*sizeof(*dblk) == block_size); 1015 if(!(dblk = alloc_block())) 1016 err(1, "Couldn't allocate double indirect block"); 1017 1018 if (p->i_zone[D_INDIRECT_IDX] == 0) 1019 p->i_zone[D_INDIRECT_IDX] = alloc_zone(); 1020 dindir = p->i_zone[D_INDIRECT_IDX] << zone_shift; 1021 put_block(b, inode); 1022 --dindir; /* Compensate for ++indir below */ 1023 for (j = 0; j < (indir_per_zone); j++) { 1024 if (j % indir_per_block == 0) 1025 get_block(++dindir, dblk); 1026 if (dblk[j % indir_per_block] == 0) 1027 dblk[j % indir_per_block] = alloc_zone(); 1028 indir = dblk[j % indir_per_block] << zone_shift; 1029 --indir; /* Compensate for ++indir below */ 1030 for (i = 0; i < (indir_per_zone); i++) { 1031 if (i % indir_per_block == 0) 1032 get_block(++indir, blk); 1033 if (blk[i % indir_per_block] == 0) { 1034 blk[i] = z; 1035 put_block(dindir, dblk); 1036 put_block(indir, blk); 1037 free(dblk); 1038 free(blk); 1039 free(inode); 1040 return; 1041 } 1042 } 1043 } 1044 1045 pexit("File has grown beyond double indirect"); 1046 } 1047 1048 1049 /* Increment the link count to inode n */ 1050 void 1051 incr_link(ino_t n) 1052 { 1053 int off; 1054 static int enter = 0; 1055 static struct inode *inodes = NULL; 1056 block_t b; 1057 1058 if (enter++) pexit("internal error: recursive call to incr_link()"); 1059 1060 b = ((n - 1) / inodes_per_block) + inode_offset; 1061 off = (n - 1) % inodes_per_block; 1062 { 1063 assert(sizeof(*inodes) * inodes_per_block == block_size); 1064 if(!inodes && !(inodes = alloc_block())) 1065 err(1, "couldn't allocate a block of inodes"); 1066 1067 get_block(b, inodes); 1068 inodes[off].i_nlinks++; 1069 /* Check overflow (particularly on V1)... */ 1070 if (inodes[off].i_nlinks <= 0) 1071 pexit("Too many links to a directory"); 1072 put_block(b, inodes); 1073 } 1074 enter = 0; 1075 } 1076 1077 1078 /* Increment the file-size in inode n */ 1079 void 1080 incr_size(ino_t n, size_t count) 1081 { 1082 block_t b; 1083 int off; 1084 1085 b = ((n - 1) / inodes_per_block) + inode_offset; 1086 off = (n - 1) % inodes_per_block; 1087 { 1088 struct inode *inodes; 1089 1090 assert(inodes_per_block * sizeof(*inodes) == block_size); 1091 if(!(inodes = alloc_block())) 1092 err(1, "couldn't allocate a block of inodes"); 1093 1094 get_block(b, inodes); 1095 /* Check overflow; avoid compiler spurious warnings */ 1096 if (inodes[off].i_size+(int)count < inodes[off].i_size || 1097 inodes[off].i_size > MAX_MAX_SIZE-(int)count) 1098 pexit("File has become too big to be handled by MFS"); 1099 inodes[off].i_size += count; 1100 put_block(b, inodes); 1101 free(inodes); 1102 } 1103 } 1104 1105 1106 /*================================================================ 1107 * allocation assist group 1108 *===============================================================*/ 1109 static ino_t 1110 alloc_inode(int mode, int usrid, int grpid) 1111 { 1112 ino_t num; 1113 int off; 1114 block_t b; 1115 struct inode *inodes; 1116 1117 num = next_inode++; 1118 if (num > nrinodes) { 1119 pexit("File system does not have enough inodes (only %llu)", nrinodes); 1120 } 1121 b = ((num - 1) / inodes_per_block) + inode_offset; 1122 off = (num - 1) % inodes_per_block; 1123 1124 assert(inodes_per_block * sizeof(*inodes) == block_size); 1125 if(!(inodes = alloc_block())) 1126 err(1, "couldn't allocate a block of inodes"); 1127 1128 get_block(b, inodes); 1129 if (inodes[off].i_mode) { 1130 pexit("allocation new inode %llu with non-zero mode - this cannot happen", 1131 num); 1132 } 1133 inodes[off].i_mode = mode; 1134 inodes[off].i_uid = usrid; 1135 inodes[off].i_gid = grpid; 1136 if (verbose && (inodes[off].i_uid != usrid || inodes[off].i_gid != grpid)) 1137 fprintf(stderr, "Uid/gid %d.%d do not fit within inode, truncated\n", usrid, grpid); 1138 put_block(b, inodes); 1139 1140 free(inodes); 1141 1142 /* Set the bit in the bit map. */ 1143 insert_bit((block_t) INODE_MAP, num); 1144 return(num); 1145 } 1146 1147 1148 /* Allocate a new zone */ 1149 static zone_t 1150 alloc_zone(void) 1151 { 1152 /* Works for zone > block */ 1153 block_t b; 1154 int i; 1155 zone_t z; 1156 1157 z = next_zone++; 1158 b = z << zone_shift; 1159 if (b > nrblocks - zone_per_block) 1160 pexit("File system not big enough for all the files"); 1161 for (i = 0; i < zone_per_block; i++) 1162 put_block(b + i, zero); /* give an empty zone */ 1163 1164 insert_bit(zone_map, z - zoff); 1165 return z; 1166 } 1167 1168 1169 /* Insert one bit into the bitmap */ 1170 void 1171 insert_bit(block_t map, bit_t bit) 1172 { 1173 int boff, w, s; 1174 unsigned int bits_per_block; 1175 block_t map_block; 1176 bitchunk_t *buf; 1177 1178 buf = alloc_block(); 1179 1180 bits_per_block = FS_BITS_PER_BLOCK(block_size); 1181 map_block = map + bit / bits_per_block; 1182 if (map_block >= inode_offset) 1183 pexit("insertbit invades inodes area - this cannot happen"); 1184 boff = bit % bits_per_block; 1185 1186 assert(boff >=0); 1187 assert(boff < FS_BITS_PER_BLOCK(block_size)); 1188 get_block(map_block, buf); 1189 w = boff / FS_BITCHUNK_BITS; 1190 s = boff % FS_BITCHUNK_BITS; 1191 buf[w] |= (1 << s); 1192 put_block(map_block, buf); 1193 1194 free(buf); 1195 } 1196 1197 1198 /*================================================================ 1199 * proto-file processing assist group 1200 *===============================================================*/ 1201 int mode_con(char *p) 1202 { 1203 /* Convert string to mode */ 1204 int o1, o2, o3, mode; 1205 char c1, c2, c3; 1206 1207 c1 = *p++; 1208 c2 = *p++; 1209 c3 = *p++; 1210 o1 = *p++ - '0'; 1211 o2 = *p++ - '0'; 1212 o3 = *p++ - '0'; 1213 mode = (o1 << 6) | (o2 << 3) | o3; 1214 if (c1 == 'd') mode |= S_IFDIR; 1215 if (c1 == 'b') mode |= S_IFBLK; 1216 if (c1 == 'c') mode |= S_IFCHR; 1217 if (c1 == 's') mode |= S_IFLNK; 1218 if (c1 == 'l') mode |= S_IFLNK; /* just to be somewhat ls-compatible*/ 1219 /* XXX note: some other mkfs programs consider L to create hardlinks */ 1220 if (c1 == '-') mode |= S_IFREG; 1221 if (c2 == 'u') mode |= S_ISUID; 1222 if (c3 == 'g') mode |= S_ISGID; 1223 /* XXX There are no way to encode S_ISVTX */ 1224 return(mode); 1225 } 1226 1227 void 1228 get_line(char line[LINE_LEN], char *parse[MAX_TOKENS]) 1229 { 1230 /* Read a line and break it up in tokens */ 1231 int k; 1232 char c, *p; 1233 int d; 1234 1235 for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0; 1236 memset(line, 0, LINE_LEN); 1237 k = 0; 1238 p = line; 1239 while (1) { 1240 if (++k > LINE_LEN) pexit("Line too long"); 1241 d = fgetc(proto); 1242 if (d == EOF) pexit("Unexpected end-of-file"); 1243 *p = d; 1244 if (*p == ' ' || *p == '\t') *p = 0; 1245 if (*p == '\n') { 1246 lct++; 1247 *p++ = 0; 1248 *p = '\n'; 1249 break; 1250 } 1251 p++; 1252 } 1253 1254 k = 0; 1255 p = line; 1256 while (1) { 1257 c = *p++; 1258 if (c == '\n') return; 1259 if (c == 0) continue; 1260 parse[k++] = p - 1; 1261 do { 1262 c = *p++; 1263 } while (c != 0 && c != '\n'); 1264 } 1265 } 1266 1267 1268 /*================================================================ 1269 * other stuff 1270 *===============================================================*/ 1271 1272 /* 1273 * Check to see if the special file named 'device' is mounted. 1274 */ 1275 void 1276 check_mtab(const char *device) /* /dev/hd1 or whatever */ 1277 { 1278 #if defined(__minix) 1279 int n, r; 1280 struct stat sb; 1281 char dev[PATH_MAX], mount_point[PATH_MAX], 1282 type[MNTNAMELEN], flags[MNTFLAGLEN]; 1283 1284 r= stat(device, &sb); 1285 if (r == -1) 1286 { 1287 if (errno == ENOENT) 1288 return; /* Does not exist, and therefore not mounted. */ 1289 err(1, "stat %s failed", device); 1290 } 1291 if (!S_ISBLK(sb.st_mode)) 1292 { 1293 /* Not a block device and therefore not mounted. */ 1294 return; 1295 } 1296 1297 if (load_mtab(__UNCONST("mkfs")) < 0) return; 1298 while (1) { 1299 n = get_mtab_entry(dev, mount_point, type, flags); 1300 if (n < 0) return; 1301 if (strcmp(device, dev) == 0) { 1302 /* Can't mkfs on top of a mounted file system. */ 1303 errx(1, "%s is mounted on %s", device, mount_point); 1304 } 1305 } 1306 #else 1307 (void) device; /* shut up warnings about unused variable... */ 1308 #endif 1309 } 1310 1311 1312 time_t 1313 file_time(int f) 1314 { 1315 struct stat statbuf; 1316 1317 if (!fstat(f, &statbuf)) 1318 return current_time; 1319 if (statbuf.st_mtime<0 || statbuf.st_mtime>(uint32_t)(-1)) 1320 return current_time; 1321 return(statbuf.st_mtime); 1322 } 1323 1324 1325 __dead void 1326 pexit(char const * s, ...) 1327 { 1328 va_list va; 1329 1330 va_start(va, s); 1331 vwarn(s, va); 1332 va_end(va); 1333 if (lct != 0) 1334 warnx("Line %d being processed when error detected.\n", lct); 1335 exit(2); 1336 } 1337 1338 1339 void * 1340 alloc_block(void) 1341 { 1342 void *buf; 1343 1344 if(!(buf = malloc(block_size))) { 1345 err(1, "couldn't allocate filesystem buffer"); 1346 } 1347 memset(buf, 0, block_size); 1348 1349 return buf; 1350 } 1351 1352 void 1353 print_fs(void) 1354 { 1355 int i, j; 1356 ino_t k; 1357 struct inode *inode2; 1358 unsigned short *usbuf; 1359 block_t b; 1360 struct direct *dir; 1361 1362 assert(inodes_per_block * sizeof(*inode2) == block_size); 1363 if(!(inode2 = alloc_block())) 1364 err(1, "couldn't allocate a block of inodes"); 1365 1366 assert(NR_DIR_ENTRIES(block_size)*sizeof(*dir) == block_size); 1367 if(!(dir = alloc_block())) 1368 err(1, "couldn't allocate a block of directory entries"); 1369 1370 usbuf = alloc_block(); 1371 get_super_block(usbuf); 1372 printf("\nSuperblock: "); 1373 for (i = 0; i < 8; i++) printf("%06ho ", usbuf[i]); 1374 printf("\n "); 1375 for (i = 0; i < 8; i++) printf("%#04hX ", usbuf[i]); 1376 printf("\n "); 1377 for (i = 8; i < 15; i++) printf("%06ho ", usbuf[i]); 1378 printf("\n "); 1379 for (i = 8; i < 15; i++) printf("%#04hX ", usbuf[i]); 1380 get_block((block_t) INODE_MAP, usbuf); 1381 printf("...\nInode map: "); 1382 for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]); 1383 get_block((block_t) zone_map, usbuf); 1384 printf("...\nZone map: "); 1385 for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]); 1386 printf("...\n"); 1387 1388 free(usbuf); 1389 usbuf = NULL; 1390 1391 k = 0; 1392 for (b = inode_offset; k < nrinodes; b++) { 1393 get_block(b, inode2); 1394 for (i = 0; i < inodes_per_block; i++) { 1395 k = inodes_per_block * (int) (b - inode_offset) + i + 1; 1396 /* Lint but OK */ 1397 if (k > nrinodes) break; 1398 { 1399 if (inode2[i].i_mode != 0) { 1400 printf("Inode %3u: mode=", (unsigned)k); 1401 printf("%06o", (unsigned)inode2[i].i_mode); 1402 printf(" uid=%2d gid=%2d size=", 1403 (int)inode2[i].i_uid, (int)inode2[i].i_gid); 1404 printf("%6ld", (long)inode2[i].i_size); 1405 printf(" zone[0]=%u\n", (unsigned)inode2[i].i_zone[0]); 1406 } 1407 if ((inode2[i].i_mode & S_IFMT) == S_IFDIR) { 1408 /* This is a directory */ 1409 get_block(inode2[i].i_zone[0] << zone_shift, dir); 1410 for (j = 0; j < NR_DIR_ENTRIES(block_size); j++) 1411 if (dir[j].d_ino) 1412 printf("\tInode %2u: %s\n", 1413 (unsigned)dir[j].d_ino, 1414 dir[j].d_name); 1415 } 1416 } 1417 } 1418 } 1419 1420 if (zone_shift) 1421 printf("%d inodes used. %u zones (%u blocks) used.\n", 1422 (int)next_inode-1, next_zone, next_zone*zone_per_block); 1423 else 1424 printf("%d inodes used. %u zones used.\n", (int)next_inode-1, next_zone); 1425 free(dir); 1426 free(inode2); 1427 } 1428 1429 1430 /* 1431 * The first time a block is read, it returns all 0s, unless there has 1432 * been a write. This routine checks to see if a block has been accessed. 1433 */ 1434 int 1435 read_and_set(block_t n) 1436 { 1437 int w, s, mask, r; 1438 1439 w = n / 8; 1440 1441 assert(n < nrblocks); 1442 if(w >= umap_array_elements) { 1443 errx(1, "umap array too small - this can't happen"); 1444 } 1445 s = n % 8; 1446 mask = 1 << s; 1447 r = (umap_array[w] & mask ? 1 : 0); 1448 umap_array[w] |= mask; 1449 return(r); 1450 } 1451 1452 __dead void 1453 usage(void) 1454 { 1455 fprintf(stderr, "Usage: %s [-dltv] [-b blocks] [-i inodes]\n" 1456 "\t[-z zone_shift] [-I offset] [-x extra] [-B blocksize] special [proto]\n", 1457 progname); 1458 exit(4); 1459 } 1460 1461 void 1462 special(char * string, int insertmode) 1463 { 1464 int openmode = O_RDWR; 1465 if(!insertmode) openmode |= O_TRUNC; 1466 fd = open(string, O_RDWR | O_CREAT, 0644); 1467 if (fd < 0) err(1, "Can't open special file %s", string); 1468 mkfs_seek(0, SEEK_SET); 1469 } 1470 1471 1472 1473 /* Read a block. */ 1474 void 1475 get_block(block_t n, void *buf) 1476 { 1477 ssize_t k; 1478 1479 /* First access returns a zero block */ 1480 if (read_and_set(n) == 0) { 1481 memcpy(buf, zero, block_size); 1482 return; 1483 } 1484 mkfs_seek((uint64_t)(n) * block_size, SEEK_SET); 1485 k = read(fd, buf, block_size); 1486 if (k != block_size) 1487 pexit("get_block couldn't read block #%u", (unsigned)n); 1488 } 1489 1490 /* Read the super block. */ 1491 void 1492 get_super_block(void *buf) 1493 { 1494 ssize_t k; 1495 1496 mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET); 1497 k = read(fd, buf, SUPER_BLOCK_BYTES); 1498 if (k != SUPER_BLOCK_BYTES) 1499 err(1, "get_super_block couldn't read super block"); 1500 } 1501 1502 /* Write a block. */ 1503 void 1504 put_block(block_t n, void *buf) 1505 { 1506 1507 (void) read_and_set(n); 1508 1509 mkfs_seek((uint64_t)(n) * block_size, SEEK_SET); 1510 mkfs_write(buf, block_size); 1511 } 1512 1513 static ssize_t 1514 mkfs_write(void * buf, size_t count) 1515 { 1516 uint64_t fssize; 1517 ssize_t w; 1518 1519 /* Perform & check write */ 1520 w = write(fd, buf, count); 1521 if(w < 0) 1522 err(1, "mkfs_write: write failed"); 1523 if(w != count) 1524 errx(1, "mkfs_write: short write: %zd != %zu", w, count); 1525 1526 /* Check if this has made the FS any bigger; count bytes after offset */ 1527 fssize = mkfs_seek(0, SEEK_CUR); 1528 1529 assert(fssize >= fs_offset_bytes); 1530 fssize -= fs_offset_bytes; 1531 fssize = roundup(fssize, block_size); 1532 if(fssize > written_fs_size) 1533 written_fs_size = fssize; 1534 1535 return w; 1536 } 1537 1538 /* Seek to position in FS we're creating. */ 1539 static uint64_t 1540 mkfs_seek(uint64_t pos, int whence) 1541 { 1542 if(whence == SEEK_SET) pos += fs_offset_bytes; 1543 off_t newpos; 1544 if((newpos=lseek(fd, pos, whence)) == (off_t) -1) 1545 err(1, "mkfs_seek: lseek failed"); 1546 return newpos; 1547 } 1548