From ee77e6d68f1da4fbdbe2fbb098049c58472dac9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 12 Aug 2002 16:29:46 +0000 Subject: [PATCH] Implemented uncached access for files (untested though). Uses a template class to provide generic access to the file. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@715 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/file_systems/bfs/Inode.cpp | 370 +---------- src/add-ons/kernel/file_systems/bfs/Stream.h | 619 ++++++++++++++++++ 2 files changed, 640 insertions(+), 349 deletions(-) create mode 100644 src/add-ons/kernel/file_systems/bfs/Stream.h diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index dc6212f639..d26dd4f414 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -9,6 +9,7 @@ #include "cpp.h" #include "Inode.h" #include "BPlusTree.h" +#include "Stream.h" #include "Index.h" #include @@ -69,7 +70,8 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **i } -void InodeAllocator::Keep() +void +InodeAllocator::Keep() { fTransaction = NULL; fInode = NULL; @@ -850,375 +852,45 @@ Inode::IsEmpty() * If successful, "offset" will then be set to the file offset * of the block_run returned; so "pos - offset" is for the block_run * what "pos" is for the whole stream. + * The caller has to make sure that "pos" is inside the stream. */ -status_t -Inode::FindBlockRun(off_t pos,block_run &run,off_t &offset) +status_t +Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset) { - data_stream *data = &Node()->data; - - // Inode::ReadAt() does already does this - //if (pos > data->size) - // return B_ENTRY_NOT_FOUND; - - // find matching block run - - if (data->max_direct_range > 0 && pos >= data->max_direct_range) { - if (data->max_double_indirect_range > 0 && pos >= data->max_indirect_range) { - // access to double indirect blocks - - CachedBlock cached(fVolume); - - off_t start = pos - data->max_indirect_range; - int32 indirectSize = (16 << fVolume->BlockShift()) * (fVolume->BlockSize() / sizeof(block_run)); - int32 directSize = 4 << fVolume->BlockShift(); - int32 index = start / indirectSize; - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - - block_run *indirect = (block_run *)cached.SetTo( - fVolume->ToBlock(data->double_indirect) + index / runsPerBlock); - if (indirect == NULL) - RETURN_ERROR(B_ERROR); - - //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); - //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); - - int32 current = (start % indirectSize) / directSize; - - indirect = (block_run *)cached.SetTo( - fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); - if (indirect == NULL) - RETURN_ERROR(B_ERROR); - - run = indirect[current % runsPerBlock]; - offset = data->max_indirect_range + (index * indirectSize) + (current * directSize); - //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); - } else { - // access to indirect blocks - - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - off_t runBlockEnd = data->max_direct_range; - - CachedBlock cached(fVolume); - off_t block = fVolume->ToBlock(data->indirect); - - for (int32 i = 0;i < data->indirect.length;i++) { - block_run *indirect = (block_run *)cached.SetTo(block + i); - if (indirect == NULL) - RETURN_ERROR(B_IO_ERROR); - - int32 current = -1; - while (++current < runsPerBlock) { - if (indirect[current].IsZero()) - break; - - runBlockEnd += indirect[current].length << fVolume->BlockShift(); - if (runBlockEnd > pos) { - run = indirect[current]; - offset = runBlockEnd - (run.length << fVolume->BlockShift()); - //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); - //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); - return fVolume->IsValidBlockRun(run); - } - } - } - RETURN_ERROR(B_ERROR); - } - } else { - // access from direct blocks - - off_t runBlockEnd = 0LL; - int32 current = -1; - - while (++current < NUM_DIRECT_BLOCKS) { - if (data->direct[current].IsZero()) - break; - - runBlockEnd += data->direct[current].length << fVolume->BlockShift(); - if (runBlockEnd > pos) { - run = data->direct[current]; - offset = runBlockEnd - (run.length << fVolume->BlockShift()); - //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); - return fVolume->IsValidBlockRun(run); - } - } - //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos)); - return B_ENTRY_NOT_FOUND; - } - return fVolume->IsValidBlockRun(run); + // The BPlusTree class will call this function, we'll provide + // standard cached access only from here + return ((Stream *)this)->FindBlockRun(pos, run, offset); } status_t Inode::ReadAt(off_t pos, uint8 *buffer, size_t *_length) { - // set/check boundaries for pos/length + // call the right ReadAt() method, depending on the inode flags - if (pos < 0) - pos = 0; - else if (pos >= Node()->data.size) { - *_length = 0; - return B_NO_ERROR; - } + if (Flags() & INODE_NO_CACHE) + return ((Stream *)this)->ReadAt(pos, buffer, _length); - size_t length = *_length; + if (Flags() & INODE_LOGGED) + return ((Stream *)this)->ReadAt(pos, buffer, _length); - if (pos + length > Node()->data.size) - length = Node()->data.size - pos; - - block_run run; - off_t offset; - if (FindBlockRun(pos,run,offset) < B_OK) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - uint32 bytesRead = 0; - uint32 blockSize = fVolume->BlockSize(); - uint32 blockShift = fVolume->BlockShift(); - uint8 *block; - - // the first block_run we read could not be aligned to the block_size boundary - // (read partial block at the beginning) - - // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 - if (pos % blockSize != 0) { - run.start += (pos - offset) / blockSize; - run.length -= (pos - offset) / blockSize; - - CachedBlock cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bytesRead = blockSize - (pos % blockSize); - if (length < bytesRead) - bytesRead = length; - - memcpy(buffer,block + (pos % blockSize),bytesRead); - pos += bytesRead; - - length -= bytesRead; - if (length == 0) { - *_length = bytesRead; - return B_OK; - } - - if (FindBlockRun(pos,run,offset) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - } - - // the first block_run is already filled in at this point - // read the following complete blocks using cached_read(), - // the last partial block is read using the CachedBlock class - - bool partial = false; - - while (length > 0) { - // offset is the offset to the current pos in the block_run - run.start += (pos - offset) >> blockShift; - run.length -= (pos - offset) >> blockShift; - - if ((run.length << blockShift) > length) { - if (length < blockSize) { - CachedBlock cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - memcpy(buffer + bytesRead,block,length); - bytesRead += length; - break; - } - run.length = length >> blockShift; - partial = true; - } - - if (cached_read(fVolume->Device(),fVolume->ToBlock(run),buffer + bytesRead, - run.length,blockSize) != B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - - int32 bytes = run.length << blockShift; - length -= bytes; - bytesRead += bytes; - if (length == 0) - break; - - pos += bytes; - - if (partial) { - // if the last block was read only partially, point block_run - // to the remaining part - run.start += run.length; - run.length = 1; - offset = pos; - } else if (FindBlockRun(pos,run,offset) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - } - - *_length = bytesRead; - return B_NO_ERROR; + return ((Stream *)this)->ReadAt(pos, buffer, _length); } status_t Inode::WriteAt(Transaction *transaction,off_t pos,const uint8 *buffer,size_t *_length) { - size_t length = *_length; + // call the right WriteAt() method, depending on the inode flags - // set/check boundaries for pos/length - if (pos < 0) - pos = 0; - else if (pos + length > Node()->data.size) { - off_t oldSize = Size(); + if (Flags() & INODE_NO_CACHE) + return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); - // the transaction doesn't have to be started already - if ((Flags() & INODE_NO_TRANSACTION) == 0) - transaction->Start(fVolume,BlockNumber()); + if (Flags() & INODE_LOGGED) + return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); - // let's grow the data stream to the size needed - status_t status = SetFileSize(transaction,pos + length); - if (status < B_OK) { - *_length = 0; - RETURN_ERROR(status); - } - // If the position of the write was beyond the file size, we - // have to fill the gap between that position and the old file - // size with zeros. - FillGapWithZeros(oldSize,pos); - } - - block_run run; - off_t offset; - if (FindBlockRun(pos,run,offset) < B_OK) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bool logStream = (Flags() & INODE_LOGGED) == INODE_LOGGED; - if (logStream) - transaction->Start(fVolume,BlockNumber()); - - uint32 bytesWritten = 0; - uint32 blockSize = fVolume->BlockSize(); - uint32 blockShift = fVolume->BlockShift(); - uint8 *block; - - // the first block_run we write could not be aligned to the block_size boundary - // (write partial block at the beginning) - - // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 - if (pos % blockSize != 0) { - run.start += (pos - offset) / blockSize; - run.length -= (pos - offset) / blockSize; - - CachedBlock cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bytesWritten = blockSize - (pos % blockSize); - if (length < bytesWritten) - bytesWritten = length; - - memcpy(block + (pos % blockSize),buffer,bytesWritten); - - // either log the stream or write it directly to disk - if (logStream) - cached.WriteBack(transaction); - else - fVolume->WriteBlocks(cached.BlockNumber(),block,1); - - pos += bytesWritten; - - length -= bytesWritten; - if (length == 0) { - *_length = bytesWritten; - return B_OK; - } - - if (FindBlockRun(pos,run,offset) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - } - - // the first block_run is already filled in at this point - // write the following complete blocks using Volume::WriteBlocks(), - // the last partial block is written using the CachedBlock class - - bool partial = false; - - while (length > 0) { - // offset is the offset to the current pos in the block_run - run.start += (pos - offset) >> blockShift; - run.length -= (pos - offset) >> blockShift; - - if ((run.length << blockShift) > length) { - if (length < blockSize) { - CachedBlock cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - memcpy(block,buffer + bytesWritten,length); - - if (logStream) - cached.WriteBack(transaction); - else - fVolume->WriteBlocks(cached.BlockNumber(),block,1); - - bytesWritten += length; - break; - } - run.length = length >> blockShift; - partial = true; - } - - status_t status; - if (logStream) { - status = transaction->WriteBlocks(fVolume->ToBlock(run), - buffer + bytesWritten,run.length); - } else { - status = fVolume->WriteBlocks(fVolume->ToBlock(run), - buffer + bytesWritten,run.length); - } - if (status != B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - - int32 bytes = run.length << blockShift; - length -= bytes; - bytesWritten += bytes; - if (length == 0) - break; - - pos += bytes; - - if (partial) { - // if the last block was written only partially, point block_run - // to the remaining part - run.start += run.length; - run.length = 1; - offset = pos; - } else if (FindBlockRun(pos,run,offset) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - } - - *_length = bytesWritten; - - return B_NO_ERROR; + return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); } diff --git a/src/add-ons/kernel/file_systems/bfs/Stream.h b/src/add-ons/kernel/file_systems/bfs/Stream.h new file mode 100644 index 0000000000..f4fa873973 --- /dev/null +++ b/src/add-ons/kernel/file_systems/bfs/Stream.h @@ -0,0 +1,619 @@ +/* Stream - inode stream access functions +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include + + +// The classes in the namespace "Access" provide different type of access +// to the inode's data stream. +// Uncached accesses the underlaying device directly, Cached uses the +// standard cache, while Logged directs write accesses through the log. +// +// The classes interface is similar to the one of the CachedBlock class, +// but adds two other (static) functions for reading/writing several +// blocks at once. +// We don't use a real pure virtual interface as the class base, but we +// provide the same mechanism using templates. + +namespace Access { + +class Uncached { + public: + Uncached(Volume *volume); + Uncached(Volume *volume,off_t block, bool empty = false); + Uncached(Volume *volume, block_run run, bool empty = false); + ~Uncached(); + + void Unset(); + uint8 *SetTo(off_t block, bool empty = false); + uint8 *SetTo(block_run run, bool empty = false); + status_t WriteBack(Transaction *transaction); + + uint8 *Block() const { return fBlock; } + off_t BlockNumber() const { return fBlockNumber; } + + static status_t Read(Volume *volume, block_run run, uint8 *buffer); + static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); + + protected: + Volume *fVolume; + off_t fBlockNumber; + uint8 *fBlock; +}; + +class Cached : public CachedBlock { + public: + Cached(Volume *volume); + Cached(Volume *volume,off_t block, bool empty = false); + Cached(Volume *volume, block_run run, bool empty = false); + + status_t WriteBack(Transaction *transaction); + static status_t Read(Volume *volume, block_run run, uint8 *buffer); + static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); +}; + +class Logged : public CachedBlock { + public: + Logged(Volume *volume); + Logged(Volume *volume,off_t block, bool empty = false); + Logged(Volume *volume, block_run run, bool empty = false); + + static status_t Read(Volume *volume, block_run run, uint8 *buffer); + static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); +}; + + +Uncached::Uncached(Volume *volume) + : + fVolume(volume), + fBlock(NULL) +{ +} + + +Uncached::Uncached(Volume *volume,off_t block, bool empty = false) + : + fVolume(volume), + fBlock(NULL) +{ + SetTo(block,empty); +} + + +Uncached::Uncached(Volume *volume,block_run run,bool empty = false) + : + fVolume(volume), + fBlock(NULL) +{ + SetTo(volume->ToBlock(run),empty); +} + + +Uncached::~Uncached() +{ + Unset(); +} + + +void +Uncached::Unset() +{ + if (fBlock != NULL) + fVolume->Pool().PutBuffer((void *)fBlock); +} + + +uint8 * +Uncached::SetTo(off_t block, bool empty = false) +{ + Unset(); + fBlockNumber = block; + if (fVolume->Pool().GetBuffer((void **)&fBlock) < B_OK) + return NULL; + + if (empty) + memset(fBlock, 0, fVolume->BlockSize()); + else + read_pos(fVolume->Device(), fBlockNumber << fVolume->BlockShift(), fBlock, fVolume->BlockSize()); + + return fBlock; +} + + +uint8 * +Uncached::SetTo(block_run run,bool empty = false) +{ + return SetTo(fVolume->ToBlock(run),empty); +} + + +status_t +Uncached::WriteBack(Transaction *transaction) +{ + if (fBlock == NULL) + RETURN_ERROR(B_BAD_VALUE); + + return write_pos(fVolume->Device(), fBlockNumber << fVolume->BlockShift(), fBlock, fVolume->BlockSize()); +} + + +status_t +Uncached::Read(Volume *volume, block_run run, uint8 *buffer) +{ + return read_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.length << volume->BlockShift()); +} + + +status_t +Uncached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) +{ + return write_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.length << volume->BlockShift()); +} + + +// #pragma mark - + + +Cached::Cached(Volume *volume) + : CachedBlock(volume) +{ +} + + +Cached::Cached(Volume *volume,off_t block,bool empty = false) + : CachedBlock(volume, block, empty) +{ +} + + +Cached::Cached(Volume *volume,block_run run,bool empty = false) + : CachedBlock(volume, run, empty) +{ +} + + +status_t +Cached::WriteBack(Transaction *transaction) +{ + if (transaction == NULL || fBlock == NULL) + RETURN_ERROR(B_BAD_VALUE); + + return fVolume->WriteBlocks(fBlockNumber, fBlock, 1); +} + + +status_t +Cached::Read(Volume *volume, block_run run, uint8 *buffer) +{ + return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.length, volume->BlockSize()); +} + + +status_t +Cached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) +{ + return volume->WriteBlocks(volume->ToBlock(run), buffer, run.length); +} + + +// #pragma mark - + + +Logged::Logged(Volume *volume) + : CachedBlock(volume) +{ +} + + +Logged::Logged(Volume *volume,off_t block,bool empty = false) + : CachedBlock(volume, block, empty) +{ +} + + +Logged::Logged(Volume *volume,block_run run,bool empty = false) + : CachedBlock(volume, run, empty) +{ +} + + +status_t +Logged::Read(Volume *volume, block_run run, uint8 *buffer) +{ + return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.length, volume->BlockSize()); +} + + +status_t +Logged::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) +{ + return transaction->WriteBlocks(volume->ToBlock(run), buffer, run.length); +} + +}; // namespace Access + + +// #pragma mark - + + +// The Stream template class allows to have only one straight-forward +// implementation of the FindBlockRun(), ReadAt(), and WriteAt() methods. +// They will access the disk through the given cache class only, which +// means either uncached, cached, or logged (see above). + +template +class Stream : public Inode { + public: + // The constructor only exists to make the compiler happy - it + // is never called in the code itself + Stream() : Inode(NULL, -1) {} + + status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); + status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); + status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length); + +}; + + +/** see Inode::FindBlockRun() for the documentation of this method */ + +template +status_t +Stream::FindBlockRun(off_t pos,block_run &run,off_t &offset) +{ + data_stream *data = &Node()->data; + + // find matching block run + + if (data->max_direct_range > 0 && pos >= data->max_direct_range) { + if (data->max_double_indirect_range > 0 && pos >= data->max_indirect_range) { + // access to double indirect blocks + + Cache cached(fVolume); + + off_t start = pos - data->max_indirect_range; + int32 indirectSize = (16 << fVolume->BlockShift()) * (fVolume->BlockSize() / sizeof(block_run)); + int32 directSize = 4 << fVolume->BlockShift(); + int32 index = start / indirectSize; + int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); + + block_run *indirect = (block_run *)cached.SetTo( + fVolume->ToBlock(data->double_indirect) + index / runsPerBlock); + if (indirect == NULL) + RETURN_ERROR(B_ERROR); + + //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); + //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); + + int32 current = (start % indirectSize) / directSize; + + indirect = (block_run *)cached.SetTo( + fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); + if (indirect == NULL) + RETURN_ERROR(B_ERROR); + + run = indirect[current % runsPerBlock]; + offset = data->max_indirect_range + (index * indirectSize) + (current * directSize); + //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); + } else { + // access to indirect blocks + + int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); + off_t runBlockEnd = data->max_direct_range; + + Cache cached(fVolume); + off_t block = fVolume->ToBlock(data->indirect); + + for (int32 i = 0;i < data->indirect.length;i++) { + block_run *indirect = (block_run *)cached.SetTo(block + i); + if (indirect == NULL) + RETURN_ERROR(B_IO_ERROR); + + int32 current = -1; + while (++current < runsPerBlock) { + if (indirect[current].IsZero()) + break; + + runBlockEnd += indirect[current].length << fVolume->BlockShift(); + if (runBlockEnd > pos) { + run = indirect[current]; + offset = runBlockEnd - (run.length << fVolume->BlockShift()); + //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); + //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); + return fVolume->IsValidBlockRun(run); + } + } + } + RETURN_ERROR(B_ERROR); + } + } else { + // access from direct blocks + + off_t runBlockEnd = 0LL; + int32 current = -1; + + while (++current < NUM_DIRECT_BLOCKS) { + if (data->direct[current].IsZero()) + break; + + runBlockEnd += data->direct[current].length << fVolume->BlockShift(); + if (runBlockEnd > pos) { + run = data->direct[current]; + offset = runBlockEnd - (run.length << fVolume->BlockShift()); + //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); + return fVolume->IsValidBlockRun(run); + } + } + //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos)); + return B_ENTRY_NOT_FOUND; + } + return fVolume->IsValidBlockRun(run); +} + + +template +status_t +Stream::ReadAt(off_t pos,uint8 *buffer,size_t *_length) +{ + // set/check boundaries for pos/length + + if (pos < 0) + pos = 0; + else if (pos >= Node()->data.size) { + *_length = 0; + return B_NO_ERROR; + } + + size_t length = *_length; + + if (pos + length > Node()->data.size) + length = Node()->data.size - pos; + + block_run run; + off_t offset; + if (FindBlockRun(pos,run,offset) < B_OK) { + *_length = 0; + RETURN_ERROR(B_BAD_VALUE); + } + + uint32 bytesRead = 0; + uint32 blockSize = fVolume->BlockSize(); + uint32 blockShift = fVolume->BlockShift(); + uint8 *block; + + // the first block_run we read could not be aligned to the block_size boundary + // (read partial block at the beginning) + + // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 + if (pos % blockSize != 0) { + run.start += (pos - offset) / blockSize; + run.length -= (pos - offset) / blockSize; + + Cache cached(fVolume,run); + if ((block = cached.Block()) == NULL) { + *_length = 0; + RETURN_ERROR(B_BAD_VALUE); + } + + bytesRead = blockSize - (pos % blockSize); + if (length < bytesRead) + bytesRead = length; + + memcpy(buffer,block + (pos % blockSize),bytesRead); + pos += bytesRead; + + length -= bytesRead; + if (length == 0) { + *_length = bytesRead; + return B_OK; + } + + if (FindBlockRun(pos,run,offset) < B_OK) { + *_length = bytesRead; + RETURN_ERROR(B_BAD_VALUE); + } + } + + // the first block_run is already filled in at this point + // read the following complete blocks using cached_read(), + // the last partial block is read using the generic Cache class + + bool partial = false; + + while (length > 0) { + // offset is the offset to the current pos in the block_run + run.start += (pos - offset) >> blockShift; + run.length -= (pos - offset) >> blockShift; + + if ((run.length << blockShift) > length) { + if (length < blockSize) { + Cache cached(fVolume,run); + if ((block = cached.Block()) == NULL) { + *_length = bytesRead; + RETURN_ERROR(B_BAD_VALUE); + } + memcpy(buffer + bytesRead,block,length); + bytesRead += length; + break; + } + run.length = length >> blockShift; + partial = true; + } + + if (Cache::Read(fVolume, run, buffer + bytesRead) != B_OK) { + *_length = bytesRead; + RETURN_ERROR(B_BAD_VALUE); + } + + int32 bytes = run.length << blockShift; + length -= bytes; + bytesRead += bytes; + if (length == 0) + break; + + pos += bytes; + + if (partial) { + // if the last block was read only partially, point block_run + // to the remaining part + run.start += run.length; + run.length = 1; + offset = pos; + } else if (FindBlockRun(pos, run, offset) < B_OK) { + *_length = bytesRead; + RETURN_ERROR(B_BAD_VALUE); + } + } + + *_length = bytesRead; + return B_NO_ERROR; +} + + +template +status_t +Stream::WriteAt(Transaction *transaction,off_t pos,const uint8 *buffer,size_t *_length) +{ + size_t length = *_length; + + // set/check boundaries for pos/length + if (pos < 0) + pos = 0; + else if (pos + length > Node()->data.size) { + off_t oldSize = Size(); + + // uncached files can't be resized + if (Flags() & INODE_NO_CACHE) + return B_BAD_VALUE; + + // the transaction doesn't have to be started already + if ((Flags() & INODE_NO_TRANSACTION) == 0) + transaction->Start(fVolume,BlockNumber()); + + // let's grow the data stream to the size needed + status_t status = SetFileSize(transaction,pos + length); + if (status < B_OK) { + *_length = 0; + RETURN_ERROR(status); + } + // If the position of the write was beyond the file size, we + // have to fill the gap between that position and the old file + // size with zeros. + FillGapWithZeros(oldSize,pos); + } + + block_run run; + off_t offset; + if (FindBlockRun(pos,run,offset) < B_OK) { + *_length = 0; + RETURN_ERROR(B_BAD_VALUE); + } + + bool logStream = (Flags() & INODE_LOGGED) == INODE_LOGGED; + if (logStream) + transaction->Start(fVolume,BlockNumber()); + + uint32 bytesWritten = 0; + uint32 blockSize = fVolume->BlockSize(); + uint32 blockShift = fVolume->BlockShift(); + uint8 *block; + + // the first block_run we write could not be aligned to the block_size boundary + // (write partial block at the beginning) + + // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 + if (pos % blockSize != 0) { + run.start += (pos - offset) / blockSize; + run.length -= (pos - offset) / blockSize; + + Cache cached(fVolume,run); + if ((block = cached.Block()) == NULL) { + *_length = 0; + RETURN_ERROR(B_BAD_VALUE); + } + + bytesWritten = blockSize - (pos % blockSize); + if (length < bytesWritten) + bytesWritten = length; + + memcpy(block + (pos % blockSize),buffer,bytesWritten); + + cached.WriteBack(transaction); + + pos += bytesWritten; + + length -= bytesWritten; + if (length == 0) { + *_length = bytesWritten; + return B_OK; + } + + if (FindBlockRun(pos,run,offset) < B_OK) { + *_length = bytesWritten; + RETURN_ERROR(B_BAD_VALUE); + } + } + + // the first block_run is already filled in at this point + // write the following complete blocks using Volume::WriteBlocks(), + // the last partial block is written using the generic Cache class + + bool partial = false; + + while (length > 0) { + // offset is the offset to the current pos in the block_run + run.start += (pos - offset) >> blockShift; + run.length -= (pos - offset) >> blockShift; + + if ((run.length << blockShift) > length) { + if (length < blockSize) { + Cache cached(fVolume,run); + if ((block = cached.Block()) == NULL) { + *_length = bytesWritten; + RETURN_ERROR(B_BAD_VALUE); + } + memcpy(block,buffer + bytesWritten,length); + + cached.WriteBack(transaction); + + bytesWritten += length; + break; + } + run.length = length >> blockShift; + partial = true; + } + + if (Cache::Write(transaction, fVolume, run, buffer + bytesWritten) < B_OK) { + *_length = bytesWritten; + RETURN_ERROR(B_BAD_VALUE); + } + + int32 bytes = run.length << blockShift; + length -= bytes; + bytesWritten += bytes; + if (length == 0) + break; + + pos += bytes; + + if (partial) { + // if the last block was written only partially, point block_run + // to the remaining part + run.start += run.length; + run.length = 1; + offset = pos; + } else if (FindBlockRun(pos,run,offset) < B_OK) { + *_length = bytesWritten; + RETURN_ERROR(B_BAD_VALUE); + } + } + + *_length = bytesWritten; + + return B_NO_ERROR; +} +