* Volume::Create{File,Directory,SymLink}():

- Simplified by using the new common _CreateNode(). Changed the constructors
    slightly -- the block index is now set via a separate SetBlockIndex()
    method.
  - The block allocated for the node was not cleared. At least for directories
    that is required, though.
* Added attribute support:
  - Refactored/extended {open,create}_file() and DirCookie a bit, so they can
    also be used for attributes/attribute directories.
  - Implemented all relevant attribute FS hooks.
  - Node::DeletingNode(): Changed interface (it creates the transaction(s) as
    needed, now and doesn't return the error code anymore. The base class
    implementation frees the attribute directory, the Directory implementation
    also unlinks all entries and frees the entry tree.
* checksumfs_write_stat(): B_STAT_MODE was not handled.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37626 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2010-07-20 17:13:34 +00:00
parent 7461b83d3b
commit 8794d8c48f
11 changed files with 1178 additions and 313 deletions

@ -72,6 +72,10 @@ public:
status_t RemoveEntry(const char* name,
Transaction& transaction);
status_t FreeTree(Transaction& transaction);
bool IsEmpty() const;
bool Check();
private:
@ -975,6 +979,71 @@ ASSERT(nextInfo.entryBlock.Check());
}
status_t
DirEntryTree::FreeTree(Transaction& transaction)
{
status_t error = _InitReadOnly();
if (error != B_OK)
RETURN_ERROR(error);
int32 depth = _Depth();
if (depth == 0)
return B_OK;
LevelInfo* infos = new(std::nothrow) LevelInfo[
kCheckSumFSMaxDirEntryTreeDepth + 1];
if (infos == NULL)
RETURN_ERROR(B_NO_MEMORY);
ArrayDeleter<LevelInfo> infosDeleter(infos);
infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize);
infos[0].index = 0;
// Iterate through the tree in post order. We don't touch the content of
// any block, we only free the blocks.
int32 level = 0;
while (true) {
LevelInfo& info = infos[level];
if (level == depth || info.index >= info.entryBlock.EntryCount()) {
// we're through with the block
if (level == 0)
break;
// free it
error = fDirectory->GetVolume()->GetBlockAllocator()->Free(
info.block.Index(), 1, transaction);
// continue with the next sibling branch
infos[--level].index++;
}
// descend to next level
uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index);
LevelInfo& nextInfo = infos[++level];
if (!nextInfo.block.GetReadable(fDirectory->GetVolume(),
nextBlockIndex)) {
RETURN_ERROR(B_ERROR);
}
nextInfo.entryBlock.SetTo(
(checksumfs_dir_entry_block*)nextInfo.block.Data(),
B_PAGE_SIZE);
}
return B_OK;
}
bool
DirEntryTree::IsEmpty() const
{
DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize);
return entryBlock.EntryCount() == 0;
}
bool
DirEntryTree::Check()
{
@ -1442,9 +1511,9 @@ Directory::Directory(Volume* volume, uint64 blockIndex,
}
Directory::Directory(Volume* volume, uint64 blockIndex, mode_t mode)
Directory::Directory(Volume* volume, mode_t mode)
:
Node(volume, blockIndex, mode)
Node(volume, mode)
{
}
@ -1454,6 +1523,72 @@ Directory::~Directory()
}
void
Directory::DeletingNode()
{
Node::DeletingNode();
// iterate through the directory and remove references to all entries' nodes
char* name = (char*)malloc(kCheckSumFSNameLength + 1);
if (name != NULL) {
name[0] = '\0';
DirEntryTree entryTree(this);
size_t nameLength;
uint64 blockIndex;
while (entryTree.LookupNextEntry(name, name, nameLength,
blockIndex) == B_OK) {
Node* node;
if (GetVolume()->GetNode(blockIndex, node) == B_OK) {
Transaction transaction(GetVolume());
if (transaction.StartAndAddNode(node) == B_OK) {
node->SetHardLinks(node->HardLinks() - 1);
if (node->HardLinks() == 0)
GetVolume()->RemoveNode(node);
if (transaction.Commit() != B_OK) {
ERROR("Failed to commit transaction for dereferencing "
"entry node of deleted directory at %" B_PRIu64
"\n", BlockIndex());
}
} else {
ERROR("Failed to start transaction for dereferencing "
"entry node of deleted directory at %" B_PRIu64 "\n",
BlockIndex());
}
GetVolume()->PutNode(node);
} else {
ERROR("Failed to get node %" B_PRIu64 " referenced by deleted "
"directory at %" B_PRIu64 "\n", blockIndex, BlockIndex());
}
}
free(name);
}
// free the directory entry block tree
Transaction transaction(GetVolume());
if (transaction.Start() != B_OK) {
ERROR("Failed to start transaction for freeing entry tree of deleted "
"directory at %" B_PRIu64 "\n", BlockIndex());
return;
}
DirEntryTree entryTree(this);
if (entryTree.FreeTree(transaction) != B_OK) {
ERROR("Failed to freeing entry tree of deleted directory at %" B_PRIu64
"\n", BlockIndex());
return;
}
if (transaction.Commit() != B_OK) {
ERROR("Failed to commit transaction for freeing entry tree of deleted "
"directory at %" B_PRIu64 "\n", BlockIndex());
}
}
status_t
Directory::LookupEntry(const char* name, uint64& _blockIndex)
{
@ -1486,12 +1621,19 @@ Directory::InsertEntry(const char* name, uint64 blockIndex,
status_t
Directory::RemoveEntry(const char* name, Transaction& transaction)
Directory::RemoveEntry(const char* name, Transaction& transaction,
bool* _lastEntryRemoved)
{
DirEntryTree entryTree(this);
status_t error = entryTree.RemoveEntry(name, transaction);
if (error == B_OK)
ASSERT(entryTree.Check());
return error;
if (error != B_OK)
return error;
ASSERT(entryTree.Check());
if (_lastEntryRemoved != NULL)
*_lastEntryRemoved = entryTree.IsEmpty();
return B_OK;
}

@ -13,10 +13,11 @@ class Directory : public Node {
public:
Directory(Volume* volume, uint64 blockIndex,
const checksumfs_node& nodeData);
Directory(Volume* volume, uint64 blockIndex,
mode_t mode);
Directory(Volume* volume, mode_t mode);
virtual ~Directory();
virtual void DeletingNode();
status_t LookupEntry(const char* name,
uint64& _blockIndex);
status_t LookupNextEntry(const char* name,
@ -26,7 +27,8 @@ public:
status_t InsertEntry(const char* name, uint64 blockIndex,
Transaction& transaction);
status_t RemoveEntry(const char* name,
Transaction& transaction);
Transaction& transaction,
bool* _lastEntryRemoved = NULL);
};

@ -54,9 +54,9 @@ File::File(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData)
}
File::File(Volume* volume, uint64 blockIndex, mode_t mode)
File::File(Volume* volume, mode_t mode)
:
Node(volume, blockIndex, mode),
Node(volume, mode),
fFileCache(NULL),
fFileMap(NULL)
{
@ -89,10 +89,32 @@ File::InitForVFS()
}
status_t
File::DeletingNode(Transaction& transaction)
void
File::DeletingNode()
{
return Resize(0, false, transaction);
Node::DeletingNode();
// start a transaction
Transaction transaction(GetVolume());
status_t error = transaction.Start();
if (error != B_OK) {
ERROR("Failed to start transaction for deleting contents of file at %"
B_PRIu64 "\n", BlockIndex());
return;
}
error = Resize(0, false, transaction);
if (error != B_OK) {
ERROR("Failed to delete contents of file at %" B_PRIu64 "\n",
BlockIndex());
return;
}
error = transaction.Commit();
if (error != B_OK) {
ERROR("Failed to commit transaction for deleting contents of file at %"
B_PRIu64 "\n", BlockIndex());
}
}

@ -16,12 +16,11 @@ class File : public Node {
public:
File(Volume* volume, uint64 blockIndex,
const checksumfs_node& nodeData);
File(Volume* volume, uint64 blockIndex,
mode_t mode);
File(Volume* volume, mode_t mode);
virtual ~File();
virtual status_t InitForVFS();
virtual status_t DeletingNode(Transaction& transaction);
virtual void DeletingNode();
virtual status_t Resize(uint64 newSize, bool fillWithZeroes,
Transaction& transaction);

@ -37,10 +37,10 @@ Node::Node(Volume* volume, uint64 blockIndex, const checksumfs_node& nodeData)
}
Node::Node(Volume* volume, uint64 blockIndex, mode_t mode)
Node::Node(Volume* volume, mode_t mode)
:
fVolume(volume),
fBlockIndex(blockIndex),
fBlockIndex(0),
fNodeDataDirty(true)
{
_Init();
@ -70,6 +70,13 @@ Node::~Node()
}
void
Node::SetBlockIndex(uint64 blockIndex)
{
fBlockIndex = blockIndex;
}
status_t
Node::InitForVFS()
{
@ -77,10 +84,22 @@ Node::InitForVFS()
}
status_t
Node::DeletingNode(Transaction& transaction)
void
Node::DeletingNode()
{
return B_OK;
// delete the node's attribute directory
if (AttributeDirectory() == 0)
return;
Node* attributeDirectory;
if (fVolume->GetNode(AttributeDirectory(), attributeDirectory) == B_OK) {
fVolume->RemoveNode(attributeDirectory);
fVolume->PutNode(attributeDirectory);
} else {
ERROR("Failed to get attribute directory (at %" B_PRIu64 ") for "
"deleted node at %" B_PRIu64 "\n", AttributeDirectory(),
BlockIndex());
}
}
@ -113,6 +132,24 @@ Node::Sync()
}
void
Node::SetMode(uint32 mode)
{
ASSERT((mode & S_IFMT) == (Mode() & S_IFMT));
fNode.mode = mode;
fNodeDataDirty = true;
}
void
Node::SetAttributeType(uint32 type)
{
fNode.attributeType = type;
fNodeDataDirty = true;
}
void
Node::SetParentDirectory(uint32 blockIndex)
{
@ -121,6 +158,14 @@ Node::SetParentDirectory(uint32 blockIndex)
}
void
Node::SetAttributeDirectory(uint32 blockIndex)
{
fNode.attributeDirectory = blockIndex;
fNodeDataDirty = true;
}
void
Node::SetHardLinks(uint32 value)
{

@ -30,12 +30,13 @@ class Node {
public:
Node(Volume* volume, uint64 blockIndex,
const checksumfs_node& nodeData);
Node(Volume* volume, uint64 blockIndex,
mode_t mode);
Node(Volume* volume, mode_t mode);
virtual ~Node();
void SetBlockIndex(uint64 blockIndex);
virtual status_t InitForVFS();
virtual status_t DeletingNode(Transaction& transaction);
virtual void DeletingNode();
virtual status_t Resize(uint64 newSize, bool fillWithZeroes,
Transaction& transaction);
@ -50,7 +51,9 @@ public:
inline Volume* GetVolume() const { return fVolume; }
inline uint64 BlockIndex() const { return fBlockIndex; }
inline uint32 Mode() const { return fNode.mode; }
inline uint32 AttributeType() const;
inline uint64 ParentDirectory() const;
inline uint64 AttributeDirectory() const;
inline uint32 HardLinks() const { return fNode.hardLinks; }
inline uint32 UID() const { return fNode.uid; }
inline uint32 GID() const { return fNode.gid; }
@ -60,7 +63,10 @@ public:
inline uint64 ModificationTime() const;
inline uint64 ChangeTime() const;
void SetMode(uint32 mode);
void SetAttributeType(uint32 type);
void SetParentDirectory(uint32 blockIndex);
void SetAttributeDirectory(uint32 blockIndex);
void SetHardLinks(uint32 value);
void SetUID(uint32 uid);
void SetGID(uint32 gid);
@ -94,6 +100,13 @@ private:
};
uint32
Node::AttributeType() const
{
return fNode.attributeType;
}
uint64
Node::ParentDirectory() const
{
@ -101,6 +114,13 @@ Node::ParentDirectory() const
}
uint64
Node::AttributeDirectory() const
{
return fNode.attributeDirectory;
}
uint64
Node::CreationTime() const
{

@ -24,9 +24,9 @@ SymLink::SymLink(Volume* volume, uint64 blockIndex,
}
SymLink::SymLink(Volume* volume, uint64 blockIndex, mode_t mode)
SymLink::SymLink(Volume* volume, mode_t mode)
:
Node(volume, blockIndex, mode)
Node(volume, mode)
{
}

@ -13,8 +13,7 @@ class SymLink : public Node {
public:
SymLink(Volume* volume, uint64 blockIndex,
const checksumfs_node& nodeData);
SymLink(Volume* volume, uint64 blockIndex,
mode_t mode);
SymLink(Volume* volume, mode_t mode);
virtual ~SymLink();
status_t ReadSymLink(char* buffer, size_t toRead,

@ -35,7 +35,7 @@ Volume::Volume(uint32 flags)
:
fFSVolume(NULL),
fFD(-1),
fFlags(B_FS_IS_PERSISTENT
fFlags(B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME
| (flags & B_FS_IS_READONLY != 0 ? B_FS_IS_READONLY : 0)),
fBlockCache(NULL),
fTotalBlocks(0),
@ -281,6 +281,13 @@ Volume::RemoveNode(Node* node)
}
status_t
Volume::UnremoveNode(Node* node)
{
return unremove_vnode(fFSVolume, node->BlockIndex());
}
status_t
Volume::ReadNode(uint64 blockIndex, Node*& _node)
{
@ -326,28 +333,14 @@ status_t
Volume::CreateDirectory(mode_t mode, Transaction& transaction,
Directory*& _directory)
{
// allocate a free block
AllocatedBlock allocatedBlock(fBlockAllocator, transaction);
status_t error = allocatedBlock.Allocate();
Directory* directory = new(std::nothrow) Directory(this,
(mode & S_IUMSK) | S_IFDIR);
status_t error = _CreateNode(directory, transaction);
if (error != B_OK)
return error;
// create the directory
Directory* directory = new(std::nothrow) Directory(this,
allocatedBlock.Index(), (mode & S_IUMSK) | S_IFDIR);
if (directory == NULL)
return B_NO_MEMORY;
// attach the directory to the transaction
error = transaction.AddNode(directory, TRANSACTION_DELETE_NODE);
if (error != B_OK) {
delete directory;
return error;
}
allocatedBlock.Detach();
_directory = directory;
return B_OK;
}
@ -355,28 +348,13 @@ Volume::CreateDirectory(mode_t mode, Transaction& transaction,
status_t
Volume::CreateFile(mode_t mode, Transaction& transaction, File*& _file)
{
// allocate a free block
AllocatedBlock allocatedBlock(fBlockAllocator, transaction);
status_t error = allocatedBlock.Allocate();
File* file = new(std::nothrow) File(this, (mode & S_IUMSK) | S_IFREG);
status_t error = _CreateNode(file, transaction);
if (error != B_OK)
return error;
// create the file
File* file = new(std::nothrow) File(this, allocatedBlock.Index(),
(mode & S_IUMSK) | S_IFREG);
if (file == NULL)
return B_NO_MEMORY;
// attach the file to the transaction
error = transaction.AddNode(file, TRANSACTION_DELETE_NODE);
if (error != B_OK) {
delete file;
return error;
}
allocatedBlock.Detach();
_file = file;
return B_OK;
}
@ -384,28 +362,14 @@ Volume::CreateFile(mode_t mode, Transaction& transaction, File*& _file)
status_t
Volume::CreateSymLink(mode_t mode, Transaction& transaction, SymLink*& _symLink)
{
// allocate a free block
AllocatedBlock allocatedBlock(fBlockAllocator, transaction);
status_t error = allocatedBlock.Allocate();
SymLink* symLink = new(std::nothrow) SymLink(this,
(mode & S_IUMSK) | S_IFLNK);
status_t error = _CreateNode(symLink, transaction);
if (error != B_OK)
return error;
// create the symlink
SymLink* symLink = new(std::nothrow) SymLink(this, allocatedBlock.Index(),
(mode & S_IUMSK) | S_IFLNK);
if (symLink == NULL)
return B_NO_MEMORY;
// attach the symlink to the transaction
error = transaction.AddNode(symLink, TRANSACTION_DELETE_NODE);
if (error != B_OK) {
delete symLink;
return error;
}
allocatedBlock.Detach();
_symLink = symLink;
return B_OK;
}
@ -413,32 +377,35 @@ Volume::CreateSymLink(mode_t mode, Transaction& transaction, SymLink*& _symLink)
status_t
Volume::DeleteNode(Node* node)
{
// let the node delete data associated with it
node->DeletingNode();
uint64 blockIndex = node->BlockIndex();
// delete the node itself
Transaction transaction(this);
status_t error = transaction.Start();
if (error == B_OK) {
error = node->DeletingNode(transaction);
if (error != B_OK) {
ERROR("Preparing deletion of failed for node at %" B_PRIu64 "\n",
node->BlockIndex());
}
error = fBlockAllocator->Free(node->BlockIndex(), 1, transaction);
if (error == B_OK) {
error = transaction.Commit();
if (error != B_OK) {
ERROR("Failed to commit transaction for delete node at %"
B_PRIu64 "\n", node->BlockIndex());
ERROR("Failed to commit transaction for deleting node at %"
B_PRIu64 "\n", blockIndex);
}
} else {
ERROR("Failed to free block for node at %" B_PRIu64 "\n",
node->BlockIndex());
blockIndex);
}
} else {
ERROR("Failed to start transaction for delete node at %" B_PRIu64 "\n",
node->BlockIndex());
ERROR("Failed to start transaction for deleting node at %" B_PRIu64
"\n", blockIndex);
}
transaction.Abort();
delete node;
return error;
}
@ -510,3 +477,38 @@ Volume::_Init(uint64 totalBlocks)
return B_OK;
}
status_t
Volume::_CreateNode(Node* node, Transaction& transaction)
{
if (node == NULL)
return B_NO_MEMORY;
ObjectDeleter<Node> nodeDeleter(node);
// allocate a free block
AllocatedBlock allocatedBlock(fBlockAllocator, transaction);
status_t error = allocatedBlock.Allocate();
if (error != B_OK)
return error;
// clear the block
{
Block block;
if (!block.GetZero(this, allocatedBlock.Index(), transaction))
return B_ERROR;
}
node->SetBlockIndex(allocatedBlock.Index());
// attach the node to the transaction
error = transaction.AddNode(node, TRANSACTION_DELETE_NODE);
if (error != B_OK)
return error;
allocatedBlock.Detach();
nodeDeleter.Detach();
return B_OK;
}

@ -41,6 +41,7 @@ public:
status_t GetNode(uint64 blockIndex, Node*& _node);
status_t PutNode(Node* node);
status_t RemoveNode(Node* node);
status_t UnremoveNode(Node* node);
status_t ReadNode(uint64 blockIndex, Node*& _node);
@ -73,6 +74,10 @@ public:
private:
status_t _Init(uint64 totalBlocks);
status_t _CreateNode(Node* node,
Transaction& transaction);
status_t _DeleteDirectoryEntries(Directory* directory);
private:
fs_volume* fFSVolume;
int fFD;

File diff suppressed because it is too large Load Diff