/*
* Copyright 2001-2008 pinc Software. All Rights Reserved.
* Released under the terms of the MIT license.
*/
//! BFS Inode classes
#include "Inode.h"
#include "BPlusTree.h"
#include <Directory.h>
#include <SymLink.h>
#include <Entry.h>
#include <Path.h>
#include <String.h>
#include <new>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
class NodeGetter {
public:
NodeGetter(Inode* inode)
:
fInode(inode)
{
fInode->AcquireBuffer();
}
~NodeGetter()
{
fInode->ReleaseBuffer();
}
private:
Inode* fInode;
};
// #pragma mark -
Inode::Inode(Disk* disk, bfs_inode* inode, bool ownBuffer)
:
fDisk(disk),
fInode(inode),
fOwnBuffer(ownBuffer),
fPath(NULL),
fRefCount(1),
fCurrentSmallData(NULL),
fAttributes(NULL),
fAttributeBuffer(NULL)
{
if (inode != NULL)
fBlockRun = inode->inode_num;
}
Inode::Inode(const Inode& inode)
:
fDisk(inode.fDisk),
fInode(inode.fInode),
fOwnBuffer(false),
fPath(NULL),
fBlockRun(inode.fBlockRun),
fRefCount(1),
fCurrentSmallData(NULL),
fAttributes(NULL),
fAttributeBuffer(NULL)
{
}
Inode::~Inode()
{
_Unset();
}
void
Inode::_Unset()
{
if (fOwnBuffer)
free(fInode);
fInode = NULL;
fBlockRun.SetTo(0, 0, 0);
free(fPath);
fPath = NULL;
delete fAttributes;
fAttributes = NULL;
}
status_t
Inode::SetTo(bfs_inode *inode)
{
_Unset();
fInode = inode;
fBlockRun = inode->inode_num;
return B_OK;
}
status_t
Inode::InitCheck() const
{
if (!fInode)
return B_ERROR;
// test inode magic and flags
if (fInode->magic1 != INODE_MAGIC1
|| !(fInode->flags & INODE_IN_USE)
|| fInode->inode_num.length != 1)
return B_ERROR;
if (fDisk->BlockSize()) {
// matches known block size?
if (fInode->inode_size != fDisk->SuperBlock()->inode_size
// parent resides on disk?
|| fInode->parent.allocation_group > fDisk->SuperBlock()->num_ags
|| fInode->parent.allocation_group < 0
|| fInode->parent.start > (1L << fDisk->SuperBlock()->ag_shift)
|| fInode->parent.length != 1
// attributes, too?
|| fInode->attributes.allocation_group > fDisk->SuperBlock()->num_ags
|| fInode->attributes.allocation_group < 0
|| fInode->attributes.start > (1L << fDisk->SuperBlock()->ag_shift))
return B_ERROR;
} else {
// is inode size one of the valid values?
switch (fInode->inode_size) {
case 1024:
case 2048:
case 4096:
case 8192:
break;
default:
return B_ERROR;
}
}
return B_OK;
// is inode on a boundary matching it's size?
//return (Offset() % fInode->inode_size) == 0 ? B_OK : B_ERROR;
}
status_t
Inode::CopyBuffer()
{
if (!fInode)
return B_ERROR;
bfs_inode *buffer = (bfs_inode *)malloc(fInode->inode_size);
if (!buffer)
return B_NO_MEMORY;
memcpy(buffer, fInode, fInode->inode_size);
fInode = buffer;
fOwnBuffer = true;
BufferClobbered();
// this must not be deleted anymore
return B_OK;
}
/*static*/ bool
Inode::_LowMemory()
{
static bigtime_t lastChecked;
static int32 percentUsed;
if (system_time() > lastChecked + 1000000LL) {
system_info info;
get_system_info(&info);
percentUsed = 100 * info.used_pages / info.max_pages;
}
return percentUsed > 75;
}
void
Inode::ReleaseBuffer()
{
if (atomic_add(&fRefCount, -1) != 1)
return;
if (fOwnBuffer) {
if (!_LowMemory())
return;
free(fInode);
fInode = NULL;
}
}
status_t
Inode::AcquireBuffer()
{
if (atomic_add(&fRefCount, 1) != 0)
return B_OK;
if (!fOwnBuffer || fInode != NULL)
return B_OK;
fInode = (bfs_inode*)malloc(fDisk->BlockSize());
if (fInode == NULL)
return B_NO_MEMORY;
ssize_t bytesRead = fDisk->ReadAt(Offset(), fInode, fDisk->BlockSize());
if (bytesRead < B_OK)
return bytesRead;
return B_OK;
}
void
Inode::BufferClobbered()
{
AcquireBuffer();
}
void
Inode::SetParent(const block_run& run)
{
fInode->parent = run;
BufferClobbered();
}
void
Inode::SetBlockRun(const block_run& run)
{
fInode->inode_num = run;
fBlockRun = run;
BufferClobbered();
}
void
Inode::SetMode(uint32 mode)
{
fInode->mode = mode;
BufferClobbered();
}
status_t
Inode::SetName(const char *name)
{
if (name == NULL || *name == '\0')
return B_BAD_VALUE;
small_data *data = fInode->small_data_start, *nameData = NULL;
BufferClobbered();
while (!data->IsLast(fInode)) {
if (data->type == FILE_NAME_TYPE
&& data->name_size == FILE_NAME_NAME_LENGTH
&& *data->Name() == FILE_NAME_NAME)
nameData = data;
data = data->Next();
}
int32 oldLength = nameData == NULL ? 0 : nameData->data_size;
int32 newLength = strlen(name) + (nameData == NULL ? sizeof(small_data) + 5 : 0);
if ((addr_t)data + newLength - oldLength >= (addr_t)(fInode
+ fDisk->BlockSize()))
return B_NO_MEMORY;
if (nameData == NULL) {
memmove(newLength + (uint8 *)fInode->small_data_start,
fInode->small_data_start,
(addr_t)data - (addr_t)fInode->small_data_start);
nameData = fInode->small_data_start;
} else {
memmove(newLength + (uint8 *)nameData, nameData,
(addr_t)data - (addr_t)fInode->small_data_start);
}
memset(nameData, 0, sizeof(small_data) + 5 + strlen(name));
nameData->type = FILE_NAME_TYPE;
nameData->name_size = FILE_NAME_NAME_LENGTH;
nameData->data_size = strlen(name);
*nameData->Name() = FILE_NAME_NAME;
strcpy((char *)nameData->Data(),name);
return B_OK;
}
const char *
Inode::Name() const
{
if (InitCheck() != B_OK) {
puts("Not getting name because node is invalid");
return NULL;
}
small_data *data = fInode->small_data_start;
while (!data->IsLast(fInode)) {
if (data->type == FILE_NAME_TYPE
&& data->name_size == FILE_NAME_NAME_LENGTH
&& *data->Name() == FILE_NAME_NAME)
return (const char *)data->Data();
data = data->Next();
}
return NULL;
}
status_t
Inode::GetNextSmallData(small_data **smallData)
{
if (!fInode)
return B_ERROR;
small_data *data = *smallData;
// begin from the start?
if (data == NULL)
data = fInode->small_data_start;
else
data = data->Next();
// is already last item?
if (data->IsLast(fInode))
return B_ENTRY_NOT_FOUND;
*smallData = data;
return B_OK;
}
status_t
Inode::RewindAttributes()
{
fCurrentSmallData = NULL;
if (fAttributes != NULL)
fAttributes->Rewind();
return B_OK;
}
status_t
Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length)
{
// read attributes out of the small data section
if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) {
if (fCurrentSmallData == NULL)
fCurrentSmallData = fInode->small_data_start;
else
fCurrentSmallData = fCurrentSmallData->Next();
// skip name attribute
if (!fCurrentSmallData->IsLast(fInode)
&& fCurrentSmallData->name_size == FILE_NAME_NAME_LENGTH
&& *fCurrentSmallData->Name() == FILE_NAME_NAME)
fCurrentSmallData = fCurrentSmallData->Next();
if (!fCurrentSmallData->IsLast(fInode)) {
strncpy(name,fCurrentSmallData->Name(), B_FILE_NAME_LENGTH);
*type = fCurrentSmallData->type;
*data = fCurrentSmallData->Data();
*length = fCurrentSmallData->data_size;
return B_OK;
}
}
// read attributes out of the attribute directory
if (Attributes().IsZero())
return B_ENTRY_NOT_FOUND;
if (fAttributes == NULL)
fAttributes = (Directory *)Inode::Factory(fDisk, Attributes());
status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR;
if (status < B_OK)
return status;
block_run run;
status = fAttributes->GetNextEntry(name, &run);
if (status < B_OK) {
free(fAttributeBuffer);
fAttributeBuffer = NULL;
return status;
}
Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run);
if (attribute == NULL || attribute->InitCheck() < B_OK)
return B_IO_ERROR;
*type = attribute->Type();
void *buffer = realloc(fAttributeBuffer, attribute->Size());
if (buffer == NULL) {
free(fAttributeBuffer);
fAttributeBuffer = NULL;
delete attribute;
return B_NO_MEMORY;
}
fAttributeBuffer = buffer;
ssize_t size = attribute->Read(fAttributeBuffer, attribute->Size());
delete attribute;
*length = size;
*data = fAttributeBuffer;
return size < B_OK ? size : B_OK;
}
status_t
Inode::_FindPath(Inode::Source *source)
{
BString path;
block_run parent = Parent();
while (!parent.IsZero() && parent != fDisk->Root()) {
Inode *inode;
if (source)
inode = source->InodeAt(parent);
else
inode = Inode::Factory(fDisk, parent);
if (inode == NULL
|| inode->InitCheck() < B_OK
|| inode->Name() == NULL
|| !*inode->Name()) {
BString sub;
sub << "__recovered " << parent.allocation_group << ":"
<< (int32)parent.start << "/";
path.Prepend(sub);
delete inode;
break;
}
parent = inode->Parent();
path.Prepend("/");
path.Prepend(inode->Name());
delete inode;
}
fPath = strdup(path.String());
return B_OK;
}
const char *
Inode::Path(Inode::Source *source)
{
if (fPath == NULL)
_FindPath(source);
return fPath;
}
status_t
Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source)
{
if (root == NULL)
return B_ENTRY_NOT_FOUND;
BString path;
if (fullPath)
path.Append(Path(source));
if (*(root + strlen(root) - 1) != '/')
path.Prepend("/");
path.Prepend(root);
return create_directory(path.String(), 0777);
}
status_t
Inode::CopyAttributesTo(BNode *node)
{
// copy attributes
RewindAttributes();
char name[B_FILE_NAME_LENGTH];
const uint32 kMaxBrokenAttributes = 64;
// sanity max value
uint32 count = 0;
uint32 type;
void *data;
size_t size;
status_t status;
while ((status = GetNextAttribute(name, &type, &data, &size))
!= B_ENTRY_NOT_FOUND) {
if (status != B_OK) {
printf("could not open attribute (possibly: %s): %s!\n",
name, strerror(status));
if (count++ > kMaxBrokenAttributes)
break;
continue;
}
ssize_t written = node->WriteAttr(name, type, 0, data, size);
if (written < B_OK) {
printf("could not write attribute \"%s\": %s\n", name,
strerror(written));
} else if ((size_t)written < size) {
printf("could only write %ld bytes (from %ld) at attribute \"%s\"\n",
written, size, name);
}
}
// copy stats
node->SetPermissions(fInode->mode);
node->SetOwner(fInode->uid);
node->SetGroup(fInode->gid);
node->SetModificationTime(fInode->last_modified_time >> 16);
node->SetCreationTime(fInode->create_time >> 16);
return B_OK;
}
Inode *
Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer)
{
// attributes (of a file)
if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR)
return new Attribute(disk, inode, ownBuffer);
// directories, attribute directories, indices
if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR)
return new Directory(disk, inode, ownBuffer);
// regular files
if (S_ISREG(inode->mode))
return new File(disk, inode, ownBuffer);
// symlinks (short and link in data-stream)
if (S_ISLNK(inode->mode))
return new Symlink(disk, inode, ownBuffer);
return NULL;
}
Inode *
Inode::Factory(Disk *disk, block_run run)
{
bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
if (!inode)
return NULL;
if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0)
return NULL;
Inode *object = Factory(disk, inode);
if (object == NULL)
free(inode);
return object;
}
Inode *
Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer)
{
bfs_inode *inodeBuffer = inode->fInode;
if (copyBuffer) {
bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size);
if (!inodeCopy)
return NULL;
memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size);
inodeBuffer = inodeCopy;
}
return Factory(disk, inodeBuffer, copyBuffer);
}
Inode *
Inode::EmptyInode(Disk *disk, const char *name, int32 mode)
{
bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize());
if (!inode)
return NULL;
memset(inode, 0, sizeof(bfs_inode));
inode->magic1 = INODE_MAGIC1;
inode->inode_size = disk->BlockSize();
inode->mode = mode;
inode->flags = INODE_IN_USE | (mode & S_IFDIR ? INODE_LOGGED : 0);
if (name) {
small_data *data = inode->small_data_start;
data->type = FILE_NAME_TYPE;
data->name_size = FILE_NAME_NAME_LENGTH;
*data->Name() = FILE_NAME_NAME;
data->data_size = strlen(name);
strcpy((char *)data->Data(), name);
}
Inode *object = new (std::nothrow) Inode(disk, inode);
if (object == NULL) {
free(inode);
return NULL;
}
object->AcquireBuffer();
// this must not be deleted anymore!
return object;
}
// #pragma mark -
DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer)
: Inode(disk,inode,ownBuffer),
fCurrent(-1),
fPosition(0LL)
{
}
DataStream::DataStream(const Inode &inode)
: Inode(inode),
fCurrent(-1),
fPosition(0LL)
{
}
DataStream::~DataStream()
{
}
status_t
DataStream::FindBlockRun(off_t pos)
{
NodeGetter _(this);
if (pos > fInode->data.size)
return B_ENTRY_NOT_FOUND;
if (fCurrent < 0)
fLevel = 0;
fRunBlockEnd = fCurrent >= 0
? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL;
// access in current block run?
if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd)
return B_OK;
// find matching block run
if (fInode->data.max_direct_range > 0
&& pos >= fInode->data.max_direct_range) {
if (fInode->data.max_double_indirect_range > 0
&& pos >= fInode->data.max_indirect_range) {
// read from double indirect blocks
//printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start);
block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect);
if (indirect == NULL)
return B_ERROR;
off_t start = pos - fInode->data.max_indirect_range;
int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run));
int32 directSize = fDisk->BlockSize() * 4;
int32 index = start / indirectSize;
//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);
indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]);
if (indirect == NULL)
return B_ERROR;
fCurrent = (start % indirectSize) / directSize;
fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize);
fRunBlockEnd = fRunFileOffset + directSize;
fRun = indirect[fCurrent];
//printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
} else {
// access from indirect blocks
block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect);
if (!indirect)
return B_ERROR;
int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run);
if (fLevel != 1 || pos < fRunFileOffset) {
fRunBlockEnd = fInode->data.max_direct_range;
fCurrent = -1;
fLevel = 1;
}
while (++fCurrent < indirectRuns) {
if (indirect[fCurrent].IsZero())
break;
fRunFileOffset = fRunBlockEnd;
fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift();
if (fRunBlockEnd > pos)
break;
}
if (fCurrent == indirectRuns || indirect[fCurrent].IsZero())
return B_ERROR;
fRun = indirect[fCurrent];
//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);
}
} else {
// access from direct blocks
if (fRunFileOffset > pos) {
fRunBlockEnd = 0LL;
fCurrent = -1;
}
fLevel = 0;
while (++fCurrent < NUM_DIRECT_BLOCKS) {
if (fInode->data.direct[fCurrent].IsZero())
break;
fRunFileOffset = fRunBlockEnd;
fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift();
if (fRunBlockEnd > pos)
break;
}
if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero())
return B_ERROR;
fRun = fInode->data.direct[fCurrent];
//printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset);
}
return B_OK;
}
ssize_t
DataStream::ReadAt(off_t pos, void *buffer, size_t size)
{
NodeGetter _(this);
//printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size);
// truncate size to read
if (pos + (off_t)size > fInode->data.size) {
if (pos > fInode->data.size) // reading outside the file
return B_ERROR;
size = fInode->data.size - pos;
if (!size) // there is nothing left to read
return 0;
}
ssize_t read = 0;
//printf("### read %ld bytes at %Ld\n",size,pos);
while (size > 0) {
status_t status = FindBlockRun(pos);
if (status < B_OK)
return status;
ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
//printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,
buffer, bytes);
if (bytes <= 0) {
if (bytes == 0) {
printf("could not read bytes at: %" B_PRId32 ",%d\n",
fRun.allocation_group, fRun.start);
}
return bytes < 0 ? bytes : B_BAD_DATA;
}
buffer = (void *)((uint8 *)buffer + bytes);
size -= bytes;
pos += bytes;
read += bytes;
}
if (read >= 0)
return read;
return B_IO_ERROR;
}
ssize_t
DataStream::WriteAt(off_t pos, const void *buffer, size_t size)
{
NodeGetter _(this);
// FIXME: truncate size -> should enlargen the file
if (pos + (off_t)size > fInode->data.size) {
if (pos > fInode->data.size) // writing outside the file
return B_ERROR;
size = fInode->data.size - pos;
if (!size) // there is nothing left to write
return 0;
}
ssize_t written = 0;
//printf("### write %ld bytes at %Ld\n",size,pos);
while (size > 0) {
status_t status = FindBlockRun(pos);
if (status < B_OK)
return status;
ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
//printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset);
bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes);
if (bytes < 0)
return bytes;
buffer = (void *)((uint8 *)buffer + bytes);
size -= bytes;
pos += bytes;
written += bytes;
}
if (written >= 0)
return written;
return B_IO_ERROR;
}
off_t
DataStream::Seek(off_t position, uint32 seekMode)
{
NodeGetter _(this);
if (seekMode == SEEK_SET)
fPosition = position;
else if (seekMode == SEEK_END)
fPosition = fInode->data.size + position;
else
fPosition += position;
return fPosition;
}
off_t
DataStream::Position() const
{
return fPosition;
}
status_t
DataStream::SetSize(off_t size)
{
NodeGetter _(this);
// FIXME: not yet supported
if (size > fInode->data.size || size > fInode->data.max_direct_range)
return B_ERROR;
if (size == fInode->data.size)
return B_OK;
BufferClobbered();
fInode->data.size = size;
fInode->data.max_direct_range = size;
fInode->data.max_indirect_range = 0;
fInode->data.max_double_indirect_range = 0;
for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) {
if (size <= 0)
fInode->data.direct[i].SetTo(0, 0, 0);
else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) {
off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize();
fInode->data.direct[i].length = blocks;
size = 0;
} else
size -= fInode->data.direct[i].length << fDisk->BlockShift();
}
return B_OK;
}
// #pragma mark -
File::File(Disk *disk, bfs_inode *inode,bool ownBuffer)
: DataStream(disk,inode,ownBuffer)
{
}
File::File(const Inode &inode)
: DataStream(inode)
{
}
File::~File()
{
}
status_t
File::InitCheck() const
{
status_t status = DataStream::InitCheck();
if (status == B_OK)
return IsFile() ? B_OK : B_ERROR;
return status;
}
status_t
File::CopyTo(const char *root, bool fullPath, Inode::Source *source)
{
status_t status = Inode::CopyTo(root, fullPath, source);
if (status < B_OK)
return status;
BPath path(root);
if (fullPath && Path(source))
path.Append(Path(source));
char *name = (char *)Name();
if (name != NULL) {
// changes the filename in the inode buffer (for deleted entries)
if (!*name)
*name = '_';
path.Append(name);
} else {
BString sub;
sub << "__untitled " << BlockRun().allocation_group << ":"
<< (int32)BlockRun().start;
path.Append(sub.String());
}
printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group,
BlockRun().start, path.Path());
BFile file;
status = file.SetTo(path.Path(),
B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS);
if (status < B_OK)
return status;
char buffer[fDisk->BlockSize()];
ssize_t size;
Seek(0, SEEK_SET);
while ((size = Read(buffer, sizeof(buffer))) > B_OK) {
ssize_t written = file.Write(buffer, size);
if (written < B_OK)
return written;
}
return CopyAttributesTo(&file);
}
// #pragma mark -
Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer)
: File(disk, inode, ownBuffer)
{
}
Attribute::Attribute(const Inode &inode)
: File(inode)
{
}
Attribute::~Attribute()
{
}
status_t
Attribute::InitCheck() const
{
status_t status = DataStream::InitCheck();
if (status == B_OK)
return IsAttribute() ? B_OK : B_ERROR;
return status;
}
status_t
Attribute::CopyTo(const char */*path*/, bool /*fullPath*/,
Inode::Source */*source*/)
{
// files and directories already copy all attributes
// eventually, this method should be implemented to recover lost
// attributes on the disk
return B_OK;
}
// #pragma mark -
Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer)
: DataStream(disk, inode, ownBuffer),
fTree(NULL)
{
}
Directory::Directory(const Inode &inode)
: DataStream(inode),
fTree(NULL)
{
}
Directory::~Directory()
{
delete fTree;
}
status_t
Directory::InitCheck() const
{
status_t status = DataStream::InitCheck();
if (status == B_OK)
return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR;
return status;
}
status_t
Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source)
{
// don't copy attributes or indices
// the recovery program should make empty files to recover lost attributes
if (IsAttributeDirectory() || IsIndex())
return B_OK;
status_t status = Inode::CopyTo(root, fullPath, source);
if (status < B_OK)
return status;
BPath path(root);
if (fullPath && Path(source))
path.Append(Path(source));
char *name = (char *)Name();
if (name != NULL) {
// changes the filename in the inode buffer (for deleted entries)
if (!*name)
*name = '_';
path.Append(name);
} else {
// create unique name
BString sub;
sub << "__untitled " << BlockRun().allocation_group << ":"
<< (int32)BlockRun().start;
path.Append(sub.String());
}
BEntry entry(path.Path());
BDirectory directory;
if ((status = entry.GetParent(&directory)) < B_OK)
return status;
status = directory.CreateDirectory(path.Leaf(), NULL);
if (status < B_OK && status != B_FILE_EXISTS)
return status;
if ((status = directory.SetTo(&entry)) < B_OK)
return status;
return CopyAttributesTo(&directory);
}
status_t
Directory::Rewind()
{
if (!fTree) {
status_t status = CreateTree();
if (status < B_OK)
return status;
}
return fTree->Rewind();
}
status_t
Directory::GetNextEntry(char *name, block_run *run)
{
status_t status;
if (!fTree) {
if ((status = Rewind()) < B_OK)
return status;
}
uint16 length;
off_t offset;
if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1,
&offset)) < B_OK)
return status;
*run = fDisk->ToBlockRun(offset);
return B_OK;
}
status_t
Directory::GetNextEntry(block_run *run)
{
char name[B_FILE_NAME_LENGTH];
return GetNextEntry(name, run);
}
status_t
Directory::Contains(const block_run *run)
{
status_t status;
if (!fTree) {
if ((status = Rewind()) < B_OK)
return status;
}
block_run searchRun;
while (GetNextEntry(&searchRun) == B_OK) {
if (searchRun == *run)
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
status_t
Directory::Contains(const Inode *inode)
{
status_t status;
if (!fTree) {
if ((status = CreateTree()) < B_OK)
return status;
}
off_t value;
const char *name = inode->Name();
status = B_ENTRY_NOT_FOUND;
if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name),
&value)) == B_OK) {
if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num)
return B_OK;
printf("inode address do not match (%s)!\n", inode->Name());
}
if (status != B_OK && status != B_ENTRY_NOT_FOUND)
return status;
return Contains(&inode->InodeBuffer()->inode_num);
}
status_t
Directory::FindEntry(const char *name, block_run *run)
{
status_t status;
if (!name)
return B_BAD_VALUE;
if (!fTree) {
if ((status = CreateTree()) < B_OK)
return status;
}
off_t value;
if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name),
&value)) >= B_OK) {
if (run)
*run = fDisk->ToBlockRun(value);
return B_OK;
}
return status;
}
status_t
Directory::AddEntry(Inode *inode)
{
status_t status;
bool created = false;
if (!fTree) {
status = CreateTree();
if (status == B_OK)
status = fTree->Validate();
if (status == B_BAD_DATA) {
//puts("bplustree corrupted!");
fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE,
false);
if ((status = fTree->InitCheck()) < B_OK) {
delete fTree;
fTree = NULL;
} else
created = true;
}
if (status < B_OK)
return status;
}
// keep all changes in memory
fTree->SetHoldChanges(true);
if (created) {
// add . and ..
fTree->Insert(".", Block());
fTree->Insert("..", fDisk->ToBlock(Parent()));
}
if (inode->Flags() & INODE_DELETED)
return B_ENTRY_NOT_FOUND;
BString name = inode->Name();
if (name == "") {
name << "__file " << inode->BlockRun().allocation_group << ":"
<< (int32)inode->BlockRun().start;
}
return fTree->Insert(name.String(), inode->Block());
}
status_t
Directory::CreateTree()
{
fTree = new BPlusTree(this);
status_t status = fTree->InitCheck();
if (status < B_OK) {
delete fTree;
fTree = NULL;
return status;
}
return B_OK;
}
status_t
Directory::GetTree(BPlusTree **tree)
{
if (!fTree) {
status_t status = CreateTree();
if (status < B_OK)
return status;
}
*tree = fTree;
return B_OK;
}
// #pragma mark -
Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer)
: Inode(disk,inode,ownBuffer)
{
}
Symlink::Symlink(const Inode &inode)
: Inode(inode)
{
}
Symlink::~Symlink()
{
}
status_t
Symlink::InitCheck() const
{
status_t status = Inode::InitCheck();
if (status == B_OK)
return IsSymlink() ? B_OK : B_ERROR;
return status;
}
status_t
Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source)
{
status_t status = Inode::CopyTo(root,fullPath,source);
if (status < B_OK)
return status;
BPath path(root);
if (fullPath && Path(source))
path.Append(Path(source));
char *name = (char *)Name();
if (name != NULL) {
// changes the filename in the inode buffer (for deleted entries)
if (!*name)
*name = '_';
path.Append(name);
} else {
// create unique name
BString sub;
sub << "__symlink " << BlockRun().allocation_group << ":"
<< (int32)BlockRun().start;
path.Append(sub.String());
}
BEntry entry(path.Path());
BDirectory directory;
if ((status = entry.GetParent(&directory)) < B_OK)
return status;
char to[2048];
if (LinksTo(to,sizeof(to)) < B_OK)
return B_ERROR;
BSymLink link;
status = directory.CreateSymLink(path.Leaf(),to,&link);
if (status < B_OK && status != B_FILE_EXISTS)
return status;
if ((status = link.SetTo(&entry)) < B_OK)
return status;
return CopyAttributesTo(&link);
}
status_t
Symlink::LinksTo(char *to,size_t maxLength)
{
if ((fInode->flags & INODE_LONG_SYMLINK) == 0) {
strcpy(to,fInode->short_symlink);
return B_OK;
}
DataStream stream(*this);
status_t status = stream.InitCheck();
if (status < B_OK)
return status;
status = stream.Read(to,maxLength);
return status < B_OK ? status : B_OK;
}
↑ V629 Consider inspecting the 'fRun.length << fDisk->BlockShift()' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fTo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fTo.
↑ V629 Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.
↑ V629 Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.
↑ V629 Consider inspecting the expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.