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