From 11a8dbc63a11de70b8bcfe11e6fc15402529d8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 21 Nov 2002 20:42:44 +0000 Subject: [PATCH] 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 --- src/add-ons/kernel/file_systems/bfs/Inode.cpp | 226 +++++++++++++----- 1 file changed, 160 insertions(+), 66 deletions(-) diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index 2d7c799dc3..e81fc37137 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -20,8 +20,9 @@ class InodeAllocator { InodeAllocator(Transaction *transaction); ~InodeAllocator(); - status_t New(block_run *parentRun,mode_t mode,block_run &run,Inode **inode); - void Keep(); + status_t New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode); + status_t CreateTree(); + status_t Keep(); private: Transaction *fTransaction; @@ -40,19 +41,24 @@ InodeAllocator::InodeAllocator(Transaction *transaction) InodeAllocator::~InodeAllocator() { + if (fTransaction != NULL) { + if (fInode != NULL) { + fInode->Node()->flags &= ~INODE_IN_USE; + fInode->Free(fTransaction); + } else + fTransaction->GetVolume()->Free(fTransaction, fRun); + } + delete fInode; - - if (fTransaction) - fTransaction->GetVolume()->Free(fTransaction,fRun); } 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(); - status_t status = volume->AllocateForInode(fTransaction,parentRun,mode,fRun); + status_t status = volume->AllocateForInode(fTransaction, parentRun, mode, fRun); if (status < B_OK) { // don't free the space in the destructor, because // the allocation failed @@ -61,28 +67,76 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **i } run = fRun; - fInode = new Inode(volume,volume->ToVnode(run),true); + fInode = new Inode(volume, volume->ToVnode(run), true); if (fInode == NULL) 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; } -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() { + ASSERT(fInode != NULL && fTransaction != NULL); + + Volume *volume = fTransaction->GetVolume(); + fInode->Node()->flags &= ~INODE_NOT_READY; + status_t status = fInode->WriteBack(fTransaction); + fTransaction = NULL; fInode = NULL; + + return status; } // #pragma mark - -Inode::Inode(Volume *volume,vnode_id id,bool empty,uint8 reenter) - : CachedBlock(volume,volume->VnodeToBlock(id),empty), +Inode::Inode(Volume *volume, vnode_id id, bool empty, uint8 reenter) + : CachedBlock(volume, volume->VnodeToBlock(id), empty), fTree(NULL), fLock("bfs inode") { @@ -121,10 +175,13 @@ Inode::InitCheck() || Node()->attributes.allocation_group > fVolume->AllocationGroups() || Node()->attributes.allocation_group < 0 || Node()->attributes.start > (1L << fVolume->AllocationGroupShift())) { - FATAL(("inode at block %Ld corrupt!\n",fBlockNumber)); + FATAL(("inode at block %Ld corrupt!\n", fBlockNumber)); 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, // especially for the data_stream! @@ -545,7 +602,7 @@ Inode::SetName(Transaction *transaction,const char *name) */ status_t -Inode::ReadAttribute(const char *name,int32 type,off_t pos,uint8 *buffer,size_t *_length) +Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, size_t *_length) { if (pos < 0) pos = 0; @@ -564,7 +621,7 @@ Inode::ReadAttribute(const char *name,int32 type,off_t pos,uint8 *buffer,size_t if (length + pos > smallData->data_size) length = smallData->data_size - pos; - memcpy(buffer,smallData->Data() + pos,length); + memcpy(buffer, smallData->Data() + pos, length); *_length = length; return B_OK; } @@ -572,10 +629,10 @@ Inode::ReadAttribute(const char *name,int32 type,off_t pos,uint8 *buffer,size_t // search in the attribute directory Inode *attribute; - status_t status = GetAttribute(name,&attribute); + status_t status = GetAttribute(name, &attribute); if (status == B_OK) { if (attribute->Lock().Lock() == B_OK) { - status = attribute->ReadAt(pos,(uint8 *)buffer,_length); + status = attribute->ReadAt(pos, (uint8 *)buffer, _length); attribute->Lock().Unlock(); } else status = B_ERROR; @@ -731,16 +788,16 @@ Inode::RemoveAttribute(Transaction *transaction,const char *name) status_t -Inode::GetAttribute(const char *name,Inode **attribute) +Inode::GetAttribute(const char *name, Inode **attribute) { // does this inode even have attributes? if (Attributes().IsZero()) return B_ENTRY_NOT_FOUND; - Vnode vnode(fVolume,Attributes()); + Vnode vnode(fVolume, Attributes()); Inode *attributes; if (vnode.Get(&attributes) < B_OK) { - FATAL(("get_vnode() failed in Inode::GetAttribute(name = \"%s\")\n",name)); + FATAL(("get_vnode() failed in Inode::GetAttribute(name = \"%s\")\n", name)); return B_ERROR; } @@ -748,8 +805,16 @@ Inode::GetAttribute(const char *name,Inode **attribute) status_t status = attributes->GetTree(&tree); if (status == B_OK) { vnode_id id; - if ((status = tree->Find((uint8 *)name,(uint16)strlen(name),&id)) == B_OK) - return get_vnode(fVolume->ID(),id,(void **)attribute); + if ((status = tree->Find((uint8 *)name, (uint16)strlen(name), &id)) == B_OK) { + 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; } @@ -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 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) 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); RETURN_ERROR(B_ERROR); } @@ -1754,59 +1850,66 @@ Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 m if (status < B_OK) 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(); - node->magic1 = INODE_MAGIC1; - node->inode_num = run; node->parent = parentRun; node->uid = geteuid(); node->gid = parent ? parent->Node()->gid : getegid(); // the group ID is inherited from the parent, if available - node->mode = mode; - node->flags = INODE_IN_USE; + 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 // don't add it to attributes, or indices if (tree && inode->IsRegularNode() && inode->SetName(transaction, name) < B_OK) 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) if (inode->IsContainer()) { - // force S_STR_INDEX to be set, if no type is set - if ((mode & S_INDEX_TYPES) == 0) - node->mode |= S_STR_INDEX; - - 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; - } + status = allocator.CreateTree(); + if (status < B_OK) + return status; } + // 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) Index index(volume); if (inode->IsRegularNode()) { // the name index only contains regular files 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; + } } inode->UpdateOldLastModified(); @@ -1820,24 +1923,15 @@ Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 m index.InsertLastModified(transaction, inode); } - if ((status = inode->WriteBack(transaction)) < B_OK) - return status; - - 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); + if (new_vnode(volume->ID(), inode->ID(), inode) != B_OK) { + // this is a really fatal error, and we can't recover from that + DIE(("new_vnode() failed for inode!")); } - // 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(); - + if (_id != NULL) *_id = inode->ID(); if (_inode != NULL)