root/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/kernel_interface.cpp
/*
 * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */

#include "kernel_interface.h"

#include <dirent.h>

#include <KernelExport.h>
#include <fs_interface.h>

#include "Debug.h"
#include "FileSystem.h"
#include "String.h"
#include "UserlandFS.h"
#include "Volume.h"


// #pragma mark - general


// parse_parameters
static status_t
parse_parameters(const char *parameters, String &fsName,
        const char **fsParameters)
{
        // check parameters
        if (!parameters)
                return B_BAD_VALUE;

        int32 len = strlen(parameters);

        // skip leading white space
        for (; len > 0; parameters++, len--) {
                if (*parameters != ' ' && *parameters != '\t' && *parameters != '\n')
                        break;
        }
        if (len == 0)
                return B_BAD_VALUE;

        // get the file system name
        int32 fsNameLen = len;
        for (int32 i = 0; i < len; i++) {
                if (parameters[i] == ' ' || parameters[i] == '\t'
                        || parameters[i] == '\n') {
                        fsNameLen = i;
                        break;
                }
        }

        fsName.SetTo(parameters, fsNameLen);
        if (fsName.GetLength() == 0) {
                exit_debugging();
                return B_NO_MEMORY;
        }
        parameters += fsNameLen;
        len -= fsNameLen;

        // skip leading white space of the FS parameters
        for (; len > 0; parameters++, len--) {
                if (*parameters != ' ' && *parameters != '\t' && *parameters != '\n')
                        break;
        }
        *fsParameters = parameters;

        return B_OK;
}

// userlandfs_mount
static status_t
userlandfs_mount(fs_volume* fsVolume, const char* device, uint32 flags,
        const char* args, ino_t* rootVnodeID)
{
        PRINT(("userlandfs_mount(%p (%" B_PRId32 "), %s, 0x%" B_PRIx32
                ", %s, %p)\n", fsVolume, fsVolume->id, device, flags, args,
                rootVnodeID));

        status_t error = B_OK;

        // get the parameters
// TODO: The parameters are in driver settings format now.
        String fsName;
        const char* fsParameters;
        error = parse_parameters(args, fsName, &fsParameters);
        if (error != B_OK)
                RETURN_ERROR(error);

        // get the UserlandFS object
        UserlandFS* userlandFS = UserlandFS::GetUserlandFS();
        if (!userlandFS)
                RETURN_ERROR(B_ERROR);

        // get the file system
        FileSystem* fileSystem = NULL;
        error = userlandFS->RegisterFileSystem(fsName.GetString(), &fileSystem);
        if (error != B_OK)
                RETURN_ERROR(error);

        // mount the volume
        Volume* volume = NULL;
        error = fileSystem->Mount(fsVolume, device, flags, fsParameters, &volume);
        if (error != B_OK) {
                userlandFS->UnregisterFileSystem(fileSystem);
                RETURN_ERROR(error);
        }

        fsVolume->private_volume = volume;
        fsVolume->ops = volume->GetVolumeOps();
        *rootVnodeID = volume->GetRootID();

        PRINT(("userlandfs_mount() done: %p, %" B_PRIdINO "\n",
                fsVolume->private_volume, *rootVnodeID));

        return error;
}

