Changeset aab85d90 in mainline for uspace/lib/ext4/src/filesystem.c
- Timestamp:
- 2018-08-27T14:17:14Z (6 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- c606e33, e43d658
- Parents:
- 8867cf6
- git-author:
- Jiri Svoboda <jiri@…> (2018-08-26 17:15:23)
- git-committer:
- Jiri Svoboda <jiri@…> (2018-08-27 14:17:14)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/ext4/src/filesystem.c
r8867cf6 raab85d90 1 1 /* 2 * Copyright (c) 2018 Jiri Svoboda 2 3 * Copyright (c) 2011 Martin Sucha 3 4 * Copyright (c) 2012 Frantisek Princ … … 47 48 #include "ext4/bitmap.h" 48 49 #include "ext4/block_group.h" 50 #include "ext4/directory.h" 49 51 #include "ext4/extent.h" 50 52 #include "ext4/filesystem.h" … … 55 57 56 58 static errno_t ext4_filesystem_check_features(ext4_filesystem_t *, bool *); 59 static errno_t ext4_filesystem_init_block_groups(ext4_filesystem_t *); 60 static errno_t ext4_filesystem_alloc_this_inode(ext4_filesystem_t *, 61 uint32_t, ext4_inode_ref_t **, int); 62 static uint32_t ext4_filesystem_inodes_per_block(ext4_superblock_t *); 57 63 58 64 /** Initialize filesystem for opening. … … 155 161 block_cache_fini(fs->device); 156 162 block_fini(fs->device); 163 } 164 165 /** Create lost+found directory. 166 * 167 * @param fs Filesystem 168 * @return EOK on success or error code 169 */ 170 static errno_t ext4_filesystem_create_lost_found(ext4_filesystem_t *fs, 171 ext4_inode_ref_t *root_dir_ref) 172 { 173 errno_t rc; 174 ext4_inode_ref_t *inode_ref; 175 176 rc = ext4_filesystem_alloc_inode(fs, &inode_ref, L_DIRECTORY); 177 if (rc != EOK) 178 goto error; 179 180 rc = ext4_directory_add_entry(inode_ref, ".", inode_ref); 181 if (rc != EOK) 182 goto error; 183 184 rc = ext4_directory_add_entry(inode_ref, "..", root_dir_ref); 185 if (rc != EOK) 186 goto error; 187 188 rc = ext4_directory_add_entry(root_dir_ref, "lost+found", inode_ref); 189 if (rc != EOK) 190 goto error; 191 192 inode_ref->dirty = true; 193 194 uint16_t nlinks = ext4_inode_get_links_count(inode_ref->inode); 195 ext4_inode_set_links_count(inode_ref->inode, nlinks + 1); 196 197 rc = ext4_filesystem_put_inode_ref(inode_ref); 198 if (rc != EOK) 199 goto error; 200 201 error: 202 return rc; 203 } 204 205 /** Create root directory. 206 * 207 * @param fs Filesystem 208 * @return EOK on success or error code 209 */ 210 static errno_t ext4_filesystem_create_root_dir(ext4_filesystem_t *fs) 211 { 212 errno_t rc; 213 ext4_inode_ref_t *inode_ref; 214 215 rc = ext4_filesystem_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, 216 &inode_ref); 217 if (rc != EOK) 218 goto error; 219 220 inode_ref->dirty = true; 221 222 rc = ext4_directory_add_entry(inode_ref, ".", inode_ref); 223 if (rc != EOK) 224 goto error; 225 226 rc = ext4_directory_add_entry(inode_ref, "..", inode_ref); 227 if (rc != EOK) 228 goto error; 229 230 uint16_t nlinks = ext4_inode_get_links_count(inode_ref->inode); 231 ext4_inode_set_links_count(inode_ref->inode, nlinks + 1); 232 233 rc = ext4_filesystem_create_lost_found(fs, inode_ref); 234 if (rc != EOK) 235 goto error; 236 237 nlinks = ext4_inode_get_links_count(inode_ref->inode); 238 ext4_inode_set_links_count(inode_ref->inode, nlinks + 1); 239 240 rc = ext4_filesystem_put_inode_ref(inode_ref); 241 if (rc != EOK) 242 goto error; 243 244 error: 245 return rc; 246 } 247 248 /** Create new filesystem. 249 * 250 * @param service_id Block device where to create new filesystem 251 */ 252 errno_t ext4_filesystem_create(service_id_t service_id) 253 { 254 errno_t rc; 255 ext4_superblock_t *superblock = NULL; 256 ext4_filesystem_t *fs = NULL; 257 size_t dev_bsize; 258 aoff64_t dev_nblocks; 259 ext4_inode_ref_t *inode_ref = NULL; 260 bool block_inited = false; 261 bool fs_inited = false; 262 uint32_t idx; 263 264 /* Initialize block library (4096 is size of communication channel) */ 265 rc = block_init(service_id, 4096); 266 if (rc != EOK) 267 goto err; 268 269 block_inited = true; 270 271 /* Get device block size */ 272 rc = block_get_bsize(service_id, &dev_bsize); 273 if (rc != EOK) 274 goto err; 275 276 /* Get device number of blocks */ 277 rc = block_get_nblocks(service_id, &dev_nblocks); 278 if (rc != EOK) 279 goto err; 280 281 /* Create superblock */ 282 rc = ext4_superblock_create(dev_bsize, dev_nblocks, &superblock); 283 if (rc != EOK) 284 goto err; 285 286 /* Write superblock to device */ 287 rc = ext4_superblock_write_direct(service_id, superblock); 288 if (rc != EOK) 289 goto err; 290 291 block_fini(service_id); 292 block_inited = false; 293 ext4_superblock_release(superblock); 294 superblock = NULL; 295 296 fs = calloc(1, sizeof(ext4_filesystem_t)); 297 if (fs == NULL) 298 goto err; 299 300 /* Open file system */ 301 rc = ext4_filesystem_init(fs, service_id, CACHE_MODE_WT); 302 if (rc != EOK) 303 goto err; 304 305 fs_inited = true; 306 307 /* Init block groups */ 308 rc = ext4_filesystem_init_block_groups(fs); 309 if (rc != EOK) 310 goto err; 311 312 /* Reserved i-nodes */ 313 for (idx = 1; idx < EXT4_REV0_FIRST_INO; idx++) { 314 if (idx == EXT4_INODE_ROOT_INDEX) { 315 rc = ext4_filesystem_alloc_this_inode(fs, idx, 316 &inode_ref, L_DIRECTORY); 317 if (rc != EOK) 318 goto error; 319 320 rc = ext4_filesystem_put_inode_ref(inode_ref); 321 if (rc != EOK) 322 goto error; 323 } else { 324 /* Allocate inode by allocation algorithm */ 325 errno_t rc = ext4_ialloc_alloc_this_inode(fs, idx, 326 false); 327 if (rc != EOK) 328 return rc; 329 330 rc = ext4_filesystem_get_inode_ref(fs, idx, 331 &inode_ref); 332 if (rc != EOK) 333 goto error; 334 335 memset(inode_ref->inode, 0, ext4_superblock_get_inode_size(fs->superblock)); 336 inode_ref->dirty = true; 337 338 rc = ext4_filesystem_put_inode_ref(inode_ref); 339 if (rc != EOK) 340 goto error; 341 } 342 } 343 344 /* Create root directory */ 345 rc = ext4_filesystem_create_root_dir(fs); 346 if (rc != EOK) 347 goto err; 348 349 /* Write superblock to device */ 350 rc = ext4_superblock_write_direct(service_id, fs->superblock); 351 if (rc != EOK) 352 goto err; 353 354 ext4_filesystem_fini(fs); 355 free(fs); 356 return EOK; 357 err: 358 if (fs_inited) 359 ext4_filesystem_fini(fs); 360 if (fs != NULL) 361 free(fs); 362 if (superblock != NULL) 363 ext4_superblock_release(superblock); 364 if (block_inited) 365 block_fini(service_id); 366 return rc; 367 error: 368 return rc; 157 369 } 158 370 … … 373 585 } 374 586 587 /** Initialize block group structures 588 */ 589 static errno_t ext4_filesystem_init_block_groups(ext4_filesystem_t *fs) 590 { 591 errno_t rc; 592 block_t *block; 593 aoff64_t b; 594 ext4_block_group_t *bg; 595 ext4_superblock_t *sb = fs->superblock; 596 ext4_block_group_ref_t *bg_ref; 597 598 uint32_t block_group_count = ext4_superblock_get_block_group_count(sb); 599 uint32_t block_size = ext4_superblock_get_block_size(sb); 600 uint32_t desc_size = ext4_superblock_get_desc_size(fs->superblock); 601 /* Number of descriptors per block */ 602 uint32_t descriptors_per_block = 603 ext4_superblock_get_block_size(fs->superblock) / desc_size; 604 /* Block where block group descriptor (and first block group) starts */ 605 aoff64_t block_id = 606 ext4_superblock_get_first_data_block(fs->superblock) + 1; 607 /* Number of blocks containing descriptor table */ 608 uint32_t dtable_blocks = 609 (block_group_count + descriptors_per_block - 1) / 610 descriptors_per_block; 611 612 uint32_t bg_index; 613 aoff64_t bg_block0; 614 uint32_t dcnt; 615 uint32_t i; 616 uint32_t now; 617 618 aoff64_t block_bitmap; 619 aoff64_t inode_bitmap; 620 aoff64_t inode_table; 621 uint32_t free_blocks; 622 uint32_t free_inodes; 623 uint32_t used_dirs; 624 uint32_t reserved; 625 uint32_t inode_table_blocks; 626 627 dcnt = block_group_count; 628 629 /* Fill in block descriptors */ 630 b = block_id; 631 bg_index = 0; 632 bg_block0 = block_id; 633 while (dcnt > 0) { 634 rc = block_get(&block, fs->device, b, BLOCK_FLAGS_NOREAD); 635 if (rc != EOK) 636 return rc; 637 638 if (dcnt > descriptors_per_block) 639 now = descriptors_per_block; 640 else 641 now = dcnt; 642 643 memset(block->data, 0, block_size); 644 645 for (i = 0; i < now; i++) { 646 bg = (ext4_block_group_t *) (block->data + i * 647 desc_size); 648 649 block_bitmap = bg_block0 + dtable_blocks; 650 inode_bitmap = block_bitmap + 1; 651 inode_table = inode_bitmap + 1; 652 653 free_blocks = ext4_superblock_get_blocks_in_group(sb, 654 bg_index); 655 656 free_inodes = 657 ext4_filesystem_bg_get_itable_size(sb, bg_index) * 658 ext4_filesystem_inodes_per_block(sb); 659 used_dirs = 0; 660 661 ext4_block_group_set_block_bitmap(bg, sb, block_bitmap); 662 ext4_block_group_set_inode_bitmap(bg, sb, inode_bitmap); 663 ext4_block_group_set_inode_table_first_block(bg, sb, 664 inode_table); 665 ext4_block_group_set_free_blocks_count(bg, sb, 666 free_blocks); 667 ext4_block_group_set_free_inodes_count(bg, sb, 668 free_inodes); 669 ext4_block_group_set_used_dirs_count(bg, sb, 670 used_dirs); 671 672 /// XX Lazy 673 ext4_block_group_set_flag(bg, 674 EXT4_BLOCK_GROUP_BLOCK_UNINIT); 675 ext4_block_group_set_flag(bg, 676 EXT4_BLOCK_GROUP_INODE_UNINIT); 677 678 bg_index++; 679 bg_block0 += ext4_superblock_get_blocks_per_group(sb); 680 } 681 682 block->dirty = true; 683 684 rc = block_put(block); 685 if (rc != EOK) 686 return rc; 687 688 ++b; 689 dcnt -= now; 690 } 691 692 /* This initializes the bitmaps and inode table */ 693 for (bg_index = 0; bg_index < block_group_count; bg_index++) { 694 rc = ext4_filesystem_get_block_group_ref(fs, bg_index, &bg_ref); 695 if (rc != EOK) 696 return rc; 697 698 /* 699 * Adjust number of free blocks 700 */ 701 free_blocks = ext4_superblock_get_blocks_in_group(sb, bg_index); 702 reserved = ext4_filesystem_bg_get_backup_blocks(bg_ref); 703 inode_table_blocks = ext4_filesystem_bg_get_itable_size(sb, 704 bg_ref->index); 705 /* One for block bitmap one for inode bitmap */ 706 free_blocks = free_blocks - reserved - 2 - inode_table_blocks; 707 708 ext4_block_group_set_free_blocks_count(bg_ref->block_group, 709 sb, free_blocks); 710 bg_ref->dirty = true; 711 712 rc = ext4_filesystem_put_block_group_ref(bg_ref); 713 if (rc != EOK) 714 return rc; 715 } 716 717 return EOK; 718 } 719 375 720 /** Initialize block bitmap in block group. 376 721 * … … 392 737 uint64_t bitmap_inode_addr = ext4_block_group_get_inode_bitmap( 393 738 bg_ref->block_group, bg_ref->fs->superblock); 739 uint32_t blocks_group = ext4_superblock_get_blocks_per_group(sb); 740 uint32_t bg_blocks = ext4_superblock_get_blocks_in_group(sb, 741 bg_ref->index); 394 742 395 743 block_t *bitmap_block; … … 428 776 itb = ext4_block_group_get_inode_table_first_block(bg_ref->block_group, 429 777 sb); 430 sz = ext4_filesystem_bg_get_itable_size(sb, bg_ref );778 sz = ext4_filesystem_bg_get_itable_size(sb, bg_ref->index); 431 779 432 780 for (i = 0; i < sz; ++i, ++itb) { … … 438 786 } 439 787 788 /* For last group need to mark blocks which are outside of the FS */ 789 for (uint32_t block = bg_blocks; block < blocks_group; block++) { 790 ext4_bitmap_set_bit(bitmap, block); 791 } 792 440 793 bitmap_block->dirty = true; 441 794 … … 498 851 ext4_superblock_t *sb = bg_ref->fs->superblock; 499 852 500 uint32_t inode_size = ext4_superblock_get_inode_size(sb);501 853 uint32_t block_size = ext4_superblock_get_block_size(sb); 502 uint32_t inodes_per_block = block_size / inode_size;854 uint32_t inodes_per_block = ext4_filesystem_inodes_per_block(sb); 503 855 504 856 uint32_t inodes_in_group = … … 612 964 EXT4_BLOCK_GROUP_ITABLE_ZEROED)) { 613 965 rc = ext4_filesystem_init_inode_table(newref); 614 if (rc != EOK) 966 if (rc != EOK) { 967 block_put(newref->block); 968 free(newref); 615 969 return rc; 970 } 616 971 617 972 ext4_block_group_set_flag(newref->block_group, … … 676 1031 /** Get the size of the block group's inode table 677 1032 * 678 * @param sb Pointer to the superblock679 * @param bg_ ref Pointer to the block group reference680 * 681 * @return Size of the inode table in blocks.1033 * @param sb Pointer to the superblock 1034 * @param bg_index Block group index 1035 * 1036 * @return Size of the inode table in blocks. 682 1037 */ 683 1038 uint32_t ext4_filesystem_bg_get_itable_size(ext4_superblock_t *sb, 684 ext4_block_group_ref_t *bg_ref)1039 uint32_t bg_index) 685 1040 { 686 1041 uint32_t itable_size; … … 690 1045 uint32_t block_size = ext4_superblock_get_block_size(sb); 691 1046 692 if (bg_ ref->index < block_group_count - 1) {1047 if (bg_index < block_group_count - 1) { 693 1048 itable_size = inodes_per_group * inode_table_item_size; 694 1049 } else { … … 703 1058 } 704 1059 705 /* Check if n is a power of p */706 static bool is_power_of(uint32_t n, unsigned p)707 {708 if (p == 1 && n != p)709 return false;710 711 while (n != p) {712 if (n < p)713 return false;714 else if ((n % p) != 0)715 return false;716 717 n /= p;718 }719 720 return true;721 }722 723 1060 /** Get the number of blocks used by superblock + gdt + reserved gdt backups 724 1061 * … … 729 1066 uint32_t ext4_filesystem_bg_get_backup_blocks(ext4_block_group_ref_t *bg) 730 1067 { 731 uint32_t const idx = bg->index; 732 uint32_t r = 0; 733 bool has_backups = false; 734 ext4_superblock_t *sb = bg->fs->superblock; 735 736 /* First step: determine if the block group contains the backups */ 737 738 if (idx <= 1) 739 has_backups = true; 740 else { 741 if (ext4_superblock_has_feature_compatible(sb, 742 EXT4_FEATURE_COMPAT_SPARSE_SUPER2)) { 743 uint32_t g1, g2; 744 745 ext4_superblock_get_backup_groups_sparse2(sb, 746 &g1, &g2); 747 748 if (idx == g1 || idx == g2) 749 has_backups = true; 750 } else if (!ext4_superblock_has_feature_read_only(sb, 751 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER)) { 752 /* 753 * Very old fs were all block groups have 754 * superblock and block descriptors backups. 755 */ 756 has_backups = true; 757 } else { 758 if ((idx & 1) && (is_power_of(idx, 3) || 759 is_power_of(idx, 5) || is_power_of(idx, 7))) 760 has_backups = true; 761 } 762 } 763 764 if (has_backups) { 765 uint32_t bg_count; 766 uint32_t bg_desc_sz; 767 uint32_t gdt_table; /* Size of the GDT in blocks */ 768 uint32_t block_size = ext4_superblock_get_block_size(sb); 769 770 /* 771 * Now we know that this block group has backups, 772 * we have to compute how many blocks are reserved 773 * for them 774 */ 775 776 if (idx == 0 && block_size == 1024) { 777 /* 778 * Special case for first group were the boot block 779 * resides 780 */ 781 r++; 782 } 783 784 /* This accounts for the superblock */ 785 r++; 786 787 /* Add the number of blocks used for the GDT */ 788 bg_count = ext4_superblock_get_block_group_count(sb); 789 bg_desc_sz = ext4_superblock_get_desc_size(sb); 790 gdt_table = ROUND_UP(bg_count * bg_desc_sz, block_size) / 791 block_size; 792 793 r += gdt_table; 794 795 /* And now the number of reserved GDT blocks */ 796 r += ext4_superblock_get_reserved_gdt_blocks(sb); 797 } 798 799 return r; 1068 return ext4_superblock_get_group_backup_blocks(bg->fs->superblock, 1069 bg->index); 800 1070 } 801 1071 … … 927 1197 } 928 1198 929 /** Allocate new i-node in the filesystem. 930 * 931 * @param fs Filesystem to allocated i-node on 1199 /** Initialize newly allocated i-node in the filesystem. 1200 * 1201 * @param fs Filesystem to initialize i-node on 1202 * @param index I-node index 932 1203 * @param inode_ref Output pointer to return reference to allocated i-node 933 1204 * @param flags Flags to be set for newly created i-node … … 936 1207 * 937 1208 */ 938 errno_t ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,1209 static errno_t ext4_filesystem_init_inode(ext4_filesystem_t *fs, uint32_t index, 939 1210 ext4_inode_ref_t **inode_ref, int flags) 940 1211 { … … 944 1215 is_dir = true; 945 1216 946 /* Allocate inode by allocation algorithm */947 uint32_t index;948 errno_t rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);949 if (rc != EOK)950 return rc;951 952 1217 /* Load i-node from on-disk i-node table */ 953 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);1218 errno_t rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref); 954 1219 if (rc != EOK) { 955 1220 ext4_ialloc_free_inode(fs, index, is_dir); … … 1021 1286 } 1022 1287 1288 /** Allocate new i-node in the filesystem. 1289 * 1290 * @param fs Filesystem to allocated i-node on 1291 * @param inode_ref Output pointer to return reference to allocated i-node 1292 * @param flags Flags to be set for newly created i-node 1293 * 1294 * @return Error code 1295 * 1296 */ 1297 errno_t ext4_filesystem_alloc_inode(ext4_filesystem_t *fs, 1298 ext4_inode_ref_t **inode_ref, int flags) 1299 { 1300 /* Check if newly allocated i-node will be a directory */ 1301 bool is_dir = false; 1302 if (flags & L_DIRECTORY) 1303 is_dir = true; 1304 1305 /* Allocate inode by allocation algorithm */ 1306 uint32_t index; 1307 errno_t rc = ext4_ialloc_alloc_inode(fs, &index, is_dir); 1308 if (rc != EOK) 1309 return rc; 1310 1311 rc = ext4_filesystem_init_inode(fs, index, inode_ref, flags); 1312 if (rc != EOK) { 1313 ext4_ialloc_free_inode(fs, index, is_dir); 1314 return rc; 1315 } 1316 1317 return EOK; 1318 } 1319 1320 /** Allocate specific i-node in the filesystem. 1321 * 1322 * @param fs Filesystem to allocated i-node on 1323 * @param index Index of i-node to allocate 1324 * @param inode_ref Output pointer to return reference to allocated i-node 1325 * @param flags Flags to be set for newly created i-node 1326 * 1327 * @return Error code 1328 * 1329 */ 1330 static errno_t ext4_filesystem_alloc_this_inode(ext4_filesystem_t *fs, 1331 uint32_t index, ext4_inode_ref_t **inode_ref, int flags) 1332 { 1333 /* Check if newly allocated i-node will be a directory */ 1334 bool is_dir = false; 1335 if (flags & L_DIRECTORY) 1336 is_dir = true; 1337 1338 /* Allocate inode by allocation algorithm */ 1339 errno_t rc = ext4_ialloc_alloc_this_inode(fs, index, is_dir); 1340 if (rc != EOK) 1341 return rc; 1342 1343 rc = ext4_filesystem_init_inode(fs, index, inode_ref, flags); 1344 if (rc != EOK) { 1345 ext4_ialloc_free_inode(fs, index, is_dir); 1346 return rc; 1347 } 1348 1349 return EOK; 1350 } 1351 1023 1352 /** Release i-node and mark it as free. 1024 1353 * … … 1674 2003 } 1675 2004 2005 /** Get the number of inodes per block. 2006 * 2007 * @param sb Superblock 2008 * @return Number of inodes per block 2009 */ 2010 static uint32_t ext4_filesystem_inodes_per_block(ext4_superblock_t *sb) 2011 { 2012 uint32_t inode_size = ext4_superblock_get_inode_size(sb); 2013 uint32_t block_size = ext4_superblock_get_block_size(sb); 2014 2015 return block_size / inode_size; 2016 } 2017 1676 2018 /** 1677 2019 * @}
Note:
See TracChangeset
for help on using the changeset viewer.