1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "ext4_utils/ext4_utils.h" 18 19 #include <fcntl.h> 20 #include <inttypes.h> 21 #include <stddef.h> 22 #include <string.h> 23 #include <sys/stat.h> 24 #include <sys/types.h> 25 26 #include <sparse/sparse.h> 27 28 #include "allocate.h" 29 #include "extent.h" 30 #include "indirect.h" 31 #include "sha1.h" 32 33 #ifdef REAL_UUID 34 #include <uuid.h> 35 #endif 36 37 #ifdef _WIN32 38 #include <winsock2.h> 39 #else 40 #include <arpa/inet.h> 41 #include <sys/ioctl.h> 42 #endif 43 44 #if defined(__linux__) 45 #include <linux/fs.h> 46 #elif defined(__APPLE__) && defined(__MACH__) 47 #include <sys/disk.h> 48 #endif 49 50 int force = 0; 51 struct fs_info info; 52 struct fs_aux_info aux_info; 53 struct sparse_file *ext4_sparse_file; 54 struct block_allocation *base_fs_allocations = NULL; 55 56 jmp_buf setjmp_env; 57 58 /* Definition from RFC-4122 */ 59 struct uuid { 60 u32 time_low; 61 u16 time_mid; 62 u16 time_hi_and_version; 63 u8 clk_seq_hi_res; 64 u8 clk_seq_low; 65 u16 node0_1; 66 u32 node2_5; 67 }; 68 69 static void sha1_hash(const char *namespace, const char *name, 70 unsigned char sha1[SHA1_DIGEST_LENGTH]) 71 { 72 SHA1_CTX ctx; 73 SHA1Init(&ctx); 74 SHA1Update(&ctx, (const u8*)namespace, strlen(namespace)); 75 SHA1Update(&ctx, (const u8*)name, strlen(name)); 76 SHA1Final(sha1, &ctx); 77 } 78 79 static void generate_sha1_uuid(const char *namespace, const char *name, u8 result[16]) 80 { 81 unsigned char sha1[SHA1_DIGEST_LENGTH]; 82 struct uuid *uuid = (struct uuid *)result; 83 84 sha1_hash(namespace, name, (unsigned char*)sha1); 85 memcpy(uuid, sha1, sizeof(struct uuid)); 86 87 uuid->time_low = ntohl(uuid->time_low); 88 uuid->time_mid = ntohs(uuid->time_mid); 89 uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version); 90 uuid->time_hi_and_version &= 0x0FFF; 91 uuid->time_hi_and_version |= (5 << 12); 92 uuid->clk_seq_hi_res &= ~(1 << 6); 93 uuid->clk_seq_hi_res |= 1 << 7; 94 } 95 96 /* returns 1 if a is a power of b */ 97 static int is_power_of(int a, int b) 98 { 99 while (a > b) { 100 if (a % b) 101 return 0; 102 a /= b; 103 } 104 105 return (a == b) ? 1 : 0; 106 } 107 108 int bitmap_get_bit(u8 *bitmap, u32 bit) 109 { 110 if (bitmap[bit / 8] & (1 << (bit % 8))) 111 return 1; 112 113 return 0; 114 } 115 116 void bitmap_clear_bit(u8 *bitmap, u32 bit) 117 { 118 bitmap[bit / 8] &= ~(1 << (bit % 8)); 119 120 return; 121 } 122 123 /* Returns 1 if the bg contains a backup superblock. On filesystems with 124 the sparse_super feature, only block groups 0, 1, and powers of 3, 5, 125 and 7 have backup superblocks. Otherwise, all block groups have backup 126 superblocks */ 127 int ext4_bg_has_super_block(int bg) 128 { 129 /* Without sparse_super, every block group has a superblock */ 130 if (!(info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER)) 131 return 1; 132 133 if (bg == 0 || bg == 1) 134 return 1; 135 136 if (is_power_of(bg, 3) || is_power_of(bg, 5) || is_power_of(bg, 7)) 137 return 1; 138 139 return 0; 140 } 141 142 /* Function to read the primary superblock */ 143 void read_sb(int fd, struct ext4_super_block *sb) 144 { 145 off64_t ret; 146 147 ret = lseek64(fd, 1024, SEEK_SET); 148 if (ret < 0) 149 critical_error_errno("failed to seek to superblock"); 150 151 ret = read(fd, sb, sizeof(*sb)); 152 if (ret < 0) 153 critical_error_errno("failed to read superblock"); 154 if (ret != sizeof(*sb)) 155 critical_error("failed to read all of superblock"); 156 } 157 158 /* Function to write a primary or backup superblock at a given offset */ 159 void write_sb(int fd, unsigned long long offset, struct ext4_super_block *sb) 160 { 161 off64_t ret; 162 163 ret = lseek64(fd, offset, SEEK_SET); 164 if (ret < 0) 165 critical_error_errno("failed to seek to superblock"); 166 167 ret = write(fd, sb, sizeof(*sb)); 168 if (ret < 0) 169 critical_error_errno("failed to write superblock"); 170 if (ret != sizeof(*sb)) 171 critical_error("failed to write all of superblock"); 172 } 173 174 static void block_device_write_sb(int fd) 175 { 176 unsigned long long offset; 177 u32 i; 178 179 /* write out the backup superblocks */ 180 for (i = 1; i < aux_info.groups; i++) { 181 if (ext4_bg_has_super_block(i)) { 182 offset = (unsigned long long)info.block_size * (aux_info.first_data_block 183 + i * info.blocks_per_group); 184 write_sb(fd, offset, aux_info.backup_sb[i]); 185 } 186 } 187 188 /* write out the primary superblock */ 189 write_sb(fd, 1024, aux_info.sb); 190 } 191 192 /* Write the filesystem image to a file */ 193 void write_ext4_image(int fd, int gz, int sparse, int crc) 194 { 195 sparse_file_write(ext4_sparse_file, fd, gz, sparse, crc); 196 197 if (info.block_device) 198 block_device_write_sb(fd); 199 } 200 201 /* Compute the rest of the parameters of the filesystem from the basic info */ 202 void ext4_create_fs_aux_info() 203 { 204 aux_info.first_data_block = (info.block_size > 1024) ? 0 : 1; 205 aux_info.len_blocks = info.len / info.block_size; 206 aux_info.inode_table_blocks = DIV_ROUND_UP(info.inodes_per_group * info.inode_size, 207 info.block_size); 208 aux_info.groups = DIV_ROUND_UP(aux_info.len_blocks - aux_info.first_data_block, 209 info.blocks_per_group); 210 aux_info.blocks_per_ind = info.block_size / sizeof(u32); 211 aux_info.blocks_per_dind = aux_info.blocks_per_ind * aux_info.blocks_per_ind; 212 aux_info.blocks_per_tind = aux_info.blocks_per_dind * aux_info.blocks_per_dind; 213 214 aux_info.bg_desc_blocks = 215 DIV_ROUND_UP(aux_info.groups * sizeof(struct ext2_group_desc), 216 info.block_size); 217 218 aux_info.default_i_flags = EXT4_NOATIME_FL; 219 220 u32 last_group_size = aux_info.len_blocks % info.blocks_per_group; 221 u32 last_header_size = 2 + aux_info.inode_table_blocks; 222 if (ext4_bg_has_super_block(aux_info.groups - 1)) 223 last_header_size += 1 + aux_info.bg_desc_blocks + 224 info.bg_desc_reserve_blocks; 225 if (aux_info.groups <= 1 && last_group_size < last_header_size) { 226 critical_error("filesystem size too small"); 227 } 228 if (last_group_size > 0 && last_group_size < last_header_size) { 229 aux_info.groups--; 230 aux_info.len_blocks -= last_group_size; 231 } 232 233 /* A zero-filled superblock to be written firstly to the block 234 * device to mark the file-system as invalid 235 */ 236 aux_info.sb_zero = calloc(1, info.block_size); 237 if (!aux_info.sb_zero) 238 critical_error_errno("calloc"); 239 240 /* The write_data* functions expect only block aligned calls. 241 * This is not an issue, except when we write out the super 242 * block on a system with a block size > 1K. So, we need to 243 * deal with that here. 244 */ 245 aux_info.sb_block = calloc(1, info.block_size); 246 if (!aux_info.sb_block) 247 critical_error_errno("calloc"); 248 249 if (info.block_size > 1024) 250 aux_info.sb = (struct ext4_super_block *)((char *)aux_info.sb_block + 1024); 251 else 252 aux_info.sb = aux_info.sb_block; 253 254 /* Alloc an array to hold the pointers to the backup superblocks */ 255 aux_info.backup_sb = calloc(aux_info.groups, sizeof(char *)); 256 257 if (!aux_info.sb) 258 critical_error_errno("calloc"); 259 260 aux_info.bg_desc = calloc(info.block_size, aux_info.bg_desc_blocks); 261 if (!aux_info.bg_desc) 262 critical_error_errno("calloc"); 263 aux_info.xattrs = NULL; 264 } 265 266 void ext4_free_fs_aux_info() 267 { 268 unsigned int i; 269 270 for (i=0; i<aux_info.groups; i++) { 271 if (aux_info.backup_sb[i]) 272 free(aux_info.backup_sb[i]); 273 } 274 free(aux_info.sb_block); 275 free(aux_info.sb_zero); 276 free(aux_info.bg_desc); 277 } 278 279 /* Fill in the superblock memory buffer based on the filesystem parameters */ 280 void ext4_fill_in_sb(int real_uuid) 281 { 282 unsigned int i; 283 struct ext4_super_block *sb = aux_info.sb; 284 285 sb->s_inodes_count = info.inodes_per_group * aux_info.groups; 286 sb->s_blocks_count_lo = aux_info.len_blocks; 287 sb->s_r_blocks_count_lo = 0; 288 sb->s_free_blocks_count_lo = 0; 289 sb->s_free_inodes_count = 0; 290 sb->s_first_data_block = aux_info.first_data_block; 291 sb->s_log_block_size = log_2(info.block_size / 1024); 292 sb->s_obso_log_frag_size = log_2(info.block_size / 1024); 293 sb->s_blocks_per_group = info.blocks_per_group; 294 sb->s_obso_frags_per_group = info.blocks_per_group; 295 sb->s_inodes_per_group = info.inodes_per_group; 296 sb->s_mtime = 0; 297 sb->s_wtime = 0; 298 sb->s_mnt_count = 0; 299 sb->s_max_mnt_count = 10; 300 sb->s_magic = EXT4_SUPER_MAGIC; 301 sb->s_state = EXT4_VALID_FS; 302 sb->s_errors = EXT4_ERRORS_RO; 303 sb->s_minor_rev_level = 0; 304 sb->s_lastcheck = 0; 305 sb->s_checkinterval = 0; 306 sb->s_creator_os = EXT4_OS_LINUX; 307 sb->s_rev_level = EXT4_DYNAMIC_REV; 308 sb->s_def_resuid = EXT4_DEF_RESUID; 309 sb->s_def_resgid = EXT4_DEF_RESGID; 310 311 sb->s_first_ino = EXT4_GOOD_OLD_FIRST_INO; 312 sb->s_inode_size = info.inode_size; 313 sb->s_block_group_nr = 0; 314 sb->s_feature_compat = info.feat_compat; 315 sb->s_feature_incompat = info.feat_incompat; 316 sb->s_feature_ro_compat = info.feat_ro_compat; 317 if (real_uuid == 1) { 318 #ifdef REAL_UUID 319 uuid_generate(sb->s_uuid); 320 #else 321 fprintf(stderr, "Not compiled with real UUID support\n"); 322 abort(); 323 #endif 324 } else { 325 generate_sha1_uuid("extandroid/make_ext4fs", info.label, sb->s_uuid); 326 } 327 memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name)); 328 strncpy(sb->s_volume_name, info.label, sizeof(sb->s_volume_name)); 329 memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted)); 330 sb->s_algorithm_usage_bitmap = 0; 331 332 sb->s_reserved_gdt_blocks = info.bg_desc_reserve_blocks; 333 sb->s_prealloc_blocks = 0; 334 sb->s_prealloc_dir_blocks = 0; 335 336 //memcpy(sb->s_journal_uuid, sb->s_uuid, sizeof(sb->s_journal_uuid)); 337 if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) 338 sb->s_journal_inum = EXT4_JOURNAL_INO; 339 sb->s_journal_dev = 0; 340 sb->s_last_orphan = 0; 341 sb->s_hash_seed[0] = 0; /* FIXME */ 342 sb->s_def_hash_version = DX_HASH_TEA; 343 sb->s_reserved_char_pad = EXT4_JNL_BACKUP_BLOCKS; 344 sb->s_desc_size = sizeof(struct ext2_group_desc); 345 sb->s_default_mount_opts = 0; /* FIXME */ 346 sb->s_first_meta_bg = 0; 347 sb->s_mkfs_time = 0; 348 //sb->s_jnl_blocks[17]; /* FIXME */ 349 350 sb->s_blocks_count_hi = aux_info.len_blocks >> 32; 351 sb->s_r_blocks_count_hi = 0; 352 sb->s_free_blocks_count_hi = 0; 353 sb->s_min_extra_isize = sizeof(struct ext4_inode) - 354 EXT4_GOOD_OLD_INODE_SIZE; 355 sb->s_want_extra_isize = sizeof(struct ext4_inode) - 356 EXT4_GOOD_OLD_INODE_SIZE; 357 sb->s_flags = 2; 358 sb->s_raid_stride = info.flash_logical_block_size / info.block_size; 359 // stride should be the max of 8kb and logical block size 360 if (info.flash_logical_block_size != 0 && info.flash_logical_block_size < 8192) { 361 sb->s_raid_stride = 8192 / info.block_size; 362 } 363 sb->s_mmp_interval = 0; 364 sb->s_mmp_block = 0; 365 sb->s_raid_stripe_width = info.flash_erase_block_size / info.block_size; 366 sb->s_log_groups_per_flex = 0; 367 sb->s_kbytes_written = 0; 368 369 for (i = 0; i < aux_info.groups; i++) { 370 u64 group_start_block = aux_info.first_data_block + i * 371 info.blocks_per_group; 372 u32 header_size = 0; 373 if (ext4_bg_has_super_block(i)) { 374 if (i != 0) { 375 aux_info.backup_sb[i] = calloc(info.block_size, 1); 376 memcpy(aux_info.backup_sb[i], sb, sizeof(struct ext4_super_block)); 377 /* Update the block group nr of this backup superblock */ 378 aux_info.backup_sb[i]->s_block_group_nr = i; 379 ext4_queue_sb(group_start_block, info.block_device ? 380 aux_info.sb_zero : aux_info.backup_sb[i]); 381 } 382 sparse_file_add_data(ext4_sparse_file, aux_info.bg_desc, 383 aux_info.bg_desc_blocks * info.block_size, 384 group_start_block + 1); 385 header_size = 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks; 386 } 387 388 aux_info.bg_desc[i].bg_block_bitmap = group_start_block + header_size; 389 aux_info.bg_desc[i].bg_inode_bitmap = group_start_block + header_size + 1; 390 aux_info.bg_desc[i].bg_inode_table = group_start_block + header_size + 2; 391 392 aux_info.bg_desc[i].bg_free_blocks_count = sb->s_blocks_per_group; 393 aux_info.bg_desc[i].bg_free_inodes_count = sb->s_inodes_per_group; 394 aux_info.bg_desc[i].bg_used_dirs_count = 0; 395 } 396 397 /* Queue the primary superblock to be written out - if it's a block device, 398 * queue a zero-filled block first, the correct version of superblock will 399 * be written to the block device after all other blocks are written. 400 * 401 * The file-system on the block device will not be valid until the correct 402 * version of superblocks are written, this is to avoid the likelihood of a 403 * partially created file-system. 404 */ 405 ext4_queue_sb(aux_info.first_data_block, info.block_device ? 406 aux_info.sb_zero : aux_info.sb_block); 407 } 408 409 410 void ext4_queue_sb(u64 start_block, struct ext4_super_block *sb) 411 { 412 sparse_file_add_data(ext4_sparse_file, sb, info.block_size, start_block); 413 } 414 415 void ext4_parse_sb_info(struct ext4_super_block *sb) 416 { 417 if (sb->s_magic != EXT4_SUPER_MAGIC) 418 error("superblock magic incorrect"); 419 420 if ((sb->s_state & EXT4_VALID_FS) != EXT4_VALID_FS) 421 error("filesystem state not valid"); 422 423 ext4_parse_sb(sb, &info); 424 425 ext4_create_fs_aux_info(); 426 427 memcpy(aux_info.sb, sb, sizeof(*sb)); 428 429 if (aux_info.first_data_block != sb->s_first_data_block) 430 critical_error("first data block does not match"); 431 } 432 433 void ext4_create_resize_inode() 434 { 435 struct block_allocation *reserve_inode_alloc = create_allocation(); 436 u32 reserve_inode_len = 0; 437 unsigned int i; 438 439 struct ext4_inode *inode = get_inode(EXT4_RESIZE_INO); 440 if (inode == NULL) { 441 error("failed to get resize inode"); 442 return; 443 } 444 445 for (i = 0; i < aux_info.groups; i++) { 446 if (ext4_bg_has_super_block(i)) { 447 u64 group_start_block = aux_info.first_data_block + i * 448 info.blocks_per_group; 449 u32 reserved_block_start = group_start_block + 1 + 450 aux_info.bg_desc_blocks; 451 u32 reserved_block_len = info.bg_desc_reserve_blocks; 452 append_region(reserve_inode_alloc, reserved_block_start, 453 reserved_block_len, i); 454 reserve_inode_len += reserved_block_len; 455 } 456 } 457 458 inode_attach_resize(inode, reserve_inode_alloc); 459 460 inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; 461 inode->i_links_count = 1; 462 463 free_alloc(reserve_inode_alloc); 464 } 465 466 /* Allocate the blocks to hold a journal inode and connect them to the 467 reserved journal inode */ 468 void ext4_create_journal_inode() 469 { 470 struct ext4_inode *inode = get_inode(EXT4_JOURNAL_INO); 471 if (inode == NULL) { 472 error("failed to get journal inode"); 473 return; 474 } 475 476 u8 *journal_data = inode_allocate_data_extents(inode, 477 info.journal_blocks * info.block_size, 478 info.journal_blocks * info.block_size); 479 if (!journal_data) { 480 error("failed to allocate extents for journal data"); 481 return; 482 } 483 484 inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; 485 inode->i_links_count = 1; 486 487 journal_superblock_t *jsb = (journal_superblock_t *)journal_data; 488 jsb->s_header.h_magic = htonl(JBD2_MAGIC_NUMBER); 489 jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2); 490 jsb->s_blocksize = htonl(info.block_size); 491 jsb->s_maxlen = htonl(info.journal_blocks); 492 jsb->s_nr_users = htonl(1); 493 jsb->s_first = htonl(1); 494 jsb->s_sequence = htonl(1); 495 496 memcpy(aux_info.sb->s_jnl_blocks, &inode->i_block, sizeof(inode->i_block)); 497 } 498 499 /* Update the number of free blocks and inodes in the filesystem and in each 500 block group */ 501 void ext4_update_free() 502 { 503 u32 i; 504 505 for (i = 0; i < aux_info.groups; i++) { 506 u32 bg_free_blocks = get_free_blocks(i); 507 u32 bg_free_inodes = get_free_inodes(i); 508 u16 crc; 509 510 aux_info.bg_desc[i].bg_free_blocks_count = bg_free_blocks; 511 aux_info.sb->s_free_blocks_count_lo += bg_free_blocks; 512 513 aux_info.bg_desc[i].bg_free_inodes_count = bg_free_inodes; 514 aux_info.sb->s_free_inodes_count += bg_free_inodes; 515 516 aux_info.bg_desc[i].bg_used_dirs_count += get_directories(i); 517 518 aux_info.bg_desc[i].bg_flags = get_bg_flags(i); 519 520 crc = ext4_crc16(~0, aux_info.sb->s_uuid, sizeof(aux_info.sb->s_uuid)); 521 crc = ext4_crc16(crc, &i, sizeof(i)); 522 crc = ext4_crc16(crc, &aux_info.bg_desc[i], offsetof(struct ext2_group_desc, bg_checksum)); 523 aux_info.bg_desc[i].bg_checksum = crc; 524 } 525 } 526 527 u64 get_block_device_size(int fd) 528 { 529 u64 size = 0; 530 int ret; 531 532 #if defined(__linux__) 533 ret = ioctl(fd, BLKGETSIZE64, &size); 534 #elif defined(__APPLE__) && defined(__MACH__) 535 ret = ioctl(fd, DKIOCGETBLOCKCOUNT, &size); 536 #else 537 close(fd); 538 return 0; 539 #endif 540 541 if (ret) 542 return 0; 543 544 return size; 545 } 546 547 int is_block_device_fd(int fd) 548 { 549 #ifdef _WIN32 550 return 0; 551 #else 552 struct stat st; 553 int ret = fstat(fd, &st); 554 if (ret < 0) 555 return 0; 556 557 return S_ISBLK(st.st_mode); 558 #endif 559 } 560 561 u64 get_file_size(int fd) 562 { 563 struct stat buf; 564 int ret; 565 u64 reserve_len = 0; 566 s64 computed_size; 567 568 ret = fstat(fd, &buf); 569 if (ret) 570 return 0; 571 572 if (info.len < 0) 573 reserve_len = -info.len; 574 575 if (S_ISREG(buf.st_mode)) 576 computed_size = buf.st_size - reserve_len; 577 else if (S_ISBLK(buf.st_mode)) 578 computed_size = get_block_device_size(fd) - reserve_len; 579 else 580 computed_size = 0; 581 582 if (computed_size < 0) { 583 warn("Computed filesystem size less than 0"); 584 computed_size = 0; 585 } 586 587 return computed_size; 588 } 589 590 u64 parse_num(const char *arg) 591 { 592 char *endptr; 593 u64 num = strtoull(arg, &endptr, 10); 594 if (*endptr == 'k' || *endptr == 'K') 595 num *= 1024LL; 596 else if (*endptr == 'm' || *endptr == 'M') 597 num *= 1024LL * 1024LL; 598 else if (*endptr == 'g' || *endptr == 'G') 599 num *= 1024LL * 1024LL * 1024LL; 600 601 return num; 602 } 603 604 int read_ext(int fd, int verbose) 605 { 606 off64_t ret; 607 struct ext4_super_block sb; 608 609 read_sb(fd, &sb); 610 611 ext4_parse_sb_info(&sb); 612 613 ret = lseek64(fd, info.len, SEEK_SET); 614 if (ret < 0) 615 critical_error_errno("failed to seek to end of input image"); 616 617 ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET); 618 if (ret < 0) 619 critical_error_errno("failed to seek to block group descriptors"); 620 621 ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks); 622 if (ret < 0) 623 critical_error_errno("failed to read block group descriptors"); 624 if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks) 625 critical_error("failed to read all of block group descriptors"); 626 627 if (verbose) { 628 printf("Found filesystem with parameters:\n"); 629 printf(" Size: %"PRIu64"\n", info.len); 630 printf(" Block size: %d\n", info.block_size); 631 printf(" Blocks per group: %d\n", info.blocks_per_group); 632 printf(" Inodes per group: %d\n", info.inodes_per_group); 633 printf(" Inode size: %d\n", info.inode_size); 634 printf(" Label: %s\n", info.label); 635 printf(" Blocks: %"PRIu64"\n", aux_info.len_blocks); 636 printf(" Block groups: %d\n", aux_info.groups); 637 printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); 638 printf(" Used %d/%d inodes and %d/%d blocks\n", 639 aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, 640 aux_info.sb->s_inodes_count, 641 aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, 642 aux_info.sb->s_blocks_count_lo); 643 } 644 645 return 0; 646 } 647 648