/* * 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 #include // 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 && size > 0) { 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); }