/* * Copyright 2001-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include "Volume.h" #include #include #include #include #include #include #include #include #include // kernel private #include #include #include "IORequest.h" // kernel internal #include "Compatibility.h" #include "Debug.h" #include "FileSystem.h" #include "IOCtlInfo.h" #include "kernel_interface.h" #include "KernelRequestHandler.h" #include "PortReleaser.h" #include "RequestAllocator.h" #include "RequestPort.h" #include "Requests.h" #include "userlandfs_ioctl.h" // missing ioctl()s // TODO: Place somewhere else. #define IOCTL_FILE_UNCACHED_IO 10000 #define IOCTL_CREATE_TIME 10002 #define IOCTL_MODIFIED_TIME 10003 // If a thread of the userland server enters userland FS kernel code and // is sending a request, this is the time after which it shall time out // waiting for a reply. static const bigtime_t kUserlandServerlandPortTimeout = 10000000; // 10s // VNode struct Volume::VNode { ino_t id; void* clientNode; void* fileCache; VNodeOps* ops; int32 useCount; bool valid; bool published; VNode* hash_link; VNode(ino_t id, void* clientNode, VNodeOps* ops) : id(id), clientNode(clientNode), fileCache(NULL), ops(ops), useCount(0), valid(true), published(true) { } void Delete(Volume* volume) { if (ops != NULL) volume->GetFileSystem()->PutVNodeOps(ops); delete this; } protected: // should be private, but gcc 2.95.3 issues a warning ~VNode() { if (fileCache != NULL) { ERROR(("VNode %" B_PRId64 " still has a file cache!\n", id)); file_cache_delete(fileCache); } } }; // VNodeHashDefinition struct Volume::VNodeHashDefinition { typedef ino_t KeyType; typedef VNode ValueType; size_t HashKey(ino_t key) const { return (uint32)key ^ (uint32)(key >> 32); } size_t Hash(const VNode* value) const { return HashKey(value->id); } bool Compare(ino_t key, const VNode* value) const { return value->id == key; } VNode*& GetLink(VNode* value) const { return value->hash_link; } }; // VNodeMap struct Volume::VNodeMap : public BOpenHashTable { }; // IORequestInfo struct Volume::IORequestInfo { io_request* request; int32 id; IORequestInfo* idLink; IORequestInfo* structLink; IORequestInfo(io_request* request, int32 id) : request(request), id(id) { } }; // IORequestIDHashDefinition struct Volume::IORequestIDHashDefinition { typedef int32 KeyType; typedef IORequestInfo ValueType; size_t HashKey(int32 key) const { return key; } size_t Hash(const IORequestInfo* value) const { return HashKey(value->id); } bool Compare(int32 key, const IORequestInfo* value) const { return value->id == key; } IORequestInfo*& GetLink(IORequestInfo* value) const { return value->idLink; } }; // IORequestStructHashDefinition struct Volume::IORequestStructHashDefinition { typedef io_request* KeyType; typedef IORequestInfo ValueType; size_t HashKey(io_request* key) const { return (size_t)(addr_t)key; } size_t Hash(const IORequestInfo* value) const { return HashKey(value->request); } bool Compare(io_request* key, const IORequestInfo* value) const { return value->request == key; } IORequestInfo*& GetLink(IORequestInfo* value) const { return value->structLink; } }; // IORequestIDMap struct Volume::IORequestIDMap : public BOpenHashTable { }; // IORequestStructMap struct Volume::IORequestStructMap : public BOpenHashTable { }; // IterativeFDIOCookie struct Volume::IterativeFDIOCookie : public BReferenceable { Volume* volume; int fd; int32 requestID; void* clientCookie; off_t offset; const file_io_vec* vecs; uint32 vecCount; IterativeFDIOCookie(Volume* volume, int fd, int32 requestID, void* clientCookie, off_t offset, const file_io_vec* vecs, uint32 vecCount) : volume(volume), fd(fd), requestID(requestID), clientCookie(clientCookie), offset(offset), vecs(vecs), vecCount(vecCount) { } ~IterativeFDIOCookie() { if (fd >= 0) close(fd); } }; // AutoIncrementer class Volume::AutoIncrementer { public: AutoIncrementer(int32* variable) : fVariable(variable) { if (fVariable) atomic_add(fVariable, 1); } ~AutoIncrementer() { if (fVariable) atomic_add(fVariable, -1); } void Keep() { fVariable = NULL; } private: int32* fVariable; }; // IORequestRemover class Volume::IORequestRemover { public: IORequestRemover(Volume* volume, int32 requestID) : fVolume(volume), fRequestID(requestID) { } ~IORequestRemover() { if (fVolume != NULL) fVolume->_UnregisterIORequest(fRequestID); } void Detach() { fVolume = NULL; } private: Volume* fVolume; int32 fRequestID; }; // VNodeRemover class Volume::VNodeRemover { public: VNodeRemover(Volume* volume, VNode* node) : fVolume(volume), fNode(node) { } ~VNodeRemover() { if (fNode != NULL) { MutexLocker locker(fVolume->fLock); fVolume->fVNodes->Remove(fNode); locker.Unlock(); fNode->Delete(fVolume); } } private: Volume* fVolume; VNode* fNode; }; // HasVNodeCapability inline bool Volume::HasVNodeCapability(VNode* vnode, int capability) const { return vnode->ops->capabilities.Get(capability); } // constructor Volume::Volume(FileSystem* fileSystem, fs_volume* fsVolume) : BReferenceable(), fFileSystem(fileSystem), fFSVolume(fsVolume), fUserlandVolume(NULL), fRootID(0), fRootNode(NULL), fOpenFiles(0), fOpenDirectories(0), fOpenAttributeDirectories(0), fOpenAttributes(0), fOpenIndexDirectories(0), fOpenQueries(0), fVNodes(NULL), fIORequestInfosByID(NULL), fIORequestInfosByStruct(NULL), fLastIORequestID(0), fVNodeCountingEnabled(false) { mutex_init(&fLock, "userlandfs volume"); } // destructor Volume::~Volume() { mutex_destroy(&fLock); delete fIORequestInfosByID; delete fIORequestInfosByStruct; delete fVNodes; } // #pragma mark - client methods // GetVNode status_t Volume::GetVNode(ino_t vnid, void** _node) { PRINT(("get_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid)); void* vnode; status_t error = get_vnode(fFSVolume, vnid, &vnode); if (error == B_OK) { _IncrementVNodeCount(vnid); *_node = ((VNode*)vnode)->clientNode; } return error; } // PutVNode status_t Volume::PutVNode(ino_t vnid) { PRINT(("put_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid)); // Decrement the count first. We might not have another chance, since // put_vnode() could put the last reference, thus causing the node to be // removed from our map. This is all not very dramatic, but this way we // avoid an erroneous error message from _DecrementVNodeCount(). _DecrementVNodeCount(vnid); return put_vnode(fFSVolume, vnid); } // AcquireVNode status_t Volume::AcquireVNode(ino_t vnid) { PRINT(("acquire_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid)); status_t error = acquire_vnode(fFSVolume, vnid); if (error == B_OK) _IncrementVNodeCount(vnid); return error; } // NewVNode status_t Volume::NewVNode(ino_t vnid, void* clientNode, const FSVNodeCapabilities& capabilities) { PRINT(("new_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid)); // lookup the node MutexLocker locker(fLock); VNode* node = fVNodes->Lookup(vnid); if (node != NULL) { // The node is already known -- this is an error. RETURN_ERROR(B_BAD_VALUE); } // get the ops vector for the node VNodeOps* ops = fFileSystem->GetVNodeOps(capabilities); if (ops == NULL) RETURN_ERROR(B_NO_MEMORY); // create the node node = new(std::nothrow) VNode(vnid, clientNode, ops); if (node == NULL) { fFileSystem->PutVNodeOps(ops); RETURN_ERROR(B_NO_MEMORY); } node->published = false; locker.Unlock(); // tell the VFS status_t error = new_vnode(fFSVolume, vnid, node, node->ops->ops); if (error != B_OK) { node->Delete(this); RETURN_ERROR(error); } // add the node to our map locker.Lock(); fVNodes->Insert(node); // Increment its use count. After new_vnode() the caller has a reference, // but a subsequent publish_vnode() won't get another one. We handle that // there. if (fVNodeCountingEnabled) node->useCount++; return B_OK; } // PublishVNode status_t Volume::PublishVNode(ino_t vnid, void* clientNode, int type, uint32 flags, const FSVNodeCapabilities& capabilities) { PRINT(("publish_vnode(%" B_PRId32 ", %" B_PRId64 ", %p)\n", GetID(), vnid, clientNode)); // lookup the node MutexLocker locker(fLock); VNode* node = fVNodes->Lookup(vnid); bool nodeKnown = node != NULL; if (nodeKnown) { if (node->published) { WARN(("publish_vnode(): vnode (%" B_PRId32 ", %" B_PRId64 ") already published!\n", GetID(), vnid)); RETURN_ERROR(B_BAD_VALUE); } } else if (!nodeKnown) { // The node is not yet known -- create it. // get the ops vector for the node VNodeOps* ops = fFileSystem->GetVNodeOps(capabilities); if (ops == NULL) RETURN_ERROR(B_NO_MEMORY); // create the node node = new(std::nothrow) VNode(vnid, clientNode, ops); if (node == NULL) { fFileSystem->PutVNodeOps(ops); RETURN_ERROR(B_NO_MEMORY); } } locker.Unlock(); // tell the VFS status_t error = publish_vnode(fFSVolume, vnid, node, node->ops->ops, type, flags); if (error != B_OK) { if (nodeKnown) { // The node was known, i.e. it had been made known via new_vnode() // and thus already had a use count of 1. Decrement that use count // and remove the node completely. _DecrementVNodeCount(vnid); _RemoveInvalidVNode(vnid); } else node->Delete(this); RETURN_ERROR(error); } // add the node to our map, if not known yet locker.Lock(); if (nodeKnown) { // The node is now published. Don't increment its use count. It already // has 1 from new_vnode() and this publish_vnode() didn't increment it. node->published = true; } else { // New node: increment its use count and add it to the map. if (fVNodeCountingEnabled) node->useCount++; fVNodes->Insert(node); } return B_OK; } // RemoveVNode status_t Volume::RemoveVNode(ino_t vnid) { PRINT(("remove_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid)); return remove_vnode(fFSVolume, vnid); } // UnremoveVNode status_t Volume::UnremoveVNode(ino_t vnid) { PRINT(("unremove_vnode(%" B_PRId32 ", %" B_PRId64 ")\n", GetID(), vnid)); return unremove_vnode(fFSVolume, vnid); } // GetVNodeRemoved status_t Volume::GetVNodeRemoved(ino_t vnid, bool* removed) { PRINT(("get_vnode_removed(%" B_PRId32 ", %" B_PRId64 ", %p)\n", GetID(), vnid, removed)); return get_vnode_removed(fFSVolume, vnid, removed); } // CreateFileCache status_t Volume::CreateFileCache(ino_t vnodeID, off_t size) { // lookup the node MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) RETURN_ERROR(B_BAD_VALUE); // does the node already have a file cache? if (vnode->fileCache != NULL) RETURN_ERROR(B_BAD_VALUE); // create the file cache locker.Unlock(); void* fileCache = file_cache_create(GetID(), vnodeID, size); locker.Lock(); // re-check whether the node still lives vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) { file_cache_delete(fileCache); RETURN_ERROR(B_BAD_VALUE); } vnode->fileCache = fileCache; return B_OK; } // DeleteFileCache status_t Volume::DeleteFileCache(ino_t vnodeID) { // lookup the node MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) RETURN_ERROR(B_BAD_VALUE); // does the node have a file cache if (vnode->fileCache == NULL) RETURN_ERROR(B_BAD_VALUE); void* fileCache = vnode->fileCache; vnode->fileCache = NULL; locker.Unlock(); file_cache_delete(fileCache); return B_OK; } // SetFileCacheEnabled status_t Volume::SetFileCacheEnabled(ino_t vnodeID, bool enabled) { // lookup the node MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) RETURN_ERROR(B_BAD_VALUE); // does the node have a file cache if (vnode->fileCache == NULL) RETURN_ERROR(B_BAD_VALUE); void* fileCache = vnode->fileCache; locker.Unlock(); // TODO: We should use some kind of ref counting to avoid that another thread // deletes the file cache now that we have dropped the lock. Applies to the // other file cache operations as well. // enable/disable the file cache if (enabled) { file_cache_enable(fileCache); return B_OK; } return file_cache_disable(fileCache); } // SetFileCacheSize status_t Volume::SetFileCacheSize(ino_t vnodeID, off_t size) { // lookup the node MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) RETURN_ERROR(B_BAD_VALUE); // does the node have a file cache if (vnode->fileCache == NULL) RETURN_ERROR(B_BAD_VALUE); void* fileCache = vnode->fileCache; locker.Unlock(); // set the size return file_cache_set_size(fileCache, size); } // SyncFileCache status_t Volume::SyncFileCache(ino_t vnodeID) { // lookup the node MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) RETURN_ERROR(B_BAD_VALUE); // does the node have a file cache if (vnode->fileCache == NULL) RETURN_ERROR(B_BAD_VALUE); void* fileCache = vnode->fileCache; locker.Unlock(); // sync return file_cache_sync(fileCache); } // ReadFileCache status_t Volume::ReadFileCache(ino_t vnodeID, void* cookie, off_t offset, void* buffer, size_t* _size) { // lookup the node MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) RETURN_ERROR(B_BAD_VALUE); // does the node have a file cache if (vnode->fileCache == NULL) RETURN_ERROR(B_BAD_VALUE); void* fileCache = vnode->fileCache; locker.Unlock(); // read return file_cache_read(fileCache, cookie, offset, buffer, _size); } // WriteFileCache status_t Volume::WriteFileCache(ino_t vnodeID, void* cookie, off_t offset, const void* buffer, size_t* _size) { // lookup the node MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnodeID); if (vnode == NULL) RETURN_ERROR(B_BAD_VALUE); // does the node have a file cache if (vnode->fileCache == NULL) RETURN_ERROR(B_BAD_VALUE); void* fileCache = vnode->fileCache; locker.Unlock(); // read return file_cache_write(fileCache, cookie, offset, buffer, _size); } status_t Volume::ReadFromIORequest(int32 requestID, void* buffer, size_t size) { // get the request io_request* request; status_t error = _FindIORequest(requestID, &request); if (error != B_OK) RETURN_ERROR(error); return read_from_io_request(request, buffer, size); } status_t Volume::WriteToIORequest(int32 requestID, const void* buffer, size_t size) { // get the request io_request* request; status_t error = _FindIORequest(requestID, &request); if (error != B_OK) RETURN_ERROR(error); return write_to_io_request(request, buffer, size); } // DoIterativeFDIO status_t Volume::DoIterativeFDIO(int fd, int32 requestID, void* clientCookie, const file_io_vec* vecs, uint32 vecCount) { // get the request io_request* request; status_t error = _FindIORequest(requestID, &request); if (error != B_OK) RETURN_ERROR(error); // copy the FD into the kernel fd = dup_foreign_fd(fFileSystem->GetTeam(), fd, true); if (fd < 0) RETURN_ERROR(fd); // create a cookie IterativeFDIOCookie* cookie = new(std::nothrow) IterativeFDIOCookie( this, fd, requestID, clientCookie, request->Offset(), vecs, vecCount); if (cookie == NULL) { close(fd); RETURN_ERROR(B_NO_MEMORY); } // we need another reference, so we can still access the cookie below cookie->AcquireReference(); // TODO: Up to this point we're responsible for calling the finished hook on // error! // call the kernel function error = do_iterative_fd_io(fd, request, &_IterativeFDIOGetVecs, &_IterativeFDIOFinished, cookie); // unset the vecs -- they are on the stack an will become invalid when we // return MutexLocker _(fLock); cookie->vecs = NULL; cookie->vecCount = 0; cookie->ReleaseReference(); return error; } status_t Volume::NotifyIORequest(int32 requestID, status_t status) { // get the request io_request* request; status_t error = _FindIORequest(requestID, &request); if (error != B_OK) RETURN_ERROR(error); notify_io_request(request, status); return B_OK; } // #pragma mark - FS // Mount status_t Volume::Mount(const char* device, uint32 flags, const char* parameters) { // create the maps fVNodes = new(std::nothrow) VNodeMap; fIORequestInfosByID = new(std::nothrow) IORequestIDMap; fIORequestInfosByStruct = new(std::nothrow) IORequestStructMap; if (fVNodes == NULL || fIORequestInfosByID == NULL || fIORequestInfosByStruct == NULL || fVNodes->Init() != B_OK || fIORequestInfosByID->Init() != B_OK || fIORequestInfosByStruct->Init() != B_OK) { return B_NO_MEMORY; } // enable vnode counting fVNodeCountingEnabled = true; // init IORequest ID's fLastIORequestID = 0; // mount status_t error = _Mount(device, flags, parameters); if (error != B_OK) RETURN_ERROR(error); MutexLocker locker(fLock); // fetch the root node, so that we can serve Walk() requests on it, // after the connection to the userland server is gone fRootNode = fVNodes->Lookup(fRootID); if (fRootNode == NULL) { // The root node was not added while mounting. That's a serious // problem -- not only because we don't have it, but also because // the VFS requires publish_vnode() to be invoked for the root node. ERROR(("Volume::Mount(): new_vnode() was not called for root node! " "Unmounting...\n")); locker.Unlock(); Unmount(); return B_ERROR; } // Decrement the root node use count. The publish_vnode() the client FS // did will be balanced by the VFS. if (fVNodeCountingEnabled) fRootNode->useCount--; // init the volume ops vector we'll give the VFS _InitVolumeOps(); return B_OK; } // Unmount status_t Volume::Unmount() { status_t error = _Unmount(); // free the memory associated with the maps { // vnodes MutexLocker _(fLock); if (fVNodes != NULL) { VNode* node = fVNodes->Clear(true); while (node != NULL) { VNode* nextNode = node->hash_link; node->Delete(this); node = nextNode; } delete fVNodes; fVNodes = NULL; } // io request infos if (fIORequestInfosByID != NULL) { fIORequestInfosByID->Clear(); delete fIORequestInfosByID; fIORequestInfosByID = NULL; } if (fIORequestInfosByStruct != NULL) { IORequestInfo* info = fIORequestInfosByStruct->Clear(true); while (info != NULL) { IORequestInfo* nextInfo = info->structLink; delete info; // TODO: We should probably also notify the request, if that // hasn't happened yet. info = nextInfo; } delete fIORequestInfosByStruct; fIORequestInfosByStruct = NULL; } } fFileSystem->VolumeUnmounted(this); return error; } // Sync status_t Volume::Sync() { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_SYNC)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); SyncVolumeRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; // send the request KernelRequestHandler handler(this, SYNC_VOLUME_REPLY); SyncVolumeReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // ReadFSInfo status_t Volume::ReadFSInfo(fs_info* info) { // When the connection to the userland server is lost, we serve // read_fs_info() requests manually. status_t error = _ReadFSInfo(info); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { WARN(("Volume::ReadFSInfo(): connection lost, emulating lookup `.'\n")); info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY; info->block_size = 512; info->io_size = 512; info->total_blocks = 0; info->free_blocks = 0; strlcpy(info->volume_name, fFileSystem->GetName(), sizeof(info->volume_name)); strlcat(info->volume_name, ":disconnected", sizeof(info->volume_name)); error = B_OK; } return error; } // WriteFSInfo status_t Volume::WriteFSInfo(const struct fs_info *info, uint32 mask) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_WRITE_FS_INFO)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); WriteFSInfoRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->info = *info; request->mask = mask; // send the request KernelRequestHandler handler(this, WRITE_FS_INFO_REPLY); WriteFSInfoReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // #pragma mark - vnodes // Lookup status_t Volume::Lookup(void* dir, const char* entryName, ino_t* vnid) { // When the connection to the userland server is lost, we serve // lookup(fRootNode, `.') requests manually to allow clean unmounting. status_t error = _Lookup(dir, entryName, vnid); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected() && dir == fRootNode && strcmp(entryName, ".") == 0) { WARN(("Volume::Lookup(): connection lost, emulating lookup `.'\n")); void* entryNode; if (GetVNode(fRootID, &entryNode) != B_OK) RETURN_ERROR(B_BAD_VALUE); *vnid = fRootID; // The VFS will balance the get_vnode() call for the FS. _DecrementVNodeCount(*vnid); return B_OK; } return error; } // GetVNodeName status_t Volume::GetVNodeName(void* _node, char* buffer, size_t bufferSize) { // We don't check the capability -- if not implemented by the client FS, // the functionality is emulated in userland. VNode* vnode = (VNode*)_node; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); GetVNodeNameRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->size = bufferSize; // send the request KernelRequestHandler handler(this, GET_VNODE_NAME_REPLY); GetVNodeNameReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; char* readBuffer = (char*)reply->buffer.GetData(); size_t nameLen = reply->buffer.GetSize(); nameLen = strnlen(readBuffer, nameLen); if (nameLen <= 0 || nameLen >= bufferSize) RETURN_ERROR(B_BAD_DATA); memcpy(buffer, readBuffer, nameLen); buffer[nameLen] = '\0'; _SendReceiptAck(port); return error; } // ReadVNode status_t Volume::ReadVNode(ino_t vnid, bool reenter, void** _node, fs_vnode_ops** _ops, int* type, uint32* flags) { // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadVNodeRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->vnid = vnid; request->reenter = reenter; // add the uninitialized node to our map VNode* vnode = new(std::nothrow) VNode(vnid, NULL, NULL); if (vnode == NULL) RETURN_ERROR(B_NO_MEMORY); vnode->valid = false; MutexLocker locker(fLock); fVNodes->Insert(vnode); locker.Unlock(); // send the request KernelRequestHandler handler(this, READ_VNODE_REPLY); ReadVNodeReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) { _RemoveInvalidVNode(vnid); return error; } RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) { _RemoveInvalidVNode(vnid); return reply->error; } // get the ops vector for the node VNodeOps* ops = fFileSystem->GetVNodeOps(reply->capabilities); if (ops == NULL) { _RemoveInvalidVNode(vnid); RETURN_ERROR(B_NO_MEMORY); } // everything went fine -- mark the node valid locker.Lock(); vnode->clientNode = reply->node; vnode->ops = ops; vnode->valid = true; *_node = vnode; *type = reply->type; *flags = reply->flags; *_ops = ops->ops; return B_OK; } // WriteVNode status_t Volume::WriteVNode(void* node, bool reenter) { status_t error = _WriteVNode(node, reenter); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, since the VFS basically ignores the // return value -- at least Haiku. The fshell panic()s; didn't check // BeOS. It doesn't harm to appear to behave nicely. :-) WARN(("Volume::WriteVNode(): connection lost, forcing write vnode\n")); return B_OK; } return error; } // RemoveVNode status_t Volume::RemoveVNode(void* _node, bool reenter) { VNode* vnode = (VNode*)_node; // At any rate remove the vnode from our map and delete it. We don't do that // right now, though, since we might still need to serve file cache requests // from the client FS. VNodeRemover nodeRemover(this, vnode); void* clientNode = vnode->clientNode; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FSRemoveVNodeRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = clientNode; request->reenter = reenter; // send the request KernelRequestHandler handler(this, FS_REMOVE_VNODE_REPLY); FSRemoveVNodeReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // #pragma mark - asynchronous I/O // DoIO status_t Volume::DoIO(void* _node, void* cookie, io_request* ioRequest) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_IO)) return B_UNSUPPORTED; // register the IO request int32 requestID; status_t error = _RegisterIORequest(ioRequest, &requestID); if (error != B_OK) { notify_io_request(ioRequest, error); return error; } IORequestRemover requestRemover(this, requestID); // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) { notify_io_request(ioRequest, B_ERROR); return B_ERROR; } PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); DoIORequest* request; error = AllocateRequest(allocator, &request); if (error != B_OK) { notify_io_request(ioRequest, error); return error; } request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->request = requestID; request->offset = ioRequest->Offset(); request->length = ioRequest->Length(); request->isWrite = ioRequest->IsWrite(); request->isVIP = (ioRequest->Flags() & B_VIP_IO_REQUEST) != 0; // send the request KernelRequestHandler handler(this, DO_IO_REPLY); DoIOReply* reply; // TODO: when to notify the io_request? error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; requestRemover.Detach(); return B_OK; } // CancelIO status_t Volume::CancelIO(void* _node, void* cookie, io_request* ioRequest) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CANCEL_IO)) return B_BAD_VALUE; // find the request int32 requestID; status_t error = _FindIORequest(ioRequest, &requestID); if (error != B_OK) return error; IORequestRemover requestRemover(this, requestID); // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CancelIORequest* request; error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->request = requestID; // send the request KernelRequestHandler handler(this, CANCEL_IO_REPLY); CancelIOReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) { _UnregisterIORequest(requestID); return error; } RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) { _UnregisterIORequest(requestID); return reply->error; } return B_OK; } // #pragma mark - nodes // IOCtl status_t Volume::IOCtl(void* _node, void* cookie, uint32 command, void *buffer, size_t len) { VNode* vnode = (VNode*)_node; // check the command and its parameters bool isBuffer = false; int32 bufferSize = 0; int32 writeSize = 0; switch (command) { case IOCTL_FILE_UNCACHED_IO: buffer = NULL; break; case IOCTL_CREATE_TIME: case IOCTL_MODIFIED_TIME: isBuffer = 0; bufferSize = 0; writeSize = sizeof(bigtime_t); break; case USERLANDFS_IOCTL: area_id area; area_info info; PRINT(("Volume::IOCtl(): USERLANDFS_IOCTL\n")); if ((area = area_for(buffer)) >= 0) { if (get_area_info(area, &info) == B_OK) { if ((uint8*)buffer - (uint8*)info.address + sizeof(userlandfs_ioctl) <= info.size) { if (strncmp(((userlandfs_ioctl*)buffer)->magic, kUserlandFSIOCtlMagic, USERLAND_IOCTL_MAGIC_LENGTH) == 0) { return _InternalIOCtl((userlandfs_ioctl*)buffer, bufferSize); } else PRINT(("Volume::IOCtl(): bad magic\n")); } else PRINT(("Volume::IOCtl(): bad buffer size\n")); } else PRINT(("Volume::IOCtl(): failed to get area info\n")); } else PRINT(("Volume::IOCtl(): bad area\n")); // fall through... default: { // We don't know the command. Check whether the FileSystem knows // about it. const IOCtlInfo* info = fFileSystem->GetIOCtlInfo(command); if (!info) { PRINT(("Volume::IOCtl(): unknown command\n")); return B_BAD_VALUE; } isBuffer = info->isBuffer; bufferSize = info->bufferSize; writeSize = info->writeBufferSize; // If the buffer shall indeed specify a buffer, check it. if (info->isBuffer) { if (!buffer) { PRINT(("Volume::IOCtl(): buffer is NULL\n")); return B_BAD_VALUE; } area_id area = area_for(buffer); if (area < 0) { PRINT(("Volume::IOCtl(): bad area\n")); return B_BAD_VALUE; } area_info info; if (get_area_info(area, &info) != B_OK) { PRINT(("Volume::IOCtl(): failed to get area info\n")); return B_BAD_VALUE; } int32 areaSize = info.size - ((uint8*)buffer - (uint8*)info.address); if (bufferSize > areaSize || writeSize > areaSize) { PRINT(("Volume::IOCtl(): bad buffer size\n")); return B_BAD_VALUE; } if (writeSize > 0 && !(info.protection & B_WRITE_AREA)) { PRINT(("Volume::IOCtl(): buffer not writable\n")); return B_BAD_VALUE; } } break; } } // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_IOCTL)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); IOCtlRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->command = command; request->bufferParameter = buffer; request->isBuffer = isBuffer; request->lenParameter = len; request->writeSize = writeSize; if (isBuffer && bufferSize > 0) { error = allocator.AllocateData(request->buffer, buffer, bufferSize, 8); if (error != B_OK) return error; } // send the request KernelRequestHandler handler(this, IOCTL_REPLY); IOCtlReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; // Copy back the buffer even if the result is not B_OK. The protocol // is defined by the FS developer and may include writing data into // the buffer in some error cases. if (isBuffer && writeSize > 0 && reply->buffer.GetData()) { if (writeSize > reply->buffer.GetSize()) writeSize = reply->buffer.GetSize(); memcpy(buffer, reply->buffer.GetData(), writeSize); _SendReceiptAck(port); } return reply->ioctlError; } // SetFlags status_t Volume::SetFlags(void* _node, void* cookie, int flags) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_SET_FLAGS)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); SetFlagsRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->flags = flags; // send the request KernelRequestHandler handler(this, SET_FLAGS_REPLY); SetFlagsReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // Select status_t Volume::Select(void* _node, void* cookie, uint8 event, selectsync* sync) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_SELECT)) { notify_select_event(sync, event); return B_OK; } // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); SelectRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->event = event; request->sync = sync; // add a selectsync entry error = fFileSystem->AddSelectSyncEntry(sync); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, SELECT_REPLY); SelectReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) { fFileSystem->RemoveSelectSyncEntry(sync); return error; } RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) { fFileSystem->RemoveSelectSyncEntry(sync); return reply->error; } return error; } // Deselect status_t Volume::Deselect(void* _node, void* cookie, uint8 event, selectsync* sync) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_DESELECT)) return B_OK; struct SyncRemover { SyncRemover(FileSystem* fs, selectsync* sync) : fs(fs), sync(sync) {} ~SyncRemover() { fs->RemoveSelectSyncEntry(sync); } FileSystem* fs; selectsync* sync; } syncRemover(fFileSystem, sync); // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); DeselectRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->event = event; request->sync = sync; // send the request KernelRequestHandler handler(this, DESELECT_REPLY); DeselectReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // FSync status_t Volume::FSync(void* _node) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FSYNC)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FSyncRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; // send the request KernelRequestHandler handler(this, FSYNC_REPLY); FSyncReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // ReadSymlink status_t Volume::ReadSymlink(void* _node, char* buffer, size_t bufferSize, size_t* bytesRead) { VNode* vnode = (VNode*)_node; *bytesRead = 0; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_SYMLINK)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadSymlinkRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->size = bufferSize; // send the request KernelRequestHandler handler(this, READ_SYMLINK_REPLY); ReadSymlinkReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; void* readBuffer = reply->buffer.GetData(); if (reply->bytesRead > (uint32)reply->buffer.GetSize() || reply->bytesRead > bufferSize) { return B_BAD_DATA; } if (reply->bytesRead > 0) memcpy(buffer, readBuffer, reply->bytesRead); *bytesRead = reply->bytesRead; _SendReceiptAck(port); return error; } // CreateSymlink status_t Volume::CreateSymlink(void* _dir, const char* name, const char* target, int mode) { VNode* vnode = (VNode*)_dir; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE_SYMLINK)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CreateSymlinkRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); if (error == B_OK) error = allocator.AllocateString(request->target, target); if (error != B_OK) return error; request->mode = mode; // send the request KernelRequestHandler handler(this, CREATE_SYMLINK_REPLY); CreateSymlinkReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // Link status_t Volume::Link(void* _dir, const char* name, void* node) { VNode* vnode = (VNode*)_dir; VNode* targetVnode = (VNode*)node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_LINK)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); LinkRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); request->target = targetVnode->clientNode; if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, LINK_REPLY); LinkReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // Unlink status_t Volume::Unlink(void* _dir, const char* name) { VNode* vnode = (VNode*)_dir; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_UNLINK)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); UnlinkRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, UNLINK_REPLY); UnlinkReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // Rename status_t Volume::Rename(void* _oldDir, const char* oldName, void* _newDir, const char* newName) { VNode* oldVNode = (VNode*)_oldDir; VNode* newVNode = (VNode*)_newDir; // check capability if (!HasVNodeCapability(oldVNode, FS_VNODE_CAPABILITY_RENAME)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RenameRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->oldDir = oldVNode->clientNode; request->newDir = newVNode->clientNode; error = allocator.AllocateString(request->oldName, oldName); if (error == B_OK) error = allocator.AllocateString(request->newName, newName); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, RENAME_REPLY); RenameReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // Access status_t Volume::Access(void* _node, int mode) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_ACCESS)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); AccessRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->mode = mode; // send the request KernelRequestHandler handler(this, ACCESS_REPLY); AccessReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // ReadStat status_t Volume::ReadStat(void* node, struct stat* st) { // When the connection to the userland server is lost, we serve // read_stat(fRootNode) requests manually to allow clean unmounting. status_t error = _ReadStat(node, st); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected() && node == fRootNode) { WARN(("Volume::ReadStat(): connection lost, emulating stat for the " "root node\n")); st->st_dev = GetID(); st->st_ino = fRootID; st->st_mode = ACCESSPERMS; st->st_nlink = 1; st->st_uid = 0; st->st_gid = 0; st->st_size = 512; st->st_blksize = 512; st->st_atime = 0; st->st_mtime = 0; st->st_ctime = 0; st->st_crtime = 0; error = B_OK; } return error; } // WriteStat status_t Volume::WriteStat(void* _node, const struct stat* st, uint32 mask) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE_STAT)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); WriteStatRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->st = *st; request->mask = mask; // send the request KernelRequestHandler handler(this, WRITE_STAT_REPLY); WriteStatReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // #pragma mark - files // Create status_t Volume::Create(void* _dir, const char* name, int openMode, int mode, void** cookie, ino_t* vnid) { VNode* vnode = (VNode*)_dir; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenFiles); // prepare the request RequestAllocator allocator(port->GetPort()); CreateRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); request->openMode = openMode; request->mode = mode; if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, CREATE_REPLY); CreateReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *vnid = reply->vnid; *cookie = reply->fileCookie; // The VFS will balance the publish_vnode() call for the FS. if (error == B_OK) _DecrementVNodeCount(*vnid); return error; } // Open status_t Volume::Open(void* _node, int openMode, void** cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenFiles); // prepare the request RequestAllocator allocator(port->GetPort()); OpenRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->openMode = openMode; // send the request KernelRequestHandler handler(this, OPEN_REPLY); OpenReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *cookie = reply->fileCookie; return error; } // Close status_t Volume::Close(void* node, void* cookie) { status_t error = _Close(node, cookie); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. Haiku ignores it completely. The fsshell returns it to the // userland, but considers the node closed anyway. WARN(("Volume::Close(): connection lost, forcing close\n")); return B_OK; } return error; } // FreeCookie status_t Volume::FreeCookie(void* node, void* cookie) { status_t error = _FreeCookie(node, cookie); bool disconnected = false; if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. It's completely ignored by Haiku as well as by the fsshell. WARN(("Volume::FreeCookie(): connection lost, forcing free cookie\n")); error = B_OK; disconnected = true; } int32 openFiles = atomic_add(&fOpenFiles, -1); if (openFiles <= 1 && disconnected) _PutAllPendingVNodes(); return error; } // Read status_t Volume::Read(void* _node, void* cookie, off_t pos, void* buffer, size_t bufferSize, size_t* bytesRead) { VNode* vnode = (VNode*)_node; *bytesRead = 0; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->pos = pos; request->size = bufferSize; // send the request KernelRequestHandler handler(this, READ_REPLY); ReadReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; void* readBuffer = reply->buffer.GetData(); if (reply->bytesRead > (uint32)reply->buffer.GetSize() || reply->bytesRead > bufferSize) { return B_BAD_DATA; } if (reply->bytesRead > 0 && user_memcpy(buffer, readBuffer, reply->bytesRead) < B_OK) { return B_BAD_ADDRESS; } *bytesRead = reply->bytesRead; _SendReceiptAck(port); return error; } // Write status_t Volume::Write(void* _node, void* cookie, off_t pos, const void* buffer, size_t size, size_t* bytesWritten) { VNode* vnode = (VNode*)_node; *bytesWritten = 0; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); WriteRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; request->pos = pos; error = allocator.AllocateData(request->buffer, buffer, size, 1); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, WRITE_REPLY); WriteReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; *bytesWritten = reply->bytesWritten; return error; } // #pragma mark - directories // CreateDir status_t Volume::CreateDir(void* _dir, const char* name, int mode) { VNode* vnode = (VNode*)_dir; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CreateDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); request->mode = mode; if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, CREATE_DIR_REPLY); CreateDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // RemoveDir status_t Volume::RemoveDir(void* _dir, const char* name) { VNode* vnode = (VNode*)_dir; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REMOVE_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RemoveDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, REMOVE_DIR_REPLY); RemoveDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // OpenDir status_t Volume::OpenDir(void* _node, void** cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenDirectories); // prepare the request RequestAllocator allocator(port->GetPort()); OpenDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; // send the request KernelRequestHandler handler(this, OPEN_DIR_REPLY); OpenDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *cookie = reply->dirCookie; return error; } // CloseDir status_t Volume::CloseDir(void* node, void* cookie) { status_t error = _CloseDir(node, cookie); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. Haiku ignores it completely. The fsshell returns it to the // userland, but considers the node closed anyway. WARN(("Volume::CloseDir(): connection lost, forcing close dir\n")); return B_OK; } return error; } // FreeDirCookie status_t Volume::FreeDirCookie(void* node, void* cookie) { status_t error = _FreeDirCookie(node, cookie); bool disconnected = false; if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. It's completely ignored by Haiku as well as by the fsshell. WARN(("Volume::FreeDirCookie(): connection lost, forcing free dir " "cookie\n")); error = B_OK; disconnected = true; } int32 openDirs = atomic_add(&fOpenDirectories, -1); if (openDirs <= 1 && disconnected) _PutAllPendingVNodes(); return error; } // ReadDir status_t Volume::ReadDir(void* _node, void* cookie, void* buffer, size_t bufferSize, uint32 count, uint32* countRead) { VNode* vnode = (VNode*)_node; *countRead = 0; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->dirCookie = cookie; request->bufferSize = bufferSize; request->count = count; // send the request KernelRequestHandler handler(this, READ_DIR_REPLY); ReadDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; if (reply->count < 0 || reply->count > count) return B_BAD_DATA; if ((int32)bufferSize < reply->buffer.GetSize()) return B_BAD_DATA; PRINT(("Volume::ReadDir(): buffer returned: %" B_PRId32 " bytes\n", reply->buffer.GetSize())); *countRead = reply->count; if (*countRead > 0) { // copy the buffer -- limit the number of bytes to copy uint32 maxBytes = *countRead * (sizeof(struct dirent) + B_FILE_NAME_LENGTH); uint32 copyBytes = reply->buffer.GetSize(); if (copyBytes > maxBytes) copyBytes = maxBytes; memcpy(buffer, reply->buffer.GetData(), copyBytes); } _SendReceiptAck(port); return error; } // RewindDir status_t Volume::RewindDir(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REWIND_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RewindDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->dirCookie = cookie; // send the request KernelRequestHandler handler(this, REWIND_DIR_REPLY); RewindDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // #pragma mark - attribute directories // OpenAttrDir status_t Volume::OpenAttrDir(void* _node, void** cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN_ATTR_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenAttributeDirectories); // prepare the request RequestAllocator allocator(port->GetPort()); OpenAttrDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; // send the request KernelRequestHandler handler(this, OPEN_ATTR_DIR_REPLY); OpenAttrDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *cookie = reply->attrDirCookie; return error; } // CloseAttrDir status_t Volume::CloseAttrDir(void* node, void* cookie) { status_t error = _CloseAttrDir(node, cookie); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. Haiku ignores it completely. The fsshell returns it to the // userland, but considers the node closed anyway. WARN(("Volume::CloseAttrDir(): connection lost, forcing close attr " "dir\n")); return B_OK; } return error; } // FreeAttrDirCookie status_t Volume::FreeAttrDirCookie(void* node, void* cookie) { status_t error = _FreeAttrDirCookie(node, cookie); bool disconnected = false; if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. It's completely ignored by Haiku as well as by the fsshell. WARN(("Volume::FreeAttrDirCookie(): connection lost, forcing free attr " "dir cookie\n")); error = B_OK; disconnected = true; } int32 openAttrDirs = atomic_add(&fOpenAttributeDirectories, -1); if (openAttrDirs <= 1 && disconnected) _PutAllPendingVNodes(); return error; } // ReadAttrDir status_t Volume::ReadAttrDir(void* _node, void* cookie, void* buffer, size_t bufferSize, uint32 count, uint32* countRead) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_ATTR_DIR)) return B_BAD_VALUE; *countRead = 0; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadAttrDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrDirCookie = cookie; request->bufferSize = bufferSize; request->count = count; // send the request KernelRequestHandler handler(this, READ_ATTR_DIR_REPLY); ReadAttrDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; if (reply->count < 0 || reply->count > count) return B_BAD_DATA; if ((int32)bufferSize < reply->buffer.GetSize()) return B_BAD_DATA; *countRead = reply->count; if (*countRead > 0) { // copy the buffer -- limit the number of bytes to copy uint32 maxBytes = *countRead * (sizeof(struct dirent) + B_ATTR_NAME_LENGTH); uint32 copyBytes = reply->buffer.GetSize(); if (copyBytes > maxBytes) copyBytes = maxBytes; memcpy(buffer, reply->buffer.GetData(), copyBytes); } _SendReceiptAck(port); return error; } // RewindAttrDir status_t Volume::RewindAttrDir(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REWIND_ATTR_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RewindAttrDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrDirCookie = cookie; // send the request KernelRequestHandler handler(this, REWIND_ATTR_DIR_REPLY); RewindAttrDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // #pragma mark - attributes // CreateAttr status_t Volume::CreateAttr(void* _node, const char* name, uint32 type, int openMode, void** cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CREATE_ATTR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenAttributes); // prepare the request RequestAllocator allocator(port->GetPort()); CreateAttrRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); request->type = type; request->openMode = openMode; if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, CREATE_ATTR_REPLY); CreateAttrReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *cookie = reply->attrCookie; return error; } // OpenAttr status_t Volume::OpenAttr(void* _node, const char* name, int openMode, void** cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_OPEN_ATTR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenAttributes); // prepare the request RequestAllocator allocator(port->GetPort()); OpenAttrRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); request->openMode = openMode; if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, OPEN_ATTR_REPLY); OpenAttrReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *cookie = reply->attrCookie; return error; } // CloseAttr status_t Volume::CloseAttr(void* node, void* cookie) { status_t error = _CloseAttr(node, cookie); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. Haiku ignores it completely. The fsshell returns it to the // userland, but considers the node closed anyway. WARN(("Volume::CloseAttr(): connection lost, forcing close attr\n")); return B_OK; } return error; } // FreeAttrCookie status_t Volume::FreeAttrCookie(void* node, void* cookie) { status_t error = _FreeAttrCookie(node, cookie); bool disconnected = false; if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. It's completely ignored by Haiku as well as by the fsshell. WARN(("Volume::FreeAttrCookie(): connection lost, forcing free attr " "cookie\n")); error = B_OK; disconnected = true; } int32 openAttributes = atomic_add(&fOpenAttributes, -1); if (openAttributes <= 1 && disconnected) _PutAllPendingVNodes(); return error; } // ReadAttr status_t Volume::ReadAttr(void* _node, void* cookie, off_t pos, void* buffer, size_t bufferSize, size_t* bytesRead) { VNode* vnode = (VNode*)_node; *bytesRead = 0; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_ATTR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadAttrRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrCookie = cookie; request->pos = pos; request->size = bufferSize; // send the request KernelRequestHandler handler(this, READ_ATTR_REPLY); ReadAttrReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; void* readBuffer = reply->buffer.GetData(); if (reply->bytesRead > (uint32)reply->buffer.GetSize() || reply->bytesRead > bufferSize) { return B_BAD_DATA; } if (reply->bytesRead > 0 && user_memcpy(buffer, readBuffer, reply->bytesRead) < B_OK) { return B_BAD_ADDRESS; } *bytesRead = reply->bytesRead; _SendReceiptAck(port); return error; } // WriteAttr status_t Volume::WriteAttr(void* _node, void* cookie, off_t pos, const void* buffer, size_t bufferSize, size_t* bytesWritten) { VNode* vnode = (VNode*)_node; *bytesWritten = 0; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE_ATTR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); WriteAttrRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrCookie = cookie; request->pos = pos; error = allocator.AllocateData(request->buffer, buffer, bufferSize, 1); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, WRITE_ATTR_REPLY); WriteAttrReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; *bytesWritten = reply->bytesWritten; return error; } // ReadAttrStat status_t Volume::ReadAttrStat(void* _node, void* cookie, struct stat *st) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_ATTR_STAT)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadAttrStatRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrCookie = cookie; // send the request KernelRequestHandler handler(this, READ_ATTR_STAT_REPLY); ReadAttrStatReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; *st = reply->st; return error; } // WriteAttrStat status_t Volume::WriteAttrStat(void* _node, void* cookie, const struct stat *st, int statMask) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_WRITE_ATTR_STAT)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); WriteAttrStatRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrCookie = cookie; request->st = *st; request->mask = statMask; // send the request KernelRequestHandler handler(this, WRITE_ATTR_STAT_REPLY); WriteAttrStatReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // RenameAttr status_t Volume::RenameAttr(void* _oldNode, const char* oldName, void* _newNode, const char* newName) { VNode* oldVNode = (VNode*)_oldNode; VNode* newVNode = (VNode*)_newNode; // check capability if (!HasVNodeCapability(oldVNode, FS_VNODE_CAPABILITY_RENAME_ATTR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RenameAttrRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->oldNode = oldVNode->clientNode; request->newNode = newVNode->clientNode; error = allocator.AllocateString(request->oldName, oldName); if (error == B_OK) error = allocator.AllocateString(request->newName, newName); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, RENAME_ATTR_REPLY); RenameAttrReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // RemoveAttr status_t Volume::RemoveAttr(void* _node, const char* name) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_REMOVE_ATTR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RemoveAttrRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->name, name); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, REMOVE_ATTR_REPLY); RemoveAttrReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // #pragma mark - indices // OpenIndexDir status_t Volume::OpenIndexDir(void** cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenIndexDirectories); // prepare the request RequestAllocator allocator(port->GetPort()); OpenIndexDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; // send the request KernelRequestHandler handler(this, OPEN_INDEX_DIR_REPLY); OpenIndexDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *cookie = reply->indexDirCookie; return error; } // CloseIndexDir status_t Volume::CloseIndexDir(void* cookie) { status_t error = _CloseIndexDir(cookie); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. Haiku ignores it completely. The fsshell returns it to the // userland, but considers the node closed anyway. WARN(("Volume::CloseIndexDir(): connection lost, forcing close " "index dir\n")); return B_OK; } return error; } // FreeIndexDirCookie status_t Volume::FreeIndexDirCookie(void* cookie) { status_t error = _FreeIndexDirCookie(cookie); bool disconnected = false; if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. It's completely ignored by Haiku as well as by the fsshell. WARN(("Volume::FreeIndexDirCookie(): connection lost, forcing free " "index dir cookie\n")); error = B_OK; disconnected = true; } int32 openIndexDirs = atomic_add(&fOpenIndexDirectories, -1); if (openIndexDirs <= 1 && disconnected) _PutAllPendingVNodes(); return error; } // ReadIndexDir status_t Volume::ReadIndexDir(void* cookie, void* buffer, size_t bufferSize, uint32 count, uint32* countRead) { *countRead = 0; // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_READ_INDEX_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadIndexDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->indexDirCookie = cookie; request->bufferSize = bufferSize; request->count = count; // send the request KernelRequestHandler handler(this, READ_INDEX_DIR_REPLY); ReadIndexDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; if (reply->count < 0 || reply->count > count) return B_BAD_DATA; if ((int32)bufferSize < reply->buffer.GetSize()) return B_BAD_DATA; *countRead = reply->count; if (*countRead > 0) { // copy the buffer -- limit the number of bytes to copy uint32 maxBytes = *countRead * (sizeof(struct dirent) + B_FILE_NAME_LENGTH); uint32 copyBytes = reply->buffer.GetSize(); if (copyBytes > maxBytes) copyBytes = maxBytes; memcpy(buffer, reply->buffer.GetData(), copyBytes); } _SendReceiptAck(port); return error; } // RewindIndexDir status_t Volume::RewindIndexDir(void* cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RewindIndexDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->indexDirCookie = cookie; // send the request KernelRequestHandler handler(this, REWIND_INDEX_DIR_REPLY); RewindIndexDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // CreateIndex status_t Volume::CreateIndex(const char* name, uint32 type, uint32 flags) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_CREATE_INDEX)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CreateIndexRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; error = allocator.AllocateString(request->name, name); request->type = type; request->flags = flags; if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, CREATE_INDEX_REPLY); CreateIndexReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // RemoveIndex status_t Volume::RemoveIndex(const char* name) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_REMOVE_INDEX)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RemoveIndexRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; error = allocator.AllocateString(request->name, name); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, REMOVE_INDEX_REPLY); RemoveIndexReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // ReadIndexStat status_t Volume::ReadIndexStat(const char* name, struct stat *st) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_READ_INDEX_STAT)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadIndexStatRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; error = allocator.AllocateString(request->name, name); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, READ_INDEX_STAT_REPLY); ReadIndexStatReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; *st = reply->st; return error; } // #pragma mark - queries // OpenQuery status_t Volume::OpenQuery(const char* queryString, uint32 flags, port_id targetPort, uint32 token, void** cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_OPEN_QUERY)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); AutoIncrementer incrementer(&fOpenQueries); // prepare the request RequestAllocator allocator(port->GetPort()); OpenQueryRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; error = allocator.AllocateString(request->queryString, queryString); if (error != B_OK) return error; request->flags = flags; request->port = targetPort; request->token = token; // send the request KernelRequestHandler handler(this, OPEN_QUERY_REPLY); OpenQueryReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; incrementer.Keep(); *cookie = reply->queryCookie; return error; } // CloseQuery status_t Volume::CloseQuery(void* cookie) { status_t error = _CloseQuery(cookie); if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. Haiku ignores it completely. The fsshell returns it to the // userland, but considers the node closed anyway. WARN(("Volume::CloseQuery(): connection lost, forcing close query\n")); return B_OK; } return error; } // FreeQueryCookie status_t Volume::FreeQueryCookie(void* cookie) { status_t error = _FreeQueryCookie(cookie); bool disconnected = false; if (error != B_OK && fFileSystem->GetPortPool()->IsDisconnected()) { // This isn't really necessary, as the return value is irrelevant to // the VFS. It's completely ignored by Haiku as well as by the fsshell. WARN(("Volume::FreeQueryCookie(): connection lost, forcing free " "query cookie\n")); error = B_OK; disconnected = true; } int32 openQueries = atomic_add(&fOpenQueries, -1); if (openQueries <= 1 && disconnected) _PutAllPendingVNodes(); return error; } // ReadQuery status_t Volume::ReadQuery(void* cookie, void* buffer, size_t bufferSize, uint32 count, uint32* countRead) { *countRead = 0; // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_READ_QUERY)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadQueryRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->queryCookie = cookie; request->bufferSize = bufferSize; request->count = count; // send the request KernelRequestHandler handler(this, READ_QUERY_REPLY); ReadQueryReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; if (reply->count < 0 || reply->count > count) return B_BAD_DATA; if ((int32)bufferSize < reply->buffer.GetSize()) return B_BAD_DATA; *countRead = reply->count; if (*countRead > 0) { // copy the buffer -- limit the number of bytes to copy uint32 maxBytes = *countRead * (sizeof(struct dirent) + B_FILE_NAME_LENGTH); uint32 copyBytes = reply->buffer.GetSize(); if (copyBytes > maxBytes) copyBytes = maxBytes; memcpy(buffer, reply->buffer.GetData(), copyBytes); } _SendReceiptAck(port); return error; } // RewindQuery status_t Volume::RewindQuery(void* cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_REWIND_QUERY)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); RewindQueryRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->queryCookie = cookie; // send the request KernelRequestHandler handler(this, REWIND_QUERY_REPLY); RewindQueryReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // #pragma mark - // #pragma mark ----- private implementations ----- // _InitVolumeOps void Volume::_InitVolumeOps() { memcpy(&fVolumeOps, &gUserlandFSVolumeOps, sizeof(fs_volume_ops)); #undef CLEAR_UNSUPPORTED #define CLEAR_UNSUPPORTED(capability, op) \ if (!fCapabilities.Get(capability)) \ fVolumeOps.op = NULL // FS operations // FS_VOLUME_CAPABILITY_UNMOUNT: unmount // always needed // FS_VOLUME_CAPABILITY_READ_FS_INFO: read_fs_info // always needed CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_WRITE_FS_INFO, write_fs_info); CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_SYNC, sync); // vnode operations // FS_VOLUME_CAPABILITY_GET_VNODE: get_vnode // always needed // index directory & index operations CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR, open_index_dir); // FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR: close_index_dir // always needed // FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE: free_index_dir_cookie // always needed CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_READ_INDEX_DIR, read_index_dir); CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR, rewind_index_dir); CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_CREATE_INDEX, create_index); CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_REMOVE_INDEX, remove_index); CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_READ_INDEX_STAT, read_index_stat); // query operations CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_OPEN_QUERY, open_query); // FS_VOLUME_CAPABILITY_CLOSE_QUERY: close_query // always needed // FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE: free_query_cookie // always needed CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_READ_QUERY, read_query); CLEAR_UNSUPPORTED(FS_VOLUME_CAPABILITY_REWIND_QUERY, rewind_query); #undef CLEAR_UNSUPPORTED } // #pragma mark - // _Mount status_t Volume::_Mount(const char* device, uint32 flags, const char* parameters) { // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // get the current working directory char cwd[B_PATH_NAME_LENGTH]; if (!getcwd(cwd, sizeof(cwd))) return errno; // prepare the request RequestAllocator allocator(port->GetPort()); MountVolumeRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->nsid = GetID(); error = allocator.AllocateString(request->cwd, cwd); if (error == B_OK) error = allocator.AllocateString(request->device, device); request->flags = flags; if (error == B_OK) error = allocator.AllocateString(request->parameters, parameters); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, MOUNT_VOLUME_REPLY); MountVolumeReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; fRootID = reply->rootID; fUserlandVolume = reply->volume; fCapabilities = reply->capabilities; return error; } // _Unmount status_t Volume::_Unmount() { // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); UnmountVolumeRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; // send the request KernelRequestHandler handler(this, UNMOUNT_VOLUME_REPLY); UnmountVolumeReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _ReadFSInfo status_t Volume::_ReadFSInfo(fs_info* info) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_READ_FS_INFO)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadFSInfoRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; // send the request KernelRequestHandler handler(this, READ_FS_INFO_REPLY); ReadFSInfoReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; *info = reply->info; return error; } // _Lookup status_t Volume::_Lookup(void* _dir, const char* entryName, ino_t* vnid) { VNode* vnode = (VNode*)_dir; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); LookupRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; error = allocator.AllocateString(request->entryName, entryName); if (error != B_OK) return error; // send the request KernelRequestHandler handler(this, LOOKUP_REPLY); LookupReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; *vnid = reply->vnid; // The VFS will balance the get_vnode() call for the FS. _DecrementVNodeCount(*vnid); return error; } // _WriteVNode status_t Volume::_WriteVNode(void* _node, bool reenter) { VNode* vnode = (VNode*)_node; // At any rate remove the vnode from our map and delete it. We don't do that // right now, though, since we might still need to serve file cache requests // from the client FS. VNodeRemover nodeRemover(this, vnode); void* clientNode = vnode->clientNode; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); WriteVNodeRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = clientNode; request->reenter = reenter; // send the request KernelRequestHandler handler(this, WRITE_VNODE_REPLY); WriteVNodeReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _ReadStat status_t Volume::_ReadStat(void* _node, struct stat* st) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_READ_STAT)) return B_BAD_VALUE; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); ReadStatRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; // send the request KernelRequestHandler handler(this, READ_STAT_REPLY); ReadStatReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; *st = reply->st; return error; } // _Close status_t Volume::_Close(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CloseRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; // send the request KernelRequestHandler handler(this, CLOSE_REPLY); CloseReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _FreeCookie status_t Volume::_FreeCookie(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_COOKIE)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FreeCookieRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->fileCookie = cookie; // send the request KernelRequestHandler handler(this, FREE_COOKIE_REPLY); FreeCookieReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _CloseDir status_t Volume::_CloseDir(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE_DIR)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CloseDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->dirCookie = cookie; // send the request KernelRequestHandler handler(this, CLOSE_DIR_REPLY); CloseDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _FreeDirCookie status_t Volume::_FreeDirCookie(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_DIR_COOKIE)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FreeDirCookieRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->dirCookie = cookie; // send the request KernelRequestHandler handler(this, FREE_DIR_COOKIE_REPLY); FreeDirCookieReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _CloseAttrDir status_t Volume::_CloseAttrDir(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CloseAttrDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrDirCookie = cookie; // send the request KernelRequestHandler handler(this, CLOSE_ATTR_DIR_REPLY); CloseAttrDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _FreeAttrDirCookie status_t Volume::_FreeAttrDirCookie(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FreeAttrDirCookieRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrDirCookie = cookie; // send the request KernelRequestHandler handler(this, FREE_ATTR_DIR_COOKIE_REPLY); FreeAttrDirCookieReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _CloseAttr status_t Volume::_CloseAttr(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_CLOSE_ATTR)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CloseAttrRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrCookie = cookie; // send the request KernelRequestHandler handler(this, CLOSE_ATTR_REPLY); CloseAttrReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _FreeAttrCookie status_t Volume::_FreeAttrCookie(void* _node, void* cookie) { VNode* vnode = (VNode*)_node; // check capability if (!HasVNodeCapability(vnode, FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FreeAttrCookieRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->node = vnode->clientNode; request->attrCookie = cookie; // send the request KernelRequestHandler handler(this, FREE_ATTR_COOKIE_REPLY); FreeAttrCookieReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _CloseIndexDir status_t Volume::_CloseIndexDir(void* cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CloseIndexDirRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->indexDirCookie = cookie; // send the request KernelRequestHandler handler(this, CLOSE_INDEX_DIR_REPLY); CloseIndexDirReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _FreeIndexDirCookie status_t Volume::_FreeIndexDirCookie(void* cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FreeIndexDirCookieRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->indexDirCookie = cookie; // send the request KernelRequestHandler handler(this, FREE_INDEX_DIR_COOKIE_REPLY); FreeIndexDirCookieReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _CloseQuery status_t Volume::_CloseQuery(void* cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_CLOSE_QUERY)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); CloseQueryRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->queryCookie = cookie; // send the request KernelRequestHandler handler(this, CLOSE_QUERY_REPLY); CloseQueryReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _FreeQueryCookie status_t Volume::_FreeQueryCookie(void* cookie) { // check capability if (!HasCapability(FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE)) return B_OK; // get a free port RequestPort* port = fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); FreeQueryCookieRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = fUserlandVolume; request->queryCookie = cookie; // send the request KernelRequestHandler handler(this, FREE_QUERY_COOKIE_REPLY); FreeQueryCookieReply* reply; error = _SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return error; } // _SendRequest status_t Volume::_SendRequest(RequestPort* port, RequestAllocator* allocator, RequestHandler* handler, Request** reply) { // fill in the caller info KernelRequest* request = static_cast( allocator->GetRequest()); Thread* thread = thread_get_current_thread(); request->team = thread->team->id; request->thread = thread->id; request->user = geteuid(); request->group = getegid(); if (!fFileSystem->IsUserlandServerThread()) return port->SendRequest(allocator, handler, reply); // Here it gets dangerous: a thread of the userland server team being here // calls for trouble. We try receiving the request with a timeout, and // close the port -- which will disconnect the whole FS. status_t error = port->SendRequest(allocator, handler, reply, kUserlandServerlandPortTimeout); if (error == B_TIMED_OUT || error == B_WOULD_BLOCK) port->Close(); return error; } // _SendReceiptAck status_t Volume::_SendReceiptAck(RequestPort* port) { RequestAllocator allocator(port->GetPort()); ReceiptAckReply* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; return port->SendRequest(&allocator); } // _IncrementVNodeCount void Volume::_IncrementVNodeCount(ino_t vnid) { MutexLocker _(fLock); if (!fVNodeCountingEnabled) return; VNode* vnode = fVNodes->Lookup(vnid); if (vnode == NULL) { ERROR(("Volume::_IncrementVNodeCount(): Node with ID %" B_PRId64 " not known!\n", vnid)); return; } vnode->useCount++; //PRINT(("_IncrementVNodeCount(%lld): count: %ld, fVNodeCountMap size: %ld\n", vnid, *count, fVNodeCountMap->Size())); } // _DecrementVNodeCount void Volume::_DecrementVNodeCount(ino_t vnid) { MutexLocker _(fLock); if (!fVNodeCountingEnabled) return; VNode* vnode = fVNodes->Lookup(vnid); if (vnode == NULL) { ERROR(("Volume::_DecrementVNodeCount(): Node with ID %" B_PRId64 " not " "known!\n", vnid)); return; } vnode->useCount--; //PRINT(("_DecrementVNodeCount(%lld): count: %ld, fVNodeCountMap size: %ld\n", vnid, tmpCount, fVNodeCountMap->Size())); } // _RemoveInvalidVNode void Volume::_RemoveInvalidVNode(ino_t vnid) { MutexLocker locker(fLock); VNode* vnode = fVNodes->Lookup(vnid); if (vnode == NULL) { ERROR(("Volume::_RemoveInvalidVNode(): Node with ID %" B_PRId64 " not known!\n", vnid)); return; } fVNodes->Remove(vnode); locker.Unlock(); // release all references acquired so far if (fVNodeCountingEnabled) { for (; vnode->useCount > 0; vnode->useCount--) put_vnode(fFSVolume, vnid); } vnode->Delete(this); } // _InternalIOCtl status_t Volume::_InternalIOCtl(userlandfs_ioctl* buffer, int32 bufferSize) { if (buffer->version != USERLAND_IOCTL_CURRENT_VERSION) return B_BAD_VALUE; status_t result = B_OK; switch (buffer->command) { case USERLAND_IOCTL_PUT_ALL_PENDING_VNODES: result = _PutAllPendingVNodes(); break; default: return B_BAD_VALUE; } buffer->error = result; return B_OK; } // _PutAllPendingVNodes status_t Volume::_PutAllPendingVNodes() { PRINT(("Volume::_PutAllPendingVNodes()\n")); if (!fFileSystem->GetPortPool()->IsDisconnected()) { PRINT(("Volume::_PutAllPendingVNodes() failed: still connected\n")); return USERLAND_IOCTL_STILL_CONNECTED; } MutexLocker locker(fLock); if (!fVNodeCountingEnabled) { PRINT(("Volume::_PutAllPendingVNodes() failed: vnode counting " "disabled\n")); return USERLAND_IOCTL_VNODE_COUNTING_DISABLED; } // Check whether there are open entities at the moment. if (atomic_get(&fOpenFiles) > 0) { PRINT(("Volume::_PutAllPendingVNodes() failed: open files\n")); return USERLAND_IOCTL_OPEN_FILES; } if (atomic_get(&fOpenDirectories) > 0) { PRINT(("Volume::_PutAllPendingVNodes() failed: open dirs\n")); return USERLAND_IOCTL_OPEN_DIRECTORIES; } if (atomic_get(&fOpenAttributeDirectories) > 0) { PRINT(("Volume::_PutAllPendingVNodes() failed: open attr dirs\n")); return USERLAND_IOCTL_OPEN_ATTRIBUTE_DIRECTORIES; } if (atomic_get(&fOpenAttributes) > 0) { PRINT(("Volume::_PutAllPendingVNodes() failed: open attributes\n")); return USERLAND_IOCTL_OPEN_ATTRIBUTES; } if (atomic_get(&fOpenIndexDirectories) > 0) { PRINT(("Volume::_PutAllPendingVNodes() failed: open index dirs\n")); return USERLAND_IOCTL_OPEN_INDEX_DIRECTORIES; } if (atomic_get(&fOpenQueries) > 0) { PRINT(("Volume::_PutAllPendingVNodes() failed: open queries\n")); return USERLAND_IOCTL_OPEN_QUERIES; } // No open entities. Since the port pool is disconnected, no new // entities can be opened. Disable node counting and put all pending // vnodes. fVNodeCountingEnabled = false; int32 putVNodeCount = 0; // Since the vnode map can still change, we need to iterate to the first // node we need to put, drop the lock, put the node, and restart from the // beginning. // TODO: Optimize by extracting batches of relevant nodes to an on-stack // array. bool nodeFound; do { nodeFound = false; // get the next node to put for (VNodeMap::Iterator it = fVNodes->GetIterator(); VNode* vnode = it.Next();) { if (vnode->useCount > 0) { ino_t vnid = vnode->id; int32 count = vnode->useCount; vnode->useCount = 0; fs_vnode_ops* ops = vnode->ops->ops; bool published = vnode->published; locker.Unlock(); // If the node has not yet been published, we have to do that // before putting otherwise the VFS will complain that the node // is busy when the last reference is gone. if (!published) publish_vnode(fFSVolume, vnid, vnode, ops, S_IFDIR, 0); for (int32 i = 0; i < count; i++) { PutVNode(vnid); putVNodeCount++; } locker.Lock(); nodeFound = true; break; } } } while (nodeFound); PRINT(("Volume::_PutAllPendingVNodes() successful: Put %" B_PRId32 " vnodes\n", putVNodeCount)); return B_OK; } // _RegisterIORequest status_t Volume::_RegisterIORequest(io_request* request, int32* requestID) { MutexLocker _(fLock); // get the next free ID while (fIORequestInfosByID->Lookup(++fLastIORequestID) != NULL) { } // allocate the info IORequestInfo* info = new(std::nothrow) IORequestInfo(request, ++fLastIORequestID); if (info == NULL) return B_NO_MEMORY; // add the info to the maps fIORequestInfosByID->Insert(info); fIORequestInfosByStruct->Insert(info); *requestID = info->id; return B_OK; } // _UnregisterIORequest status_t Volume::_UnregisterIORequest(int32 requestID) { MutexLocker _(fLock); if (IORequestInfo* info = fIORequestInfosByID->Lookup(requestID)) { fIORequestInfosByID->Remove(info); fIORequestInfosByStruct->Remove(info); return B_OK; } return B_ENTRY_NOT_FOUND; } // _FindIORequest status_t Volume::_FindIORequest(int32 requestID, io_request** request) { MutexLocker _(fLock); if (IORequestInfo* info = fIORequestInfosByID->Lookup(requestID)) { *request = info->request; return B_OK; } return B_ENTRY_NOT_FOUND; } // _FindIORequest status_t Volume::_FindIORequest(io_request* request, int32* requestID) { MutexLocker _(fLock); if (IORequestInfo* info = fIORequestInfosByStruct->Lookup(request)) { *requestID = info->id; return B_OK; } return B_ENTRY_NOT_FOUND; } /*static*/ status_t Volume::_IterativeFDIOGetVecs(void* _cookie, io_request* ioRequest, off_t offset, size_t size, struct file_io_vec* vecs, size_t* _count) { IterativeFDIOCookie* cookie = (IterativeFDIOCookie*)_cookie; Volume* volume = cookie->volume; MutexLocker locker(volume->fLock); // If there are vecs cached in the cookie and the offset matches, return // those. if (cookie->vecs != NULL) { size_t vecCount = 0; if (offset == cookie->offset) { // good, copy the vecs while (size > 0 && vecCount < cookie->vecCount && vecCount < *_count) { off_t maxSize = std::min((off_t)size, cookie->vecs[vecCount].length); vecs[vecCount].offset = cookie->vecs[vecCount].offset; vecs[vecCount].length = maxSize; size -= maxSize; vecCount++; } } cookie->vecs = NULL; cookie->vecCount = 0; // got some vecs? -- then we're done if (vecCount > 0) { *_count = vecCount; return B_OK; } } // we have to ask the client FS int32 requestID = cookie->requestID; void* clientCookie = cookie->clientCookie; locker.Unlock(); // get a free port RequestPort* port = volume->fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _(volume->fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); IterativeIOGetVecsRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = volume->fUserlandVolume; request->cookie = clientCookie; request->offset = offset; request->request = requestID; request->size = size; size_t maxVecs = std::min(*_count, (size_t)IterativeIOGetVecsReply::MAX_VECS); request->vecCount = maxVecs; // send the request KernelRequestHandler handler(volume, ITERATIVE_IO_GET_VECS_REPLY); IterativeIOGetVecsReply* reply; error = volume->_SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; uint32 vecCount = reply->vecCount; if (vecCount < 0 || vecCount > maxVecs) return B_BAD_DATA; memcpy(vecs, reply->vecs, vecCount * sizeof(file_io_vec)); *_count = vecCount; return B_OK; } /*static*/ status_t Volume::_IterativeFDIOFinished(void* _cookie, io_request* ioRequest, status_t status, bool partialTransfer, size_t bytesTransferred) { IterativeFDIOCookie* cookie = (IterativeFDIOCookie*)_cookie; Volume* volume = cookie->volume; // At any rate, we're done with the cookie after this call -- it will not // be used anymore. BReference _(cookie, true); // We also want to dispose of the request. IORequestRemover _2(volume, cookie->requestID); // get a free port RequestPort* port = volume->fFileSystem->GetPortPool()->AcquirePort(); if (!port) return B_ERROR; PortReleaser _3(volume->fFileSystem->GetPortPool(), port); // prepare the request RequestAllocator allocator(port->GetPort()); IterativeIOFinishedRequest* request; status_t error = AllocateRequest(allocator, &request); if (error != B_OK) return error; request->volume = volume->fUserlandVolume; request->cookie = cookie->clientCookie; request->request = cookie->requestID; request->status = status; request->partialTransfer = partialTransfer; request->bytesTransferred = bytesTransferred; // send the request KernelRequestHandler handler(volume, ITERATIVE_IO_FINISHED_REPLY); IterativeIOFinishedReply* reply; error = volume->_SendRequest(port, &allocator, &handler, (Request**)&reply); if (error != B_OK) return error; RequestReleaser requestReleaser(port, reply); // process the reply if (reply->error != B_OK) return reply->error; return B_OK; }