root/src/add-ons/kernel/file_systems/bindfs/kernel_interface.cpp
/*
 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
 * Distributed under the terms of the MIT License.
 */


#include <new>

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

#include <vfs.h>

#include <AutoDeleterDrivers.h>

#include "DebugSupport.h"
#include "kernel_interface.h"
#include "Node.h"
#include "Volume.h"


/*!     \brief Binds an arbitrary folder to a given path (which must be that of a
        folder, too). All requests to the mounted path will be passed to the
        corresponding node of the bound (source) filesystem.

        TODO: node monitoring!

        TODO: path filter, such that /dev can be bind-mounted with only a subset
                  of entries

        TODO: Since the source node IDs are used for our nodes, this doesn't work
                  for source trees with submounts.

        TODO: There's no file cache support (required for mmap()). We implement the
                  hooks, but they aren't used.
*/


// #pragma mark - helper macros


#define FETCH_SOURCE_VOLUME_AND_NODE(volume, nodeID)                            \
        fs_volume* sourceVolume = volume->SourceFSVolume();                             \
        if (sourceVolume == NULL)                                                                               \
                RETURN_ERROR(B_ERROR);                                                                          \
        vnode* sourceVnode;                                                                                             \
        status_t error = vfs_get_vnode(volume->SourceFSVolume()->id,    \
                nodeID, true, &sourceVnode);                                                            \
        if (error != B_OK)                                                                                              \
                RETURN_ERROR(error);                                                                            \
        VnodePutter putter(sourceVnode);                                                                \
        fs_vnode* sourceNode = vfs_fsnode_for_vnode(sourceVnode);               \
        if (sourceNode == NULL)                                                                                 \
                RETURN_ERROR(B_ERROR);


// #pragma mark - Volume


static status_t
bindfs_mount(fs_volume* fsVolume, const char* device, uint32 flags,
        const char* parameters, ino_t* _rootID)
{
        FUNCTION("fsVolume: %p, device: \"%s\", flags: %#" B_PRIx32 ", "
                        "parameters: \"%s\"\n",
                fsVolume, device, flags, parameters);

        // create a Volume object
        Volume* volume = new(std::nothrow) Volume(fsVolume);
        if (volume == NULL)
                RETURN_ERROR(B_NO_MEMORY);
        ObjectDeleter<Volume> volumeDeleter(volume);

        status_t error = volume->Mount(parameters);
        if (error != B_OK)
                return error;

        // set return values
        *_rootID = volume->RootNode()->ID();
        fsVolume->private_volume = volumeDeleter.Detach();
        fsVolume->ops = &gBindFSVolumeOps;

        return B_OK;
}


static status_t
bindfs_unmount(fs_volume* fsVolume)
{
        Volume* volume = (Volume*)fsVolume->private_volume;

        FUNCTION("volume: %p\n", volume);

        volume->Unmount();
        delete volume;

        return B_OK;
}


static status_t
bindfs_read_fs_info(fs_volume* fsVolume, struct fs_info* info)
{
        Volume* volume = (Volume*)fsVolume->private_volume;

        FUNCTION("volume: %p, info: %p\n", volume, info);

        fs_volume* sourceVolume = volume->SourceFSVolume();

        if (sourceVolume->ops->read_fs_info != NULL) {
                status_t error = sourceVolume->ops->read_fs_info(sourceVolume, info);
                if (error != B_OK)
                        RETURN_ERROR(error);
        } else {
                info->block_size = 512;
                info->io_size = 64 * 1024;
        }

        info->dev = volume->ID();
        info->root = volume->RootNode()->ID();
        info->total_blocks = info->free_blocks = 0;
        info->total_nodes = info->free_nodes = 0;

        strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));

        return B_OK;
}


// #pragma mark - VNodes


static status_t
bindfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* entryName,
        ino_t* _vnid)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsDir->private_node;

        FUNCTION("volume: %p, dir: %p (%" B_PRIdINO "), entry: \"%s\"\n",
                volume, node, node->ID(), entryName);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        error = sourceNode->ops->lookup(sourceVolume, sourceNode, entryName, _vnid);
        if (error != B_OK)
                RETURN_ERROR(error);

        error = get_vnode(fsVolume, *_vnid, NULL);

        // lookup() on the source gave us a reference we don't need any longer
        vnode* sourceChildVnode;
        if (vfs_lookup_vnode(sourceVolume->id, *_vnid, &sourceChildVnode) == B_OK)
                vfs_put_vnode(sourceChildVnode);

        return error;
}


