// ClientConnection.cpp #include "ClientConnection.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Compatibility.h" #include "Connection.h" #include "DebugSupport.h" #include "Directory.h" #include "Entry.h" #include "FDManager.h" #include "NodeHandle.h" #include "NodeHandleMap.h" #include "NodeMonitoringEvent.h" #include "Path.h" #include "RequestBufferReplacer.h" #include "RequestChannel.h" #include "RequestConnection.h" #include "RequestDumper.h" #include "RequestFlattener.h" #include "Requests.h" #include "SecurityContext.h" #include "ServerNodeID.h" #include "UserSecurityContext.h" #include "Utils.h" #include "Volume.h" #include "VolumeManager.h" static const int32 kMaxSaneReadLinkSize = 10240; // 10 KB static const int32 kMaxReadBufferSize = 10240; // 10 KB static const int32 kMaxReadDirBufferSize = 10240; // Locking: // // fLock: Guards fReferenceCount and fClosed. // fSecurityContextLock: Guards fSecurityContext. // fVolumes: Guards the map itself. // #pragma mark - // #pragma mark ----- ClientConnection ----- // ConnectionReference class ClientConnection::ConnectionReference { public: ConnectionReference(ClientConnection* connection) : fConnection(connection) { if (!fConnection || !fConnection->GetReference()) fConnection = NULL; } ~ConnectionReference() { if (fConnection) fConnection->PutReference(); } bool IsValid() const { return fConnection; } private: ClientConnection* fConnection; }; // VolumeMap struct ClientConnection::VolumeMap : public SynchronizedHashMap, ClientVolume*> { }; // ClientVolumePutter class ClientConnection::ClientVolumePutter { public: ClientVolumePutter(ClientConnection* connection, ClientVolume* volume) : fConnection(connection), fVolume(volume) { } ~ClientVolumePutter() { if (fConnection && fVolume) fConnection->_PutVolume(fVolume); } void Detach() { fConnection = NULL; fVolume = NULL; } private: ClientConnection* fConnection; ClientVolume* fVolume; }; // VolumeNodeMonitoringEvent struct ClientConnection::VolumeNodeMonitoringEvent { VolumeNodeMonitoringEvent(int32 volumeID, NodeMonitoringEvent* event) : volumeID(volumeID), event(event) { if (event) event->AcquireReference(); } ~VolumeNodeMonitoringEvent() { if (event) event->ReleaseReference(); } int32 volumeID; NodeMonitoringEvent* event; }; // NodeMonitoringEventQueue struct ClientConnection::NodeMonitoringEventQueue : BlockingQueue { NodeMonitoringEventQueue() : BlockingQueue("client NM requests") { } }; // QueryHandleUnlocker struct ClientConnection::QueryHandleUnlocker { QueryHandleUnlocker(ClientConnection* connection, NodeHandle* nodeHandle) : fConnection(connection), fHandle(nodeHandle) { } ~QueryHandleUnlocker() { if (fConnection && fHandle) { fConnection->_UnlockQueryHandle(fHandle); fConnection = NULL; fHandle = NULL; } } private: ClientConnection* fConnection; NodeHandle* fHandle; }; // ClientVolumeFilter struct ClientConnection::ClientVolumeFilter { virtual ~ClientVolumeFilter() {} virtual bool FilterVolume(ClientConnection* connection, ClientVolume* volume) = 0; }; // HasQueryPermissionClientVolumeFilter struct ClientConnection::HasQueryPermissionClientVolumeFilter : ClientConnection::ClientVolumeFilter { virtual bool FilterVolume(ClientConnection* connection, ClientVolume* volume) { return volume->GetSharePermissions().ImpliesQuerySharePermission(); } }; // #pragma mark - // constructor ClientConnection::ClientConnection(Connection* connection, SecurityContext* securityContext, User* user, ClientConnectionListener* listener) : RequestHandler(), ClientVolume::NodeMonitoringProcessor(), fConnection(NULL), fSecurityContext(securityContext), fSecurityContextLock("security context lock"), fUser(user), fVolumes(NULL), fQueryHandles(NULL), fListener(listener), fNodeMonitoringEvents(NULL), fNodeMonitoringProcessor(-1), fLock("client connection locker"), fReferenceCount(0), fInitialized(0), fClosed(false), fError(false), fInverseClientEndianess(false) { fConnection = new(std::nothrow) RequestConnection(connection, this); if (!fConnection) delete connection; } // destructor ClientConnection::~ClientConnection() { _Close(); delete fConnection; // delete all volumes for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) delete it.Next().value; delete fVolumes; delete fQueryHandles; delete fNodeMonitoringEvents; } // Init status_t ClientConnection::Init() { // create a client volume map fVolumes = new(std::nothrow) VolumeMap; if (!fVolumes) return B_NO_MEMORY; status_t error = fVolumes->InitCheck(); if (error != B_OK) return error; // create the query handle map fQueryHandles = new(std::nothrow) NodeHandleMap("query handles"); if (!fQueryHandles) return B_NO_MEMORY; error = fQueryHandles->Init(); if (error != B_OK) return error; // create the node monitoring event queue fNodeMonitoringEvents = new(std::nothrow) NodeMonitoringEventQueue; if (!fNodeMonitoringEvents) return B_NO_MEMORY; error = fNodeMonitoringEvents->InitCheck(); if (error != B_OK) return error; // initialize the connection error = fConnection->Init(); if (error != B_OK) return error; // start the node monitoring processor fNodeMonitoringProcessor = spawn_thread(_NodeMonitoringProcessorEntry, "client connection NM processor", B_NORMAL_PRIORITY, this); if (fNodeMonitoringProcessor < 0) { _Close(); return fNodeMonitoringProcessor; } resume_thread(fNodeMonitoringProcessor); return B_OK; } // Close /*! Called by the NetFSServer. Not for internal use. Waits for the connection to be closed (at least for the node monitoring thread to be gone). */ void ClientConnection::Close() { { ConnectionReference connectionReference(this); if (connectionReference.IsValid()) _MarkClosed(false); fListener = NULL; } // Wait at least for the node monitoring processor; this is not perfect, // but not too bad either. if (fNodeMonitoringProcessor >= 0 && find_thread(NULL) != fNodeMonitoringProcessor) { int32 result; wait_for_thread(fNodeMonitoringProcessor, &result); } } // GetReference bool ClientConnection::GetReference() { AutoLocker _(fLock); if (fClosed || !atomic_or(&fInitialized, 0)) return false; fReferenceCount++; return true; } // PutReference void ClientConnection::PutReference() { bool close = false; { AutoLocker _(fLock); --fReferenceCount; if (fClosed) close = (fReferenceCount == 0); } if (close) _Close(); } // UserRemoved void ClientConnection::UserRemoved(User* user) { // get all volumes ClientVolume** volumes = NULL; int32 volumeCount = 0; AutoLocker volumesLocker(fVolumes); volumes = new(std::nothrow) ClientVolume*[fVolumes->Size()]; if (!volumes) { ERROR(("ClientConnection::UserRemoved(): ERROR: Failed to " "allocate memory for volume array.\n")); volumesLocker.Unlock(); _UnmountAllVolumes(); return; } for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) { if (ClientVolume* volume = _GetVolume(it.Next().value->GetID())) volumes[volumeCount++] = volume; } volumesLocker.Unlock(); // unmount the concerned volumes for (int32 i = 0; i < volumeCount; i++) { ClientVolume* volume = volumes[i]; fSecurityContextLock.Lock(); bool unmount = (volume->GetSecurityContext()->GetUser() == user); fSecurityContextLock.Unlock(); if (unmount) _UnmountVolume(volume); } // put the volumes for (int32 i = 0; i < volumeCount; i++) _PutVolume(volumes[i]); delete[] volumes; } // ShareRemoved void ClientConnection::ShareRemoved(Share* share) { // get all volumes ClientVolume** volumes = NULL; int32 volumeCount = 0; AutoLocker volumesLocker(fVolumes); volumes = new(std::nothrow) ClientVolume*[fVolumes->Size()]; if (!volumes) { ERROR(("ClientConnection::ShareRemoved(): ERROR: Failed to " "allocate memory for volume array.\n")); volumesLocker.Unlock(); _UnmountAllVolumes(); return; } for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) { if (ClientVolume* volume = _GetVolume(it.Next().value->GetID())) volumes[volumeCount++] = volume; } volumesLocker.Unlock(); // unmount the concerned volumes for (int32 i = 0; i < volumeCount; i++) { ClientVolume* volume = volumes[i]; fSecurityContextLock.Lock(); bool unmount = (volume->GetShare() == share); fSecurityContextLock.Unlock(); if (unmount) _UnmountVolume(volume); } // put the volumes for (int32 i = 0; i < volumeCount; i++) _PutVolume(volumes[i]); delete[] volumes; } // UserPermissionsChanged void ClientConnection::UserPermissionsChanged(Share* share, User* user, Permissions permissions) { bool unmountAll = (!permissions.ImpliesMountSharePermission()); // get all volumes ClientVolume** volumes = NULL; int32 volumeCount = 0; AutoLocker volumesLocker(fVolumes); volumes = new(std::nothrow) ClientVolume*[fVolumes->Size()]; if (!volumes) { ERROR(("ClientConnection::ShareRemoved(): ERROR: Failed to " "allocate memory for volume array.\n")); volumesLocker.Unlock(); _UnmountAllVolumes(); return; } for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) { if (ClientVolume* volume = _GetVolume(it.Next().value->GetID())) volumes[volumeCount++] = volume; } volumesLocker.Unlock(); // update the concerned volumes for (int32 i = 0; i < volumeCount; i++) { ClientVolume* volume = volumes[i]; fSecurityContextLock.Lock(); bool concerned = (volume->GetShare() == share && volume->GetSecurityContext()->GetUser() == user); fSecurityContextLock.Unlock(); if (concerned) { // create a new user security context for the volume status_t error = B_OK; if (unmountAll) { _UnmountVolume(volume); } else { // create a new user security context AutoLocker securityContextLocker(fSecurityContextLock); UserSecurityContext* userSecurityContext = new(std::nothrow) UserSecurityContext; // init it if (userSecurityContext) { error = fSecurityContext->GetUserSecurityContext(user, userSecurityContext); } else error = B_NO_MEMORY; if (error != B_OK) { delete userSecurityContext; securityContextLocker.Unlock(); _UnmountVolume(volume); continue; } // set the volume's new user security context securityContextLocker.Unlock(); volume->SetSecurityContext(userSecurityContext); } } } // put the volumes for (int32 i = 0; i < volumeCount; i++) _PutVolume(volumes[i]); delete[] volumes; } // #pragma mark - // VisitConnectionBrokenRequest status_t ClientConnection::VisitConnectionBrokenRequest(ConnectionBrokenRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; _MarkClosed(true); return B_OK; } // VisitInitConnectionRequest status_t ClientConnection::VisitInitConnectionRequest(InitConnectionRequest* request) { bool alreadyInitialized = atomic_or(&fInitialized, ~0); ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; if (!alreadyInitialized) fInverseClientEndianess = (request->bigEndian != B_HOST_IS_BENDIAN); // prepare the reply InitConnectionReply reply; // send the reply reply.error = (alreadyInitialized ? B_BAD_VALUE : B_OK); status_t error = GetChannel()->SendRequest(&reply); // on error just close if (error != B_OK) _MarkClosed(true); return B_OK; } // VisitMountRequest status_t ClientConnection::VisitMountRequest(MountRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; status_t result = B_OK; const char* shareName = request->share.GetString(); if (!shareName) SET_ERROR(result, B_BAD_DATA); // create a volume ClientVolume* volume = NULL; if (result == B_OK) result = _CreateVolume(&volume); ClientVolumePutter volumePutter(this, volume); // if we haven't been supplied with a user yet, use the info from the // mount request for authentication VolumeManagerLocker managerLocker; AutoLocker securityContextLocker(fSecurityContextLock); const char* userName = request->user.GetString(); User* user = fUser; BReference userReference(user); bool noPermission = false; if (result == B_OK && !user) { if (userName) { SET_ERROR(result, fSecurityContext->AuthenticateUser(userName, request->password.GetString(), &user)); if (result == B_OK) userReference.SetTo(user, true); } else result = B_PERMISSION_DENIED; if (result == B_PERMISSION_DENIED) noPermission = true; } // create a user security context UserSecurityContext* securityContext = NULL; if (result == B_OK) { securityContext = new(std::nothrow) UserSecurityContext; if (securityContext) { SET_ERROR(result, fSecurityContext->GetUserSecurityContext(user, securityContext)); } else SET_ERROR(result, B_NO_MEMORY); } ObjectDeleter securityContextDeleter(securityContext); // get the share Share* share = NULL; Permissions sharePermissions; node_ref mountPoint; if (result == B_OK) { AutoLocker _(fSecurityContext); share = fSecurityContext->FindShare(shareName); if (share) { mountPoint = share->GetNodeRef(); sharePermissions = securityContext->GetNodePermissions( mountPoint); if (!sharePermissions.ImpliesMountSharePermission()) { SET_ERROR(result, B_PERMISSION_DENIED); noPermission = true; } } else SET_ERROR(result, B_ENTRY_NOT_FOUND); } BReference shareReference(share, true); // mount the volume MountReply reply; if (result == B_OK) { SET_ERROR(result, volume->Mount(securityContext, share)); securityContextDeleter.Detach(); } if (result == B_OK) { _GetNodeInfo(volume->GetRootDirectory(), &reply.nodeInfo); reply.sharePermissions = sharePermissions.GetPermissions(); reply.volumeID = volume->GetID(); } // make sure, the volume is removed on error if (result != B_OK && volume) { AutoLocker volumeMapLocker(fVolumes); volume->MarkRemoved(); } securityContextLocker.Unlock(); managerLocker.Unlock(); // send the reply reply.error = result; reply.noPermission = noPermission; return GetChannel()->SendRequest(&reply); } // VisitUnmountRequest status_t ClientConnection::VisitUnmountRequest(UnmountRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; if (ClientVolume* volume = _GetVolume(request->volumeID)) { _UnmountVolume(volume); _PutVolume(volume); } return B_OK; } // VisitReadVNodeRequest status_t ClientConnection::VisitReadVNodeRequest(ReadVNodeRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // prepare the reply ReadVNodeReply reply; if (result == B_OK) _GetNodeInfo(node, &reply.nodeInfo); managerLocker.Unlock(); // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitWriteStatRequest status_t ClientConnection::VisitWriteStatRequest(WriteStatRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the path Path path; if (result == B_OK) result = node->GetPath(&path); // write the stat uint32 mask = request->mask; // size if (result == B_OK && (mask & WSTAT_SIZE)) { if (truncate(path.GetPath(), request->nodeInfo.st.st_size) < 0) result = errno; } // mode if (result == B_OK && (mask & WSTAT_MODE)) { if (chmod(path.GetPath(), request->nodeInfo.st.st_mode) < 0) result = errno; } // mtime if (result == B_OK && (mask & (WSTAT_ATIME | WSTAT_MTIME))) { utimbuf buffer; buffer.actime = (mask & WSTAT_ATIME) ? request->nodeInfo.st.st_atime : node->GetStat().st_atime; buffer.modtime = (mask & WSTAT_MTIME) ? request->nodeInfo.st.st_mtime : node->GetStat().st_mtime; if (utime(path.GetPath(), &buffer) < 0) result = errno; } // ignore WSTAT_CRTIME, WSTAT_UID, WSTAT_GID for the time being // prepare the reply WriteStatReply reply; // update the node stat reply.nodeInfoValid = false; if (node) { if (node->UpdateStat() == B_OK) { _GetNodeInfo(node, &reply.nodeInfo); reply.nodeInfoValid = true; } } managerLocker.Unlock(); // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitCreateFileRequest status_t ClientConnection::VisitCreateFileRequest(CreateFileRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->directoryID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions int openMode = request->openMode; if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the path Path path; if (result == B_OK) { result = directory->GetPath(&path); if (result == B_OK) result = path.Append(request->name.GetString()); } // create the file if (result == B_OK) { int fd = -1; result = FDManager::Open(path.GetPath(), openMode | O_CREAT | O_NOTRAVERSE, request->mode, fd); if (result == B_OK) close(fd); } // load the new entry Entry* entry = NULL; if (result == B_OK) { VolumeManager* volumeManager = VolumeManager::GetDefault(); // if there existed an entry before, we need to delete it, to avoid that // we open the wrong node entry = volumeManager->GetEntry(directory->GetVolumeID(), directory->GetID(), request->name.GetString()); if (entry) volumeManager->DeleteEntry(entry, false); // load the new entry entry = NULL; result = volume->LoadEntry(directory, request->name.GetString(), &entry); } // open the node FileHandle* handle = NULL; if (result == B_OK) { openMode &= ~(O_CREAT | O_EXCL | O_TRUNC); result = volume->Open(entry->GetNode(), openMode, &handle); } NodeHandleUnlocker handleUnlocker(volume, handle); // prepare the reply CreateFileReply reply; if (result == B_OK) { _GetEntryInfo(entry, &reply.entryInfo); reply.cookie = handle->GetCookie(); } managerLocker.Unlock(); // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); // close the handle, if a send error occurred if (error != B_OK && result == B_OK) volume->Close(handle); return error; } // VisitOpenRequest status_t ClientConnection::VisitOpenRequest(OpenRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // check permissions int openMode = request->openMode; if (result == B_OK) { Permissions permissions = volume->GetNodePermissions(node); if ((openMode & O_RWMASK) == O_RDWR) { // read+write: fall back to read/write only, if the other permission // is missing if (!permissions.ImpliesReadPermission()) openMode = (openMode & ~O_RWMASK) | O_WRONLY; else if (!permissions.ImpliesWritePermission()) openMode = (openMode & ~O_RWMASK) | O_RDONLY; } if ((openMode & O_RWMASK) == O_RDONLY) { if (!permissions.ImpliesReadPermission()) result = B_PERMISSION_DENIED; } else if ((openMode & O_RWMASK) == O_WRONLY) { if (!permissions.ImpliesWritePermission()) result = B_PERMISSION_DENIED; } } // open the node FileHandle* handle = NULL; if (result == B_OK) result = volume->Open(node, openMode, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); // prepare the reply OpenReply reply; if (result == B_OK) { _GetNodeInfo(node, &reply.nodeInfo); reply.cookie = handle->GetCookie(); } managerLocker.Unlock(); // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); // close the handle, if a send error occurred if (error != B_OK && result == B_OK) volume->Close(handle); return error; } // VisitCloseRequest status_t ClientConnection::VisitCloseRequest(CloseRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; status_t result = B_OK; if (request->volumeID >= 0) { // get the volume ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) SET_ERROR(result, B_BAD_VALUE); ClientVolumePutter volumePutter(this, volume); // get the node handle NodeHandle* handle = NULL; if (result == B_OK) SET_ERROR(result, volume->LockNodeHandle(request->cookie, &handle)); NodeHandleUnlocker handleUnlocker(volume, handle); VolumeManagerLocker managerLocker; // close it if (result == B_OK) SET_ERROR(result, volume->Close(handle)); managerLocker.Unlock(); } else { // no volume ID given, so this is a query handle // lock the handle QueryHandle* handle = NULL; SET_ERROR(result, _LockQueryHandle(request->cookie, &handle)); QueryHandleUnlocker handleUnlocker(this, handle); // close it if (result == B_OK) SET_ERROR(result, _CloseQuery(handle)); } // send the reply CloseReply reply; reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitReadRequest status_t ClientConnection::VisitReadRequest(ReadRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); // get the node handle NodeHandle* handle = NULL; if (result == B_OK) result = volume->LockNodeHandle(request->cookie, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); // check if it is a file handle FileHandle* fileHandle = NULL; if (result == B_OK) { fileHandle = dynamic_cast(handle); if (!fileHandle) result = B_BAD_VALUE; } VolumeManagerLocker managerLocker; // check read permission if (result == B_OK) { Node* node = volume->GetNode(fileHandle->GetNodeRef()); if (!node || !volume->GetNodePermissions(node).ImpliesReadPermission()) result = B_PERMISSION_DENIED; } managerLocker.Unlock(); off_t pos = request->pos; int32 size = request->size; int32 bufferSize = min(size, kMaxReadBufferSize); // allocate a buffer uint8* buffer = NULL; if (result == B_OK) { buffer = (uint8*)malloc(bufferSize); if (!buffer) result = B_NO_MEMORY; } MemoryDeleter bufferDeleter(buffer); // read as long as there are bytes left to read or an error occurs bool moreToRead = true; do { int32 bytesToRead = min(size, bufferSize); size_t bytesRead = 0; if (result == B_OK) result = fileHandle->Read(pos, buffer, bytesToRead, &bytesRead); moreToRead = (result == B_OK && bytesRead > 0 && (int32)bytesRead < size); // prepare the reply ReadReply reply; if (result == B_OK) { reply.pos = pos; reply.data.SetTo(buffer, bytesRead); reply.moreToCome = moreToRead; pos += bytesRead; size -= bytesRead; } // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); if (error != B_OK) return error; } while (moreToRead); return B_OK; } // VisitWriteRequest status_t ClientConnection::VisitWriteRequest(WriteRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); // get the node handle NodeHandle* handle = NULL; if (result == B_OK) result = volume->LockNodeHandle(request->cookie, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); // check if it is a file handle FileHandle* fileHandle = NULL; if (result == B_OK) { fileHandle = dynamic_cast(handle); if (!fileHandle) result = B_BAD_VALUE; } VolumeManagerLocker managerLocker; // check read permission if (result == B_OK) { Node* node = volume->GetNode(fileHandle->GetNodeRef()); if (!node || !volume->GetNodePermissions(node).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } managerLocker.Unlock(); // write until all has been written or an error occurs off_t pos = request->pos; int32 size = request->data.GetSize(); const char* buffer = (const char*)request->data.GetData(); while (result == B_OK && size > 0) { size_t bytesWritten; result = fileHandle->Write(pos, buffer, size, &bytesWritten); if (result == B_OK) { pos += bytesWritten; buffer += bytesWritten; size -= bytesWritten; } } // prepare the reply WriteReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitCreateLinkRequest status_t ClientConnection::VisitCreateLinkRequest(CreateLinkRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the target node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // get the target node path Path targetPath; if (result == B_OK) result = node->GetPath(&targetPath); // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->directoryID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the new entry's path Path path; if (result == B_OK) { result = directory->GetPath(&path); if (result == B_OK) result = path.Append(request->name.GetString()); } managerLocker.Unlock(); // create the link if (result == B_OK) { if (link(targetPath.GetPath(), path.GetPath()) < 0) result = errno; } // prepare the reply CreateSymlinkReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitUnlinkRequest status_t ClientConnection::VisitUnlinkRequest(UnlinkRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->directoryID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the entry's path Path path; if (result == B_OK) { result = directory->GetPath(&path); if (result == B_OK) result = path.Append(request->name.GetString()); } managerLocker.Unlock(); // remove the entry if (result == B_OK) { if (unlink(path.GetPath()) < 0) result = errno; } // prepare the reply UnlinkReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitCreateSymlinkRequest status_t ClientConnection::VisitCreateSymlinkRequest(CreateSymlinkRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->directoryID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the new entry's path Path path; if (result == B_OK) { result = directory->GetPath(&path); if (result == B_OK) result = path.Append(request->name.GetString()); } managerLocker.Unlock(); // create the symlink if (result == B_OK) { if (symlink(request->target.GetString(), path.GetPath()) < 0) result = errno; } // prepare the reply CreateSymlinkReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitReadLinkRequest status_t ClientConnection::VisitReadLinkRequest(ReadLinkRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } int32 bufferSize = request->maxSize; // check read permission if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesReadPermission()) result = B_PERMISSION_DENIED; } // allocate a buffer void* buffer = NULL; if (result == B_OK) { if (bufferSize < 0 || bufferSize > kMaxSaneReadLinkSize) result = B_BAD_DATA; } if (result == B_OK) { buffer = malloc(bufferSize); if (!buffer) result = B_NO_MEMORY; } MemoryDeleter bufferDeleter(buffer); // read the link and prepare the reply ReadLinkReply reply; int32 bytesRead = 0; if (result == B_OK) result = node->ReadSymlink((char*)buffer, bufferSize, &bytesRead); if (result == B_OK) { reply.data.SetTo(buffer, bytesRead); _GetNodeInfo(node, &reply.nodeInfo); } managerLocker.Unlock(); // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitRenameRequest status_t ClientConnection::VisitRenameRequest(RenameRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the new directory Directory* newDirectory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->newDirectoryID); if (node) { newDirectory = dynamic_cast(node); if (!newDirectory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(newDirectory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the new path Path newPath; if (result == B_OK) { result = newDirectory->GetPath(&newPath); if (result == B_OK) result = newPath.Append(request->newName.GetString()); } // get the old directory Directory* oldDirectory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->oldDirectoryID); if (node) { oldDirectory = dynamic_cast(node); if (!oldDirectory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(oldDirectory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the new path Path oldPath; if (result == B_OK) { result = oldDirectory->GetPath(&oldPath); if (result == B_OK) result = oldPath.Append(request->oldName.GetString()); } managerLocker.Unlock(); // rename the entry if (result == B_OK) { if (rename(oldPath.GetPath(), newPath.GetPath()) < 0) result = errno; } // prepare the reply RenameReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitMakeDirRequest status_t ClientConnection::VisitMakeDirRequest(MakeDirRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->directoryID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the path Path path; if (result == B_OK) { result = directory->GetPath(&path); if (result == B_OK) result = path.Append(request->name.GetString()); } managerLocker.Unlock(); // create the directory if (result == B_OK) { if (mkdir(path.GetPath(), request->mode) < 0) result = errno; } // prepare the reply MakeDirReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitRemoveDirRequest status_t ClientConnection::VisitRemoveDirRequest(RemoveDirRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->directoryID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permissions if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // get the path Path path; if (result == B_OK) { result = directory->GetPath(&path); if (result == B_OK) result = path.Append(request->name.GetString()); } managerLocker.Unlock(); // remove the directory if (result == B_OK) { if (rmdir(path.GetPath()) < 0) result = errno; } // prepare the reply RemoveDirReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitOpenDirRequest status_t ClientConnection::VisitOpenDirRequest(OpenDirRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->nodeID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permission if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesReadDirPermission()) result = B_PERMISSION_DENIED; } // open the directory DirIterator* handle = NULL; if (result == B_OK) result = volume->OpenDir(directory, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); // prepare the reply OpenDirReply reply; if (result == B_OK) { _GetNodeInfo(directory, &reply.nodeInfo); reply.cookie = handle->GetCookie(); } else { if (directory != NULL) { PRINT("OpenDir() failed: client volume: %" B_PRId32 ", " "node: (%" B_PRIdDEV ", %" B_PRIdINO ")\n", volume->GetID(), directory->GetVolumeID(), directory->GetID()); } } managerLocker.Unlock(); // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); // close the handle, if a send error occurred if (error != B_OK && result == B_OK) volume->Close(handle); return error; } // VisitReadDirRequest status_t ClientConnection::VisitReadDirRequest(ReadDirRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); // get the node handle NodeHandle* handle = NULL; if (result == B_OK) result = volume->LockNodeHandle(request->cookie, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); // check if it is a directory iterator DirIterator* iterator = NULL; if (result == B_OK) { iterator = dynamic_cast(handle); if (!iterator) result = B_BAD_VALUE; } VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(iterator->GetNodeRef()); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check read permission if (result == B_OK) { if (!volume->GetNodePermissions(directory).ImpliesReadDirPermission()) result = B_PERMISSION_DENIED; } if (result == B_OK) { PRINT("ReadDir: (%" B_PRId32 ", %" B_PRIdINO ")\n", request->volumeID, directory->GetID()); } // rewind, if requested if (result == B_OK && request->rewind) iterator->Rewind(); // read the directory bool done = false; ReadDirReply reply; int32 toRead = request->count; while (result == B_OK && toRead > 0) { // get the next entry Entry* entry = iterator->NextEntry(); if (!entry) { done = true; break; } // get and add an entry info EntryInfo entryInfo; _GetEntryInfo(entry, &entryInfo); result = reply.entryInfos.Append(entryInfo); toRead--; } reply.revision = VolumeManager::GetDefault()->GetRevision(); //PRINT(("ReadDir: (%lld) -> (%lx, %ld, dir: %lld, node: %lld, `%s')\n", //directoryID, reply.error, reply.entryInfos.CountElements(), //reply.entryInfo.directoryID, //reply.entryInfo.nodeID, reply.entryInfo.name.GetString())); if (directory != NULL) { PRINT("ReadDir done: volume: %" B_PRId32 ", " "(%" B_PRIdDEV ", %" B_PRIdINO ") -> " "(%" B_PRIx32 ", %" B_PRId32 ")\n", volume->GetID(), directory->GetVolumeID(), directory->GetID(), result, reply.entryInfos.CountElements()); } managerLocker.Unlock(); // send the reply reply.error = result; reply.done = (result != B_OK || done); return GetChannel()->SendRequest(&reply); } // VisitWalkRequest status_t ClientConnection::VisitWalkRequest(WalkRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->nodeID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permission if (result == B_OK) { if (!volume->GetNodePermissions(directory) .ImpliesResolveDirEntryPermission()) { result = B_PERMISSION_DENIED; } } WalkReply reply; char linkPath[B_PATH_NAME_LENGTH]; if (result == B_OK) { // load the entry Entry* entry; result = volume->LoadEntry(directory, request->name.GetString(), &entry); // fill in the reply if (result == B_OK) { _GetEntryInfo(entry, &reply.entryInfo); // resolve a symlink, if desired Node* node = entry->GetNode(); if (request->resolveLink && node->IsSymlink()) { result = node->ReadSymlink(linkPath, B_PATH_NAME_LENGTH); if (result == B_OK) reply.linkPath.SetTo(linkPath); } } } managerLocker.Unlock(); // send the reply reply.error = result; PRINT("Walk: (%" B_PRIdDEV ", %" B_PRIdINO ", `%s') -> " "(%" B_PRIx32 ", (%" B_PRIdDEV ", %" B_PRIdINO "), `%s')\n", request->nodeID.volumeID, request->nodeID.nodeID, request->name.GetString(), result, reply.entryInfo.nodeInfo.st.st_dev, reply.entryInfo.nodeInfo.st.st_ino, reply.linkPath.GetString()); return GetChannel()->SendRequest(&reply); } // VisitMultiWalkRequest status_t ClientConnection::VisitMultiWalkRequest(MultiWalkRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the directory Directory* directory = NULL; if (result == B_OK) { Node* node = volume->GetNode(request->nodeID); if (node) { directory = dynamic_cast(node); if (!directory) result = B_NOT_A_DIRECTORY; } else result = B_ENTRY_NOT_FOUND; } // check permission if (result == B_OK) { if (!volume->GetNodePermissions(directory) .ImpliesResolveDirEntryPermission()) { result = B_PERMISSION_DENIED; } } MultiWalkReply reply; StringData* names = request->names.GetElements(); int32 count = request->names.CountElements(); for (int32 i = 0; result == B_OK && i < count; i++) { // load the entry Entry* entry; if (volume->LoadEntry(directory, names[i].GetString(), &entry) == B_OK) { // add an entry info EntryInfo entryInfo; _GetEntryInfo(entry, &entryInfo); // append the info result = reply.entryInfos.Append(entryInfo); } } managerLocker.Unlock(); // send the reply reply.error = result; PRINT("MultiWalk: (%" B_PRIdDEV ", %" B_PRIdINO ", %" B_PRId32 ") -> " "(%" B_PRIx32 ", %" B_PRId32 ")\n", request->nodeID.volumeID, request->nodeID.nodeID, count, result, reply.entryInfos.CountElements()); return GetChannel()->SendRequest(&reply); } // VisitOpenAttrDirRequest status_t ClientConnection::VisitOpenAttrDirRequest(OpenAttrDirRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // check permission if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesReadPermission()) result = B_PERMISSION_DENIED; } // load/cache the attribute directory bool attrDirCached = (node->LoadAttrDir() == B_OK); // open the attribute directory, if caching it failed AttrDirIterator* handle = NULL; if (result == B_OK && !attrDirCached) result = volume->OpenAttrDir(node, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); // prepare the reply OpenAttrDirReply reply; if (result == B_OK) { if (handle) { reply.cookie = handle->GetCookie(); } else { // the attribute directory is cached reply.cookie = -1; result = _GetAttrDirInfo(request, node, &reply.attrDirInfo); } } managerLocker.Unlock(); // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); // close the handle, if a send error occurred if (error != B_OK && result == B_OK && handle) volume->Close(handle); return error; } // VisitReadAttrDirRequest status_t ClientConnection::VisitReadAttrDirRequest(ReadAttrDirRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); // get the node handle NodeHandle* handle = NULL; if (result == B_OK) result = volume->LockNodeHandle(request->cookie, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); // check if it is a attribute directory iterator AttrDirIterator* iterator = NULL; if (result == B_OK) { iterator = dynamic_cast(handle); if (!iterator) result = B_BAD_VALUE; } VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(iterator->GetNodeRef()); if (!node) result = B_ENTRY_NOT_FOUND; } // check read permission (we already checked when opening, but anyway...) if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesReadPermission()) result = B_PERMISSION_DENIED; } managerLocker.Unlock(); // read the attribute directory uint8 buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH]; struct dirent* dirEntry = (struct dirent*)buffer; int32 countRead = 0; bool done = true; if (result == B_OK) { if (request->rewind) result = iterator->RewindDir(); if (result == B_OK) { result = iterator->ReadDir(dirEntry, sizeof(buffer), 1, &countRead, &done); } } // prepare the reply ReadAttrDirReply reply; reply.name.SetTo(dirEntry->d_name); // send the reply reply.error = result; reply.count = countRead; return GetChannel()->SendRequest(&reply); } // VisitReadAttrRequest status_t ClientConnection::VisitReadAttrRequest(ReadAttrRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // check read permission if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesReadPermission()) result = B_PERMISSION_DENIED; } // open the node FileHandle* handle = NULL; if (result == B_OK) result = volume->Open(node, O_RDONLY, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); managerLocker.Unlock(); // read the attribute if (result == B_OK) { // Due to a bug in BFS the `pos' is ignored. This means that the loop // below won't work if the attribute is non-empty and the buffer is // larger than the attribute, because the we would again and again // read the beginning of the attribute until the buffer is full. // Hence we first get an attr_info and don't try to read more than // the size of the attribute. attr_info info; result = handle->StatAttr(request->name.GetString(), &info); off_t originalPos = max(request->pos, (off_t)0); int32 originalSize = max(request->size, (int32)0); off_t pos = originalPos; int32 size = originalSize; type_code type = B_SWAP_INT32(request->type); bool convert = false; if (result == B_OK) { originalSize = min((off_t)originalSize, max((off_t)0, info.size - pos)); size = originalSize; // deal with inverse endianess clients if (fInverseClientEndianess) { convert = _KnownAttributeType(info.type); if (convert) { // read the whole attribute pos = 0; size = info.size; } else type = B_SWAP_INT32(request->type); } } int32 bufferSize = min(size, kMaxReadBufferSize); // allocate a buffer uint8* buffer = NULL; if (result == B_OK) { buffer = (uint8*)malloc(bufferSize); if (!buffer) result = B_NO_MEMORY; } MemoryDeleter bufferDeleter(buffer); if (convert) { // read the whole attribute and convert it if (result == B_OK) { // read size_t bytesRead = 0; result = handle->ReadAttr(request->name.GetString(), type, 0, buffer, size, &bytesRead); if (result == B_OK && (int32)bytesRead != size) result = B_ERROR; // convert if (result == B_OK) _ConvertAttribute(info, buffer); } // prepare the reply ReadAttrReply reply; if (result == B_OK) { reply.pos = originalPos; reply.data.SetTo(buffer + originalPos, originalSize); reply.moreToCome = false; } // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); if (error != B_OK) return error; } else { // read as long as there are bytes left to read or an error occurs bool moreToRead = true; do { int32 bytesToRead = min(size, bufferSize); size_t bytesRead = 0; if (result == B_OK) { result = handle->ReadAttr(request->name.GetString(), request->type, pos, buffer, bytesToRead, &bytesRead); } moreToRead = (result == B_OK && bytesRead > 0 && (int32)bytesRead < size); // prepare the reply ReadAttrReply reply; if (result == B_OK) { reply.pos = pos; reply.data.SetTo(buffer, bytesRead); reply.moreToCome = moreToRead; pos += bytesRead; size -= bytesRead; } // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); if (error != B_OK) return error; } while (moreToRead); } // close the handle volume->Close(handle); } else { // opening the node failed (or something even earlier): send an error // reply ReadAttrReply reply; reply.error = result; status_t error = GetChannel()->SendRequest(&reply); if (error != B_OK) return error; } return B_OK; } // VisitWriteAttrRequest status_t ClientConnection::VisitWriteAttrRequest(WriteAttrRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // check read permission if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // open the node FileHandle* handle = NULL; if (result == B_OK) result = volume->Open(node, O_RDWR, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); managerLocker.Unlock(); if (result == B_OK) { off_t pos = max(request->pos, (off_t)0); int32 size = request->data.GetSize(); type_code type = request->type; char* buffer = (char*)request->data.GetData(); // convert the data, if necessary if (fInverseClientEndianess) { if (_KnownAttributeType(type)) { if (pos != 0) { WARN("WriteAttr(): WARNING: Need to convert attribute " "endianess, but position is not 0: attribute: %s, " "pos: %" B_PRIdOFF ", size: %" B_PRId32 "\n", request->name.GetString(), pos, size); } swap_data(type, buffer, size, B_SWAP_ALWAYS); } else type = B_SWAP_INT32(type); } // write the data while (result == B_OK && size > 0) { size_t bytesWritten; result = handle->WriteAttr(request->name.GetString(), type, pos, buffer, size, &bytesWritten); if (result == B_OK) { pos += bytesWritten; buffer += bytesWritten; size -= bytesWritten; } } // close the handle volume->Close(handle); } // prepare the reply WriteAttrReply reply; // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitRemoveAttrRequest status_t ClientConnection::VisitRemoveAttrRequest(RemoveAttrRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // check read permission if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesWritePermission()) result = B_PERMISSION_DENIED; } // open the node FileHandle* handle = NULL; if (result == B_OK) result = volume->Open(node, O_RDWR, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); managerLocker.Unlock(); // remove the attribute and close the node if (result == B_OK) { result = handle->RemoveAttr(request->name.GetString()); volume->Close(handle); } // send the reply RemoveAttrReply reply; reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitRenameAttrRequest status_t ClientConnection::VisitRenameAttrRequest(RenameAttrRequest* request) { // Not supported, since there's no API function to rename an attribute. // send the reply RemoveAttrReply reply; reply.error = B_UNSUPPORTED; return GetChannel()->SendRequest(&reply); } // VisitStatAttrRequest status_t ClientConnection::VisitStatAttrRequest(StatAttrRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // get the volume status_t result = B_OK; ClientVolume* volume = _GetVolume(request->volumeID); if (!volume) result = B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); VolumeManagerLocker managerLocker; // get the node Node* node = NULL; if (result == B_OK) { node = volume->GetNode(request->nodeID); if (!node) result = B_ENTRY_NOT_FOUND; } // check read permission if (result == B_OK) { if (!volume->GetNodePermissions(node).ImpliesReadPermission()) result = B_PERMISSION_DENIED; } // open the node FileHandle* handle = NULL; if (result == B_OK) result = volume->Open(node, O_RDONLY, &handle); NodeHandleUnlocker handleUnlocker(volume, handle); managerLocker.Unlock(); // stat the attribute and close the node attr_info attrInfo; StatAttrReply reply; if (result == B_OK) { result = handle->StatAttr(request->name.GetString(), &attrInfo); volume->Close(handle); } // set the attribute info if (result == B_OK) { result = _GetAttrInfo(request, request->name.GetString(), attrInfo, NULL, &reply.attrInfo); } // send the reply reply.error = result; return GetChannel()->SendRequest(&reply); } // VisitOpenQueryRequest status_t ClientConnection::VisitOpenQueryRequest(OpenQueryRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; VolumeManagerLocker managerLocker; // open the query status_t result; QueryHandle* handle = NULL; result = _OpenQuery(request->queryString.GetString(), request->flags, request->port, request->token, &handle); QueryHandleUnlocker handleUnlocker(this, handle); // prepare the reply OpenQueryReply reply; if (result == B_OK) reply.cookie = handle->GetCookie(); managerLocker.Unlock(); // send the reply reply.error = result; status_t error = GetChannel()->SendRequest(&reply); // close the handle, if a send error occurred if (error != B_OK && result == B_OK) _CloseQuery(handle); return error; } // VisitReadQueryRequest status_t ClientConnection::VisitReadQueryRequest(ReadQueryRequest* request) { ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // create an array for the IDs of the client volumes a found entry may // reside on status_t result = B_OK; int32 volumeCount = fVolumes->Size(); int32* volumeIDs = new(std::nothrow) int32[volumeCount]; if (!volumeIDs) result = B_NO_MEMORY; ArrayDeleter volumeIDsDeleter(volumeIDs); // get the query handle QueryHandle* handle = NULL; if (result == B_OK) result = _LockQueryHandle(request->cookie, &handle); QueryHandleUnlocker handleUnlocker(this, handle); // check if it is a query handle QueryHandle* queryHandle = NULL; if (result == B_OK) { queryHandle = dynamic_cast(handle); if (!queryHandle) result = B_BAD_VALUE; } // read the query ReadQueryReply reply; int32 countRead = 0; while (result == B_OK) { uint8 buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH]; struct dirent* dirEntry = (struct dirent*)buffer; result = queryHandle->ReadDir(dirEntry, 1, &countRead); if (result != B_OK) break; if (countRead == 0) break; PRINT(" query entry: %" B_PRIdDEV ", %" B_PRIdINO ", \"%s\"\n", dirEntry->d_pdev, dirEntry->d_pino, dirEntry->d_name); VolumeManagerLocker managerLocker; VolumeManager* volumeManager = VolumeManager::GetDefault(); // load the entry Entry* entry = NULL; result = volumeManager->LoadEntry(dirEntry->d_pdev, dirEntry->d_pino, dirEntry->d_name, true, &entry); // if at least one client volume contains the entry, get an entry info if (result == B_OK) { HasQueryPermissionClientVolumeFilter filter; int32 entryVolumeCount = _GetContainingClientVolumes( entry->GetDirectory(), volumeIDs, volumeCount, &filter); if (entryVolumeCount > 0) { // store all the client volume IDs in the reply for (int32 i = 0; i < entryVolumeCount; i++) { result = reply.clientVolumeIDs.Append(volumeIDs[i]); if (result != B_OK) break; } // get an entry info _GetNodeInfo(entry->GetDirectory(), &reply.dirInfo); _GetEntryInfo(entry, &reply.entryInfo); break; } else PRINT((" -> no client volumes\n")); } // entry is not in the volume: next round... result = B_OK; } // send the reply reply.error = result; reply.count = countRead; PRINT("ReadQuery: (%" B_PRIx32 ", %" B_PRId32 ", " "dir: (%" B_PRIdDEV ", %" B_PRIdINO "), " "node: (%" B_PRIdDEV ", %" B_PRIdINO ", `%s')\n", reply.error, reply.count, reply.entryInfo.directoryID.volumeID, reply.entryInfo.directoryID.nodeID, reply.entryInfo.nodeInfo.st.st_dev, reply.entryInfo.nodeInfo.st.st_ino, reply.entryInfo.name.GetString()); return GetChannel()->SendRequest(&reply); } // #pragma mark - // ProcessNodeMonitoringEvent void ClientConnection::ProcessNodeMonitoringEvent(int32 volumeID, NodeMonitoringEvent* event) { // get a connection reference ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return; _PushNodeMonitoringEvent(volumeID, event); } // CloseNodeMonitoringEventQueue void ClientConnection::CloseNodeMonitoringEventQueue() { typedef Vector RequestVector; const RequestVector* requests = NULL; if (fNodeMonitoringEvents->Close(false, &requests) == B_OK) { for (RequestVector::ConstIterator it = requests->Begin(); it != requests->End(); it++) { delete *it; } } } // #pragma mark - // QueryDomainIntersectsWith bool ClientConnection::QueryDomainIntersectsWith(Volume* volume) { // Iterate through the the client volumes and check whether any one contains // the supplied volume or its root dir is on the volume. We don't check // directory inclusion for the latter, since we don't need to query the // volume, if the client volume is located on a volume mounted somewhere // under the supplied volume (e.g. the root FS contains everything, but does // seldomly need to be queried). VolumeManager* volumeManager = VolumeManager::GetDefault(); AutoLocker volumesLocker(fVolumes); for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) { ClientVolume* clientVolume = it.Next().value; Directory* volumeRoot = volume->GetRootDirectory(); Directory* clientVolumeRoot = clientVolume->GetRootDirectory(); if (volumeManager->DirectoryContains(clientVolumeRoot, volumeRoot, true) || volumeRoot->GetVolumeID() == clientVolumeRoot->GetVolumeID()) { return true; } } return false; } // ProcessQueryEvent void ClientConnection::ProcessQueryEvent(NodeMonitoringEvent* event) { dev_t volumeID; ino_t directoryID; if (event->opcode == B_ENTRY_CREATED) { // "entry created" event EntryCreatedEvent* createdEvent = dynamic_cast(event); if (!createdEvent) return; volumeID = createdEvent->volumeID; directoryID = createdEvent->directoryID; } else if (event->opcode == B_ENTRY_REMOVED) { // "entry removed" event EntryRemovedEvent* removedEvent = dynamic_cast(event); if (!removedEvent) return; volumeID = removedEvent->volumeID; directoryID = removedEvent->directoryID; } else { // We only support "entry created" and "entry removed" query events. // "entry moved" is split by the volume manager into those. ERROR("Ignoring unexpected query event: opcode: 0x%" B_PRIx32 "\n", event->opcode); return; } PRINT("ClientConnection::ProcessQueryEvent(): event: %p, type: %s:" " directory: (%" B_PRIdDEV ", %" B_PRIdINO ")\n", event, typeid(event).name(), volumeID, directoryID); // create an array for the IDs of the client volumes a found entry may // reside on int32 volumeCount = fVolumes->Size(); int32* volumeIDs = new(std::nothrow) int32[volumeCount]; if (!volumeIDs) return; ArrayDeleter volumeIDsDeleter(volumeIDs); HasQueryPermissionClientVolumeFilter filter; // load the directory the concerned entry belongs/belonged to Directory* directory; int32 concernedVolumes = 0; if (VolumeManager::GetDefault()->LoadDirectory(volumeID, directoryID, &directory) == B_OK) { // find out, which client volumes the directory is located in concernedVolumes = _GetContainingClientVolumes(directory, volumeIDs, volumeCount, &filter); } else { // Failed to load the directory, so maybe it has already been // deleted. For "entry removed" events, we consider all client // volumes to be notified -- those that don't know the entry will // ignore the event. if (event->opcode == B_ENTRY_REMOVED) { concernedVolumes = _GetAllClientVolumeIDs(volumeIDs, volumeCount, &filter); } } // now push the event for each concerned client volume for (int32 i = 0; i < concernedVolumes; i++) _PushNodeMonitoringEvent(volumeIDs[i], event); // TODO: More than one volume will usually only be concerned in case of // nested client volumes. We could optimize the case by having an array of // volume IDs in the respective requests sent over the net (just as in the // ReadQueryReply). } // #pragma mark - // _Close void ClientConnection::_Close() { // terminate node monitoring processor CloseNodeMonitoringEventQueue(); if (fNodeMonitoringProcessor >= 0 && find_thread(NULL) != fNodeMonitoringProcessor) { int32 result; wait_for_thread(fNodeMonitoringProcessor, &result); // The variable is not unset, when this is the node monitoring // processor thread -- which is good, since the destructor will // wait for the thread in this case. fNodeMonitoringProcessor = -1; } if (fConnection) fConnection->Close(); // notify the listener ClientConnectionListener* listener = fListener; fListener = NULL; if (listener) listener->ClientConnectionClosed(this, fError); } // _MarkClosed void ClientConnection::_MarkClosed(bool error) { AutoLocker _(fLock); if (!fClosed) { fClosed = true; fError = error; } } // _GetNodeInfo void ClientConnection::_GetNodeInfo(Node* node, NodeInfo* info) { if (node && info) { info->st = node->GetStat(); info->revision = VolumeManager::GetDefault()->GetRevision(); } } // _GetEntryInfo void ClientConnection::_GetEntryInfo(Entry* entry, EntryInfo* info) { if (entry && info) { info->directoryID.volumeID = entry->GetVolumeID(); info->directoryID.nodeID = entry->GetDirectoryID(); info->name.SetTo(entry->GetName()); _GetNodeInfo(entry->GetNode(), &info->nodeInfo); } } // _GetAttrInfo status_t ClientConnection::_GetAttrInfo(Request* request, const char* name, const attr_info& attrInfo, const void* data, AttributeInfo* info) { if (!request || !name || !info) return B_BAD_VALUE; info->name.SetTo(name); info->info = attrInfo; data = (attrInfo.size > 0 ? data : NULL); int32 dataSize = (data ? attrInfo.size : 0); info->data.SetTo(data, dataSize); // if the client has inverse endianess, swap the type, if we don't know it if (fInverseClientEndianess) { if (_KnownAttributeType(info->info.type)) { // we need to convert the data, if supplied if (data) { // allocate a buffer RequestBuffer* requestBuffer = RequestBuffer::Create(dataSize); if (!requestBuffer) return B_NO_MEMORY; // convert the data memcpy(requestBuffer->GetData(), data, dataSize); _ConvertAttribute(info->info, requestBuffer->GetData()); } } else info->info.type = B_SWAP_INT32(info->info.type); } return B_OK; } // _GetAttrDirInfo status_t ClientConnection::_GetAttrDirInfo(Request* request, AttributeDirectory* attrDir, AttrDirInfo* info) { if (!request || !attrDir || !info || !attrDir->IsAttrDirValid()) return B_BAD_VALUE; // add the attribute infos for (Attribute* attribute = attrDir->GetFirstAttribute(); attribute; attribute = attrDir->GetNextAttribute(attribute)) { // get the attribute info AttributeInfo attrInfo; attr_info bAttrInfo; attribute->GetInfo(&bAttrInfo); status_t error = _GetAttrInfo(request, attribute->GetName(), bAttrInfo, attribute->GetData(), &attrInfo); // append it if (error == B_OK) error = info->attributeInfos.Append(attrInfo); if (error != B_OK) return error; } info->revision = VolumeManager::GetDefault()->GetRevision(); info->isValid = true; return B_OK; } // _CreateVolume status_t ClientConnection::_CreateVolume(ClientVolume** _volume) { // create and init the volume ClientVolume* volume = new(std::nothrow) ClientVolume(fSecurityContextLock, this); if (!volume) return B_NO_MEMORY; status_t error = volume->Init(); if (error != B_OK) { delete volume; return error; } // add it to the volume map AutoLocker locker(fVolumes); error = fVolumes->Put(volume->GetID(), volume); locker.Unlock(); if (error == B_OK) *_volume = volume; else delete volume; return error; } // _GetVolume ClientVolume* ClientConnection::_GetVolume(int32 id) { AutoLocker _(fVolumes); ClientVolume* volume = fVolumes->Get(id); if (!volume || volume->IsRemoved()) return NULL; volume->AcquireReference(); return volume; } // _PutVolume // // The VolumeManager may be locked, but no other lock must be held. void ClientConnection::_PutVolume(ClientVolume* volume) { if (!volume) return; // decrement reference counter and remove the volume, if 0 AutoLocker locker(fVolumes); bool removed = (volume->ReleaseReference() == 1 && volume->IsRemoved()); if (removed) fVolumes->Remove(volume->GetID()); locker.Unlock(); if (removed) { VolumeManagerLocker managerLocker; delete volume; } } // _UnmountVolume // // The caller must have a reference to the volume. void ClientConnection::_UnmountVolume(ClientVolume* volume) { if (!volume) return; AutoLocker locker(fVolumes); volume->MarkRemoved(); locker.Unlock(); // push a notification event if (VolumeUnmountedEvent* event = new(std::nothrow) VolumeUnmountedEvent) { VolumeManagerLocker managerLocker; event->opcode = B_DEVICE_UNMOUNTED; _PushNodeMonitoringEvent(volume->GetID(), event); event->ReleaseReference(); } } // _UnmountAllVolumes void ClientConnection::_UnmountAllVolumes() { while (true) { // To avoid heap allocation (which can fail) we unmount the volumes // chunkwise. // get the volumes const int32 volumeChunkSize = 32; ClientVolume* volumes[volumeChunkSize]; int32 volumeCount = 0; AutoLocker volumesLocker(fVolumes); for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext();) { if (ClientVolume* volume = _GetVolume(it.Next().value->GetID())) { volumes[volumeCount++] = volume; } if (volumeCount == volumeChunkSize) break; } volumesLocker.Unlock(); // unmount and put the volumes for (int32 i = 0; i < volumeCount; i++) { ClientVolume* volume = volumes[i]; _UnmountVolume(volume); _PutVolume(volume); } if (volumeCount < volumeChunkSize) break; } } // _NodeMonitoringProcessorEntry int32 ClientConnection::_NodeMonitoringProcessorEntry(void* data) { return ((ClientConnection*)data)->_NodeMonitoringProcessor(); } // _NodeMonitoringProcessor int32 ClientConnection::_NodeMonitoringProcessor() { while (!fClosed) { // get the next request NodeMonitoringRequest* request = NULL; status_t error = fNodeMonitoringEvents->Pop(&request); // get a client connection reference ConnectionReference connectionReference(this); if (!connectionReference.IsValid()) return B_OK; // No request? Next round... if (error != B_OK) continue; ObjectDeleter requestDeleter(request); // send the request error = fConnection->SendRequest(request); if (error != B_OK) { ERROR(("ClientConnection::_NodeMonitoringProcessor(): " "Failed to send request.\n")); } } return 0; } // _PushNodeMonitoringEvent // // The caller must have a connection reference. Moreover the VolumeManager // must be locked. status_t ClientConnection::_PushNodeMonitoringEvent(int32 volumeID, NodeMonitoringEvent* event) { if (!event) return B_BAD_VALUE; // get the volume ClientVolume* volume = _GetVolume(volumeID); if (!volume && event->opcode != B_DEVICE_UNMOUNTED) return B_BAD_VALUE; ClientVolumePutter volumePutter(this, volume); // create a node monitoring request NodeMonitoringRequest* request = NULL; status_t error = B_ERROR; switch (event->opcode) { case B_ENTRY_CREATED: error = _EntryCreated(volume, dynamic_cast(event), request); break; case B_ENTRY_REMOVED: error = _EntryRemoved(volume, dynamic_cast(event), request); break; case B_ENTRY_MOVED: error = _EntryMoved(volume, dynamic_cast(event), request); break; case B_STAT_CHANGED: error = _NodeStatChanged(volume, dynamic_cast(event), request); break; case B_ATTR_CHANGED: error = _NodeAttributeChanged(volume, dynamic_cast(event), request); break; case B_DEVICE_UNMOUNTED: error = B_OK; break; } // replace all data buffers -- when the request is actually sent, they // might no longer exist if (error == B_OK) error = RequestBufferReplacer().ReplaceBuffer(request); if (error == B_OK) { // common initialization request->volumeID = volumeID; request->opcode = event->opcode; request->revision = VolumeManager::GetDefault()->GetRevision(); // push the request error = fNodeMonitoringEvents->Push(request); if (error != B_OK) delete request; } return error; } // _EntryCreated status_t ClientConnection::_EntryCreated(ClientVolume* volume, EntryCreatedEvent* event, NodeMonitoringRequest*& _request) { // allocate the request EntryCreatedRequest* request = new(std::nothrow) EntryCreatedRequest; if (!request) return B_NO_MEMORY; ObjectDeleter requestDeleter(request); // get the name const char* name = event->name.GetString(); // set the request fields request->directoryID = NodeID(event->volumeID, event->directoryID); request->nodeID = NodeID(event->volumeID, event->nodeID); request->name.SetTo(name); if (event->queryHandler) { request->port = event->remotePort; request->token = event->remoteToken; request->queryUpdate = true; } else request->queryUpdate = false; // try to get an entry info Entry* entry; if (VolumeManager::GetDefault()->LoadEntry(event->volumeID, event->directoryID, name, true, &entry) == B_OK && entry->GetNode()->GetVolumeID() == event->volumeID && entry->GetNode()->GetID() == event->nodeID) { _GetEntryInfo(entry, &request->entryInfo); request->entryInfoValid = true; } else request->entryInfoValid = false; requestDeleter.Detach(); _request = request; return B_OK; } // _EntryRemoved status_t ClientConnection::_EntryRemoved(ClientVolume* volume, EntryRemovedEvent* event, NodeMonitoringRequest*& _request) { // special handling, if it is the root node of the client volume that has // been removed if (!event->queryHandler && NodeRef(event->nodeVolumeID, event->nodeID) == volume->GetRootNodeRef()) { NoAllocEntryRef ref(event->nodeVolumeID, event->nodeID, "."); BEntry entry; if (FDManager::SetEntry(&entry, &ref) != B_OK || !entry.Exists()) _UnmountVolume(volume); // don't send the "entry removed" event return B_ERROR; } // allocate the request EntryRemovedRequest* request = new(std::nothrow) EntryRemovedRequest; if (!request) return B_NO_MEMORY; ObjectDeleter requestDeleter(request); // get the name const char* name = event->name.GetString(); // set the request fields request->directoryID = NodeID(event->volumeID, event->directoryID); request->nodeID = NodeID(event->nodeVolumeID, event->nodeID); request->name.SetTo(name); if (event->queryHandler) { request->port = event->remotePort; request->token = event->remoteToken; request->queryUpdate = true; } else request->queryUpdate = false; requestDeleter.Detach(); _request = request; return B_OK; } // _EntryMoved status_t ClientConnection::_EntryMoved(ClientVolume* volume, EntryMovedEvent* event, NodeMonitoringRequest*& _request) { // allocate the request EntryMovedRequest* request = new(std::nothrow) EntryMovedRequest; if (!request) return B_NO_MEMORY; ObjectDeleter requestDeleter(request); // allocate memory for the names int32 fromNameLen = event->fromName.GetLength(); const char* fromName = (fromNameLen > 0 ? event->fromName.GetString() : NULL); const char* toName = event->toName.GetString(); // set the request fields request->fromDirectoryID = NodeID(event->volumeID, event->fromDirectoryID); request->toDirectoryID = NodeID(event->volumeID, event->toDirectoryID); request->nodeID = NodeID(event->nodeVolumeID, event->nodeID); request->fromName.SetTo(fromName); request->toName.SetTo(toName); request->queryUpdate = false; // try to get an entry info Entry* entry; if (VolumeManager::GetDefault()->LoadEntry(event->volumeID, event->toDirectoryID, toName, true, &entry) == B_OK && entry->GetNode()->GetVolumeID() == event->nodeVolumeID && entry->GetNode()->GetID() == event->nodeID) { _GetEntryInfo(entry, &request->entryInfo); request->entryInfoValid = true; } else request->entryInfoValid = false; requestDeleter.Detach(); _request = request; return B_OK; } // _NodeStatChanged status_t ClientConnection::_NodeStatChanged(ClientVolume* volume, StatChangedEvent* event, NodeMonitoringRequest*& _request) { // get the node Node* node = volume->GetNode(event->volumeID, event->nodeID); if (!node) return B_ENTRY_NOT_FOUND; // allocate the request StatChangedRequest* request = new(std::nothrow) StatChangedRequest; if (!request) return B_NO_MEMORY; ObjectDeleter requestDeleter(request); // set the request fields request->nodeID = NodeID(event->volumeID, event->nodeID); _GetNodeInfo(node, &request->nodeInfo); request->queryUpdate = false; requestDeleter.Detach(); _request = request; return B_OK; } // _NodeAttributeChanged status_t ClientConnection::_NodeAttributeChanged(ClientVolume* volume, AttributeChangedEvent* event, NodeMonitoringRequest*& _request) { // get the node Node* node = volume->GetNode(event->volumeID, event->nodeID); if (!node) return B_ENTRY_NOT_FOUND; // update the attribute directory bool removed = false; bool valid = false; attr_info info; const void* data = NULL; status_t error = node->UpdateAttribute(event->attribute.GetString(), &removed, &info, &data); valid = (error == B_OK); // allocate the request AttributeChangedRequest* request = new(std::nothrow) AttributeChangedRequest; if (!request) return B_NO_MEMORY; ObjectDeleter requestDeleter(request); // get an attr dir info, if the directory is valid if (node->IsAttrDirValid()) { status_t error = _GetAttrDirInfo(request, node, &request->attrDirInfo); if (error != B_OK) return error; } // get name and the data size int32 dataSize = (data ? info.size : 0); const char* name = event->attribute.GetString(); // set the request fields request->nodeID = NodeID(event->volumeID, event->nodeID); request->attrInfo.name.SetTo(name); request->valid = valid; request->removed = removed; if (!removed && valid) { request->attrInfo.info = info; request->attrInfo.data.SetTo(data, dataSize); } request->queryUpdate = false; requestDeleter.Detach(); _request = request; return B_OK; } // _KnownAttributeType bool ClientConnection::_KnownAttributeType(type_code type) { if (!fInverseClientEndianess) return false; switch (type) { case B_BOOL_TYPE: case B_CHAR_TYPE: case B_COLOR_8_BIT_TYPE: case B_DOUBLE_TYPE: case B_FLOAT_TYPE: case B_GRAYSCALE_8_BIT_TYPE: case B_INT64_TYPE: case B_INT32_TYPE: case B_INT16_TYPE: case B_INT8_TYPE: case B_MESSAGE_TYPE: case B_MESSENGER_TYPE: case B_MIME_TYPE: case B_MONOCHROME_1_BIT_TYPE: case B_OFF_T_TYPE: case B_POINTER_TYPE: case B_POINT_TYPE: case B_RECT_TYPE: case B_REF_TYPE: case B_RGB_COLOR_TYPE: case B_SIZE_T_TYPE: case B_SSIZE_T_TYPE: case B_STRING_TYPE: case B_TIME_TYPE: case B_UINT64_TYPE: case B_UINT32_TYPE: case B_UINT16_TYPE: case B_UINT8_TYPE: case B_ASCII_TYPE: case B_MIME_STRING_TYPE: return true; //B_RGB_32_BIT_TYPE: We could translate it, but it's heavy... } return false; } // _ConvertAttribute void ClientConnection::_ConvertAttribute(const attr_info& info, void* buffer) { swap_data(info.type, buffer, info.size, B_SWAP_ALWAYS); } // #pragma mark - // _OpenQuery status_t ClientConnection::_OpenQuery(const char* queryString, uint32 flags, port_id remotePort, int32 remoteToken, QueryHandle** _handle) { if (!queryString || !_handle) return B_BAD_VALUE; // open query QueryHandle* queryHandle; status_t error = VolumeManager::GetDefault()->OpenQuery(this, queryString, flags, remotePort, remoteToken, &queryHandle); if (error != B_OK) return error; BReference handleReference(queryHandle, true); // lock the handle queryHandle->Lock(); // add the handle error = fQueryHandles->AddNodeHandle(queryHandle); if (error != B_OK) return error; handleReference.Detach(); *_handle = queryHandle; return B_OK; } // _CloseQuery status_t ClientConnection::_CloseQuery(QueryHandle* handle) { if (!handle || !fQueryHandles->RemoveNodeHandle(handle)) return B_BAD_VALUE; return B_OK; } // _LockQueryHandle // // VolumeManager must NOT be locked. status_t ClientConnection::_LockQueryHandle(int32 cookie, QueryHandle** _handle) { NodeHandle* handle; status_t error = fQueryHandles->LockNodeHandle(cookie, &handle); if (error == B_OK) *_handle = static_cast(handle); return error; } // _UnlockQueryHandle // // VolumeManager may or may not be locked. void ClientConnection::_UnlockQueryHandle(NodeHandle* nodeHandle) { fQueryHandles->UnlockNodeHandle(nodeHandle); } // #pragma mark - // _GetAllClientVolumeIDs int32 ClientConnection::_GetAllClientVolumeIDs(int32* volumeIDs, int32 arraySize, ClientVolumeFilter* filter) { int32 count = 0; AutoLocker volumesLocker(fVolumes); for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext() && arraySize > count;) { ClientVolume* clientVolume = it.Next().value; if (!filter || filter->FilterVolume(this, clientVolume)) volumeIDs[count++] = clientVolume->GetID(); } return count; } // _GetContainingClientVolumes int32 ClientConnection::_GetContainingClientVolumes(Directory* directory, int32* volumeIDs, int32 arraySize, ClientVolumeFilter* filter) { int32 count = 0; VolumeManager* volumeManager = VolumeManager::GetDefault(); AutoLocker volumesLocker(fVolumes); for (VolumeMap::Iterator it = fVolumes->GetIterator(); it.HasNext() && arraySize > count;) { ClientVolume* clientVolume = it.Next().value; Directory* clientVolumeRoot = clientVolume->GetRootDirectory(); if (volumeManager->DirectoryContains(clientVolumeRoot, directory, true) && (!filter || filter->FilterVolume(this, clientVolume))) { volumeIDs[count++] = clientVolume->GetID(); } } return count; } // #pragma mark - // #pragma mark ----- ClientConnectionListener ----- // constructor ClientConnectionListener::ClientConnectionListener() { } // destructor ClientConnectionListener::~ClientConnectionListener() { }