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
This commit is contained in:
Axel Dörfler 2002-08-12 16:29:46 +00:00
parent 09d84e61b6
commit ee77e6d68f
2 changed files with 640 additions and 349 deletions

View File

@ -9,6 +9,7 @@
#include "cpp.h"
#include "Inode.h"
#include "BPlusTree.h"
#include "Stream.h"
#include "Index.h"
#include <string.h>
@ -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<Access::Cached> *)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<Access::Uncached> *)this)->ReadAt(pos, buffer, _length);
size_t length = *_length;
if (Flags() & INODE_LOGGED)
return ((Stream<Access::Logged> *)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<Access::Cached> *)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<Access::Uncached> *)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<Access::Logged> *)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<Access::Cached> *)this)->WriteAt(transaction, pos, buffer, _length);
}

View File

@ -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 <Inode.h>
// 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 Cache>
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<class Cache>
status_t
Stream<Cache>::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<class Cache>
status_t
Stream<Cache>::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<class Cache>
status_t
Stream<Cache>::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;
}