* 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:
Axel Dörfler 2008-08-01 12:28:28 +00:00
parent ebaab6d5f4
commit 2e3477e3d8
11 changed files with 474 additions and 387 deletions

View File

@ -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)

View File

@ -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();

View File

@ -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,

View File

@ -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)

View File

@ -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,229 +20,292 @@ 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 {
public:
Inode(Volume *volume, ino_t id);
Inode(Volume *volume, Transaction &transaction, ino_t id,
mode_t mode, block_run &run);
//Inode(CachedBlock *cached);
~Inode();
//bfs_inode *Node() const { return (bfs_inode *)fBlock; } class Inode : public SinglyLinkedListLinkImpl<Inode> {
ino_t ID() const { return fID; } public:
off_t BlockNumber() const { return fVolume->VnodeToBlock(fID); } Inode(Volume* volume, ino_t id);
Inode(Volume* volume, Transaction& transaction,
ino_t id, mode_t mode, block_run& run);
~Inode();
rw_lock &Lock() { return fLock; } ino_t ID() const { return fID; }
recursive_lock &SmallDataLock() { return fSmallDataLock; } off_t BlockNumber() const
status_t WriteBack(Transaction &transaction); { return fVolume->VnodeToBlock(fID); }
bool IsContainer() const rw_lock& Lock() { return fLock; }
{ return S_ISDIR(Mode()); } ReadLocker ReadLock() { return ReadLocker(fLock); }
bool IsDirectory() const void WriteLockInTransaction(Transaction& transaction)
{ return (Mode() & (S_INDEX_DIR | S_ATTR_DIR | S_IFDIR)) { transaction.AddInode(this); }
== S_IFDIR; }
bool IsIndex() const
{ return (Mode() & (S_INDEX_DIR | 0777)) == S_INDEX_DIR; }
// that's a stupid check, but AFAIK the only possible method...
bool IsAttributeDirectory() const recursive_lock& SmallDataLock() { return fSmallDataLock; }
{ return (Mode() & S_ATTR_DIR) != 0; } status_t WriteBack(Transaction& transaction);
bool IsAttribute() const
{ return (Mode() & S_ATTR) != 0; }
bool IsFile() const
{ return (Mode() & (S_IFMT | S_ATTR)) == S_FILE; }
bool IsRegularNode() const
{ return (Mode() & (S_ATTR_DIR | S_INDEX_DIR | S_ATTR)) == 0; }
// a regular node in the standard namespace
// (i.e. not an index or attribute)
bool IsSymLink() const { return S_ISLNK(Mode()); }
bool HasUserAccessableStream() const { return IsFile(); }
// currently only files can be accessed with bfs_read()/bfs_write()
bool IsDeleted() const { return (Flags() & INODE_DELETED) != 0; } bool IsContainer() const
{ return S_ISDIR(Mode()); }
bool IsDirectory() const
{ return is_directory(Mode()); }
bool IsIndex() const
{ return is_index(Mode()); }
mode_t Mode() const { return fNode.Mode(); } bool IsAttributeDirectory() const
uint32 Type() const { return fNode.Type(); } { return (Mode() & S_ATTR_DIR) != 0; }
int32 Flags() const { return fNode.Flags(); } bool IsAttribute() const
{ return (Mode() & S_ATTR) != 0; }
bool IsFile() const
{ return (Mode()
& (S_IFMT | S_ATTR)) == S_FILE; }
bool IsRegularNode() const
{ return (Mode()
& (S_ATTR_DIR | S_INDEX_DIR | S_ATTR))
== 0; }
// a regular node in the standard namespace
// (i.e. not an index or attribute)
bool IsSymLink() const { return S_ISLNK(Mode()); }
bool HasUserAccessableStream() const { return IsFile(); }
// currently only files can be accessed with
// bfs_read()/bfs_write()
off_t Size() const { return fNode.data.Size(); } bool IsDeleted() const
off_t LastModified() const { return fNode.last_modified_time; } { return (Flags() & INODE_DELETED) != 0; }
const block_run &BlockRun() const { return fNode.inode_num; } mode_t Mode() const { return fNode.Mode(); }
block_run &Parent() { return fNode.parent; } uint32 Type() const { return fNode.Type(); }
block_run &Attributes() { return fNode.attributes; } int32 Flags() const { return fNode.Flags(); }
Volume *GetVolume() const { return fVolume; } off_t Size() const { return fNode.data.Size(); }
off_t LastModified() const
{ return fNode.last_modified_time; }
status_t InitCheck(bool checkNode = true); const block_run& BlockRun() const
{ return fNode.inode_num; }
block_run& Parent() { return fNode.parent; }
block_run& Attributes() { return fNode.attributes; }
status_t CheckPermissions(int accessMode) const; Volume* GetVolume() const { return fVolume; }
// small_data access methods status_t InitCheck(bool checkNode = true);
small_data *FindSmallData(const bfs_inode *node,
const char *name) const;
const char *Name(const bfs_inode *node) const;
status_t GetName(char *buffer,
size_t bufferSize = B_FILE_NAME_LENGTH) const;
status_t SetName(Transaction &transaction, const char *name);
// high-level attribute methods status_t CheckPermissions(int accessMode) const;
status_t ReadAttribute(const char *name, int32 type, off_t pos,
uint8 *buffer, size_t *_length);
status_t WriteAttribute(Transaction &transaction, const char *name,
int32 type, off_t pos, const uint8 *buffer, size_t *_length);
status_t RemoveAttribute(Transaction &transaction, const char *name);
// attribute methods // small_data access methods
status_t GetAttribute(const char *name, Inode **attribute); small_data* FindSmallData(const bfs_inode* node,
void ReleaseAttribute(Inode *attribute); const char* name) const;
status_t CreateAttribute(Transaction &transaction, const char *name, const char* Name(const bfs_inode* node) const;
uint32 type, Inode **attribute); status_t GetName(char* buffer,
size_t bufferSize = B_FILE_NAME_LENGTH) const;
status_t SetName(Transaction& transaction, const char* name);
// for directories only: // high-level attribute methods
status_t GetTree(BPlusTree **_tree); status_t ReadAttribute(const char* name, int32 type,
bool IsEmpty(); off_t pos, uint8* buffer, size_t* _length);
status_t WriteAttribute(Transaction& transaction,
const char* name, int32 type, off_t pos,
const uint8* buffer, size_t* _length);
status_t RemoveAttribute(Transaction& transaction,
const char* name);
// manipulating the data stream // attribute methods
status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); status_t GetAttribute(const char* name, Inode** attribute);
void ReleaseAttribute(Inode* attribute);
status_t CreateAttribute(Transaction& transaction,
const char* name, uint32 type,
Inode** attribute);
status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); // for directories only:
status_t WriteAt(Transaction &transaction, off_t pos, status_t GetTree(BPlusTree** _tree);
const uint8 *buffer, size_t *length); bool IsEmpty();
status_t FillGapWithZeros(off_t oldSize, off_t newSize);
status_t SetFileSize(Transaction &transaction, off_t size); // manipulating the data stream
status_t Append(Transaction &transaction, off_t bytes); status_t FindBlockRun(off_t pos, block_run& run,
status_t TrimPreallocation(Transaction &transaction); off_t& offset);
bool NeedsTrimming();
status_t Free(Transaction &transaction); status_t ReadAt(off_t pos, uint8* buffer, size_t* length);
status_t Sync(); status_t WriteAt(Transaction& transaction, off_t pos,
const uint8* buffer, size_t* length);
status_t FillGapWithZeros(off_t oldSize, off_t newSize);
bfs_inode &Node() { return fNode; } status_t SetFileSize(Transaction& transaction, off_t size);
status_t Append(Transaction& transaction, off_t bytes);
status_t TrimPreallocation(Transaction& transaction);
bool NeedsTrimming();
// create/remove inodes status_t Free(Transaction& transaction);
status_t Remove(Transaction &transaction, const char *name, status_t Sync();
ino_t *_id = NULL, bool isDirectory = false);
static status_t Create(Transaction &transaction, Inode *parent,
const char *name, int32 mode, int openMode, uint32 type,
bool *_created = NULL, ino_t *_id = NULL, Inode **_inode = NULL,
fs_vnode_ops *vnodeOps = NULL, uint32 publishFlags = 0);
// index maintaining helper bfs_inode& Node() { return fNode; }
void UpdateOldSize()
{ fOldSize = Size(); }
void UpdateOldLastModified()
{ fOldLastModified = Node().LastModifiedTime(); }
off_t OldSize()
{ return fOldSize; }
off_t OldLastModified()
{ return fOldLastModified; }
// file cache // create/remove inodes
void *FileCache() const { return fCache; } status_t Remove(Transaction& transaction, const char* name,
void SetFileCache(void *cache) { fCache = cache; } ino_t* _id = NULL, bool isDirectory = false);
void *Map() const { return fMap; } static status_t Create(Transaction& transaction, Inode* parent,
void SetMap(void *map) { fMap = map; } const char* name, int32 mode, int openMode,
uint32 type, bool* _created = NULL,
ino_t* _id = NULL, Inode** _inode = NULL,
fs_vnode_ops* vnodeOps = NULL,
uint32 publishFlags = 0);
private: // index maintaining helper
Inode(const Inode &); void UpdateOldSize() { fOldSize = Size(); }
Inode &operator=(const Inode &); void UpdateOldLastModified()
// no implementation { fOldLastModified
= Node().LastModifiedTime(); }
off_t OldSize() { return fOldSize; }
off_t OldLastModified() { return fOldLastModified; }
friend class AttributeIterator; // file cache
friend class InodeAllocator; void* FileCache() const { return fCache; }
void SetFileCache(void* cache) { fCache = cache; }
void* Map() const { return fMap; }
void SetMap(void* map) { fMap = map; }
// small_data access methods #if _KERNEL_MODE && KDEBUG
status_t _MakeSpaceForSmallData(Transaction &transaction, void AssertReadLocked()
bfs_inode *node, const char *name, int32 length); { ASSERT_READ_LOCKED_RW_LOCK(&fLock); }
status_t _RemoveSmallData(Transaction &transaction, NodeGetter &node, void AssertWriteLocked()
const char *name); { ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); }
status_t _AddSmallData(Transaction &transaction, NodeGetter &node, #endif
const char *name, uint32 type, const uint8 *data, size_t length,
bool force = false);
status_t _GetNextSmallData(bfs_inode *node,
small_data **_smallData) const;
status_t _RemoveSmallData(bfs_inode *node, small_data *item,
int32 index);
status_t _RemoveAttribute(Transaction &transaction, const char *name,
bool hasIndex, Index *index);
void _AddIterator(AttributeIterator *iterator); private:
void _RemoveIterator(AttributeIterator *iterator); Inode(const Inode& other);
Inode& operator=(const Inode& other);
// no implementation
status_t _FreeStaticStreamArray(Transaction &transaction, int32 level, friend class AttributeIterator;
block_run run, off_t size, off_t offset, off_t &max); friend class InodeAllocator;
status_t _FreeStreamArray(Transaction &transaction, block_run *array, friend class InodeReadLocker;
uint32 arrayLength, off_t size, off_t &offset, off_t &max); friend class Transaction;
status_t _AllocateBlockArray(Transaction &transaction, block_run &run);
status_t _GrowStream(Transaction &transaction, off_t size);
status_t _ShrinkStream(Transaction &transaction, off_t size);
private: // small_data access methods
rw_lock fLock; status_t _MakeSpaceForSmallData(Transaction& transaction,
Volume *fVolume; bfs_inode* node, const char* name,
ino_t fID; int32 length);
BPlusTree *fTree; status_t _RemoveSmallData(Transaction& transaction,
Inode *fAttributes; NodeGetter& node, const char* name);
void *fCache; status_t _AddSmallData(Transaction& transaction,
void *fMap; NodeGetter& node, const char* name, uint32 type,
bfs_inode fNode; const uint8* data, size_t length,
bool force = false);
status_t _GetNextSmallData(bfs_inode* node,
small_data** _smallData) const;
status_t _RemoveSmallData(bfs_inode* node, small_data* item,
int32 index);
status_t _RemoveAttribute(Transaction& transaction,
const char* name, bool hasIndex, Index* index);
off_t fOldSize; void _AddIterator(AttributeIterator* iterator);
off_t fOldLastModified; void _RemoveIterator(AttributeIterator* iterator);
// we need those values to ensure we will remove
// the correct keys from the indices
mutable recursive_lock fSmallDataLock; status_t _FreeStaticStreamArray(Transaction& transaction,
Chain<AttributeIterator> fIterators; int32 level, block_run run, off_t size,
off_t offset, off_t& max);
status_t _FreeStreamArray(Transaction& transaction,
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 _ShrinkStream(Transaction& transaction, off_t size);
private:
rw_lock fLock;
Volume* fVolume;
ino_t fID;
BPlusTree* fTree;
Inode* fAttributes;
void* fCache;
void* fMap;
bfs_inode fNode;
off_t fOldSize;
off_t fOldLastModified;
// we need those values to ensure we will remove
// the correct keys from the indices
mutable recursive_lock fSmallDataLock;
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:
NodeGetter(Volume *volume) NodeGetter(Volume* volume)
: CachedBlock(volume) : CachedBlock(volume)
{ {
} }
NodeGetter(Volume *volume, const Inode *inode) NodeGetter(Volume* volume, const Inode* inode)
: CachedBlock(volume) : CachedBlock(volume)
{ {
SetTo(volume->VnodeToBlock(inode->ID())); SetTo(volume->VnodeToBlock(inode->ID()));
} }
NodeGetter(Volume *volume, Transaction &transaction, NodeGetter(Volume* volume, Transaction& transaction,
const Inode *inode, bool empty = false) const Inode* inode, bool empty = false)
: CachedBlock(volume) : CachedBlock(volume)
{ {
SetToWritable(transaction, volume->VnodeToBlock(inode->ID()), empty); SetToWritable(transaction, volume->VnodeToBlock(inode->ID()), empty);
} }
~NodeGetter() ~NodeGetter()
{ {
} }
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())); }
}
const bfs_inode *Node() const { return (const bfs_inode *)Block(); } const bfs_inode* Node() const { return (const bfs_inode*)Block(); }
bfs_inode *WritableNode() const { return (bfs_inode *)Block(); } bfs_inode* WritableNode() const { return (bfs_inode*)Block(); }
}; };
@ -251,114 +314,98 @@ class NodeGetter : public CachedBlock {
// readable in some cases // readable in some cases
class Vnode { class Vnode {
public: public:
Vnode(Volume* volume, ino_t id) Vnode(Volume* volume, ino_t id)
: :
fInode(NULL) fInode(NULL)
{ {
SetTo(volume, id); SetTo(volume, id);
} }
Vnode(Volume* volume, block_run run) Vnode(Volume* volume, block_run run)
: :
fInode(NULL) fInode(NULL)
{ {
SetTo(volume, run); SetTo(volume, run);
} }
Vnode() Vnode()
: :
fStatus(B_NO_INIT), fStatus(B_NO_INIT),
fInode(NULL) fInode(NULL)
{ {
} }
~Vnode() ~Vnode()
{ {
Unset(); Unset();
} }
status_t InitCheck() status_t InitCheck()
{ {
return fStatus; return fStatus;
} }
void Unset() void Unset()
{ {
if (fInode != NULL) { if (fInode != NULL) {
put_vnode(fInode->GetVolume()->FSVolume(), fInode->ID()); put_vnode(fInode->GetVolume()->FSVolume(), fInode->ID());
fInode = NULL;
fStatus = B_NO_INIT;
}
}
status_t SetTo(Volume* volume, ino_t id)
{
Unset();
return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode);
}
status_t SetTo(Volume* volume, block_run run)
{
return SetTo(volume, volume->ToVnode(run));
}
status_t Get(Inode** _inode)
{
*_inode = fInode;
return fStatus;
}
void Keep()
{
fInode = NULL; fInode = NULL;
fStatus = B_NO_INIT;
} }
}
private: status_t SetTo(Volume* volume, ino_t id)
status_t fStatus; {
Inode* fInode; Unset();
return fStatus = get_vnode(volume->FSVolume(), id, (void**)&fInode);
}
status_t SetTo(Volume* volume, block_run run)
{
return SetTo(volume, volume->ToVnode(run));
}
status_t Get(Inode** _inode)
{
*_inode = fInode;
return fStatus;
}
void Keep()
{
fInode = NULL;
}
private:
status_t fStatus;
Inode* fInode;
}; };
class AttributeIterator { class AttributeIterator {
public: public:
AttributeIterator(Inode *inode); AttributeIterator(Inode* inode);
~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:
int32 fCurrentSmallData; AttributeIterator* fNext;
Inode *fInode, *fAttributes; int32 fCurrentSmallData;
TreeIterator *fIterator; Inode* fInode;
void *fBuffer; Inode* fAttributes;
TreeIterator* fIterator;
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 */

View File

@ -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());
}
}

View File

@ -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
Journal *fJournal; void _UnlockInodes();
Journal* fJournal;
InodeList fLockedInodes;
}; };
#ifdef BFS_DEBUGGER_COMMANDS #ifdef BFS_DEBUGGER_COMMANDS

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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>