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:
Axel Dörfler
2002-11-21 20:42:44 +00:00
parent 9fec50f9db
commit 11a8dbc63a
+145 -51
View File
@@ -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)