// userlandfs_unmount
static status_t
userlandfs_unmount(fs_volume* fsVolume)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_unmount(%p)\n", volume));

        FileSystem* fileSystem = volume->GetFileSystem();
        status_t error = volume->Unmount();
        // The error code the FS's unmount hook returns is completely irrelevant to
        // the VFS. It considers the volume unmounted in any case.
        volume->ReleaseReference();
        UserlandFS::GetUserlandFS()->UnregisterFileSystem(fileSystem);

        PRINT(("userlandfs_unmount() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_sync
static status_t
userlandfs_sync(fs_volume* fsVolume)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_sync(%p)\n", volume));
        status_t error = volume->Sync();
        PRINT(("userlandfs_sync() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_read_fs_info
static status_t
userlandfs_read_fs_info(fs_volume* fsVolume, struct fs_info* info)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_fs_info(%p, %p)\n", volume, info));
        status_t error = volume->ReadFSInfo(info);
        PRINT(("userlandfs_read_fs_info() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_write_fs_info
static status_t
userlandfs_write_fs_info(fs_volume* fsVolume, const struct fs_info* info,
        uint32 mask)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_write_fs_info(%p, %p, 0x%" B_PRIx32 ")\n", volume, info,
                mask));
        status_t error = volume->WriteFSInfo(info, mask);
        PRINT(("userlandfs_write_fs_info() done: %" B_PRIx32 "\n", error));
        return error;
}


// #pragma mark - vnodes


// userlandfs_lookup
static status_t
userlandfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* entryName,
        ino_t* vnid)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_lookup(%p, %p, `%s', %p)\n", volume, fsDir->private_node,
                entryName, vnid));
        status_t error = volume->Lookup(fsDir->private_node, entryName, vnid);
        PRINT(("userlandfs_lookup() done: (%" B_PRIx32 ", %" B_PRIdINO ")\n", error,
                *vnid));
        return error;
}

// userlandfs_get_vnode_name
static status_t
userlandfs_get_vnode_name(fs_volume* fsVolume, fs_vnode* fsNode, char* buffer,
        size_t bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_get_vnode_name(%p, %p, %p, %" B_PRIuSIZE ")\n", volume,
                fsNode->private_node, buffer, bufferSize));
        status_t error = volume->GetVNodeName(fsNode->private_node, buffer,
                bufferSize);
        PRINT(("userlandfs_get_vnode_name() done: (%" B_PRIx32 ", \"%.*s\")\n",
                error, (int)bufferSize, (error == B_OK ? buffer : NULL)));
        return error;
}

// userlandfs_get_vnode
static status_t
userlandfs_get_vnode(fs_volume* fsVolume, ino_t vnid, fs_vnode* fsNode,
        int* _type, uint32* _flags, bool reenter)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_get_vnode(%p, %" B_PRIdINO ", %p, %d)\n", volume, vnid,
                fsNode->private_node, reenter));
        void* node;
        fs_vnode_ops* ops;
        status_t error = volume->ReadVNode(vnid, reenter, &node, &ops, _type,
                _flags);
        if (error == B_OK) {
                fsNode->private_node = node;
                fsNode->ops = ops;
        }

        PRINT(("userlandfs_get_vnode() done: (%" B_PRIx32 ", %p, %#x, %#" B_PRIx32
                ")\n", error, node, *_type, *_flags));
        return error;
}

// userlandfs_put_vnode
static status_t
userlandfs_put_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
// DANGER: If dbg_printf() is used, this thread will enter another FS and
// even perform a write operation. The is dangerous here, since this hook
// may be called out of the other FSs, since, for instance a put_vnode()
// called from another FS may cause the VFS layer to free vnodes and thus
// invoke this hook.
//      PRINT(("userlandfs_put_vnode(%p, %p, %d)\n", volume, fsNode->private_node,
//              reenter));
        status_t error = volume->WriteVNode(fsNode->private_node, reenter);
//      PRINT(("userlandfs_put_vnode() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_remove_vnode
static status_t
userlandfs_remove_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
// DANGER: See userlandfs_write_vnode().
//      PRINT(("userlandfs_remove_vnode(%p, %p, %d)\n", volume,
//              fsNode->private_node, reenter));
        status_t error = volume->RemoveVNode(fsNode->private_node, reenter);
//      PRINT(("userlandfs_remove_vnode() done: %" B_PRIx32 "\n", error));
        return error;
}


// #pragma mark - asynchronous I/O


// userlandfs_io
status_t
userlandfs_io(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        io_request* request)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_io(%p, %p, %p, %p)\n", volume, fsNode->private_node,
                cookie, request));
        status_t error = volume->DoIO(fsNode->private_node, cookie, request);
        PRINT(("userlandfs_io() done: (%" B_PRIx32 ")\n", error));
        return error;
}


// userlandfs_cancel_io
status_t
userlandfs_cancel_io(fs_volume* fsVolume, fs_vnode* fsNode, void *cookie,
        io_request *request)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_cancel_io(%p, %p, %p, %p)\n", volume,
                fsNode->private_node, cookie, request));
        status_t error = volume->CancelIO(fsNode->private_node, cookie, request);
        PRINT(("userlandfs_cancel_io() done: (%" B_PRIx32 ")\n", error));
        return error;
}


// #pragma mark - common


// userlandfs_ioctl
static status_t
userlandfs_ioctl(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie, uint32 op,
        void* buffer, size_t length)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_ioctl(%p, %p, %p, %" B_PRIu32 ", %p, %" B_PRIuSIZE ")\n",
                volume, fsNode->private_node, cookie, op, buffer, length));
        status_t error = volume->IOCtl(fsNode->private_node, cookie, op, buffer,
                length);
        PRINT(("userlandfs_ioctl() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_set_flags
static status_t
userlandfs_set_flags(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        int flags)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_set_flags(%p, %p, %p, %d)\n", volume,
                fsNode->private_node, cookie, flags));
        status_t error = volume->SetFlags(fsNode->private_node, cookie, flags);
        PRINT(("userlandfs_set_flags() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_select
static status_t
userlandfs_select(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        uint8 event, selectsync* sync)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_select(%p, %p, %p, %hhd, %p)\n", volume,
                fsNode->private_node, cookie, event, sync));
        status_t error = volume->Select(fsNode->private_node, cookie, event, sync);
        PRINT(("userlandfs_select() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_deselect
static status_t
userlandfs_deselect(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        uint8 event, selectsync* sync)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_deselect(%p, %p, %p, %hhd, %p)\n", volume,
                fsNode->private_node, cookie, event, sync));
        status_t error = volume->Deselect(fsNode->private_node, cookie, event,
                sync);
        PRINT(("userlandfs_deselect() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_fsync
static status_t
userlandfs_fsync(fs_volume* fsVolume, fs_vnode* fsNode, bool dataOnly)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_fsync(%p, %p)\n", volume, fsNode->private_node));
        status_t error = volume->FSync(fsNode->private_node);
        PRINT(("userlandfs_fsync() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_read_symlink
static status_t
userlandfs_read_symlink(fs_volume* fsVolume, fs_vnode* fsLink, char* buffer,
        size_t* bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_symlink(%p, %p, %p, %" B_PRIuSIZE ")\n", volume,
                fsLink->private_node, buffer, *bufferSize));
        status_t error = volume->ReadSymlink(fsLink->private_node, buffer,
                *bufferSize, bufferSize);
        PRINT(("userlandfs_read_symlink() done: (%" B_PRIx32 ", %" B_PRIuSIZE ")\n",
                error, *bufferSize));
        return error;
}

// userlandfs_create_symlink
static status_t
userlandfs_create_symlink(fs_volume* fsVolume, fs_vnode* fsDir,
        const char* name, const char* path, int mode)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_create_symlink(%p, %p, `%s', `%s', %d)\n", volume,
                fsDir->private_node, name, path, mode));
        status_t error = volume->CreateSymlink(fsDir->private_node, name, path,
                mode);
        PRINT(("userlandfs_create_symlink() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_link
static status_t
userlandfs_link(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
        fs_vnode* fsNode)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_link(%p, %p, `%s', %p)\n", volume,
                fsDir->private_node, name, fsNode->private_node));
        status_t error = volume->Link(fsDir->private_node, name,
                fsNode->private_node);
        PRINT(("userlandfs_link() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_unlink
static status_t
userlandfs_unlink(fs_volume* fsVolume, fs_vnode* fsDir, const char* name)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_unlink(%p, %p, `%s')\n", volume, fsDir->private_node,
                name));
        status_t error = volume->Unlink(fsDir->private_node, name);
        PRINT(("userlandfs_unlink() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_rename
static status_t
userlandfs_rename(fs_volume* fsVolume, fs_vnode* fsFromDir,
        const char *fromName, fs_vnode* fsToDir, const char *toName)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_rename(%p, %p, `%s', %p, `%s')\n", volume,
                fsFromDir->private_node, fromName, fsToDir->private_node, toName));
        status_t error = volume->Rename(fsFromDir->private_node, fromName,
                fsToDir->private_node, toName);
        PRINT(("userlandfs_rename() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_access
static status_t
userlandfs_access(fs_volume* fsVolume, fs_vnode* fsNode, int mode)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_access(%p, %p, %d)\n", volume, fsNode->private_node,
                mode));
        status_t error = volume->Access(fsNode->private_node, mode);
        PRINT(("userlandfs_access() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_read_stat
static status_t
userlandfs_read_stat(fs_volume* fsVolume, fs_vnode* fsNode, struct stat* st)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_stat(%p, %p, %p)\n", volume, fsNode->private_node,
                st));
        status_t error = volume->ReadStat(fsNode->private_node, st);
        PRINT(("userlandfs_read_stat() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_write_stat
static status_t
userlandfs_write_stat(fs_volume* fsVolume, fs_vnode* fsNode,
        const struct stat* st, uint32 mask)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_write_stat(%p, %p, %p, %" B_PRIu32 ")\n", volume,
                fsNode->private_node, st, mask));
        status_t error = volume->WriteStat(fsNode->private_node, st, mask);
        PRINT(("userlandfs_write_stat() done: %" B_PRIx32 "\n", error));
        return error;
}


// #pragma mark - files


// userlandfs_create
static status_t
userlandfs_create(fs_volume* fsVolume, fs_vnode* fsDir, const char* name,
        int openMode, int perms, void** cookie, ino_t* vnid)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_create(%p, %p, `%s', %d, %d, %p, %p)\n", volume,
                fsDir->private_node, name, openMode, perms, cookie, vnid));
        status_t error = volume->Create(fsDir->private_node, name, openMode, perms,
                cookie, vnid);
        PRINT(("userlandfs_create() done: (%" B_PRIx32 ", %" B_PRIdINO ", %p)\n",
                error, *vnid, *cookie));
        return error;
}

// userlandfs_open
static status_t
userlandfs_open(fs_volume* fsVolume, fs_vnode* fsNode, int openMode,
        void** cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_open(%p, %p, %d)\n", volume, fsNode->private_node,
                openMode));
        status_t error = volume->Open(fsNode->private_node, openMode, cookie);
        PRINT(("userlandfs_open() done: (%" B_PRIx32 ", %p)\n", error, *cookie));
        return error;
}

// userlandfs_close
static status_t
userlandfs_close(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_close(%p, %p, %p)\n", volume, fsNode->private_node,
                cookie));
        status_t error = volume->Close(fsNode->private_node, cookie);
        PRINT(("userlandfs_close() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_free_cookie
static status_t
userlandfs_free_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_free_cookie(%p, %p, %p)\n", volume, fsNode->private_node,
                cookie));
        status_t error = volume->FreeCookie(fsNode->private_node, cookie);
        PRINT(("userlandfs_free_cookie() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_read
static status_t
userlandfs_read(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie, off_t pos,
        void* buffer, size_t* length)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read(%p, %p, %p, %" B_PRIdOFF ", %p, %" B_PRIuSIZE ")\n",
                volume, fsNode->private_node, cookie, pos, buffer, *length));
        status_t error = volume->Read(fsNode->private_node, cookie, pos, buffer,
                *length, length);
        PRINT(("userlandfs_read() done: (%" B_PRIx32 ", %" B_PRIuSIZE ")\n", error,
                *length));
        return error;
}

// userlandfs_write
static status_t
userlandfs_write(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie, off_t pos,
        const void* buffer, size_t* length)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_write(%p, %p, %p, %" B_PRIdOFF ", %p, %" B_PRIuSIZE
                ")\n", volume, fsNode->private_node, cookie, pos, buffer, *length));
        status_t error = volume->Write(fsNode->private_node, cookie, pos, buffer,
                *length, length);
        PRINT(("userlandfs_write() done: (%" B_PRIx32 ", %" B_PRIuSIZE ")\n", error,
                *length));
        return error;
}


// #pragma mark - directories


// userlandfs_create_dir
static status_t
userlandfs_create_dir(fs_volume* fsVolume, fs_vnode* fsParent, const char* name,
        int perms)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_create_dir(%p, %p, `%s', %#x)\n", volume,
                fsParent->private_node, name, perms));
        status_t error = volume->CreateDir(fsParent->private_node, name, perms);
        PRINT(("userlandfs_create_dir() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_remove_dir
static status_t
userlandfs_remove_dir(fs_volume* fsVolume, fs_vnode* fsParent, const char* name)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_remove_dir(%p, %p, `%s')\n", volume,
                fsParent->private_node, name));
        status_t error = volume->RemoveDir(fsParent->private_node, name);
        PRINT(("userlandfs_remove_dir() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_open_dir
static status_t
userlandfs_open_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_open_dir(%p, %p)\n", volume, fsNode->private_node));
        status_t error = volume->OpenDir(fsNode->private_node, cookie);
        PRINT(("userlandfs_open_dir() done: (%" B_PRIx32 ", %p)\n", error,
                *cookie));
        return error;
}

// userlandfs_close_dir
static status_t
userlandfs_close_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_close_dir(%p, %p, %p)\n", volume, fsNode->private_node,
                cookie));
        status_t error = volume->CloseDir(fsNode->private_node, cookie);
        PRINT(("userlandfs_close_dir() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_free_dir_cookie
static status_t
userlandfs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_free_dir_cookie(%p, %p, %p)\n", volume,
                fsNode->private_node, cookie));
        status_t error = volume->FreeDirCookie(fsNode->private_node, cookie);
        PRINT(("userlandfs_free_dir_cookie() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_read_dir
static status_t
userlandfs_read_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        struct dirent* buffer, size_t bufferSize, uint32* count)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_dir(%p, %p, %p, %p, %" B_PRIuSIZE ", %" B_PRIu32
                ")\n", volume, fsNode->private_node, cookie, buffer, bufferSize,
                *count));
        status_t error = volume->ReadDir(fsNode->private_node, cookie, buffer,
                bufferSize, *count, count);
        PRINT(("userlandfs_read_dir() done: (%" B_PRIx32 ", %" B_PRIu32 ")\n",
                error, *count));
        #if DEBUG
                dirent* entry = buffer;
                for (uint32 i = 0; error == B_OK && i < *count; i++) {
                        // R5's kernel vsprintf() doesn't seem to know `%.<number>s', so
                        // we need to work around.
                        char name[B_FILE_NAME_LENGTH];
                        int nameLen = strnlen(entry->d_name, B_FILE_NAME_LENGTH - 1);
                        strncpy(name, entry->d_name, nameLen);
                        name[nameLen] = '\0';
                        PRINT(("  entry: d_dev: %" B_PRIdDEV ", d_pdev: %" B_PRIdDEV
                                ", d_ino: %" B_PRIdINO ", d_pino: %" B_PRIdINO ", "
                                "d_reclen: %" B_PRIu16 ", d_name: `%s'\n",
                                entry->d_dev, entry->d_pdev, entry->d_ino, entry->d_pino,
                                entry->d_reclen, name));
                        entry = (dirent*)((char*)entry + entry->d_reclen);
                }
        #endif
        return error;
}

// userlandfs_rewind_dir
static status_t
userlandfs_rewind_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_rewind_dir(%p, %p, %p)\n", volume, fsNode->private_node,
                cookie));
        status_t error = volume->RewindDir(fsNode->private_node, cookie);
        PRINT(("userlandfs_rewind_dir() done: %" B_PRIx32 "\n", error));
        return error;
}


