root/src/add-ons/kernel/file_systems/ramfs/Node.cpp
/*
 * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
 * All rights reserved. Distributed under the terms of the MIT license.
 */

#include "Node.h"

#include <util/AutoLock.h>

#include "AllocationInfo.h"
#include "DebugSupport.h"
#include "EntryIterator.h"
#include "LastModifiedIndex.h"
#include "Volume.h"


Node::Node(Volume *volume, uint8 type)
        :
        fVolume(volume),
        fID(fVolume->NextNodeID()),
        fRefCount(0),
        fMode(0),
        fUID(0),
        fGID(0),
        fATime(0),
        fMTime(0),
        fCTime(0),
        fCrTime(0),
        fModified(0),
        fIsKnownToVFS(false),
        fAttributes(),
        fReferrers()
{
        // set file type
        switch (type) {
                case NODE_TYPE_DIRECTORY:
                        fMode = S_IFDIR;
                        break;
                case NODE_TYPE_FILE:
                        fMode = S_IFREG;
                        break;
                case NODE_TYPE_SYMLINK:
                        fMode = S_IFLNK;
                        break;
        }
        // set defaults for time
        fATime = fMTime = fCTime = fCrTime = time(NULL);
}


Node::~Node()
{
        ASSERT(fRefCount == 0);

        // delete all attributes
        while (Attribute *attribute = fAttributes.First()) {
                status_t error = DeleteAttribute(attribute);
                if (error != B_OK) {
                        FATAL("Node::~Node(): Failed to delete attribute!\n");
                        break;
                }
        }
}


status_t
Node::InitCheck() const
{
        return (fVolume && fID >= 0 ? B_OK : B_NO_INIT);
}


status_t
Node::AddReference()
{
        if (++fRefCount == 1) {
                status_t error = GetVolume()->PublishVNode(this);
                if (error != B_OK) {
                        fRefCount--;
                        return error;
                }

                fIsKnownToVFS = true;
        }

        return B_OK;
}


void
Node::RemoveReference()
{
        ASSERT(fRefCount > 0);
        if (--fRefCount == 0) {
                GetVolume()->RemoveVNode(this);
                        // RemoveVNode can potentially delete us immediately!
        }
}


status_t
Node::Link(Entry *entry)
{
PRINT("Node[%" B_PRIdINO "]::Link(): %" B_PRId32 " ->...\n", fID, fRefCount);
        fReferrers.Insert(entry);

        status_t error = AddReference();
        if (error != B_OK)
                fReferrers.Remove(entry);

        return error;
}


status_t
Node::Unlink(Entry *entry)
{
PRINT("Node[%" B_PRIdINO "]::Unlink(): %" B_PRId32 " ->...\n", fID, fRefCount);
        fReferrers.Remove(entry);

        RemoveReference();
        return B_OK;
}


void
Node::SetMTime(time_t mTime)
{
        time_t oldMTime = fMTime;
        fATime = fMTime = mTime;
        if (oldMTime != fMTime) {
                if (LastModifiedIndex *index = fVolume->GetLastModifiedIndex())
                        index->Changed(this, oldMTime);
        }
}


uint32
Node::MarkUnmodified()
{
        uint32 modified = fModified;
        if (modified) {
                fCTime = time(NULL);
                SetMTime(fCTime);
                fModified = 0;
        }
        return modified;
}


status_t
Node::CheckPermissions(int mode) const
{
        return check_access_permissions(mode, fMode, fGID, fUID);
}


status_t
Node::CreateAttribute(const char *name, Attribute **_attribute)
{
        status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
        if (error == B_OK) {
                // create attribute
                Attribute *attribute = new(nothrow) Attribute(fVolume, NULL, name);
                if (attribute) {
                        error = attribute->InitCheck();
                        if (error == B_OK) {
                                // add attribute to node
                                error = AddAttribute(attribute);
                                if (error == B_OK)
                                        *_attribute = attribute;
                        }
                        if (error != B_OK)
                                delete attribute;
                } else
                        SET_ERROR(error, B_NO_MEMORY);
        }
        return error;
}


status_t
Node::DeleteAttribute(Attribute *attribute)
{
        status_t error = RemoveAttribute(attribute);
        if (error == B_OK)
                delete attribute;
        return error;
}


