* Changed the inode write locking to be held as long as the transaction is
running - this fixes several cases where someone could see outdated data when a transaction had to be reverted (the time between unlocking the inode and actually reverting the blocks). For that, Inodes can now be put into a singly linked list. * Added a TODO in Inode::WriteAt() which explains why it cannot use the above method: seems that our VFS/VM locking model isn't really that good. * Fixed a possible deadlock in Attribute::_Truncate() where the inode write lock was held before starting the transaction. * Added an InodeReadLocker convenience class, that should be used instead of ReadLocker - Inode::Lock() only still exists because of the needs of bfs_io(). * Moved the bfs_io() callback hooks out of the exported module API region, and removed their bfs_ prefix. * Added a Volume::IsInitializing() method that should be used rather than checking if Volume::ID() is >= 0. * Removed the MultiInodeLocker again, as it's pretty much superfluous now. * Moved openModeToAccess() to the Utility.h header. * Minor cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26715 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
ebaab6d5f4
commit
2e3477e3d8
@ -58,9 +58,10 @@ Attribute::CheckAccess(const char *name, int openMode)
|
|||||||
// Opening the name attribute using this function is not allowed,
|
// Opening the name attribute using this function is not allowed,
|
||||||
// also using the reserved indices name, last_modified, and size
|
// also using the reserved indices name, last_modified, and size
|
||||||
// shouldn't be allowed.
|
// shouldn't be allowed.
|
||||||
// ToDo: we might think about allowing to update those values, but
|
// TODO: we might think about allowing to update those values, but
|
||||||
// really change their corresponding values in the bfs_inode structure
|
// really change their corresponding values in the bfs_inode structure
|
||||||
if (name[0] == FILE_NAME_NAME && name[1] == '\0'
|
if (name[0] == FILE_NAME_NAME && name[1] == '\0'
|
||||||
|
// TODO: reenable this check -- some WonderBrush locale files used them
|
||||||
/* || !strcmp(name, "name")
|
/* || !strcmp(name, "name")
|
||||||
|| !strcmp(name, "last_modified")
|
|| !strcmp(name, "last_modified")
|
||||||
|| !strcmp(name, "size")*/)
|
|| !strcmp(name, "size")*/)
|
||||||
@ -223,9 +224,9 @@ Attribute::_Truncate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fAttribute != NULL) {
|
if (fAttribute != NULL) {
|
||||||
WriteLocker locker(fAttribute->Lock());
|
|
||||||
Transaction transaction(fAttribute->GetVolume(),
|
Transaction transaction(fAttribute->GetVolume(),
|
||||||
fAttribute->BlockNumber());
|
fAttribute->BlockNumber());
|
||||||
|
fAttribute->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
status_t status = fAttribute->SetFileSize(transaction, 0);
|
status_t status = fAttribute->SetFileSize(transaction, 0);
|
||||||
if (status >= B_OK)
|
if (status >= B_OK)
|
||||||
|
@ -241,7 +241,7 @@ CachedNode::Free(Transaction &transaction, off_t offset)
|
|||||||
if (fTree == NULL || fTree->fStream == NULL || offset == BPLUSTREE_NULL)
|
if (fTree == NULL || fTree->fStream == NULL || offset == BPLUSTREE_NULL)
|
||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
// ToDo: scan the free nodes list and remove all nodes at the end
|
// TODO: scan the free nodes list and remove all nodes at the end
|
||||||
// of the tree - perhaps that shouldn't be done everytime that
|
// of the tree - perhaps that shouldn't be done everytime that
|
||||||
// function is called, perhaps it should be done when the directory
|
// function is called, perhaps it should be done when the directory
|
||||||
// inode is closed or based on some calculation or whatever...
|
// inode is closed or based on some calculation or whatever...
|
||||||
@ -1258,7 +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
|
||||||
|
|
||||||
ASSERT_WRITE_LOCKED_RW_LOCK(&fStream->Lock());
|
ASSERT_WRITE_LOCKED_INODE(fStream);
|
||||||
|
|
||||||
Stack<node_and_key> stack;
|
Stack<node_and_key> stack;
|
||||||
if (_SeekDown(stack, key, keyLength) != B_OK)
|
if (_SeekDown(stack, key, keyLength) != B_OK)
|
||||||
@ -1649,7 +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);
|
||||||
|
|
||||||
ASSERT_WRITE_LOCKED_RW_LOCK(&fStream->Lock());
|
ASSERT_WRITE_LOCKED_INODE(fStream);
|
||||||
|
|
||||||
Stack<node_and_key> stack;
|
Stack<node_and_key> stack;
|
||||||
if (_SeekDown(stack, key, keyLength) != B_OK)
|
if (_SeekDown(stack, key, keyLength) != B_OK)
|
||||||
@ -1766,7 +1766,7 @@ BPlusTree::Replace(Transaction &transaction, const uint8 *key,
|
|||||||
if (fAllowDuplicates)
|
if (fAllowDuplicates)
|
||||||
RETURN_ERROR(B_BAD_TYPE);
|
RETURN_ERROR(B_BAD_TYPE);
|
||||||
|
|
||||||
ASSERT_WRITE_LOCKED_RW_LOCK(&fStream->Lock());
|
ASSERT_WRITE_LOCKED_INODE(fStream);
|
||||||
|
|
||||||
off_t nodeOffset = fHeader->RootNode();
|
off_t nodeOffset = fHeader->RootNode();
|
||||||
CachedNode cached(this);
|
CachedNode cached(this);
|
||||||
@ -1819,7 +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);
|
||||||
|
|
||||||
ASSERT_READ_LOCKED_RW_LOCK(&fStream->Lock());
|
ASSERT_READ_LOCKED_INODE(fStream);
|
||||||
|
|
||||||
off_t nodeOffset = fHeader->RootNode();
|
off_t nodeOffset = fHeader->RootNode();
|
||||||
CachedNode cached(this);
|
CachedNode cached(this);
|
||||||
@ -1883,7 +1883,7 @@ TreeIterator::Goto(int8 to)
|
|||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
// lock access to stream
|
// lock access to stream
|
||||||
ReadLocker locker(fTree->fStream->Lock());
|
InodeReadLocker locker(fTree->fStream);
|
||||||
|
|
||||||
off_t nodeOffset = fTree->fHeader->RootNode();
|
off_t nodeOffset = fTree->fHeader->RootNode();
|
||||||
CachedNode cached(fTree);
|
CachedNode cached(fTree);
|
||||||
@ -1953,7 +1953,7 @@ TreeIterator::Traverse(int8 direction, void *key, uint16 *keyLength,
|
|||||||
return B_ENTRY_NOT_FOUND;
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
|
||||||
// lock access to stream
|
// lock access to stream
|
||||||
ReadLocker locker(fTree->fStream->Lock());
|
InodeReadLocker locker(fTree->fStream);
|
||||||
|
|
||||||
CachedNode cached(fTree);
|
CachedNode cached(fTree);
|
||||||
const bplustree_node *node;
|
const bplustree_node *node;
|
||||||
@ -2088,7 +2088,7 @@ TreeIterator::Find(const uint8 *key, uint16 keyLength)
|
|||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
// lock access to stream
|
// lock access to stream
|
||||||
ReadLocker locker(fTree->fStream->Lock());
|
InodeReadLocker locker(fTree->fStream);
|
||||||
|
|
||||||
off_t nodeOffset = fTree->fHeader->RootNode();
|
off_t nodeOffset = fTree->fHeader->RootNode();
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ 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());
|
InodeReadLocker locker(indices);
|
||||||
|
|
||||||
BPlusTree *tree;
|
BPlusTree *tree;
|
||||||
if (indices->GetTree(&tree) != B_OK)
|
if (indices->GetTree(&tree) != B_OK)
|
||||||
@ -201,7 +201,6 @@ Index::Create(Transaction &transaction, const char *name, uint32 type)
|
|||||||
return B_BAD_TYPE;
|
return B_BAD_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// do we need to create the index directory first?
|
// do we need to create the index directory first?
|
||||||
if (fVolume->IndicesNode() == NULL) {
|
if (fVolume->IndicesNode() == NULL) {
|
||||||
status_t status = fVolume->CreateIndicesRoot(transaction);
|
status_t status = fVolume->CreateIndicesRoot(transaction);
|
||||||
@ -270,7 +269,7 @@ 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());
|
Node()->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
if (oldKey != NULL) {
|
if (oldKey != NULL) {
|
||||||
status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength,
|
status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength,
|
||||||
|
@ -169,7 +169,6 @@ 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);
|
||||||
@ -199,7 +198,7 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run,
|
|||||||
if (fInode == NULL)
|
if (fInode == NULL)
|
||||||
RETURN_ERROR(B_NO_MEMORY);
|
RETURN_ERROR(B_NO_MEMORY);
|
||||||
|
|
||||||
if (volume->ID() >= 0) {
|
if (!volume->IsInitializing()) {
|
||||||
status = new_vnode(volume->FSVolume(), fInode->ID(), fInode,
|
status = new_vnode(volume->FSVolume(), fInode->ID(), fInode,
|
||||||
vnodeOps != NULL ? vnodeOps : &gBFSVnodeOps);
|
vnodeOps != NULL ? vnodeOps : &gBFSVnodeOps);
|
||||||
if (status < B_OK) {
|
if (status < B_OK) {
|
||||||
@ -209,7 +208,7 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rw_lock_write_lock(&fInode->Lock());
|
fInode->WriteLockInTransaction(*fTransaction);
|
||||||
*_inode = fInode;
|
*_inode = fInode;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@ -261,8 +260,6 @@ 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;
|
||||||
|
|
||||||
@ -305,7 +302,7 @@ bfs_inode::InitCheck(Volume *volume)
|
|||||||
if (Flags() & INODE_DELETED)
|
if (Flags() & INODE_DELETED)
|
||||||
return B_NOT_ALLOWED;
|
return B_NOT_ALLOWED;
|
||||||
|
|
||||||
// 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!
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@ -1059,7 +1056,8 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (attribute != NULL) {
|
if (attribute != NULL) {
|
||||||
if (rw_lock_write_lock(&attribute->Lock()) == B_OK) {
|
// TODO: we need to lock the inode in the transaction, see WriteAt()!
|
||||||
|
if (rw_lock_write_lock(&attribute->fLock) == B_OK) {
|
||||||
// Save the old attribute data (if this fails, oldLength will
|
// Save the old attribute data (if this fails, oldLength will
|
||||||
// reflect it)
|
// reflect it)
|
||||||
if (fVolume->CheckForLiveQuery(name) && attribute->Size() > 0) {
|
if (fVolume->CheckForLiveQuery(name) && attribute->Size() > 0) {
|
||||||
@ -1075,14 +1073,14 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
|
|||||||
|
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
// it does - remove its file
|
// it does - remove its file
|
||||||
rw_lock_write_unlock(&attribute->Lock());
|
rw_lock_write_unlock(&attribute->fLock);
|
||||||
status = _RemoveAttribute(transaction, name, false, NULL);
|
status = _RemoveAttribute(transaction, name, false, NULL);
|
||||||
} else {
|
} else {
|
||||||
// The attribute type might have been changed - we need to
|
// The attribute type might have been changed - we need to
|
||||||
// adopt the new one
|
// adopt the new one
|
||||||
attribute->Node().type = HOST_ENDIAN_TO_BFS_INT32(type);
|
attribute->Node().type = HOST_ENDIAN_TO_BFS_INT32(type);
|
||||||
status = attribute->WriteBack(transaction);
|
status = attribute->WriteBack(transaction);
|
||||||
rw_lock_write_unlock(&attribute->Lock());
|
rw_lock_write_unlock(&attribute->fLock);
|
||||||
|
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
status = attribute->WriteAt(transaction, pos, buffer,
|
status = attribute->WriteAt(transaction, pos, buffer,
|
||||||
@ -1167,7 +1165,7 @@ 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());
|
InodeReadLocker locker(attributes);
|
||||||
|
|
||||||
ino_t id;
|
ino_t id;
|
||||||
status = tree->Find((uint8 *)name, (uint16)strlen(name), &id);
|
status = tree->Find((uint8 *)name, (uint16)strlen(name), &id);
|
||||||
@ -1382,7 +1380,7 @@ Inode::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
|
|||||||
if (pos < 0)
|
if (pos < 0)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
ReadLocker locker(Lock());
|
InodeReadLocker locker(this);
|
||||||
|
|
||||||
if (pos >= Size() || length == 0) {
|
if (pos >= Size() || length == 0) {
|
||||||
*_length = 0;
|
*_length = 0;
|
||||||
@ -1399,13 +1397,11 @@ status_t
|
|||||||
Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
|
Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
|
||||||
size_t *_length)
|
size_t *_length)
|
||||||
{
|
{
|
||||||
WriteLocker locker(Lock());
|
InodeReadLocker locker(this);
|
||||||
if (!locker.IsLocked())
|
|
||||||
RETURN_ERROR(B_ERROR);
|
|
||||||
|
|
||||||
// update the last modification time in memory, it will be written
|
// update the last modification time in memory, it will be written
|
||||||
// back to the inode, and the index when the file is closed
|
// back to the inode, and the index when the file is closed
|
||||||
// ToDo: should update the internal last modified time only at this point!
|
// TODO: should update the internal last modified time only at this point!
|
||||||
Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL)
|
Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL)
|
||||||
<< INODE_TIME_SHIFT);
|
<< INODE_TIME_SHIFT);
|
||||||
|
|
||||||
@ -1427,7 +1423,12 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
|
|||||||
if (changeSize && !transaction.IsStarted())
|
if (changeSize && !transaction.IsStarted())
|
||||||
transaction.Start(fVolume, BlockNumber());
|
transaction.Start(fVolume, BlockNumber());
|
||||||
|
|
||||||
locker.Lock();
|
// TODO: we actually need to call WriteLockInTransaction() here, but we
|
||||||
|
// cannot do this with the current locking model (ie. file cache functions
|
||||||
|
// are not to be called with the inode lock held).
|
||||||
|
// But this cannot work anyway, since we hold the lock when calling
|
||||||
|
// file_cache_set_size(), too... (possible deadlock)
|
||||||
|
rw_lock_write_lock(&fLock);
|
||||||
|
|
||||||
if (pos + length > Size()) {
|
if (pos + length > Size()) {
|
||||||
// let's grow the data stream to the size needed
|
// let's grow the data stream to the size needed
|
||||||
@ -1453,7 +1454,7 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
|
|||||||
if (length == 0)
|
if (length == 0)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
locker.Unlock();
|
rw_lock_write_unlock(&fLock);
|
||||||
|
|
||||||
return file_cache_write(FileCache(), NULL, pos, buffer, _length);
|
return file_cache_write(FileCache(), NULL, pos, buffer, _length);
|
||||||
}
|
}
|
||||||
@ -2117,7 +2118,7 @@ Inode::Sync()
|
|||||||
if (IsSymLink() && (Flags() & INODE_LONG_SYMLINK) == 0)
|
if (IsSymLink() && (Flags() & INODE_LONG_SYMLINK) == 0)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
ReadLocker locker(Lock());
|
InodeReadLocker locker(this);
|
||||||
|
|
||||||
data_stream *data = &Node().data;
|
data_stream *data = &Node().data;
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
@ -2212,7 +2213,7 @@ 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());
|
WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
// does the file even exist?
|
// does the file even exist?
|
||||||
off_t id;
|
off_t id;
|
||||||
@ -2231,6 +2232,7 @@ Inode::Remove(Transaction &transaction, const char *name, ino_t *_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
T(Remove(inode, name));
|
T(Remove(inode, name));
|
||||||
|
inode->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
// Inode::IsContainer() is true also for indices (furthermore, the S_IFDIR
|
// Inode::IsContainer() is true also for indices (furthermore, the S_IFDIR
|
||||||
// bit is set for indices in BFS, not for attribute directories) - but you
|
// bit is set for indices in BFS, not for attribute directories) - but you
|
||||||
@ -2318,14 +2320,16 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
|
|||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteLocker locker(parent != NULL ? &parent->Lock() : NULL);
|
if (parent != NULL) {
|
||||||
// the parent directory is locked during the whole inode creation
|
// the parent directory is locked during the whole inode creation
|
||||||
|
parent->WriteLockInTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
if (parent != NULL && parent->IsDirectory()) {
|
if (parent != NULL && !volume->IsInitializing() && parent->IsContainer()) {
|
||||||
// don't create anything in removed directories
|
// don't create anything in removed directories
|
||||||
bool removed;
|
bool removed;
|
||||||
if (get_vnode_removed(volume->FSVolume(), parent->ID(), &removed)
|
if (get_vnode_removed(volume->FSVolume(), parent->ID(), &removed)
|
||||||
!= B_OK || removed) {
|
== B_OK && removed) {
|
||||||
RETURN_ERROR(B_ENTRY_NOT_FOUND);
|
RETURN_ERROR(B_ENTRY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2362,7 +2366,7 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
// truncate the existing file
|
// truncate the existing file
|
||||||
WriteLocker _(inode->Lock());
|
inode->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
status_t status = inode->SetFileSize(transaction, 0);
|
status_t status = inode->SetFileSize(transaction, 0);
|
||||||
if (status >= B_OK)
|
if (status >= B_OK)
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
#include "system_dependencies.h"
|
#include "system_dependencies.h"
|
||||||
|
|
||||||
#include "Volume.h"
|
#include "CachedBlock.h"
|
||||||
#include "Journal.h"
|
|
||||||
#include "Chain.h"
|
#include "Chain.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "CachedBlock.h"
|
#include "Journal.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
|
||||||
|
|
||||||
class BPlusTree;
|
class BPlusTree;
|
||||||
@ -20,67 +20,78 @@ class TreeIterator;
|
|||||||
class AttributeIterator;
|
class AttributeIterator;
|
||||||
class Index;
|
class Index;
|
||||||
class InodeAllocator;
|
class InodeAllocator;
|
||||||
|
class InodeReadLocker;
|
||||||
class NodeGetter;
|
class NodeGetter;
|
||||||
|
class Transaction;
|
||||||
|
|
||||||
enum inode_type {
|
enum inode_type {
|
||||||
S_DIRECTORY = S_IFDIR,
|
S_DIRECTORY = S_IFDIR,
|
||||||
S_FILE = S_IFREG,
|
S_FILE = S_IFREG,
|
||||||
S_SYMLINK = S_IFLNK,
|
S_SYMLINK = S_IFLNK,
|
||||||
|
|
||||||
S_INDEX_TYPES = (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX
|
S_INDEX_TYPES = (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
|
||||||
| S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX)
|
| S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX
|
||||||
|
| S_FLOAT_INDEX | S_DOUBLE_INDEX)
|
||||||
};
|
};
|
||||||
|
|
||||||
class Inode {
|
|
||||||
|
class Inode : public SinglyLinkedListLinkImpl<Inode> {
|
||||||
public:
|
public:
|
||||||
Inode(Volume* volume, ino_t id);
|
Inode(Volume* volume, ino_t id);
|
||||||
Inode(Volume *volume, Transaction &transaction, ino_t id,
|
Inode(Volume* volume, Transaction& transaction,
|
||||||
mode_t mode, block_run &run);
|
ino_t id, mode_t mode, block_run& run);
|
||||||
//Inode(CachedBlock *cached);
|
|
||||||
~Inode();
|
~Inode();
|
||||||
|
|
||||||
//bfs_inode *Node() const { return (bfs_inode *)fBlock; }
|
|
||||||
ino_t ID() const { return fID; }
|
ino_t ID() const { return fID; }
|
||||||
off_t BlockNumber() const { return fVolume->VnodeToBlock(fID); }
|
off_t BlockNumber() const
|
||||||
|
{ return fVolume->VnodeToBlock(fID); }
|
||||||
|
|
||||||
rw_lock& Lock() { return fLock; }
|
rw_lock& Lock() { return fLock; }
|
||||||
|
ReadLocker ReadLock() { return ReadLocker(fLock); }
|
||||||
|
void WriteLockInTransaction(Transaction& transaction)
|
||||||
|
{ transaction.AddInode(this); }
|
||||||
|
|
||||||
recursive_lock& SmallDataLock() { return fSmallDataLock; }
|
recursive_lock& SmallDataLock() { return fSmallDataLock; }
|
||||||
status_t WriteBack(Transaction& transaction);
|
status_t WriteBack(Transaction& transaction);
|
||||||
|
|
||||||
bool IsContainer() const
|
bool IsContainer() const
|
||||||
{ return S_ISDIR(Mode()); }
|
{ return S_ISDIR(Mode()); }
|
||||||
bool IsDirectory() const
|
bool IsDirectory() const
|
||||||
{ return (Mode() & (S_INDEX_DIR | S_ATTR_DIR | S_IFDIR))
|
{ return is_directory(Mode()); }
|
||||||
== S_IFDIR; }
|
|
||||||
bool IsIndex() const
|
bool IsIndex() const
|
||||||
{ return (Mode() & (S_INDEX_DIR | 0777)) == S_INDEX_DIR; }
|
{ return is_index(Mode()); }
|
||||||
// that's a stupid check, but AFAIK the only possible method...
|
|
||||||
|
|
||||||
bool IsAttributeDirectory() const
|
bool IsAttributeDirectory() const
|
||||||
{ return (Mode() & S_ATTR_DIR) != 0; }
|
{ return (Mode() & S_ATTR_DIR) != 0; }
|
||||||
bool IsAttribute() const
|
bool IsAttribute() const
|
||||||
{ return (Mode() & S_ATTR) != 0; }
|
{ return (Mode() & S_ATTR) != 0; }
|
||||||
bool IsFile() const
|
bool IsFile() const
|
||||||
{ return (Mode() & (S_IFMT | S_ATTR)) == S_FILE; }
|
{ return (Mode()
|
||||||
|
& (S_IFMT | S_ATTR)) == S_FILE; }
|
||||||
bool IsRegularNode() const
|
bool IsRegularNode() const
|
||||||
{ return (Mode() & (S_ATTR_DIR | S_INDEX_DIR | S_ATTR)) == 0; }
|
{ return (Mode()
|
||||||
|
& (S_ATTR_DIR | S_INDEX_DIR | S_ATTR))
|
||||||
|
== 0; }
|
||||||
// a regular node in the standard namespace
|
// a regular node in the standard namespace
|
||||||
// (i.e. not an index or attribute)
|
// (i.e. not an index or attribute)
|
||||||
bool IsSymLink() const { return S_ISLNK(Mode()); }
|
bool IsSymLink() const { return S_ISLNK(Mode()); }
|
||||||
bool HasUserAccessableStream() const { return IsFile(); }
|
bool HasUserAccessableStream() const { return IsFile(); }
|
||||||
// currently only files can be accessed with bfs_read()/bfs_write()
|
// currently only files can be accessed with
|
||||||
|
// bfs_read()/bfs_write()
|
||||||
|
|
||||||
bool IsDeleted() const { return (Flags() & INODE_DELETED) != 0; }
|
bool IsDeleted() const
|
||||||
|
{ return (Flags() & INODE_DELETED) != 0; }
|
||||||
|
|
||||||
mode_t Mode() const { return fNode.Mode(); }
|
mode_t Mode() const { return fNode.Mode(); }
|
||||||
uint32 Type() const { return fNode.Type(); }
|
uint32 Type() const { return fNode.Type(); }
|
||||||
int32 Flags() const { return fNode.Flags(); }
|
int32 Flags() const { return fNode.Flags(); }
|
||||||
|
|
||||||
off_t Size() const { return fNode.data.Size(); }
|
off_t Size() const { return fNode.data.Size(); }
|
||||||
off_t LastModified() const { return fNode.last_modified_time; }
|
off_t LastModified() const
|
||||||
|
{ return fNode.last_modified_time; }
|
||||||
|
|
||||||
const block_run &BlockRun() const { return fNode.inode_num; }
|
const block_run& BlockRun() const
|
||||||
|
{ return fNode.inode_num; }
|
||||||
block_run& Parent() { return fNode.parent; }
|
block_run& Parent() { return fNode.parent; }
|
||||||
block_run& Attributes() { return fNode.attributes; }
|
block_run& Attributes() { return fNode.attributes; }
|
||||||
|
|
||||||
@ -99,24 +110,28 @@ class Inode {
|
|||||||
status_t SetName(Transaction& transaction, const char* name);
|
status_t SetName(Transaction& transaction, const char* name);
|
||||||
|
|
||||||
// high-level attribute methods
|
// high-level attribute methods
|
||||||
status_t ReadAttribute(const char *name, int32 type, off_t pos,
|
status_t ReadAttribute(const char* name, int32 type,
|
||||||
uint8 *buffer, size_t *_length);
|
off_t pos, uint8* buffer, size_t* _length);
|
||||||
status_t WriteAttribute(Transaction &transaction, const char *name,
|
status_t WriteAttribute(Transaction& transaction,
|
||||||
int32 type, off_t pos, const uint8 *buffer, size_t *_length);
|
const char* name, int32 type, off_t pos,
|
||||||
status_t RemoveAttribute(Transaction &transaction, const char *name);
|
const uint8* buffer, size_t* _length);
|
||||||
|
status_t RemoveAttribute(Transaction& transaction,
|
||||||
|
const char* name);
|
||||||
|
|
||||||
// attribute methods
|
// attribute methods
|
||||||
status_t GetAttribute(const char* name, Inode** attribute);
|
status_t GetAttribute(const char* name, Inode** attribute);
|
||||||
void ReleaseAttribute(Inode* attribute);
|
void ReleaseAttribute(Inode* attribute);
|
||||||
status_t CreateAttribute(Transaction &transaction, const char *name,
|
status_t CreateAttribute(Transaction& transaction,
|
||||||
uint32 type, Inode **attribute);
|
const char* name, uint32 type,
|
||||||
|
Inode** attribute);
|
||||||
|
|
||||||
// for directories only:
|
// for directories only:
|
||||||
status_t GetTree(BPlusTree** _tree);
|
status_t GetTree(BPlusTree** _tree);
|
||||||
bool IsEmpty();
|
bool IsEmpty();
|
||||||
|
|
||||||
// manipulating the data stream
|
// manipulating the data stream
|
||||||
status_t FindBlockRun(off_t pos, block_run &run, off_t &offset);
|
status_t FindBlockRun(off_t pos, block_run& run,
|
||||||
|
off_t& offset);
|
||||||
|
|
||||||
status_t ReadAt(off_t pos, uint8* buffer, size_t* length);
|
status_t ReadAt(off_t pos, uint8* buffer, size_t* length);
|
||||||
status_t WriteAt(Transaction& transaction, off_t pos,
|
status_t WriteAt(Transaction& transaction, off_t pos,
|
||||||
@ -137,19 +152,19 @@ class Inode {
|
|||||||
status_t Remove(Transaction& transaction, const char* name,
|
status_t Remove(Transaction& transaction, const char* name,
|
||||||
ino_t* _id = NULL, bool isDirectory = false);
|
ino_t* _id = NULL, bool isDirectory = false);
|
||||||
static status_t Create(Transaction& transaction, Inode* parent,
|
static status_t Create(Transaction& transaction, Inode* parent,
|
||||||
const char *name, int32 mode, int openMode, uint32 type,
|
const char* name, int32 mode, int openMode,
|
||||||
bool *_created = NULL, ino_t *_id = NULL, Inode **_inode = NULL,
|
uint32 type, bool* _created = NULL,
|
||||||
fs_vnode_ops *vnodeOps = NULL, uint32 publishFlags = 0);
|
ino_t* _id = NULL, Inode** _inode = NULL,
|
||||||
|
fs_vnode_ops* vnodeOps = NULL,
|
||||||
|
uint32 publishFlags = 0);
|
||||||
|
|
||||||
// index maintaining helper
|
// index maintaining helper
|
||||||
void UpdateOldSize()
|
void UpdateOldSize() { fOldSize = Size(); }
|
||||||
{ fOldSize = Size(); }
|
|
||||||
void UpdateOldLastModified()
|
void UpdateOldLastModified()
|
||||||
{ fOldLastModified = Node().LastModifiedTime(); }
|
{ fOldLastModified
|
||||||
off_t OldSize()
|
= Node().LastModifiedTime(); }
|
||||||
{ return fOldSize; }
|
off_t OldSize() { return fOldSize; }
|
||||||
off_t OldLastModified()
|
off_t OldLastModified() { return fOldLastModified; }
|
||||||
{ return fOldLastModified; }
|
|
||||||
|
|
||||||
// file cache
|
// file cache
|
||||||
void* FileCache() const { return fCache; }
|
void* FileCache() const { return fCache; }
|
||||||
@ -157,37 +172,51 @@ class Inode {
|
|||||||
void* Map() const { return fMap; }
|
void* Map() const { return fMap; }
|
||||||
void SetMap(void* map) { fMap = map; }
|
void SetMap(void* map) { fMap = map; }
|
||||||
|
|
||||||
|
#if _KERNEL_MODE && KDEBUG
|
||||||
|
void AssertReadLocked()
|
||||||
|
{ ASSERT_READ_LOCKED_RW_LOCK(&fLock); }
|
||||||
|
void AssertWriteLocked()
|
||||||
|
{ ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); }
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Inode(const Inode &);
|
Inode(const Inode& other);
|
||||||
Inode &operator=(const Inode &);
|
Inode& operator=(const Inode& other);
|
||||||
// no implementation
|
// no implementation
|
||||||
|
|
||||||
friend class AttributeIterator;
|
friend class AttributeIterator;
|
||||||
friend class InodeAllocator;
|
friend class InodeAllocator;
|
||||||
|
friend class InodeReadLocker;
|
||||||
|
friend class Transaction;
|
||||||
|
|
||||||
// small_data access methods
|
// small_data access methods
|
||||||
status_t _MakeSpaceForSmallData(Transaction& transaction,
|
status_t _MakeSpaceForSmallData(Transaction& transaction,
|
||||||
bfs_inode *node, const char *name, int32 length);
|
bfs_inode* node, const char* name,
|
||||||
status_t _RemoveSmallData(Transaction &transaction, NodeGetter &node,
|
int32 length);
|
||||||
const char *name);
|
status_t _RemoveSmallData(Transaction& transaction,
|
||||||
status_t _AddSmallData(Transaction &transaction, NodeGetter &node,
|
NodeGetter& node, const char* name);
|
||||||
const char *name, uint32 type, const uint8 *data, size_t length,
|
status_t _AddSmallData(Transaction& transaction,
|
||||||
|
NodeGetter& node, const char* name, uint32 type,
|
||||||
|
const uint8* data, size_t length,
|
||||||
bool force = false);
|
bool force = false);
|
||||||
status_t _GetNextSmallData(bfs_inode* node,
|
status_t _GetNextSmallData(bfs_inode* node,
|
||||||
small_data** _smallData) const;
|
small_data** _smallData) const;
|
||||||
status_t _RemoveSmallData(bfs_inode* node, small_data* item,
|
status_t _RemoveSmallData(bfs_inode* node, small_data* item,
|
||||||
int32 index);
|
int32 index);
|
||||||
status_t _RemoveAttribute(Transaction &transaction, const char *name,
|
status_t _RemoveAttribute(Transaction& transaction,
|
||||||
bool hasIndex, Index *index);
|
const char* name, bool hasIndex, Index* index);
|
||||||
|
|
||||||
void _AddIterator(AttributeIterator* iterator);
|
void _AddIterator(AttributeIterator* iterator);
|
||||||
void _RemoveIterator(AttributeIterator* iterator);
|
void _RemoveIterator(AttributeIterator* iterator);
|
||||||
|
|
||||||
status_t _FreeStaticStreamArray(Transaction &transaction, int32 level,
|
status_t _FreeStaticStreamArray(Transaction& transaction,
|
||||||
block_run run, off_t size, off_t offset, off_t &max);
|
int32 level, block_run run, off_t size,
|
||||||
status_t _FreeStreamArray(Transaction &transaction, block_run *array,
|
off_t offset, off_t& max);
|
||||||
uint32 arrayLength, off_t size, off_t &offset, off_t &max);
|
status_t _FreeStreamArray(Transaction& transaction,
|
||||||
status_t _AllocateBlockArray(Transaction &transaction, block_run &run);
|
block_run* array, uint32 arrayLength,
|
||||||
|
off_t size, off_t& offset, off_t& max);
|
||||||
|
status_t _AllocateBlockArray(Transaction& transaction,
|
||||||
|
block_run& run);
|
||||||
status_t _GrowStream(Transaction& transaction, off_t size);
|
status_t _GrowStream(Transaction& transaction, off_t size);
|
||||||
status_t _ShrinkStream(Transaction& transaction, off_t size);
|
status_t _ShrinkStream(Transaction& transaction, off_t size);
|
||||||
|
|
||||||
@ -210,6 +239,41 @@ class Inode {
|
|||||||
Chain<AttributeIterator> fIterators;
|
Chain<AttributeIterator> fIterators;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if _KERNEL_MODE && KDEBUG
|
||||||
|
# define ASSERT_READ_LOCKED_INODE(inode) inode->AssertReadLocked()
|
||||||
|
# define ASSERT_WRITE_LOCKED_INODE(inode) inode->AssertWriteLocked()
|
||||||
|
#else
|
||||||
|
# define ASSERT_READ_LOCKED_INODE(inode)
|
||||||
|
# define ASSERT_WRITE_LOCKED_INODE(inode)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class InodeReadLocker {
|
||||||
|
public:
|
||||||
|
InodeReadLocker(Inode* inode)
|
||||||
|
:
|
||||||
|
fLock(&inode->fLock)
|
||||||
|
{
|
||||||
|
rw_lock_read_lock(fLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
~InodeReadLocker()
|
||||||
|
{
|
||||||
|
if (fLock != NULL)
|
||||||
|
rw_lock_read_unlock(fLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unlock()
|
||||||
|
{
|
||||||
|
if (fLock != NULL) {
|
||||||
|
rw_lock_read_unlock(fLock);
|
||||||
|
fLock = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
rw_lock* fLock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class NodeGetter : public CachedBlock {
|
class NodeGetter : public CachedBlock {
|
||||||
public:
|
public:
|
||||||
@ -235,8 +299,7 @@ class NodeGetter : public CachedBlock {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const bfs_inode *
|
const bfs_inode* SetToNode(const Inode* inode)
|
||||||
SetToNode(const Inode *inode)
|
|
||||||
{
|
{
|
||||||
return (const bfs_inode*)SetTo(fVolume->VnodeToBlock(inode->ID()));
|
return (const bfs_inode*)SetTo(fVolume->VnodeToBlock(inode->ID()));
|
||||||
}
|
}
|
||||||
@ -327,38 +390,22 @@ class AttributeIterator {
|
|||||||
~AttributeIterator();
|
~AttributeIterator();
|
||||||
|
|
||||||
status_t Rewind();
|
status_t Rewind();
|
||||||
status_t GetNext(char *name, size_t *length, uint32 *type, ino_t *id);
|
status_t GetNext(char* name, size_t* length, uint32* type,
|
||||||
|
ino_t* id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Chain<AttributeIterator>;
|
friend class Chain<AttributeIterator>;
|
||||||
friend class Inode;
|
friend class Inode;
|
||||||
|
|
||||||
void Update(uint16 index, int8 change);
|
void Update(uint16 index, int8 change);
|
||||||
AttributeIterator *fNext;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
AttributeIterator* fNext;
|
||||||
int32 fCurrentSmallData;
|
int32 fCurrentSmallData;
|
||||||
Inode *fInode, *fAttributes;
|
Inode* fInode;
|
||||||
|
Inode* fAttributes;
|
||||||
TreeIterator* fIterator;
|
TreeIterator* fIterator;
|
||||||
void* fBuffer;
|
void* fBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif // INODE_H
|
||||||
/*!
|
|
||||||
Converts the open mode, the open flags given to bfs_open(), into
|
|
||||||
access modes, e.g. since O_RDONLY requires read access to the
|
|
||||||
file, it will be converted to R_OK.
|
|
||||||
*/
|
|
||||||
inline int
|
|
||||||
openModeToAccess(int openMode)
|
|
||||||
{
|
|
||||||
openMode &= O_RWMASK;
|
|
||||||
if (openMode == O_RDONLY)
|
|
||||||
return R_OK;
|
|
||||||
else if (openMode == O_WRONLY)
|
|
||||||
return W_OK;
|
|
||||||
|
|
||||||
return R_OK | W_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* INODE_H */
|
|
||||||
|
@ -7,8 +7,9 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Journal.h"
|
#include "Journal.h"
|
||||||
#include "Inode.h"
|
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
|
#include "Inode.h"
|
||||||
|
|
||||||
|
|
||||||
struct run_array {
|
struct run_array {
|
||||||
@ -668,7 +669,7 @@ Journal::_TransactionIdle(int32 transactionID, int32 event, void *_journal)
|
|||||||
status_t
|
status_t
|
||||||
Journal::_WriteTransactionToLog()
|
Journal::_WriteTransactionToLog()
|
||||||
{
|
{
|
||||||
// ToDo: in case of a failure, we need a backup plan like writing all
|
// TODO: in case of a failure, we need a backup plan like writing all
|
||||||
// changed blocks back to disk immediately (hello disk corruption!)
|
// changed blocks back to disk immediately (hello disk corruption!)
|
||||||
|
|
||||||
bool detached = false;
|
bool detached = false;
|
||||||
@ -746,7 +747,7 @@ Journal::_WriteTransactionToLog()
|
|||||||
|
|
||||||
iovec *vecs = (iovec *)malloc(sizeof(iovec) * maxVecs);
|
iovec *vecs = (iovec *)malloc(sizeof(iovec) * maxVecs);
|
||||||
if (vecs == NULL) {
|
if (vecs == NULL) {
|
||||||
// ToDo: write back log entries directly?
|
// TODO: write back log entries directly?
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,7 +920,7 @@ Journal::Lock(Transaction *owner)
|
|||||||
|
|
||||||
fOwner = owner;
|
fOwner = owner;
|
||||||
|
|
||||||
// ToDo: we need a way to find out how big the current transaction is;
|
// TODO: we need a way to find out how big the current transaction is;
|
||||||
// we need to be able to either detach the latest sub transaction on
|
// we need to be able to either detach the latest sub transaction on
|
||||||
// demand, as well as having some kind of fall back plan in case the
|
// demand, as well as having some kind of fall back plan in case the
|
||||||
// sub transaction itself grows bigger than the log.
|
// sub transaction itself grows bigger than the log.
|
||||||
@ -950,7 +951,7 @@ Journal::Unlock(Transaction *owner, bool success)
|
|||||||
{
|
{
|
||||||
if (recursive_lock_get_recursion(&fLock) == 1) {
|
if (recursive_lock_get_recursion(&fLock) == 1) {
|
||||||
// we only end the transaction if we would really unlock it
|
// we only end the transaction if we would really unlock it
|
||||||
// ToDo: what about failing transactions that do not unlock?
|
// TODO: what about failing transactions that do not unlock?
|
||||||
_TransactionDone(success);
|
_TransactionDone(success);
|
||||||
|
|
||||||
fTimestamp = system_time();
|
fTimestamp = system_time();
|
||||||
@ -1065,3 +1066,35 @@ Transaction::Start(Volume *volume, off_t refBlock)
|
|||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Transaction::AddInode(Inode* inode)
|
||||||
|
{
|
||||||
|
if (GetVolume()->IsInitializing())
|
||||||
|
return;
|
||||||
|
if (fJournal == NULL)
|
||||||
|
panic("Transaction is not running!");
|
||||||
|
|
||||||
|
InodeList::Iterator iterator = fLockedInodes.GetIterator();
|
||||||
|
while (iterator.HasNext()) {
|
||||||
|
if (iterator.Next() == inode) {
|
||||||
|
//dprintf(" inode %Ld already in transaction\n", inode->ID());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acquire_vnode(GetVolume()->FSVolume(), inode->ID());
|
||||||
|
rw_lock_write_lock(&inode->fLock);
|
||||||
|
fLockedInodes.Add(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Transaction::_UnlockInodes()
|
||||||
|
{
|
||||||
|
while (Inode* inode = fLockedInodes.RemoveHead()) {
|
||||||
|
rw_lock_write_unlock(&inode->fLock);
|
||||||
|
put_vnode(GetVolume()->FSVolume(), inode->ID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,10 @@
|
|||||||
|
|
||||||
|
|
||||||
struct run_array;
|
struct run_array;
|
||||||
|
class Inode;
|
||||||
class LogEntry;
|
class LogEntry;
|
||||||
typedef DoublyLinkedList<LogEntry> LogEntryList;
|
typedef DoublyLinkedList<LogEntry> LogEntryList;
|
||||||
|
typedef SinglyLinkedList<Inode> InodeList;
|
||||||
|
|
||||||
|
|
||||||
// Locking policy in BFS: if you need both, the volume lock and the
|
// Locking policy in BFS: if you need both, the volume lock and the
|
||||||
@ -118,23 +120,25 @@ class Transaction {
|
|||||||
|
|
||||||
~Transaction()
|
~Transaction()
|
||||||
{
|
{
|
||||||
if (fJournal)
|
if (fJournal != NULL) {
|
||||||
fJournal->Unlock(this, false);
|
fJournal->Unlock(this, false);
|
||||||
|
_UnlockInodes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t Start(Volume *volume, off_t refBlock);
|
status_t Start(Volume *volume, off_t refBlock);
|
||||||
bool IsStarted() const { return fJournal != NULL; }
|
bool IsStarted() const { return fJournal != NULL; }
|
||||||
|
|
||||||
void
|
void Done()
|
||||||
Done()
|
|
||||||
{
|
{
|
||||||
if (fJournal != NULL)
|
if (fJournal != NULL) {
|
||||||
fJournal->Unlock(this, true);
|
fJournal->Unlock(this, true);
|
||||||
|
_UnlockInodes();
|
||||||
|
}
|
||||||
fJournal = NULL;
|
fJournal = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool HasParent()
|
||||||
HasParent()
|
|
||||||
{
|
{
|
||||||
if (fJournal != NULL)
|
if (fJournal != NULL)
|
||||||
return fJournal->CurrentTransaction() == this;
|
return fJournal->CurrentTransaction() == this;
|
||||||
@ -142,8 +146,7 @@ class Transaction {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t
|
status_t WriteBlocks(off_t blockNumber, const uint8 *buffer,
|
||||||
WriteBlocks(off_t blockNumber, const uint8 *buffer,
|
|
||||||
size_t numBlocks = 1)
|
size_t numBlocks = 1)
|
||||||
{
|
{
|
||||||
if (fJournal == NULL)
|
if (fJournal == NULL)
|
||||||
@ -172,12 +175,17 @@ class Transaction {
|
|||||||
int32 ID() const
|
int32 ID() const
|
||||||
{ return fJournal->TransactionID(); }
|
{ return fJournal->TransactionID(); }
|
||||||
|
|
||||||
|
void AddInode(Inode* inode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Transaction(const Transaction &);
|
Transaction(const Transaction &);
|
||||||
Transaction &operator=(const Transaction &);
|
Transaction &operator=(const Transaction &);
|
||||||
// no implementation
|
// no implementation
|
||||||
|
|
||||||
|
void _UnlockInodes();
|
||||||
|
|
||||||
Journal* fJournal;
|
Journal* fJournal;
|
||||||
|
InodeList fLockedInodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef BFS_DEBUGGER_COMMANDS
|
#ifdef BFS_DEBUGGER_COMMANDS
|
||||||
|
@ -58,4 +58,21 @@ is_directory(int mode)
|
|||||||
return (mode & (S_INDEX_DIR | S_ATTR_DIR | S_IFDIR)) == S_IFDIR;
|
return (mode & (S_INDEX_DIR | S_ATTR_DIR | S_IFDIR)) == S_IFDIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* UTILITY_H */
|
|
||||||
|
/*! Converts the open mode, the open flags given to bfs_open(), into
|
||||||
|
access modes, e.g. since O_RDONLY requires read access to the
|
||||||
|
file, it will be converted to R_OK.
|
||||||
|
*/
|
||||||
|
inline int
|
||||||
|
openModeToAccess(int openMode)
|
||||||
|
{
|
||||||
|
openMode &= O_RWMASK;
|
||||||
|
if (openMode == O_RDONLY)
|
||||||
|
return R_OK;
|
||||||
|
if (openMode == O_WRONLY)
|
||||||
|
return W_OK;
|
||||||
|
|
||||||
|
return R_OK | W_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UTILITY_H
|
||||||
|
@ -34,6 +34,8 @@ class Volume {
|
|||||||
status_t Initialize(int fd, const char *name,
|
status_t Initialize(int fd, const char *name,
|
||||||
uint32 blockSize, uint32 flags);
|
uint32 blockSize, uint32 flags);
|
||||||
|
|
||||||
|
bool IsInitializing() const { return fVolume == NULL; }
|
||||||
|
|
||||||
bool IsValidSuperBlock();
|
bool IsValidSuperBlock();
|
||||||
bool IsReadOnly() const;
|
bool IsReadOnly() const;
|
||||||
void Panic();
|
void Panic();
|
||||||
|
@ -24,30 +24,6 @@ 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);
|
||||||
|
|
||||||
|
|
||||||
@ -78,6 +54,28 @@ fill_stat_buffer(Inode *inode, struct stat &stat)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! bfs_io() callback hook
|
||||||
|
static status_t
|
||||||
|
iterative_io_get_vecs_hook(void* cookie, io_request *request, off_t offset,
|
||||||
|
size_t size, struct file_io_vec *vecs, size_t *_count)
|
||||||
|
{
|
||||||
|
Inode *inode = (Inode*)cookie;
|
||||||
|
return file_map_translate(inode->Map(), offset, size, vecs, _count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! bfs_io() callback hook
|
||||||
|
static status_t
|
||||||
|
iterative_io_finished_hook(void *cookie, io_request *request, status_t status,
|
||||||
|
bool partialTransfer, size_t bytesTransferred)
|
||||||
|
{
|
||||||
|
Inode *inode = (Inode*)cookie;
|
||||||
|
|
||||||
|
rw_lock_read_unlock(&inode->Lock());
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - Scanning
|
// #pragma mark - Scanning
|
||||||
|
|
||||||
|
|
||||||
@ -299,7 +297,7 @@ bfs_put_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
|
|||||||
if (inode->TrimPreallocation(transaction) == B_OK)
|
if (inode->TrimPreallocation(transaction) == B_OK)
|
||||||
transaction.Done();
|
transaction.Done();
|
||||||
else if (transaction.HasParent()) {
|
else if (transaction.HasParent()) {
|
||||||
// ToDo: for now, we don't let sub-transactions fail
|
// TODO: for now, we don't let sub-transactions fail
|
||||||
transaction.Done();
|
transaction.Done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,7 +335,7 @@ bfs_remove_vnode(fs_volume *_volume, fs_vnode *_node, bool reenter)
|
|||||||
|
|
||||||
delete inode;
|
delete inode;
|
||||||
} else if (transaction.HasParent()) {
|
} else if (transaction.HasParent()) {
|
||||||
// ToDo: for now, we don't let sub-transactions fail
|
// TODO: for now, we don't let sub-transactions fail
|
||||||
transaction.Done();
|
transaction.Done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +361,7 @@ bfs_read_pages(fs_volume *_volume, fs_vnode *_node, void *_cookie,
|
|||||||
if (inode->FileCache() == NULL)
|
if (inode->FileCache() == NULL)
|
||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
rw_lock_read_lock(&inode->Lock());
|
InodeReadLocker _(inode);
|
||||||
|
|
||||||
uint32 vecIndex = 0;
|
uint32 vecIndex = 0;
|
||||||
size_t vecOffset = 0;
|
size_t vecOffset = 0;
|
||||||
@ -391,8 +389,6 @@ bfs_read_pages(fs_volume *_volume, fs_vnode *_node, void *_cookie,
|
|||||||
bytesLeft -= bytes;
|
bytesLeft -= bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
rw_lock_read_unlock(&inode->Lock());
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,7 +406,7 @@ bfs_write_pages(fs_volume *_volume, fs_vnode *_node, void *_cookie,
|
|||||||
if (inode->FileCache() == NULL)
|
if (inode->FileCache() == NULL)
|
||||||
RETURN_ERROR(B_BAD_VALUE);
|
RETURN_ERROR(B_BAD_VALUE);
|
||||||
|
|
||||||
rw_lock_read_lock(&inode->Lock());
|
InodeReadLocker _(inode);
|
||||||
|
|
||||||
uint32 vecIndex = 0;
|
uint32 vecIndex = 0;
|
||||||
size_t vecOffset = 0;
|
size_t vecOffset = 0;
|
||||||
@ -438,33 +434,10 @@ bfs_write_pages(fs_volume *_volume, fs_vnode *_node, void *_cookie,
|
|||||||
bytesLeft -= bytes;
|
bytesLeft -= bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
rw_lock_read_unlock(&inode->Lock());
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
|
||||||
bfs_iterative_io_get_vecs(void* cookie, io_request *request, off_t offset,
|
|
||||||
size_t size, struct file_io_vec *vecs, size_t *_count)
|
|
||||||
{
|
|
||||||
Inode *inode = (Inode*)cookie;
|
|
||||||
return file_map_translate(inode->Map(), offset, size, vecs, _count);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
|
||||||
bfs_iterative_io_finished(void *cookie, io_request *request, status_t status,
|
|
||||||
bool partialTransfer, size_t bytesTransferred)
|
|
||||||
{
|
|
||||||
Inode *inode = (Inode*)cookie;
|
|
||||||
|
|
||||||
rw_lock_read_unlock(&inode->Lock());
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
bfs_io(fs_volume *_volume, fs_vnode *_node, void *_cookie, io_request *request)
|
bfs_io(fs_volume *_volume, fs_vnode *_node, void *_cookie, io_request *request)
|
||||||
{
|
{
|
||||||
@ -481,7 +454,7 @@ bfs_io(fs_volume *_volume, fs_vnode *_node, void *_cookie, io_request *request)
|
|||||||
rw_lock_read_lock(&inode->Lock());
|
rw_lock_read_lock(&inode->Lock());
|
||||||
|
|
||||||
return do_iterative_fd_io(volume->Device(), request,
|
return do_iterative_fd_io(volume->Device(), request,
|
||||||
bfs_iterative_io_get_vecs, bfs_iterative_io_finished, inode);
|
iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -546,6 +519,8 @@ bfs_lookup(fs_volume *_volume, fs_vnode *_directory, const char *file,
|
|||||||
Volume *volume = (Volume *)_volume->private_volume;
|
Volume *volume = (Volume *)_volume->private_volume;
|
||||||
Inode *directory = (Inode *)_directory->private_node;
|
Inode *directory = (Inode *)_directory->private_node;
|
||||||
|
|
||||||
|
InodeReadLocker locker(directory);
|
||||||
|
|
||||||
// check access permissions
|
// check access permissions
|
||||||
status_t status = directory->CheckPermissions(X_OK);
|
status_t status = directory->CheckPermissions(X_OK);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
@ -555,14 +530,14 @@ 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)));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
locker.Unlock();
|
||||||
|
|
||||||
Inode *inode;
|
Inode *inode;
|
||||||
status = get_vnode(volume->FSVolume(), *_vnodeID, (void **)&inode);
|
status = get_vnode(volume->FSVolume(), *_vnodeID, (void **)&inode);
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
@ -729,10 +704,7 @@ bfs_write_stat(fs_volume *_volume, fs_vnode *_node, const struct stat *stat,
|
|||||||
RETURN_ERROR(status);
|
RETURN_ERROR(status);
|
||||||
|
|
||||||
Transaction transaction(volume, inode->BlockNumber());
|
Transaction transaction(volume, inode->BlockNumber());
|
||||||
|
inode->WriteLockInTransaction(transaction);
|
||||||
WriteLocker locker(inode->Lock());
|
|
||||||
if (!locker.IsLocked())
|
|
||||||
RETURN_ERROR(B_ERROR);
|
|
||||||
|
|
||||||
bfs_inode &node = inode->Node();
|
bfs_inode &node = inode->Node();
|
||||||
|
|
||||||
@ -972,7 +944,10 @@ bfs_rename(fs_volume *_volume, fs_vnode *_oldDir, const char *oldName,
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
Transaction transaction(volume, oldDirectory->BlockNumber());
|
Transaction transaction(volume, oldDirectory->BlockNumber());
|
||||||
MultiWriteLocker locker(oldDirectory, newDirectory);
|
|
||||||
|
oldDirectory->WriteLockInTransaction(transaction);
|
||||||
|
if (oldDirectory != newDirectory)
|
||||||
|
newDirectory->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
// 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);
|
||||||
@ -1069,7 +1044,7 @@ bfs_rename(fs_volume *_volume, fs_vnode *_oldDir, const char *oldName,
|
|||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
WriteLocker _(inode->Lock());
|
inode->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
// update the name only when they differ
|
// update the name only when they differ
|
||||||
bool nameUpdated = false;
|
bool nameUpdated = false;
|
||||||
@ -1165,7 +1140,7 @@ bfs_open(fs_volume *_volume, fs_vnode *_node, int openMode, void **_cookie)
|
|||||||
return B_IS_A_DIRECTORY;
|
return B_IS_A_DIRECTORY;
|
||||||
|
|
||||||
Transaction transaction(volume, inode->BlockNumber());
|
Transaction transaction(volume, inode->BlockNumber());
|
||||||
WriteLocker locker(inode->Lock());
|
inode->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
status_t status = inode->SetFileSize(transaction, 0);
|
status_t status = inode->SetFileSize(transaction, 0);
|
||||||
if (status >= B_OK)
|
if (status >= B_OK)
|
||||||
@ -1232,7 +1207,7 @@ bfs_write(fs_volume *_volume, fs_vnode *_node, void *_cookie, off_t pos,
|
|||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
transaction.Done();
|
transaction.Done();
|
||||||
|
|
||||||
ReadLocker locker(inode->Lock());
|
InodeReadLocker locker(inode);
|
||||||
|
|
||||||
// periodically notify if the file size has changed
|
// periodically notify if the file size has changed
|
||||||
// TODO: should we better test for a change in the last_modified time only?
|
// TODO: should we better test for a change in the last_modified time only?
|
||||||
@ -1271,7 +1246,7 @@ bfs_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
|
|||||||
bool needsTrimming = false;
|
bool needsTrimming = false;
|
||||||
|
|
||||||
if (!volume->IsReadOnly()) {
|
if (!volume->IsReadOnly()) {
|
||||||
ReadLocker locker(inode->Lock());
|
InodeReadLocker locker(inode);
|
||||||
needsTrimming = inode->NeedsTrimming();
|
needsTrimming = inode->NeedsTrimming();
|
||||||
|
|
||||||
if ((cookie->open_mode & O_RWMASK) != 0
|
if ((cookie->open_mode & O_RWMASK) != 0
|
||||||
@ -1287,7 +1262,7 @@ bfs_free_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
|
|||||||
status_t status = transaction.IsStarted() ? B_OK : B_ERROR;
|
status_t status = transaction.IsStarted() ? B_OK : B_ERROR;
|
||||||
|
|
||||||
if (status == B_OK) {
|
if (status == B_OK) {
|
||||||
WriteLocker locker(inode->Lock());
|
inode->WriteLockInTransaction(transaction);
|
||||||
|
|
||||||
// trim the preallocated blocks and update the size,
|
// trim the preallocated blocks and update the size,
|
||||||
// and last_modified indices if needed
|
// and last_modified indices if needed
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <util/AutoLock.h>
|
#include <util/AutoLock.h>
|
||||||
#include <util/DoublyLinkedList.h>
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
#include <util/SinglyLinkedList.h>
|
||||||
#include <util/Stack.h>
|
#include <util/Stack.h>
|
||||||
|
|
||||||
#include <ByteOrder.h>
|
#include <ByteOrder.h>
|
||||||
|
Loading…
Reference in New Issue
Block a user