// #pragma mark - attribute directories


// userlandfs_open_attr_dir
static status_t
userlandfs_open_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_open_attr_dir(%p, %p)\n", volume, fsNode->private_node));
        status_t error = volume->OpenAttrDir(fsNode->private_node, cookie);
        PRINT(("userlandfs_open_attr_dir() done: (%" B_PRIx32 ", %p)\n", error,
                *cookie));
        return error;
}

// userlandfs_close_attr_dir
static status_t
userlandfs_close_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_close_attr_dir(%p, %p, %p)\n", volume,
                fsNode->private_node, cookie));
        status_t error = volume->CloseAttrDir(fsNode->private_node, cookie);
        PRINT(("userlandfs_close_attr_dir() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_free_attr_dir_cookie
static status_t
userlandfs_free_attr_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode,
        void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_free_attr_dir_cookie(%p, %p, %p)\n", volume,
                fsNode->private_node, cookie));
        status_t error = volume->FreeAttrDirCookie(fsNode->private_node, cookie);
        PRINT(("userlandfs_free_attr_dir_cookie() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_read_attr_dir
static status_t
userlandfs_read_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        struct dirent* buffer, size_t bufferSize, uint32* count)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_attr_dir(%p, %p, %p, %p, %" B_PRIuSIZE ", %"
                B_PRIu32 ")\n", volume, fsNode->private_node, cookie, buffer,
                bufferSize, *count));
        status_t error = volume->ReadAttrDir(fsNode->private_node, cookie, buffer,
                bufferSize, *count, count);
        PRINT(("userlandfs_read_attr_dir() done: (%" B_PRIx32 ", %" B_PRIu32 ")\n",
                error, *count));
        return error;
}

