Completely worked over the inode creation mechanism. Now more work is delegated
to the InodeAllocator class (initialization of the inode, tree creation for containers). The InodeAllocator class now frees all the space the inode occupies, including the B+tree if it's a container (by calling the new Inode::Free() method). Inode::Free() implementation (logic removed from bfs_remove_vnode()). Now secures the inode from being loaded by setting the INODE_NOT_READY flag (completely handled by the InodeAllocator class). The inode is now removed from its parent if something went wrong after it had been added. Utilizes the new BPlusTree::Remove() method where appropriate. Inode::GetAttribute() now checks if the inode opened is really an attribute. Inode::InitCheck() now fails with B_BUSY if the INODE_NOT_READY flag is set. InodeAllocator::Keep() now writes back the inode. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2055 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -20,8 +20,9 @@ class InodeAllocator {
|
|||||||
InodeAllocator(Transaction *transaction);
|
InodeAllocator(Transaction *transaction);
|
||||||
~InodeAllocator();
|
~InodeAllocator();
|
||||||
|
|
||||||
status_t New(block_run *parentRun,mode_t mode,block_run &run,Inode **inode);
|
status_t New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode);
|
||||||
void Keep();
|
status_t CreateTree();
|
||||||
|
status_t Keep();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Transaction *fTransaction;
|
Transaction *fTransaction;
|
||||||
@@ -40,15 +41,20 @@ InodeAllocator::InodeAllocator(Transaction *transaction)
|
|||||||
|
|
||||||
InodeAllocator::~InodeAllocator()
|
InodeAllocator::~InodeAllocator()
|
||||||
{
|
{
|
||||||
delete fInode;
|
if (fTransaction != NULL) {
|
||||||
|
if (fInode != NULL) {
|
||||||
if (fTransaction)
|
fInode->Node()->flags &= ~INODE_IN_USE;
|
||||||
|
fInode->Free(fTransaction);
|
||||||
|
} else
|
||||||
fTransaction->GetVolume()->Free(fTransaction, fRun);
|
fTransaction->GetVolume()->Free(fTransaction, fRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete fInode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **inode)
|
InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode)
|
||||||
{
|
{
|
||||||
Volume *volume = fTransaction->GetVolume();
|
Volume *volume = fTransaction->GetVolume();
|
||||||
|
|
||||||
@@ -65,16 +71,64 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **i
|
|||||||
if (fInode == NULL)
|
if (fInode == NULL)
|
||||||
RETURN_ERROR(B_NO_MEMORY);
|
RETURN_ERROR(B_NO_MEMORY);
|
||||||
|
|
||||||
*inode = fInode;
|
// initialize the on-disk bfs_inode structure
|
||||||
|
|
||||||
|
bfs_inode *node = fInode->Node();
|
||||||
|
|
||||||
|
node->magic1 = INODE_MAGIC1;
|
||||||
|
node->inode_num = run;
|
||||||
|
node->mode = mode;
|
||||||
|
node->flags = INODE_IN_USE | INODE_NOT_READY;
|
||||||
|
// INODE_NOT_READY prevents the inode from being opened - it is
|
||||||
|
// cleared in InodeAllocator::Keep()
|
||||||
|
|
||||||
|
node->create_time = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
|
||||||
|
node->last_modified_time = node->create_time | (volume->GetUniqueID() & INODE_TIME_MASK);
|
||||||
|
// we use Volume::GetUniqueID() to avoid having too many duplicates in the
|
||||||
|
// last_modified index
|
||||||
|
|
||||||
|
node->inode_size = volume->InodeSize();
|
||||||
|
|
||||||
|
*_inode = fInode;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
status_t
|
||||||
|
InodeAllocator::CreateTree()
|
||||||
|
{
|
||||||
|
Volume *volume = fTransaction->GetVolume();
|
||||||
|
|
||||||
|
// force S_STR_INDEX to be set, if no type is set
|
||||||
|
if ((fInode->Mode() & S_INDEX_TYPES) == 0)
|
||||||
|
fInode->Node()->mode |= S_STR_INDEX;
|
||||||
|
|
||||||
|
BPlusTree *tree = fInode->fTree = new BPlusTree(fTransaction, fInode);
|
||||||
|
if (tree == NULL || tree->InitCheck() < B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
if (fInode->IsRegularNode()) {
|
||||||
|
if (tree->Insert(fTransaction, ".", fInode->ID()) < B_OK
|
||||||
|
|| tree->Insert(fTransaction, "..", volume->ToVnode(fInode->Parent())) < B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
InodeAllocator::Keep()
|
InodeAllocator::Keep()
|
||||||
{
|
{
|
||||||
|
ASSERT(fInode != NULL && fTransaction != NULL);
|
||||||
|
|
||||||
|
Volume *volume = fTransaction->GetVolume();
|
||||||
|
fInode->Node()->flags &= ~INODE_NOT_READY;
|
||||||
|
status_t status = fInode->WriteBack(fTransaction);
|
||||||
|
|
||||||
fTransaction = NULL;
|
fTransaction = NULL;
|
||||||
fInode = NULL;
|
fInode = NULL;
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -125,6 +179,9 @@ Inode::InitCheck()
|
|||||||
RETURN_ERROR(B_BAD_DATA);
|
RETURN_ERROR(B_BAD_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Flags() & INODE_NOT_READY)
|
||||||
|
return B_BUSY;
|
||||||
|
|
||||||
// ToDo: Add some tests to check the integrity of the other stuff here,
|
// ToDo: Add some tests to check the integrity of the other stuff here,
|
||||||
// especially for the data_stream!
|
// especially for the data_stream!
|
||||||
|
|
||||||
@@ -748,8 +805,16 @@ Inode::GetAttribute(const char *name,Inode **attribute)
|
|||||||
status_t status = attributes->GetTree(&tree);
|
status_t status = attributes->GetTree(&tree);
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
vnode_id id;
|
vnode_id id;
|
||||||
if ((status = tree->Find((uint8 *)name,(uint16)strlen(name),&id)) == B_OK)
|
if ((status = tree->Find((uint8 *)name, (uint16)strlen(name), &id)) == B_OK) {
|
||||||
return get_vnode(fVolume->ID(),id,(void **)attribute);
|
Vnode vnode(fVolume, id);
|
||||||
|
// Check if the attribute is really an attribute
|
||||||
|
if (vnode.Get(attribute) < B_OK
|
||||||
|
|| !(*attribute)->IsAttribute())
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
vnode.Keep();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -1510,6 +1575,37 @@ Inode::Trim(Transaction *transaction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
Inode::Free(Transaction *transaction)
|
||||||
|
{
|
||||||
|
// Perhaps there should be an implementation of Inode::ShrinkStream() that
|
||||||
|
// just frees the data_stream, but doesn't change the inode (since it is
|
||||||
|
// freed anyway) - that would make an undelete command possible
|
||||||
|
status_t status = SetFileSize(transaction, 0);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// Free all attributes, and remove their indices
|
||||||
|
{
|
||||||
|
// We have to limit the scope of AttributeIterator, so that its
|
||||||
|
// destructor is not called after the inode is deleted
|
||||||
|
AttributeIterator iterator(this);
|
||||||
|
|
||||||
|
char name[B_FILE_NAME_LENGTH];
|
||||||
|
uint32 type;
|
||||||
|
size_t length;
|
||||||
|
vnode_id id;
|
||||||
|
while ((status = iterator.GetNext(name, &length, &type, &id)) == B_OK)
|
||||||
|
RemoveAttribute(transaction, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WriteBack(transaction) < B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
return fVolume->Free(transaction, BlockRun());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Inode::Sync()
|
Inode::Sync()
|
||||||
{
|
{
|
||||||
@@ -1642,7 +1738,7 @@ Inode::Remove(Transaction *transaction, const char *name, off_t *_id, bool isDir
|
|||||||
if (remove_vnode(fVolume->ID(), id) != B_OK)
|
if (remove_vnode(fVolume->ID(), id) != B_OK)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
if (tree->Remove(transaction,(uint8 *)name, (uint16)strlen(name), id) < B_OK) {
|
if (tree->Remove(transaction, name, id) < B_OK) {
|
||||||
unremove_vnode(fVolume->ID(), id);
|
unremove_vnode(fVolume->ID(), id);
|
||||||
RETURN_ERROR(B_ERROR);
|
RETURN_ERROR(B_ERROR);
|
||||||
}
|
}
|
||||||
@@ -1754,60 +1850,67 @@ Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 m
|
|||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// initialize the on-disk bfs_inode structure
|
// Initialize the parts of the bfs_inode structure that
|
||||||
|
// InodeAllocator::New() hasn't touched yet
|
||||||
|
|
||||||
bfs_inode *node = inode->Node();
|
bfs_inode *node = inode->Node();
|
||||||
|
|
||||||
node->magic1 = INODE_MAGIC1;
|
|
||||||
node->inode_num = run;
|
|
||||||
node->parent = parentRun;
|
node->parent = parentRun;
|
||||||
|
|
||||||
node->uid = geteuid();
|
node->uid = geteuid();
|
||||||
node->gid = parent ? parent->Node()->gid : getegid();
|
node->gid = parent ? parent->Node()->gid : getegid();
|
||||||
// the group ID is inherited from the parent, if available
|
// the group ID is inherited from the parent, if available
|
||||||
node->mode = mode;
|
|
||||||
node->flags = INODE_IN_USE;
|
|
||||||
node->type = type;
|
node->type = type;
|
||||||
|
|
||||||
node->create_time = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
|
|
||||||
node->last_modified_time = node->create_time | (volume->GetUniqueID() & INODE_TIME_MASK);
|
|
||||||
// we use Volume::GetUniqueID() to avoid having too many duplicates in the
|
|
||||||
// last_modified index
|
|
||||||
|
|
||||||
node->inode_size = volume->InodeSize();
|
|
||||||
|
|
||||||
// only add the name to regular files, directories, or symlinks
|
// only add the name to regular files, directories, or symlinks
|
||||||
// don't add it to attributes, or indices
|
// don't add it to attributes, or indices
|
||||||
if (tree && inode->IsRegularNode() && inode->SetName(transaction, name) < B_OK)
|
if (tree && inode->IsRegularNode() && inode->SetName(transaction, name) < B_OK)
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
|
|
||||||
// initialize b+tree if it's a directory (and add "." & ".." if it's
|
// Initialize b+tree if it's a directory (and add "." & ".." if it's
|
||||||
// a standard directory for files - not for attributes or indices)
|
// a standard directory for files - not for attributes or indices)
|
||||||
if (inode->IsContainer()) {
|
if (inode->IsContainer()) {
|
||||||
// force S_STR_INDEX to be set, if no type is set
|
status = allocator.CreateTree();
|
||||||
if ((mode & S_INDEX_TYPES) == 0)
|
if (status < B_OK)
|
||||||
node->mode |= S_STR_INDEX;
|
return status;
|
||||||
|
|
||||||
BPlusTree *tree = inode->fTree = new BPlusTree(transaction, inode);
|
|
||||||
if (tree == NULL || tree->InitCheck() < B_OK)
|
|
||||||
return B_ERROR;
|
|
||||||
|
|
||||||
if (inode->IsRegularNode()) {
|
|
||||||
if (tree->Insert(transaction, ".", inode->BlockNumber()) < B_OK
|
|
||||||
|| tree->Insert(transaction, "..", volume->ToBlock(inode->Parent())) < B_OK)
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a link to the inode from the parent, depending on its type
|
||||||
|
// (the INODE_NOT_READY flag is set, so it is safe to make the inode
|
||||||
|
// accessable to the file system here)
|
||||||
|
if (tree) {
|
||||||
|
status = tree->Insert(transaction, name, inode->ID());
|
||||||
|
} else if (parent && (mode & S_ATTR_DIR) != 0) {
|
||||||
|
parent->Attributes() = run;
|
||||||
|
status = parent->WriteBack(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note, we only care if the inode could be made accessable for the
|
||||||
|
// two cases above; the root node or the indices root node must
|
||||||
|
// handle this case on their own (or other cases where "parent" is
|
||||||
|
// NULL)
|
||||||
|
if (status < B_OK)
|
||||||
|
RETURN_ERROR(status);
|
||||||
|
|
||||||
// update the main indices (name, size & last_modified)
|
// update the main indices (name, size & last_modified)
|
||||||
|
|
||||||
Index index(volume);
|
Index index(volume);
|
||||||
if (inode->IsRegularNode()) {
|
if (inode->IsRegularNode()) {
|
||||||
// the name index only contains regular files
|
// the name index only contains regular files
|
||||||
status = index.InsertName(transaction, name, inode);
|
status = index.InsertName(transaction, name, inode);
|
||||||
if (status < B_OK && status != B_BAD_INDEX)
|
if (status < B_OK && status != B_BAD_INDEX) {
|
||||||
|
// We have to remove the node from the parent at this point,
|
||||||
|
// because the InodeAllocator destructor can't handle this
|
||||||
|
// case (and if it fails, we can't do anything about it...)
|
||||||
|
if (tree)
|
||||||
|
tree->Remove(transaction, name, inode->ID());
|
||||||
|
else
|
||||||
|
parent->Node()->attributes.SetTo(0, 0, 0);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inode->UpdateOldLastModified();
|
inode->UpdateOldLastModified();
|
||||||
|
|
||||||
@@ -1820,22 +1923,13 @@ Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 m
|
|||||||
index.InsertLastModified(transaction, inode);
|
index.InsertLastModified(transaction, inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((status = inode->WriteBack(transaction)) < B_OK)
|
if (new_vnode(volume->ID(), inode->ID(), inode) != B_OK) {
|
||||||
return status;
|
// this is a really fatal error, and we can't recover from that
|
||||||
|
DIE(("new_vnode() failed for inode!"));
|
||||||
if (new_vnode(volume->ID(), inode->ID(), inode) != B_OK)
|
|
||||||
return B_ERROR;
|
|
||||||
|
|
||||||
// add a link to the inode from the parent, depending on its type
|
|
||||||
if (tree && tree->Insert(transaction, name, volume->ToBlock(run)) < B_OK) {
|
|
||||||
put_vnode(volume->ID(), inode->ID());
|
|
||||||
RETURN_ERROR(B_ERROR);
|
|
||||||
} else if (parent && mode & S_ATTR_DIR) {
|
|
||||||
parent->Attributes() = run;
|
|
||||||
parent->WriteBack(transaction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// everything worked well, so we want to keep the inode
|
// Everything worked well until this point, we have a fully
|
||||||
|
// initialized inode, and we want to keep it
|
||||||
allocator.Keep();
|
allocator.Keep();
|
||||||
|
|
||||||
if (_id != NULL)
|
if (_id != NULL)
|
||||||
|
|||||||
Reference in New Issue
Block a user