status_t
Node::AddAttribute(Attribute *attribute)
{
        status_t error = (attribute && !attribute->GetNode() ? B_OK : B_BAD_VALUE);
        if (error == B_OK) {
                fAttributes.Insert(attribute);
                attribute->SetNode(this);
                MarkModified(B_STAT_MODIFICATION_TIME);
        }
        return error;
}


status_t
Node::RemoveAttribute(Attribute *attribute)
{
        if (attribute == NULL || attribute->GetNode() != this)
                return B_BAD_VALUE;

        RecursiveLocker locker(GetVolume()->GetAttributeIteratorLock());
        if (!locker.IsLocked())
                return B_ERROR;

        // move all iterators pointing to the attribute to the next attribute
        Attribute *nextAttr = fAttributes.GetNext(attribute);
        DoublyLinkedList<AttributeIterator> *iterators
                = attribute->GetAttributeIteratorList();
        for (AttributeIterator *iterator = iterators->First();
                        iterator != NULL; iterator = iterators->GetNext(iterator)) {
                iterator->SetCurrent(nextAttr, true);
        }

        // Move the iterators from one list to the other, or just remove
        // them, if there is no next attribute.
        if (nextAttr != NULL) {
                DoublyLinkedList<AttributeIterator> *nextIterators
                        = nextAttr->GetAttributeIteratorList();
                nextIterators->TakeFrom(iterators);
        } else
                iterators->RemoveAll();

        locker.Unlock();

        // remove the attribute
        fAttributes.Remove(attribute);
        attribute->SetNode(NULL);
        MarkModified(B_STAT_MODIFICATION_TIME);

        return B_OK;
}


status_t
Node::FindAttribute(const char *name, Attribute **_attribute) const
{
        status_t error = (name && _attribute ? B_OK : B_BAD_VALUE);
        if (error == B_OK) {
                Attribute *attribute = NULL;
                while (GetNextAttribute(&attribute) == B_OK) {
                        if (!strcmp(attribute->GetName(), name)) {
                                *_attribute = attribute;
                                return B_OK;
                        }
                }
                error = B_ENTRY_NOT_FOUND;
        }
        return error;
}


status_t
Node::GetPreviousAttribute(Attribute **attribute) const
{
        status_t error = (attribute ? B_OK : B_BAD_VALUE);
        if (error == B_OK) {
                if (!*attribute)
                        *attribute = fAttributes.Last();
                else if ((*attribute)->GetNode() == this)
                        *attribute = fAttributes.GetPrevious(*attribute);
                else
                        error = B_BAD_VALUE;
                if (error == B_OK && !*attribute)
                        error = B_ENTRY_NOT_FOUND;
        }
        return error;
}


status_t
Node::GetNextAttribute(Attribute **attribute) const
{
        status_t error = (attribute ? B_OK : B_BAD_VALUE);
        if (error == B_OK) {
                if (!*attribute)
                        *attribute = fAttributes.First();
                else if ((*attribute)->GetNode() == this)
                        *attribute = fAttributes.GetNext(*attribute);
                else
                        error = B_BAD_VALUE;
                if (error == B_OK && !*attribute)
                        error = B_ENTRY_NOT_FOUND;
        }
        return error;
}


Entry *
Node::GetFirstReferrer() const
{
        return fReferrers.First();
}


Entry *
Node::GetLastReferrer() const
{
        return fReferrers.Last();
}


Entry *
Node::GetPreviousReferrer(Entry *entry) const
{
        return (entry ? fReferrers.GetPrevious(entry) : NULL );
}


Entry *
Node::GetNextReferrer(Entry *entry) const
{
        return (entry ? fReferrers.GetNext(entry) : NULL );
}


void
Node::GetAllocationInfo(AllocationInfo &info)
{
        Attribute *attribute = NULL;
        while (GetNextAttribute(&attribute) == B_OK)
                attribute->GetAllocationInfo(info);
}


NodeStatChangeNotifier::~NodeStatChangeNotifier()
{
        if (fNode == NULL || !fNode->IsModified())
                return;

        uint32 statFields = fNode->MarkUnmodified();
        for (Entry* entry = fNode->GetFirstReferrer(); entry != NULL;
                        entry = fNode->GetNextReferrer(entry)) {
                ino_t parentID = -1;
                if (entry->GetParent() != NULL)
                        parentID = ((Node*)entry->GetParent())->GetID();
                notify_stat_changed(fNode->GetVolume()->GetID(), parentID,
                        fNode->GetID(), statFields);
        }
}