// userlandfs_rewind_attr_dir
static status_t
userlandfs_rewind_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_rewind_attr_dir(%p, %p, %p)\n", volume,
                fsNode->private_node, cookie));
        status_t error = volume->RewindAttrDir(fsNode->private_node, cookie);
        PRINT(("userlandfs_rewind_attr_dir() done: (%" B_PRIx32 ")\n", error));
        return error;
}


// #pragma mark - attributes


// userlandfs_create_attr
status_t
userlandfs_create_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        uint32 type, int openMode, void** cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_create_attr(%p, %p, \"%s\", 0x%" B_PRIx32 ", %d, %p)\n",
                volume, fsNode->private_node, name, type, openMode, cookie));
        status_t error = volume->CreateAttr(fsNode->private_node, name, type,
                openMode, cookie);
        PRINT(("userlandfs_create_attr() done: (%" B_PRIx32 ", %p)\n", error,
                *cookie));
        return error;
}

// userlandfs_open_attr
status_t
userlandfs_open_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        int openMode, void** cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_open_attr(%p, %p, \"%s\", %d, %p)\n", volume,
                fsNode->private_node, name, openMode, cookie));
        status_t error = volume->OpenAttr(fsNode->private_node, name, openMode,
                cookie);
        PRINT(("userlandfs_open_attr() done: (%" B_PRIx32 ", %p)\n", error,
                *cookie));
        return error;
}

