* Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "AllocationInfo.h"
#include "DebugSupport.h"
#include "Directory.h"
#include "Entry.h"
#include "EntryIterator.h"
#include "File.h"
#include "SymLink.h"
#include "Volume.h"
Directory::Directory(Volume *volume)
: Node(volume, NODE_TYPE_DIRECTORY),
fEntries()
{
}
Directory::~Directory()
{
while (Entry *entry = fEntries.First()) {
if (DeleteEntry(entry) != B_OK) {
FATAL("Could not delete all entries in directory.\n");
break;
}
}
}
status_t
Directory::Link(Entry *entry)
{
if (fReferrers.IsEmpty())
return Node::Link(entry);
return B_IS_A_DIRECTORY;
}
status_t
Directory::Unlink(Entry *entry)
{
if (entry == fReferrers.First())
return Node::Unlink(entry);
return B_BAD_VALUE;
}
status_t
Directory::SetSize(off_t )
{
return B_IS_A_DIRECTORY;
}
off_t
Directory::GetSize() const
{
return 0;
}
Directory *
Directory::GetParent() const
{
Entry *entry = fReferrers.First();
return (entry ? entry->GetParent() : NULL);
}
status_t
Directory::CreateDirectory(const char *name, Directory **directory)
{
status_t error = (name && directory ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (Directory *node = new(nothrow) Directory(GetVolume())) {
error = _CreateCommon(node, name);
if (error == B_OK)
*directory = node;
} else
SET_ERROR(error, B_NO_MEMORY);
}
return error;
}
status_t
Directory::CreateFile(const char *name, File **file)
{
status_t error = (name && file ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (File *node = new(nothrow) File(GetVolume())) {
error = _CreateCommon(node, name);
if (error == B_OK)
*file = node;
} else
SET_ERROR(error, B_NO_MEMORY);
}
return error;
}
status_t
Directory::CreateSymLink(const char *name, const char *path, SymLink **symLink)
{
status_t error = (name && symLink ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (SymLink *node = new(nothrow) SymLink(GetVolume())) {
error = node->SetLinkedPath(path);
if (error == B_OK) {
error = _CreateCommon(node, name);
if (error == B_OK)
*symLink = node;
} else
delete node;
} else
SET_ERROR(error, B_NO_MEMORY);
}
return error;
}
status_t
Directory::AddEntry(Entry *entry)
{
status_t error = (entry && !entry->GetParent() ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
fEntries.Insert(entry);
entry->SetParent(this);
error = GetVolume()->EntryAdded(GetID(), entry);
if (error == B_OK) {
MarkModified(B_STAT_MODIFICATION_TIME);
} else {
fEntries.Remove(entry);
entry->SetParent(NULL);
}
}
return error;
}
status_t
Directory::CreateEntry(Node *node, const char *name, Entry **_entry)
{
status_t error = (node ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
Entry *entry = new(nothrow) Entry(name);
if (entry) {
error = entry->InitCheck();
if (error == B_OK) {
error = entry->Link(node);
if (error == B_OK) {
error = AddEntry(entry);
if (error == B_OK) {
if (_entry)
*_entry = entry;
} else {
entry->Unlink();
}
}
}
if (error != B_OK)
delete entry;
} else
SET_ERROR(error, B_NO_MEMORY);
}
return error;
}
status_t
Directory::RemoveEntry(Entry *entry)
{
status_t error = (entry && entry->GetParent() == this ? B_OK
: B_BAD_VALUE);
if (error == B_OK) {
if (GetVolume()->IteratorLock()) {
Entry *nextEntry = fEntries.GetNext(entry);
DoublyLinkedList<EntryIterator> *iterators
= entry->GetEntryIteratorList();
for (EntryIterator *iterator = iterators->First();
iterator;
iterator = iterators->GetNext(iterator)) {
iterator->SetCurrent(nextEntry, true);
}
if (nextEntry) {
DoublyLinkedList<EntryIterator> *nextIterators
= nextEntry->GetEntryIteratorList();
nextIterators->MoveFrom(iterators);
} else
iterators->RemoveAll();
GetVolume()->IteratorUnlock();
} else
error = B_ERROR;
if (error == B_OK) {
error = GetVolume()->EntryRemoved(GetID(), entry);
if (error == B_OK) {
fEntries.Remove(entry);
entry->SetParent(NULL);
MarkModified(B_STAT_MODIFICATION_TIME);
}
}
}
return error;
}
status_t
Directory::DeleteEntry(Entry *entry)
{
status_t error = RemoveEntry(entry);
if (error == B_OK) {
error = entry->Unlink();
if (error == B_OK)
delete entry;
else {
FATAL("Failed to Unlink() entry %p from node %" B_PRIdINO "!\n", entry,
entry->GetNode()->GetID());
AddEntry(entry);
}
}
return error;
}
status_t
Directory::FindEntry(const char *name, Entry **_entry) const
{
status_t error = (name && _entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
Entry *entry = NULL;
while (GetNextEntry(&entry) == B_OK) {
if (!strcmp(entry->GetName(), name)) {
*_entry = entry;
return B_OK;
}
}
error = B_ENTRY_NOT_FOUND;
*/
error = GetVolume()->FindEntry(GetID(), name, _entry);
}
return error;
}
status_t
Directory::FindNode(const char *name, Node **node) const
{
status_t error = (name && node ? B_OK : B_BAD_VALUE);
Entry *entry = NULL;
if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK)
*node = entry->GetNode();
return error;
}
status_t
Directory::FindAndGetNode(const char *name, Node **node, Entry **_entry) const
{
status_t error = (name && node ? B_OK : B_BAD_VALUE);
Entry *entry = NULL;
if (error == B_OK && (error = FindEntry(name, &entry)) == B_OK) {
*node = entry->GetNode();
if (_entry)
*_entry = entry;
error = GetVolume()->GetVNode(*node);
}
return error;
}
status_t
Directory::GetPreviousEntry(Entry **entry) const
{
status_t error = (entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (!*entry)
*entry = fEntries.Last();
else if ((*entry)->GetParent() == this)
*entry = fEntries.GetPrevious(*entry);
else
error = B_BAD_VALUE;
if (error == B_OK && !*entry)
error = B_ENTRY_NOT_FOUND;
}
return error;
}
status_t
Directory::GetNextEntry(Entry **entry) const
{
status_t error = (entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (!*entry)
*entry = fEntries.First();
else if ((*entry)->GetParent() == this)
*entry = fEntries.GetNext(*entry);
else
error = B_BAD_VALUE;
if (error == B_OK && !*entry)
error = B_ENTRY_NOT_FOUND;
}
return error;
}
void
Directory::GetAllocationInfo(AllocationInfo &info)
{
Node::GetAllocationInfo(info);
info.AddDirectoryAllocation();
Entry *entry = NULL;
while (GetNextEntry(&entry) == B_OK)
entry->GetAllocationInfo(info);
}
status_t
Directory::_CreateCommon(Node *node, const char *name)
{
status_t error = node->InitCheck();
if (error == B_OK) {
error = CreateEntry(node, name);
}
if (error != B_OK)
delete node;
return error;
}