Changeset a35b458 in mainline for uspace/lib/ext4
- Timestamp:
- 2018-03-02T20:10:49Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- f1380b7
- Parents:
- 3061bc1
- git-author:
- Jiří Zárevúcky <zarevucky.jiri@…> (2018-02-28 17:38:31)
- git-committer:
- Jiří Zárevúcky <zarevucky.jiri@…> (2018-03-02 20:10:49)
- Location:
- uspace/lib/ext4
- Files:
-
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/ext4/include/ext4/types.h
r3061bc1 ra35b458 66 66 uint16_t def_resuid; /* Default uid for reserved blocks */ 67 67 uint16_t def_resgid; /* Default gid for reserved blocks */ 68 68 69 69 /* Fields for EXT4_DYNAMIC_REV superblocks only. */ 70 70 uint32_t first_inode; /* First non-reserved inode */ … … 78 78 char last_mounted[64]; /* Directory where last mounted */ 79 79 uint32_t algorithm_usage_bitmap; /* For compression */ 80 80 81 81 /* 82 82 * Performance hints. Directory preallocation should only … … 86 86 uint8_t prealloc_dir_blocks; /* Number to preallocate for dirs */ 87 87 uint16_t reserved_gdt_blocks; /* Per group desc for online growth */ 88 88 89 89 /* 90 90 * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set. … … 251 251 uint16_t itable_unused_lo; /* Unused inodes count */ 252 252 uint16_t checksum; /* crc16(sb_uuid+group+desc) */ 253 253 254 254 uint32_t block_bitmap_hi; /* Blocks bitmap block MSB */ 255 255 uint32_t inode_bitmap_hi; /* I-nodes bitmap block MSB */ … … 307 307 uint32_t size_hi; 308 308 uint32_t obso_faddr; /* Obsoleted fragment address */ 309 309 310 310 union { 311 311 struct { … … 324 324 } hurd2; 325 325 } __attribute__ ((packed)) osd2; 326 326 327 327 uint16_t extra_isize; 328 328 uint16_t pad1; … … 403 403 uint16_t entry_length; /* Distance to the next directory entry */ 404 404 uint8_t name_length; /* Lower 8 bits of name length */ 405 405 406 406 union { 407 407 uint8_t name_length_high; /* Higher 8 bits of name length */ 408 408 uint8_t inode_type; /* Type of referenced inode (in rev >= 0.5) */ 409 409 } __attribute__ ((packed)); 410 410 411 411 uint8_t name[EXT4_DIRECTORY_FILENAME_LEN]; /* Entry name */ 412 412 } __attribute__((packed)) ext4_directory_entry_ll_t; … … 495 495 typedef struct ext4_extent_index { 496 496 uint32_t first_block; /* Index covers logical blocks from 'block' */ 497 497 498 498 /** 499 499 * Pointer to the physical block of the next -
uspace/lib/ext4/src/balloc.c
r3061bc1 ra35b458 57 57 ext4_filesystem_t *fs = inode_ref->fs; 58 58 ext4_superblock_t *sb = fs->superblock; 59 59 60 60 /* Compute indexes */ 61 61 uint32_t block_group = ext4_filesystem_blockaddr2group(sb, block_addr); 62 62 uint32_t index_in_group = 63 63 ext4_filesystem_blockaddr2_index_in_group(sb, block_addr); 64 64 65 65 /* Load block group reference */ 66 66 ext4_block_group_ref_t *bg_ref; … … 68 68 if (rc != EOK) 69 69 return rc; 70 70 71 71 /* Load block with bitmap */ 72 72 uint32_t bitmap_block_addr = … … 78 78 return rc; 79 79 } 80 80 81 81 /* Modify bitmap */ 82 82 ext4_bitmap_free_bit(bitmap_block->data, index_in_group); 83 83 bitmap_block->dirty = true; 84 84 85 85 /* Release block with bitmap */ 86 86 rc = block_put(bitmap_block); … … 90 90 return rc; 91 91 } 92 92 93 93 uint32_t block_size = ext4_superblock_get_block_size(sb); 94 94 95 95 /* Update superblock free blocks count */ 96 96 uint32_t sb_free_blocks = … … 98 98 sb_free_blocks++; 99 99 ext4_superblock_set_free_blocks_count(sb, sb_free_blocks); 100 100 101 101 /* Update inode blocks count */ 102 102 uint64_t ino_blocks = … … 105 105 ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks); 106 106 inode_ref->dirty = true; 107 107 108 108 /* Update block group free blocks count */ 109 109 uint32_t free_blocks = … … 113 113 sb, free_blocks); 114 114 bg_ref->dirty = true; 115 115 116 116 /* Release block group reference */ 117 117 return ext4_filesystem_put_block_group_ref(bg_ref); … … 346 346 { 347 347 uint32_t allocated_block = 0; 348 348 349 349 uint32_t bitmap_block_addr; 350 350 block_t *bitmap_block; … … 352 352 uint32_t free_blocks; 353 353 uint32_t goal; 354 354 355 355 /* Find GOAL */ 356 356 errno_t rc = ext4_balloc_find_goal(inode_ref, &goal); … … 359 359 360 360 ext4_superblock_t *sb = inode_ref->fs->superblock; 361 361 362 362 /* Load block group number for goal and relative index */ 363 363 uint32_t block_group = ext4_filesystem_blockaddr2group(sb, goal); 364 364 uint32_t index_in_group = 365 365 ext4_filesystem_blockaddr2_index_in_group(sb, goal); 366 366 367 367 /* Load block group reference */ 368 368 ext4_block_group_ref_t *bg_ref; … … 378 378 goto goal_failed; 379 379 } 380 380 381 381 /* Compute indexes */ 382 382 uint32_t first_in_group = 383 383 ext4_balloc_get_first_data_block_in_group(sb, bg_ref); 384 384 385 385 uint32_t first_in_group_index = 386 386 ext4_filesystem_blockaddr2_index_in_group(sb, first_in_group); 387 387 388 388 if (index_in_group < first_in_group_index) 389 389 index_in_group = first_in_group_index; 390 390 391 391 /* Load block with bitmap */ 392 392 bitmap_block_addr = 393 393 ext4_block_group_get_block_bitmap(bg_ref->block_group, sb); 394 394 395 395 rc = block_get(&bitmap_block, inode_ref->fs->device, 396 396 bitmap_block_addr, BLOCK_FLAGS_NONE); … … 399 399 return rc; 400 400 } 401 401 402 402 /* Check if goal is free */ 403 403 if (ext4_bitmap_is_free_bit(bitmap_block->data, index_in_group)) { … … 409 409 return rc; 410 410 } 411 411 412 412 allocated_block = 413 413 ext4_filesystem_index_in_group2blockaddr(sb, index_in_group, 414 414 block_group); 415 415 416 416 goto success; 417 417 } 418 418 419 419 uint32_t blocks_in_group = 420 420 ext4_superblock_get_blocks_in_group(sb, block_group); 421 421 422 422 uint32_t end_idx = (index_in_group + 63) & ~63; 423 423 if (end_idx > blocks_in_group) 424 424 end_idx = blocks_in_group; 425 425 426 426 /* Try to find free block near to goal */ 427 427 for (uint32_t tmp_idx = index_in_group + 1; tmp_idx < end_idx; … … 433 433 if (rc != EOK) 434 434 return rc; 435 435 436 436 allocated_block = 437 437 ext4_filesystem_index_in_group2blockaddr(sb, tmp_idx, 438 438 block_group); 439 439 440 440 goto success; 441 441 } 442 442 } 443 443 444 444 /* Find free BYTE in bitmap */ 445 445 rc = ext4_bitmap_find_free_byte_and_set_bit(bitmap_block->data, … … 450 450 if (rc != EOK) 451 451 return rc; 452 452 453 453 allocated_block = 454 454 ext4_filesystem_index_in_group2blockaddr(sb, rel_block_idx, 455 455 block_group); 456 456 457 457 goto success; 458 458 } 459 459 460 460 /* Find free bit in bitmap */ 461 461 rc = ext4_bitmap_find_free_bit_and_set(bitmap_block->data, … … 466 466 if (rc != EOK) 467 467 return rc; 468 468 469 469 allocated_block = 470 470 ext4_filesystem_index_in_group2blockaddr(sb, rel_block_idx, 471 471 block_group); 472 472 473 473 goto success; 474 474 } 475 475 476 476 /* No free block found yet */ 477 477 rc = block_put(bitmap_block); … … 486 486 if (rc != EOK) 487 487 return rc; 488 488 489 489 /* Try other block groups */ 490 490 uint32_t block_group_count = ext4_superblock_get_block_group_count(sb); 491 491 492 492 uint32_t bgid = (block_group + 1) % block_group_count; 493 493 uint32_t count = block_group_count; 494 494 495 495 while (count > 0) { 496 496 rc = ext4_filesystem_get_block_group_ref(inode_ref->fs, bgid, … … 509 509 bitmap_block_addr = 510 510 ext4_block_group_get_block_bitmap(bg_ref->block_group, sb); 511 511 512 512 rc = block_get(&bitmap_block, inode_ref->fs->device, 513 513 bitmap_block_addr, 0); … … 516 516 return rc; 517 517 } 518 518 519 519 /* Compute indexes */ 520 520 first_in_group = … … 523 523 ext4_filesystem_blockaddr2_index_in_group(sb, first_in_group); 524 524 blocks_in_group = ext4_superblock_get_blocks_in_group(sb, bgid); 525 525 526 526 first_in_group_index = 527 527 ext4_filesystem_blockaddr2_index_in_group(sb, first_in_group); 528 528 529 529 if (index_in_group < first_in_group_index) 530 530 index_in_group = first_in_group_index; 531 531 532 532 /* Try to find free byte in bitmap */ 533 533 rc = ext4_bitmap_find_free_byte_and_set_bit(bitmap_block->data, … … 540 540 return rc; 541 541 } 542 542 543 543 allocated_block = 544 544 ext4_filesystem_index_in_group2blockaddr(sb, rel_block_idx, 545 545 bgid); 546 546 547 547 goto success; 548 548 } 549 549 550 550 /* Try to find free bit in bitmap */ 551 551 rc = ext4_bitmap_find_free_bit_and_set(bitmap_block->data, … … 558 558 return rc; 559 559 } 560 560 561 561 allocated_block = 562 562 ext4_filesystem_index_in_group2blockaddr(sb, rel_block_idx, 563 563 bgid); 564 564 565 565 goto success; 566 566 } 567 567 568 568 rc = block_put(bitmap_block); 569 569 if (rc != EOK) { … … 576 576 if (rc != EOK) 577 577 return rc; 578 578 579 579 /* Goto next group */ 580 580 bgid = (bgid + 1) % block_group_count; 581 581 count--; 582 582 } 583 583 584 584 return ENOSPC; 585 585 586 586 success: 587 587 /* Empty command - because of syntax */ 588 588 ; 589 589 590 590 uint32_t block_size = ext4_superblock_get_block_size(sb); 591 591 592 592 /* Update superblock free blocks count */ 593 593 uint32_t sb_free_blocks = ext4_superblock_get_free_blocks_count(sb); 594 594 sb_free_blocks--; 595 595 ext4_superblock_set_free_blocks_count(sb, sb_free_blocks); 596 596 597 597 /* Update inode blocks (different block size!) count */ 598 598 uint64_t ino_blocks = … … 601 601 ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks); 602 602 inode_ref->dirty = true; 603 603 604 604 /* Update block group free blocks count */ 605 605 uint32_t bg_free_blocks = … … 609 609 bg_free_blocks); 610 610 bg_ref->dirty = true; 611 611 612 612 rc = ext4_filesystem_put_block_group_ref(bg_ref); 613 613 614 614 *fblock = allocated_block; 615 615 return rc; … … 629 629 { 630 630 errno_t rc; 631 631 632 632 ext4_filesystem_t *fs = inode_ref->fs; 633 633 ext4_superblock_t *sb = fs->superblock; 634 634 635 635 /* Compute indexes */ 636 636 uint32_t block_group = ext4_filesystem_blockaddr2group(sb, fblock); 637 637 uint32_t index_in_group = 638 638 ext4_filesystem_blockaddr2_index_in_group(sb, fblock); 639 639 640 640 /* Load block group reference */ 641 641 ext4_block_group_ref_t *bg_ref; … … 643 643 if (rc != EOK) 644 644 return rc; 645 645 646 646 /* Load block with bitmap */ 647 647 uint32_t bitmap_block_addr = … … 653 653 return rc; 654 654 } 655 655 656 656 /* Check if block is free */ 657 657 *free = ext4_bitmap_is_free_bit(bitmap_block->data, index_in_group); 658 658 659 659 /* Allocate block if possible */ 660 660 if (*free) { … … 662 662 bitmap_block->dirty = true; 663 663 } 664 664 665 665 /* Release block with bitmap */ 666 666 rc = block_put(bitmap_block); … … 670 670 return rc; 671 671 } 672 672 673 673 /* If block is not free, return */ 674 674 if (!(*free)) 675 675 goto terminate; 676 676 677 677 uint32_t block_size = ext4_superblock_get_block_size(sb); 678 678 679 679 /* Update superblock free blocks count */ 680 680 uint32_t sb_free_blocks = ext4_superblock_get_free_blocks_count(sb); 681 681 sb_free_blocks--; 682 682 ext4_superblock_set_free_blocks_count(sb, sb_free_blocks); 683 683 684 684 /* Update inode blocks count */ 685 685 uint64_t ino_blocks = … … 688 688 ext4_inode_set_blocks_count(sb, inode_ref->inode, ino_blocks); 689 689 inode_ref->dirty = true; 690 690 691 691 /* Update block group free blocks count */ 692 692 uint32_t free_blocks = … … 696 696 sb, free_blocks); 697 697 bg_ref->dirty = true; 698 698 699 699 terminate: 700 700 return ext4_filesystem_put_block_group_ref(bg_ref); -
uspace/lib/ext4/src/bitmap.c
r3061bc1 ra35b458 52 52 uint32_t byte_index = index / 8; 53 53 uint32_t bit_index = index % 8; 54 54 55 55 uint8_t *target = bitmap + byte_index; 56 56 57 57 *target &= ~ (1 << bit_index); 58 58 } … … 73 73 uint32_t remaining = count; 74 74 uint32_t byte_index; 75 75 76 76 /* Align index to multiple of 8 */ 77 77 while (((idx % 8) != 0) && (remaining > 0)) { 78 78 byte_index = idx / 8; 79 79 uint32_t bit_index = idx % 8; 80 80 81 81 target = bitmap + byte_index; 82 82 *target &= ~ (1 << bit_index); 83 83 84 84 idx++; 85 85 remaining--; 86 86 } 87 87 88 88 /* For < 8 bits this check necessary */ 89 89 if (remaining == 0) 90 90 return; 91 91 92 92 assert((idx % 8) == 0); 93 93 94 94 byte_index = idx / 8; 95 95 target = bitmap + byte_index; 96 96 97 97 /* Zero the whole bytes */ 98 98 while (remaining >= 8) { 99 99 *target = 0; 100 100 101 101 idx += 8; 102 102 remaining -= 8; 103 103 target++; 104 104 } 105 105 106 106 assert(remaining < 8); 107 107 108 108 /* Zero remaining bytes */ 109 109 while (remaining != 0) { 110 110 byte_index = idx / 8; 111 111 uint32_t bit_index = idx % 8; 112 112 113 113 target = bitmap + byte_index; 114 114 *target &= ~ (1 << bit_index); 115 115 116 116 idx++; 117 117 remaining--; … … 129 129 uint32_t byte_index = index / 8; 130 130 uint32_t bit_index = index % 8; 131 131 132 132 uint8_t *target = bitmap + byte_index; 133 133 134 134 *target |= 1 << bit_index; 135 135 } … … 147 147 uint32_t byte_index = index / 8; 148 148 uint32_t bit_index = index % 8; 149 149 150 150 uint8_t *target = bitmap + byte_index; 151 151 152 152 if (*target & (1 << bit_index)) 153 153 return false; … … 173 173 { 174 174 uint32_t idx; 175 175 176 176 /* Align idx */ 177 177 if (start % 8) … … 179 179 else 180 180 idx = start; 181 181 182 182 uint8_t *pos = bitmap + (idx / 8); 183 183 184 184 /* Try to find free byte */ 185 185 while (idx < max) { 186 186 if (*pos == 0) { 187 187 *pos |= 1; 188 188 189 189 *index = idx; 190 190 return EOK; 191 191 } 192 192 193 193 idx += 8; 194 194 ++pos; 195 195 } 196 196 197 197 /* Free byte not found */ 198 198 return ENOSPC; … … 217 217 uint32_t idx = start_idx; 218 218 bool byte_part = false; 219 219 220 220 /* Check the rest of first byte */ 221 221 while ((idx % 8) != 0) { 222 222 byte_part = true; 223 223 224 224 if ((*pos & (1 << (idx % 8))) == 0) { 225 225 *pos |= (1 << (idx % 8)); … … 227 227 return EOK; 228 228 } 229 229 230 230 ++idx; 231 231 } 232 232 233 233 if (byte_part) 234 234 ++pos; 235 235 236 236 /* Check the whole bytes (255 = 11111111 binary) */ 237 237 while (idx < max) { … … 240 240 break; 241 241 } 242 242 243 243 idx += 8; 244 244 ++pos; 245 245 } 246 246 247 247 /* If idx < max, some free bit found */ 248 248 if (idx < max) { … … 252 252 /* Free bit found */ 253 253 *pos |= (1 << i); 254 254 255 255 *index = idx; 256 256 return EOK; 257 257 } 258 258 259 259 idx++; 260 260 } 261 261 } 262 262 263 263 /* Free bit not found */ 264 264 return ENOSPC; -
uspace/lib/ext4/src/block_group.c
r3061bc1 ra35b458 70 70 { 71 71 bg->block_bitmap_lo = host2uint32_t_le((block_bitmap << 32) >> 32); 72 72 73 73 if (ext4_superblock_get_desc_size(sb) > 74 74 EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE) … … 106 106 { 107 107 bg->inode_bitmap_lo = host2uint32_t_le((inode_bitmap << 32) >> 32); 108 108 109 109 if (ext4_superblock_get_desc_size(sb) > 110 110 EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE) … … 144 144 bg->inode_table_first_block_lo = 145 145 host2uint32_t_le((inode_table_first << 32) >> 32); 146 146 147 147 if (ext4_superblock_get_desc_size(sb) > 148 148 EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE) … … 353 353 if (ext4_block_group_get_flags(bg) & flag) 354 354 return true; 355 355 356 356 return false; 357 357 } -
uspace/lib/ext4/src/directory.c
r3061bc1 ra35b458 110 110 return ((uint16_t)de->name_length_high) << 8 | 111 111 ((uint16_t)de->name_length); 112 112 113 113 return de->name_length; 114 114 … … 126 126 { 127 127 de->name_length = (length << 8) >> 8; 128 128 129 129 if ((ext4_superblock_get_rev_level(sb) == 0) && 130 130 (ext4_superblock_get_minor_rev_level(sb) < 5)) 131 131 de->name_length_high = length >> 8; 132 132 133 133 /* Else do nothing */ 134 134 } … … 148 148 (ext4_superblock_get_minor_rev_level(sb) >= 5)) 149 149 return de->inode_type; 150 150 151 151 return EXT4_DIRECTORY_FILETYPE_UNKNOWN; 152 152 } … … 165 165 (ext4_superblock_get_minor_rev_level(sb) >= 5)) 166 166 de->inode_type = type; 167 167 168 168 /* Else do nothing */ 169 169 } … … 190 190 it->current_offset = 0; 191 191 it->current_block = NULL; 192 192 193 193 return ext4_directory_iterator_seek(it, pos); 194 194 } … … 204 204 { 205 205 assert(it->current != NULL); 206 206 207 207 uint16_t skip = ext4_directory_entry_ll_get_entry_length(it->current); 208 208 209 209 return ext4_directory_iterator_seek(it, it->current_offset + skip); 210 210 } … … 224 224 uint64_t size = ext4_inode_get_size(it->inode_ref->fs->superblock, 225 225 it->inode_ref->inode); 226 226 227 227 /* The iterator is not valid until we seek to the desired position */ 228 228 it->current = NULL; 229 229 230 230 /* Are we at the end? */ 231 231 if (pos >= size) { … … 233 233 errno_t rc = block_put(it->current_block); 234 234 it->current_block = NULL; 235 235 236 236 if (rc != EOK) 237 237 return rc; 238 238 } 239 239 240 240 it->current_offset = pos; 241 241 return EOK; 242 242 } 243 243 244 244 /* Compute next block address */ 245 245 uint32_t block_size = … … 247 247 aoff64_t current_block_idx = it->current_offset / block_size; 248 248 aoff64_t next_block_idx = pos / block_size; 249 249 250 250 /* 251 251 * If we don't have a block or are moving accross block boundary, … … 257 257 errno_t rc = block_put(it->current_block); 258 258 it->current_block = NULL; 259 259 260 260 if (rc != EOK) 261 261 return rc; 262 262 } 263 263 264 264 uint32_t next_block_phys_idx; 265 265 errno_t rc = ext4_filesystem_get_inode_data_block_index(it->inode_ref, … … 267 267 if (rc != EOK) 268 268 return rc; 269 269 270 270 rc = block_get(&it->current_block, it->inode_ref->fs->device, 271 271 next_block_phys_idx, BLOCK_FLAGS_NONE); … … 275 275 } 276 276 } 277 277 278 278 it->current_offset = pos; 279 279 280 280 return ext4_directory_iterator_set(it, block_size); 281 281 } … … 293 293 { 294 294 it->current = NULL; 295 295 296 296 uint32_t offset_in_block = it->current_offset % block_size; 297 297 298 298 /* Ensure proper alignment */ 299 299 if ((offset_in_block % 4) != 0) 300 300 return EIO; 301 301 302 302 /* Ensure that the core of the entry does not overflow the block */ 303 303 if (offset_in_block > block_size - 8) 304 304 return EIO; 305 305 306 306 ext4_directory_entry_ll_t *entry = 307 307 it->current_block->data + offset_in_block; 308 308 309 309 /* Ensure that the whole entry does not overflow the block */ 310 310 uint16_t length = ext4_directory_entry_ll_get_entry_length(entry); 311 311 if (offset_in_block + length > block_size) 312 312 return EIO; 313 313 314 314 /* Ensure the name length is not too large */ 315 315 if (ext4_directory_entry_ll_get_name_length( 316 316 it->inode_ref->fs->superblock, entry) > length-8) 317 317 return EIO; 318 318 319 319 /* Everything OK - "publish" the entry */ 320 320 it->current = entry; … … 335 335 it->inode_ref = NULL; 336 336 it->current = NULL; 337 337 338 338 if (it->current_block) 339 339 return block_put(it->current_block); 340 340 341 341 return EOK; 342 342 } … … 359 359 uint32_t block_size = ext4_superblock_get_block_size(sb); 360 360 assert(entry_len <= block_size); 361 361 362 362 /* Set basic attributes */ 363 363 ext4_directory_entry_ll_set_inode(entry, child->index); 364 364 ext4_directory_entry_ll_set_entry_length(entry, entry_len); 365 365 ext4_directory_entry_ll_set_name_length(sb, entry, name_len); 366 366 367 367 /* Write name */ 368 368 memcpy(entry->name, name, name_len); 369 369 370 370 /* Set type of entry */ 371 371 if (ext4_inode_is_type(sb, child->inode, EXT4_INODE_MODE_DIRECTORY)) … … 390 390 { 391 391 ext4_filesystem_t *fs = parent->fs; 392 392 393 393 /* Index adding (if allowed) */ 394 394 if ((ext4_superblock_has_feature_compatible(fs->superblock, … … 405 405 parent->dirty = true; 406 406 } 407 407 408 408 /* Linear algorithm */ 409 409 410 410 uint32_t iblock = 0; 411 411 uint32_t fblock = 0; … … 413 413 uint32_t inode_size = ext4_inode_get_size(fs->superblock, parent->inode); 414 414 uint32_t total_blocks = inode_size / block_size; 415 415 416 416 uint32_t name_len = str_size(name); 417 417 418 418 /* Find block, where is space for new entry and try to add */ 419 419 bool success = false; … … 423 423 if (rc != EOK) 424 424 return rc; 425 425 426 426 block_t *block; 427 427 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE); 428 428 if (rc != EOK) 429 429 return rc; 430 430 431 431 /* If adding is successful, function can finish */ 432 432 rc = ext4_directory_try_insert_entry(fs->superblock, block, … … 434 434 if (rc == EOK) 435 435 success = true; 436 436 437 437 rc = block_put(block); 438 438 if (rc != EOK) 439 439 return rc; 440 440 441 441 if (success) 442 442 return EOK; 443 443 } 444 444 445 445 /* No free block found - needed to allocate next data block */ 446 446 447 447 iblock = 0; 448 448 fblock = 0; … … 450 450 if (rc != EOK) 451 451 return rc; 452 452 453 453 /* Load new block */ 454 454 block_t *new_block; … … 456 456 if (rc != EOK) 457 457 return rc; 458 458 459 459 /* Fill block with zeroes */ 460 460 memset(new_block->data, 0, block_size); … … 462 462 ext4_directory_write_entry(fs->superblock, block_entry, block_size, 463 463 child, name, name_len); 464 464 465 465 /* Save new block */ 466 466 new_block->dirty = true; 467 467 rc = block_put(new_block); 468 468 469 469 return rc; 470 470 } … … 483 483 { 484 484 uint32_t name_len = str_size(name); 485 485 486 486 ext4_superblock_t *sb = parent->fs->superblock; 487 487 488 488 /* Index search */ 489 489 if ((ext4_superblock_has_feature_compatible(sb, … … 492 492 errno_t rc = ext4_directory_dx_find_entry(result, parent, name_len, 493 493 name); 494 494 495 495 /* Check if index is not corrupted */ 496 496 if (rc != EXT4_ERR_BAD_DX_DIR) { 497 497 if (rc != EOK) 498 498 return rc; 499 499 500 500 return EOK; 501 501 } 502 502 503 503 /* Needed to clear dir index flag if corrupted */ 504 504 ext4_inode_clear_flag(parent->inode, EXT4_INODE_FLAG_INDEX); 505 505 parent->dirty = true; 506 506 } 507 507 508 508 /* Linear algorithm */ 509 509 510 510 uint32_t iblock; 511 511 uint32_t fblock; … … 513 513 uint32_t inode_size = ext4_inode_get_size(sb, parent->inode); 514 514 uint32_t total_blocks = inode_size / block_size; 515 515 516 516 /* Walk through all data blocks */ 517 517 for (iblock = 0; iblock < total_blocks; ++iblock) { … … 521 521 if (rc != EOK) 522 522 return rc; 523 523 524 524 /* Load data block */ 525 525 block_t *block; … … 527 527 if (rc != EOK) 528 528 return rc; 529 529 530 530 /* Try to find entry in block */ 531 531 ext4_directory_entry_ll_t *res_entry; … … 537 537 return EOK; 538 538 } 539 539 540 540 /* Entry not found - put block and continue to the next block */ 541 541 542 542 rc = block_put(block); 543 543 if (rc != EOK) 544 544 return rc; 545 545 } 546 546 547 547 /* Entry was not found */ 548 548 549 549 result->block = NULL; 550 550 result->dentry = NULL; 551 551 552 552 return ENOENT; 553 553 } … … 567 567 EXT4_INODE_MODE_DIRECTORY)) 568 568 return ENOTDIR; 569 569 570 570 /* Try to find entry */ 571 571 ext4_directory_search_result_t result; … … 573 573 if (rc != EOK) 574 574 return rc; 575 575 576 576 /* Invalidate entry */ 577 577 ext4_directory_entry_ll_set_inode(result.dentry, 0); 578 578 579 579 /* Store entry position in block */ 580 580 uint32_t pos = (void *) result.dentry - result.block->data; 581 581 582 582 /* 583 583 * If entry is not the first in block, it must be merged … … 586 586 if (pos != 0) { 587 587 uint32_t offset = 0; 588 588 589 589 /* Start from the first entry in block */ 590 590 ext4_directory_entry_ll_t *tmp_dentry = result.block->data; 591 591 uint16_t tmp_dentry_length = 592 592 ext4_directory_entry_ll_get_entry_length(tmp_dentry); 593 593 594 594 /* Find direct predecessor of removed entry */ 595 595 while ((offset + tmp_dentry_length) < pos) { … … 600 600 ext4_directory_entry_ll_get_entry_length(tmp_dentry); 601 601 } 602 602 603 603 assert(tmp_dentry_length + offset == pos); 604 604 605 605 /* Add to removed entry length to predecessor's length */ 606 606 uint16_t del_entry_length = … … 609 609 tmp_dentry_length + del_entry_length); 610 610 } 611 611 612 612 result.block->dirty = true; 613 613 614 614 return ext4_directory_destroy_result(&result); 615 615 } … … 633 633 uint32_t block_size = ext4_superblock_get_block_size(sb); 634 634 uint16_t required_len = sizeof(ext4_fake_directory_entry_t) + name_len; 635 635 636 636 if ((required_len % 4) != 0) 637 637 required_len += 4 - (required_len % 4); 638 638 639 639 /* Initialize pointers, stop means to upper bound */ 640 640 ext4_directory_entry_ll_t *dentry = target_block->data; 641 641 ext4_directory_entry_ll_t *stop = target_block->data + block_size; 642 642 643 643 /* 644 644 * Walk through the block and check for invalid entries … … 648 648 uint32_t inode = ext4_directory_entry_ll_get_inode(dentry); 649 649 uint16_t rec_len = ext4_directory_entry_ll_get_entry_length(dentry); 650 650 651 651 /* If invalid and large enough entry, use it */ 652 652 if ((inode == 0) && (rec_len >= required_len)) { … … 654 654 name, name_len); 655 655 target_block->dirty = true; 656 656 657 657 return EOK; 658 658 } 659 659 660 660 /* Valid entry, try to split it */ 661 661 if (inode != 0) { 662 662 uint16_t used_name_len = 663 663 ext4_directory_entry_ll_get_name_length(sb, dentry); 664 664 665 665 uint16_t used_space = 666 666 sizeof(ext4_fake_directory_entry_t) + used_name_len; 667 667 668 668 if ((used_name_len % 4) != 0) 669 669 used_space += 4 - (used_name_len % 4); 670 670 671 671 uint16_t free_space = rec_len - used_space; 672 672 673 673 /* There is free space for new entry */ 674 674 if (free_space >= required_len) { … … 679 679 ext4_directory_write_entry(sb, new_entry, 680 680 free_space, child, name, name_len); 681 681 682 682 target_block->dirty = true; 683 683 684 684 return EOK; 685 685 } 686 686 } 687 687 688 688 /* Jump to the next entry */ 689 689 dentry = (void *) dentry + rec_len; 690 690 } 691 691 692 692 /* No free space found for new entry */ 693 693 return ENOSPC; … … 711 711 ext4_directory_entry_ll_t *dentry = 712 712 (ext4_directory_entry_ll_t *) block->data; 713 713 714 714 /* Set upper bound for cycling */ 715 715 uint8_t *addr_limit = block->data + ext4_superblock_get_block_size(sb); 716 716 717 717 /* Walk through the block and check entries */ 718 718 while ((uint8_t *) dentry < addr_limit) { … … 720 720 if ((uint8_t *) dentry + name_len > addr_limit) 721 721 break; 722 722 723 723 /* Valid entry - check it */ 724 724 if (dentry->inode != 0) { … … 733 733 } 734 734 } 735 735 736 736 uint16_t dentry_len = 737 737 ext4_directory_entry_ll_get_entry_length(dentry); 738 738 739 739 /* Corrupted entry */ 740 740 if (dentry_len == 0) 741 741 return EINVAL; 742 742 743 743 /* Jump to next entry */ 744 744 dentry = (ext4_directory_entry_ll_t *) ((uint8_t *) dentry + dentry_len); 745 745 } 746 746 747 747 /* Entry not found */ 748 748 return ENOENT; … … 760 760 if (result->block) 761 761 return block_put(result->block); 762 762 763 763 return EOK; 764 764 } -
uspace/lib/ext4/src/directory_index.c
r3061bc1 ra35b458 244 244 if (rc != EOK) 245 245 return rc; 246 246 247 247 block_t *block; 248 248 rc = block_get(&block, dir->fs->device, fblock, BLOCK_FLAGS_NONE); 249 249 if (rc != EOK) 250 250 return rc; 251 251 252 252 /* Initialize pointers to data structures */ 253 253 ext4_directory_dx_root_t *root = block->data; 254 254 ext4_directory_dx_root_info_t *info = &(root->info); 255 255 256 256 /* Initialize root info structure */ 257 257 uint8_t hash_version = 258 258 ext4_superblock_get_default_hash_version(dir->fs->superblock); 259 259 260 260 ext4_directory_dx_root_info_set_hash_version(info, hash_version); 261 261 ext4_directory_dx_root_info_set_indirect_levels(info, 0); 262 262 ext4_directory_dx_root_info_set_info_length(info, 8); 263 263 264 264 /* Set limit and current number of entries */ 265 265 ext4_directory_dx_countlimit_t *countlimit = 266 266 (ext4_directory_dx_countlimit_t *) &root->entries; 267 267 ext4_directory_dx_countlimit_set_count(countlimit, 1); 268 268 269 269 uint32_t block_size = 270 270 ext4_superblock_get_block_size(dir->fs->superblock); … … 274 274 uint16_t root_limit = entry_space / sizeof(ext4_directory_dx_entry_t); 275 275 ext4_directory_dx_countlimit_set_limit(countlimit, root_limit); 276 276 277 277 /* Append new block, where will be new entries inserted in the future */ 278 278 uint32_t iblock; … … 282 282 return rc; 283 283 } 284 284 285 285 block_t *new_block; 286 286 rc = block_get(&new_block, dir->fs->device, fblock, BLOCK_FLAGS_NOREAD); … … 289 289 return rc; 290 290 } 291 291 292 292 /* Fill the whole block with empty entry */ 293 293 ext4_directory_entry_ll_t *block_entry = new_block->data; 294 294 ext4_directory_entry_ll_set_entry_length(block_entry, block_size); 295 295 ext4_directory_entry_ll_set_inode(block_entry, 0); 296 296 297 297 new_block->dirty = true; 298 298 rc = block_put(new_block); … … 301 301 return rc; 302 302 } 303 303 304 304 /* Connect new block to the only entry in index */ 305 305 ext4_directory_dx_entry_t *entry = root->entries; 306 306 ext4_directory_dx_entry_set_block(entry, iblock); 307 307 308 308 block->dirty = true; 309 309 310 310 return block_put(block); 311 311 } … … 328 328 ext4_directory_dx_root_t *root = 329 329 (ext4_directory_dx_root_t *) root_block->data; 330 330 331 331 if ((root->info.hash_version != EXT4_HASH_VERSION_TEA) && 332 332 (root->info.hash_version != EXT4_HASH_VERSION_HALF_MD4) && 333 333 (root->info.hash_version != EXT4_HASH_VERSION_LEGACY)) 334 334 return EXT4_ERR_BAD_DX_DIR; 335 335 336 336 /* Check unused flags */ 337 337 if (root->info.unused_flags != 0) 338 338 return EXT4_ERR_BAD_DX_DIR; 339 339 340 340 /* Check indirect levels */ 341 341 if (root->info.indirect_levels > 1) 342 342 return EXT4_ERR_BAD_DX_DIR; 343 343 344 344 /* Check if node limit is correct */ 345 345 uint32_t block_size = ext4_superblock_get_block_size(sb); … … 348 348 entry_space -= sizeof(ext4_directory_dx_root_info_t); 349 349 entry_space = entry_space / sizeof(ext4_directory_dx_entry_t); 350 350 351 351 uint16_t limit = ext4_directory_dx_countlimit_get_limit( 352 352 (ext4_directory_dx_countlimit_t *) &root->entries); 353 353 if (limit != entry_space) 354 354 return EXT4_ERR_BAD_DX_DIR; 355 355 356 356 /* Check hash version and modify if necessary */ 357 357 hinfo->hash_version = … … 362 362 hinfo->hash_version += 3; 363 363 } 364 364 365 365 /* Load hash seed from superblock */ 366 366 hinfo->seed = ext4_superblock_get_hash_seed(sb); 367 367 368 368 /* Compute hash value of name */ 369 369 if (name) 370 370 ext4_hash_string(hinfo, name_len, name); 371 371 372 372 return EOK; 373 373 } … … 393 393 ext4_directory_dx_entry_t *entries = 394 394 (ext4_directory_dx_entry_t *) &root->entries; 395 395 396 396 uint16_t limit = ext4_directory_dx_countlimit_get_limit( 397 397 (ext4_directory_dx_countlimit_t *) entries); 398 398 uint8_t indirect_level = 399 399 ext4_directory_dx_root_info_get_indirect_levels(&root->info); 400 400 401 401 block_t *tmp_block = root_block; 402 402 ext4_directory_dx_entry_t *p; … … 404 404 ext4_directory_dx_entry_t *m; 405 405 ext4_directory_dx_entry_t *at; 406 406 407 407 /* Walk through the index tree */ 408 408 while (true) { … … 411 411 if ((count == 0) || (count > limit)) 412 412 return EXT4_ERR_BAD_DX_DIR; 413 413 414 414 /* Do binary search in every node */ 415 415 p = entries + 1; 416 416 q = entries + count - 1; 417 417 418 418 while (p <= q) { 419 419 m = p + (q - p) / 2; … … 423 423 p = m + 1; 424 424 } 425 425 426 426 at = p - 1; 427 427 428 428 /* Write results */ 429 429 tmp_dx_block->block = tmp_block; 430 430 tmp_dx_block->entries = entries; 431 431 tmp_dx_block->position = at; 432 432 433 433 /* Is algorithm in the leaf? */ 434 434 if (indirect_level == 0) { … … 436 436 return EOK; 437 437 } 438 438 439 439 /* Goto child node */ 440 440 uint32_t next_block = ext4_directory_dx_entry_get_block(at); 441 441 442 442 indirect_level--; 443 443 444 444 uint32_t fblock; 445 445 errno_t rc = ext4_filesystem_get_inode_data_block_index(inode_ref, … … 447 447 if (rc != EOK) 448 448 return rc; 449 449 450 450 rc = block_get(&tmp_block, inode_ref->fs->device, fblock, 451 451 BLOCK_FLAGS_NONE); 452 452 if (rc != EOK) 453 453 return rc; 454 454 455 455 entries = ((ext4_directory_dx_node_t *) tmp_block->data)->entries; 456 456 limit = ext4_directory_dx_countlimit_get_limit( 457 457 (ext4_directory_dx_countlimit_t *) entries); 458 458 459 459 uint16_t entry_space = 460 460 ext4_superblock_get_block_size(inode_ref->fs->superblock) - 461 461 sizeof(ext4_directory_dx_dot_entry_t); 462 462 entry_space = entry_space / sizeof(ext4_directory_dx_entry_t); 463 463 464 464 if (limit != entry_space) { 465 465 block_put(tmp_block); 466 466 return EXT4_ERR_BAD_DX_DIR; 467 467 } 468 468 469 469 ++tmp_dx_block; 470 470 } 471 471 472 472 /* Unreachable */ 473 473 return EOK; … … 490 490 uint32_t num_handles = 0; 491 491 ext4_directory_dx_block_t *p = dx_block; 492 492 493 493 /* Try to find data block with next bunch of entries */ 494 494 while (true) { … … 496 496 uint16_t count = ext4_directory_dx_countlimit_get_count( 497 497 (ext4_directory_dx_countlimit_t *) p->entries); 498 498 499 499 if (p->position < p->entries + count) 500 500 break; 501 501 502 502 if (p == dx_blocks) 503 503 return EOK; 504 504 505 505 num_handles++; 506 506 p--; 507 507 } 508 508 509 509 /* Check hash collision (if not occured - no next block cannot be used) */ 510 510 uint32_t current_hash = ext4_directory_dx_entry_get_hash(p->position); … … 513 513 return 0; 514 514 } 515 515 516 516 /* Fill new path */ 517 517 while (num_handles--) { … … 519 519 ext4_directory_dx_entry_get_block(p->position); 520 520 uint32_t block_addr; 521 521 522 522 errno_t rc = ext4_filesystem_get_inode_data_block_index(inode_ref, 523 523 block_idx, &block_addr); 524 524 if (rc != EOK) 525 525 return rc; 526 526 527 527 block_t *block; 528 528 rc = block_get(&block, inode_ref->fs->device, block_addr, BLOCK_FLAGS_NONE); 529 529 if (rc != EOK) 530 530 return rc; 531 531 532 532 p++; 533 533 534 534 /* Don't forget to put old block (prevent memory leak) */ 535 535 rc = block_put(p->block); 536 536 if (rc != EOK) 537 537 return rc; 538 538 539 539 p->block = block; 540 540 p->entries = ((ext4_directory_dx_node_t *) block->data)->entries; 541 541 p->position = p->entries; 542 542 } 543 543 544 544 return ENOENT; 545 545 } … … 566 566 if (rc != EOK) 567 567 return rc; 568 568 569 569 ext4_filesystem_t *fs = inode_ref->fs; 570 570 571 571 block_t *root_block; 572 572 rc = block_get(&root_block, fs->device, root_block_addr, … … 574 574 if (rc != EOK) 575 575 return rc; 576 576 577 577 /* Initialize hash info (compute hash value) */ 578 578 ext4_hash_info_t hinfo; … … 583 583 return EXT4_ERR_BAD_DX_DIR; 584 584 } 585 585 586 586 /* 587 587 * Hardcoded number 2 means maximum height of index tree, … … 591 591 ext4_directory_dx_block_t *dx_block; 592 592 ext4_directory_dx_block_t *tmp; 593 593 594 594 rc = ext4_directory_dx_get_leaf(&hinfo, inode_ref, root_block, 595 595 &dx_block, dx_blocks); … … 598 598 return EXT4_ERR_BAD_DX_DIR; 599 599 } 600 600 601 601 do { 602 602 /* Load leaf block */ … … 604 604 ext4_directory_dx_entry_get_block(dx_block->position); 605 605 uint32_t leaf_block_addr; 606 606 607 607 rc = ext4_filesystem_get_inode_data_block_index(inode_ref, 608 608 leaf_block_idx, &leaf_block_addr); 609 609 if (rc != EOK) 610 610 goto cleanup; 611 611 612 612 block_t *leaf_block; 613 613 rc = block_get(&leaf_block, fs->device, leaf_block_addr, … … 615 615 if (rc != EOK) 616 616 goto cleanup; 617 617 618 618 /* Linear search inside block */ 619 619 ext4_directory_entry_ll_t *res_dentry; 620 620 rc = ext4_directory_find_in_block(leaf_block, fs->superblock, 621 621 name_len, name, &res_dentry); 622 622 623 623 /* Found => return it */ 624 624 if (rc == EOK) { … … 627 627 goto cleanup; 628 628 } 629 629 630 630 /* Not found, leave untouched */ 631 631 rc2 = block_put(leaf_block); 632 632 if (rc2 != EOK) 633 633 goto cleanup; 634 634 635 635 if (rc != ENOENT) 636 636 goto cleanup; 637 637 638 638 /* check if the next block could be checked */ 639 639 rc = ext4_directory_dx_next_block(inode_ref, hinfo.hash, … … 643 643 644 644 } while (rc == ENOENT); 645 645 646 646 /* Entry not found */ 647 647 rc = ENOENT; 648 648 649 649 cleanup: 650 650 /* The whole path must be released (preventing memory leak) */ 651 651 tmp = dx_blocks; 652 652 653 653 while (tmp <= dx_block) { 654 654 rc2 = block_put(tmp->block); … … 657 657 ++tmp; 658 658 } 659 659 660 660 return rc; 661 661 } … … 677 677 ext4_dx_sort_entry_t const *entry1 = arg1; 678 678 ext4_dx_sort_entry_t const *entry2 = arg2; 679 679 680 680 if (entry1->hash == entry2->hash) 681 681 return 0; 682 682 683 683 if (entry1->hash < entry2->hash) 684 684 return -1; … … 701 701 ext4_directory_dx_entry_t *old_index_entry = index_block->position; 702 702 ext4_directory_dx_entry_t *new_index_entry = old_index_entry + 1; 703 703 704 704 ext4_directory_dx_countlimit_t *countlimit = 705 705 (ext4_directory_dx_countlimit_t *) index_block->entries; 706 706 uint32_t count = ext4_directory_dx_countlimit_get_count(countlimit); 707 707 708 708 ext4_directory_dx_entry_t *start_index = index_block->entries; 709 709 size_t bytes = (void *) (start_index + count) - (void *) (new_index_entry); 710 710 711 711 memmove(new_index_entry + 1, new_index_entry, bytes); 712 712 713 713 ext4_directory_dx_entry_set_block(new_index_entry, iblock); 714 714 ext4_directory_dx_entry_set_hash(new_index_entry, hash); 715 715 716 716 ext4_directory_dx_countlimit_set_count(countlimit, count + 1); 717 717 718 718 index_block->block->dirty = true; 719 719 } … … 733 733 { 734 734 errno_t rc = EOK; 735 735 736 736 /* Allocate buffer for directory entries */ 737 737 uint32_t block_size = … … 740 740 if (entry_buffer == NULL) 741 741 return ENOMEM; 742 742 743 743 /* dot entry has the smallest size available */ 744 744 uint32_t max_entry_count = 745 745 block_size / sizeof(ext4_directory_dx_dot_entry_t); 746 746 747 747 /* Allocate sort entry */ 748 748 ext4_dx_sort_entry_t *sort_array = … … 752 752 return ENOMEM; 753 753 } 754 754 755 755 uint32_t idx = 0; 756 756 uint32_t real_size = 0; 757 757 758 758 /* Initialize hinfo */ 759 759 ext4_hash_info_t tmp_hinfo; 760 760 memcpy(&tmp_hinfo, hinfo, sizeof(ext4_hash_info_t)); 761 761 762 762 /* Load all valid entries to the buffer */ 763 763 ext4_directory_entry_ll_t *dentry = old_data_block->data; … … 769 769 inode_ref->fs->superblock, dentry); 770 770 ext4_hash_string(&tmp_hinfo, len, (char *) dentry->name); 771 771 772 772 uint32_t rec_len = 8 + len; 773 773 774 774 if ((rec_len % 4) != 0) 775 775 rec_len += 4 - (rec_len % 4); 776 776 777 777 memcpy(entry_buffer_ptr, dentry, rec_len); 778 778 779 779 sort_array[idx].dentry = entry_buffer_ptr; 780 780 sort_array[idx].rec_len = rec_len; 781 781 sort_array[idx].hash = tmp_hinfo.hash; 782 782 783 783 entry_buffer_ptr += rec_len; 784 784 real_size += rec_len; 785 785 idx++; 786 786 } 787 787 788 788 dentry = (void *) dentry + 789 789 ext4_directory_entry_ll_get_entry_length(dentry); 790 790 } 791 791 792 792 /* Sort all entries */ 793 793 qsort(sort_array, idx, sizeof(ext4_dx_sort_entry_t), 794 794 ext4_directory_dx_entry_comparator); 795 795 796 796 /* Allocate new block for store the second part of entries */ 797 797 uint32_t new_fblock; … … 804 804 return rc; 805 805 } 806 806 807 807 /* Load new block */ 808 808 block_t *new_data_block_tmp; … … 814 814 return rc; 815 815 } 816 816 817 817 /* 818 818 * Distribute entries to two blocks (by size) … … 828 828 break; 829 829 } 830 830 831 831 current_size += sort_array[i].rec_len; 832 832 } 833 833 834 834 /* Check hash collision */ 835 835 uint32_t continued = 0; 836 836 if (new_hash == sort_array[mid-1].hash) 837 837 continued = 1; 838 838 839 839 uint32_t offset = 0; 840 840 void *ptr; 841 841 842 842 /* First part - to the old block */ 843 843 for (uint32_t i = 0; i < mid; ++i) { 844 844 ptr = old_data_block->data + offset; 845 845 memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len); 846 846 847 847 ext4_directory_entry_ll_t *tmp = ptr; 848 848 if (i < (mid - 1)) … … 852 852 ext4_directory_entry_ll_set_entry_length(tmp, 853 853 block_size - offset); 854 854 855 855 offset += sort_array[i].rec_len; 856 856 } 857 857 858 858 /* Second part - to the new block */ 859 859 offset = 0; … … 861 861 ptr = new_data_block_tmp->data + offset; 862 862 memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len); 863 863 864 864 ext4_directory_entry_ll_t *tmp = ptr; 865 865 if (i < (idx - 1)) … … 869 869 ext4_directory_entry_ll_set_entry_length(tmp, 870 870 block_size - offset); 871 871 872 872 offset += sort_array[i].rec_len; 873 873 } 874 874 875 875 /* Do some steps to finish operation */ 876 876 old_data_block->dirty = true; 877 877 new_data_block_tmp->dirty = true; 878 878 879 879 free(sort_array); 880 880 free(entry_buffer); 881 881 882 882 ext4_directory_dx_insert_entry(index_block, new_hash + continued, 883 883 new_iblock); 884 884 885 885 *new_data_block = new_data_block_tmp; 886 886 887 887 return EOK; 888 888 } … … 907 907 entries = 908 908 ((ext4_directory_dx_node_t *) dx_block->block->data)->entries; 909 909 910 910 ext4_directory_dx_countlimit_t *countlimit = 911 911 (ext4_directory_dx_countlimit_t *) entries; 912 912 913 913 uint16_t leaf_limit = 914 914 ext4_directory_dx_countlimit_get_limit(countlimit); 915 915 uint16_t leaf_count = 916 916 ext4_directory_dx_countlimit_get_count(countlimit); 917 917 918 918 /* Check if is necessary to split index block */ 919 919 if (leaf_limit == leaf_count) { 920 920 size_t levels = dx_block - dx_blocks; 921 921 922 922 ext4_directory_dx_entry_t *root_entries = 923 923 ((ext4_directory_dx_root_t *) dx_blocks[0].block->data)->entries; 924 924 925 925 ext4_directory_dx_countlimit_t *root_countlimit = 926 926 (ext4_directory_dx_countlimit_t *) root_entries; … … 929 929 uint16_t root_count = 930 930 ext4_directory_dx_countlimit_get_count(root_countlimit); 931 931 932 932 /* Linux limitation */ 933 933 if ((levels > 0) && (root_limit == root_count)) 934 934 return ENOSPC; 935 935 936 936 /* Add new block to directory */ 937 937 uint32_t new_fblock; … … 941 941 if (rc != EOK) 942 942 return rc; 943 943 944 944 /* load new block */ 945 945 block_t *new_block; … … 948 948 if (rc != EOK) 949 949 return rc; 950 950 951 951 ext4_directory_dx_node_t *new_node = new_block->data; 952 952 ext4_directory_dx_entry_t *new_entries = new_node->entries; 953 953 954 954 uint32_t block_size = 955 955 ext4_superblock_get_block_size(inode_ref->fs->superblock); 956 956 957 957 /* Split leaf node */ 958 958 if (levels > 0) { … … 961 961 uint32_t hash_right = 962 962 ext4_directory_dx_entry_get_hash(entries + count_left); 963 963 964 964 /* Copy data to new node */ 965 965 memcpy((void *) new_entries, (void *) (entries + count_left), 966 966 count_right * sizeof(ext4_directory_dx_entry_t)); 967 967 968 968 /* Initialize new node */ 969 969 ext4_directory_dx_countlimit_t *left_countlimit = … … 971 971 ext4_directory_dx_countlimit_t *right_countlimit = 972 972 (ext4_directory_dx_countlimit_t *) new_entries; 973 973 974 974 ext4_directory_dx_countlimit_set_count(left_countlimit, count_left); 975 975 ext4_directory_dx_countlimit_set_count(right_countlimit, count_right); 976 976 977 977 uint32_t entry_space = 978 978 block_size - sizeof(ext4_fake_directory_entry_t); … … 980 980 entry_space / sizeof(ext4_directory_dx_entry_t); 981 981 ext4_directory_dx_countlimit_set_limit(right_countlimit, node_limit); 982 982 983 983 /* Which index block is target for new entry */ 984 984 uint32_t position_index = (dx_block->position - dx_block->entries); 985 985 if (position_index >= count_left) { 986 986 dx_block->block->dirty = true; 987 987 988 988 block_t *block_tmp = dx_block->block; 989 989 dx_block->block = new_block; … … 991 991 new_entries + position_index - count_left; 992 992 dx_block->entries = new_entries; 993 993 994 994 new_block = block_tmp; 995 995 } 996 996 997 997 /* Finally insert new entry */ 998 998 ext4_directory_dx_insert_entry(dx_blocks, hash_right, new_iblock); 999 999 1000 1000 return block_put(new_block); 1001 1001 } else { 1002 1002 /* Create second level index */ 1003 1003 1004 1004 /* Copy data from root to child block */ 1005 1005 memcpy((void *) new_entries, (void *) entries, 1006 1006 leaf_count * sizeof(ext4_directory_dx_entry_t)); 1007 1007 1008 1008 ext4_directory_dx_countlimit_t *new_countlimit = 1009 1009 (ext4_directory_dx_countlimit_t *) new_entries; 1010 1010 1011 1011 uint32_t entry_space = 1012 1012 block_size - sizeof(ext4_fake_directory_entry_t); … … 1014 1014 entry_space / sizeof(ext4_directory_dx_entry_t); 1015 1015 ext4_directory_dx_countlimit_set_limit(new_countlimit, node_limit); 1016 1016 1017 1017 /* Set values in root node */ 1018 1018 ext4_directory_dx_countlimit_t *new_root_countlimit = 1019 1019 (ext4_directory_dx_countlimit_t *) entries; 1020 1020 1021 1021 ext4_directory_dx_countlimit_set_count(new_root_countlimit, 1); 1022 1022 ext4_directory_dx_entry_set_block(entries, new_iblock); 1023 1023 1024 1024 ((ext4_directory_dx_root_t *) 1025 1025 dx_blocks[0].block->data)->info.indirect_levels = 1; 1026 1026 1027 1027 /* Add new entry to the path */ 1028 1028 dx_block = dx_blocks + 1; … … 1032 1032 } 1033 1033 } 1034 1034 1035 1035 return EOK; 1036 1036 } … … 1049 1049 { 1050 1050 errno_t rc2 = EOK; 1051 1051 1052 1052 /* Get direct block 0 (index root) */ 1053 1053 uint32_t root_block_addr; … … 1056 1056 if (rc != EOK) 1057 1057 return rc; 1058 1058 1059 1059 ext4_filesystem_t *fs = parent->fs; 1060 1060 1061 1061 block_t *root_block; 1062 1062 rc = block_get(&root_block, fs->device, root_block_addr, … … 1064 1064 if (rc != EOK) 1065 1065 return rc; 1066 1066 1067 1067 /* Initialize hinfo structure (mainly compute hash) */ 1068 1068 uint32_t name_len = str_size(name); … … 1074 1074 return EXT4_ERR_BAD_DX_DIR; 1075 1075 } 1076 1076 1077 1077 /* 1078 1078 * Hardcoded number 2 means maximum height of index … … 1082 1082 ext4_directory_dx_block_t *dx_block; 1083 1083 ext4_directory_dx_block_t *dx_it; 1084 1084 1085 1085 rc = ext4_directory_dx_get_leaf(&hinfo, parent, root_block, 1086 1086 &dx_block, dx_blocks); … … 1089 1089 goto release_index; 1090 1090 } 1091 1091 1092 1092 /* Try to insert to existing data block */ 1093 1093 uint32_t leaf_block_idx = … … 1098 1098 if (rc != EOK) 1099 1099 goto release_index; 1100 1100 1101 1101 block_t *target_block; 1102 1102 rc = block_get(&target_block, fs->device, leaf_block_addr, … … 1104 1104 if (rc != EOK) 1105 1105 goto release_index; 1106 1106 1107 1107 /* Check if insert operation passed */ 1108 1108 rc = ext4_directory_try_insert_entry(fs->superblock, target_block, child, … … 1110 1110 if (rc == EOK) 1111 1111 goto release_target_index; 1112 1112 1113 1113 /* 1114 1114 * Check if there is needed to split index node … … 1118 1118 if (rc != EOK) 1119 1119 goto release_target_index; 1120 1120 1121 1121 /* Split entries to two blocks (includes sorting by hash value) */ 1122 1122 block_t *new_block = NULL; … … 1127 1127 goto release_target_index; 1128 1128 } 1129 1129 1130 1130 /* Where to save new entry */ 1131 1131 uint32_t new_block_hash = … … 1137 1137 rc = ext4_directory_try_insert_entry(fs->superblock, target_block, 1138 1138 child, name, name_len); 1139 1139 1140 1140 /* Cleanup */ 1141 1141 rc = block_put(new_block); 1142 1142 if (rc != EOK) 1143 1143 return rc; 1144 1144 1145 1145 /* Cleanup operations */ 1146 1146 1147 1147 release_target_index: 1148 1148 rc2 = rc; 1149 1149 1150 1150 rc = block_put(target_block); 1151 1151 if (rc != EOK) 1152 1152 return rc; 1153 1153 1154 1154 release_index: 1155 1155 if (rc != EOK) 1156 1156 rc2 = rc; 1157 1157 1158 1158 dx_it = dx_blocks; 1159 1159 1160 1160 while (dx_it <= dx_block) { 1161 1161 rc = block_put(dx_it->block); 1162 1162 if (rc != EOK) 1163 1163 return rc; 1164 1164 1165 1165 dx_it++; 1166 1166 } 1167 1167 1168 1168 return rc2; 1169 1169 } -
uspace/lib/ext4/src/extent.c
r3061bc1 ra35b458 295 295 ext4_extent_index_t *l; 296 296 ext4_extent_index_t *m; 297 297 298 298 uint16_t entries_count = 299 299 ext4_extent_header_get_entries_count(header); 300 300 301 301 /* Initialize bounds */ 302 302 l = EXT4_EXTENT_FIRST_INDEX(header) + 1; 303 303 r = EXT4_EXTENT_FIRST_INDEX(header) + entries_count - 1; 304 304 305 305 /* Do binary search */ 306 306 while (l <= r) { 307 307 m = l + (r - l) / 2; 308 308 uint32_t first_block = ext4_extent_index_get_first_block(m); 309 309 310 310 if (iblock < first_block) 311 311 r = m - 1; … … 313 313 l = m + 1; 314 314 } 315 315 316 316 /* Set output value */ 317 317 *index = l - 1; … … 332 332 ext4_extent_t *l; 333 333 ext4_extent_t *m; 334 334 335 335 uint16_t entries_count = 336 336 ext4_extent_header_get_entries_count(header); 337 337 338 338 if (entries_count == 0) { 339 339 /* this leaf is empty */ … … 341 341 return; 342 342 } 343 343 344 344 /* Initialize bounds */ 345 345 l = EXT4_EXTENT_FIRST(header) + 1; 346 346 r = EXT4_EXTENT_FIRST(header) + entries_count - 1; 347 347 348 348 /* Do binary search */ 349 349 while (l <= r) { 350 350 m = l + (r - l) / 2; 351 351 uint32_t first_block = ext4_extent_get_first_block(m); 352 352 353 353 if (iblock < first_block) 354 354 r = m - 1; … … 356 356 l = m + 1; 357 357 } 358 358 359 359 /* Set output value */ 360 360 *extent = l - 1; … … 379 379 uint64_t inode_size = 380 380 ext4_inode_get_size(inode_ref->fs->superblock, inode_ref->inode); 381 381 382 382 uint32_t block_size = 383 383 ext4_superblock_get_block_size(inode_ref->fs->superblock); 384 384 385 385 uint32_t last_idx = (inode_size - 1) / block_size; 386 386 387 387 /* Check if requested iblock is not over size of i-node */ 388 388 if (iblock > last_idx) { … … 390 390 return EOK; 391 391 } 392 392 393 393 block_t *block = NULL; 394 394 395 395 /* Walk through extent tree */ 396 396 ext4_extent_header_t *header = 397 397 ext4_inode_get_extent_header(inode_ref->inode); 398 398 399 399 while (ext4_extent_header_get_depth(header) != 0) { 400 400 /* Search index in node */ 401 401 ext4_extent_index_t *index; 402 402 ext4_extent_binsearch_idx(header, &index, iblock); 403 403 404 404 /* Load child node and set values for the next iteration */ 405 405 uint64_t child = ext4_extent_index_get_leaf(index); 406 406 407 407 if (block != NULL) { 408 408 rc = block_put(block); … … 410 410 return rc; 411 411 } 412 412 413 413 rc = block_get(&block, inode_ref->fs->device, child, 414 414 BLOCK_FLAGS_NONE); 415 415 if (rc != EOK) 416 416 return rc; 417 417 418 418 header = (ext4_extent_header_t *)block->data; 419 419 } 420 420 421 421 /* Search extent in the leaf block */ 422 422 ext4_extent_t* extent = NULL; 423 423 ext4_extent_binsearch(header, &extent, iblock); 424 424 425 425 /* Prevent empty leaf */ 426 426 if (extent == NULL) { … … 431 431 uint32_t first = ext4_extent_get_first_block(extent); 432 432 phys_block = ext4_extent_get_start(extent) + iblock - first; 433 433 434 434 *fblock = phys_block; 435 435 } 436 436 437 437 /* Cleanup */ 438 438 if (block != NULL) 439 439 rc = block_put(block); 440 440 441 441 return rc; 442 442 } … … 459 459 ext4_extent_header_t *eh = 460 460 ext4_inode_get_extent_header(inode_ref->inode); 461 461 462 462 uint16_t depth = ext4_extent_header_get_depth(eh); 463 463 464 464 ext4_extent_path_t *tmp_path; 465 465 466 466 /* Added 2 for possible tree growing */ 467 467 tmp_path = malloc(sizeof(ext4_extent_path_t) * (depth + 2)); 468 468 if (tmp_path == NULL) 469 469 return ENOMEM; 470 470 471 471 /* Initialize structure for algorithm start */ 472 472 tmp_path[0].block = inode_ref->block; 473 473 tmp_path[0].header = eh; 474 474 475 475 /* Walk through the extent tree */ 476 476 uint16_t pos = 0; … … 480 480 ext4_extent_binsearch_idx(tmp_path[pos].header, 481 481 &tmp_path[pos].index, iblock); 482 482 483 483 tmp_path[pos].depth = depth; 484 484 tmp_path[pos].extent = NULL; 485 485 486 486 assert(tmp_path[pos].index != NULL); 487 487 488 488 /* Load information for the next iteration */ 489 489 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index); 490 490 491 491 block_t *block; 492 492 rc = block_get(&block, inode_ref->fs->device, fblock, … … 494 494 if (rc != EOK) 495 495 goto cleanup; 496 496 497 497 pos++; 498 498 499 499 eh = (ext4_extent_header_t *)block->data; 500 500 tmp_path[pos].block = block; 501 501 tmp_path[pos].header = eh; 502 502 } 503 503 504 504 tmp_path[pos].depth = 0; 505 505 tmp_path[pos].extent = NULL; 506 506 tmp_path[pos].index = NULL; 507 507 508 508 /* Find extent in the leaf node */ 509 509 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock); 510 510 *ret_path = tmp_path; 511 511 512 512 return EOK; 513 513 514 514 cleanup: 515 515 ; … … 528 528 } 529 529 } 530 530 531 531 /* Destroy temporary data structure */ 532 532 free(tmp_path); 533 533 534 534 return rc; 535 535 } … … 549 549 uint64_t start = ext4_extent_get_start(extent); 550 550 uint16_t block_count = ext4_extent_get_block_count(extent); 551 551 552 552 return ext4_balloc_free_blocks(inode_ref, start, block_count); 553 553 } … … 569 569 { 570 570 uint32_t fblock = ext4_extent_index_get_leaf(index); 571 571 572 572 block_t* block; 573 573 errno_t rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NONE); 574 574 if (rc != EOK) 575 575 return rc; 576 576 577 577 ext4_extent_header_t *header = block->data; 578 578 579 579 if (ext4_extent_header_get_depth(header)) { 580 580 /* The node is non-leaf, do recursion */ 581 581 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header); 582 582 583 583 /* Release all subbranches */ 584 584 for (uint32_t i = 0; … … 592 592 /* Leaf node reached */ 593 593 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header); 594 594 595 595 /* Release all extents and stop recursion */ 596 596 for (uint32_t i = 0; … … 602 602 } 603 603 } 604 604 605 605 /* Release data block where the node was stored */ 606 606 607 607 rc = block_put(block); 608 608 if (rc != EOK) 609 609 return rc; 610 610 611 611 return ext4_balloc_free_block(inode_ref, fblock); 612 612 } … … 626 626 if (rc != EOK) 627 627 return rc; 628 628 629 629 /* Jump to last item of the path (extent) */ 630 630 ext4_extent_path_t *path_ptr = path; 631 631 while (path_ptr->depth != 0) 632 632 path_ptr++; 633 633 634 634 assert(path_ptr->extent != NULL); 635 635 636 636 /* First extent maybe released partially */ 637 637 uint32_t first_iblock = … … 639 639 uint32_t first_fblock = 640 640 ext4_extent_get_start(path_ptr->extent) + iblock_from - first_iblock; 641 641 642 642 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent); 643 643 644 644 uint16_t delete_count = block_count - 645 645 (ext4_extent_get_start(path_ptr->extent) - first_fblock); 646 646 647 647 /* Release all blocks */ 648 648 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count); 649 649 if (rc != EOK) 650 650 goto cleanup; 651 651 652 652 /* Correct counter */ 653 653 block_count -= delete_count; 654 654 ext4_extent_set_block_count(path_ptr->extent, block_count); 655 655 656 656 /* Initialize the following loop */ 657 657 uint16_t entries = … … 659 659 ext4_extent_t *tmp_ext = path_ptr->extent + 1; 660 660 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries; 661 661 662 662 /* If first extent empty, release it */ 663 663 if (block_count == 0) 664 664 entries--; 665 665 666 666 /* Release all successors of the first extent in the same node */ 667 667 while (tmp_ext < stop_ext) { 668 668 first_fblock = ext4_extent_get_start(tmp_ext); 669 669 delete_count = ext4_extent_get_block_count(tmp_ext); 670 670 671 671 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count); 672 672 if (rc != EOK) 673 673 goto cleanup; 674 674 675 675 entries--; 676 676 tmp_ext++; 677 677 } 678 678 679 679 ext4_extent_header_set_entries_count(path_ptr->header, entries); 680 680 path_ptr->block->dirty = true; 681 681 682 682 /* If leaf node is empty, parent entry must be modified */ 683 683 bool remove_parent_record = false; 684 684 685 685 /* Don't release root block (including inode data) !!! */ 686 686 if ((path_ptr != path) && (entries == 0)) { … … 688 688 if (rc != EOK) 689 689 goto cleanup; 690 690 691 691 remove_parent_record = true; 692 692 } 693 693 694 694 /* Jump to the parent */ 695 695 --path_ptr; 696 696 697 697 /* Release all successors in all tree levels */ 698 698 while (path_ptr >= path) { … … 701 701 ext4_extent_index_t *stop = 702 702 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries; 703 703 704 704 /* Correct entries count because of changes in the previous iteration */ 705 705 if (remove_parent_record) 706 706 entries--; 707 707 708 708 /* Iterate over all entries and release the whole subtrees */ 709 709 while (index < stop) { … … 711 711 if (rc != EOK) 712 712 goto cleanup; 713 713 714 714 ++index; 715 715 --entries; 716 716 } 717 717 718 718 ext4_extent_header_set_entries_count(path_ptr->header, entries); 719 719 path_ptr->block->dirty = true; 720 720 721 721 /* Free the node if it is empty */ 722 722 if ((entries == 0) && (path_ptr != path)) { … … 724 724 if (rc != EOK) 725 725 goto cleanup; 726 726 727 727 /* Mark parent to be checked */ 728 728 remove_parent_record = true; 729 729 } else 730 730 remove_parent_record = false; 731 731 732 732 --path_ptr; 733 733 } 734 734 735 735 cleanup: 736 736 ; … … 749 749 } 750 750 } 751 751 752 752 /* Destroy temporary data structure */ 753 753 free(path); 754 754 755 755 return rc; 756 756 } … … 771 771 { 772 772 ext4_extent_path_t *path_ptr = path + path->depth; 773 773 774 774 uint32_t block_size = 775 775 ext4_superblock_get_block_size(inode_ref->fs->superblock); 776 776 777 777 /* Start splitting */ 778 778 while (path_ptr > path) { … … 781 781 uint16_t limit = 782 782 ext4_extent_header_get_max_entries_count(path_ptr->header); 783 783 784 784 if (entries == limit) { 785 785 /* Full node - allocate block for new one */ … … 788 788 if (rc != EOK) 789 789 return rc; 790 790 791 791 block_t *block; 792 792 rc = block_get(&block, inode_ref->fs->device, fblock, … … 796 796 return rc; 797 797 } 798 798 799 799 /* Put back not modified old block */ 800 800 rc = block_put(path_ptr->block); … … 804 804 return rc; 805 805 } 806 806 807 807 /* Initialize newly allocated block and remember it */ 808 808 memset(block->data, 0, block_size); 809 809 path_ptr->block = block; 810 810 811 811 /* Update pointers in extent path structure */ 812 812 path_ptr->header = block->data; … … 823 823 sizeof(ext4_extent_t); 824 824 } 825 825 826 826 /* Initialize on-disk structure (header) */ 827 827 ext4_extent_header_set_entries_count(path_ptr->header, 1); … … 830 830 ext4_extent_header_set_depth(path_ptr->header, path_ptr->depth); 831 831 ext4_extent_header_set_generation(path_ptr->header, 0); 832 832 833 833 path_ptr->block->dirty = true; 834 834 835 835 /* Jump to the preceeding item */ 836 836 path_ptr--; … … 845 845 ext4_extent_set_first_block(path_ptr->extent, iblock); 846 846 } 847 847 848 848 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1); 849 849 path_ptr->block->dirty = true; 850 850 851 851 /* No more splitting needed */ 852 852 return EOK; 853 853 } 854 854 } 855 855 856 856 assert(path_ptr == path); 857 857 858 858 /* Should be the root split too? */ 859 859 860 860 uint16_t entries = ext4_extent_header_get_entries_count(path->header); 861 861 uint16_t limit = ext4_extent_header_get_max_entries_count(path->header); 862 862 863 863 if (entries == limit) { 864 864 uint32_t new_fblock; … … 866 866 if (rc != EOK) 867 867 return rc; 868 868 869 869 block_t *block; 870 870 rc = block_get(&block, inode_ref->fs->device, new_fblock, … … 872 872 if (rc != EOK) 873 873 return rc; 874 874 875 875 /* Initialize newly allocated block */ 876 876 memset(block->data, 0, block_size); 877 877 878 878 /* Move data from root to the new block */ 879 879 memcpy(block->data, inode_ref->inode->blocks, 880 880 EXT4_INODE_BLOCKS * sizeof(uint32_t)); 881 881 882 882 /* Data block is initialized */ 883 883 884 884 block_t *root_block = path->block; 885 885 uint16_t root_depth = path->depth; 886 886 ext4_extent_header_t *root_header = path->header; 887 887 888 888 /* Make space for tree growing */ 889 889 ext4_extent_path_t *new_root = path; 890 890 ext4_extent_path_t *old_root = path + 1; 891 891 892 892 size_t nbytes = sizeof(ext4_extent_path_t) * (path->depth + 1); 893 893 memmove(old_root, new_root, nbytes); 894 894 memset(new_root, 0, sizeof(ext4_extent_path_t)); 895 895 896 896 /* Update old root structure */ 897 897 old_root->block = block; 898 898 old_root->header = (ext4_extent_header_t *)block->data; 899 899 900 900 /* Add new entry and update limit for entries */ 901 901 if (old_root->depth) { … … 913 913 old_root->index = NULL; 914 914 } 915 915 916 916 ext4_extent_header_set_entries_count(old_root->header, entries + 1); 917 917 ext4_extent_header_set_max_entries_count(old_root->header, limit); 918 918 919 919 old_root->block->dirty = true; 920 920 921 921 /* Re-initialize new root metadata */ 922 922 new_root->depth = root_depth + 1; … … 925 925 new_root->extent = NULL; 926 926 new_root->index = EXT4_EXTENT_FIRST_INDEX(new_root->header); 927 927 928 928 ext4_extent_header_set_depth(new_root->header, new_root->depth); 929 929 930 930 /* Create new entry in root */ 931 931 ext4_extent_header_set_entries_count(new_root->header, 1); 932 932 ext4_extent_index_set_first_block(new_root->index, 0); 933 933 ext4_extent_index_set_leaf(new_root->index, new_fblock); 934 934 935 935 new_root->block->dirty = true; 936 936 } else { … … 943 943 ext4_extent_set_first_block(path->extent, iblock); 944 944 } 945 945 946 946 ext4_extent_header_set_entries_count(path->header, entries + 1); 947 947 path->block->dirty = true; 948 948 } 949 949 950 950 return EOK; 951 951 } … … 970 970 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode); 971 971 uint32_t block_size = ext4_superblock_get_block_size(sb); 972 972 973 973 /* Calculate number of new logical block */ 974 974 uint32_t new_block_idx = 0; … … 976 976 if ((inode_size % block_size) != 0) 977 977 inode_size += block_size - (inode_size % block_size); 978 978 979 979 new_block_idx = inode_size / block_size; 980 980 } 981 981 982 982 /* Load the nearest leaf (with extent) */ 983 983 ext4_extent_path_t *path; … … 985 985 if (rc != EOK) 986 986 return rc; 987 987 988 988 /* Jump to last item of the path (extent) */ 989 989 ext4_extent_path_t *path_ptr = path; 990 990 while (path_ptr->depth != 0) 991 991 path_ptr++; 992 992 993 993 /* Add new extent to the node if not present */ 994 994 if (path_ptr->extent == NULL) 995 995 goto append_extent; 996 996 997 997 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent); 998 998 uint16_t block_limit = (1 << 15); 999 999 1000 1000 uint32_t phys_block = 0; 1001 1001 if (block_count < block_limit) { … … 1006 1006 if (rc != EOK) 1007 1007 goto finish; 1008 1008 1009 1009 /* Initialize extent */ 1010 1010 ext4_extent_set_first_block(path_ptr->extent, new_block_idx); 1011 1011 ext4_extent_set_start(path_ptr->extent, phys_block); 1012 1012 ext4_extent_set_block_count(path_ptr->extent, 1); 1013 1013 1014 1014 /* Update i-node */ 1015 1015 if (update_size) { … … 1017 1017 inode_ref->dirty = true; 1018 1018 } 1019 1019 1020 1020 path_ptr->block->dirty = true; 1021 1021 1022 1022 goto finish; 1023 1023 } else { … … 1025 1025 phys_block = ext4_extent_get_start(path_ptr->extent); 1026 1026 phys_block += ext4_extent_get_block_count(path_ptr->extent); 1027 1027 1028 1028 /* Check if the following block is free for allocation */ 1029 1029 bool free; … … 1031 1031 if (rc != EOK) 1032 1032 goto finish; 1033 1033 1034 1034 if (!free) { 1035 1035 /* Target is not free, new block must be appended to new extent */ 1036 1036 goto append_extent; 1037 1037 } 1038 1038 1039 1039 /* Update extent */ 1040 1040 ext4_extent_set_block_count(path_ptr->extent, block_count + 1); 1041 1041 1042 1042 /* Update i-node */ 1043 1043 if (update_size) { … … 1045 1045 inode_ref->dirty = true; 1046 1046 } 1047 1047 1048 1048 path_ptr->block->dirty = true; 1049 1049 1050 1050 goto finish; 1051 1051 } 1052 1052 } 1053 1054 1053 1054 1055 1055 append_extent: 1056 1056 /* Append new extent to the tree */ 1057 1057 phys_block = 0; 1058 1058 1059 1059 /* Allocate new data block */ 1060 1060 rc = ext4_balloc_alloc_block(inode_ref, &phys_block); 1061 1061 if (rc != EOK) 1062 1062 goto finish; 1063 1063 1064 1064 /* Append extent for new block (includes tree splitting if needed) */ 1065 1065 rc = ext4_extent_append_extent(inode_ref, path, new_block_idx); … … 1068 1068 goto finish; 1069 1069 } 1070 1070 1071 1071 uint32_t tree_depth = ext4_extent_header_get_depth(path->header); 1072 1072 path_ptr = path + tree_depth; 1073 1073 1074 1074 /* Initialize newly created extent */ 1075 1075 ext4_extent_set_block_count(path_ptr->extent, 1); 1076 1076 ext4_extent_set_first_block(path_ptr->extent, new_block_idx); 1077 1077 ext4_extent_set_start(path_ptr->extent, phys_block); 1078 1078 1079 1079 /* Update i-node */ 1080 1080 if (update_size) { … … 1082 1082 inode_ref->dirty = true; 1083 1083 } 1084 1084 1085 1085 path_ptr->block->dirty = true; 1086 1086 1087 1087 finish: 1088 1088 ; … … 1093 1093 *iblock = new_block_idx; 1094 1094 *fblock = phys_block; 1095 1095 1096 1096 /* 1097 1097 * Put loaded blocks … … 1105 1105 } 1106 1106 } 1107 1107 1108 1108 /* Destroy temporary data structure */ 1109 1109 free(path); 1110 1110 1111 1111 return rc; 1112 1112 } -
uspace/lib/ext4/src/filesystem.c
r3061bc1 ra35b458 289 289 return EOK; 290 290 } 291 291 292 292 /* 293 293 * Check incompatible features - if filesystem has some, … … 300 300 if (incompatible_features > 0) 301 301 return ENOTSUP; 302 302 303 303 /* 304 304 * Check read-only features, if filesystem has some, … … 313 313 return EOK; 314 314 } 315 315 316 316 return EOK; 317 317 } … … 331 331 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb); 332 332 uint32_t first_block = ext4_superblock_get_first_data_block(sb); 333 333 334 334 /* First block == 0 or 1 */ 335 335 if (first_block == 0) … … 351 351 { 352 352 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb); 353 353 354 354 if (ext4_superblock_get_first_data_block(sb) == 0) 355 355 return bgid * blocks_per_group + index; … … 392 392 uint64_t bitmap_inode_addr = ext4_block_group_get_inode_bitmap( 393 393 bg_ref->block_group, bg_ref->fs->superblock); 394 394 395 395 block_t *bitmap_block; 396 396 errno_t rc = block_get(&bitmap_block, bg_ref->fs->device, … … 398 398 if (rc != EOK) 399 399 return rc; 400 400 401 401 uint8_t *bitmap = bitmap_block->data; 402 402 403 403 /* Initialize all bitmap bits to zero */ 404 404 uint32_t block_size = ext4_superblock_get_block_size(sb); 405 405 memset(bitmap, 0, block_size); 406 406 407 407 /* Determine the number of reserved blocks in the group */ 408 408 uint32_t reserved_cnt = ext4_filesystem_bg_get_backup_blocks(bg_ref); … … 439 439 440 440 bitmap_block->dirty = true; 441 441 442 442 /* Save bitmap */ 443 443 return block_put(bitmap_block); … … 457 457 bg_ref->block_group, bg_ref->fs->superblock); 458 458 block_t *bitmap_block; 459 459 460 460 errno_t rc = block_get(&bitmap_block, bg_ref->fs->device, 461 461 bitmap_block_addr, BLOCK_FLAGS_NOREAD); 462 462 if (rc != EOK) 463 463 return rc; 464 464 465 465 uint8_t *bitmap = bitmap_block->data; 466 466 467 467 /* Initialize all bitmap bits to zero */ 468 468 uint32_t block_size = ext4_superblock_get_block_size(bg_ref->fs->superblock); … … 470 470 ext4_superblock_get_inodes_per_group(bg_ref->fs->superblock); 471 471 memset(bitmap, 0, (inodes_per_group + 7) / 8); 472 472 473 473 uint32_t start_bit = inodes_per_group; 474 474 uint32_t end_bit = block_size * 8; 475 475 476 476 uint32_t i; 477 477 for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++) 478 478 ext4_bitmap_set_bit(bitmap, i); 479 479 480 480 if (i < end_bit) 481 481 memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3); 482 482 483 483 bitmap_block->dirty = true; 484 484 485 485 /* Save bitmap */ 486 486 return block_put(bitmap_block); … … 497 497 { 498 498 ext4_superblock_t *sb = bg_ref->fs->superblock; 499 499 500 500 uint32_t inode_size = ext4_superblock_get_inode_size(sb); 501 501 uint32_t block_size = ext4_superblock_get_block_size(sb); 502 502 uint32_t inodes_per_block = block_size / inode_size; 503 503 504 504 uint32_t inodes_in_group = 505 505 ext4_superblock_get_inodes_in_group(sb, bg_ref->index); 506 506 507 507 uint32_t table_blocks = inodes_in_group / inodes_per_block; 508 508 509 509 if (inodes_in_group % inodes_per_block) 510 510 table_blocks++; 511 511 512 512 /* Compute initialization bounds */ 513 513 uint32_t first_block = ext4_block_group_get_inode_table_first_block( 514 514 bg_ref->block_group, sb); 515 515 516 516 uint32_t last_block = first_block + table_blocks - 1; 517 517 518 518 /* Initialization of all itable blocks */ 519 519 for (uint32_t fblock = first_block; fblock <= last_block; ++fblock) { … … 523 523 if (rc != EOK) 524 524 return rc; 525 525 526 526 memset(block->data, 0, block_size); 527 527 block->dirty = true; 528 528 529 529 rc = block_put(block); 530 530 if (rc != EOK) 531 531 return rc; 532 532 } 533 533 534 534 return EOK; 535 535 } … … 552 552 if (newref == NULL) 553 553 return ENOMEM; 554 554 555 555 /* Compute number of descriptors, that fits in one data block */ 556 556 uint32_t descriptors_per_block = 557 557 ext4_superblock_get_block_size(fs->superblock) / 558 558 ext4_superblock_get_desc_size(fs->superblock); 559 559 560 560 /* Block group descriptor table starts at the next block after superblock */ 561 561 aoff64_t block_id = 562 562 ext4_superblock_get_first_data_block(fs->superblock) + 1; 563 563 564 564 /* Find the block containing the descriptor we are looking for */ 565 565 block_id += bgid / descriptors_per_block; 566 566 uint32_t offset = (bgid % descriptors_per_block) * 567 567 ext4_superblock_get_desc_size(fs->superblock); 568 568 569 569 /* Load block with descriptors */ 570 570 errno_t rc = block_get(&newref->block, fs->device, block_id, 0); … … 573 573 return rc; 574 574 } 575 575 576 576 /* Initialize in-memory representation */ 577 577 newref->block_group = newref->block->data + offset; … … 579 579 newref->index = bgid; 580 580 newref->dirty = false; 581 581 582 582 *ref = newref; 583 583 584 584 if (ext4_block_group_has_flag(newref->block_group, 585 585 EXT4_BLOCK_GROUP_BLOCK_UNINIT)) { … … 590 590 return rc; 591 591 } 592 592 593 593 ext4_block_group_clear_flag(newref->block_group, 594 594 EXT4_BLOCK_GROUP_BLOCK_UNINIT); 595 595 596 596 newref->dirty = true; 597 597 } 598 598 599 599 if (ext4_block_group_has_flag(newref->block_group, 600 600 EXT4_BLOCK_GROUP_INODE_UNINIT)) { … … 605 605 return rc; 606 606 } 607 607 608 608 ext4_block_group_clear_flag(newref->block_group, 609 609 EXT4_BLOCK_GROUP_INODE_UNINIT); 610 610 611 611 if (!ext4_block_group_has_flag(newref->block_group, 612 612 EXT4_BLOCK_GROUP_ITABLE_ZEROED)) { … … 614 614 if (rc != EOK) 615 615 return rc; 616 616 617 617 ext4_block_group_set_flag(newref->block_group, 618 618 EXT4_BLOCK_GROUP_ITABLE_ZEROED); 619 619 } 620 620 621 621 newref->dirty = true; 622 622 } 623 623 624 624 return EOK; 625 625 } … … 645 645 void *base = bg; 646 646 void *checksum = &bg->checksum; 647 647 648 648 uint32_t offset = (uint32_t) (checksum - base); 649 649 650 650 /* Convert block group index to little endian */ 651 651 uint32_t le_group = host2uint32_t_le(bgid); 652 652 653 653 /* Initialization */ 654 654 crc = crc16_ibm(~0, sb->uuid, sizeof(sb->uuid)); 655 655 656 656 /* Include index of block group */ 657 657 crc = crc16_ibm(crc, (uint8_t *) &le_group, sizeof(le_group)); 658 658 659 659 /* Compute crc from the first part (stop before checksum field) */ 660 660 crc = crc16_ibm(crc, (uint8_t *) bg, offset); 661 661 662 662 /* Skip checksum */ 663 663 offset += sizeof(bg->checksum); 664 664 665 665 /* Checksum of the rest of block group descriptor */ 666 666 if ((ext4_superblock_has_feature_incompatible(sb, … … 670 670 ext4_superblock_get_desc_size(sb) - offset); 671 671 } 672 672 673 673 return crc; 674 674 } … … 813 813 ref->block_group); 814 814 ext4_block_group_set_checksum(ref->block_group, checksum); 815 815 816 816 /* Mark block dirty for writing changes to physical device */ 817 817 ref->block->dirty = true; 818 818 } 819 819 820 820 /* Put back block, that contains block group descriptor */ 821 821 errno_t rc = block_put(ref->block); 822 822 free(ref); 823 823 824 824 return rc; 825 825 } … … 842 842 if (newref == NULL) 843 843 return ENOMEM; 844 844 845 845 /* Compute number of i-nodes, that fits in one data block */ 846 846 uint32_t inodes_per_group = 847 847 ext4_superblock_get_inodes_per_group(fs->superblock); 848 848 849 849 /* 850 850 * Inode numbers are 1-based, but it is simpler to work with 0-based … … 854 854 uint32_t block_group = index / inodes_per_group; 855 855 uint32_t offset_in_group = index % inodes_per_group; 856 856 857 857 /* Load block group, where i-node is located */ 858 858 ext4_block_group_ref_t *bg_ref; … … 862 862 return rc; 863 863 } 864 864 865 865 /* Load block address, where i-node table is located */ 866 866 uint32_t inode_table_start = 867 867 ext4_block_group_get_inode_table_first_block(bg_ref->block_group, 868 868 fs->superblock); 869 869 870 870 /* Put back block group reference (not needed more) */ 871 871 rc = ext4_filesystem_put_block_group_ref(bg_ref); … … 874 874 return rc; 875 875 } 876 876 877 877 /* Compute position of i-node in the block group */ 878 878 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock); 879 879 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock); 880 880 uint32_t byte_offset_in_group = offset_in_group * inode_size; 881 881 882 882 /* Compute block address */ 883 883 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size); … … 887 887 return rc; 888 888 } 889 889 890 890 /* Compute position of i-node in the data block */ 891 891 uint32_t offset_in_block = byte_offset_in_group % block_size; 892 892 newref->inode = newref->block->data + offset_in_block; 893 893 894 894 /* We need to store the original value of index in the reference */ 895 895 newref->index = index + 1; 896 896 newref->fs = fs; 897 897 newref->dirty = false; 898 898 899 899 *ref = newref; 900 900 901 901 return EOK; 902 902 } … … 916 916 ref->block->dirty = true; 917 917 } 918 918 919 919 /* Put back block, that contains i-node */ 920 920 errno_t rc = block_put(ref->block); 921 921 free(ref); 922 922 923 923 return rc; 924 924 } … … 940 940 if (flags & L_DIRECTORY) 941 941 is_dir = true; 942 942 943 943 /* Allocate inode by allocation algorithm */ 944 944 uint32_t index; … … 946 946 if (rc != EOK) 947 947 return rc; 948 948 949 949 /* Load i-node from on-disk i-node table */ 950 950 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref); … … 953 953 return rc; 954 954 } 955 955 956 956 /* Initialize i-node */ 957 957 ext4_inode_t *inode = (*inode_ref)->inode; 958 958 959 959 uint16_t mode; 960 960 if (is_dir) { … … 963 963 * 0777 (octal) == rwxrwxrwx 964 964 */ 965 965 966 966 mode = 0777; 967 967 mode |= EXT4_INODE_MODE_DIRECTORY; … … 973 973 * 0666 (octal) == rw-rw-rw- 974 974 */ 975 975 976 976 mode = 0666; 977 977 mode |= EXT4_INODE_MODE_FILE; … … 979 979 ext4_inode_set_links_count(inode, 0); 980 980 } 981 981 982 982 ext4_inode_set_uid(inode, 0); 983 983 ext4_inode_set_gid(inode, 0); … … 990 990 ext4_inode_set_flags(inode, 0); 991 991 ext4_inode_set_generation(inode, 0); 992 992 993 993 /* Reset blocks array */ 994 994 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++) 995 995 inode->blocks[i] = 0; 996 996 997 997 /* Initialize extents if needed */ 998 998 if (ext4_superblock_has_feature_incompatible( 999 999 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS)) { 1000 1000 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS); 1001 1001 1002 1002 /* Initialize extent root header */ 1003 1003 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode); … … 1006 1006 ext4_extent_header_set_generation(header, 0); 1007 1007 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC); 1008 1008 1009 1009 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) - 1010 1010 sizeof(ext4_extent_header_t)) / sizeof(ext4_extent_t); 1011 1011 1012 1012 ext4_extent_header_set_max_entries_count(header, max_entries); 1013 1013 } 1014 1014 1015 1015 (*inode_ref)->dirty = true; 1016 1016 1017 1017 return EOK; 1018 1018 } … … 1028 1028 { 1029 1029 ext4_filesystem_t *fs = inode_ref->fs; 1030 1030 1031 1031 /* For extents must be data block destroyed by other way */ 1032 1032 if ((ext4_superblock_has_feature_incompatible(fs->superblock, … … 1036 1036 goto finish; 1037 1037 } 1038 1038 1039 1039 /* Release all indirect (no data) blocks */ 1040 1040 1041 1041 /* 1) Single indirect */ 1042 1042 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0); … … 1045 1045 if (rc != EOK) 1046 1046 return rc; 1047 1047 1048 1048 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0); 1049 1049 } 1050 1050 1051 1051 block_t *block; 1052 1052 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock); 1053 1053 uint32_t count = block_size / sizeof(uint32_t); 1054 1054 1055 1055 /* 2) Double indirect */ 1056 1056 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1); … … 1059 1059 if (rc != EOK) 1060 1060 return rc; 1061 1061 1062 1062 uint32_t ind_block; 1063 1063 for (uint32_t offset = 0; offset < count; ++offset) { 1064 1064 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]); 1065 1065 1066 1066 if (ind_block != 0) { 1067 1067 rc = ext4_balloc_free_block(inode_ref, ind_block); … … 1072 1072 } 1073 1073 } 1074 1074 1075 1075 rc = block_put(block); 1076 1076 if (rc != EOK) … … 1080 1080 if (rc != EOK) 1081 1081 return rc; 1082 1082 1083 1083 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0); 1084 1084 } 1085 1085 1086 1086 /* 3) Tripple indirect */ 1087 1087 block_t *subblock; … … 1091 1091 if (rc != EOK) 1092 1092 return rc; 1093 1093 1094 1094 uint32_t ind_block; 1095 1095 for (uint32_t offset = 0; offset < count; ++offset) { 1096 1096 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]); 1097 1097 1098 1098 if (ind_block != 0) { 1099 1099 rc = block_get(&subblock, fs->device, ind_block, … … 1103 1103 return rc; 1104 1104 } 1105 1105 1106 1106 uint32_t ind_subblock; 1107 1107 for (uint32_t suboffset = 0; suboffset < count; … … 1109 1109 ind_subblock = uint32_t_le2host(((uint32_t *) 1110 1110 subblock->data)[suboffset]); 1111 1111 1112 1112 if (ind_subblock != 0) { 1113 1113 rc = ext4_balloc_free_block(inode_ref, ind_subblock); … … 1119 1119 } 1120 1120 } 1121 1121 1122 1122 rc = block_put(subblock); 1123 1123 if (rc != EOK) { … … 1126 1126 } 1127 1127 } 1128 1128 1129 1129 rc = ext4_balloc_free_block(inode_ref, ind_block); 1130 1130 if (rc != EOK) { … … 1133 1133 } 1134 1134 } 1135 1135 1136 1136 rc = block_put(block); 1137 1137 if (rc != EOK) … … 1141 1141 if (rc != EOK) 1142 1142 return rc; 1143 1143 1144 1144 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0); 1145 1145 } 1146 1146 1147 1147 finish: 1148 1148 /* Mark inode dirty for writing to the physical device */ 1149 1149 inode_ref->dirty = true; 1150 1150 1151 1151 /* Free block with extended attributes if present */ 1152 1152 uint32_t xattr_block = ext4_inode_get_file_acl( … … 1156 1156 if (rc != EOK) 1157 1157 return rc; 1158 1158 1159 1159 ext4_inode_set_file_acl(inode_ref->inode, fs->superblock, 0); 1160 1160 } 1161 1161 1162 1162 /* Free inode by allocator */ 1163 1163 errno_t rc; … … 1167 1167 else 1168 1168 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false); 1169 1169 1170 1170 return rc; 1171 1171 } … … 1183 1183 { 1184 1184 ext4_superblock_t *sb = inode_ref->fs->superblock; 1185 1185 1186 1186 /* Check flags, if i-node can be truncated */ 1187 1187 if (!ext4_inode_can_truncate(sb, inode_ref->inode)) 1188 1188 return EINVAL; 1189 1189 1190 1190 /* If sizes are equal, nothing has to be done. */ 1191 1191 aoff64_t old_size = ext4_inode_get_size(sb, inode_ref->inode); 1192 1192 if (old_size == new_size) 1193 1193 return EOK; 1194 1194 1195 1195 /* It's not suppported to make the larger file by truncate operation */ 1196 1196 if (old_size < new_size) 1197 1197 return EINVAL; 1198 1198 1199 1199 /* Compute how many blocks will be released */ 1200 1200 aoff64_t size_diff = old_size - new_size; … … 1203 1203 if (size_diff % block_size != 0) 1204 1204 diff_blocks_count++; 1205 1205 1206 1206 uint32_t old_blocks_count = old_size / block_size; 1207 1207 if (old_size % block_size != 0) 1208 1208 old_blocks_count++; 1209 1209 1210 1210 if ((ext4_superblock_has_feature_incompatible(inode_ref->fs->superblock, 1211 1211 EXT4_FEATURE_INCOMPAT_EXTENTS)) && … … 1218 1218 } else { 1219 1219 /* Release data blocks from the end of file */ 1220 1220 1221 1221 /* Starting from 1 because of logical blocks are numbered from 0 */ 1222 1222 for (uint32_t i = 1; i <= diff_blocks_count; ++i) { … … 1227 1227 } 1228 1228 } 1229 1229 1230 1230 /* Update i-node */ 1231 1231 ext4_inode_set_size(inode_ref->inode, new_size); 1232 1232 inode_ref->dirty = true; 1233 1233 1234 1234 return EOK; 1235 1235 } … … 1248 1248 { 1249 1249 ext4_filesystem_t *fs = inode_ref->fs; 1250 1250 1251 1251 /* For empty file is situation simple */ 1252 1252 if (ext4_inode_get_size(fs->superblock, inode_ref->inode) == 0) { … … 1254 1254 return EOK; 1255 1255 } 1256 1256 1257 1257 uint32_t current_block; 1258 1258 1259 1259 /* Handle i-node using extents */ 1260 1260 if ((ext4_superblock_has_feature_incompatible(fs->superblock, … … 1264 1264 if (rc != EOK) 1265 1265 return rc; 1266 1266 1267 1267 *fblock = current_block; 1268 1268 return EOK; 1269 1269 } 1270 1270 1271 1271 ext4_inode_t *inode = inode_ref->inode; 1272 1272 1273 1273 /* Direct block are read directly from array in i-node structure */ 1274 1274 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) { … … 1277 1277 return EOK; 1278 1278 } 1279 1279 1280 1280 /* Determine indirection level of the target block */ 1281 1281 unsigned int level = 0; … … 1286 1286 } 1287 1287 } 1288 1288 1289 1289 if (level == 0) 1290 1290 return EIO; 1291 1291 1292 1292 /* Compute offsets for the topmost level */ 1293 1293 aoff64_t block_offset_in_level = … … 1296 1296 uint32_t offset_in_block = 1297 1297 block_offset_in_level / fs->inode_blocks_per_level[level - 1]; 1298 1298 1299 1299 /* Sparse file */ 1300 1300 if (current_block == 0) { … … 1302 1302 return EOK; 1303 1303 } 1304 1304 1305 1305 block_t *block; 1306 1306 1307 1307 /* 1308 1308 * Navigate through other levels, until we find the block number … … 1314 1314 if (rc != EOK) 1315 1315 return rc; 1316 1316 1317 1317 /* Read block address from indirect block */ 1318 1318 current_block = 1319 1319 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]); 1320 1320 1321 1321 /* Put back indirect block untouched */ 1322 1322 rc = block_put(block); 1323 1323 if (rc != EOK) 1324 1324 return rc; 1325 1325 1326 1326 /* Check for sparse file */ 1327 1327 if (current_block == 0) { … … 1329 1329 return EOK; 1330 1330 } 1331 1331 1332 1332 /* Jump to the next level */ 1333 1333 level--; 1334 1334 1335 1335 /* Termination condition - we have address of data block loaded */ 1336 1336 if (level == 0) 1337 1337 break; 1338 1338 1339 1339 /* Visit the next level */ 1340 1340 block_offset_in_level %= fs->inode_blocks_per_level[level]; … … 1342 1342 block_offset_in_level / fs->inode_blocks_per_level[level - 1]; 1343 1343 } 1344 1344 1345 1345 *fblock = current_block; 1346 1346 1347 1347 return EOK; 1348 1348 } … … 1361 1361 { 1362 1362 ext4_filesystem_t *fs = inode_ref->fs; 1363 1363 1364 1364 /* Handle inode using extents */ 1365 1365 if ((ext4_superblock_has_feature_compatible(fs->superblock, … … 1369 1369 return ENOTSUP; 1370 1370 } 1371 1371 1372 1372 /* Handle simple case when we are dealing with direct reference */ 1373 1373 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) { 1374 1374 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t) iblock, fblock); 1375 1375 inode_ref->dirty = true; 1376 1376 1377 1377 return EOK; 1378 1378 } 1379 1379 1380 1380 /* Determine the indirection level needed to get the desired block */ 1381 1381 unsigned int level = 0; … … 1386 1386 } 1387 1387 } 1388 1388 1389 1389 if (level == 0) 1390 1390 return EIO; 1391 1391 1392 1392 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock); 1393 1393 1394 1394 /* Compute offsets for the topmost level */ 1395 1395 aoff64_t block_offset_in_level = … … 1399 1399 uint32_t offset_in_block = 1400 1400 block_offset_in_level / fs->inode_blocks_per_level[level - 1]; 1401 1401 1402 1402 uint32_t new_block_addr; 1403 1403 block_t *block; 1404 1404 block_t *new_block; 1405 1405 1406 1406 /* Is needed to allocate indirect block on the i-node level */ 1407 1407 if (current_block == 0) { … … 1410 1410 if (rc != EOK) 1411 1411 return rc; 1412 1412 1413 1413 /* Update i-node */ 1414 1414 ext4_inode_set_indirect_block(inode_ref->inode, level - 1, 1415 1415 new_block_addr); 1416 1416 inode_ref->dirty = true; 1417 1417 1418 1418 /* Load newly allocated block */ 1419 1419 rc = block_get(&new_block, fs->device, new_block_addr, … … 1423 1423 return rc; 1424 1424 } 1425 1425 1426 1426 /* Initialize new block */ 1427 1427 memset(new_block->data, 0, block_size); 1428 1428 new_block->dirty = true; 1429 1429 1430 1430 /* Put back the allocated block */ 1431 1431 rc = block_put(new_block); 1432 1432 if (rc != EOK) 1433 1433 return rc; 1434 1434 1435 1435 current_block = new_block_addr; 1436 1436 } 1437 1437 1438 1438 /* 1439 1439 * Navigate through other levels, until we find the block number … … 1444 1444 if (rc != EOK) 1445 1445 return rc; 1446 1446 1447 1447 current_block = 1448 1448 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]); 1449 1449 1450 1450 if ((level > 1) && (current_block == 0)) { 1451 1451 /* Allocate new block */ … … 1455 1455 return rc; 1456 1456 } 1457 1457 1458 1458 /* Load newly allocated block */ 1459 1459 rc = block_get(&new_block, fs->device, new_block_addr, … … 1463 1463 return rc; 1464 1464 } 1465 1465 1466 1466 /* Initialize allocated block */ 1467 1467 memset(new_block->data, 0, block_size); 1468 1468 new_block->dirty = true; 1469 1469 1470 1470 rc = block_put(new_block); 1471 1471 if (rc != EOK) { … … 1473 1473 return rc; 1474 1474 } 1475 1475 1476 1476 /* Write block address to the parent */ 1477 1477 ((uint32_t *) block->data)[offset_in_block] = … … 1480 1480 current_block = new_block_addr; 1481 1481 } 1482 1482 1483 1483 /* Will be finished, write the fblock address */ 1484 1484 if (level == 1) { … … 1487 1487 block->dirty = true; 1488 1488 } 1489 1489 1490 1490 rc = block_put(block); 1491 1491 if (rc != EOK) 1492 1492 return rc; 1493 1493 1494 1494 level--; 1495 1495 1496 1496 /* 1497 1497 * If we are on the last level, break here as … … 1500 1500 if (level == 0) 1501 1501 break; 1502 1502 1503 1503 /* Visit the next level */ 1504 1504 block_offset_in_level %= fs->inode_blocks_per_level[level]; … … 1506 1506 block_offset_in_level / fs->inode_blocks_per_level[level - 1]; 1507 1507 } 1508 1508 1509 1509 return EOK; 1510 1510 } … … 1522 1522 { 1523 1523 uint32_t fblock; 1524 1524 1525 1525 ext4_filesystem_t *fs = inode_ref->fs; 1526 1526 1527 1527 /* Extents are handled otherwise = there is not support in this function */ 1528 1528 assert(!(ext4_superblock_has_feature_incompatible(fs->superblock, 1529 1529 EXT4_FEATURE_INCOMPAT_EXTENTS) && 1530 1530 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)))); 1531 1531 1532 1532 ext4_inode_t *inode = inode_ref->inode; 1533 1533 1534 1534 /* Handle simple case when we are dealing with direct reference */ 1535 1535 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) { 1536 1536 fblock = ext4_inode_get_direct_block(inode, iblock); 1537 1537 1538 1538 /* Sparse file */ 1539 1539 if (fblock == 0) 1540 1540 return EOK; 1541 1541 1542 1542 ext4_inode_set_direct_block(inode, iblock, 0); 1543 1543 return ext4_balloc_free_block(inode_ref, fblock); 1544 1544 } 1545 1545 1546 1546 /* Determine the indirection level needed to get the desired block */ 1547 1547 unsigned int level = 0; … … 1552 1552 } 1553 1553 } 1554 1554 1555 1555 if (level == 0) 1556 1556 return EIO; 1557 1557 1558 1558 /* Compute offsets for the topmost level */ 1559 1559 aoff64_t block_offset_in_level = … … 1563 1563 uint32_t offset_in_block = 1564 1564 block_offset_in_level / fs->inode_blocks_per_level[level - 1]; 1565 1565 1566 1566 /* 1567 1567 * Navigate through other levels, until we find the block number … … 1570 1570 block_t *block; 1571 1571 while (level > 0) { 1572 1572 1573 1573 /* Sparse check */ 1574 1574 if (current_block == 0) 1575 1575 return EOK; 1576 1576 1577 1577 errno_t rc = block_get(&block, fs->device, current_block, 0); 1578 1578 if (rc != EOK) 1579 1579 return rc; 1580 1580 1581 1581 current_block = 1582 1582 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]); 1583 1583 1584 1584 /* Set zero if physical data block address found */ 1585 1585 if (level == 1) { … … 1588 1588 block->dirty = true; 1589 1589 } 1590 1590 1591 1591 rc = block_put(block); 1592 1592 if (rc != EOK) 1593 1593 return rc; 1594 1594 1595 1595 level--; 1596 1596 1597 1597 /* 1598 1598 * If we are on the last level, break here as … … 1601 1601 if (level == 0) 1602 1602 break; 1603 1603 1604 1604 /* Visit the next level */ 1605 1605 block_offset_in_level %= fs->inode_blocks_per_level[level]; … … 1607 1607 block_offset_in_level / fs->inode_blocks_per_level[level - 1]; 1608 1608 } 1609 1609 1610 1610 fblock = current_block; 1611 1611 if (fblock == 0) 1612 1612 return EOK; 1613 1613 1614 1614 /* Physical block is not referenced, it can be released */ 1615 1615 return ext4_balloc_free_block(inode_ref, fblock); … … 1633 1633 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) 1634 1634 return ext4_extent_append_block(inode_ref, iblock, fblock, true); 1635 1635 1636 1636 ext4_superblock_t *sb = inode_ref->fs->superblock; 1637 1637 1638 1638 /* Compute next block index and allocate data block */ 1639 1639 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode); 1640 1640 uint32_t block_size = ext4_superblock_get_block_size(sb); 1641 1641 1642 1642 /* Align size i-node size */ 1643 1643 if ((inode_size % block_size) != 0) 1644 1644 inode_size += block_size - (inode_size % block_size); 1645 1645 1646 1646 /* Logical blocks are numbered from 0 */ 1647 1647 uint32_t new_block_idx = inode_size / block_size; 1648 1648 1649 1649 /* Allocate new physical block */ 1650 1650 uint32_t phys_block; … … 1652 1652 if (rc != EOK) 1653 1653 return rc; 1654 1654 1655 1655 /* Add physical block address to the i-node */ 1656 1656 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, … … 1660 1660 return rc; 1661 1661 } 1662 1662 1663 1663 /* Update i-node */ 1664 1664 ext4_inode_set_size(inode_ref->inode, inode_size + block_size); 1665 1665 inode_ref->dirty = true; 1666 1666 1667 1667 *fblock = phys_block; 1668 1668 *iblock = new_block_idx; 1669 1669 1670 1670 return EOK; 1671 1671 } -
uspace/lib/ext4/src/ialloc.c
r3061bc1 ra35b458 100 100 { 101 101 ext4_superblock_t *sb = fs->superblock; 102 102 103 103 /* Compute index of block group and load it */ 104 104 uint32_t block_group = ext4_ialloc_get_bgid_of_inode(sb, index); 105 105 106 106 ext4_block_group_ref_t *bg_ref; 107 107 errno_t rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref); 108 108 if (rc != EOK) 109 109 return rc; 110 110 111 111 /* Load i-node bitmap */ 112 112 uint32_t bitmap_block_addr = ext4_block_group_get_inode_bitmap( … … 117 117 if (rc != EOK) 118 118 return rc; 119 119 120 120 /* Free i-node in the bitmap */ 121 121 uint32_t index_in_group = ext4_ialloc_inode2index_in_group(sb, index); 122 122 ext4_bitmap_free_bit(bitmap_block->data, index_in_group); 123 123 bitmap_block->dirty = true; 124 124 125 125 /* Put back the block with bitmap */ 126 126 rc = block_put(bitmap_block); … … 130 130 return rc; 131 131 } 132 132 133 133 /* If released i-node is a directory, decrement used directories count */ 134 134 if (is_dir) { … … 139 139 bg_used_dirs); 140 140 } 141 141 142 142 /* Update block group free inodes count */ 143 143 uint32_t free_inodes = ext4_block_group_get_free_inodes_count( … … 146 146 ext4_block_group_set_free_inodes_count(bg_ref->block_group, sb, 147 147 free_inodes); 148 148 149 149 bg_ref->dirty = true; 150 150 151 151 /* Put back the modified block group */ 152 152 rc = ext4_filesystem_put_block_group_ref(bg_ref); 153 153 if (rc != EOK) 154 154 return rc; 155 155 156 156 /* Update superblock free inodes count */ 157 157 uint32_t sb_free_inodes = … … 159 159 sb_free_inodes++; 160 160 ext4_superblock_set_free_inodes_count(sb, sb_free_inodes); 161 161 162 162 return EOK; 163 163 } … … 178 178 { 179 179 ext4_superblock_t *sb = fs->superblock; 180 180 181 181 uint32_t bgid = 0; 182 182 uint32_t bg_count = ext4_superblock_get_block_group_count(sb); 183 183 uint32_t sb_free_inodes = ext4_superblock_get_free_inodes_count(sb); 184 184 uint32_t avg_free_inodes = sb_free_inodes / bg_count; 185 185 186 186 /* Try to find free i-node in all block groups */ 187 187 while (bgid < bg_count) { … … 191 191 if (rc != EOK) 192 192 return rc; 193 193 194 194 ext4_block_group_t *bg = bg_ref->block_group; 195 195 196 196 /* Read necessary values for algorithm */ 197 197 uint32_t free_blocks = ext4_block_group_get_free_blocks_count(bg, sb); 198 198 uint32_t free_inodes = ext4_block_group_get_free_inodes_count(bg, sb); 199 199 uint32_t used_dirs = ext4_block_group_get_used_dirs_count(bg, sb); 200 200 201 201 /* 202 202 * Check if this block group is a good candidate … … 216 216 uint32_t bitmap_block_addr = ext4_block_group_get_inode_bitmap( 217 217 bg_ref->block_group, sb); 218 218 219 219 block_t *bitmap_block; 220 220 rc = block_get(&bitmap_block, fs->device, bitmap_block_addr, … … 224 224 return rc; 225 225 } 226 226 227 227 /* Try to allocate i-node in the bitmap */ 228 228 uint32_t inodes_in_group = ext4_superblock_get_inodes_in_group(sb, bgid); … … 230 230 rc = ext4_bitmap_find_free_bit_and_set(bitmap_block->data, 231 231 0, &index_in_group, inodes_in_group); 232 232 233 233 /* Block group has not any free i-node */ 234 234 if (rc == ENOSPC) { … … 246 246 continue; 247 247 } 248 248 249 249 /* Free i-node found, save the bitmap */ 250 250 bitmap_block->dirty = true; 251 251 252 252 rc = block_put(bitmap_block); 253 253 if (rc != EOK) { … … 255 255 return rc; 256 256 } 257 257 258 258 /* Modify filesystem counters */ 259 259 free_inodes--; 260 260 ext4_block_group_set_free_inodes_count(bg, sb, free_inodes); 261 261 262 262 /* Increment used directories counter */ 263 263 if (is_dir) { … … 265 265 ext4_block_group_set_used_dirs_count(bg, sb, used_dirs); 266 266 } 267 267 268 268 /* Decrease unused inodes count */ 269 269 if (ext4_block_group_has_flag(bg, … … 271 271 uint32_t unused = 272 272 ext4_block_group_get_itable_unused(bg, sb); 273 273 274 274 uint32_t inodes_in_group = 275 275 ext4_superblock_get_inodes_in_group(sb, bgid); 276 276 277 277 uint32_t free = inodes_in_group - unused; 278 278 279 279 if (index_in_group >= free) { 280 280 unused = inodes_in_group - (index_in_group + 1); … … 282 282 } 283 283 } 284 284 285 285 /* Save modified block group */ 286 286 bg_ref->dirty = true; 287 287 288 288 rc = ext4_filesystem_put_block_group_ref(bg_ref); 289 289 if (rc != EOK) 290 290 return rc; 291 291 292 292 /* Update superblock */ 293 293 sb_free_inodes--; 294 294 ext4_superblock_set_free_inodes_count(sb, sb_free_inodes); 295 295 296 296 /* Compute the absolute i-nodex number */ 297 297 *index = ext4_ialloc_index_in_group2inode(sb, index_in_group, bgid); 298 298 299 299 return EOK; 300 300 } 301 301 302 302 /* Block group not modified, put it and jump to the next block group */ 303 303 rc = ext4_filesystem_put_block_group_ref(bg_ref); … … 307 307 ++bgid; 308 308 } 309 309 310 310 return ENOSPC; 311 311 } -
uspace/lib/ext4/src/inode.c
r3061bc1 ra35b458 53 53 uint32_t bits = 8; 54 54 uint32_t size = block_size; 55 55 56 56 do { 57 57 bits++; 58 58 size = size >> 1; 59 59 } while (size > 256); 60 60 61 61 return bits; 62 62 } … … 76 76 ((uint32_t) uint16_t_le2host(inode->mode)); 77 77 } 78 78 79 79 return uint16_t_le2host(inode->mode); 80 80 } … … 90 90 { 91 91 inode->mode = host2uint16_t_le((mode << 16) >> 16); 92 92 93 93 if (ext4_superblock_get_creator_os(sb) == EXT4_SUPERBLOCK_OS_HURD) 94 94 inode->osd2.hurd2.mode_high = host2uint16_t_le(mode >> 16); … … 129 129 { 130 130 uint32_t major_rev = ext4_superblock_get_rev_level(sb); 131 131 132 132 if ((major_rev > 0) && 133 133 (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE))) 134 134 return ((uint64_t)uint32_t_le2host(inode->size_hi)) << 32 | 135 135 ((uint64_t)uint32_t_le2host(inode->size_lo)); 136 136 137 137 return uint32_t_le2host(inode->size_lo); 138 138 } … … 304 304 uint16_t_le2host(inode->osd2.linux2.blocks_high)) << 32 | 305 305 uint32_t_le2host(inode->blocks_count_lo); 306 306 307 307 if (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_HUGE_FILE)) { 308 308 uint32_t block_size = ext4_superblock_get_block_size(sb); … … 330 330 uint64_t max = 0; 331 331 max = ~max >> 32; 332 332 333 333 if (count <= max) { 334 334 inode->blocks_count_lo = host2uint32_t_le(count); 335 335 inode->osd2.linux2.blocks_high = 0; 336 336 ext4_inode_clear_flag(inode, EXT4_INODE_FLAG_HUGE_FILE); 337 337 338 338 return EOK; 339 339 } 340 340 341 341 /* Check if there can be used huge files (many blocks) */ 342 342 if (!ext4_superblock_has_feature_read_only(sb, 343 343 EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) 344 344 return EINVAL; 345 345 346 346 /* 48-bit maximum */ 347 347 max = 0; 348 348 max = ~max >> 16; 349 349 350 350 if (count <= max) { 351 351 inode->blocks_count_lo = host2uint32_t_le(count); … … 360 360 inode->osd2.linux2.blocks_high = host2uint16_t_le(count >> 32); 361 361 } 362 362 363 363 return EOK; 364 364 } … … 424 424 uint16_t_le2host(inode->osd2.linux2.file_acl_high)) << 16 | 425 425 (uint32_t_le2host(inode->file_acl_lo)); 426 426 427 427 return uint32_t_le2host(inode->file_acl_lo); 428 428 } … … 439 439 { 440 440 inode->file_acl_lo = host2uint32_t_le((file_acl << 32) >> 32); 441 441 442 442 if (ext4_superblock_get_creator_os(sb) == EXT4_SUPERBLOCK_OS_LINUX) 443 443 inode->osd2.linux2.file_acl_high = host2uint16_t_le(file_acl >> 32); … … 455 455 { 456 456 assert(idx < EXT4_INODE_DIRECT_BLOCK_COUNT); 457 457 458 458 return uint32_t_le2host(inode->blocks[idx]); 459 459 } … … 469 469 { 470 470 assert(idx < EXT4_INODE_DIRECT_BLOCK_COUNT); 471 471 472 472 inode->blocks[idx] = host2uint32_t_le(fblock); 473 473 } … … 540 540 if (ext4_inode_get_flags(inode) & flag) 541 541 return true; 542 542 543 543 return false; 544 544 } … … 583 583 (ext4_inode_has_flag(inode, EXT4_INODE_FLAG_IMMUTABLE))) 584 584 return false; 585 585 586 586 if ((ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_FILE)) || 587 587 (ext4_inode_is_type(sb, inode, EXT4_INODE_MODE_DIRECTORY))) 588 588 return true; 589 589 590 590 return false; 591 591 } -
uspace/lib/ext4/src/ops.c
r3061bc1 ra35b458 117 117 node_key_t *key = (node_key_t *)key_arg; 118 118 ext4_node_t *enode = hash_table_get_inst(item, ext4_node_t, link); 119 119 120 120 return key->service_id == enode->instance->service_id 121 121 && key->index == enode->inode_ref->index; … … 142 142 if (!hash_table_create(&open_nodes, 0, 0, &open_nodes_ops)) 143 143 return ENOMEM; 144 144 145 145 return EOK; 146 146 } … … 173 173 { 174 174 fibril_mutex_lock(&instance_list_mutex); 175 175 176 176 if (list_empty(&instance_list)) { 177 177 fibril_mutex_unlock(&instance_list_mutex); 178 178 return EINVAL; 179 179 } 180 180 181 181 list_foreach(instance_list, link, ext4_instance_t, tmp) { 182 182 if (tmp->service_id == service_id) { … … 186 186 } 187 187 } 188 188 189 189 fibril_mutex_unlock(&instance_list_mutex); 190 190 return EINVAL; … … 219 219 ext4_node_t *eparent = EXT4_NODE(pfn); 220 220 ext4_filesystem_t *fs = eparent->instance->filesystem; 221 221 222 222 if (!ext4_inode_is_type(fs->superblock, eparent->inode_ref->inode, 223 223 EXT4_INODE_MODE_DIRECTORY)) 224 224 return ENOTDIR; 225 225 226 226 /* Try to find entry */ 227 227 ext4_directory_search_result_t result; … … 233 233 return EOK; 234 234 } 235 236 return rc; 237 } 238 235 236 return rc; 237 } 238 239 239 /* Load node from search result */ 240 240 uint32_t inode = ext4_directory_entry_ll_get_inode(result.dentry); … … 268 268 if (rc != EOK) 269 269 return rc; 270 270 271 271 return ext4_node_get_core(rfn, inst, index); 272 272 } … … 285 285 { 286 286 fibril_mutex_lock(&open_nodes_lock); 287 287 288 288 /* Check if the node is not already open */ 289 289 node_key_t key = { … … 291 291 .index = index 292 292 }; 293 293 294 294 ht_link_t *already_open = hash_table_find(&open_nodes, &key); 295 295 ext4_node_t *enode = NULL; … … 298 298 *rfn = enode->fs_node; 299 299 enode->references++; 300 300 301 301 fibril_mutex_unlock(&open_nodes_lock); 302 302 return EOK; 303 303 } 304 304 305 305 /* Prepare new enode */ 306 306 enode = malloc(sizeof(ext4_node_t)); … … 309 309 return ENOMEM; 310 310 } 311 311 312 312 /* Prepare new fs_node and initialize */ 313 313 fs_node_t *fs_node = malloc(sizeof(fs_node_t)); … … 317 317 return ENOMEM; 318 318 } 319 319 320 320 fs_node_initialize(fs_node); 321 321 322 322 /* Load i-node from filesystem */ 323 323 ext4_inode_ref_t *inode_ref; … … 330 330 return rc; 331 331 } 332 332 333 333 /* Initialize enode */ 334 334 enode->inode_ref = inode_ref; … … 336 336 enode->references = 1; 337 337 enode->fs_node = fs_node; 338 338 339 339 fs_node->data = enode; 340 340 *rfn = fs_node; 341 341 342 342 hash_table_insert(&open_nodes, &enode->link); 343 343 inst->open_nodes_count++; 344 344 345 345 fibril_mutex_unlock(&open_nodes_lock); 346 346 347 347 return EOK; 348 348 } … … 360 360 assert(enode->instance->open_nodes_count > 0); 361 361 enode->instance->open_nodes_count--; 362 362 363 363 /* Put inode back in filesystem */ 364 364 errno_t rc = ext4_filesystem_put_inode_ref(enode->inode_ref); 365 365 if (rc != EOK) 366 366 return rc; 367 367 368 368 /* Destroy data structure */ 369 369 free(enode->fs_node); 370 370 free(enode); 371 371 372 372 return EOK; 373 373 } … … 399 399 { 400 400 fibril_mutex_lock(&open_nodes_lock); 401 401 402 402 ext4_node_t *enode = EXT4_NODE(fn); 403 403 assert(enode->references > 0); … … 410 410 } 411 411 } 412 412 413 413 fibril_mutex_unlock(&open_nodes_lock); 414 414 415 415 return EOK; 416 416 } … … 432 432 if (enode == NULL) 433 433 return ENOMEM; 434 434 435 435 /* Allocate fs_node */ 436 436 fs_node_t *fs_node; … … 440 440 return ENOMEM; 441 441 } 442 442 443 443 /* Load instance */ 444 444 ext4_instance_t *inst; … … 449 449 return rc; 450 450 } 451 451 452 452 /* Allocate new i-node in filesystem */ 453 453 ext4_inode_ref_t *inode_ref; … … 458 458 return rc; 459 459 } 460 460 461 461 /* Do some interconnections in references */ 462 462 enode->inode_ref = inode_ref; 463 463 enode->instance = inst; 464 464 enode->references = 1; 465 465 466 466 fibril_mutex_lock(&open_nodes_lock); 467 467 hash_table_insert(&open_nodes, &enode->link); 468 468 fibril_mutex_unlock(&open_nodes_lock); 469 469 inst->open_nodes_count++; 470 470 471 471 enode->inode_ref->dirty = true; 472 472 473 473 fs_node_initialize(fs_node); 474 474 fs_node->data = enode; 475 475 enode->fs_node = fs_node; 476 476 *rfn = fs_node; 477 477 478 478 return EOK; 479 479 } … … 495 495 return rc; 496 496 } 497 497 498 498 if (has_children) { 499 499 ext4_node_put(fn); 500 500 return EINVAL; 501 501 } 502 502 503 503 ext4_node_t *enode = EXT4_NODE(fn); 504 504 ext4_inode_ref_t *inode_ref = enode->inode_ref; 505 505 506 506 /* Release data blocks */ 507 507 rc = ext4_filesystem_truncate_inode(inode_ref, 0); … … 510 510 return rc; 511 511 } 512 512 513 513 /* 514 514 * TODO: Sset real deletion time when it will be supported. … … 517 517 ext4_inode_set_deletion_time(inode_ref->inode, 0xdeadbeef); 518 518 inode_ref->dirty = true; 519 519 520 520 /* Free inode */ 521 521 rc = ext4_filesystem_free_inode(inode_ref); … … 524 524 return rc; 525 525 } 526 526 527 527 return ext4_node_put(fn); 528 528 } … … 542 542 if (str_size(name) > EXT4_DIRECTORY_FILENAME_LEN) 543 543 return ENAMETOOLONG; 544 544 545 545 ext4_node_t *parent = EXT4_NODE(pfn); 546 546 ext4_node_t *child = EXT4_NODE(cfn); 547 547 ext4_filesystem_t *fs = parent->instance->filesystem; 548 548 549 549 /* Add entry to parent directory */ 550 550 errno_t rc = ext4_directory_add_entry(parent->inode_ref, name, … … 552 552 if (rc != EOK) 553 553 return rc; 554 554 555 555 /* Fill new dir -> add '.' and '..' entries */ 556 556 if (ext4_inode_is_type(fs->superblock, child->inode_ref->inode, … … 562 562 return rc; 563 563 } 564 564 565 565 rc = ext4_directory_add_entry(child->inode_ref, "..", 566 566 parent->inode_ref); … … 570 570 return rc; 571 571 } 572 572 573 573 /* Initialize directory index if supported */ 574 574 if (ext4_superblock_has_feature_compatible(fs->superblock, … … 577 577 if (rc != EOK) 578 578 return rc; 579 579 580 580 ext4_inode_set_flag(child->inode_ref->inode, 581 581 EXT4_INODE_FLAG_INDEX); 582 582 child->inode_ref->dirty = true; 583 583 } 584 584 585 585 uint16_t parent_links = 586 586 ext4_inode_get_links_count(parent->inode_ref->inode); 587 587 parent_links++; 588 588 ext4_inode_set_links_count(parent->inode_ref->inode, parent_links); 589 589 590 590 parent->inode_ref->dirty = true; 591 591 } 592 592 593 593 uint16_t child_links = 594 594 ext4_inode_get_links_count(child->inode_ref->inode); 595 595 child_links++; 596 596 ext4_inode_set_links_count(child->inode_ref->inode, child_links); 597 597 598 598 child->inode_ref->dirty = true; 599 599 600 600 return EOK; 601 601 } … … 616 616 if (rc != EOK) 617 617 return rc; 618 618 619 619 /* Cannot unlink non-empty node */ 620 620 if (has_children) 621 621 return ENOTEMPTY; 622 622 623 623 /* Remove entry from parent directory */ 624 624 ext4_inode_ref_t *parent = EXT4_NODE(pfn)->inode_ref; … … 626 626 if (rc != EOK) 627 627 return rc; 628 628 629 629 /* Decrement links count */ 630 630 ext4_inode_ref_t *child_inode_ref = EXT4_NODE(cfn)->inode_ref; 631 631 632 632 uint32_t lnk_count = 633 633 ext4_inode_get_links_count(child_inode_ref->inode); 634 634 lnk_count--; 635 635 636 636 /* If directory - handle links from parent */ 637 637 if ((lnk_count <= 1) && (ext4_is_directory(cfn))) { 638 638 assert(lnk_count == 1); 639 639 640 640 lnk_count--; 641 641 642 642 ext4_inode_ref_t *parent_inode_ref = EXT4_NODE(pfn)->inode_ref; 643 643 644 644 uint32_t parent_lnk_count = ext4_inode_get_links_count( 645 645 parent_inode_ref->inode); 646 646 647 647 parent_lnk_count--; 648 648 ext4_inode_set_links_count(parent_inode_ref->inode, parent_lnk_count); 649 649 650 650 parent->dirty = true; 651 651 } … … 659 659 * parent->dirty = true; 660 660 */ 661 661 662 662 /* 663 663 * TODO: Update timestamp for inode. … … 666 666 * (uint32_t) now); 667 667 */ 668 668 669 669 ext4_inode_set_links_count(child_inode_ref->inode, lnk_count); 670 670 child_inode_ref->dirty = true; 671 671 672 672 return EOK; 673 673 } … … 687 687 ext4_node_t *enode = EXT4_NODE(fn); 688 688 ext4_filesystem_t *fs = enode->instance->filesystem; 689 689 690 690 /* Check if node is directory */ 691 691 if (!ext4_inode_is_type(fs->superblock, enode->inode_ref->inode, … … 694 694 return EOK; 695 695 } 696 696 697 697 ext4_directory_iterator_t it; 698 698 errno_t rc = ext4_directory_iterator_init(&it, enode->inode_ref, 0); 699 699 if (rc != EOK) 700 700 return rc; 701 701 702 702 /* Find a non-empty directory entry */ 703 703 bool found = false; … … 712 712 } 713 713 } 714 714 715 715 rc = ext4_directory_iterator_next(&it); 716 716 if (rc != EOK) { … … 719 719 } 720 720 } 721 721 722 722 rc = ext4_directory_iterator_fini(&it); 723 723 if (rc != EOK) 724 724 return rc; 725 725 726 726 *has_children = found; 727 727 728 728 return EOK; 729 729 } … … 767 767 ext4_node_t *enode = EXT4_NODE(fn); 768 768 uint32_t lnkcnt = ext4_inode_get_links_count(enode->inode_ref->inode); 769 769 770 770 if (ext4_is_directory(fn)) { 771 771 if (lnkcnt > 1) … … 774 774 return 0; 775 775 } 776 776 777 777 /* For regular files return real links count */ 778 778 return lnkcnt; … … 790 790 ext4_node_t *enode = EXT4_NODE(fn); 791 791 ext4_superblock_t *sb = enode->instance->filesystem->superblock; 792 792 793 793 return ext4_inode_is_type(sb, enode->inode_ref->inode, 794 794 EXT4_INODE_MODE_DIRECTORY); … … 806 806 ext4_node_t *enode = EXT4_NODE(fn); 807 807 ext4_superblock_t *sb = enode->instance->filesystem->superblock; 808 808 809 809 return ext4_inode_is_type(sb, enode->inode_ref->inode, 810 810 EXT4_INODE_MODE_FILE); … … 928 928 { 929 929 ext4_filesystem_t *fs; 930 930 931 931 /* Allocate instance structure */ 932 932 ext4_instance_t *inst = (ext4_instance_t *) … … 934 934 if (inst == NULL) 935 935 return ENOMEM; 936 936 937 937 enum cache_mode cmode; 938 938 if (str_cmp(opts, "wtcache") == 0) … … 940 940 else 941 941 cmode = CACHE_MODE_WB; 942 942 943 943 /* Initialize instance */ 944 944 link_initialize(&inst->link); 945 945 inst->service_id = service_id; 946 946 inst->open_nodes_count = 0; 947 947 948 948 /* Initialize the filesystem */ 949 949 aoff64_t rnsize; … … 953 953 return rc; 954 954 } 955 955 956 956 /* Add instance to the list */ 957 957 fibril_mutex_lock(&instance_list_mutex); 958 958 list_append(&inst->link, &instance_list); 959 959 fibril_mutex_unlock(&instance_list_mutex); 960 960 961 961 *index = EXT4_INODE_ROOT_INDEX; 962 962 *size = rnsize; 963 963 964 964 return EOK; 965 965 } … … 980 980 if (rc != EOK) 981 981 return rc; 982 982 983 983 fibril_mutex_lock(&open_nodes_lock); 984 984 985 985 if (inst->open_nodes_count != 0) { 986 986 fibril_mutex_unlock(&open_nodes_lock); 987 987 return EBUSY; 988 988 } 989 989 990 990 /* Remove the instance from the list */ 991 991 fibril_mutex_lock(&instance_list_mutex); 992 992 list_remove(&inst->link); 993 993 fibril_mutex_unlock(&instance_list_mutex); 994 994 995 995 fibril_mutex_unlock(&open_nodes_lock); 996 996 997 997 rc = ext4_filesystem_close(inst->filesystem); 998 998 if (rc != EOK) { … … 1028 1028 return EINVAL; 1029 1029 } 1030 1030 1031 1031 ext4_instance_t *inst; 1032 1032 errno_t rc = ext4_instance_get(service_id, &inst); … … 1035 1035 return rc; 1036 1036 } 1037 1037 1038 1038 /* Load i-node */ 1039 1039 ext4_inode_ref_t *inode_ref; … … 1043 1043 return rc; 1044 1044 } 1045 1045 1046 1046 /* Read from i-node by type */ 1047 1047 if (ext4_inode_is_type(inst->filesystem->superblock, inode_ref->inode, … … 1058 1058 rc = ENOTSUP; 1059 1059 } 1060 1060 1061 1061 errno_t const rc2 = ext4_filesystem_put_inode_ref(inode_ref); 1062 1062 1063 1063 return rc == EOK ? rc2 : rc; 1064 1064 } … … 1076 1076 if ((name_size == 1) && (name[0] == '.')) 1077 1077 return true; 1078 1078 1079 1079 if ((name_size == 2) && (name[0] == '.') && (name[1] == '.')) 1080 1080 return true; 1081 1081 1082 1082 return false; 1083 1083 } … … 1104 1104 return rc; 1105 1105 } 1106 1106 1107 1107 /* 1108 1108 * Find next interesting directory entry. … … 1114 1114 if (it.current->inode == 0) 1115 1115 goto skip; 1116 1116 1117 1117 uint16_t name_size = ext4_directory_entry_ll_get_name_length( 1118 1118 inst->filesystem->superblock, it.current); 1119 1119 1120 1120 /* Skip . and .. */ 1121 1121 if (ext4_is_dots(it.current->name, name_size)) 1122 1122 goto skip; 1123 1123 1124 1124 /* 1125 1125 * The on-disk entry does not contain \0 at the end … … 1133 1133 return ENOMEM; 1134 1134 } 1135 1135 1136 1136 memcpy(buf, &it.current->name, name_size); 1137 1137 *(buf + name_size) = 0; 1138 1138 found = true; 1139 1139 1140 1140 (void) async_data_read_finalize(callid, buf, name_size + 1); 1141 1141 free(buf); 1142 1142 break; 1143 1143 1144 1144 skip: 1145 1145 rc = ext4_directory_iterator_next(&it); … … 1150 1150 } 1151 1151 } 1152 1152 1153 1153 uint64_t next; 1154 1154 if (found) { … … 1156 1156 if (rc != EOK) 1157 1157 return rc; 1158 1158 1159 1159 next = it.current_offset; 1160 1160 } 1161 1161 1162 1162 rc = ext4_directory_iterator_fini(&it); 1163 1163 if (rc != EOK) 1164 1164 return rc; 1165 1165 1166 1166 /* Prepare return values */ 1167 1167 if (found) { … … 1191 1191 ext4_superblock_t *sb = inst->filesystem->superblock; 1192 1192 uint64_t file_size = ext4_inode_get_size(sb, inode_ref->inode); 1193 1193 1194 1194 if (pos >= file_size) { 1195 1195 /* Read 0 bytes successfully */ … … 1198 1198 return EOK; 1199 1199 } 1200 1200 1201 1201 /* For now, we only read data from one block at a time */ 1202 1202 uint32_t block_size = ext4_superblock_get_block_size(sb); … … 1204 1204 uint32_t offset_in_block = pos % block_size; 1205 1205 uint32_t bytes = min(block_size - offset_in_block, size); 1206 1206 1207 1207 /* Handle end of file */ 1208 1208 if (pos + bytes > file_size) 1209 1209 bytes = file_size - pos; 1210 1210 1211 1211 /* Get the real block number */ 1212 1212 uint32_t fs_block; … … 1217 1217 return rc; 1218 1218 } 1219 1219 1220 1220 /* 1221 1221 * Check for sparse file. … … 1231 1231 return ENOMEM; 1232 1232 } 1233 1233 1234 1234 memset(buffer, 0, bytes); 1235 1235 1236 1236 rc = async_data_read_finalize(callid, buffer, bytes); 1237 1237 *rbytes = bytes; 1238 1238 1239 1239 free(buffer); 1240 1240 return rc; 1241 1241 } 1242 1242 1243 1243 /* Usual case - we need to read a block from device */ 1244 1244 block_t *block; … … 1248 1248 return rc; 1249 1249 } 1250 1250 1251 1251 assert(offset_in_block + bytes <= block_size); 1252 1252 rc = async_data_read_finalize(callid, block->data + offset_in_block, bytes); … … 1255 1255 return rc; 1256 1256 } 1257 1257 1258 1258 rc = block_put(block); 1259 1259 if (rc != EOK) 1260 1260 return rc; 1261 1261 1262 1262 *rbytes = bytes; 1263 1263 return EOK; … … 1282 1282 if (rc != EOK) 1283 1283 return rc; 1284 1284 1285 1285 ipc_callid_t callid; 1286 1286 size_t len; … … 1290 1290 goto exit; 1291 1291 } 1292 1292 1293 1293 ext4_node_t *enode = EXT4_NODE(fn); 1294 1294 ext4_filesystem_t *fs = enode->instance->filesystem; 1295 1295 1296 1296 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock); 1297 1297 1298 1298 /* Prevent writing to more than one block */ 1299 1299 uint32_t bytes = min(len, block_size - (pos % block_size)); 1300 1300 1301 1301 int flags = BLOCK_FLAGS_NONE; 1302 1302 if (bytes == block_size) 1303 1303 flags = BLOCK_FLAGS_NOREAD; 1304 1304 1305 1305 uint32_t iblock = pos / block_size; 1306 1306 uint32_t fblock; 1307 1307 1308 1308 /* Load inode */ 1309 1309 ext4_inode_ref_t *inode_ref = enode->inode_ref; … … 1314 1314 goto exit; 1315 1315 } 1316 1316 1317 1317 /* Check for sparse file */ 1318 1318 if (fblock == 0) { … … 1323 1323 ext4_inode_get_size(fs->superblock, inode_ref->inode) / 1324 1324 block_size; 1325 1325 1326 1326 while (last_iblock < iblock) { 1327 1327 rc = ext4_extent_append_block(inode_ref, &last_iblock, … … 1332 1332 } 1333 1333 } 1334 1334 1335 1335 rc = ext4_extent_append_block(inode_ref, &last_iblock, 1336 1336 &fblock, false); … … 1345 1345 goto exit; 1346 1346 } 1347 1347 1348 1348 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, 1349 1349 iblock, fblock); … … 1354 1354 } 1355 1355 } 1356 1356 1357 1357 flags = BLOCK_FLAGS_NOREAD; 1358 1358 inode_ref->dirty = true; 1359 1359 } 1360 1360 1361 1361 /* Load target block */ 1362 1362 block_t *write_block; … … 1366 1366 goto exit; 1367 1367 } 1368 1368 1369 1369 if (flags == BLOCK_FLAGS_NOREAD) 1370 1370 memset(write_block->data, 0, block_size); … … 1419 1419 if (rc != EOK) 1420 1420 return rc; 1421 1421 1422 1422 ext4_node_t *enode = EXT4_NODE(fn); 1423 1423 ext4_inode_ref_t *inode_ref = enode->inode_ref; 1424 1424 1425 1425 rc = ext4_filesystem_truncate_inode(inode_ref, new_size); 1426 1426 errno_t const rc2 = ext4_node_put(fn); 1427 1427 1428 1428 return rc == EOK ? rc2 : rc; 1429 1429 } … … 1456 1456 if (rc != EOK) 1457 1457 return rc; 1458 1458 1459 1459 /* Destroy the inode */ 1460 1460 return ext4_destroy_node(fn); … … 1473 1473 if (rc != EOK) 1474 1474 return rc; 1475 1475 1476 1476 ext4_node_t *enode = EXT4_NODE(fn); 1477 1477 enode->inode_ref->dirty = true; 1478 1478 1479 1479 return ext4_node_put(fn); 1480 1480 } -
uspace/lib/ext4/src/superblock.c
r3061bc1 ra35b458 241 241 uint32_t log = 0; 242 242 uint32_t tmp = size / EXT4_MIN_BLOCK_SIZE; 243 243 244 244 tmp >>= 1; 245 245 while (tmp) { … … 247 247 tmp >>= 1; 248 248 } 249 249 250 250 ext4_superblock_set_log_block_size(sb, log); 251 251 } … … 297 297 uint32_t log = 0; 298 298 uint32_t tmp = size / EXT4_MIN_BLOCK_SIZE; 299 299 300 300 tmp >>= 1; 301 301 while (tmp) { … … 303 303 tmp >>= 1; 304 304 } 305 305 306 306 ext4_superblock_set_log_frag_size(sb, log); 307 307 } … … 736 736 if (ext4_superblock_get_rev_level(sb) == 0) 737 737 return EXT4_REV0_INODE_SIZE; 738 738 739 739 return uint16_t_le2host(sb->inode_size); 740 740 } … … 1001 1001 { 1002 1002 uint16_t size = uint16_t_le2host(sb->desc_size); 1003 1003 1004 1004 if (size < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE) 1005 1005 size = EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE; 1006 1006 1007 1007 return size; 1008 1008 } … … 1021 1021 sb->desc_size = 1022 1022 host2uint16_t_le(EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE); 1023 1023 1024 1024 sb->desc_size = host2uint16_t_le(size); 1025 1025 } … … 1064 1064 if (ext4_superblock_get_flags(sb) & flag) 1065 1065 return true; 1066 1066 1067 1067 return false; 1068 1068 } … … 1081 1081 if (ext4_superblock_get_features_compatible(sb) & feature) 1082 1082 return true; 1083 1083 1084 1084 return false; 1085 1085 } … … 1098 1098 if (ext4_superblock_get_features_incompatible(sb) & feature) 1099 1099 return true; 1100 1100 1101 1101 return false; 1102 1102 } … … 1115 1115 if (ext4_superblock_get_features_read_only(sb) & feature) 1116 1116 return true; 1117 1117 1118 1118 return false; 1119 1119 } … … 1133 1133 if (data == NULL) 1134 1134 return ENOMEM; 1135 1135 1136 1136 /* Read data from block device */ 1137 1137 errno_t rc = block_read_bytes_direct(service_id, EXT4_SUPERBLOCK_OFFSET, 1138 1138 EXT4_SUPERBLOCK_SIZE, data); 1139 1139 1140 1140 if (rc != EOK) { 1141 1141 free(data); 1142 1142 return rc; 1143 1143 } 1144 1144 1145 1145 /* Set output value */ 1146 1146 (*sb) = data; 1147 1147 1148 1148 return EOK; 1149 1149 } … … 1164 1164 if (rc != EOK) 1165 1165 return rc; 1166 1166 1167 1167 /* Compute address of the first block */ 1168 1168 uint64_t first_block = EXT4_SUPERBLOCK_OFFSET / phys_block_size; 1169 1169 1170 1170 /* Compute number of block to write */ 1171 1171 size_t block_count = EXT4_SUPERBLOCK_SIZE / phys_block_size; 1172 1172 1173 1173 /* Check alignment */ 1174 1174 if (EXT4_SUPERBLOCK_SIZE % phys_block_size) 1175 1175 block_count++; 1176 1176 1177 1177 /* Write data */ 1178 1178 return block_write_direct(service_id, first_block, block_count, sb); … … 1203 1203 if (ext4_superblock_get_magic(sb) != EXT4_SUPERBLOCK_MAGIC) 1204 1204 return ENOTSUP; 1205 1205 1206 1206 if (ext4_superblock_get_inodes_count(sb) == 0) 1207 1207 return ENOTSUP; 1208 1208 1209 1209 if (ext4_superblock_get_blocks_count(sb) == 0) 1210 1210 return ENOTSUP; 1211 1211 1212 1212 if (ext4_superblock_get_blocks_per_group(sb) == 0) 1213 1213 return ENOTSUP; 1214 1214 1215 1215 if (ext4_superblock_get_inodes_per_group(sb) == 0) 1216 1216 return ENOTSUP; 1217 1217 1218 1218 if (ext4_superblock_get_inode_size(sb) < 128) 1219 1219 return ENOTSUP; 1220 1220 1221 1221 if (ext4_superblock_get_first_inode(sb) < 11) 1222 1222 return ENOTSUP; 1223 1223 1224 1224 if (ext4_superblock_get_desc_size(sb) < 1225 1225 EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE) 1226 1226 return ENOTSUP; 1227 1227 1228 1228 if (ext4_superblock_get_desc_size(sb) > 1229 1229 EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE) 1230 1230 return ENOTSUP; 1231 1231 1232 1232 return EOK; 1233 1233 } … … 1244 1244 uint64_t blocks_count = ext4_superblock_get_blocks_count(sb); 1245 1245 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb); 1246 1246 1247 1247 uint32_t block_groups_count = blocks_count / blocks_per_group; 1248 1248 1249 1249 if (blocks_count % blocks_per_group) 1250 1250 block_groups_count++; 1251 1251 1252 1252 return block_groups_count; 1253 1253 } … … 1269 1269 uint64_t total_blocks = 1270 1270 ext4_superblock_get_blocks_count(sb); 1271 1271 1272 1272 if (bgid < block_group_count - 1) 1273 1273 return blocks_per_group; … … 1292 1292 uint32_t total_inodes = 1293 1293 ext4_superblock_get_inodes_count(sb); 1294 1294 1295 1295 if (bgid < block_group_count - 1) 1296 1296 return inodes_per_group;
Note:
See TracChangeset
for help on using the changeset viewer.