static status_t
bindfs_get_vnode(fs_volume* fsVolume, ino_t vnid, fs_vnode* fsNode,
        int* _type, uint32* _flags, bool reenter)
{
        Volume* volume = (Volume*)fsVolume->private_volume;

        FUNCTION("volume: %p, vnid: %" B_PRIdINO "\n", volume, vnid);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, vnid);

        struct stat st;
        error = sourceNode->ops->read_stat(sourceVolume, sourceNode, &st);

        Node* node = new(std::nothrow) Node(vnid, st.st_mode);
        if (node == NULL)
                RETURN_ERROR(B_NO_MEMORY);

        fsNode->private_node = node;
        fsNode->ops = const_cast<fs_vnode_ops*>(volume->VnodeOps());
        *_type = node->Mode() & S_IFMT;
        *_flags = 0;

        return B_OK;
}


static status_t
bindfs_get_vnode_name(fs_volume* fsVolume, fs_vnode* fsNode, char* buffer,
        size_t bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p\n", volume, node);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->get_vnode_name(sourceVolume, sourceNode, buffer,
                bufferSize);
}

static status_t
bindfs_put_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p\n", volume, node);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        delete node;

        return B_OK;
}


static status_t
bindfs_remove_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p\n", volume, node);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        delete node;

        return sourceNode->ops->remove_vnode(sourceVolume, sourceNode, reenter);
}


// #pragma mark - VM access


// TODO: These hooks are obsolete. Since we don't create a file cache, they
// aren't needed anyway.


static bool
bindfs_can_page(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->can_page(sourceVolume, sourceNode, cookie);
}


static status_t
bindfs_read_pages(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, "
                        "pos: %" B_PRIdOFF ", vecs: %p, count: %ld\n",
                volume, node, node->ID(), cookie, pos, vecs, count);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->read_pages(sourceVolume, sourceNode, cookie, pos,
                vecs, count, _numBytes);
}


static status_t
bindfs_write_pages(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, "
                        "pos: %" B_PRIdOFF ", vecs: %p, count: %ld\n",
                volume, node, node->ID(), cookie, pos, vecs, count);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->write_pages(sourceVolume, sourceNode, cookie, pos,
                vecs, count, _numBytes);
}


// #pragma mark - Request I/O


// TODO: Since we don't create a file cache, these hooks aren't needed.


static status_t
bindfs_io(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        io_request* request)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, request: %p\n",
                volume, node, node->ID(), cookie, request);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->io(sourceVolume, sourceNode, cookie, request);
}


static status_t
bindfs_cancel_io(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        io_request* request)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, request: %p\n",
                volume, node, node->ID(), cookie, request);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->cancel_io(sourceVolume, sourceNode, cookie,
                request);
}


// #pragma mark - File Map


static status_t
bindfs_get_file_map(fs_volume* fsVolume, fs_vnode* fsNode, off_t offset,
        size_t size, struct file_io_vec* vecs, size_t* _count)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), offset: %" B_PRIdOFF ", "
                        "size: %ld, vecs: %p\n",
                volume, node, node->ID(), offset, size, vecs);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->get_file_map(sourceVolume, sourceNode, offset, size,
                vecs, _count);
}


// #pragma mark - Special


static status_t
bindfs_ioctl(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie, uint32 op,
        void* buffer, size_t length)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, "
                        "op: %" B_PRIx32 ", buffer: %p, length: %ld\n",
                volume, node, node->ID(), cookie, op, buffer, length);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->ioctl(sourceVolume, sourceNode, cookie, op, buffer,
                length);
}


static status_t
bindfs_set_flags(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie, int flags)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, flags: %x\n",
                volume, node, node->ID(), cookie, flags);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->set_flags(sourceVolume, sourceNode, cookie, flags);
}


static status_t
bindfs_select(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie, uint8 event,
        selectsync* sync)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, event: %x, "
                        "sync: %p\n",
                volume, node, node->ID(), cookie, event, sync);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->select(sourceVolume, sourceNode, cookie, event,
                sync);
}