// userlandfs_close_attr
status_t
userlandfs_close_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_close_attr(%p, %p, %p)\n", volume, fsNode->private_node,
                cookie));
        status_t error = volume->CloseAttr(fsNode->private_node, cookie);
        PRINT(("userlandfs_close_attr() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_free_attr_cookie
status_t
userlandfs_free_attr_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_free_attr_cookie(%p, %p, %p)\n", volume,
                fsNode->private_node, cookie));
        status_t error = volume->FreeAttrCookie(fsNode->private_node, cookie);
        PRINT(("userlandfs_free_attr_cookie() done: %" B_PRIx32 "\n", error));
        return error;
}

// userlandfs_read_attr
static status_t
userlandfs_read_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t pos, void* buffer, size_t* length)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_attr(%p, %p, %p, %" B_PRIdOFF ", %p, %"
                B_PRIuSIZE ")\n", volume, fsNode->private_node, cookie, pos, buffer,
                *length));
        status_t error = volume->ReadAttr(fsNode->private_node, cookie, pos, buffer,
                *length, length);
        PRINT(("userlandfs_read_attr() done: (%" B_PRIx32 ", %" B_PRIuSIZE ")\n",
                error, *length));
        return error;
}

// userlandfs_write_attr
static status_t
userlandfs_write_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t pos, const void* buffer, size_t* length)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_write_attr(%p, %p, %p, %" B_PRIdOFF ", %p, %" B_PRIuSIZE
                ")\n", volume, fsNode->private_node, cookie, pos, buffer, *length));
        status_t error = volume->WriteAttr(fsNode->private_node, cookie, pos,
                buffer, *length, length);
        PRINT(("userlandfs_write_attr() done: (%" B_PRIx32 ", %" B_PRIuSIZE ")\n",
                error, *length));
        return error;
}

