diff --git a/src/add-ons/kernel/file_systems/ext2/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/ext2/BlockAllocator.cpp index 99a9a510c4..cfd2f5dfc4 100644 --- a/src/add-ons/kernel/file_systems/ext2/BlockAllocator.cpp +++ b/src/add-ons/kernel/file_systems/ext2/BlockAllocator.cpp @@ -39,7 +39,7 @@ public: status_t ScanFreeRanges(); bool IsFull() const; - status_t Allocate(Transaction& transaction, uint32 start, + status_t Allocate(Transaction& transaction, fsblock_t start, uint32 length); status_t Free(Transaction& transaction, uint32 start, uint32 length); @@ -48,9 +48,9 @@ public: uint32 NumBits() const; uint32 FreeBits() const; - uint32 Start() const; + fsblock_t Start() const; - uint32 LargestStart() const; + fsblock_t LargestStart() const; uint32 LargestLength() const; // TransactionListener implementation @@ -70,9 +70,9 @@ private: mutex fTransactionLock; int32 fCurrentTransaction; - uint32 fStart; + fsblock_t fStart; uint32 fNumBits; - off_t fBitmapBlock; + fsblock_t fBitmapBlock; uint32 fFreeBits; uint32 fFirstFree; @@ -122,6 +122,7 @@ AllocationBlockGroup::Initialize(Volume* volume, uint32 blockGroup, fVolume = volume; fBlockGroup = blockGroup; fNumBits = numBits; + fStart = blockGroup * numBits + fVolume->FirstDataBlock(); status_t status = fVolume->GetBlockGroup(blockGroup, &fGroupDescriptor); if (status != B_OK) @@ -187,13 +188,17 @@ AllocationBlockGroup::IsFull() const status_t -AllocationBlockGroup::Allocate(Transaction& transaction, uint32 start, +AllocationBlockGroup::Allocate(Transaction& transaction, fsblock_t _start, uint32 length) { + uint32 start = _start - fStart; TRACE("AllocationBlockGroup::Allocate(%ld,%ld)\n", start, length); + if (length == 0) + return B_OK; + uint32 end = start + length; if (end > fNumBits) - return B_BAD_DATA; + return B_BAD_VALUE; _LockInTransaction(transaction); @@ -263,9 +268,8 @@ AllocationBlockGroup::Free(Transaction& transaction, uint32 start, return B_OK; uint32 end = start + length; - if (end > fNumBits) - return B_BAD_DATA; + return B_BAD_VALUE; _LockInTransaction(transaction); @@ -342,17 +346,17 @@ AllocationBlockGroup::FreeBits() const } -uint32 +fsblock_t AllocationBlockGroup::Start() const { return fStart; } -uint32 +fsblock_t AllocationBlockGroup::LargestStart() const { - return fLargestStart; + return fStart + fLargestStart; } @@ -457,7 +461,7 @@ BlockAllocator::Initialize() fNumBlocks = fVolume->NumBlocks(); TRACE("BlockAllocator::Initialize(): blocks per group: %lu, block groups: " - "%lu, first block: %lu, num blocks: %lu\n", fBlocksPerGroup, + "%lu, first block: %llu, num blocks: %llu\n", fBlocksPerGroup, fNumGroups, fFirstBlock, fNumBlocks); fGroups = new(std::nothrow) AllocationBlockGroup[fNumGroups]; @@ -486,7 +490,7 @@ BlockAllocator::Initialize() status_t BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum, - uint32 maximum, uint32& blockGroup, off_t& start, uint32& length) + uint32 maximum, uint32& blockGroup, fsblock_t& start, uint32& length) { TRACE("BlockAllocator::AllocateBlocks()\n"); MutexLocker lock(fLock); @@ -496,7 +500,7 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum, "max: %lu, block group: %lu, start: %llu, num groups: %lu\n", transaction.ID(), minimum, maximum, blockGroup, start, fNumGroups); - off_t bestStart = 0; + fsblock_t bestStart = 0; uint32 bestLength = 0; uint32 bestGroup = 0; @@ -567,10 +571,12 @@ BlockAllocator::AllocateBlocks(Transaction& transaction, uint32 minimum, status_t BlockAllocator::Allocate(Transaction& transaction, Inode* inode, - off_t numBlocks, uint32 minimum, off_t& start, uint32& allocated) + off_t numBlocks, uint32 minimum, fsblock_t& start, uint32& allocated) { if (numBlocks <= 0) return B_ERROR; + if (start > fNumBlocks) + return B_BAD_VALUE; uint32 group = inode->ID() / fVolume->InodesPerGroup(); uint32 preferred = 0; @@ -644,28 +650,32 @@ BlockAllocator::Allocate(Transaction& transaction, Inode* inode, status_t -BlockAllocator::Free(Transaction& transaction, off_t start, uint32 length) +BlockAllocator::Free(Transaction& transaction, fsblock_t start, uint32 length) { TRACE("BlockAllocator::Free(%llu, %lu)\n", start, length); MutexLocker lock(fLock); if (start <= fFirstBlock) { panic("Trying to free superblock!\n"); - return B_BAD_DATA; + return B_BAD_VALUE; } if (length == 0) return B_OK; + if (start > fNumBlocks || length > fNumBlocks) + return B_BAD_VALUE; - TRACE("BlockAllocator::Free(): first block: %lu, blocks per group: %lu\n", + TRACE("BlockAllocator::Free(): first block: %llu, blocks per group: %lu\n", fFirstBlock, fBlocksPerGroup); start -= fFirstBlock; off_t end = start + length - 1; uint32 group = start / fBlocksPerGroup; - if (group >= fNumGroups) - panic("BlockAllocator::Free() group %ld too big (fNumGroups %ld)\n", group, fNumGroups); + if (group >= fNumGroups) { + panic("BlockAllocator::Free() group %ld too big (fNumGroups %ld)\n", + group, fNumGroups); + } uint32 lastGroup = end / fBlocksPerGroup; start = start % fBlocksPerGroup; diff --git a/src/add-ons/kernel/file_systems/ext2/BlockAllocator.h b/src/add-ons/kernel/file_systems/ext2/BlockAllocator.h index e951bfcee3..8062fdba73 100644 --- a/src/add-ons/kernel/file_systems/ext2/BlockAllocator.h +++ b/src/add-ons/kernel/file_systems/ext2/BlockAllocator.h @@ -8,8 +8,10 @@ #ifndef BLOCKALLOCATOR_H #define BLOCKALLOCATOR_H + #include +#include "ext2.h" #include "Transaction.h" @@ -27,11 +29,11 @@ public: status_t AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, uint32& blockGroup, - off_t& start, uint32& length); + fsblock_t& start, uint32& length); status_t Allocate(Transaction& transaction, Inode* inode, - off_t numBlocks, uint32 minimum, off_t& start, + off_t numBlocks, uint32 minimum, fsblock_t& start, uint32& length); - status_t Free(Transaction& transaction, off_t start, + status_t Free(Transaction& transaction, fsblock_t start, uint32 length); uint32 FreeBlocks(); @@ -45,9 +47,9 @@ protected: AllocationBlockGroup* fGroups; uint32 fBlocksPerGroup; - uint32 fNumBlocks; + fsblock_t fNumBlocks; uint32 fNumGroups; - uint32 fFirstBlock; + fsblock_t fFirstBlock; }; #endif // BLOCKALLOCATOR_H diff --git a/src/add-ons/kernel/file_systems/ext2/DataStream.cpp b/src/add-ons/kernel/file_systems/ext2/DataStream.cpp index 68267a4504..6a28b87715 100644 --- a/src/add-ons/kernel/file_systems/ext2/DataStream.cpp +++ b/src/add-ons/kernel/file_systems/ext2/DataStream.cpp @@ -53,7 +53,7 @@ DataStream::~DataStream() status_t -DataStream::FindBlock(off_t offset, off_t& block, uint32 *_count) +DataStream::FindBlock(off_t offset, fsblock_t& block, uint32 *_count) { uint32 index = offset >> fVolume->BlockShift(); @@ -377,8 +377,7 @@ DataStream::_GetBlock(Transaction& transaction, uint32& blockNum) if (fAllocated == 0) { uint32 blockGroup = (fAllocatedPos - fFirstBlock) / fVolume->BlocksPerGroup(); - fAllocatedPos %= fVolume->BlocksPerGroup(); - + status_t status = fVolume->AllocateBlocks(transaction, 1, fWaiting, blockGroup, fAllocatedPos, fAllocated); if (status != B_OK) { @@ -387,7 +386,6 @@ DataStream::_GetBlock(Transaction& transaction, uint32& blockNum) } fWaiting -= fAllocated; - fAllocatedPos += fVolume->BlocksPerGroup() * blockGroup + fFirstBlock; TRACE("DataStream::_GetBlock(): newAllocated: %lu, newpos: %llu," "newwaiting: %lu\n", fAllocated, fAllocatedPos, fWaiting); diff --git a/src/add-ons/kernel/file_systems/ext2/DataStream.h b/src/add-ons/kernel/file_systems/ext2/DataStream.h index 9c39332f0b..c2c769001f 100644 --- a/src/add-ons/kernel/file_systems/ext2/DataStream.h +++ b/src/add-ons/kernel/file_systems/ext2/DataStream.h @@ -8,6 +8,7 @@ #ifndef DATASTREAM_H #define DATASTREAM_H + #include "ext2.h" #include "Transaction.h" @@ -22,7 +23,7 @@ public: off_t size); ~DataStream(); - status_t FindBlock(off_t offset, off_t& block, + status_t FindBlock(off_t offset, fsblock_t& block, uint32 *_count = NULL); status_t Enlarge(Transaction& transaction, off_t& numBlocks); status_t Shrink(Transaction& transaction, off_t& numBlocks); @@ -82,7 +83,7 @@ private: uint32 fFirstBlock; uint32 fAllocated; - off_t fAllocatedPos; + fsblock_t fAllocatedPos; uint32 fWaiting; uint32 fFreeStart; diff --git a/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.cpp b/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.cpp index cb87a40c82..21e64bd458 100644 --- a/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.cpp +++ b/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.cpp @@ -517,7 +517,7 @@ DirectoryIterator::_SplitIndexedBlock(Transaction& transaction, return B_NO_MEMORY; ArrayDeleter bufferDeleter(buffer); - off_t firstPhysicalBlock = 0; + fsblock_t firstPhysicalBlock = 0; // Prepare block to hold the first half of the entries and fill the buffer CachedBlock cachedFirst(fVolume); diff --git a/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.h b/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.h index e662a99a33..0c88cdd4e4 100644 --- a/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.h +++ b/src/add-ons/kernel/file_systems/ext2/DirectoryIterator.h @@ -6,6 +6,8 @@ #define DIRECTORY_ITERATOR_H +#include "ext2.h" + #include #include "Transaction.h" @@ -69,7 +71,7 @@ protected: uint32 fNumBlocks; uint32 fLogicalBlock; - off_t fPhysicalBlock; + fsblock_t fPhysicalBlock; uint32 fDisplacement; uint32 fPreviousDisplacement; diff --git a/src/add-ons/kernel/file_systems/ext2/ExtentStream.cpp b/src/add-ons/kernel/file_systems/ext2/ExtentStream.cpp new file mode 100644 index 0000000000..8cc516a362 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ext2/ExtentStream.cpp @@ -0,0 +1,510 @@ +/* + * Copyright 2001-2010, Haiku Inc. All rights reserved. + * This file may be used under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + */ + + +#include "ExtentStream.h" + +#include + +#include "CachedBlock.h" +#include "Volume.h" + + +#undef ASSERT +//#define TRACE_EXT2 +#ifdef TRACE_EXT2 +# define TRACE(x...) dprintf("\33[34mext2:\33[0m ExtentStream::" x) +# define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); } +#else +# define TRACE(x...) ; +# define ASSERT(x) ; +#endif +#define ERROR(x...) dprintf("\33[34mext2:\33[0m ExtentStream::" x) + + +ExtentStream::ExtentStream(Volume* volume, ext2_extent_stream* stream, + off_t size) + : + fVolume(volume), + fStream(stream), + fFirstBlock(volume->FirstDataBlock()), + fAllocatedPos(fFirstBlock), + fSize(size) +{ + fNumBlocks = size == 0 ? 0 : ((size - 1) >> fVolume->BlockShift()) + 1; +} + + +ExtentStream::~ExtentStream() +{ +} + + +status_t +ExtentStream::FindBlock(off_t offset, fsblock_t& block, uint32 *_count) +{ + fileblock_t index = offset >> fVolume->BlockShift(); + TRACE("FindBlock(%lld, %lld)\n", offset, index); + + if (offset >= fSize) { + TRACE("FindBlock: offset larger than inode size\n"); + return B_ENTRY_NOT_FOUND; + } + + ext2_extent_stream *stream = fStream; + if (!stream->extent_header.IsValid()) + panic("ExtentStream::FindBlock() invalid header\n"); + + CachedBlock cached(fVolume); + while (stream->extent_header.Depth() != 0) { + TRACE("FindBlock() depth %d\n", + stream->extent_header.Depth()); + int32 i = 1; + while (i < stream->extent_header.NumEntries() + && stream->extent_index[i].LogicalBlock() <= index) { + i++; + } + TRACE("FindBlock() getting index %ld at %lld\n", i - 1, + stream->extent_index[i - 1].PhysicalBlock()); + stream = (ext2_extent_stream *)cached.SetTo( + stream->extent_index[i - 1].PhysicalBlock()); + if (!stream->extent_header.IsValid()) + panic("ExtentStream::FindBlock() invalid header\n"); + } + + for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) { + if (stream->extent_entries[i].LogicalBlock() > index) { + // sparse block + TRACE("FindBlock() sparse block index %lld at %ld\n", index, + stream->extent_entries[i].LogicalBlock()); + block = 0xffffffff; + return B_OK; + } + fileblock_t diff = index - stream->extent_entries[i].LogicalBlock(); + if (diff < stream->extent_entries[i].Length()) { + block = stream->extent_entries[i].PhysicalBlock() + diff; + if (_count) + *_count = stream->extent_entries[i].Length() - diff; + TRACE("FindBlock(offset %lld): %lld %ld\n", offset, + block, _count != NULL ? *_count : 1); + return B_OK; + } + } + + return B_ERROR; +} + + +status_t +ExtentStream::Enlarge(Transaction& transaction, off_t& numBlocks) +{ + TRACE("Enlarge(): current size: %llu, target size: %llu\n", + fNumBlocks, numBlocks); + + off_t targetBlocks = numBlocks; + numBlocks = targetBlocks - fNumBlocks; + uint32 allocated = 0; + + while (fNumBlocks < targetBlocks) { + // allocate new blocks + uint32 blockGroup = (fAllocatedPos - fFirstBlock) + / fVolume->BlocksPerGroup(); + + if (allocated == 0) { + status_t status = fVolume->AllocateBlocks(transaction, 1, + targetBlocks - fNumBlocks, blockGroup, fAllocatedPos, + allocated); + if (status != B_OK) { + ERROR("Enlarge(): AllocateBlocks() failed()\n"); + return status; + } + } + + ASSERT(_CheckBlock(fStream, fAllocatedPos) == B_OK); + + ext2_extent_stream *stream = fStream; + fsblock_t path[stream->extent_header.Depth()]; + + // search for the leaf + CachedBlock cached(fVolume); + int32 level = -1; + for (; stream->extent_header.Depth() != 0;) { + if (stream->extent_header.NumEntries() == 0) + panic("stream->extent_header.NumEntries() == 0\n"); + int32 lastIndex = stream->extent_header.NumEntries() - 1; + TRACE("Enlarge() depth %d\n", stream->extent_header.Depth()); + TRACE("Enlarge() getting index %ld at %lld\n", lastIndex, + stream->extent_index[lastIndex].PhysicalBlock()); + path[++level] = stream->extent_index[lastIndex].PhysicalBlock(); + stream = (ext2_extent_stream *)cached.SetTo(path[level]); + if (stream == NULL) + return B_IO_ERROR; + } + + // check if the new extent is adjacent + if (stream->extent_header.NumEntries() > 0) { + ext2_extent_entry &last = stream->extent_entries[ + stream->extent_header.NumEntries() - 1]; + TRACE("Enlarge() last %lld allocatedPos %lld\n", + last.PhysicalBlock() + last.Length(), fAllocatedPos); + if (last.PhysicalBlock() + last.Length() == fAllocatedPos + && (last.Length() + allocated) <= 0xffff) { + if (stream != fStream) { + stream = (ext2_extent_stream *)cached.SetToWritable( + transaction, cached.BlockNumber()); + if (stream == NULL) + return B_IO_ERROR; + } + stream->extent_entries[stream->extent_header.NumEntries() - 1] + .SetLength(last.Length() + allocated); + fNumBlocks += allocated; + allocated = 0; + TRACE("Enlarge() entry extended\n"); + continue; + } + } + + if (stream->extent_header.NumEntries() + >= stream->extent_header.MaxEntries()) { + TRACE("Enlarge() adding leaf and indexes at depth %d level %ld\n", + stream->extent_header.Depth(), level); + // try to add a leaf and indexes + while (--level >= 0) { + stream = (ext2_extent_stream *)cached.SetTo(path[level]); + if (stream == NULL) + return B_IO_ERROR; + if (stream->extent_header.NumEntries() + < stream->extent_header.MaxEntries()) { + break; + } + TRACE("Enlarge() going up from level %ld\n", level); + } + + if (level < 0 && fStream->extent_header.NumEntries() + >= fStream->extent_header.MaxEntries()) { + TRACE("Enlarge() add a level, increment root depth\n"); + fsblock_t newBlock = fStream->extent_index[0].PhysicalBlock(); + uint32 _allocated; + status_t status = fVolume->AllocateBlocks(transaction, 1, 1, + blockGroup, newBlock, _allocated); + if (status != B_OK) { + ERROR("Enlarge() AllocateBlocks() failed()\n"); + return status; + } + ASSERT(_CheckBlock(fStream, newBlock) == B_OK); + TRACE("Enlarge() move root to block %lld\n", newBlock); + numBlocks++; + stream = (ext2_extent_stream *)cached.SetToWritable( + transaction, newBlock); + if (stream == NULL) + return B_IO_ERROR; + ASSERT(fStream->extent_header.IsValid()); + memcpy(stream, fStream, sizeof(ext2_extent_stream)); + fStream->extent_header.SetDepth(stream->extent_header.Depth() + + 1); + TRACE("Enlarge() new root depth %d\n", + fStream->extent_header.Depth()); + fStream->extent_header.SetNumEntries(1); + fStream->extent_index[0].SetLogicalBlock(0); + fStream->extent_index[0].SetPhysicalBlock(newBlock); + stream->extent_header.SetMaxEntries((fVolume->BlockSize() + - sizeof(ext2_extent_header)) / sizeof(ext2_extent_index)); + ASSERT(stream->extent_header.IsValid()); + + ASSERT(Check()); + continue; + } + + if (level < 0) + stream = fStream; + + uint16 depth = stream->extent_header.Depth(); + while (depth > 1) { + TRACE("Enlarge() adding an index block at depth %d\n", depth); + fsblock_t newBlock = path[level]; + uint32 _allocated; + status_t status = fVolume->AllocateBlocks(transaction, 1, 1, + blockGroup, newBlock, _allocated); + if (status != B_OK) { + ERROR("Enlarge(): AllocateBlocks() failed()\n"); + return status; + } + ASSERT(_CheckBlock(fStream, newBlock) == B_OK); + numBlocks++; + int32 index = stream->extent_header.NumEntries(); + stream->extent_index[index].SetLogicalBlock(fNumBlocks); + stream->extent_index[index].SetPhysicalBlock(newBlock); + stream->extent_header.SetNumEntries(index + 1); + path[level++] = newBlock; + + depth = stream->extent_header.Depth() - 1; + TRACE("Enlarge() init index block %lld at depth %d\n", + newBlock, depth); + stream = (ext2_extent_stream *)cached.SetToWritable( + transaction, newBlock); + if (stream == NULL) + return B_IO_ERROR; + stream->extent_header.magic = EXT2_EXTENT_MAGIC; + stream->extent_header.SetNumEntries(0); + stream->extent_header.SetMaxEntries((fVolume->BlockSize() + - sizeof(ext2_extent_header)) / sizeof(ext2_extent_index)); + stream->extent_header.SetDepth(depth); + stream->extent_header.SetGeneration(0); + + ASSERT(Check()); + } + + TRACE("Enlarge() depth %d level %ld\n", + stream->extent_header.Depth(), level); + + if (stream->extent_header.Depth() == 1) { + TRACE("Enlarge() adding an entry block at depth %d level %ld\n", + depth, level); + fsblock_t newBlock; + if (level >= 0) + newBlock = path[level]; + else + newBlock = fStream->extent_index[0].PhysicalBlock(); + uint32 _allocated; + status_t status = fVolume->AllocateBlocks(transaction, 1, 1, + blockGroup, newBlock, _allocated); + if (status != B_OK) { + ERROR("Enlarge(): AllocateBlocks() failed()\n"); + return status; + } + + ASSERT(_CheckBlock(fStream, newBlock) == B_OK); + numBlocks++; + int32 index = stream->extent_header.NumEntries(); + stream->extent_index[index].SetLogicalBlock(fNumBlocks); + stream->extent_index[index].SetPhysicalBlock(newBlock); + stream->extent_header.SetNumEntries(index + 1); + + TRACE("Enlarge() init entry block %lld at depth %d\n", + newBlock, depth); + stream = (ext2_extent_stream *)cached.SetToWritable( + transaction, newBlock); + if (stream == NULL) + return B_IO_ERROR; + stream->extent_header.magic = EXT2_EXTENT_MAGIC; + stream->extent_header.SetNumEntries(0); + stream->extent_header.SetMaxEntries((fVolume->BlockSize() + - sizeof(ext2_extent_header)) / sizeof(ext2_extent_entry)); + stream->extent_header.SetDepth(0); + stream->extent_header.SetGeneration(0); + ASSERT(Check()); + } + } + + // add a new entry + TRACE("Enlarge() add entry %lld\n", fAllocatedPos); + if (stream != fStream) { + stream = (ext2_extent_stream *)cached.SetToWritable( + transaction, cached.BlockNumber()); + if (stream == NULL) + return B_IO_ERROR; + } + int32 index = stream->extent_header.NumEntries(); + stream->extent_entries[index].SetLogicalBlock(fNumBlocks); + stream->extent_entries[index].SetLength(allocated); + stream->extent_entries[index].SetPhysicalBlock(fAllocatedPos); + stream->extent_header.SetNumEntries(index + 1); + TRACE("Enlarge() entry added at index %ld\n", index); + ASSERT(stream->extent_header.IsValid()); + + fNumBlocks += allocated; + allocated = 0; + } + + return B_OK; +} + + +status_t +ExtentStream::Shrink(Transaction& transaction, off_t& numBlocks) +{ + TRACE("DataStream::Shrink(): current size: %llu, target size: %llu\n", + fNumBlocks, numBlocks); + + off_t targetBlocks = numBlocks; + numBlocks = fNumBlocks - targetBlocks; + + while (targetBlocks < fNumBlocks) { + ext2_extent_stream *stream = fStream; + fsblock_t path[stream->extent_header.Depth()]; + + // search for the leaf + CachedBlock cached(fVolume); + int32 level = -1; + for (; stream->extent_header.Depth() != 0;) { + if (stream->extent_header.NumEntries() == 0) + panic("stream->extent_header.NumEntries() == 0\n"); + int32 lastIndex = stream->extent_header.NumEntries() - 1; + TRACE("Shrink() depth %d\n", stream->extent_header.Depth()); + TRACE("Shrink() getting index %ld at %lld\n", lastIndex, + stream->extent_index[lastIndex].PhysicalBlock()); + path[++level] = stream->extent_index[lastIndex].PhysicalBlock(); + stream = (ext2_extent_stream *)cached.SetToWritable(transaction, + path[level]); + if (stream == NULL) + return B_IO_ERROR; + if (!stream->extent_header.IsValid()) + panic("Shrink() invalid header\n"); + } + + int32 index = stream->extent_header.NumEntries() - 1; + status_t status = B_OK; + while (index >= 0 && targetBlocks < fNumBlocks) { + ext2_extent_entry &last = stream->extent_entries[index]; + if (last.LogicalBlock() + last.Length() < targetBlocks) { + status = B_BAD_DATA; + break; + } + uint16 length = min_c(last.Length(), fNumBlocks - targetBlocks); + fsblock_t block = last.PhysicalBlock() + last.Length() - length; + TRACE("Shrink() free block %lld length %d\n", block, length); + status = fVolume->FreeBlocks(transaction, block, length); + if (status != B_OK) + break; + fNumBlocks -= length; + stream->extent_entries[index].SetLength(last.Length() - length); + TRACE("Shrink() new length for %ld: %d\n", index, last.Length()); + if (last.Length() != 0) + break; + index--; + TRACE("Shrink() next index: %ld\n", index); + } + TRACE("Shrink() new entry count: %ld\n", index + 1); + stream->extent_header.SetNumEntries(index + 1); + ASSERT(Check()); + + if (status != B_OK) + return status; + + while (stream != fStream && stream->extent_header.NumEntries() == 0) { + TRACE("Shrink() empty leaf at depth %d\n", + stream->extent_header.Depth()); + level--; + if (level >= 0) { + stream = (ext2_extent_stream *)cached.SetToWritable( + transaction, path[level]); + if (stream == NULL) + return B_IO_ERROR; + } else + stream = fStream; + if (!stream->extent_header.IsValid()) + panic("Shrink() invalid header\n"); + uint16 index = stream->extent_header.NumEntries() - 1; + ext2_extent_index &last = stream->extent_index[index]; + if (last.PhysicalBlock() != path[level + 1]) + panic("Shrink() last.PhysicalBlock() != path[level + 1]\n"); + status = fVolume->FreeBlocks(transaction, last.PhysicalBlock(), 1); + if (status != B_OK) + return status; + numBlocks++; + stream->extent_header.SetNumEntries(index); + TRACE("Shrink() new entry count: %d\n", index); + } + if (stream == fStream && stream->extent_header.NumEntries() == 0) + stream->extent_header.SetDepth(0); + + ASSERT(Check()); + } + + return B_OK; +} + + +void +ExtentStream::Init() +{ + fStream->extent_header.magic = EXT2_EXTENT_MAGIC; + fStream->extent_header.SetNumEntries(0); + fStream->extent_header.SetMaxEntries(4); + fStream->extent_header.SetDepth(0); + fStream->extent_header.SetGeneration(0); +} + + +status_t +ExtentStream::_Check(ext2_extent_stream *stream, fileblock_t &block) +{ + if (!stream->extent_header.IsValid()) { + panic("_Check() invalid header\n"); + return B_BAD_VALUE; + } + if (stream->extent_header.Depth() == 0) { + for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) { + ext2_extent_entry &entry = stream->extent_entries[i]; + if (entry.LogicalBlock() < block) { + panic("_Check() entry %ld %lld %ld\n", i, block, + entry.LogicalBlock()); + return B_BAD_VALUE; + } + block = entry.LogicalBlock() + entry.Length(); + } + return B_OK; + } + + CachedBlock cached(fVolume); + for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) { + ext2_extent_index &index = stream->extent_index[i]; + if (index.LogicalBlock() < block) { + panic("_Check() index %ld %lld %ld\n", i, block, + index.LogicalBlock()); + return B_BAD_VALUE; + } + ext2_extent_stream *child = (ext2_extent_stream *)cached.SetTo( + index.PhysicalBlock()); + if (child->extent_header.Depth() != (stream->extent_header.Depth() - 1) + || _Check(child, block) != B_OK) + return B_BAD_VALUE; + } + return B_OK; +} + + +bool +ExtentStream::Check() +{ + fileblock_t block = 0; + return _Check(fStream, block) == B_OK; +} + + +status_t +ExtentStream::_CheckBlock(ext2_extent_stream *stream, fsblock_t block) +{ + if (stream->extent_header.Depth() == 0) { + for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) { + ext2_extent_entry &entry = stream->extent_entries[i]; + if (entry.PhysicalBlock() <= block + && (entry.PhysicalBlock() + entry.Length()) > block) { + panic("_CheckBlock() entry %ld %lld %lld %d\n", i, block, + entry.PhysicalBlock(), entry.Length()); + return B_BAD_VALUE; + } + } + return B_OK; + } + + CachedBlock cached(fVolume); + for (int32 i = 0; i < stream->extent_header.NumEntries(); i++) { + ext2_extent_index &index = stream->extent_index[i]; + if (index.PhysicalBlock() == block) { + panic("_CheckBlock() index %ld %lld\n", i, block); + return B_BAD_VALUE; + } + ext2_extent_stream *child = (ext2_extent_stream *)cached.SetTo( + index.PhysicalBlock()); + if (child->extent_header.Depth() != (stream->extent_header.Depth() - 1) + || _CheckBlock(child, block) != B_OK) + return B_BAD_VALUE; + } + return B_OK; +} diff --git a/src/add-ons/kernel/file_systems/ext2/ExtentStream.h b/src/add-ons/kernel/file_systems/ext2/ExtentStream.h new file mode 100644 index 0000000000..cf110885f2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/ext2/ExtentStream.h @@ -0,0 +1,48 @@ +/* + * Copyright 2001-2010, Haiku Inc. All rights reserved. + * This file may be used under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + */ +#ifndef EXTENTSTREAM_H +#define EXTENTSTREAM_H + +#include "ext2.h" +#include "Transaction.h" + + +class Volume; + + +class ExtentStream +{ +public: + ExtentStream(Volume* volume, ext2_extent_stream* stream, + off_t size); + ~ExtentStream(); + + status_t FindBlock(off_t offset, fsblock_t& block, + uint32 *_count = NULL); + status_t Enlarge(Transaction& transaction, off_t& numBlocks); + status_t Shrink(Transaction& transaction, off_t& numBlocks); + void Init(); + + bool Check(); + +private: + status_t _Check(ext2_extent_stream *stream, fileblock_t &block); + status_t _CheckBlock(ext2_extent_stream *stream, fsblock_t block); + + Volume* fVolume; + ext2_extent_stream* fStream; + fsblock_t fFirstBlock; + + fsblock_t fAllocatedPos; + + off_t fNumBlocks; + off_t fSize; +}; + +#endif // EXTENTSTREAM_H + diff --git a/src/add-ons/kernel/file_systems/ext2/HTree.cpp b/src/add-ons/kernel/file_systems/ext2/HTree.cpp index 842b8471b3..e4fe207069 100644 --- a/src/add-ons/kernel/file_systems/ext2/HTree.cpp +++ b/src/add-ons/kernel/file_systems/ext2/HTree.cpp @@ -84,7 +84,7 @@ HTree::~HTree() status_t HTree::PrepareForHash() { - off_t blockNum; + fsblock_t blockNum; status_t status = fDirectory->FindBlock(0, blockNum); if (status != B_OK) return status; @@ -115,7 +115,7 @@ HTree::Lookup(const char* name, DirectoryIterator** iterator) return _FallbackToLinearIteration(iterator); } - off_t blockNum; + fsblock_t blockNum; status_t status = fDirectory->FindBlock(0, blockNum); if (status != B_OK) return _FallbackToLinearIteration(iterator); diff --git a/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.cpp b/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.cpp index a7d47c8bbf..09299f9246 100644 --- a/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.cpp +++ b/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.cpp @@ -189,7 +189,7 @@ HTreeEntryIterator::Lookup(uint32 hash, int indirections, TRACE("HTreeEntryIterator::Lookup(): Creating a HTree entry iterator " "starting at block: %lu, hash: 0x%lX\n", start->Block(), start->Hash()); - off_t blockNum; + fsblock_t blockNum; status_t status = fDirectory->FindBlock(start->Block() * fBlockSize, blockNum); if (status != B_OK) @@ -322,7 +322,7 @@ HTreeEntryIterator::InsertEntry(Transaction& transaction, uint32 hash, panic("Splitting a HTree node required, but isn't yet fully " "supported\n"); - off_t physicalBlock; + fsblock_t physicalBlock; status_t status = fDirectory->FindBlock(newBlocksPos, physicalBlock); if (status != B_OK) return status; diff --git a/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.h b/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.h index d41d598c64..73a432eec2 100644 --- a/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.h +++ b/src/add-ons/kernel/file_systems/ext2/HTreeEntryIterator.h @@ -53,7 +53,7 @@ private: uint16 fCurrentEntry; uint32 fBlockSize; - off_t fBlockNum; + fsblock_t fBlockNum; HTreeEntryIterator* fParent; HTreeEntryIterator* fChild; diff --git a/src/add-ons/kernel/file_systems/ext2/IndexedDirectoryIterator.cpp b/src/add-ons/kernel/file_systems/ext2/IndexedDirectoryIterator.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/add-ons/kernel/file_systems/ext2/IndexedDirectoryIterator.h b/src/add-ons/kernel/file_systems/ext2/IndexedDirectoryIterator.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/add-ons/kernel/file_systems/ext2/Inode.cpp b/src/add-ons/kernel/file_systems/ext2/Inode.cpp index ebe03262b3..9ef558c20f 100644 --- a/src/add-ons/kernel/file_systems/ext2/Inode.cpp +++ b/src/add-ons/kernel/file_systems/ext2/Inode.cpp @@ -13,6 +13,7 @@ #include "CachedBlock.h" #include "DataStream.h" #include "DirectoryIterator.h" +#include "ExtentStream.h" #include "HTree.h" #include "Utility.h" @@ -224,8 +225,12 @@ Inode::CheckPermissions(int accessMode) const status_t -Inode::FindBlock(off_t offset, off_t& block, uint32 *_count) +Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count) { + if (Flags() & EXT2_INODE_EXTENTS) { + ExtentStream stream(fVolume, &fNode.extent_stream, Size()); + return stream.FindBlock(offset, block, _count); + } DataStream stream(fVolume, &fNode.stream, Size()); return stream.FindBlock(offset, block, _count); } @@ -407,9 +412,14 @@ Inode::InitDirectory(Transaction& transaction, Inode* parent) if (status != B_OK) return status; - off_t blockNum; - DataStream stream(fVolume, &fNode.stream, Size()); - status = stream.FindBlock(0, blockNum); + fsblock_t blockNum; + if (Flags() & EXT2_INODE_EXTENTS) { + ExtentStream stream(fVolume, &fNode.extent_stream, Size()); + status = stream.FindBlock(0, blockNum); + } else { + DataStream stream(fVolume, &fNode.stream, Size()); + status = stream.FindBlock(0, blockNum); + } if (status != B_OK) return status; @@ -613,6 +623,14 @@ Inode::Create(Transaction& transaction, Inode* parent, const char* name, inode->SetAccessTime(×pec); inode->SetCreationTime(×pec); inode->SetModificationTime(×pec); + node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED); + if (volume->HasExtentsFeature() + && (inode->IsDirectory() || inode->IsFile())) { + node.SetFlag(EXT2_INODE_EXTENTS); + ExtentStream stream(volume, &node.extent_stream, 0); + stream.Init(); + ASSERT(stream.Check()); + } if (sizeof(ext2_inode) < volume->InodeSize()) node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); @@ -803,9 +821,14 @@ Inode::_EnlargeDataStream(Transaction& transaction, off_t size) } off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; - DataStream stream(fVolume, &fNode.stream, oldSize); - stream.Enlarge(transaction, end); - + if (Flags() & EXT2_INODE_EXTENTS) { + ExtentStream stream(fVolume, &fNode.extent_stream, Size()); + stream.Enlarge(transaction, end); + ASSERT(stream.Check()); + } else { + DataStream stream(fVolume, &fNode.stream, oldSize); + stream.Enlarge(transaction, end); + } TRACE("Inode::_EnlargeDataStream(): Setting size to %lld\n", size); fNode.SetSize(size); TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %llu\n", @@ -837,8 +860,14 @@ Inode::_ShrinkDataStream(Transaction& transaction, off_t size) } off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; - DataStream stream(fVolume, &fNode.stream, oldSize); - stream.Shrink(transaction, end); + if (Flags() & EXT2_INODE_EXTENTS) { + ExtentStream stream(fVolume, &fNode.extent_stream, Size()); + stream.Shrink(transaction, end); + ASSERT(stream.Check()); + } else { + DataStream stream(fVolume, &fNode.stream, oldSize); + stream.Shrink(transaction, end); + } fNode.SetSize(size); return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512)); diff --git a/src/add-ons/kernel/file_systems/ext2/Inode.h b/src/add-ons/kernel/file_systems/ext2/Inode.h index 0655ef821c..393d29b690 100644 --- a/src/add-ons/kernel/file_systems/ext2/Inode.h +++ b/src/add-ons/kernel/file_systems/ext2/Inode.h @@ -79,7 +79,7 @@ public: //::Volume* _Volume() const { return fVolume; } Volume* GetVolume() const { return fVolume; } - status_t FindBlock(off_t offset, off_t& block, + status_t FindBlock(off_t offset, fsblock_t& block, uint32 *_count = NULL); status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); status_t WriteAt(Transaction& transaction, off_t pos, diff --git a/src/add-ons/kernel/file_systems/ext2/InodeAllocator.cpp b/src/add-ons/kernel/file_systems/ext2/InodeAllocator.cpp index e0f5d5be5f..0a3caabfa0 100644 --- a/src/add-ons/kernel/file_systems/ext2/InodeAllocator.cpp +++ b/src/add-ons/kernel/file_systems/ext2/InodeAllocator.cpp @@ -156,7 +156,7 @@ InodeAllocator::_Allocate(Transaction& transaction, uint32 preferredBlockGroup, status_t -InodeAllocator::_MarkInBitmap(Transaction& transaction, off_t bitmapBlock, +InodeAllocator::_MarkInBitmap(Transaction& transaction, fsblock_t bitmapBlock, uint32 blockGroup, uint32 numInodes, ino_t& id) { BitmapBlock inodeBitmap(fVolume, numInodes); @@ -190,7 +190,7 @@ InodeAllocator::_MarkInBitmap(Transaction& transaction, off_t bitmapBlock, status_t -InodeAllocator::_UnmarkInBitmap(Transaction& transaction, off_t bitmapBlock, +InodeAllocator::_UnmarkInBitmap(Transaction& transaction, fsblock_t bitmapBlock, uint32 numInodes, ino_t id) { BitmapBlock inodeBitmap(fVolume, numInodes); diff --git a/src/add-ons/kernel/file_systems/ext2/InodeAllocator.h b/src/add-ons/kernel/file_systems/ext2/InodeAllocator.h index e50e6f6a25..a608346594 100644 --- a/src/add-ons/kernel/file_systems/ext2/InodeAllocator.h +++ b/src/add-ons/kernel/file_systems/ext2/InodeAllocator.h @@ -10,6 +10,7 @@ #include +#include "ext2.h" #include "Transaction.h" @@ -29,13 +30,13 @@ public: private: status_t _Allocate(Transaction& transaction, - uint32 prefferedBlockGroup, bool isDirectory, + uint32 preferredBlockGroup, bool isDirectory, ino_t& id); status_t _MarkInBitmap(Transaction& transaction, - off_t bitmapBlock, uint32 blockGroup, + fsblock_t bitmapBlock, uint32 blockGroup, uint32 numInodes, ino_t& id); status_t _UnmarkInBitmap(Transaction& transaction, - off_t bitmapBlock, uint32 numInodes, ino_t id); + fsblock_t bitmapBlock, uint32 numInodes, ino_t id); Volume* fVolume; diff --git a/src/add-ons/kernel/file_systems/ext2/InodeJournal.cpp b/src/add-ons/kernel/file_systems/ext2/InodeJournal.cpp index b919ee6c06..8e7b76915f 100644 --- a/src/add-ons/kernel/file_systems/ext2/InodeJournal.cpp +++ b/src/add-ons/kernel/file_systems/ext2/InodeJournal.cpp @@ -84,7 +84,7 @@ InodeJournal::InitCheck() status_t -InodeJournal::MapBlock(off_t logical, off_t& physical) +InodeJournal::MapBlock(off_t logical, fsblock_t& physical) { TRACE("InodeJournal::MapBlock()\n"); return fInode->FindBlock(logical * fBlockSize, physical); diff --git a/src/add-ons/kernel/file_systems/ext2/InodeJournal.h b/src/add-ons/kernel/file_systems/ext2/InodeJournal.h index 2486f78ef3..b75d08d11e 100644 --- a/src/add-ons/kernel/file_systems/ext2/InodeJournal.h +++ b/src/add-ons/kernel/file_systems/ext2/InodeJournal.h @@ -20,7 +20,7 @@ public: status_t InitCheck(); - status_t MapBlock(off_t logical, off_t& physical); + status_t MapBlock(off_t logical, fsblock_t& physical); private: Inode* fInode; status_t fInitStatus; diff --git a/src/add-ons/kernel/file_systems/ext2/Jamfile b/src/add-ons/kernel/file_systems/ext2/Jamfile index ab8b5ee6ae..ae320f0a77 100644 --- a/src/add-ons/kernel/file_systems/ext2/Jamfile +++ b/src/add-ons/kernel/file_systems/ext2/Jamfile @@ -11,6 +11,7 @@ KernelAddon ext2 : Inode.cpp Attribute.cpp DirectoryIterator.cpp + ExtentStream.cpp HTree.cpp HTreeEntryIterator.cpp RevokeManager.cpp diff --git a/src/add-ons/kernel/file_systems/ext2/Journal.cpp b/src/add-ons/kernel/file_systems/ext2/Journal.cpp index 0f9984d4f7..4858a07c23 100644 --- a/src/add-ons/kernel/file_systems/ext2/Journal.cpp +++ b/src/add-ons/kernel/file_systems/ext2/Journal.cpp @@ -304,7 +304,7 @@ Journal::Unlock(Transaction* owner, bool success) status_t -Journal::MapBlock(off_t logical, off_t& physical) +Journal::MapBlock(off_t logical, fsblock_t& physical) { TRACE("Journal::MapBlock()\n"); physical = logical; @@ -402,7 +402,7 @@ Journal::_WritePartialTransactionToLog(JournalHeader* descriptorBlock, logBlock = _WrapAroundLog(logBlock + 1); - off_t physicalBlock; + fsblock_t physicalBlock; status = MapBlock(logBlock, physicalBlock); if (status != B_OK) return status; @@ -434,7 +434,7 @@ Journal::_WritePartialTransactionToLog(JournalHeader* descriptorBlock, --tag; tag->SetLastTagFlag(); - off_t physicalBlock; + fsblock_t physicalBlock; status = MapBlock(descriptorBlockPos, physicalBlock); if (status != B_OK) return status; @@ -579,7 +579,7 @@ Journal::_WriteTransactionToLog() // written. When it recovery reaches where the first commit should be // and doesn't find it, it considers it found the end of the log. - off_t physicalBlock; + fsblock_t physicalBlock; status = MapBlock(logBlock, physicalBlock); if (status != B_OK) return status; @@ -602,7 +602,7 @@ Journal::_WriteTransactionToLog() } // Transaction will enter the Commit state - off_t physicalBlock; + fsblock_t physicalBlock; status = MapBlock(commitBlockPos, physicalBlock); if (status != B_OK) return status; @@ -661,7 +661,7 @@ status_t Journal::_SaveSuperBlock() { TRACE("Journal::_SaveSuperBlock()\n"); - off_t physicalBlock; + fsblock_t physicalBlock; status_t status = MapBlock(0, physicalBlock); if (status != B_OK) return status; @@ -695,7 +695,7 @@ status_t Journal::_LoadSuperBlock() { TRACE("Journal::_LoadSuperBlock()\n"); - off_t superblockPos; + fsblock_t superblockPos; status_t status = MapBlock(0, superblockPos); if (status != B_OK) @@ -842,7 +842,7 @@ Journal::_RecoverPassScan(uint32& lastCommitID) JournalHeader* header; uint32 nextCommitID = fFirstCommitID; uint32 nextBlock = fLogStart; - off_t nextBlockPos; + fsblock_t nextBlockPos; status_t status = MapBlock(nextBlock, nextBlockPos); if (status != B_OK) @@ -898,7 +898,7 @@ Journal::_RecoverPassRevoke(uint32 lastCommitID) JournalHeader* header; uint32 nextCommitID = fFirstCommitID; uint32 nextBlock = fLogStart; - off_t nextBlockPos; + fsblock_t nextBlockPos; status_t status = MapBlock(nextBlock, nextBlockPos); if (status != B_OK) @@ -960,7 +960,7 @@ Journal::_RecoverPassReplay(uint32 lastCommitID) uint32 nextCommitID = fFirstCommitID; uint32 nextBlock = fLogStart; - off_t nextBlockPos; + fsblock_t nextBlockPos; status_t status = MapBlock(nextBlock, nextBlockPos); if (status != B_OK) diff --git a/src/add-ons/kernel/file_systems/ext2/Journal.h b/src/add-ons/kernel/file_systems/ext2/Journal.h index d95d489661..4ebbb51a71 100644 --- a/src/add-ons/kernel/file_systems/ext2/Journal.h +++ b/src/add-ons/kernel/file_systems/ext2/Journal.h @@ -173,7 +173,7 @@ public: bool separateSubTransactions); virtual status_t Unlock(Transaction* owner, bool success); - virtual status_t MapBlock(off_t logical, off_t& physical); + virtual status_t MapBlock(off_t logical, fsblock_t& physical); inline uint32 FreeLogBlocks() const; status_t FlushLogAndBlocks(); diff --git a/src/add-ons/kernel/file_systems/ext2/Volume.cpp b/src/add-ons/kernel/file_systems/ext2/Volume.cpp index 54519e1aaa..febc435610 100644 --- a/src/add-ons/kernel/file_systems/ext2/Volume.cpp +++ b/src/add-ons/kernel/file_systems/ext2/Volume.cpp @@ -279,8 +279,10 @@ Volume::Mount(const char* deviceName, uint32 flags) DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0 ? O_RDONLY : O_RDWR); fDevice = opener.Device(); - if (fDevice < B_OK) + if (fDevice < B_OK) { + FATAL("Volume::Mount(): couldn't open device\n"); return fDevice; + } if (opener.IsReadOnly()) fFlags |= VOLUME_READ_ONLY; @@ -291,8 +293,10 @@ Volume::Mount(const char* deviceName, uint32 flags) // read the super block status_t status = Identify(fDevice, &fSuperBlock); - if (status != B_OK) + if (status != B_OK) { + FATAL("Volume::Mount(): Identify() failed\n"); return status; + } // check read-only features if mounting read-write if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0) @@ -300,7 +304,9 @@ Volume::Mount(const char* deviceName, uint32 flags) // initialize short hands to the super block (to save byte swapping) fBlockShift = fSuperBlock.BlockShift(); - fBlockSize = 1UL << fSuperBlock.BlockShift(); + if (fBlockShift < 10 || fBlockShift > 16) + return B_ERROR; + fBlockSize = 1UL << fBlockShift; fFirstDataBlock = fSuperBlock.FirstDataBlock(); fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature()); @@ -338,7 +344,7 @@ Volume::Mount(const char* deviceName, uint32 flags) status = opener.GetSize(&diskSize); if (status != B_OK) return status; - if (diskSize < (NumBlocks() << BlockShift())) + if (diskSize < ((off_t)NumBlocks() << BlockShift())) return B_BAD_VALUE; fBlockCache = opener.InitCache(NumBlocks(), fBlockSize); @@ -501,6 +507,7 @@ Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock) uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE | EXT2_INCOMPATIBLE_FEATURE_RECOVER | EXT2_INCOMPATIBLE_FEATURE_JOURNAL + | EXT2_INCOMPATIBLE_FEATURE_EXTENTS; /*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/; uint32 unsupported = superBlock.IncompatibleFeatures() & ~supportedIncompatible; @@ -742,7 +749,7 @@ Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory) status_t Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, - uint32& blockGroup, off_t& start, uint32& length) + uint32& blockGroup, fsblock_t& start, uint32& length) { TRACE("Volume::AllocateBlocks()\n"); if (IsReadOnly()) @@ -764,7 +771,7 @@ Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, status_t -Volume::FreeBlocks(Transaction& transaction, off_t start, uint32 length) +Volume::FreeBlocks(Transaction& transaction, fsblock_t start, uint32 length) { TRACE("Volume::FreeBlocks(%llu, %lu)\n", start, length); if (IsReadOnly()) diff --git a/src/add-ons/kernel/file_systems/ext2/Volume.h b/src/add-ons/kernel/file_systems/ext2/Volume.h index eb28f31fb1..3d00e4df41 100644 --- a/src/add-ons/kernel/file_systems/ext2/Volume.h +++ b/src/add-ons/kernel/file_systems/ext2/Volume.h @@ -48,7 +48,7 @@ public: { return fNumInodes; } uint32 NumGroups() const { return fNumGroups; } - off_t NumBlocks() const + fsblock_t NumBlocks() const { return fSuperBlock.NumBlocks( Has64bitFeature()); } off_t NumFreeBlocks() const @@ -81,6 +81,9 @@ public: bool Has64bitFeature() const { return (fSuperBlock.CompatibleFeatures() & EXT2_INCOMPATIBLE_FEATURE_64BIT) != 0; } + bool HasExtentsFeature() const + { return (fSuperBlock.IncompatibleFeatures() + & EXT2_INCOMPATIBLE_FEATURE_EXTENTS) != 0; } uint8 DefaultHashVersion() const { return fSuperBlock.default_hash_version; } bool HugeFiles() const @@ -100,10 +103,10 @@ public: status_t AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum, - uint32& blockGroup, off_t& start, + uint32& blockGroup, fsblock_t& start, uint32& length); status_t FreeBlocks(Transaction& transaction, - off_t start, uint32 length); + fsblock_t start, uint32 length); status_t LoadSuperBlock(); status_t WriteSuperBlock(Transaction& transaction); diff --git a/src/add-ons/kernel/file_systems/ext2/ext2.h b/src/add-ons/kernel/file_systems/ext2/ext2.h index 24887581df..b489446c45 100644 --- a/src/add-ons/kernel/file_systems/ext2/ext2.h +++ b/src/add-ons/kernel/file_systems/ext2/ext2.h @@ -15,6 +15,11 @@ //#define TRACE_EXT2 + +typedef uint64 fileblock_t; // file block number +typedef uint64 fsblock_t; // filesystem block number + + #define EXT2_SUPER_BLOCK_OFFSET 1024 struct ext2_super_block { @@ -213,7 +218,7 @@ struct ext2_block_group { uint16 unused_inodes_high; uint32 _reserved2[3]; - uint64 BlockBitmap(bool has64bits) const + fsblock_t BlockBitmap(bool has64bits) const { uint64 block = B_LENDIAN_TO_HOST_INT32(block_bitmap); if (has64bits) @@ -221,7 +226,7 @@ struct ext2_block_group { ((uint64)B_LENDIAN_TO_HOST_INT32(block_bitmap_high) << 32); return block; } - uint64 InodeBitmap(bool has64bits) const + fsblock_t InodeBitmap(bool has64bits) const { uint64 bitmap = B_LENDIAN_TO_HOST_INT32(inode_bitmap); if (has64bits) @@ -296,6 +301,76 @@ struct ext2_data_stream { uint32 triple_indirect; } _PACKED; +#define EXT2_EXTENT_MAGIC 0xf30a + +struct ext2_extent_header { + uint16 magic; + uint16 num_entries; + uint16 max_entries; + uint16 depth; + uint32 generation; + bool IsValid() const + { + return B_LENDIAN_TO_HOST_INT16(magic) == EXT2_EXTENT_MAGIC; + } + uint16 NumEntries() const { return B_LENDIAN_TO_HOST_INT16(num_entries); } + uint16 MaxEntries() const { return B_LENDIAN_TO_HOST_INT16(max_entries); } + uint16 Depth() const { return B_LENDIAN_TO_HOST_INT16(depth); } + uint32 Generation() const { return B_LENDIAN_TO_HOST_INT32(generation); } + void SetNumEntries(uint16 num) + { num_entries = B_HOST_TO_LENDIAN_INT16(num); } + void SetMaxEntries(uint16 max) + { max_entries = B_HOST_TO_LENDIAN_INT16(max); } + void SetDepth(uint16 _depth) + { depth = B_HOST_TO_LENDIAN_INT16(_depth); } + void SetGeneration(uint32 _generation) + { generation = B_HOST_TO_LENDIAN_INT32(_generation); } +} _PACKED; + +struct ext2_extent_index { + uint32 logical_block; + uint32 physical_block; + uint16 physical_block_high; + uint16 _reserved; + uint32 LogicalBlock() const + { return B_LENDIAN_TO_HOST_INT32(logical_block); } + uint64 PhysicalBlock() const { return B_LENDIAN_TO_HOST_INT32(physical_block) + | ((uint64)B_LENDIAN_TO_HOST_INT16(physical_block_high) << 32); } + void SetLogicalBlock(uint32 block) { + logical_block = B_HOST_TO_LENDIAN_INT32(block); } + void SetPhysicalBlock(uint64 block) { + physical_block = B_HOST_TO_LENDIAN_INT32(block & 0xffffffff); + physical_block_high = B_HOST_TO_LENDIAN_INT16((block >> 32) & 0xffff); } +} _PACKED; + +struct ext2_extent_entry { + uint32 logical_block; + uint16 length; + uint16 physical_block_high; + uint32 physical_block; + uint32 LogicalBlock() const + { return B_LENDIAN_TO_HOST_INT32(logical_block); } + uint16 Length() const { return B_LENDIAN_TO_HOST_INT16(length) == 0x8000 + ? 0x8000 : B_LENDIAN_TO_HOST_INT16(length) & 0x7fff; } + uint64 PhysicalBlock() const { return B_LENDIAN_TO_HOST_INT32(physical_block) + | ((uint64)B_LENDIAN_TO_HOST_INT16(physical_block_high) << 32); } + void SetLogicalBlock(uint32 block) { + logical_block = B_HOST_TO_LENDIAN_INT32(block); } + void SetLength(uint16 _length) { + length = B_HOST_TO_LENDIAN_INT16(_length) & 0x7fff; } + void SetPhysicalBlock(uint64 block) { + physical_block = B_HOST_TO_LENDIAN_INT32(block & 0xffffffff); + physical_block_high = B_HOST_TO_LENDIAN_INT16((block >> 32) & 0xffff); } +} _PACKED; + +struct ext2_extent_stream { + ext2_extent_header extent_header; + union { + ext2_extent_entry extent_entries[4]; + ext2_extent_index extent_index[4]; + }; +} _PACKED; + #define EXT2_INODE_NORMAL_SIZE 128 struct ext2_inode { @@ -314,6 +389,7 @@ struct ext2_inode { union { ext2_data_stream stream; char symlink[EXT2_SHORT_SYMLINK_LENGTH]; + ext2_extent_stream extent_stream; }; uint32 generation; uint32 file_access_control; @@ -546,9 +622,18 @@ struct ext2_inode { #define EXT2_INODE_COMPRESSION_ERROR 0x00000800 #define EXT2_INODE_BTREE 0x00001000 #define EXT2_INODE_INDEXED 0x00001000 +#define EXT2_INODE_JOURNAL_DATA 0x00004000 +#define EXT2_INODE_NO_MERGE_TAIL 0x00008000 +#define EXT2_INODE_DIR_SYNCH 0x00010000 #define EXT2_INODE_HUGE_FILE 0x00040000 #define EXT2_INODE_EXTENTS 0x00080000 +#define EXT2_INODE_INHERITED (EXT2_INODE_SECURE_DELETION | EXT2_INODE_UNDELETE \ + | EXT2_INODE_COMPRESSED | EXT2_INODE_SYNCHRONOUS | EXT2_INODE_IMMUTABLE \ + | EXT2_INODE_APPEND_ONLY | EXT2_INODE_NO_DUMP | EXT2_INODE_NO_ACCESS_TIME \ + | EXT2_INODE_DO_NOT_COMPRESS | EXT2_INODE_JOURNAL_DATA \ + | EXT2_INODE_NO_MERGE_TAIL | EXT2_INODE_DIR_SYNCH) + #define EXT2_NAME_LENGTH 255 struct ext2_dir_entry { @@ -680,6 +765,7 @@ struct file_cookie { int open_mode; }; + #define EXT2_OPEN_MODE_USER_MASK 0x7fffffff #define INODE_NOTIFICATION_INTERVAL 10000000LL diff --git a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp index f3c31a959b..8dff14128e 100644 --- a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp @@ -407,12 +407,17 @@ ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, size_t index = 0, max = *_count; while (true) { - off_t block; + fsblock_t block; uint32 count = 1; status_t status = inode->FindBlock(offset, block, &count); if (status != B_OK) return status; + if (block > volume->NumBlocks()) { + panic("ext2_get_file_map() found block %lld for offset %lld\n", + block, offset); + } + off_t blockOffset = block << volume->BlockShift(); uint32 blockLength = volume->BlockSize() * count; @@ -506,7 +511,7 @@ ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, uint32 blocksPerGroup = volume->BlocksPerGroup(); uint32 blockSize = volume->BlockSize(); uint32 firstBlock = volume->FirstDataBlock(); - off_t start = 0; + fsblock_t start = 0; uint32 group = 0; uint32 length; @@ -955,7 +960,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, if (status != B_OK) return B_IO_ERROR; - off_t blockNum; + fsblock_t blockNum; status = parent->FindBlock(0, blockNum); if (status != B_OK) return status;