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

#include "Compatibility.h"
#include "Debug.h"
#include "FileSystem.h"
#include "KernelRequestHandler.h"
#include "RequestPort.h"
#include "Requests.h"
#include "SingleReplyRequestHandler.h"
#include "Volume.h"

#include <NodeMonitor.h>

#include <AutoDeleter.h>


// VolumePutter
class VolumePutter {
public:
        VolumePutter(Volume* volume) : fVolume(volume) {}
        ~VolumePutter()
        {
                if (fVolume)
                        fVolume->ReleaseReference();
        }

private:
        Volume  *fVolume;
};

// constructor
KernelRequestHandler::KernelRequestHandler(Volume* volume, uint32 expectedReply)
        : RequestHandler(),
          fFileSystem(volume->GetFileSystem()),
          fVolume(volume),
          fExpectedReply(expectedReply)
{
}

// constructor
KernelRequestHandler::KernelRequestHandler(FileSystem* fileSystem,
        uint32 expectedReply)
        : RequestHandler(),
          fFileSystem(fileSystem),
          fVolume(NULL),
          fExpectedReply(expectedReply)
{
}

// destructor
KernelRequestHandler::~KernelRequestHandler()
{
}

// HandleRequest
status_t
KernelRequestHandler::HandleRequest(Request* request)
{
        if (request->GetType() == fExpectedReply) {
                fDone = true;
                return B_OK;
        }
        switch (request->GetType()) {
                // notifications
                case NOTIFY_LISTENER_REQUEST:
                        return _HandleRequest((NotifyListenerRequest*)request);
                case NOTIFY_SELECT_EVENT_REQUEST:
                        return _HandleRequest((NotifySelectEventRequest*)request);
                case NOTIFY_QUERY_REQUEST:
                        return _HandleRequest((NotifyQueryRequest*)request);
                // vnodes
                case GET_VNODE_REQUEST:
                        return _HandleRequest((GetVNodeRequest*)request);
                case PUT_VNODE_REQUEST:
                        return _HandleRequest((PutVNodeRequest*)request);
                case ACQUIRE_VNODE_REQUEST:
                        return _HandleRequest((AcquireVNodeRequest*)request);
                case NEW_VNODE_REQUEST:
                        return _HandleRequest((NewVNodeRequest*)request);
                case PUBLISH_VNODE_REQUEST:
                        return _HandleRequest((PublishVNodeRequest*)request);
                case REMOVE_VNODE_REQUEST:
                        return _HandleRequest((RemoveVNodeRequest*)request);
                case UNREMOVE_VNODE_REQUEST:
                        return _HandleRequest((UnremoveVNodeRequest*)request);
                case GET_VNODE_REMOVED_REQUEST:
                        return _HandleRequest((GetVNodeRemovedRequest*)request);
                // file cache
                case FILE_CACHE_CREATE_REQUEST:
                        return _HandleRequest((FileCacheCreateRequest*)request);
                case FILE_CACHE_DELETE_REQUEST:
                        return _HandleRequest((FileCacheDeleteRequest*)request);
                case FILE_CACHE_SET_ENABLED_REQUEST:
                        return _HandleRequest((FileCacheSetEnabledRequest*)request);
                case FILE_CACHE_SET_SIZE_REQUEST:
                        return _HandleRequest((FileCacheSetSizeRequest*)request);
                case FILE_CACHE_SYNC_REQUEST:
                        return _HandleRequest((FileCacheSyncRequest*)request);
                case FILE_CACHE_READ_REQUEST:
                        return _HandleRequest((FileCacheReadRequest*)request);
                case FILE_CACHE_WRITE_REQUEST:
                        return _HandleRequest((FileCacheWriteRequest*)request);
                // I/O
                case DO_ITERATIVE_FD_IO_REQUEST:
                        return _HandleRequest((DoIterativeFDIORequest*)request);
                case READ_FROM_IO_REQUEST_REQUEST:
                        return _HandleRequest((ReadFromIORequestRequest*)request);
                case WRITE_TO_IO_REQUEST_REQUEST:
                        return _HandleRequest((WriteToIORequestRequest*)request);
                case NOTIFY_IO_REQUEST_REQUEST:
                        return _HandleRequest((NotifyIORequestRequest*)request);
                // node monitoring
                case ADD_NODE_LISTENER_REQUEST:
                        return _HandleRequest((AddNodeListenerRequest*)request);
                case REMOVE_NODE_LISTENER_REQUEST:
                        return _HandleRequest((RemoveNodeListenerRequest*)request);
        }

        PRINT(("KernelRequestHandler::HandleRequest(): unexpected request: %"
                B_PRIu32 "\n", request->GetType()));
        return B_BAD_DATA;
}

