⛏️ index : haiku.git

// ClientVolume.cpp

#include <new>

#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <HashMap.h>
#include <Path.h>

#include "ClientVolume.h"
#include "DebugSupport.h"
#include "Directory.h"
#include "Entry.h"
#include "GlobalBlockerPool.h"
#include "NodeHandle.h"
#include "NodeHandleMap.h"
#include "NodeMonitoringEvent.h"
#include "SecurityContext.h"
#include "StatisticsManager.h"
#include "UserSecurityContext.h"
#include "Volume.h"
#include "VolumeManager.h"

// constructor
ClientVolume::ClientVolume(Locker& securityContextLocker,
	NodeMonitoringProcessor* nodeMonitoringProcessor)
	: FSObject(),
	  fID(_NextVolumeID()),
	  fSecurityContext(NULL),
	  fSecurityContextLock(securityContextLocker),
	  fNodeMonitoringProcessor(nodeMonitoringProcessor),
	  fNodeHandles(NULL),
	  fShare(NULL),
	  fRootNodeRef(),
	  fSharePermissions(),
	  fMounted(false)
{
}

// destructor
ClientVolume::~ClientVolume()
{
	Unmount();

	if (fShare)
		fShare->ReleaseReference();

	delete fNodeHandles;
	delete fSecurityContext;
}

// Init
status_t
ClientVolume::Init()
{
	// create the node handle map
	fNodeHandles = new(std::nothrow) NodeHandleMap("node handles");
	if (!fNodeHandles)
		return B_NO_MEMORY;
	status_t error = fNodeHandles->Init();
	if (error != B_OK)
		return error;

	return B_OK;
}

// GetID
int32
ClientVolume::GetID() const
{
	return fID;
}

// Mount
status_t
ClientVolume::Mount(UserSecurityContext* securityContext, Share* share)
{
	if (!securityContext || !share)
		return B_BAD_VALUE;
	ObjectDeleter<UserSecurityContext> securityContextDeleter(securityContext);
	if (IsMounted())
		return B_BAD_VALUE;
	fSecurityContext = securityContext;
	securityContextDeleter.Detach();

	fShare = share;
	fShare->AcquireReference();
	dev_t volumeID = share->GetVolumeID();
	ino_t nodeID = share->GetNodeID();

	// into root node ref
	fRootNodeRef.device = volumeID;
	fRootNodeRef.node = nodeID;

	// get the share permissions
	fSharePermissions = securityContext->GetNodePermissions(volumeID, nodeID);

	// get the root directory
	VolumeManager* volumeManager = VolumeManager::GetDefault();
	Directory* rootDir;
	status_t error = volumeManager->LoadDirectory(volumeID, nodeID, &rootDir);
	if (error != B_OK)
		return error;

	// register with the volume manager
	error = volumeManager->AddClientVolume(this);
	if (error != B_OK) {
		Unmount();
		return error;
	}
	fMounted = true;

	// notify the statistics manager
	StatisticsManager::GetDefault()->ShareMounted(fShare,
		fSecurityContext->GetUser());

	return B_OK;
}

// Unmount
void
ClientVolume::Unmount()
{
	PRINT(("ClientVolume::Unmount()\n"));

	if (fMounted) {
		fMounted = false;

		// notify the statistics manager
		StatisticsManager::GetDefault()->ShareUnmounted(fShare,
			fSecurityContext->GetUser());
	}

	// remove ourselves from the volume manager
	VolumeManager::GetDefault()->RemoveClientVolume(this);

	// close all node handles
//	while (true) {
//		// get a cookie
//		int32 cookie;
//		{
//			NodeHandleMap::Iterator it = fNodeHandles->GetIterator();
//			if (!it.HasNext())
//				break;
//			cookie = it.Next().key.value;
//		}
//
//		// get the handle
//		NodeHandle* handle;
//		status_t error = LockNodeHandle(cookie, &handle);
//		if (error == B_OK) {
//			// close the node handle
//			ClientNodeUnlocker _(handle->GetClientNode());
//			Close(handle);
//		} else {
//			ClientVolumeLocker _(this);
//			if (fNodeHandles->ContainsKey(cookie)) {
//				// something went seriously wrong
//				ERROR(("ClientVolume::Unmount(): ERROR: Failed to lock "
//					"existing node handle! Can't continue Unmount().\n"));
//				return;
//			}
//		}
//	}
}


// IsMounted
bool
ClientVolume::IsMounted() const
{
	return fMounted;
}