static status_t
bindfs_deselect(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        uint8 event, selectsync* sync)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, event: %x, "
                        "sync: %p\n",
                volume, node, node->ID(), cookie, event, sync);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->deselect(sourceVolume, sourceNode, cookie, event,
                sync);
}


static status_t
bindfs_fsync(fs_volume* fsVolume, fs_vnode* fsNode, bool dataOnly)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO ")\n",
                volume, node, node->ID());

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->fsync(sourceVolume, sourceNode, dataOnly);
}


// #pragma mark - Nodes


static status_t
bindfs_read_symlink(fs_volume* fsVolume, fs_vnode* fsNode, char* buffer,
        size_t* _bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO ")\n",
                volume, node, node->ID());

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->read_symlink(sourceVolume, sourceNode, buffer,
                _bufferSize);
}


static status_t
bindfs_create_symlink(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        const char* path, int mode)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), "
                        "name: %s, path: %s, mode: %x\n",
                volume, node, node->ID(), name, path, mode);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->create_symlink(sourceVolume, sourceNode, name, path,
                mode);
}


static status_t
bindfs_link(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        fs_vnode* toNode)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), "
                        "name: %s, tonode: %p\n",
                volume, node, node->ID(), name, toNode);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->link(sourceVolume, sourceNode, name, toNode);
}


static status_t
bindfs_unlink(fs_volume* fsVolume, fs_vnode* fsNode, const char* name)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), name: %s\n",
                volume, node, node->ID(), name);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->unlink(sourceVolume, sourceNode, name);
}


static status_t
bindfs_rename(fs_volume* fsVolume, fs_vnode* fsNode, const char* fromName,
        fs_vnode* toDir, const char* toName)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), "
                        "from: %s, toDir: %p, to: %s\n",
                volume, node, node->ID(), fromName, toDir, toName);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->rename(sourceVolume, sourceNode, fromName, toDir,
                toName);
}


static status_t
bindfs_access(fs_volume* fsVolume, fs_vnode* fsNode, int mode)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO" )\n",
                volume, node, node->ID());

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->access(sourceVolume, sourceNode, mode);
}


static status_t
bindfs_read_stat(fs_volume* fsVolume, fs_vnode* fsNode, struct stat* st)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO ")\n",
                volume, node, node->ID());

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        error = sourceNode->ops->read_stat(sourceVolume, sourceNode, st);
        if (error != B_OK)
                RETURN_ERROR(error);

        st->st_dev = volume->ID();

        return B_OK;
}


static status_t
bindfs_write_stat(fs_volume* fsVolume, fs_vnode* fsNode,
        const struct stat* _st, uint32 statMask)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO ")\n",
                volume, node, node->ID());

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        struct stat st;
        memcpy(&st, _st, sizeof(st));
        st.st_dev = sourceVolume->id;

        return sourceNode->ops->write_stat(sourceVolume, sourceNode, &st, statMask);
}


static status_t
bindfs_preallocate(fs_volume* fsVolume, fs_vnode* fsNode, off_t pos,
        off_t length)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), pos: %" B_PRIdOFF ", "
                        "length: %" B_PRIdOFF "\n",
                volume, node, node->ID(), pos, length);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->preallocate(sourceVolume, sourceNode, pos, length);
}


// #pragma mark - Files


static status_t
bindfs_create(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        int openMode, int perms, void** _cookie, ino_t* _newVnodeID)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), "
                        "name: %s, openMode %#x, perms: %x\n",
                volume, node, node->ID(), name, openMode, perms);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        error = sourceNode->ops->create(sourceVolume, sourceNode, name, openMode,
                perms, _cookie, _newVnodeID);
        if (error != B_OK)
                return error;

        error = get_vnode(fsVolume, *_newVnodeID, NULL);

        // on error remove the newly created source entry
        if (error != B_OK)
                sourceNode->ops->unlink(sourceVolume, sourceNode, name);

        // create() on the source gave us a reference we don't need any longer
        vnode* newSourceVnode;
        if (vfs_lookup_vnode(sourceVolume->id, *_newVnodeID, &newSourceVnode)
                        == B_OK) {
                vfs_put_vnode(newSourceVnode);
        }

        return error;

}