// #pragma mark -
// #pragma mark ----- notifications -----

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(NotifyListenerRequest* request)
{
        // check and execute the request
        status_t result = B_OK;
        if (fVolume && request->device != fVolume->GetID())
                result = B_BAD_VALUE;

        // get the names
        // name
        char* name = (char*)request->name.GetData();
        int32 nameLen = request->name.GetSize();
        if (name && (nameLen <= 0))
                name = NULL;
        else if (name)
                name[nameLen - 1] = '\0';       // NULL-terminate to be safe

        // old name
        char* oldName = (char*)request->oldName.GetData();
        int32 oldNameLen = request->oldName.GetSize();
        if (oldName && (oldNameLen <= 0))
                oldName = NULL;
        else if (oldName)
                oldName[oldNameLen - 1] = '\0'; // NULL-terminate to be safe

        // check the names
        if (result == B_OK) {
                switch (request->operation) {
                        case B_ENTRY_MOVED:
                                if (!oldName) {
                                        ERROR(("NotifyListenerRequest: NULL oldName for "
                                                "B_ENTRY_MOVED\n"));
                                        result = B_BAD_VALUE;
                                        break;
                                }
                                // fall through...
                        case B_ENTRY_CREATED:
                        case B_ENTRY_REMOVED:
                        case B_ATTR_CHANGED:
                                if (!name) {
                                        ERROR(("NotifyListenerRequest: NULL name for opcode: %"
                                                B_PRId32 "\n", request->operation));
                                        result = B_BAD_VALUE;
                                }
                                break;
                        case B_STAT_CHANGED:
                                break;
                }
        }

        // execute the request
        if (result == B_OK) {
                switch (request->operation) {
                        case B_ENTRY_CREATED:
                                PRINT(("notify_entry_created(%" B_PRId32 ", %" B_PRId64 ", "
                                        "\"%s\", %" B_PRId64 ")\n", request->device,
                                        request->directory, name, request->node));
                                result = notify_entry_created(request->device,
                                        request->directory, name, request->node);
                                break;

                        case B_ENTRY_REMOVED:
                                PRINT(("notify_entry_removed(%" B_PRId32 ", %" B_PRId64 ", "
                                        "\"%s\", %" B_PRId64 ")\n", request->device,
                                        request->directory, name, request->node));
                                result = notify_entry_removed(request->device,
                                        request->directory, name, request->node);
                                break;

                        case B_ENTRY_MOVED:
                                PRINT(("notify_entry_moved(%" B_PRId32 ", %" B_PRId64 ", "
                                        "\"%s\", %" B_PRId64 ", \"%s\", %" B_PRId64 ")\n",
                                        request->device, request->oldDirectory, oldName,
                                        request->directory, name, request->node));
                                result = notify_entry_moved(request->device,
                                        request->oldDirectory, oldName, request->directory, name,
                                        request->node);
                                break;

                        case B_STAT_CHANGED:
                                PRINT(("notify_stat_changed(%" B_PRId32 ", %" B_PRId64 ", "
                                        "0x%" B_PRIx32 ")\n", request->device, request->node,
                                        request->details));
                                result = notify_stat_changed(request->device,
                                        request->directory, request->node, request->details);
                                break;

                        case B_ATTR_CHANGED:
                                PRINT(("notify_attribute_changed(%" B_PRId32 ", %" B_PRId64 ", "
                                        "\"%s\", 0x%" B_PRIx32 ")\n", request->device,
                                        request->node, name, (int32)request->details));
                                result = notify_attribute_changed(request->device,
                                        request->directory, request->node, name,
                                        (int32)request->details);
                                break;

                        default:
                                ERROR(("NotifyQueryRequest: unsupported operation: %" B_PRId32
                                        "\n", request->operation));
                                result = B_BAD_VALUE;
                                break;
                }
        }

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        NotifyListenerReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;

        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(NotifySelectEventRequest* request)
{
        // check and execute the request
        status_t result = B_OK;
        if (fFileSystem->KnowsSelectSyncEntry(request->sync)) {
                if (request->unspecifiedEvent) {
                        // old style add-ons can't provide an event argument; we shoot
                        // all events
                        notify_select_event(request->sync, B_SELECT_READ);
                        notify_select_event(request->sync, B_SELECT_WRITE);
                        notify_select_event(request->sync, B_SELECT_ERROR);
                } else {
                        PRINT(("notify_select_event(%p, %d)\n", request->sync,
                                (int)request->event));
                        notify_select_event(request->sync, request->event);
                }
        } else
                result = B_BAD_VALUE;

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        NotifySelectEventReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;

        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(NotifyQueryRequest* request)
{
        // check and execute the request
        status_t result = B_OK;
        if (fVolume && request->device != fVolume->GetID())
                result = B_BAD_VALUE;

        // check the name
        char* name = (char*)request->name.GetData();
        int32 nameLen = request->name.GetSize();
        if (!name || nameLen <= 0) {
                ERROR(("NotifyQueryRequest: NULL name!\n"));
                result = B_BAD_VALUE;
        } else
                name[nameLen - 1] = '\0';       // NULL-terminate to be safe

        // execute the request
        if (result == B_OK) {
                switch (request->operation) {
                        case B_ENTRY_CREATED:
                                PRINT(("notify_query_entry_created(%" B_PRId32 ", %" B_PRId32
                                        ", %" B_PRId32 ", %" B_PRId64 ", \"%s\", %" B_PRId64 ")\n",
                                        request->port, request->token, request->device,
                                        request->directory, name, request->node));
                                result = notify_query_entry_created(request->port,
                                        request->token, request->device, request->directory, name,
                                        request->node);
                                break;

                        case B_ENTRY_REMOVED:
                                PRINT(("notify_query_entry_removed(%" B_PRId32 ", %" B_PRId32
                                        ", %" B_PRId32 ", %" B_PRId64 ", \"%s\", %" B_PRId64 ")\n",
                                        request->port, request->token, request->device,
                                        request->directory, name, request->node));
                                result = notify_query_entry_removed(request->port,
                                        request->token, request->device, request->directory, name,
                                        request->node);
                                break;

                        case B_ENTRY_MOVED:
                        default:
                                ERROR(("NotifyQueryRequest: unsupported operation: %" B_PRId32
                                        "\n", request->operation));
                                result = B_BAD_VALUE;
                                break;
                }
        }

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        NotifyQueryReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;

        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}

// #pragma mark -
// #pragma mark ----- vnodes -----

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(GetVNodeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        void* node;
        if (result == B_OK)
                result = volume->GetVNode(request->vnid, &node);
        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        GetVNodeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;
        reply->node = node;
        // send the reply
        return fPort->SendRequest(&allocator);
}

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(PutVNodeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        if (result == B_OK)
                result = volume->PutVNode(request->vnid);
        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        PutVNodeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;
        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(AcquireVNodeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        if (result == B_OK)
                result = volume->AcquireVNode(request->vnid);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        AcquireVNodeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(NewVNodeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        if (result == B_OK) {
                result = volume->NewVNode(request->vnid, request->node,
                        request->capabilities);
        }

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        NewVNodeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(PublishVNodeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        if (result == B_OK) {
                result = volume->PublishVNode(request->vnid, request->node,
                        request->type, request->flags, request->capabilities);
        }

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        PublishVNodeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;

        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(RemoveVNodeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        if (result == B_OK)
                result = volume->RemoveVNode(request->vnid);
        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        RemoveVNodeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;
        // send the reply
        return fPort->SendRequest(&allocator);
}

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(UnremoveVNodeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        if (result == B_OK)
                result = volume->UnremoveVNode(request->vnid);
        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        UnremoveVNodeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;
        // send the reply
        return fPort->SendRequest(&allocator);
}

// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(GetVNodeRemovedRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);
        bool removed = false;
        if (result == B_OK)
                result = volume->GetVNodeRemoved(request->vnid, &removed);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        GetVNodeRemovedReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;

        reply->error = result;
        reply->removed = removed;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(FileCacheCreateRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        if (result == B_OK)
                result = volume->CreateFileCache(request->vnid, request->size);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        FileCacheCreateReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(FileCacheDeleteRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        if (result == B_OK)
                result = volume->DeleteFileCache(request->vnid);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        FileCacheDeleteReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(FileCacheSetEnabledRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        if (result == B_OK)
                result = volume->SetFileCacheEnabled(request->vnid, request->enabled);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        FileCacheSetEnabledReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(FileCacheSetSizeRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        if (result == B_OK)
                result = volume->SetFileCacheSize(request->vnid, request->size);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        FileCacheSetSizeReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(FileCacheSyncRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        if (result == B_OK)
                result = volume->SyncFileCache(request->vnid);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        FileCacheSyncReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(FileCacheReadRequest* request)
{
        // check the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        size_t size = request->size;

        // allocate the reply
        RequestAllocator allocator(fPort->GetPort());
        FileCacheReadReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                RETURN_ERROR(error);

        void* buffer;
        if (result == B_OK) {
                result = allocator.AllocateAddress(reply->buffer, size, 1, &buffer,
                        true);
        }

        // execute the request
        if (result == B_OK) {
                result = volume->ReadFileCache(request->vnid, request->cookie,
                        request->pos, buffer, &size);
        }

        // prepare the reply
        reply->error = result;
        reply->bytesRead = size;

        // send the reply
        if (reply->error == B_OK && reply->bytesRead > 0) {
                SingleReplyRequestHandler handler(RECEIPT_ACK_REPLY);
                return fPort->SendRequest(&allocator, &handler);
        }

        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(FileCacheWriteRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        size_t size = 0;
        if (result == B_OK) {
                const void* data = request->buffer.GetData();
                size = request->size;
                if (data != NULL) {
                        if (size != (size_t)request->buffer.GetSize())
                                result = B_BAD_DATA;
                }

                if (result == B_OK) {
                        result = volume->WriteFileCache(request->vnid, request->cookie,
                                request->pos, data, &size);
                }
        }

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        FileCacheWriteReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;
        reply->bytesWritten = size;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(DoIterativeFDIORequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        uint32 vecCount = request->vecCount;
        if (result == B_OK && vecCount > DoIterativeFDIORequest::MAX_VECS)
                result = B_BAD_VALUE;

        if (result == B_OK) {
                result = volume->DoIterativeFDIO(request->fd, request->request,
                        request->cookie, request->vecs, vecCount);
        }

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        DoIterativeFDIOReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


status_t
KernelRequestHandler::_HandleRequest(ReadFromIORequestRequest* request)
{
        // check the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        size_t size = request->size;

        // allocate the reply
        RequestAllocator allocator(fPort->GetPort());
        ReadFromIORequestReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                RETURN_ERROR(error);

        void* buffer;
        if (result == B_OK) {
                result = allocator.AllocateAddress(reply->buffer, size, 1, &buffer,
                        true);
        }

        // execute the request
        if (result == B_OK)
                result = volume->ReadFromIORequest(request->request, buffer, size);

        // prepare the reply
        reply->error = result;

        // send the reply
        if (reply->error == B_OK) {
                SingleReplyRequestHandler handler(RECEIPT_ACK_REPLY);
                return fPort->SendRequest(&allocator, &handler);
        }

        return fPort->SendRequest(&allocator);
}


status_t
KernelRequestHandler::_HandleRequest(WriteToIORequestRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        if (result == B_OK) {
                result = volume->WriteToIORequest(request->request,
                        request->buffer.GetData(), request->buffer.GetSize());
        }

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        WriteToIORequestReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _HandleRequest
status_t
KernelRequestHandler::_HandleRequest(NotifyIORequestRequest* request)
{
        // check and execute the request
        Volume* volume = NULL;
        status_t result = _GetVolume(request->nsid, &volume);
        VolumePutter _(volume);

        if (result == B_OK)
                result = volume->NotifyIORequest(request->request, request->status);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        NotifyIORequestReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;
        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


status_t
KernelRequestHandler::_HandleRequest(AddNodeListenerRequest* request)
{
        // check and execute the request
        status_t result = fFileSystem->AddNodeListener(request->device,
                request->node, request->flags, request->listener);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        AddNodeListenerReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;

        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


status_t
KernelRequestHandler::_HandleRequest(RemoveNodeListenerRequest* request)
{
        // check and execute the request
        status_t result = fFileSystem->RemoveNodeListener(request->device,
                request->node, request->listener);

        // prepare the reply
        RequestAllocator allocator(fPort->GetPort());
        RemoveNodeListenerReply* reply;
        status_t error = AllocateRequest(allocator, &reply);
        if (error != B_OK)
                return error;

        reply->error = result;

        // send the reply
        return fPort->SendRequest(&allocator);
}


// _GetVolume
status_t
KernelRequestHandler::_GetVolume(dev_t id, Volume** volume)
{
        if (fVolume) {
                if (fVolume->GetID() != id) {
                        *volume = NULL;
                        return B_BAD_VALUE;
                }
                fVolume->AcquireReference();
                *volume = fVolume;
                return B_OK;
        }
        *volume = fFileSystem->GetVolume(id);
        return (*volume ? B_OK : B_BAD_VALUE);
}