1 /* mke2fs.c - Create an ext2 filesystem image. 2 * 3 * Copyright 2006, 2007 Rob Landley <rob (at) landley.net> 4 5 // Still to go: "E:jJ:L:m:O:" 6 USE_MKE2FS(NEWTOY(mke2fs, "<1>2g:Fnqm#N#i#b#", TOYFLAG_SBIN)) 7 8 config MKE2FS 9 bool "mke2fs" 10 default n 11 help 12 usage: mke2fs [-Fnq] [-b ###] [-N|i ###] [-m ###] device 13 14 Create an ext2 filesystem on a block device or filesystem image. 15 16 -F Force to run on a mounted device 17 -n Don't write to device 18 -q Quiet (no output) 19 -b size Block size (1024, 2048, or 4096) 20 -N inodes Allocate this many inodes 21 -i bytes Allocate one inode for every XXX bytes of device 22 -m percent Reserve this percent of filesystem space for root user 23 24 config MKE2FS_JOURNAL 25 bool "Journaling support (ext3)" 26 default n 27 depends on MKE2FS 28 help 29 usage: mke2fs [-j] [-J size=###,device=XXX] 30 31 -j Create journal (ext3) 32 -J Journal options 33 size: Number of blocks (1024-102400) 34 device: Specify an external journal 35 36 config MKE2FS_GEN 37 bool "Generate (gene2fs)" 38 default n 39 depends on MKE2FS 40 help 41 usage: gene2fs [options] device filename 42 43 The [options] are the same as mke2fs. 44 45 config MKE2FS_LABEL 46 bool "Label support" 47 default n 48 depends on MKE2FS 49 help 50 usage: mke2fs [-L label] [-M path] [-o string] 51 52 -L Volume label 53 -M Path to mount point 54 -o Created by 55 56 config MKE2FS_EXTENDED 57 bool "Extended options" 58 default n 59 depends on MKE2FS 60 help 61 usage: mke2fs [-E stride=###] [-O option[,option]] 62 63 -E stride= Set RAID stripe size (in blocks) 64 -O [opts] Specify fewer ext2 option flags (for old kernels) 65 All of these are on by default (as appropriate) 66 none Clear default options (all but journaling) 67 dir_index Use htree indexes for large directories 68 filetype Store file type info in directory entry 69 has_journal Set by -j 70 journal_dev Set by -J device=XXX 71 sparse_super Don't allocate huge numbers of redundant superblocks 72 */ 73 74 #define FOR_mke2fs 75 #include "toys.h" 76 77 GLOBALS( 78 // Command line arguments. 79 long blocksize; 80 long bytes_per_inode; 81 long inodes; // Total inodes in filesystem. 82 long reserved_percent; // Integer precent of space to reserve for root. 83 char *gendir; // Where to read dirtree from. 84 85 // Internal data. 86 struct dirtree *dt; // Tree of files to copy into the new filesystem. 87 unsigned treeblocks; // Blocks used by dt 88 unsigned treeinodes; // Inodes used by dt 89 90 unsigned blocks; // Total blocks in the filesystem. 91 unsigned freeblocks; // Free blocks in the filesystem. 92 unsigned inodespg; // Inodes per group 93 unsigned groups; // Total number of block groups. 94 unsigned blockbits; // Bits per block. (Also blocks per group.) 95 96 // For gene2fs 97 unsigned nextblock; // Next data block to allocate 98 unsigned nextgroup; // Next group we'll be allocating from 99 int fsfd; // File descriptor of filesystem (to output to). 100 ) 101 102 // Stuff defined in linux/ext2_fs.h 103 104 #define EXT2_SUPER_MAGIC 0xEF53 105 106 struct ext2_superblock { 107 uint32_t inodes_count; // Inodes count 108 uint32_t blocks_count; // Blocks count 109 uint32_t r_blocks_count; // Reserved blocks count 110 uint32_t free_blocks_count; // Free blocks count 111 uint32_t free_inodes_count; // Free inodes count 112 uint32_t first_data_block; // First Data Block 113 uint32_t log_block_size; // Block size 114 uint32_t log_frag_size; // Fragment size 115 uint32_t blocks_per_group; // Blocks per group 116 uint32_t frags_per_group; // Fragments per group 117 uint32_t inodes_per_group; // Inodes per group 118 uint32_t mtime; // Mount time 119 uint32_t wtime; // Write time 120 uint16_t mnt_count; // Mount count 121 uint16_t max_mnt_count; // Maximal mount count 122 uint16_t magic; // Magic signature 123 uint16_t state; // File system state 124 uint16_t errors; // Behaviour when detecting errors 125 uint16_t minor_rev_level; // minor revision level 126 uint32_t lastcheck; // time of last check 127 uint32_t checkinterval; // max. time between checks 128 uint32_t creator_os; // OS 129 uint32_t rev_level; // Revision level 130 uint16_t def_resuid; // Default uid for reserved blocks 131 uint16_t def_resgid; // Default gid for reserved blocks 132 uint32_t first_ino; // First non-reserved inode 133 uint16_t inode_size; // size of inode structure 134 uint16_t block_group_nr; // block group # of this superblock 135 uint32_t feature_compat; // compatible feature set 136 uint32_t feature_incompat; // incompatible feature set 137 uint32_t feature_ro_compat; // readonly-compatible feature set 138 char uuid[16]; // 128-bit uuid for volume 139 char volume_name[16]; // volume name 140 char last_mounted[64]; // directory where last mounted 141 uint32_t alg_usage_bitmap; // For compression 142 // For EXT2_COMPAT_PREALLOC 143 uint8_t prealloc_blocks; // Nr of blocks to try to preallocate 144 uint8_t prealloc_dir_blocks; //Nr to preallocate for dirs 145 uint16_t padding1; 146 // For EXT3_FEATURE_COMPAT_HAS_JOURNAL 147 uint8_t journal_uuid[16]; // uuid of journal superblock 148 uint32_t journal_inum; // inode number of journal file 149 uint32_t journal_dev; // device number of journal file 150 uint32_t last_orphan; // start of list of inodes to delete 151 uint32_t hash_seed[4]; // HTREE hash seed 152 uint8_t def_hash_version; // Default hash version to use 153 uint8_t padding2[3]; 154 uint32_t default_mount_opts; 155 uint32_t first_meta_bg; // First metablock block group 156 uint32_t mkfs_time; // Creation timestamp 157 uint32_t jnl_blocks[17]; // Backup of journal inode 158 // uint32_t reserved[172]; // Padding to the end of the block 159 }; 160 161 struct ext2_group 162 { 163 uint32_t block_bitmap; // Block number of block bitmap 164 uint32_t inode_bitmap; // Block number of inode bitmap 165 uint32_t inode_table; // Block number of inode table 166 uint16_t free_blocks_count; // How many free blocks in this group? 167 uint16_t free_inodes_count; // How many free inodes in this group? 168 uint16_t used_dirs_count; // How many directories? 169 uint16_t reserved[7]; // pad to 32 bytes 170 }; 171 172 struct ext2_dentry { 173 uint32_t inode; // Inode number 174 uint16_t rec_len; // Directory entry length 175 uint8_t name_len; // Name length 176 uint8_t file_type; 177 char name[0]; // File name 178 }; 179 180 struct ext2_inode { 181 uint16_t mode; // File mode 182 uint16_t uid; // Low 16 bits of Owner Uid 183 uint32_t size; // Size in bytes 184 uint32_t atime; // Access time 185 uint32_t ctime; // Creation time 186 uint32_t mtime; // Modification time 187 uint32_t dtime; // Deletion Time 188 uint16_t gid; // Low 16 bits of Group Id 189 uint16_t links_count; // Links count 190 uint32_t blocks; // Blocks count 191 uint32_t flags; // File flags 192 uint32_t reserved1; 193 uint32_t block[15]; // Pointers to blocks 194 uint32_t generation; // File version (for NFS) 195 uint32_t file_acl; // File ACL 196 uint32_t dir_acl; // Directory ACL (or top bits of file length) 197 uint32_t faddr; // Last block in file 198 uint8_t frag; // Fragment number 199 uint8_t fsize; // Fragment size 200 uint16_t pad1; 201 uint16_t uid_high; // High bits of uid 202 uint16_t gid_high; // High bits of gid 203 uint32_t reserved2; 204 }; 205 206 #define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 207 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 208 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 209 #define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 210 #define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010 211 #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 212 213 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 214 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 215 #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 216 217 #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 218 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 219 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 220 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 221 #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 222 223 #define EXT2_NAME_LEN 255 224 225 // Ext2 directory file types. Only the low 3 bits are used. The 226 // other bits are reserved for now. 227 228 enum { 229 EXT2_FT_UNKNOWN, 230 EXT2_FT_REG_FILE, 231 EXT2_FT_DIR, 232 EXT2_FT_CHRDEV, 233 EXT2_FT_BLKDEV, 234 EXT2_FT_FIFO, 235 EXT2_FT_SOCK, 236 EXT2_FT_SYMLINK, 237 EXT2_FT_MAX 238 }; 239 240 #define INODES_RESERVED 10 241 242 static uint32_t div_round_up(uint32_t a, uint32_t b) 243 { 244 uint32_t c = a/b; 245 246 if (a%b) c++; 247 return c; 248 } 249 250 // Calculate data blocks plus index blocks needed to hold a file. 251 252 static uint32_t file_blocks_used(uint64_t size, uint32_t *blocklist) 253 { 254 uint32_t dblocks = (uint32_t)((size+(TT.blocksize-1))/TT.blocksize); 255 uint32_t idx=TT.blocksize/4, iblocks=0, diblocks=0, tiblocks=0; 256 257 // Fill out index blocks in inode. 258 259 if (blocklist) { 260 int i; 261 262 // Direct index blocks 263 for (i=0; i<13 && i<dblocks; i++) blocklist[i] = i; 264 // Singly indirect index blocks 265 if (dblocks > 13+idx) blocklist[13] = 13+idx; 266 // Doubly indirect index blocks 267 idx = 13 + idx + (idx*idx); 268 if (dblocks > idx) blocklist[14] = idx; 269 270 return 0; 271 } 272 273 // Account for direct, singly, doubly, and triply indirect index blocks 274 275 if (dblocks > 12) { 276 iblocks = ((dblocks-13)/idx)+1; 277 if (iblocks > 1) { 278 diblocks = ((iblocks-2)/idx)+1; 279 if (diblocks > 1) 280 tiblocks = ((diblocks-2)/idx)+1; 281 } 282 } 283 284 return dblocks + iblocks + diblocks + tiblocks; 285 } 286 287 // Use the parent pointer to iterate through the tree non-recursively. 288 static struct dirtree *treenext(struct dirtree *this) 289 { 290 while (this && !this->next) this = this->parent; 291 if (this) this = this->next; 292 293 return this; 294 } 295 296 // Recursively calculate the number of blocks used by each inode in the tree. 297 // Returns blocks used by this directory, assigns bytes used to *size. 298 // Writes total block count to TT.treeblocks and inode count to TT.treeinodes. 299 300 static long check_treesize(struct dirtree *that, off_t *size) 301 { 302 long blocks; 303 304 while (that) { 305 *size += sizeof(struct ext2_dentry) + strlen(that->name); 306 307 if (that->child) 308 that->st.st_blocks = check_treesize(that->child, &that->st.st_size); 309 else if (S_ISREG(that->st.st_mode)) { 310 that->st.st_blocks = file_blocks_used(that->st.st_size, 0); 311 TT.treeblocks += that->st.st_blocks; 312 } 313 that = that->next; 314 } 315 TT.treeblocks += blocks = file_blocks_used(*size, 0); 316 TT.treeinodes++; 317 318 return blocks; 319 } 320 321 // Calculate inode numbers and link counts. 322 // 323 // To do this right I need to copy the tree and sort it, but here's a really 324 // ugly n^2 way of dealing with the problem that doesn't scale well to large 325 // numbers of files (> 100,000) but can be done in very little code. 326 // This rewrites inode numbers to their final values, allocating depth first. 327 328 static void check_treelinks(struct dirtree *tree) 329 { 330 struct dirtree *current=tree, *that; 331 long inode = INODES_RESERVED; 332 333 while (current) { 334 ++inode; 335 // Since we can't hardlink to directories, we know their link count. 336 if (S_ISDIR(current->st.st_mode)) current->st.st_nlink = 2; 337 else { 338 dev_t new = current->st.st_dev; 339 340 if (!new) continue; 341 342 // Look for other copies of current node 343 current->st.st_nlink = 0; 344 for (that = tree; that; that = treenext(that)) { 345 if (current->st.st_ino == that->st.st_ino && 346 current->st.st_dev == that->st.st_dev) 347 { 348 current->st.st_nlink++; 349 current->st.st_ino = inode; 350 } 351 } 352 } 353 current->st.st_ino = inode; 354 current = treenext(current); 355 } 356 } 357 358 // Calculate inodes per group from total inodes. 359 static uint32_t get_inodespg(uint32_t inodes) 360 { 361 uint32_t temp; 362 363 // Round up to fill complete inode blocks. 364 temp = (inodes + TT.groups - 1) / TT.groups; 365 inodes = TT.blocksize/sizeof(struct ext2_inode); 366 return ((temp + inodes - 1)/inodes)*inodes; 367 } 368 369 // Fill out superblock and TT structures. 370 371 static void init_superblock(struct ext2_superblock *sb) 372 { 373 uint32_t temp; 374 375 // Set log_block_size and log_frag_size. 376 377 for (temp = 0; temp < 4; temp++) if (TT.blocksize == 1024<<temp) break; 378 if (temp==4) error_exit("bad blocksize"); 379 sb->log_block_size = sb->log_frag_size = SWAP_LE32(temp); 380 381 // Fill out blocks_count, r_blocks_count, first_data_block 382 383 sb->blocks_count = SWAP_LE32(TT.blocks); 384 sb->free_blocks_count = SWAP_LE32(TT.freeblocks); 385 temp = (TT.blocks * (uint64_t)TT.reserved_percent) / 100; 386 sb->r_blocks_count = SWAP_LE32(temp); 387 388 sb->first_data_block = SWAP_LE32(TT.blocksize == 1024 ? 1 : 0); 389 390 // Set blocks_per_group and frags_per_group, which is the size of an 391 // allocation bitmap that fits in one block (I.E. how many bits per block)? 392 393 sb->blocks_per_group = sb->frags_per_group = SWAP_LE32(TT.blockbits); 394 395 // Set inodes_per_group and total inodes_count 396 sb->inodes_per_group = SWAP_LE32(TT.inodespg); 397 sb->inodes_count = SWAP_LE32(TT.inodespg * TT.groups); 398 399 // Determine free inodes. 400 temp = TT.inodespg*TT.groups - INODES_RESERVED; 401 if (temp < TT.treeinodes) error_exit("Not enough inodes.\n"); 402 sb->free_inodes_count = SWAP_LE32(temp - TT.treeinodes); 403 404 // Fill out the rest of the superblock. 405 sb->max_mnt_count=0xFFFF; 406 sb->wtime = sb->lastcheck = sb->mkfs_time = SWAP_LE32(time(NULL)); 407 sb->magic = SWAP_LE32(0xEF53); 408 sb->state = sb->errors = SWAP_LE16(1); 409 410 sb->rev_level = SWAP_LE32(1); 411 sb->first_ino = SWAP_LE32(INODES_RESERVED+1); 412 sb->inode_size = SWAP_LE16(sizeof(struct ext2_inode)); 413 sb->feature_incompat = SWAP_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE); 414 sb->feature_ro_compat = SWAP_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER); 415 416 create_uuid(sb->uuid); 417 418 // TODO If we're called as mke3fs or mkfs.ext3, do a journal. 419 420 //if (strchr(toys.which->name,'3')) 421 // sb->feature_compat |= SWAP_LE32(EXT3_FEATURE_COMPAT_HAS_JOURNAL); 422 } 423 424 // Does this group contain a superblock backup (and group descriptor table)? 425 static int is_sb_group(uint32_t group) 426 { 427 int i; 428 429 // Superblock backups are on groups 0, 1, and powers of 3, 5, and 7. 430 if(!group || group==1) return 1; 431 for (i=3; i<9; i+=2) { 432 int j = i; 433 while (j<group) j*=i; 434 if (j==group) return 1; 435 } 436 return 0; 437 } 438 439 440 // Number of blocks used in group by optional superblock/group list backup. 441 static int group_superblock_overhead(uint32_t group) 442 { 443 int used; 444 445 if (!is_sb_group(group)) return 0; 446 447 // How many blocks does the group descriptor table take up? 448 used = TT.groups * sizeof(struct ext2_group); 449 used += TT.blocksize - 1; 450 used /= TT.blocksize; 451 // Plus the superblock itself. 452 used++; 453 // And a corner case. 454 if (!group && TT.blocksize == 1024) used++; 455 456 return used; 457 } 458 459 // Number of blocks used in group to store superblock/group/inode list 460 static int group_overhead(uint32_t group) 461 { 462 // Return superblock backup overhead (if any), plus block/inode 463 // allocation bitmaps, plus inode tables. 464 return group_superblock_overhead(group) + 2 + get_inodespg(TT.inodespg) 465 / (TT.blocksize/sizeof(struct ext2_inode)); 466 } 467 468 // In bitmap "array" set "len" bits starting at position "start" (from 0). 469 static void bits_set(char *array, int start, int len) 470 { 471 while(len) { 472 if ((start&7) || len<8) { 473 array[start/8]|=(1<<(start&7)); 474 start++; 475 len--; 476 } else { 477 array[start/8]=255; 478 start+=8; 479 len-=8; 480 } 481 } 482 } 483 484 // Seek past len bytes (to maintain sparse file), or write zeroes if output 485 // not seekable 486 static void put_zeroes(int len) 487 { 488 if(-1 == lseek(TT.fsfd, len, SEEK_SET)) { 489 memset(toybuf, 0, sizeof(toybuf)); 490 while (len) { 491 int out = len > sizeof(toybuf) ? sizeof(toybuf) : len; 492 xwrite(TT.fsfd, toybuf, out); 493 len -= out; 494 } 495 } 496 } 497 498 // Fill out an inode structure from struct stat info in dirtree. 499 static void fill_inode(struct ext2_inode *in, struct dirtree *that) 500 { 501 uint32_t fbu[15]; 502 int temp; 503 504 file_blocks_used(that->st.st_size, fbu); 505 506 // If that inode needs data blocks allocated to it. 507 if (that->st.st_size) { 508 int i, group = TT.nextblock/TT.blockbits; 509 510 // TODO: teach this about indirect blocks. 511 for (i=0; i<15; i++) { 512 // If we just jumped into a new group, skip group overhead blocks. 513 while (group >= TT.nextgroup) 514 TT.nextblock += group_overhead(TT.nextgroup++); 515 } 516 } 517 // TODO : S_ISREG/DIR/CHR/BLK/FIFO/LNK/SOCK(m) 518 in->mode = SWAP_LE32(that->st.st_mode); 519 520 in->uid = SWAP_LE16(that->st.st_uid & 0xFFFF); 521 in->uid_high = SWAP_LE16(that->st.st_uid >> 16); 522 in->gid = SWAP_LE16(that->st.st_gid & 0xFFFF); 523 in->gid_high = SWAP_LE16(that->st.st_gid >> 16); 524 in->size = SWAP_LE32(that->st.st_size & 0xFFFFFFFF); 525 526 // Contortions to make the compiler not generate a warning for x>>32 527 // when x is 32 bits. The optimizer should clean this up. 528 if (sizeof(that->st.st_size) > 4) temp = 32; 529 else temp = 0; 530 if (temp) in->dir_acl = SWAP_LE32(that->st.st_size >> temp); 531 532 in->atime = SWAP_LE32(that->st.st_atime); 533 in->ctime = SWAP_LE32(that->st.st_ctime); 534 in->mtime = SWAP_LE32(that->st.st_mtime); 535 536 in->links_count = SWAP_LE16(that->st.st_nlink); 537 in->blocks = SWAP_LE32(that->st.st_blocks); 538 // in->faddr 539 } 540 541 // Works like an archiver. 542 // The first argument is the name of the file to create. If it already 543 // exists, that size will be used. 544 545 void mke2fs_main(void) 546 { 547 int i, temp; 548 off_t length; 549 uint32_t usedblocks, usedinodes, dtiblk, dtbblk; 550 struct dirtree *dti, *dtb; 551 struct ext2_superblock sb; 552 553 // Handle command line arguments. 554 555 if (toys.optargs[1]) { 556 sscanf(toys.optargs[1], "%u", &TT.blocks); 557 temp = O_RDWR|O_CREAT; 558 } else temp = O_RDWR; 559 if (!TT.reserved_percent) TT.reserved_percent = 5; 560 561 // TODO: Check if filesystem is mounted here 562 563 // For mke?fs, open file. For gene?fs, create file. 564 TT.fsfd = xcreate(*toys.optargs, temp, 0777); 565 566 // Determine appropriate block size and block count from file length. 567 // (If no length, default to 4k. They can override it on the cmdline.) 568 569 length = fdlength(TT.fsfd); 570 if (!TT.blocksize) TT.blocksize = (length && length < 1<<29) ? 1024 : 4096; 571 TT.blockbits = 8*TT.blocksize; 572 if (!TT.blocks) TT.blocks = length/TT.blocksize; 573 574 // Collect gene2fs list or lost+found, calculate requirements. 575 576 if (TT.gendir) { 577 strncpy(toybuf, TT.gendir, sizeof(toybuf)); 578 dti = dirtree_read(toybuf, dirtree_notdotdot); 579 } else { 580 dti = xzalloc(sizeof(struct dirtree)+11); 581 strcpy(dti->name, "lost+found"); 582 dti->st.st_mode = S_IFDIR|0755; 583 dti->st.st_ctime = dti->st.st_mtime = time(NULL); 584 } 585 586 // Add root directory inode. This is iterated through for when finding 587 // blocks, but not when finding inodes. The tree's parent pointers don't 588 // point back into this. 589 590 dtb = xzalloc(sizeof(struct dirtree)+1); 591 dtb->st.st_mode = S_IFDIR|0755; 592 dtb->st.st_ctime = dtb->st.st_mtime = time(NULL); 593 dtb->child = dti; 594 595 // Figure out how much space is used by preset files 596 length = check_treesize(dtb, &(dtb->st.st_size)); 597 check_treelinks(dtb); 598 599 // Figure out how many total inodes we need. 600 601 if (!TT.inodes) { 602 if (!TT.bytes_per_inode) TT.bytes_per_inode = 8192; 603 TT.inodes = (TT.blocks * (uint64_t)TT.blocksize) / TT.bytes_per_inode; 604 } 605 606 // If we're generating a filesystem and have no idea how many blocks it 607 // needs, start with a minimal guess, find the overhead of that many 608 // groups, and loop until this is enough groups to store this many blocks. 609 if (!TT.blocks) TT.groups = (TT.treeblocks/TT.blockbits)+1; 610 else TT.groups = div_round_up(TT.blocks, TT.blockbits); 611 612 for (;;) { 613 temp = TT.treeblocks; 614 615 for (i = 0; i<TT.groups; i++) temp += group_overhead(i); 616 617 if (TT.blocks) { 618 if (TT.blocks < temp) error_exit("Not enough space.\n"); 619 break; 620 } 621 if (temp <= TT.groups * TT.blockbits) { 622 TT.blocks = temp; 623 break; 624 } 625 TT.groups++; 626 } 627 TT.freeblocks = TT.blocks - temp; 628 629 // Now we know all the TT data, initialize superblock structure. 630 631 init_superblock(&sb); 632 633 // Start writing. Skip the first 1k to avoid the boot sector (if any). 634 put_zeroes(1024); 635 636 // Loop through block groups, write out each one. 637 dtiblk = dtbblk = usedblocks = usedinodes = 0; 638 for (i=0; i<TT.groups; i++) { 639 struct ext2_inode *in = (struct ext2_inode *)toybuf; 640 uint32_t start, itable, used, end; 641 int j, slot; 642 643 // Where does this group end? 644 end = TT.blockbits; 645 if ((i+1)*TT.blockbits > TT.blocks) end = TT.blocks & (TT.blockbits-1); 646 647 // Blocks used by inode table 648 itable = (TT.inodespg*sizeof(struct ext2_inode))/TT.blocksize; 649 650 // If a superblock goes here, write it out. 651 start = group_superblock_overhead(i); 652 if (start) { 653 struct ext2_group *bg = (struct ext2_group *)toybuf; 654 int treeblocks = TT.treeblocks, treeinodes = TT.treeinodes; 655 656 sb.block_group_nr = SWAP_LE16(i); 657 658 // Write superblock and pad it up to block size 659 xwrite(TT.fsfd, &sb, sizeof(struct ext2_superblock)); 660 temp = TT.blocksize - sizeof(struct ext2_superblock); 661 if (!i && TT.blocksize > 1024) temp -= 1024; 662 memset(toybuf, 0, TT.blocksize); 663 xwrite(TT.fsfd, toybuf, temp); 664 665 // Loop through groups to write group descriptor table. 666 for(j=0; j<TT.groups; j++) { 667 668 // Figure out what sector this group starts in. 669 used = group_superblock_overhead(j); 670 671 // Find next array slot in this block (flush block if full). 672 slot = j % (TT.blocksize/sizeof(struct ext2_group)); 673 if (!slot) { 674 if (j) xwrite(TT.fsfd, bg, TT.blocksize); 675 memset(bg, 0, TT.blocksize); 676 } 677 678 // How many free inodes in this group? 679 temp = TT.inodespg; 680 if (!i) temp -= INODES_RESERVED; 681 if (temp > treeinodes) { 682 treeinodes -= temp; 683 temp = 0; 684 } else { 685 temp -= treeinodes; 686 treeinodes = 0; 687 } 688 bg[slot].free_inodes_count = SWAP_LE16(temp); 689 690 // How many free blocks in this group? 691 temp = TT.inodespg/(TT.blocksize/sizeof(struct ext2_inode)) + 2; 692 temp = end-used-temp; 693 if (temp > treeblocks) { 694 treeblocks -= temp; 695 temp = 0; 696 } else { 697 temp -= treeblocks; 698 treeblocks = 0; 699 } 700 bg[slot].free_blocks_count = SWAP_LE32(temp); 701 702 // Fill out rest of group structure 703 used += j*TT.blockbits; 704 bg[slot].block_bitmap = SWAP_LE32(used++); 705 bg[slot].inode_bitmap = SWAP_LE32(used++); 706 bg[slot].inode_table = SWAP_LE32(used); 707 bg[slot].used_dirs_count = 0; // (TODO) 708 } 709 xwrite(TT.fsfd, bg, TT.blocksize); 710 } 711 712 // Now write out stuff that every block group has. 713 714 // Write block usage bitmap 715 716 start += 2 + itable; 717 memset(toybuf, 0, TT.blocksize); 718 bits_set(toybuf, 0, start); 719 bits_set(toybuf, end, TT.blockbits-end); 720 temp = TT.treeblocks - usedblocks; 721 if (temp) { 722 if (end-start > temp) temp = end-start; 723 bits_set(toybuf, start, temp); 724 } 725 xwrite(TT.fsfd, toybuf, TT.blocksize); 726 727 // Write inode bitmap 728 memset(toybuf, 0, TT.blocksize); 729 j = 0; 730 if (!i) bits_set(toybuf, 0, j = INODES_RESERVED); 731 bits_set(toybuf, TT.inodespg, slot = TT.blockbits-TT.inodespg); 732 temp = TT.treeinodes - usedinodes; 733 if (temp) { 734 if (slot-j > temp) temp = slot-j; 735 bits_set(toybuf, j, temp); 736 } 737 xwrite(TT.fsfd, toybuf, TT.blocksize); 738 739 // Write inode table for this group (TODO) 740 for (j = 0; j<TT.inodespg; j++) { 741 slot = j % (TT.blocksize/sizeof(struct ext2_inode)); 742 if (!slot) { 743 if (j) xwrite(TT.fsfd, in, TT.blocksize); 744 memset(in, 0, TT.blocksize); 745 } 746 if (!i && j<INODES_RESERVED) { 747 // Write root inode 748 if (j == 2) fill_inode(in+slot, dtb); 749 } else if (dti) { 750 fill_inode(in+slot, dti); 751 dti = treenext(dti); 752 } 753 } 754 xwrite(TT.fsfd, in, TT.blocksize); 755 756 while (dtb) { 757 // TODO write index data block 758 // TODO write root directory data block 759 // TODO write directory data block 760 // TODO write file data block 761 put_zeroes(TT.blocksize); 762 start++; 763 if (start == end) break; 764 } 765 // Write data blocks (TODO) 766 put_zeroes((end-start) * TT.blocksize); 767 } 768 } 769