1/* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "File.h" 8 9#include <errno.h> 10#include <string.h> 11#include <unistd.h> 12 13#include <algorithm> 14#include <new> 15 16#include <fs_cache.h> 17 18#include <AutoDeleter.h> 19 20#include "Block.h" 21#include "BlockAllocator.h" 22#include "DebugSupport.h" 23#include "Transaction.h" 24#include "Volume.h" 25 26 27static const size_t kFileRootBlockOffset = sizeof(checksumfs_node); 28static const size_t kFileRootBlockSize = B_PAGE_SIZE 29 - kFileRootBlockOffset; 30static const uint32 kFileRootBlockMaxCount = kFileRootBlockSize / 8; 31static const uint32 kFileBlockMaxCount = B_PAGE_SIZE / 8; 32static const uint32 kFileBlockShift = 9; 33static const uint32 kFileMaxTreeDepth = (64 + kFileBlockShift - 1) 34 / kFileBlockShift + 1; 35 36 37#define BLOCK_ROUND_UP(value) (((value) + B_PAGE_SIZE - 1) / B_PAGE_SIZE \ 38 * B_PAGE_SIZE) 39 40 41namespace { 42 struct WriteTempData { 43 SHA256 sha256; 44 checksum_device_ioctl_check_sum indexAndCheckSum; 45 file_io_vec fileVecs[16]; 46 uint8 blockData[B_PAGE_SIZE]; 47 }; 48} 49 50 51struct File::LevelInfo { 52 uint64 addressableShift; // 1 << addressableShift is the number of 53 // descendent data blocks a child block (and its 54 // descendents) can address 55 uint32 childCount; // number of child blocks of the last block of 56 // this level 57 Block block; 58 uint64* blockData; 59 int32 index; 60}; 61 62 63File::File(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData) 64 : 65 Node(volume, blockIndex, nodeData), 66 fFileCache(NULL) 67{ 68 STATIC_ASSERT(kFileBlockMaxCount == (uint32)1 << kFileBlockShift); 69} 70 71 72File::File(Volume* volume, mode_t mode) 73 : 74 Node(volume, mode), 75 fFileCache(NULL), 76 fFileMap(NULL) 77{ 78} 79 80 81File::~File() 82{ 83 if (fFileCache != NULL) 84 file_cache_delete(fFileCache); 85 if (fFileMap != NULL) 86 file_map_delete(fFileMap); 87} 88 89 90status_t 91File::InitForVFS() 92{ 93 // create the file map 94 fFileMap = file_map_create(GetVolume()->ID(), BlockIndex(), Size()); 95 if (fFileMap == NULL) 96 RETURN_ERROR(B_NO_MEMORY); 97 98 // create the file cache 99 fFileCache = file_cache_create(GetVolume()->ID(), BlockIndex(), Size()); 100 if (fFileCache == NULL) 101 RETURN_ERROR(B_NO_MEMORY); 102 103 return B_OK; 104} 105 106 107void 108File::DeletingNode() 109{ 110 Node::DeletingNode(); 111 112 // start a transaction 113 Transaction transaction(GetVolume()); 114 status_t error = transaction.Start(); 115 if (error != B_OK) { 116 ERROR("Failed to start transaction for deleting contents of file at %" 117 B_PRIu64 "\n", BlockIndex()); 118 return; 119 } 120 121 error = Resize(0, false, transaction); 122 if (error != B_OK) { 123 ERROR("Failed to delete contents of file at %" B_PRIu64 "\n", 124 BlockIndex()); 125 return; 126 } 127 128 error = transaction.Commit(); 129 if (error != B_OK) { 130 ERROR("Failed to commit transaction for deleting contents of file at %" 131 B_PRIu64 "\n", BlockIndex()); 132 } 133} 134 135 136status_t 137File::Resize(uint64 newSize, bool fillWithZeroes, Transaction& transaction) 138{ 139 uint64 size = Size(); 140 if (newSize == size) 141 return B_OK; 142 143 FUNCTION("%" B_PRIu64 " -> %" B_PRIu64 "\n", size, newSize); 144 145 uint64 blockCount = BLOCK_ROUND_UP(size) / B_PAGE_SIZE; 146 uint64 newBlockCount = BLOCK_ROUND_UP(newSize) / B_PAGE_SIZE; 147 148 if (newBlockCount != blockCount) { 149 status_t error; 150 if (newBlockCount < blockCount) 151 error = _ShrinkTree(blockCount, newBlockCount, transaction); 152 else 153 error = _GrowTree(blockCount, newBlockCount, transaction); 154 155 if (error != B_OK) 156 RETURN_ERROR(error); 157 } 158 159 SetSize(newSize); 160 161 file_cache_set_size(fFileCache, newSize); 162 file_map_set_size(fFileMap, newSize); 163 164 if (newSize > size && fillWithZeroes) { 165 status_t error = _WriteZeroes(size, newSize - size); 166 if (error != B_OK) { 167 file_cache_set_size(fFileCache, size); 168 file_map_set_size(fFileMap, size); 169 RETURN_ERROR(error); 170 } 171 } 172 173 return B_OK; 174} 175 176 177status_t 178File::Read(off_t pos, void* buffer, size_t size, size_t& _bytesRead) 179{ 180 if (pos < 0) 181 return B_BAD_VALUE; 182 183 if (size == 0) { 184 _bytesRead = 0; 185 return B_OK; 186 } 187 188 NodeReadLocker locker(this); 189 190 uint64 fileSize = Size(); 191 if ((uint64)pos >= fileSize) { 192 _bytesRead = 0; 193 return B_OK; 194 } 195 196 if (fileSize - pos < size) 197 size = fileSize - pos; 198 199 locker.Unlock(); 200 201 size_t bytesRead = size; 202 status_t error = file_cache_read(fFileCache, NULL, pos, buffer, &bytesRead); 203 if (error != B_OK) 204 RETURN_ERROR(error); 205 206 _bytesRead = bytesRead; 207 return B_OK; 208} 209 210 211status_t 212File::Write(off_t pos, const void* buffer, size_t size, size_t& _bytesWritten, 213 bool& _sizeChanged) 214{ 215 _sizeChanged = false; 216 217 if (size == 0) { 218 _bytesWritten = 0; 219 return B_OK; 220 } 221 222 NodeWriteLocker locker(this); 223 224 uint64 fileSize = Size(); 225 if (pos < 0) 226 pos = fileSize; 227 228 uint64 newFileSize = (uint64)pos + size; 229 230 if (newFileSize > fileSize) { 231 // we have to resize the file 232 Transaction transaction(GetVolume()); 233 status_t error = transaction.Start(); 234 if (error != B_OK) 235 RETURN_ERROR(error); 236 237 // attach the node to the transaction (write locks it, too) 238 error = transaction.AddNode(this, 239 TRANSACTION_NODE_ALREADY_LOCKED | TRANSACTION_KEEP_NODE_LOCKED); 240 if (error != B_OK) 241 RETURN_ERROR(error); 242 243 // resize 244 error = Resize((uint64)pos + size, false, transaction); 245 if (error != B_OK) 246 RETURN_ERROR(error); 247 248 SetSize(newFileSize); 249 250 // commit the transaction 251 error = transaction.Commit(); 252 if (error != B_OK) 253 RETURN_ERROR(error); 254 255 _sizeChanged = true; 256 } 257 258 // now the file has the right size -- do the write 259 locker.Unlock(); 260 261 if (fileSize < (uint64)pos) { 262 // fill the gap between old file end and write position with zeroes 263 _WriteZeroes(fileSize, pos - fileSize); 264 } 265 266 size_t bytesWritten; 267 status_t error = _WriteData(pos, buffer, size, bytesWritten); 268 if (error != B_OK) 269 RETURN_ERROR(error); 270 271 // update the file times 272 Transaction transaction(GetVolume()); 273 if (transaction.Start() == B_OK && transaction.AddNode(this) == B_OK) { 274 // note: we don't fail, if we only couldn't update the times 275 Touched(NODE_MODIFIED); 276 transaction.Commit(); 277 } 278 279 _bytesWritten = bytesWritten; 280 return B_OK; 281} 282 283 284status_t 285File::Sync() 286{ 287 return file_cache_sync(fFileCache); 288} 289 290 291void 292File::RevertNodeData(const checksumfs_node& nodeData) 293{ 294 Node::RevertNodeData(nodeData); 295 296 // in case the file size was reverted, reset file cache and map 297 uint64 size = Size(); 298 file_cache_set_size(fFileCache, size); 299 file_map_set_size(fFileMap, size); 300} 301 302 303status_t 304File::GetFileVecs(uint64 offset, size_t size, file_io_vec* vecs, size_t count, 305 size_t& _count) 306{ 307 FUNCTION("offset: %" B_PRIu64 ", size: %" B_PRIuSIZE ", count: %" B_PRIuSIZE 308 "\n", offset, size, count); 309 310 // Round size to block size, but restrict to file size. This semantics is 311 // fine with the caller (the file map) and it will help avoiding partial 312 // block I/O. 313 uint32 inBlockOffset = offset % B_PAGE_SIZE; 314 315 uint64 firstBlock = offset / B_PAGE_SIZE; 316 uint64 neededBlockCount = BLOCK_ROUND_UP((uint64)size + inBlockOffset) 317 / B_PAGE_SIZE; 318 uint64 fileBlockCount = BLOCK_ROUND_UP(Size()) / B_PAGE_SIZE; 319 320 if (firstBlock >= fileBlockCount) { 321 _count = 0; 322 return B_OK; 323 } 324 325 if (firstBlock + neededBlockCount > fileBlockCount) 326 neededBlockCount = fileBlockCount - firstBlock; 327 328 // get the level infos 329 int32 depth; 330 LevelInfo* infos = _GetLevelInfos(fileBlockCount, depth); 331 if (infos == NULL) 332 RETURN_ERROR(B_NO_MEMORY); 333 ArrayDeleter<LevelInfo> infosDeleter(infos); 334 335 // prepare for the iteration 336 uint64 blockIndex = BlockIndex(); 337 338 PRINT(" preparing iteration: firstBlock: %" B_PRIu64 ", blockIndex: %" 339 B_PRIu64 "\n", firstBlock, blockIndex); 340 341 for (int32 i = 0; i < depth; i++) { 342 LevelInfo& info = infos[i]; 343 if (!info.block.GetReadable(GetVolume(), blockIndex)) 344 RETURN_ERROR(B_ERROR); 345 346 if (i == 0) { 347 info.blockData = (uint64*)((uint8*)info.block.Data() 348 + kFileRootBlockOffset); 349 } else 350 info.blockData = (uint64*)info.block.Data(); 351 352 info.index = firstBlock >> info.addressableShift; 353 firstBlock -= (uint64)info.index << info.addressableShift; 354 355 blockIndex = info.blockData[info.index]; 356 357 PRINT(" preparing level %" B_PRId32 ": index: %" B_PRId32 358 ", firstBlock: %" B_PRIu64 ", blockIndex: %" B_PRIu64 "\n", i, 359 info.index, firstBlock, blockIndex); 360 } 361 362 // and iterate 363 int32 level = depth - 1; 364 size_t countAdded = 0; 365 366 while (true) { 367 LevelInfo& info = infos[level]; 368 369 if (info.index == (int32)kFileBlockMaxCount) { 370 // end of block -- back track to next greater branch 371 PRINT(" level: %" B_PRId32 ": index: %" B_PRId32 " -> back " 372 "tracking\n", level, info.index); 373 374 level--; 375 infos[level].index++; 376 continue; 377 } 378 379 blockIndex = info.blockData[info.index]; 380 381 PRINT(" level: %" B_PRId32 ": index: %" B_PRId32 " -> blockIndex: %" 382 B_PRIu64 "\n", level, info.index, blockIndex); 383 384 if (level < depth - 1) { 385 // descend to next level 386 level++; 387 388 if (!infos[level].block.GetReadable(GetVolume(), blockIndex)) 389 RETURN_ERROR(B_ERROR); 390 391 infos[level].blockData = (uint64*)infos[level].block.Data(); 392 infos[level].index = 0; 393 continue; 394 } 395 396 info.index++; 397 398 // add the block 399 uint64 blockOffset = blockIndex * B_PAGE_SIZE; 400 if (countAdded > 0 401 && blockOffset 402 == (uint64)vecs[countAdded - 1].offset 403 + vecs[countAdded - 1].length) { 404 // the block continues where the previous block ends -- just extend 405 // the vector 406 vecs[countAdded - 1].length += B_PAGE_SIZE; 407 408 PRINT(" -> extended vector %" B_PRIuSIZE ": offset: %" 409 B_PRIdOFF " size: %" B_PRIdOFF "\n", countAdded - 1, 410 vecs[countAdded - 1].offset, vecs[countAdded - 1].length); 411 } else { 412 // we need a new block 413 if (countAdded == count) 414 break; 415 416 vecs[countAdded].offset = blockOffset + inBlockOffset; 417 vecs[countAdded].length = B_PAGE_SIZE - inBlockOffset; 418 countAdded++; 419 inBlockOffset = 0; 420 421 PRINT(" -> added vector %" B_PRIuSIZE ": offset: %" 422 B_PRIdOFF " size: %" B_PRIdOFF "\n", countAdded - 1, 423 vecs[countAdded - 1].offset, vecs[countAdded - 1].length); 424 } 425 426 if (--neededBlockCount == 0) 427 break; 428 } 429 430 _count = countAdded; 431 return B_OK; 432} 433 434 435/*static*/ uint32 436File::_DepthForBlockCount(uint64 blockCount) 437{ 438 uint64 addressableBlocks = kFileRootBlockMaxCount; 439 440 uint32 depth = 1; 441 while (blockCount > addressableBlocks) { 442 addressableBlocks *= kFileBlockMaxCount; 443 depth++; 444 } 445 446 return depth; 447} 448 449 450/*static*/ void 451File::_UpdateLevelInfos(LevelInfo* infos, int32 levelCount, uint64 blockCount) 452{ 453 if (blockCount == 0) { 454 infos[0].addressableShift = 0; 455 infos[0].childCount = 0; 456 return; 457 } 458 459 uint64 addressableShift = 0; 460 for (int32 i = levelCount - 1; i >= 0; i--) { 461 infos[i].addressableShift = addressableShift; 462 infos[i].childCount = (blockCount - 1) % kFileBlockMaxCount + 1; 463 addressableShift += kFileBlockShift; 464 blockCount = (blockCount + kFileBlockMaxCount - 1) / kFileBlockMaxCount; 465 } 466} 467 468 469/*static*/ File::LevelInfo* 470File::_GetLevelInfos(uint64 blockCount, int32& _levelCount) 471{ 472 LevelInfo* infos = new(std::nothrow) LevelInfo[kFileMaxTreeDepth]; 473// TODO: We need to allocate differently, if requested by the page writer! 474 if (infos == NULL) 475 return NULL; 476 477 int32 levelCount = _DepthForBlockCount(blockCount); 478 _UpdateLevelInfos(infos, levelCount, blockCount); 479 480 _levelCount = levelCount; 481 return infos; 482} 483 484 485status_t 486File::_ShrinkTree(uint64 blockCount, uint64 newBlockCount, 487 Transaction& transaction) 488{ 489 FUNCTION("blockCount: %" B_PRIu64 " -> %" B_PRIu64 "\n", blockCount, 490 newBlockCount); 491 492 int32 depth; 493 LevelInfo* infos = _GetLevelInfos(blockCount, depth); 494 if (infos == NULL) 495 return B_NO_MEMORY; 496 ArrayDeleter<LevelInfo> infosDeleter(infos); 497 498 // load the root block 499 if (!infos[0].block.GetWritable(GetVolume(), BlockIndex(), transaction)) 500 RETURN_ERROR(B_ERROR); 501 infos[0].blockData = (uint64*)((uint8*)infos[0].block.Data() 502 + kFileRootBlockOffset); 503 504 int32 level = 0; 505 506 // remove blocks 507 bool removeBlock = false; 508 while (true) { 509 PRINT(" level %" B_PRId32 ", child count: %" B_PRIu32 "\n", level, 510 infos[level].childCount); 511 512 // If the block is empty, remove it. 513 if (infos[level].childCount == 0) { 514 if (level == 0) 515 break; 516 517 // prepare for the next iteration 518 infos[level].childCount = kFileBlockMaxCount; 519 520 removeBlock = true; 521 level--; 522 continue; 523 } 524 525 // block not empty -- we might already be done 526 if (blockCount == newBlockCount) 527 break; 528 529 uint64 blockIndex = infos[level].blockData[infos[level].childCount - 1]; 530 531 // unless we're in the last level or shall remove, descend 532 if (level < depth - 1 && !removeBlock) { 533 LevelInfo& info = infos[++level]; 534 if (!info.block.GetWritable(GetVolume(), blockIndex, transaction)) 535 RETURN_ERROR(B_ERROR); 536 info.blockData = (uint64*)info.block.Data(); 537 continue; 538 } 539 540 // remove the block 541 542 LevelInfo& info = infos[level]; 543 544 PRINT(" freeing block: %" B_PRId64 "\n", blockIndex); 545 546 // clear the entry (not strictly necessary) 547 info.blockData[info.childCount - 1] = 0; 548 549 // free the block 550 status_t error = GetVolume()->GetBlockAllocator()->Free(blockIndex, 1, 551 transaction); 552 if (error != B_OK) 553 RETURN_ERROR(error); 554 555 if (level == depth - 1) 556 blockCount--; 557 558 infos[level].childCount--; 559 560 removeBlock = false; 561 } 562 563 // We got rid of all unnecessary data blocks and empty node blocks. We might 564 // need to cull the lower levels of the tree, now. 565 int32 newDepth = _DepthForBlockCount(newBlockCount); 566 if (newDepth == depth) 567 return B_OK; 568 569 for (int32 i = 1; i <= depth - newDepth; i++) { 570 uint64 blockIndex = infos[0].blockData[0]; 571 572 PRINT(" removing block %" B_PRIu64 " at level %" B_PRIi32 "\n", 573 blockIndex, i); 574 575 Block block; 576 if (!block.GetReadable(GetVolume(), blockIndex)) 577 RETURN_ERROR(B_ERROR); 578 579 // copy to the root block 580 const uint64* blockData = (uint64*)infos[i].block.Data(); 581 memcpy(infos[0].blockData, blockData, infos[i].childCount * 8); 582 583 // free the block 584 block.Put(); 585 status_t error = GetVolume()->GetBlockAllocator()->Free(blockIndex, 1, 586 transaction); 587 if (error != B_OK) 588 RETURN_ERROR(error); 589 } 590 591 return B_OK; 592} 593 594 595status_t 596File::_GrowTree(uint64 blockCount, uint64 newBlockCount, 597 Transaction& transaction) 598{ 599 FUNCTION("blockCount: %" B_PRIu64 " -> %" B_PRIu64 "\n", blockCount, 600 newBlockCount); 601 602 int32 depth; 603 LevelInfo* infos = _GetLevelInfos(blockCount, depth); 604 if (infos == NULL) 605 return B_NO_MEMORY; 606 ArrayDeleter<LevelInfo> infosDeleter(infos); 607 608 int32 newDepth = _DepthForBlockCount(newBlockCount); 609 610 Block& rootBlock = infos[0].block; 611 if (!rootBlock.GetWritable(GetVolume(), BlockIndex(), transaction)) 612 RETURN_ERROR(B_ERROR); 613 infos[0].blockData = (uint64*)((uint8*)rootBlock.Data() 614 + kFileRootBlockOffset); 615 616 // add new levels, if necessary 617 if (depth < newDepth) { 618 uint32 childCount = infos[0].childCount; 619 620 // update the level infos 621 _UpdateLevelInfos(infos, newDepth, blockCount); 622 623 // allocate a block per new level 624 for (int32 i = newDepth - depth - 1; i >= 0; i--) { 625 // allocate a new block 626 AllocatedBlock allocatedBlock(GetVolume()->GetBlockAllocator(), 627 transaction); 628 status_t error = allocatedBlock.Allocate(BlockIndex()); 629 if (error != B_OK) 630 RETURN_ERROR(error); 631 632 Block newBlock; 633 if (!newBlock.GetZero(GetVolume(), allocatedBlock.Index(), 634 transaction)) { 635 RETURN_ERROR(B_ERROR); 636 } 637 638 allocatedBlock.Detach(); 639 640 PRINT(" inserting block %" B_PRIu64 " at level %" B_PRIi32 641 "\n", newBlock.Index(), i + 1); 642 643 // copy the root block 644 memcpy(newBlock.Data(), infos[0].blockData, childCount * 8); 645 646 // set the block in the root block 647 infos[0].blockData[0] = newBlock.Index(); 648 childCount = 1; 649 } 650 } 651 652 depth = newDepth; 653 654 // prepare the iteration 655 int32 level = depth - 1; 656 for (int32 i = 0; i < level; i++) { 657 // get the block for the next level 658 LevelInfo& info = infos[i]; 659 if (!infos[i + 1].block.GetWritable(GetVolume(), 660 info.blockData[info.childCount - 1], transaction)) { 661 RETURN_ERROR(B_ERROR); 662 } 663 infos[i + 1].blockData = (uint64*)infos[i + 1].block.Data(); 664 } 665 666 // add the new blocks 667 while (blockCount < newBlockCount) { 668 PRINT(" level %" B_PRId32 ", child count: %" B_PRIu32 "\n", level, 669 infos[level].childCount); 670 671 if (infos[level].childCount >= (int32)kFileBlockMaxCount) { 672 // block is full -- back track 673 level--; 674 } 675 676 // allocate and insert block 677 AllocatedBlock allocatedBlock(GetVolume()->GetBlockAllocator(), 678 transaction); 679 status_t error = allocatedBlock.Allocate(BlockIndex()); 680 if (error != B_OK) 681 RETURN_ERROR(error); 682 683 uint64 blockIndex = allocatedBlock.Index(); 684 infos[level].blockData[infos[level].childCount++] = blockIndex; 685 686 PRINT(" allocated block: %" B_PRId64 "\n", blockIndex); 687 688 if (level < depth - 1) { 689 // descend to the next level 690 level++; 691 infos[level].childCount = 0; 692 693 if (!infos[level].block.GetZero(GetVolume(), blockIndex, 694 transaction)) { 695 RETURN_ERROR(B_ERROR); 696 } 697 698 infos[level].blockData = (uint64*)infos[level].block.Data(); 699 } else { 700 // That's a data block -- make the block cache forget it, so it 701 // doesn't conflict with the file cache. 702 block_cache_discard(GetVolume()->BlockCache(), blockIndex, 1); 703 blockCount++; 704 } 705 706 allocatedBlock.Detach(); 707 } 708 709 return B_OK; 710} 711 712 713status_t 714File::_WriteZeroes(uint64 offset, uint64 size) 715{ 716 while (size > 0) { 717 size_t bytesWritten; 718 status_t error = _WriteData(offset, NULL, 719 std::min(size, (uint64)SIZE_MAX), bytesWritten); 720 if (error != B_OK) 721 RETURN_ERROR(error); 722 if (bytesWritten == 0) 723 RETURN_ERROR(B_ERROR); 724 725 size -= bytesWritten; 726 offset += bytesWritten; 727 } 728 729 return B_OK; 730} 731 732 733status_t 734File::_WriteData(uint64 offset, const void* buffer, size_t size, 735 size_t& _bytesWritten) 736{ 737 uint32 inBlockOffset = offset % B_PAGE_SIZE; 738 uint64 blockCount = ((uint64)size + inBlockOffset + B_PAGE_SIZE - 1) 739 / B_PAGE_SIZE; 740 741 // allocate storage for the indices of the blocks 742 uint64* blockIndices = new(std::nothrow) uint64[blockCount]; 743 if (blockIndices == NULL) 744 RETURN_ERROR(B_NO_MEMORY); 745 ArrayDeleter<uint64> blockIndicesDeleter(blockIndices); 746 747 // allocate temporary storage for the check sum computation 748 WriteTempData* tempData = new(std::nothrow) WriteTempData; 749 if (tempData == NULL) 750 RETURN_ERROR(B_NO_MEMORY); 751 ObjectDeleter<WriteTempData> tempDataDeleter(tempData); 752 753 // get the block indices 754 uint64 firstBlockIndex = offset / B_PAGE_SIZE; 755 for (uint64 i = 0; i < blockCount;) { 756 size_t count; 757 status_t error = GetFileVecs((firstBlockIndex + i) * B_PAGE_SIZE, 758 size + inBlockOffset - i * B_PAGE_SIZE, tempData->fileVecs, 759 sizeof(tempData->fileVecs) / sizeof(file_io_vec), count); 760 if (error != B_OK) 761 RETURN_ERROR(error); 762 763 for (size_t k = 0; k < count && i < blockCount; k++) { 764 off_t vecBlockIndex = tempData->fileVecs[k].offset / B_PAGE_SIZE; 765 off_t vecLength = tempData->fileVecs[k].length; 766 while (vecLength > 0 && i < blockCount) { 767 blockIndices[i++] = vecBlockIndex++; 768 vecLength -= B_PAGE_SIZE; 769 } 770 } 771 } 772 773 // clear the check sums of the affected blocks 774 memset(&tempData->indexAndCheckSum.checkSum, 0, sizeof(CheckSum)); 775 for (uint64 i = 0; i < blockCount; i++) { 776 tempData->indexAndCheckSum.blockIndex = blockIndices[i]; 777 if (ioctl(GetVolume()->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM, 778 &tempData->indexAndCheckSum, 779 sizeof(tempData->indexAndCheckSum)) < 0) { 780 RETURN_ERROR(errno); 781 } 782 } 783 784 // write 785 size_t bytesWritten = size; 786 status_t error = file_cache_write(fFileCache, NULL, offset, buffer, 787 &bytesWritten); 788 if (error != B_OK) 789 RETURN_ERROR(error); 790 791 // compute and set the new check sums 792 for (uint64 i = 0; i < blockCount; i++) { 793 // copy the data to our temporary buffer 794 if (i == 0 && inBlockOffset != 0) { 795 // partial block -- read complete block from cache 796 size_t bytesRead = B_PAGE_SIZE; 797 error = file_cache_read(fFileCache, NULL, offset - inBlockOffset, 798 tempData->blockData, &bytesRead); 799 if (error != B_OK) 800 RETURN_ERROR(error); 801 802 if (bytesRead < B_PAGE_SIZE) { 803 // partial read (the file is possibly shorter) -- clear the rest 804 memset(tempData->blockData + bytesRead, 0, 805 B_PAGE_SIZE - bytesRead); 806 } 807 808 // copy provided data 809 size_t toCopy = std::min((size_t)B_PAGE_SIZE - inBlockOffset, size); 810 if (buffer != NULL) { 811 error = user_memcpy(tempData->blockData + inBlockOffset, 812 buffer, toCopy); 813 if (error != B_OK) 814 RETURN_ERROR(error); 815 } else 816 memset(tempData->blockData + inBlockOffset, 0, toCopy); 817 } else if (i == blockCount - 1 818 && (size + inBlockOffset) % B_PAGE_SIZE != 0) { 819 // partial block -- read complete block from cache 820 size_t bytesRead = B_PAGE_SIZE; 821 error = file_cache_read(fFileCache, NULL, 822 offset - inBlockOffset + i * B_PAGE_SIZE, 823 tempData->blockData, &bytesRead); 824 if (error != B_OK) 825 RETURN_ERROR(error); 826 827 if (bytesRead < B_PAGE_SIZE) { 828 // partial read (the file is possibly shorter) -- clear the rest 829 memset(tempData->blockData + bytesRead, 0, 830 B_PAGE_SIZE - bytesRead); 831 } 832 833 // copy provided data 834 size_t toCopy = (size + inBlockOffset) % B_PAGE_SIZE; 835 // we start at the beginning of the block, since i > 0 836 if (buffer != NULL) { 837 error = user_memcpy(tempData->blockData, 838 (const uint8*)buffer + i * B_PAGE_SIZE - inBlockOffset, 839 toCopy); 840 if (error != B_OK) 841 RETURN_ERROR(error); 842 } else 843 memset(tempData->blockData, 0, toCopy); 844 } else { 845 // complete block 846 if (buffer != NULL) { 847 error = user_memcpy(tempData->blockData, 848 (const uint8*)buffer + i * B_PAGE_SIZE - inBlockOffset, 849 B_PAGE_SIZE); 850 if (error != B_OK) 851 RETURN_ERROR(error); 852 } else if (i == 0 || (i == 1 && inBlockOffset != 0)) { 853 // clear only once 854 memset(tempData->blockData, 0, B_PAGE_SIZE); 855 } 856 } 857 858 // compute the check sum 859 if (buffer != NULL || i == 0 || (i == 1 && inBlockOffset != 0)) { 860 tempData->sha256.Init(); 861 tempData->sha256.Update(tempData->blockData, B_PAGE_SIZE); 862 tempData->indexAndCheckSum.checkSum = tempData->sha256.Digest(); 863 } 864 865 // set it 866 tempData->indexAndCheckSum.blockIndex = blockIndices[i]; 867 868 if (ioctl(GetVolume()->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM, 869 &tempData->indexAndCheckSum, 870 sizeof(tempData->indexAndCheckSum)) < 0) { 871 RETURN_ERROR(errno); 872 } 873 } 874 875 _bytesWritten = bytesWritten; 876 return B_OK; 877} 878