First round of locking fixes:
* Moved locking the data stream out of the BPlusTree methods; this fixes at least two locations where another thread could have seen outdated/incorrect data. * Removed some superfluous code in bfs_rename(): we don't have to revert to the previous situation, the transaction does this for us automatically. * Added a simple MultiWriteLocker, even though it's not really necessary anymore (since we always hold the transaction lock first when we lock more than one inode at a time). * Inode::Create() called InodeAllocator::Keep() a bit too early, the file cache and map wasn't created and assigned yet. * InodeAllocator now keeps the inode write locked. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26689 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1242,9 +1242,9 @@ BPlusTree::_SplitNode(bplustree_node *node, off_t nodeOffset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*! This inserts a key into the tree. The changes made to the tree will
|
||||||
This inserts a key into the tree. The changes made to the tree will
|
|
||||||
all be part of the \a transaction.
|
all be part of the \a transaction.
|
||||||
|
You need to have the inode write locked.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
BPlusTree::Insert(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
BPlusTree::Insert(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
||||||
@@ -1258,8 +1258,7 @@ BPlusTree::Insert(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
|||||||
panic("tried to insert invalid value %Ld!\n", value);
|
panic("tried to insert invalid value %Ld!\n", value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// lock access to stream
|
ASSERT_WRITE_LOCKED_RW_LOCK(&fStream->Lock());
|
||||||
WriteLocker locker(fStream->Lock());
|
|
||||||
|
|
||||||
Stack<node_and_key> stack;
|
Stack<node_and_key> stack;
|
||||||
if (_SeekDown(stack, key, keyLength) != B_OK)
|
if (_SeekDown(stack, key, keyLength) != B_OK)
|
||||||
@@ -1395,8 +1394,7 @@ BPlusTree::Insert(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*! Removes the duplicate index/value pair from the tree.
|
||||||
Removes the duplicate index/value pair from the tree.
|
|
||||||
It's part of the private tree interface.
|
It's part of the private tree interface.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
@@ -1578,8 +1576,7 @@ BPlusTree::_RemoveDuplicate(Transaction &transaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*! Removes the key with the given index from the specified node.
|
||||||
Removes the key with the given index from the specified node.
|
|
||||||
Since it has to get the key from the node anyway (to obtain it's
|
Since it has to get the key from the node anyway (to obtain it's
|
||||||
pointer), it's not needed to pass the key & its length, although
|
pointer), it's not needed to pass the key & its length, although
|
||||||
the calling method (BPlusTree::Remove()) have this data.
|
the calling method (BPlusTree::Remove()) have this data.
|
||||||
@@ -1639,10 +1636,10 @@ BPlusTree::_RemoveKey(bplustree_node *node, uint16 index)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*! Removes the specified key from the tree. The "value" parameter is only used
|
||||||
Removes the specified key from the tree. The "value" parameter is only used
|
|
||||||
for trees which allow duplicates, so you may safely ignore it.
|
for trees which allow duplicates, so you may safely ignore it.
|
||||||
It's not an optional parameter, so at least you have to think about it.
|
It's not an optional parameter, so at least you have to think about it.
|
||||||
|
You need to have the inode write locked.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
BPlusTree::Remove(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
BPlusTree::Remove(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
||||||
@@ -1652,8 +1649,7 @@ BPlusTree::Remove(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
|||||||
|| keyLength > BPLUSTREE_MAX_KEY_LENGTH)
|
|| keyLength > BPLUSTREE_MAX_KEY_LENGTH)
|
||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
// lock access to stream
|
ASSERT_WRITE_LOCKED_RW_LOCK(&fStream->Lock());
|
||||||
WriteLocker locker(fStream->Lock());
|
|
||||||
|
|
||||||
Stack<node_and_key> stack;
|
Stack<node_and_key> stack;
|
||||||
if (_SeekDown(stack, key, keyLength) != B_OK)
|
if (_SeekDown(stack, key, keyLength) != B_OK)
|
||||||
@@ -1749,14 +1745,14 @@ BPlusTree::Remove(Transaction &transaction, const uint8 *key, uint16 keyLength,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*! Replaces the value for the key in the tree.
|
||||||
Replaces the value for the key in the tree.
|
|
||||||
Returns B_OK if the key could be found and its value replaced,
|
Returns B_OK if the key could be found and its value replaced,
|
||||||
B_ENTRY_NOT_FOUND if the key couldn't be found, and other errors
|
B_ENTRY_NOT_FOUND if the key couldn't be found, and other errors
|
||||||
to indicate that something went terribly wrong.
|
to indicate that something went terribly wrong.
|
||||||
Note that this doesn't work with duplicates - it will just
|
Note that this doesn't work with duplicates - it will just
|
||||||
return B_BAD_TYPE if you call this function on a tree where
|
return B_BAD_TYPE if you call this function on a tree where
|
||||||
duplicates are allowed.
|
duplicates are allowed.
|
||||||
|
You need to have the inode write locked.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
BPlusTree::Replace(Transaction &transaction, const uint8 *key,
|
BPlusTree::Replace(Transaction &transaction, const uint8 *key,
|
||||||
@@ -1770,8 +1766,7 @@ BPlusTree::Replace(Transaction &transaction, const uint8 *key,
|
|||||||
if (fAllowDuplicates)
|
if (fAllowDuplicates)
|
||||||
RETURN_ERROR(B_BAD_TYPE);
|
RETURN_ERROR(B_BAD_TYPE);
|
||||||
|
|
||||||
// lock access to stream (a read lock is okay for this purpose)
|
ASSERT_WRITE_LOCKED_RW_LOCK(&fStream->Lock());
|
||||||
ReadLocker locker(fStream->Lock());
|
|
||||||
|
|
||||||
off_t nodeOffset = fHeader->RootNode();
|
off_t nodeOffset = fHeader->RootNode();
|
||||||
CachedNode cached(this);
|
CachedNode cached(this);
|
||||||
@@ -1801,8 +1796,7 @@ BPlusTree::Replace(Transaction &transaction, const uint8 *key,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*! Searches the key in the tree, and stores the offset found in
|
||||||
Searches the key in the tree, and stores the offset found in
|
|
||||||
_value, if successful.
|
_value, if successful.
|
||||||
It's very similar to BPlusTree::SeekDown(), but doesn't fill
|
It's very similar to BPlusTree::SeekDown(), but doesn't fill
|
||||||
a stack while it descends the tree.
|
a stack while it descends the tree.
|
||||||
@@ -1812,6 +1806,7 @@ BPlusTree::Replace(Transaction &transaction, const uint8 *key,
|
|||||||
Note that this doesn't work with duplicates - it will just
|
Note that this doesn't work with duplicates - it will just
|
||||||
return B_BAD_TYPE if you call this function on a tree where
|
return B_BAD_TYPE if you call this function on a tree where
|
||||||
duplicates are allowed.
|
duplicates are allowed.
|
||||||
|
You need to have the inode read or write locked.
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
BPlusTree::Find(const uint8 *key, uint16 keyLength, off_t *_value)
|
BPlusTree::Find(const uint8 *key, uint16 keyLength, off_t *_value)
|
||||||
@@ -1824,8 +1819,7 @@ BPlusTree::Find(const uint8 *key, uint16 keyLength, off_t *_value)
|
|||||||
if (fAllowDuplicates)
|
if (fAllowDuplicates)
|
||||||
RETURN_ERROR(B_BAD_TYPE);
|
RETURN_ERROR(B_BAD_TYPE);
|
||||||
|
|
||||||
// lock access to stream
|
ASSERT_READ_LOCKED_RW_LOCK(&fStream->Lock());
|
||||||
ReadLocker locker(fStream->Lock());
|
|
||||||
|
|
||||||
off_t nodeOffset = fHeader->RootNode();
|
off_t nodeOffset = fHeader->RootNode();
|
||||||
CachedNode cached(this);
|
CachedNode cached(this);
|
||||||
@@ -2080,8 +2074,7 @@ TreeIterator::Traverse(int8 direction, void *key, uint16 *keyLength,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*! This is more or less a copy of BPlusTree::Find() - but it just
|
||||||
This is more or less a copy of BPlusTree::Find() - but it just
|
|
||||||
sets the current position in the iterator, regardless of if the
|
sets the current position in the iterator, regardless of if the
|
||||||
key could be found or not.
|
key could be found or not.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ Index::SetTo(const char *name)
|
|||||||
if (indices == NULL)
|
if (indices == NULL)
|
||||||
return B_ENTRY_NOT_FOUND;
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
|
ReadLocker locker(indices->Lock());
|
||||||
|
|
||||||
BPlusTree *tree;
|
BPlusTree *tree;
|
||||||
if (indices->GetTree(&tree) != B_OK)
|
if (indices->GetTree(&tree) != B_OK)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
@@ -268,6 +270,8 @@ Index::Update(Transaction &transaction, const char *name, int32 type,
|
|||||||
|
|
||||||
// remove the old key from the tree
|
// remove the old key from the tree
|
||||||
|
|
||||||
|
WriteLocker locker(Node()->Lock());
|
||||||
|
|
||||||
if (oldKey != NULL) {
|
if (oldKey != NULL) {
|
||||||
status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength,
|
status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength,
|
||||||
inode->ID());
|
inode->ID());
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ InodeAllocator::~InodeAllocator()
|
|||||||
fInode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE);
|
fInode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE);
|
||||||
// this unblocks any pending bfs_read_vnode() calls
|
// this unblocks any pending bfs_read_vnode() calls
|
||||||
fInode->Free(*fTransaction);
|
fInode->Free(*fTransaction);
|
||||||
|
rw_lock_write_unlock(&fInode->Lock());
|
||||||
remove_vnode(volume->FSVolume(), fInode->ID());
|
remove_vnode(volume->FSVolume(), fInode->ID());
|
||||||
} else
|
} else
|
||||||
volume->Free(*fTransaction, fRun);
|
volume->Free(*fTransaction, fRun);
|
||||||
@@ -208,6 +209,7 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rw_lock_write_lock(&fInode->Lock());
|
||||||
*_inode = fInode;
|
*_inode = fInode;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -259,6 +261,8 @@ InodeAllocator::Keep(fs_vnode_ops *vnodeOps, uint32 publishFlags)
|
|||||||
TRANSACTION_ABORTED, &_TransactionListener, fInode);
|
TRANSACTION_ABORTED, &_TransactionListener, fInode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rw_lock_write_unlock(&fInode->Lock());
|
||||||
|
|
||||||
fTransaction = NULL;
|
fTransaction = NULL;
|
||||||
fInode = NULL;
|
fInode = NULL;
|
||||||
|
|
||||||
@@ -1163,6 +1167,8 @@ Inode::GetAttribute(const char *name, Inode **_attribute)
|
|||||||
BPlusTree *tree;
|
BPlusTree *tree;
|
||||||
status_t status = attributes->GetTree(&tree);
|
status_t status = attributes->GetTree(&tree);
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
|
ReadLocker locker(attributes->Lock());
|
||||||
|
|
||||||
ino_t id;
|
ino_t id;
|
||||||
status = tree->Find((uint8 *)name, (uint16)strlen(name), &id);
|
status = tree->Find((uint8 *)name, (uint16)strlen(name), &id);
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
@@ -2206,6 +2212,8 @@ Inode::Remove(Transaction &transaction, const char *name, ino_t *_id,
|
|||||||
if (GetTree(&tree) != B_OK)
|
if (GetTree(&tree) != B_OK)
|
||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
|
WriteLocker locker(Lock());
|
||||||
|
|
||||||
// does the file even exist?
|
// does the file even exist?
|
||||||
off_t id;
|
off_t id;
|
||||||
if (tree->Find((uint8 *)name, (uint16)strlen(name), &id) < B_OK)
|
if (tree->Find((uint8 *)name, (uint16)strlen(name), &id) < B_OK)
|
||||||
@@ -2482,10 +2490,6 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
|
|||||||
index.InsertLastModified(transaction, inode);
|
index.InsertLastModified(transaction, inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything worked well until this point, we have a fully
|
|
||||||
// initialized inode, and we want to keep it
|
|
||||||
allocator.Keep(vnodeOps, publishFlags);
|
|
||||||
|
|
||||||
if (inode->IsFile() || inode->IsAttribute()) {
|
if (inode->IsFile() || inode->IsAttribute()) {
|
||||||
inode->SetFileCache(file_cache_create(volume->ID(), inode->ID(),
|
inode->SetFileCache(file_cache_create(volume->ID(), inode->ID(),
|
||||||
inode->Size()));
|
inode->Size()));
|
||||||
@@ -2493,6 +2497,10 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
|
|||||||
inode->Size()));
|
inode->Size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Everything worked well until this point, we have a fully
|
||||||
|
// initialized inode, and we want to keep it
|
||||||
|
allocator.Keep(vnodeOps, publishFlags);
|
||||||
|
|
||||||
if (_created)
|
if (_created)
|
||||||
*_created = true;
|
*_created = true;
|
||||||
if (_id != NULL)
|
if (_id != NULL)
|
||||||
|
|||||||
@@ -24,6 +24,30 @@ struct identify_cookie {
|
|||||||
disk_super_block super_block;
|
disk_super_block super_block;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MultiWriteLocker {
|
||||||
|
public:
|
||||||
|
MultiWriteLocker(Inode* inodeA, Inode* inodeB)
|
||||||
|
{
|
||||||
|
if (inodeA->ID() < inodeB->ID()) {
|
||||||
|
Inode* tempInode = inodeA;
|
||||||
|
inodeA = inodeB;
|
||||||
|
inodeB = tempInode;
|
||||||
|
}
|
||||||
|
|
||||||
|
fOuterLocker.SetTo(inodeA->Lock(), false);
|
||||||
|
if (inodeA != inodeB)
|
||||||
|
fInnerLocker.SetTo(inodeB->Lock(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MultiWriteLocker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
WriteLocker fOuterLocker;
|
||||||
|
WriteLocker fInnerLocker;
|
||||||
|
};
|
||||||
|
|
||||||
extern void fill_stat_buffer(Inode *inode, struct stat &stat);
|
extern void fill_stat_buffer(Inode *inode, struct stat &stat);
|
||||||
|
|
||||||
|
|
||||||
@@ -530,6 +554,8 @@ bfs_lookup(fs_volume *_volume, fs_vnode *_directory, const char *file,
|
|||||||
if (directory->GetTree(&tree) != B_OK)
|
if (directory->GetTree(&tree) != B_OK)
|
||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
|
ReadLocker locker(directory->Lock());
|
||||||
|
|
||||||
status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID);
|
status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID);
|
||||||
if (status < B_OK) {
|
if (status < B_OK) {
|
||||||
//PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status)));
|
//PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status)));
|
||||||
@@ -944,6 +970,9 @@ bfs_rename(fs_volume *_volume, fs_vnode *_oldDir, const char *oldName,
|
|||||||
if (oldDirectory == newDirectory && !strcmp(oldName, newName))
|
if (oldDirectory == newDirectory && !strcmp(oldName, newName))
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
|
Transaction transaction(volume, oldDirectory->BlockNumber());
|
||||||
|
MultiWriteLocker locker(oldDirectory, newDirectory);
|
||||||
|
|
||||||
// are we allowed to do what we've been told?
|
// are we allowed to do what we've been told?
|
||||||
status_t status = oldDirectory->CheckPermissions(W_OK);
|
status_t status = oldDirectory->CheckPermissions(W_OK);
|
||||||
if (status == B_OK)
|
if (status == B_OK)
|
||||||
@@ -951,8 +980,6 @@ bfs_rename(fs_volume *_volume, fs_vnode *_oldDir, const char *oldName,
|
|||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
Transaction transaction(volume, oldDirectory->BlockNumber());
|
|
||||||
|
|
||||||
// Get the directory's tree, and a pointer to the inode which should be
|
// Get the directory's tree, and a pointer to the inode which should be
|
||||||
// changed
|
// changed
|
||||||
BPlusTree *tree;
|
BPlusTree *tree;
|
||||||
@@ -1041,10 +1068,6 @@ bfs_rename(fs_volume *_volume, fs_vnode *_oldDir, const char *oldName,
|
|||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// If anything fails now, we have to remove the inode from the
|
|
||||||
// new directory in any case to restore the previous state
|
|
||||||
status_t bailStatus = B_OK;
|
|
||||||
|
|
||||||
// update the name only when they differ
|
// update the name only when they differ
|
||||||
bool nameUpdated = false;
|
bool nameUpdated = false;
|
||||||
if (strcmp(oldName, newName)) {
|
if (strcmp(oldName, newName)) {
|
||||||
@@ -1082,41 +1105,9 @@ bfs_rename(fs_volume *_volume, fs_vnode *_oldDir, const char *oldName,
|
|||||||
newDirectory->ID(), newName, id);
|
newDirectory->ID(), newName, id);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
// If we get here, something has gone wrong already!
|
|
||||||
|
|
||||||
// Those better don't fail, or we switch to a read-only
|
|
||||||
// device for safety reasons (Volume::Panic() does this
|
|
||||||
// for us)
|
|
||||||
// Anyway, if we overwrote a file in the target directory
|
|
||||||
// this is lost now (only in-memory, not on-disk)...
|
|
||||||
bailStatus = tree->Insert(transaction, (const uint8 *)oldName,
|
|
||||||
strlen(oldName), id);
|
|
||||||
if (movedTree != NULL) {
|
|
||||||
movedTree->Replace(transaction, (const uint8 *)"..", 2,
|
|
||||||
oldDirectory->ID());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bailStatus == B_OK && nameUpdated) {
|
|
||||||
bailStatus = inode->SetName(transaction, oldName);
|
|
||||||
if (status == B_OK) {
|
|
||||||
// update inode and index
|
|
||||||
inode->WriteBack(transaction);
|
|
||||||
|
|
||||||
Index index(volume);
|
|
||||||
index.UpdateName(transaction, newName, oldName, inode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bailStatus == B_OK) {
|
|
||||||
bailStatus = newTree->Remove(transaction, (const uint8 *)newName,
|
|
||||||
strlen(newName), id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bailStatus < B_OK)
|
|
||||||
volume->Panic();
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user