static status_t
bindfs_open(fs_volume* fsVolume, fs_vnode* fsNode, int openMode,
        void** _cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), openMode %#x\n",
                volume, node, node->ID(), openMode);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->open(sourceVolume, sourceNode, openMode, _cookie);
}


static status_t
bindfs_close(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->close(sourceVolume, sourceNode, cookie);
}


static status_t
bindfs_free_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->free_cookie(sourceVolume, sourceNode, cookie);
}


static status_t
bindfs_read(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t offset, void* buffer, size_t* bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, "
                        "offset: %" B_PRIdOFF ", buffer: %p, size: %lu\n",
                volume, node, node->ID(), cookie, offset, buffer, *bufferSize);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->read(sourceVolume, sourceNode, cookie, offset,
                buffer, bufferSize);
}


static status_t
bindfs_write(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t offset, const void* buffer, size_t* bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p, "
                        "offset: %" B_PRIdOFF ", buffer: %p, size: %lu\n",
                volume, node, node->ID(), cookie, offset, buffer, *bufferSize);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->write(sourceVolume, sourceNode, cookie, offset,
                buffer, bufferSize);
}


// #pragma mark - Directories


static status_t
bindfs_create_dir(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        int perms)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), name: %s, perms: %x\n",
                volume, node, node->ID(), name, perms);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->create_dir(sourceVolume, sourceNode, name, perms);
}


static status_t
bindfs_remove_dir(fs_volume* fsVolume, fs_vnode* fsNode, const char* name)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), name: %s\n", volume, node,
                node->ID(), name);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->remove_dir(sourceVolume, sourceNode, name);
}


static status_t
bindfs_open_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** _cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO ")\n",
                volume, node, node->ID());

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->open_dir(sourceVolume, sourceNode, _cookie);
}


static status_t
bindfs_close_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->close_dir(sourceVolume, sourceNode, cookie);
}


static status_t
bindfs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->free_dir_cookie(sourceVolume, sourceNode, cookie);
}


static status_t
bindfs_read_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        struct dirent* buffer, size_t bufferSize, uint32* _count)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->read_dir(sourceVolume, sourceNode, cookie, buffer,
                bufferSize, _count);
}


static status_t
bindfs_rewind_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->rewind_dir(sourceVolume, sourceNode, cookie);
}


// #pragma mark - Attribute Directories


status_t
bindfs_open_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** _cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO ")\n",
                volume, node, node->ID());

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->open_attr_dir(sourceVolume, sourceNode, _cookie);
}


status_t
bindfs_close_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->close_attr_dir(sourceVolume, sourceNode, cookie);
}


status_t
bindfs_free_attr_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode,
        void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->free_attr_dir_cookie(sourceVolume, sourceNode,
                cookie);
}


status_t
bindfs_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;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->read_attr_dir(sourceVolume, sourceNode, cookie,
                buffer, bufferSize, _count);
}


status_t
bindfs_rewind_attr_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->rewind_attr_dir(sourceVolume, sourceNode, cookie);
}


// #pragma mark - Attribute Operations


status_t
bindfs_create_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        uint32 type, int openMode, void** _cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), name: \"%s\", "
                        "type: %" B_PRIx32 ", openMode %#x\n",
                volume, node, node->ID(), name, type, openMode);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->create_attr(sourceVolume, sourceNode, name, type,
                openMode, _cookie);
}


status_t
bindfs_open_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name,
        int openMode, void** _cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), name: \"%s\", "
                        "openMode %#x\n",
                volume, node, node->ID(), name, openMode);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->open_attr(sourceVolume, sourceNode, name, openMode,
                _cookie);
}


status_t
bindfs_close_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->close_attr(sourceVolume, sourceNode, cookie);
}


status_t
bindfs_free_attr_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->free_attr_cookie(sourceVolume, sourceNode, cookie);
}


status_t
bindfs_read_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t offset, void* buffer, size_t* bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->read_attr(sourceVolume, sourceNode, cookie, offset,
                buffer, bufferSize);
}


status_t
bindfs_write_attr(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        off_t offset, const void* buffer, size_t* bufferSize)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->write_attr(sourceVolume, sourceNode, cookie, offset,
                buffer, bufferSize);
}


status_t
bindfs_read_attr_stat(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        struct stat* st)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        error
                = sourceNode->ops->read_attr_stat(sourceVolume, sourceNode, cookie, st);
        if (error != B_OK)
                RETURN_ERROR(error);

        st->st_dev = volume->ID();

        return B_OK;
}


