root/src/system/boot/loader/file_systems/amiga_ffs/Directory.cpp
/*
 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "Directory.h"
#include "Volume.h"
#include "File.h"

#include <StorageDefs.h>

#include <string.h>
#include <unistd.h>
#include <stdio.h>


using std::nothrow;


namespace FFS {


Directory::Directory(Volume &volume, int32 block)
        :
        fVolume(volume)
{
        void *data = malloc(volume.BlockSize());
        if (data == NULL)
                return;

        if (read_pos(volume.Device(), block * volume.BlockSize(), data, volume.BlockSize()) == volume.BlockSize())
                fNode.SetTo(data, volume.BlockSize());
}


Directory::Directory(Volume &volume, RootBlock &root)
        :
        fVolume(volume)
{
        fNode.SetTo(root.BlockData(), root.BlockSize());
}


Directory::~Directory()
{
        free(fNode.BlockData());
}


status_t
Directory::InitCheck()
{
        return fNode.ValidateCheckSum();
}


status_t
Directory::Open(void **_cookie, int mode)
{
        _inherited::Open(_cookie, mode);

        HashIterator *iterator = new(nothrow) HashIterator(fVolume.Device(), fNode);
        if (iterator == NULL)
                return B_NO_MEMORY;

        if (iterator->InitCheck() != B_OK) {
                delete iterator;
                return B_NO_MEMORY;
        }

        *_cookie = (void *)iterator;
        return B_OK;
}


status_t
Directory::Close(void *cookie)
{
        _inherited::Close(cookie);

        delete (HashIterator *)cookie;
        return B_OK;
}


Node*
Directory::LookupDontTraverse(const char* name)
{
        if (!strcmp(name, ".")) {
                Acquire();
                return this;
        }

        HashIterator iterator(fVolume.Device(), fNode);
        if (iterator.InitCheck() != B_OK)
                return NULL;

        iterator.Goto(fNode.HashIndexFor(fVolume.Type(), name));

        NodeBlock *node;
        int32 block;
        while ((node = iterator.GetNext(block)) != NULL) {
                char fileName[FFS_NAME_LENGTH];
                if (node->GetName(fileName, sizeof(fileName)) == B_OK
                        && !strcmp(name, fileName)) {
                        if (node->IsFile())
                                return new(nothrow) File(fVolume, block);
                        if (node->IsDirectory())
                                return new(nothrow) Directory(fVolume, block);

                        return NULL;
                }
        }
        return NULL;
}


status_t
Directory::GetNextEntry(void *cookie, char *name, size_t size)
{
        HashIterator *iterator = (HashIterator *)cookie;
        int32 block;

        NodeBlock *node = iterator->GetNext(block);
        if (node == NULL)
                return B_ENTRY_NOT_FOUND;

        return node->GetName(name, size);
}


status_t
Directory::GetNextNode(void *cookie, Node **_node)
{
        return B_ERROR;
}


status_t
Directory::Rewind(void *cookie)
{
        HashIterator *iterator = (HashIterator *)cookie;
        iterator->Rewind();

        return B_OK;
}


bool
Directory::IsEmpty()
{
        int32 index;
        return fNode.FirstHashValue(index) == -1;
}


status_t
Directory::GetName(char *name, size_t size) const
{
        return fNode.GetName(name, size);
}


ino_t
Directory::Inode() const
{
        return fNode.HeaderKey();
}


}       // namespace FFS