// userlandfs_read_attr_stat
static status_t
userlandfs_read_attr_stat(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        struct stat* st)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_attr_stat(%p, %p, %p, %p)\n", volume,
                fsNode->private_node, cookie, st));
        status_t error = volume->ReadAttrStat(fsNode->private_node, cookie, st);
        PRINT(("userlandfs_read_attr_stat() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_write_attr_stat
static status_t
userlandfs_write_attr_stat(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        const struct stat* st, int statMask)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_write_attr_stat(%p, %p, %p, %p, 0x%x)\n", volume,
                fsNode->private_node, cookie, st, statMask));
        status_t error = volume->WriteAttrStat(fsNode->private_node, cookie, st,
                statMask);
        PRINT(("userlandfs_write_attr_stat() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_rename_attr
static status_t
userlandfs_rename_attr(fs_volume* fsVolume, fs_vnode* fsFromNode,
        const char* fromName, fs_vnode* fsToNode, const char* toName)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_rename_attr(%p, %p, `%s', %p, `%s')\n", volume,
                fsFromNode->private_node, fromName, fsToNode->private_node, toName));
        status_t error = volume->RenameAttr(fsFromNode->private_node, fromName,
                fsToNode->private_node, toName);
        PRINT(("userlandfs_rename_attr() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_remove_attr
static status_t
userlandfs_remove_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_remove_attr(%p, %p, `%s')\n", volume,
                fsNode->private_node, name));
        status_t error = volume->RemoveAttr(fsNode->private_node, name);
        PRINT(("userlandfs_remove_attr() done: (%" B_PRIx32 ")\n", error));
        return error;
}


// #pragma mark - indices


// userlandfs_open_index_dir
static status_t
userlandfs_open_index_dir(fs_volume* fsVolume, void** cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_open_index_dir(%p, %p)\n", volume, cookie));
        status_t error = volume->OpenIndexDir(cookie);
        PRINT(("userlandfs_open_index_dir() done: (%" B_PRIx32 ", %p)\n", error,
                *cookie));
        return error;
}

// userlandfs_close_index_dir
static status_t
userlandfs_close_index_dir(fs_volume* fsVolume, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_close_index_dir(%p, %p)\n", volume, cookie));
        status_t error = volume->CloseIndexDir(cookie);
        PRINT(("userlandfs_close_index_dir() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_free_index_dir_cookie
static status_t
userlandfs_free_index_dir_cookie(fs_volume* fsVolume, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_free_index_dir_cookie(%p, %p)\n", volume, cookie));
        status_t error = volume->FreeIndexDirCookie(cookie);
        PRINT(("userlandfs_free_index_dir_cookie() done: (%" B_PRIx32 ")\n",
                error));
        return error;
}

// userlandfs_read_index_dir
static status_t
userlandfs_read_index_dir(fs_volume* fsVolume, void* cookie,
        struct dirent* buffer, size_t bufferSize, uint32* count)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_index_dir(%p, %p, %p, %" B_PRIuSIZE ", %" B_PRIu32
                ")\n", volume, cookie, buffer, bufferSize, *count));
        status_t error = volume->ReadIndexDir(cookie, buffer, bufferSize,
                *count, count);
        PRINT(("userlandfs_read_index_dir() done: (%" B_PRIx32 ", %" B_PRIu32 ")\n",
                error, *count));
        return error;
}

// userlandfs_rewind_index_dir
static status_t
userlandfs_rewind_index_dir(fs_volume* fsVolume, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_rewind_index_dir(%p, %p)\n", volume, cookie));
        status_t error = volume->RewindIndexDir(cookie);
        PRINT(("userlandfs_rewind_index_dir() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_create_index
static status_t
userlandfs_create_index(fs_volume* fsVolume, const char* name, uint32 type,
        uint32 flags)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_create_index(%p, `%s', 0x%" B_PRIx32 ", 0x%" B_PRIx32
                ")\n", volume, name, type, flags));
        status_t error = volume->CreateIndex(name, type, flags);
        PRINT(("userlandfs_create_index() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_remove_index
static status_t
userlandfs_remove_index(fs_volume* fsVolume, const char* name)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_remove_index(%p, `%s')\n", volume, name));
        status_t error = volume->RemoveIndex(name);
        PRINT(("userlandfs_remove_index() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_read_index_stat
static status_t
userlandfs_read_index_stat(fs_volume* fsVolume, const char* name,
        struct stat* st)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_index_stat(%p, `%s', %p)\n", volume, name, st));
        status_t error = volume->ReadIndexStat(name, st);
        PRINT(("userlandfs_read_index_stat() done: (%" B_PRIx32 ")\n", error));
        return error;
}


// #pragma mark - queries


// userlandfs_open_query
static status_t
userlandfs_open_query(fs_volume* fsVolume, const char *queryString,
        uint32 flags, port_id port, uint32 token, void** cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_open_query(%p, `%s', %" B_PRIu32 ", %" B_PRId32 ", %"
                B_PRIu32 ", %p)\n", volume, queryString, flags, port, token, cookie));
        status_t error = volume->OpenQuery(queryString, flags, port, token, cookie);
        PRINT(("userlandfs_open_query() done: (%" B_PRIx32 ", %p)\n", error,
                *cookie));
        return error;
}

// userlandfs_close_query
static status_t
userlandfs_close_query(fs_volume* fsVolume, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_close_query(%p, %p)\n", volume, cookie));
        status_t error = volume->CloseQuery(cookie);
        PRINT(("userlandfs_close_query() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_free_query_cookie
static status_t
userlandfs_free_query_cookie(fs_volume* fsVolume, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_free_query_cookie(%p, %p)\n", volume, cookie));
        status_t error = volume->FreeQueryCookie(cookie);
        PRINT(("userlandfs_free_query_cookie() done: (%" B_PRIx32 ")\n", error));
        return error;
}

// userlandfs_read_query
static status_t
userlandfs_read_query(fs_volume* fsVolume, void* cookie,
        struct dirent* buffer, size_t bufferSize, uint32* count)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_read_query(%p, %p, %p, %" B_PRIuSIZE ", %" B_PRIu32
                ")\n", volume, cookie, buffer, bufferSize, *count));
        status_t error = volume->ReadQuery(cookie, buffer, bufferSize, *count,
                count);
        PRINT(("userlandfs_read_query() done: (%" B_PRIx32 ", %" B_PRIu32 ")\n",
                error, *count));
        #if DEBUG
                if (error == B_OK && *count > 0) {
                        // R5's kernel vsprintf() doesn't seem to know `%.<number>s', so
                        // we need to work around.
                        char name[B_FILE_NAME_LENGTH];
                        int nameLen = strnlen(buffer->d_name, B_FILE_NAME_LENGTH - 1);
                        strncpy(name, buffer->d_name, nameLen);
                        name[nameLen] = '\0';
                        PRINT(("  entry: d_dev: %" B_PRIdDEV ", d_pdev: %" B_PRIdDEV
                                ", d_ino: %" B_PRIdINO ", d_pino: %" B_PRIdINO ", d_reclen: %"
                                B_PRIu16 ", d_name: `%s'\n", buffer->d_dev, buffer->d_pdev,
                                buffer->d_ino, buffer->d_pino, buffer->d_reclen, name));
                }
        #endif
        return error;
}

// userlandfs_rewind_query
static status_t
userlandfs_rewind_query(fs_volume* fsVolume, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        PRINT(("userlandfs_rewind_query(%p, %p)\n", volume, cookie));
        status_t error = volume->RewindQuery(cookie);
        PRINT(("userlandfs_rewind_query() done: (%" B_PRIx32 ")\n", error));
        return error;
}


// userlandfs_initialize
/*
static status_t
userlandfs_initialize(const char *deviceName, void *parameters,
        size_t len)
{
        // get the parameters
        String fsName;
        const char* fsParameters;
        int32 fsParameterLength;
        status_t error = parse_parameters(parameters, len, fsName, &fsParameters,
                &fsParameterLength);
        // make sure there is a UserlandFS we can work with
        UserlandFS* userlandFS = NULL;
        error = UserlandFS::RegisterUserlandFS(&userlandFS);
        if (error != B_OK) {
                exit_debugging();
                return error;
        }
        // get the file system
        FileSystem* fileSystem = NULL;
        if (error == B_OK)
                error = userlandFS->RegisterFileSystem(fsName.GetString(), &fileSystem);
        // initialize the volume
        if (error == B_OK) {
                error = fileSystem->Initialize(deviceName, fsParameters,
                        fsParameterLength);
        }
        // cleanup
        if (fileSystem)
                userlandFS->UnregisterFileSystem(fileSystem);
        UserlandFS::UnregisterUserlandFS();
        return error;
}*/



// #pragma mark ----- module -----


static status_t
userlandfs_std_ops(int32 op, ...)
{
        switch (op) {
                case B_MODULE_INIT:
                {
                        init_debugging();
                        PRINT(("userlandfs_std_ops(): B_MODULE_INIT\n"));

                        // make sure there is a UserlandFS we can work with
                        UserlandFS* userlandFS = NULL;
                        status_t error = UserlandFS::InitUserlandFS(&userlandFS);
                        if (error != B_OK) {
                                exit_debugging();
                                return error;
                        }

                        return B_OK;
                }

                case B_MODULE_UNINIT:
                        PRINT(("userlandfs_std_ops(): B_MODULE_UNINIT\n"));
                        UserlandFS::UninitUserlandFS();
                        exit_debugging();
                        return B_OK;

                default:
                        return B_ERROR;
        }
}


static file_system_module_info sUserlandFSModuleInfo = {
        {
                "file_systems/userlandfs" B_CURRENT_FS_API_VERSION,
                0,
                userlandfs_std_ops,
        },

        "userlandfs",                           // short name
        "Userland File System",         // pretty name
        0,      // DDM flags

        // scanning
        NULL,   // identify_partition()
        NULL,   // scan_partition()
        NULL,   // free_identify_partition_cookie()
        NULL,   // free_partition_content_cookie()

        // general operations
        &userlandfs_mount,

        // capability querying
        NULL,   // get_supported_operations()
        NULL,   // validate_resize()
        NULL,   // validate_move()
        NULL,   // validate_set_content_name()
        NULL,   // validate_set_content_parameters()
        NULL,   // validate_initialize()

        // shadow partition modification
        NULL,   // shadow_changed()

        // writing
        NULL,   // defragment()
        NULL,   // repair()
        NULL,   // resize()
        NULL,   // move()
        NULL,   // set_content_name()
        NULL,   // set_content_parameters()
        NULL    // initialize()
};


fs_volume_ops gUserlandFSVolumeOps = {
        // general operations
        &userlandfs_unmount,
        &userlandfs_read_fs_info,
        &userlandfs_write_fs_info,
        &userlandfs_sync,

        &userlandfs_get_vnode,

        // index directory & index operations
        &userlandfs_open_index_dir,
        &userlandfs_close_index_dir,
        &userlandfs_free_index_dir_cookie,
        &userlandfs_read_index_dir,
        &userlandfs_rewind_index_dir,

        &userlandfs_create_index,
        &userlandfs_remove_index,
        &userlandfs_read_index_stat,

        // query operations
        &userlandfs_open_query,
        &userlandfs_close_query,
        &userlandfs_free_query_cookie,
        &userlandfs_read_query,
        &userlandfs_rewind_query,

        /* support for FS layers */
        NULL,   // all_layers_mounted()
        NULL,   // create_sub_vnode()
        NULL    // delete_sub_vnode()
};


fs_vnode_ops gUserlandFSVnodeOps = {
        // vnode operations
        &userlandfs_lookup,
        &userlandfs_get_vnode_name,
        &userlandfs_put_vnode,
        &userlandfs_remove_vnode,

        // VM file access
        NULL,   // can_page() -- obsolete
        NULL,   // read_pages() -- obsolete
        NULL,   // write_pages() -- obsolete

        // asynchronous I/O
        &userlandfs_io,
        &userlandfs_cancel_io,

        // cache file access
        NULL,   // get_file_map() -- not needed

        // common operations
        &userlandfs_ioctl,
        &userlandfs_set_flags,
        &userlandfs_select,
        &userlandfs_deselect,
        &userlandfs_fsync,

        &userlandfs_read_symlink,
        &userlandfs_create_symlink,

        &userlandfs_link,
        &userlandfs_unlink,
        &userlandfs_rename,

        &userlandfs_access,
        &userlandfs_read_stat,
        &userlandfs_write_stat,
        NULL,   // preallocate()

        // file operations
        &userlandfs_create,
        &userlandfs_open,
        &userlandfs_close,
        &userlandfs_free_cookie,
        &userlandfs_read,
        &userlandfs_write,

        // directory operations
        &userlandfs_create_dir,
        &userlandfs_remove_dir,
        &userlandfs_open_dir,
        &userlandfs_close_dir,
        &userlandfs_free_dir_cookie,
        &userlandfs_read_dir,
        &userlandfs_rewind_dir,

        // attribute directory operations
        &userlandfs_open_attr_dir,
        &userlandfs_close_attr_dir,
        &userlandfs_free_attr_dir_cookie,
        &userlandfs_read_attr_dir,
        &userlandfs_rewind_attr_dir,

        // attribute operations
        &userlandfs_create_attr,
        &userlandfs_open_attr,
        &userlandfs_close_attr,
        &userlandfs_free_attr_cookie,
        &userlandfs_read_attr,
        &userlandfs_write_attr,

        &userlandfs_read_attr_stat,
        &userlandfs_write_attr_stat,
        &userlandfs_rename_attr,
        &userlandfs_remove_attr,

        // support for node and FS layers
        NULL,   // create_special_node()
        NULL    // get_super_vnode()
};


module_info *modules[] = {
        (module_info *)&sUserlandFSModuleInfo,
        NULL,
};