status_t
bindfs_write_attr_stat(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie,
        const struct stat* _st, int statMask)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO"), cookie: %p\n",
                volume, node, node->ID(), cookie);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        struct stat st;
        memcpy(&st, _st, sizeof(st));
        st.st_dev = sourceVolume->id;

        return sourceNode->ops->write_attr_stat(sourceVolume, sourceNode, cookie,
                &st, statMask);
}


static status_t
bindfs_rename_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* fromName,
        fs_vnode* toDir, const char* toName)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), from: %s, toDir: %p, "
                        "to: %s\n",
                volume, node, node->ID(), fromName, toDir, toName);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->rename_attr(sourceVolume, sourceNode, fromName,
                toDir, toName);
}


static status_t
bindfs_remove_attr(fs_volume* fsVolume, fs_vnode* fsNode, const char* name)
{
        Volume* volume = (Volume*)fsVolume->private_volume;
        Node* node = (Node*)fsNode->private_node;

        FUNCTION("volume: %p, node: %p (%" B_PRIdINO "), name: %s\n",
                volume, node, node->ID(), name);

        FETCH_SOURCE_VOLUME_AND_NODE(volume, node->ID());

        return sourceNode->ops->remove_attr(sourceVolume, sourceNode, name);
}


// #pragma mark - Module Interface


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

                        return B_OK;
                }

                case B_MODULE_UNINIT:
                {
                        PRINT("bind_std_ops(): B_MODULE_UNINIT\n");
                        exit_debugging();
                        return B_OK;
                }

                default:
                        return B_ERROR;
        }
}


static file_system_module_info sBindFSModuleInfo = {
        {
                "file_systems/bindfs" B_CURRENT_FS_API_VERSION,
                0,
                bindfs_std_ops,
        },

        "bindfs",                               // short_name
        "Bind File System",             // pretty_name
        0,                                              // DDM flags


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

        &bindfs_mount
};


fs_volume_ops gBindFSVolumeOps = {
        &bindfs_unmount,
        &bindfs_read_fs_info,
        NULL,   // write_fs_info,
        NULL,   // sync,

        &bindfs_get_vnode

        // TODO: index operations
        // TODO: query operations
        // TODO: FS layer operations
};


fs_vnode_ops gBindFSVnodeOps = {
        // vnode operations
        &bindfs_lookup,
        &bindfs_get_vnode_name,
        &bindfs_put_vnode,
        &bindfs_remove_vnode,

        // VM file access
        &bindfs_can_page,
        &bindfs_read_pages,
        &bindfs_write_pages,

        &bindfs_io,
        &bindfs_cancel_io,

        &bindfs_get_file_map,

        &bindfs_ioctl,
        &bindfs_set_flags,
        &bindfs_select,
        &bindfs_deselect,
        &bindfs_fsync,

        &bindfs_read_symlink,
        &bindfs_create_symlink,

        &bindfs_link,
        &bindfs_unlink,
        &bindfs_rename,

        &bindfs_access,
        &bindfs_read_stat,
        &bindfs_write_stat,
        &bindfs_preallocate,

        // file operations
        &bindfs_create,
        &bindfs_open,
        &bindfs_close,
        &bindfs_free_cookie,
        &bindfs_read,
        &bindfs_write,

        // directory operations
        &bindfs_create_dir,
        &bindfs_remove_dir,
        &bindfs_open_dir,
        &bindfs_close_dir,
        &bindfs_free_dir_cookie,
        &bindfs_read_dir,
        &bindfs_rewind_dir,

        // attribute directory operations
        &bindfs_open_attr_dir,
        &bindfs_close_attr_dir,
        &bindfs_free_attr_dir_cookie,
        &bindfs_read_attr_dir,
        &bindfs_rewind_attr_dir,

        // attribute operations
        &bindfs_create_attr,
        &bindfs_open_attr,
        &bindfs_close_attr,
        &bindfs_free_attr_cookie,
        &bindfs_read_attr,
        &bindfs_write_attr,

        &bindfs_read_attr_stat,
        &bindfs_write_attr_stat,
        &bindfs_rename_attr,
        &bindfs_remove_attr,

        // TODO: FS layer operations
};


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