// ClientConnection.cpp
#include "ClientConnection.h"
#include <new>
#include <typeinfo>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <utime.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Entry.h>
#include <fs_query.h>
#include <GraphicsDefs.h>
#include <HashMap.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <Rect.h>
#include <Mime.h>
#include <fsproto.h>
#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<HashKey32<int32>, 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<NodeMonitoringRequest> {
NodeMonitoringEventQueue()
: BlockingQueue<NodeMonitoringRequest>("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<Locker> _(fLock);
if (fClosed || !atomic_or(&fInitialized, 0))
return false;
fReferenceCount++;
return true;
}
// PutReference
void
ClientConnection::PutReference()
{
bool close = false;
{
AutoLocker<Locker> _(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<VolumeMap> 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<VolumeMap> 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<VolumeMap> 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<Locker> 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<Locker> securityContextLocker(fSecurityContextLock);
const char* userName = request->user.GetString();
User* user = fUser;
BReference<User> 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<UserSecurityContext> securityContextDeleter(securityContext);
// get the share
Share* share = NULL;
Permissions sharePermissions;
node_ref mountPoint;
if (result == B_OK) {
AutoLocker<SecurityContext> _(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<Share> 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<VolumeMap> 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<Directory*>(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;
if (result == B_OK)
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<FileHandle*>(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<FileHandle*>(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<Directory*>(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<Directory*>(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<Directory*>(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<Directory*>(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<Directory*>(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<Directory*>(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<Directory*>(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<Directory*>(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)
PRINT("OpenDir() failed: client volume: %ld, node: (%ld, %lld)\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<DirIterator*>(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<Directory*>(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: (%ld, %lld)\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) {
PRINT("ReadDir done: volume: %ld, (%ld, %lld) -> (%lx, %ld)\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<Directory*>(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: (%ld, %lld, `%s') -> (%lx, (%ld, %lld), `%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<Directory*>(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: (%ld, %lld, %ld) -> (%lx, %ld)\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<AttrDirIterator*>(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[sizeof(struct dirent) + 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 = B_OK;
QueryHandle* handle = NULL;
if (result == B_OK) {
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<int32> 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<QueryHandle*>(handle);
if (!queryHandle)
result = B_BAD_VALUE;
}
// read the query
ReadQueryReply reply;
int32 countRead = 0;
while (result == B_OK) {
uint8 buffer[sizeof(struct dirent) + 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: %ld, %lld, \"%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: (%lx, %ld, dir: (%ld, %lld), node: (%ld, %lld, `%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<NodeMonitoringRequest*> 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<VolumeMap> 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<EntryCreatedEvent*>(event);
if (!createdEvent)
return;
volumeID = createdEvent->volumeID;
directoryID = createdEvent->directoryID;
} else if (event->opcode == B_ENTRY_REMOVED) {
// "entry removed" event
EntryRemovedEvent* removedEvent
= dynamic_cast<EntryRemovedEvent*>(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: (%ld, %lld)\n", event, typeid(event).name(),
volumeID, directoryID);
// 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<int32> 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<Locker> _(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<VolumeMap> 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<VolumeMap> _(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<VolumeMap> 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<VolumeMap> 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<VolumeMap> 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<NodeMonitoringRequest> requestDeleter(request);
// send the request
if (error == B_OK) {
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<EntryCreatedEvent*>(event), request);
break;
case B_ENTRY_REMOVED:
error = _EntryRemoved(volume,
dynamic_cast<EntryRemovedEvent*>(event), request);
break;
case B_ENTRY_MOVED:
error = _EntryMoved(volume,
dynamic_cast<EntryMovedEvent*>(event), request);
break;
case B_STAT_CHANGED:
error = _NodeStatChanged(volume,
dynamic_cast<StatChangedEvent*>(event), request);
break;
case B_ATTR_CHANGED:
error = _NodeAttributeChanged(volume,
dynamic_cast<AttributeChangedEvent*>(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<NodeMonitoringRequest> 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<NodeMonitoringRequest> 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<NodeMonitoringRequest> 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<NodeMonitoringRequest> 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<NodeMonitoringRequest> 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<QueryHandle> 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<QueryHandle*>(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<VolumeMap> 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<VolumeMap> 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()
{
}
↑ V547 Expression 'error == ((int) 0)' is always true.
↑ V547 Expression 'result == ((int) 0)' is always true.
↑ V547 Expression 'result == ((int) 0)' is always true.
↑ V595 The 'volume' pointer was utilized before it was verified against nullptr. Check lines: 596, 606.
↑ V595 The 'node' pointer was utilized before it was verified against nullptr. Check lines: 729, 739.