// GetSecurityContext
//
// Caller must hold fSecurityContextLock. Only the ClientConnection should
// do this.
UserSecurityContext*
ClientVolume::GetSecurityContext() const
{
	return fSecurityContext;
}

// SetSecurityContext
void
ClientVolume::SetSecurityContext(UserSecurityContext* securityContext)
{
	AutoLocker<Locker> locker(fSecurityContextLock);

	// unset old
	delete fSecurityContext;

	// set new
	fSecurityContext = securityContext;
	fSharePermissions = fSecurityContext->GetNodePermissions(fRootNodeRef);
}

// GetShare
Share*
ClientVolume::GetShare() const
{
	return fShare;
}

// GetRootDirectory
Directory*
ClientVolume::GetRootDirectory() const
{
	return VolumeManager::GetDefault()->GetDirectory(
		fRootNodeRef.device, fRootNodeRef.node);
}

// GetRootNodeRef
const NodeRef&
ClientVolume::GetRootNodeRef() const
{
	return fRootNodeRef;
}

// GetSharePermissions
Permissions
ClientVolume::GetSharePermissions() const
{
	return fSharePermissions;
}

// GetNodePermissions
Permissions
ClientVolume::GetNodePermissions(dev_t volumeID, ino_t nodeID)
{
	return fSharePermissions;
}

// GetNodePermissions
Permissions
ClientVolume::GetNodePermissions(Node* node)
{
// TODO: We should also check whether the node is located on the client volume
// in the first place. Otherwise someone with access to a low-security share
// could get access to arbitrary nodes on the server.
	return fSharePermissions;
}

// GetNode
Node*
ClientVolume::GetNode(dev_t volumeID, ino_t nodeID)
{
	VolumeManager* volumeManager = VolumeManager::GetDefault();

	// get the node
	Node* node = volumeManager->GetNode(volumeID, nodeID);
	if (!node)
		return NULL;

	// check, if the node is contained by the root dir of the client volume
	if (volumeManager->DirectoryContains(GetRootDirectory(), node, true))
		return node;

	return NULL;
}

// GetNode
Node*
ClientVolume::GetNode(NodeID nodeID)
{
	return GetNode(nodeID.volumeID, nodeID.nodeID);
}

// GetNode
Node*
ClientVolume::GetNode(const node_ref& nodeRef)
{
	return GetNode(nodeRef.device, nodeRef.node);
}

// GetDirectory
Directory*
ClientVolume::GetDirectory(dev_t volumeID, ino_t nodeID)
{
	VolumeManager* volumeManager = VolumeManager::GetDefault();

	// get the directory
	Directory* dir = GetDirectory(volumeID, nodeID);
	if (!dir)
		return NULL;

	// check, if the dir is contained by the root dir of the client volume
	if (volumeManager->DirectoryContains(GetRootDirectory(), dir, true))
		return dir;

	return NULL;
}

// GetDirectory
Directory*
ClientVolume::GetDirectory(NodeID nodeID)
{
	return GetDirectory(nodeID.volumeID, nodeID.nodeID);
}

// LoadDirectory
status_t
ClientVolume::LoadDirectory(dev_t volumeID, ino_t nodeID,
	Directory** _directory)
{
	if (!_directory)
		return B_BAD_VALUE;

	VolumeManager* volumeManager = VolumeManager::GetDefault();

	// load the directory
	Directory* dir;
	status_t error = volumeManager->LoadDirectory(volumeID, nodeID, &dir);
	if (error != B_OK)
		return error;

	// check, if the dir is contained by the root dir of the client volume
	if (!volumeManager->DirectoryContains(GetRootDirectory(), dir, true))
		return B_ENTRY_NOT_FOUND;

	*_directory = dir;
	return B_OK;
}

// GetEntry
Entry*
ClientVolume::GetEntry(dev_t volumeID, ino_t dirID, const char* name)
{
	VolumeManager* volumeManager = VolumeManager::GetDefault();

	// get the entry
	Entry* entry = volumeManager->GetEntry(volumeID, dirID, name);
	if (!entry)
		return NULL;

	// check, if the entry is contained by the root dir of the client volume
	if (volumeManager->DirectoryContains(GetRootDirectory(), entry))
		return entry;

	return NULL;
}

// GetEntry
Entry*
ClientVolume::GetEntry(Directory* directory, const char* name)
{
	if (!directory)
		return NULL;

	return GetEntry(directory->GetVolumeID(), directory->GetID(), name);
}

