/* * Copyright 2001-2010, Haiku Inc. All rights reserved. * This file may be used under the terms of the MIT License. * * Authors: * Janito V. Ferreira Filho */ #include "DataStream.h" #include "CachedBlock.h" #include "Volume.h" //#define TRACE_EXT2 #ifdef TRACE_EXT2 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) #else # define TRACE(x...) ; #endif #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x) DataStream::DataStream(Volume* volume, ext2_data_stream* stream, off_t size) : kBlockSize(volume->BlockSize()), kIndirectsPerBlock(kBlockSize / sizeof(uint32)), kIndirectsPerBlock2(kIndirectsPerBlock * kIndirectsPerBlock), kIndirectsPerBlock3(kIndirectsPerBlock2 * kIndirectsPerBlock), kMaxDirect(EXT2_DIRECT_BLOCKS), kMaxIndirect(kMaxDirect + kIndirectsPerBlock), kMaxDoubleIndirect(kMaxIndirect + kIndirectsPerBlock2), fVolume(volume), fStream(stream), fFirstBlock(volume->FirstDataBlock()), fAllocated(0), fAllocatedPos(fFirstBlock), fWaiting(0), fFreeStart(0), fFreeCount(0), fRemovedBlocks(0), fSize(size) { fNumBlocks = size == 0 ? 0 : ((size - 1) >> fVolume->BlockShift()) + 1; } DataStream::~DataStream() { } status_t DataStream::FindBlock(off_t offset, fsblock_t& block, uint32 *_count) { uint32 index = offset >> fVolume->BlockShift(); if (offset >= fSize) { TRACE("FindBlock: offset larger than inode size\n"); return B_ENTRY_NOT_FOUND; } // TODO: we could return the size of the sparse range, as this might be more // than just a block if (index < EXT2_DIRECT_BLOCKS) { // direct blocks block = B_LENDIAN_TO_HOST_INT32(fStream->direct[index]); ASSERT(block != 0); if (_count) { *_count = 1; uint32 nextBlock = block; while (++index < EXT2_DIRECT_BLOCKS && fStream->direct[index] == ++nextBlock) (*_count)++; } } else if ((index -= EXT2_DIRECT_BLOCKS) < kIndirectsPerBlock) { // indirect blocks CachedBlock cached(fVolume); uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( fStream->indirect)); if (indirectBlocks == NULL) return B_IO_ERROR; block = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index]); ASSERT(block != 0); if (_count) { *_count = 1; uint32 nextBlock = block; while (++index < kIndirectsPerBlock && indirectBlocks[index] == ++nextBlock) (*_count)++; } } else if ((index -= kIndirectsPerBlock) < kIndirectsPerBlock2) { // double indirect blocks CachedBlock cached(fVolume); uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( fStream->double_indirect)); if (indirectBlocks == NULL) return B_IO_ERROR; uint32 indirectIndex = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index / kIndirectsPerBlock]); if (indirectIndex == 0) { // a sparse indirect block block = 0; } else { indirectBlocks = (uint32*)cached.SetTo(indirectIndex); if (indirectBlocks == NULL) return B_IO_ERROR; block = B_LENDIAN_TO_HOST_INT32( indirectBlocks[index & (kIndirectsPerBlock - 1)]); if (_count) { *_count = 1; uint32 nextBlock = block; while (((++index & (kIndirectsPerBlock - 1)) != 0) && indirectBlocks[index & (kIndirectsPerBlock - 1)] == ++nextBlock) (*_count)++; } } ASSERT(block != 0); } else if ((index -= kIndirectsPerBlock2) < kIndirectsPerBlock3) { // triple indirect blocks CachedBlock cached(fVolume); uint32* indirectBlocks = (uint32*)cached.SetTo(B_LENDIAN_TO_HOST_INT32( fStream->triple_indirect)); if (indirectBlocks == NULL) return B_IO_ERROR; uint32 indirectIndex = B_LENDIAN_TO_HOST_INT32(indirectBlocks[index / kIndirectsPerBlock2]); if (indirectIndex == 0) { // a sparse indirect block block = 0; } else { indirectBlocks = (uint32*)cached.SetTo(indirectIndex); if (indirectBlocks == NULL) return B_IO_ERROR; indirectIndex = B_LENDIAN_TO_HOST_INT32( indirectBlocks[(index / kIndirectsPerBlock) & (kIndirectsPerBlock - 1)]); if (indirectIndex == 0) { // a sparse indirect block block = 0; } else { indirectBlocks = (uint32*)cached.SetTo(indirectIndex); if (indirectBlocks == NULL) return B_IO_ERROR; block = B_LENDIAN_TO_HOST_INT32( indirectBlocks[index & (kIndirectsPerBlock - 1)]); if (_count) { *_count = 1; uint32 nextBlock = block; while (((++index & (kIndirectsPerBlock - 1)) != 0) && indirectBlocks[index & (kIndirectsPerBlock - 1)] == ++nextBlock) (*_count)++; } } } ASSERT(block != 0); } else { // Outside of the possible data stream ERROR("ext2: block outside datastream!\n"); return B_ERROR; } TRACE("FindBlock(offset %" B_PRIdOFF "): %" B_PRIu64" %" B_PRIu32 "\n", offset, block, _count != NULL ? *_count : 1); return B_OK; } status_t DataStream::Enlarge(Transaction& transaction, off_t& numBlocks) { TRACE("DataStream::Enlarge(): current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, numBlocks); off_t targetBlocks = numBlocks; fWaiting = _BlocksNeeded(numBlocks); numBlocks = fWaiting; status_t status; if (fNumBlocks <= kMaxDirect) { status = _AddForDirectBlocks(transaction, targetBlocks); if (status != B_OK) { ERROR("DataStream::Enlarge(): _AddForDirectBlocks() failed\n"); return status; } TRACE("DataStream::Enlarge(): current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, targetBlocks); if (fNumBlocks == targetBlocks) return B_OK; } TRACE("DataStream::Enlarge(): indirect current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, targetBlocks); if (fNumBlocks <= kMaxIndirect) { status = _AddForIndirectBlock(transaction, targetBlocks); if (status != B_OK) { ERROR("DataStream::Enlarge(): _AddForIndirectBlock() failed\n"); return status; } TRACE("DataStream::Enlarge(): current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, targetBlocks); if (fNumBlocks == targetBlocks) return B_OK; } TRACE("DataStream::Enlarge(): indirect2 current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, targetBlocks); if (fNumBlocks <= kMaxDoubleIndirect) { status = _AddForDoubleIndirectBlock(transaction, targetBlocks); if (status != B_OK) { ERROR("DataStream::Enlarge(): _AddForDoubleIndirectBlock() failed\n"); return status; } TRACE("DataStream::Enlarge(): current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, targetBlocks); if (fNumBlocks == targetBlocks) return B_OK; } TRACE("DataStream::Enlarge(): indirect3 current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, targetBlocks); TRACE("DataStream::Enlarge(): allocated: %" B_PRIu32 ", waiting: %" B_PRIu32 "\n", fAllocated, fWaiting); return _AddForTripleIndirectBlock(transaction, targetBlocks); } status_t DataStream::Shrink(Transaction& transaction, off_t& numBlocks) { TRACE("DataStream::Shrink(): current size: %" B_PRIdOFF ", target size: %" B_PRIdOFF "\n", fNumBlocks, numBlocks); fFreeStart = 0; fFreeCount = 0; fRemovedBlocks = 0; off_t oldNumBlocks = fNumBlocks; off_t blocksToRemove = fNumBlocks - numBlocks; status_t status; if (numBlocks < kMaxDirect) { status = _RemoveFromDirectBlocks(transaction, numBlocks); if (status != B_OK) { ERROR("DataStream::Shrink(): _RemoveFromDirectBlocks() failed\n"); return status; } if (fRemovedBlocks == blocksToRemove) { fNumBlocks -= fRemovedBlocks; numBlocks = _BlocksNeeded(oldNumBlocks); return _PerformFree(transaction); } } if (numBlocks < kMaxIndirect) { status = _RemoveFromIndirectBlock(transaction, numBlocks); if (status != B_OK) { ERROR("DataStream::Shrink(): _RemoveFromIndirectBlock() failed\n"); return status; } if (fRemovedBlocks == blocksToRemove) { fNumBlocks -= fRemovedBlocks; numBlocks = _BlocksNeeded(oldNumBlocks); return _PerformFree(transaction); } } if (numBlocks < kMaxDoubleIndirect) { status = _RemoveFromDoubleIndirectBlock(transaction, numBlocks); if (status != B_OK) { ERROR("DataStream::Shrink(): _RemoveFromDoubleIndirectBlock() failed\n"); return status; } if (fRemovedBlocks == blocksToRemove) { fNumBlocks -= fRemovedBlocks; numBlocks = _BlocksNeeded(oldNumBlocks); return _PerformFree(transaction); } } status = _RemoveFromTripleIndirectBlock(transaction, numBlocks); if (status != B_OK) { ERROR("DataStream::Shrink(): _RemoveFromTripleIndirectBlock() failed\n"); return status; } fNumBlocks -= fRemovedBlocks; numBlocks = _BlocksNeeded(oldNumBlocks); return _PerformFree(transaction); } uint32 DataStream::_BlocksNeeded(off_t numBlocks) { TRACE("DataStream::BlocksNeeded(): num blocks %" B_PRIdOFF "\n", numBlocks); off_t blocksNeeded = 0; if (numBlocks > fNumBlocks) { blocksNeeded += numBlocks - fNumBlocks; if (numBlocks > kMaxDirect) { if (fNumBlocks <= kMaxDirect) blocksNeeded += 1; if (numBlocks > kMaxIndirect) { if (fNumBlocks <= kMaxIndirect) { blocksNeeded += 2 + (numBlocks - kMaxIndirect - 1) / kIndirectsPerBlock; } else { blocksNeeded += (numBlocks - kMaxIndirect - 1) / kIndirectsPerBlock - (fNumBlocks - kMaxIndirect - 1) / kIndirectsPerBlock; } if (numBlocks > kMaxDoubleIndirect) { if (fNumBlocks <= kMaxDoubleIndirect) { blocksNeeded += 2 + (numBlocks - kMaxDoubleIndirect - 1) / kIndirectsPerBlock2; } else { blocksNeeded += (numBlocks - kMaxDoubleIndirect - 1) / kIndirectsPerBlock - (fNumBlocks - kMaxDoubleIndirect - 1) / kIndirectsPerBlock; } } } } } TRACE("DataStream::BlocksNeeded(): %" B_PRIdOFF "\n", blocksNeeded); return blocksNeeded; } status_t DataStream::_GetBlock(Transaction& transaction, uint32& blockNum) { TRACE("DataStream::_GetBlock(): allocated: %" B_PRIu32 ", pos: %" B_PRIu64 ", waiting: %" B_PRIu32 "\n", fAllocated, fAllocatedPos, fWaiting); if (fAllocated == 0) { uint32 blockGroup = (fAllocatedPos - fFirstBlock) / fVolume->BlocksPerGroup(); status_t status = fVolume->AllocateBlocks(transaction, 1, fWaiting, blockGroup, fAllocatedPos, fAllocated); if (status != B_OK) { ERROR("DataStream::_GetBlock(): AllocateBlocks() failed()\n"); return status; } if (fAllocatedPos > UINT_MAX) return B_FILE_TOO_LARGE; fWaiting -= fAllocated; TRACE("DataStream::_GetBlock(): newAllocated: %" B_PRIu32 ", newpos: %" B_PRIu64 ", newwaiting: %" B_PRIu32 "\n", fAllocated, fAllocatedPos, fWaiting); } fAllocated--; blockNum = (uint32)fAllocatedPos++; return B_OK; } status_t DataStream::_PrepareBlock(Transaction& transaction, uint32* pos, uint32& blockNum, bool& clear) { blockNum = B_LENDIAN_TO_HOST_INT32(*pos); clear = false; if (blockNum == 0) { status_t status = _GetBlock(transaction, blockNum); if (status != B_OK) { ERROR("DataStream::_PrepareBlock() _GetBlock() failed blockNum %" B_PRIu32 "\n", blockNum); return status; } *pos = B_HOST_TO_LENDIAN_INT32(blockNum); clear = true; } return B_OK; } status_t DataStream::_AddBlocks(Transaction& transaction, uint32* block, off_t _count) { off_t count = _count; TRACE("DataStream::_AddBlocks(): count: %" B_PRIdOFF "\n", count); while (count > 0) { uint32 blockNum; status_t status = _GetBlock(transaction, blockNum); if (status != B_OK) return status; *(block++) = B_HOST_TO_LENDIAN_INT32(blockNum); --count; } fNumBlocks += _count; return B_OK; } status_t DataStream::_AddBlocks(Transaction& transaction, uint32* block, off_t start, off_t end, int recursion) { TRACE("DataStream::_AddBlocks(): start: %" B_PRIdOFF ", end %" B_PRIdOFF ", recursion: %d\n", start, end, recursion); bool clear; uint32 blockNum; status_t status = _PrepareBlock(transaction, block, blockNum, clear); if (status != B_OK) return status; CachedBlock cached(fVolume); uint32* childBlock = (uint32*)cached.SetToWritable(transaction, blockNum, clear); if (childBlock == NULL) return B_IO_ERROR; if (recursion == 0) return _AddBlocks(transaction, &childBlock[start], end - start); uint32 elementWidth; if (recursion == 1) elementWidth = kIndirectsPerBlock; else if (recursion == 2) elementWidth = kIndirectsPerBlock2; else { panic("Undefined recursion level\n"); elementWidth = 0; } uint32 elementPos = start / elementWidth; uint32 endPos = end / elementWidth; TRACE("DataStream::_AddBlocks(): element pos: %" B_PRIu32 ", end pos: %" B_PRIu32 "\n", elementPos, endPos); recursion--; if (elementPos == endPos) { return _AddBlocks(transaction, &childBlock[elementPos], start % elementWidth, end % elementWidth, recursion); } if (start % elementWidth != 0) { status = _AddBlocks(transaction, &childBlock[elementPos], start % elementWidth, elementWidth, recursion); if (status != B_OK) { ERROR("DataStream::_AddBlocks() _AddBlocks() start failed\n"); return status; } elementPos++; } while (elementPos < endPos) { status = _AddBlocks(transaction, &childBlock[elementPos], 0, elementWidth, recursion); if (status != B_OK) { ERROR("DataStream::_AddBlocks() _AddBlocks() mid failed\n"); return status; } elementPos++; } if (end % elementWidth != 0) { status = _AddBlocks(transaction, &childBlock[elementPos], 0, end % elementWidth, recursion); if (status != B_OK) { ERROR("DataStream::_AddBlocks() _AddBlocks() end failed\n"); return status; } } return B_OK; } status_t DataStream::_AddForDirectBlocks(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_AddForDirectBlocks(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32* direct = &fStream->direct[fNumBlocks]; uint32 end = numBlocks > kMaxDirect ? kMaxDirect : numBlocks; return _AddBlocks(transaction, direct, end - fNumBlocks); } status_t DataStream::_AddForIndirectBlock(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_AddForIndirectBlocks(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32 *indirect = &fStream->indirect; uint32 start = fNumBlocks - kMaxDirect; uint32 end = numBlocks - kMaxDirect; if (end > kIndirectsPerBlock) end = kIndirectsPerBlock; return _AddBlocks(transaction, indirect, start, end, 0); } status_t DataStream::_AddForDoubleIndirectBlock(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_AddForDoubleIndirectBlock(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32 *doubleIndirect = &fStream->double_indirect; uint32 start = fNumBlocks - kMaxIndirect; uint32 end = numBlocks - kMaxIndirect; if (end > kIndirectsPerBlock2) end = kIndirectsPerBlock2; return _AddBlocks(transaction, doubleIndirect, start, end, 1); } status_t DataStream::_AddForTripleIndirectBlock(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_AddForTripleIndirectBlock(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32 *tripleIndirect = &fStream->triple_indirect; uint32 start = fNumBlocks - kMaxDoubleIndirect; uint32 end = numBlocks - kMaxDoubleIndirect; return _AddBlocks(transaction, tripleIndirect, start, end, 2); } status_t DataStream::_PerformFree(Transaction& transaction) { TRACE("DataStream::_PerformFree(): start: %" B_PRIu32 ", count: %" B_PRIu32 "\n", fFreeStart, fFreeCount); status_t status; if (fFreeCount == 0) status = B_OK; else status = fVolume->FreeBlocks(transaction, fFreeStart, fFreeCount); fFreeStart = 0; fFreeCount = 0; return status; } status_t DataStream::_MarkBlockForRemoval(Transaction& transaction, uint32* block) { TRACE("DataStream::_MarkBlockForRemoval(*(%p) = %" B_PRIu32 "): free start: %" B_PRIu32 ", free count: %" B_PRIu32 "\n", block, B_LENDIAN_TO_HOST_INT32(*block), fFreeStart, fFreeCount); uint32 blockNum = B_LENDIAN_TO_HOST_INT32(*block); *block = 0; if (blockNum != fFreeStart + fFreeCount) { if (fFreeCount != 0) { status_t status = fVolume->FreeBlocks(transaction, fFreeStart, fFreeCount); if (status != B_OK) return status; } fFreeStart = blockNum; fFreeCount = 0; } fFreeCount++; return B_OK; } status_t DataStream::_FreeBlocks(Transaction& transaction, uint32* block, uint32 _count) { uint32 count = _count; TRACE("DataStream::_FreeBlocks(%p, %" B_PRIu32 ")\n", block, count); while (count > 0) { status_t status = _MarkBlockForRemoval(transaction, block); if (status != B_OK) return status; block++; count--; } fRemovedBlocks += _count; return B_OK; } status_t DataStream::_FreeBlocks(Transaction& transaction, uint32* block, off_t start, off_t end, bool freeParent, int recursion) { // TODO: Designed specifically for shrinking. Perhaps make it more general? TRACE("DataStream::_FreeBlocks(%p, %" B_PRIdOFF ", %" B_PRIdOFF ", %c, %d)\n", block, start, end, freeParent ? 't' : 'f', recursion); uint32 blockNum = B_LENDIAN_TO_HOST_INT32(*block); if (freeParent) { status_t status = _MarkBlockForRemoval(transaction, block); if (status != B_OK) return status; } CachedBlock cached(fVolume); uint32* childBlock = (uint32*)cached.SetToWritable(transaction, blockNum); if (childBlock == NULL) return B_IO_ERROR; if (recursion == 0) return _FreeBlocks(transaction, &childBlock[start], end - start); uint32 elementWidth; if (recursion == 1) elementWidth = kIndirectsPerBlock; else if (recursion == 2) elementWidth = kIndirectsPerBlock2; else { panic("Undefinied recursion level\n"); elementWidth = 0; } uint32 elementPos = start / elementWidth; uint32 endPos = end / elementWidth; recursion--; if (elementPos == endPos) { bool free = freeParent || start % elementWidth == 0; return _FreeBlocks(transaction, &childBlock[elementPos], start % elementWidth, end % elementWidth, free, recursion); } status_t status = B_OK; if (start % elementWidth != 0) { status = _FreeBlocks(transaction, &childBlock[elementPos], start % elementWidth, elementWidth, false, recursion); if (status != B_OK) return status; elementPos++; } while (elementPos < endPos) { status = _FreeBlocks(transaction, &childBlock[elementPos], 0, elementWidth, true, recursion); if (status != B_OK) return status; elementPos++; } if (end % elementWidth != 0) { status = _FreeBlocks(transaction, &childBlock[elementPos], 0, end % elementWidth, true, recursion); } return status; } status_t DataStream::_RemoveFromDirectBlocks(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_RemoveFromDirectBlocks(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32* direct = &fStream->direct[numBlocks]; off_t end = fNumBlocks > kMaxDirect ? kMaxDirect : fNumBlocks; return _FreeBlocks(transaction, direct, end - numBlocks); } status_t DataStream::_RemoveFromIndirectBlock(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_RemoveFromIndirectBlock(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32* indirect = &fStream->indirect; off_t start = numBlocks <= kMaxDirect ? 0 : numBlocks - kMaxDirect; off_t end = fNumBlocks - kMaxDirect; if (end > kIndirectsPerBlock) end = kIndirectsPerBlock; bool freeAll = start == 0; return _FreeBlocks(transaction, indirect, start, end, freeAll, 0); } status_t DataStream::_RemoveFromDoubleIndirectBlock(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_RemoveFromDoubleIndirectBlock(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32* doubleIndirect = &fStream->double_indirect; off_t start = numBlocks <= kMaxIndirect ? 0 : numBlocks - kMaxIndirect; off_t end = fNumBlocks - kMaxIndirect; if (end > kIndirectsPerBlock2) end = kIndirectsPerBlock2; bool freeAll = start == 0; return _FreeBlocks(transaction, doubleIndirect, start, end, freeAll, 1); } status_t DataStream::_RemoveFromTripleIndirectBlock(Transaction& transaction, uint32 numBlocks) { TRACE("DataStream::_RemoveFromTripleIndirectBlock(): current size: %" B_PRIdOFF ", target size: %" B_PRIu32 "\n", fNumBlocks, numBlocks); uint32* tripleIndirect = &fStream->triple_indirect; off_t start = numBlocks <= kMaxDoubleIndirect ? 0 : numBlocks - kMaxDoubleIndirect; off_t end = fNumBlocks - kMaxDoubleIndirect; bool freeAll = start == 0; return _FreeBlocks(transaction, tripleIndirect, start, end, freeAll, 2); }