root/src/add-ons/kernel/file_systems/packagefs/package_links/PackageLinkSymlink.cpp
/*
 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "PackageLinkSymlink.h"

#include <algorithm>

#include <NodeMonitor.h>

#include "AutoPackageAttributeDirectoryCookie.h"
#include "DebugSupport.h"
#include "NodeListener.h"
#include "PackageLinksListener.h"
#include "Utils.h"
#include "Volume.h"


static const char* const kUnknownLinkTarget = "?";

static const char* const kLinkPaths[PackageLinkSymlink::TYPE_ENUM_COUNT]
                [PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT] = {
        {
                "../..",
                "../../../home/config",
                kUnknownLinkTarget
        },
        {
                "../../../system/settings",
                "../../../home/config/settings",
                kUnknownLinkTarget
        }
};


static const char*
link_path_for_mount_type(MountType mountType, PackageLinkSymlink::Type linkType)
{
        if (mountType < 0 || mountType >= PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT
                || linkType < 0 || linkType >= PackageLinkSymlink::TYPE_ENUM_COUNT) {
                return kUnknownLinkTarget;
        }

        return kLinkPaths[linkType][mountType];
}


// #pragma mark - OldAttributes


struct PackageLinkSymlink::OldAttributes : OldNodeAttributes {
        OldAttributes(const timespec& modifiedTime, off_t fileSize)
                :
                fModifiedTime(modifiedTime),
                fFileSize(fileSize)
        {
        }

        virtual timespec ModifiedTime() const
        {
                return fModifiedTime;
        }

        virtual off_t FileSize() const
        {
                return fFileSize;
        }

private:
        timespec        fModifiedTime;
        off_t           fFileSize;
};


// #pragma mark - PackageLinkSymlink


PackageLinkSymlink::PackageLinkSymlink(Package* package, Type type)
        :
        Node(0),
        fPackage(),
        fLinkPath(kUnknownLinkTarget),
        fType(type)
{
        Update(package, NULL);
}


PackageLinkSymlink::~PackageLinkSymlink()
{
}


void
PackageLinkSymlink::Update(Package* package, PackageLinksListener* listener)
{
        OldAttributes oldAttributes(fModifiedTime, FileSize());

        fPackage = package;

        if (package != NULL) {
                fLinkPath = package->InstallPath();
                if (fLinkPath[0] != '\0') {
                        if (fType == TYPE_SETTINGS)
                                fLinkPath = ".self/settings";
                } else {
                        fLinkPath = link_path_for_mount_type(
                                package->Volume()->MountType(), fType);
                }
        } else
                fLinkPath = kUnknownLinkTarget;

        get_real_time(fModifiedTime);

        if (listener != NULL) {
                listener->PackageLinkNodeChanged(this,
                        B_STAT_SIZE | B_STAT_MODIFICATION_TIME, oldAttributes);
        }
}


mode_t
PackageLinkSymlink::Mode() const
{
        return S_IFLNK | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
}


timespec
PackageLinkSymlink::ModifiedTime() const
{
        return fModifiedTime;
}


off_t
PackageLinkSymlink::FileSize() const
{
        return strlen(fLinkPath);
}


status_t
PackageLinkSymlink::Read(off_t offset, void* buffer, size_t* bufferSize)
{
        return B_BAD_VALUE;
}


status_t
PackageLinkSymlink::Read(io_request* request)
{
        return B_BAD_VALUE;
}


status_t
PackageLinkSymlink::ReadSymlink(void* buffer, size_t* bufferSize)
{
        size_t linkLength = strnlen(fLinkPath, B_PATH_NAME_LENGTH);

        size_t bytesToCopy = std::min(linkLength, *bufferSize);

        *bufferSize = linkLength;

        memcpy(buffer, fLinkPath, bytesToCopy);
        return B_OK;
}


status_t
PackageLinkSymlink::OpenAttributeDirectory(AttributeDirectoryCookie*& _cookie)
{
        AutoPackageAttributeDirectoryCookie* cookie = new(std::nothrow)
                AutoPackageAttributeDirectoryCookie();
        if (cookie == NULL)
                return B_NO_MEMORY;

        _cookie = cookie;
        return B_OK;
}


status_t
PackageLinkSymlink::OpenAttribute(const StringKey& name, int openMode,
        AttributeCookie*& _cookie)
{
        if (!fPackage.IsSet())
                return B_ENTRY_NOT_FOUND;

        return AutoPackageAttributes::OpenCookie(fPackage, name, openMode, _cookie);
}