// LoadEntry
status_t
ClientVolume::LoadEntry(dev_t volumeID, ino_t dirID, const char* name,
	Entry** _entry)
{
	if (!name || !_entry)
		return B_BAD_VALUE;

	VolumeManager* volumeManager = VolumeManager::GetDefault();

	// get the entry
	Entry* entry;
	status_t error = VolumeManager::GetDefault()->LoadEntry(volumeID, dirID,
		name, true, &entry);
	if (error != B_OK)
		return error;

	// check, if the entry is contained by the root dir of the client volume
	if (!volumeManager->DirectoryContains(GetRootDirectory(), entry))
		return B_ENTRY_NOT_FOUND;

	*_entry = entry;
	return B_OK;
}

// LoadEntry
status_t
ClientVolume::LoadEntry(Directory* directory, const char* name, Entry** entry)
{
	if (!directory)
		return B_BAD_VALUE;

	return LoadEntry(directory->GetVolumeID(), directory->GetID(), name, entry);
}

// Open
//
// The caller gets a lock to the returned node handle.
status_t
ClientVolume::Open(Node* node, int openMode, FileHandle** _handle)
{
	if (!node || !_handle)
		return B_BAD_VALUE;

	// open the node
	FileHandle* handle = NULL;
	status_t error = node->Open(openMode, &handle);
	if (error != B_OK)
		return error;
	BReference<NodeHandle> handleReference(handle, true);

	// lock the handle
	handle->Lock();

	// add the handle
	error = fNodeHandles->AddNodeHandle(handle);
	if (error != B_OK)
		return error;

	handleReference.Detach();
	*_handle = handle;
	return B_OK;
}

// OpenDir
//
// The caller gets a lock to the returned node handle.
status_t
ClientVolume::OpenDir(Directory* directory, DirIterator** _iterator)
{
	if (!directory || !_iterator)
		return B_BAD_VALUE;

	// open the directory
	DirIterator* iterator = NULL;
	status_t error = directory->OpenDir(&iterator);
	if (error != B_OK)
		return error;
	BReference<NodeHandle> handleReference(iterator, true);

	// lock the handle
	iterator->Lock();

	// add the handle
	error = fNodeHandles->AddNodeHandle(iterator);
	if (error != B_OK)
		return error;

	handleReference.Detach();
	*_iterator = iterator;
	return B_OK;
}

// OpenAttrDir
//
// The caller gets a lock to the returned node handle.
status_t
ClientVolume::OpenAttrDir(Node* node, AttrDirIterator** _iterator)
{
	if (!node || !_iterator)
		return B_BAD_VALUE;

	// open the attribut directory
	AttrDirIterator* iterator = NULL;
	status_t error = node->OpenAttrDir(&iterator);
	if (error != B_OK)
		return error;
	BReference<NodeHandle> handleReference(iterator, true);

	// lock the handle
	iterator->Lock();

	// add the handle
	error = fNodeHandles->AddNodeHandle(iterator);
	if (error != B_OK)
		return error;

	handleReference.Detach();
	*_iterator = iterator;
	return B_OK;
}

// Close
//
// VolumeManager MUST be locked. After closing the handle must still be
// unlocked. When the last reference is surrendered it will finally be deleted.
status_t
ClientVolume::Close(NodeHandle* handle)
{
	if (!handle || !fNodeHandles->RemoveNodeHandle(handle))
		return B_BAD_VALUE;

	return B_OK;
}

// LockNodeHandle
//
// VolumeManager must NOT be locked.
status_t
ClientVolume::LockNodeHandle(int32 cookie, NodeHandle** _handle)
{
	return fNodeHandles->LockNodeHandle(cookie, _handle);
}

// UnlockNodeHandle
//
// VolumeManager may or may not be locked.
void
ClientVolume::UnlockNodeHandle(NodeHandle* nodeHandle)
{
	fNodeHandles->UnlockNodeHandle(nodeHandle);
}

// ProcessNodeMonitoringEvent
void
ClientVolume::ProcessNodeMonitoringEvent(NodeMonitoringEvent* event)
{
	if (fNodeMonitoringProcessor)
		fNodeMonitoringProcessor->ProcessNodeMonitoringEvent(fID, event);
}

// _NextVolumeID
int32
ClientVolume::_NextVolumeID()
{
	return atomic_add(&sNextVolumeID, 1);
}

// sNextVolumeID
int32 ClientVolume::sNextVolumeID = 0;


// #pragma -

// destructor
ClientVolume::NodeMonitoringProcessor::~NodeMonitoringProcessor()
{
}