* Copyright 2001-2008 pinc Software. All Rights Reserved.
* Released under the terms of the MIT license.
*/
#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;
};
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;
if (fInode->magic1 != INODE_MAGIC1
|| !(fInode->flags & INODE_IN_USE)
|| fInode->inode_num.length != 1)
return B_ERROR;
if (fDisk->BlockSize()) {
if (fInode->inode_size != fDisk->SuperBlock()->inode_size
|| 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
|| 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 {
switch (fInode->inode_size) {
case 1024:
case 2048:
case 4096:
case 8192:
break;
default:
return B_ERROR;
}
}
return B_OK;
}
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();
return B_OK;
}
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;
if (data == NULL)
data = fInode->small_data_start;
else
data = data->Next();
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)
{
if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) {
if (fCurrentSmallData == NULL)
fCurrentSmallData = fInode->small_data_start;
else
fCurrentSmallData = fCurrentSmallData->Next();
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;
}
}
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)
{
RewindAttributes();
char name[B_FILE_NAME_LENGTH];
const uint32 kMaxBrokenAttributes = 64;
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);
}
}
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)
{
if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR)
return new Attribute(disk, inode, ownBuffer);
if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR)
return new Directory(disk, inode, ownBuffer);
if (S_ISREG(inode->mode))
return new File(disk, inode, ownBuffer);
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();
return object;
}
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;
if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd)
return B_OK;
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) {
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;
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];
} else {
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];
}
} else {
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];
}
return B_OK;
}
ssize_t
DataStream::ReadAt(off_t pos, void *buffer, size_t size)
{
NodeGetter _(this);
if (pos + (off_t)size > fInode->data.size) {
if (pos > fInode->data.size)
return B_ERROR;
size = fInode->data.size - pos;
if (!size)
return 0;
}
ssize_t read = 0;
while (size > 0) {
status_t status = FindBlockRun(pos);
if (status < B_OK)
return status;
ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
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);
if (pos + (off_t)size > fInode->data.size) {
if (pos > fInode->data.size)
return B_ERROR;
size = fInode->data.size - pos;
if (!size)
return 0;
}
ssize_t written = 0;
while (size > 0) {
status_t status = FindBlockRun(pos);
if (status < B_OK)
return status;
ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos);
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);
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;
}
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) {
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);
}
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 *, bool ,
Inode::Source *)
{
return B_OK;
}
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)
{
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) {
if (!*name)
*name = '_';
path.Append(name);
} else {
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) {
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;
}
fTree->SetHoldChanges(true);
if (created) {
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;
}
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) {
if (!*name)
*name = '_';
path.Append(name);
} else {
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;
}