root/src/add-ons/kernel/file_systems/xfs/ShortDirectory.cpp
/*
 * Copyright 2020, Shubham Bhagat, shubhambhagat111@yahoo.com
 * All rights reserved. Distributed under the terms of the MIT License.
 */


#include "ShortDirectory.h"

#include "Utility.h"


ShortDirectory::ShortDirectory(Inode* inode)
        :
        fInode(inode),
        fTrack(0)
{
        fHeader = (ShortFormHeader*)(DIR_DFORK_PTR(fInode->Buffer(), fInode->CoreInodeSize()));
        fLastEntryOffset = 0;
}


ShortDirectory::~ShortDirectory()
{
}


uint8
ShortDirectory::GetFileType(ShortFormEntry* entry)
{
        ASSERT(fInode->HasFileTypeField() == true);
        return entry->name[entry->namelen];
}


ShortFormEntry*
ShortDirectory::FirstEntry()
{
        return (ShortFormEntry*) ((char*) fHeader + HeaderSize());
}


size_t
ShortDirectory::HeaderSize()
{
        if (fHeader->i8count)
                return sizeof(ShortFormHeader);
        else
                return sizeof(ShortFormHeader) - sizeof(uint32);
}


xfs_ino_t
ShortDirectory::GetIno(ShortFormInodeUnion* inum)
{
        if (fHeader->i8count)
                return B_BENDIAN_TO_HOST_INT64(inum->i8);
        else
                return B_BENDIAN_TO_HOST_INT32(inum->i4);
}


xfs_ino_t
ShortDirectory::GetEntryIno(ShortFormEntry* entry)
{
        if (fInode->HasFileTypeField())
                return GetIno((ShortFormInodeUnion*)(entry->name
                                + entry->namelen + sizeof(uint8)));
        else
                return GetIno((ShortFormInodeUnion*)(entry->name + entry->namelen));
}


size_t
ShortDirectory::EntrySize(int namelen)
{
        return sizeof(ShortFormEntry) + namelen
                        + (fInode->HasFileTypeField() ? sizeof(uint8) : 0)
                        + (fHeader->i8count ? sizeof(uint64) : sizeof(uint32));
}


status_t
ShortDirectory::Rewind()
{
        fTrack = 0;
        fLastEntryOffset = 0;
        return B_OK;
}


status_t
ShortDirectory::Lookup(const char* name, size_t length, xfs_ino_t* ino)
{
        TRACE("ShortDirectory::Lookup\n");

        if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
                xfs_ino_t rootIno = fInode->GetVolume()->Root();
                if (strcmp(name, ".") == 0 || (rootIno == fInode->ID())) {
                        *ino = fInode->ID();
                        TRACE("ShortDirectory:Lookup: name: \".\" ino: (%" B_PRIu64 ")\n", *ino);
                        return B_OK;
                }
                *ino = GetIno(&fHeader->parent);
                TRACE("Parent: (%" B_PRIu64 ")\n", *ino);
                return B_OK;
        }

        ShortFormEntry* entry = FirstEntry();
        TRACE("Length of first entry: (%" B_PRIu8 "),offset of first entry:"
                "(%" B_PRIu16 ")\n", entry->namelen, B_BENDIAN_TO_HOST_INT16(entry->offset.i));

        for (int i = 0; i < fHeader->count; i++) {
                if (xfs_name_comp(name, length, entry->name, entry->namelen)) {
                        *ino = GetEntryIno(entry);
                        return B_OK;
                }
                entry = (ShortFormEntry*)
                        ((char*) entry + EntrySize(entry->namelen));
        }

        return B_ENTRY_NOT_FOUND;
}


status_t
ShortDirectory::GetNext(char* name, size_t* length, xfs_ino_t* ino)
{
        if (fTrack == 0) {
                // Return '.'
                if (*length < 2)
                        return B_BUFFER_OVERFLOW;
                *length = 2;
                strlcpy(name, ".", *length + 1);
                *ino = fInode->ID();
                fTrack = 1;
                TRACE("ShortDirectory:GetNext: name: \".\" ino: (%" B_PRIu64 ")\n", *ino);
                return B_OK;
        }
        if (fTrack == 1) {
                // Return '..'
                if (*length < 3)
                        return B_BUFFER_OVERFLOW;
                *length = 3;
                strlcpy(name, "..", *length + 1);
                *ino = GetIno(&fHeader->parent);
                fTrack = 2;
                TRACE("ShortDirectory:GetNext: name: \"..\" ino: (%" B_PRIu64 ")\n", *ino);
                return B_OK;
        }

        ShortFormEntry* entry = FirstEntry();
        TRACE("Length of first entry: (%" B_PRIu8 "),offset of first entry:"
                "(%" B_PRIu16 ")\n", entry->namelen, B_BENDIAN_TO_HOST_INT16(entry->offset.i));

        for (int i = 0; i < fHeader->count; i++) {
                uint16 curOffset = B_BENDIAN_TO_HOST_INT16(entry->offset.i);
                if (curOffset > fLastEntryOffset) {

                        if ((size_t)(entry->namelen + 1) > *length)
                                return B_BUFFER_OVERFLOW;

                        fLastEntryOffset = curOffset;
                        memcpy(name, entry->name, entry->namelen);
                        name[entry->namelen] = '\0';
                        *length = entry->namelen + 1;
                        *ino = GetEntryIno(entry);
                        TRACE("Entry found. Name: (%s), Length: (%" B_PRIuSIZE "),ino: (%" B_PRIu64 ")\n",
                                name,*length, *ino);
                        return B_OK;
                }
                entry = (ShortFormEntry*)((char*)entry + EntrySize(entry->namelen));
        }

        return B_ENTRY_NOT_FOUND;
}