1// ClientVolume.cpp
2
3#include <new>
4
5#include <AutoDeleter.h>
6#include <AutoLocker.h>
7#include <HashMap.h>
8#include <Path.h>
9
10#include "ClientVolume.h"
11#include "DebugSupport.h"
12#include "Directory.h"
13#include "Entry.h"
14#include "GlobalBlockerPool.h"
15#include "NodeHandle.h"
16#include "NodeHandleMap.h"
17#include "NodeMonitoringEvent.h"
18#include "SecurityContext.h"
19#include "StatisticsManager.h"
20#include "UserSecurityContext.h"
21#include "Volume.h"
22#include "VolumeManager.h"
23
24// constructor
25ClientVolume::ClientVolume(Locker& securityContextLocker,
26	NodeMonitoringProcessor* nodeMonitoringProcessor)
27	: FSObject(),
28	  fID(_NextVolumeID()),
29	  fSecurityContext(NULL),
30	  fSecurityContextLock(securityContextLocker),
31	  fNodeMonitoringProcessor(nodeMonitoringProcessor),
32	  fNodeHandles(NULL),
33	  fShare(NULL),
34	  fRootNodeRef(),
35	  fSharePermissions(),
36	  fMounted(false)
37{
38}
39
40// destructor
41ClientVolume::~ClientVolume()
42{
43	Unmount();
44
45	if (fShare)
46		fShare->ReleaseReference();
47
48	delete fNodeHandles;
49	delete fSecurityContext;
50}
51
52// Init
53status_t
54ClientVolume::Init()
55{
56	// create the node handle map
57	fNodeHandles = new(std::nothrow) NodeHandleMap("node handles");
58	if (!fNodeHandles)
59		return B_NO_MEMORY;
60	status_t error = fNodeHandles->Init();
61	if (error != B_OK)
62		return error;
63
64	return B_OK;
65}
66
67// GetID
68int32
69ClientVolume::GetID() const
70{
71	return fID;
72}
73
74// Mount
75status_t
76ClientVolume::Mount(UserSecurityContext* securityContext, Share* share)
77{
78	if (!securityContext || !share)
79		return B_BAD_VALUE;
80	ObjectDeleter<UserSecurityContext> securityContextDeleter(securityContext);
81	if (IsMounted())
82		return B_BAD_VALUE;
83	fSecurityContext = securityContext;
84	securityContextDeleter.Detach();
85
86	fShare = share;
87	fShare->AcquireReference();
88	dev_t volumeID = share->GetVolumeID();
89	ino_t nodeID = share->GetNodeID();
90
91	// into root node ref
92	fRootNodeRef.device = volumeID;
93	fRootNodeRef.node = nodeID;
94
95	// get the share permissions
96	fSharePermissions = securityContext->GetNodePermissions(volumeID, nodeID);
97
98	// get the root directory
99	VolumeManager* volumeManager = VolumeManager::GetDefault();
100	Directory* rootDir;
101	status_t error = volumeManager->LoadDirectory(volumeID, nodeID, &rootDir);
102	if (error != B_OK)
103		return error;
104
105	// register with the volume manager
106	error = volumeManager->AddClientVolume(this);
107	if (error != B_OK) {
108		Unmount();
109		return error;
110	}
111	fMounted = true;
112
113	// notify the statistics manager
114	StatisticsManager::GetDefault()->ShareMounted(fShare,
115		fSecurityContext->GetUser());
116
117	return B_OK;
118}
119
120// Unmount
121void
122ClientVolume::Unmount()
123{
124	PRINT(("ClientVolume::Unmount()\n"));
125
126	if (fMounted) {
127		fMounted = false;
128
129		// notify the statistics manager
130		StatisticsManager::GetDefault()->ShareUnmounted(fShare,
131			fSecurityContext->GetUser());
132	}
133
134	// remove ourselves from the volume manager
135	VolumeManager::GetDefault()->RemoveClientVolume(this);
136
137	// close all node handles
138//	while (true) {
139//		// get a cookie
140//		int32 cookie;
141//		{
142//			NodeHandleMap::Iterator it = fNodeHandles->GetIterator();
143//			if (!it.HasNext())
144//				break;
145//			cookie = it.Next().key.value;
146//		}
147//
148//		// get the handle
149//		NodeHandle* handle;
150//		status_t error = LockNodeHandle(cookie, &handle);
151//		if (error == B_OK) {
152//			// close the node handle
153//			ClientNodeUnlocker _(handle->GetClientNode());
154//			Close(handle);
155//		} else {
156//			ClientVolumeLocker _(this);
157//			if (fNodeHandles->ContainsKey(cookie)) {
158//				// something went seriously wrong
159//				ERROR(("ClientVolume::Unmount(): ERROR: Failed to lock "
160//					"existing node handle! Can't continue Unmount().\n"));
161//				return;
162//			}
163//		}
164//	}
165}
166
167
168// IsMounted
169bool
170ClientVolume::IsMounted() const
171{
172	return fMounted;
173}
174
175// GetSecurityContext
176//
177// Caller must hold fSecurityContextLock. Only the ClientConnection should
178// do this.
179UserSecurityContext*
180ClientVolume::GetSecurityContext() const
181{
182	return fSecurityContext;
183}
184
185// SetSecurityContext
186void
187ClientVolume::SetSecurityContext(UserSecurityContext* securityContext)
188{
189	AutoLocker<Locker> locker(fSecurityContextLock);
190
191	// unset old
192	delete fSecurityContext;
193
194	// set new
195	fSecurityContext = securityContext;
196	fSharePermissions = fSecurityContext->GetNodePermissions(fRootNodeRef);
197}
198
199// GetShare
200Share*
201ClientVolume::GetShare() const
202{
203	return fShare;
204}
205
206// GetRootDirectory
207Directory*
208ClientVolume::GetRootDirectory() const
209{
210	return VolumeManager::GetDefault()->GetDirectory(
211		fRootNodeRef.device, fRootNodeRef.node);
212}
213
214// GetRootNodeRef
215const NodeRef&
216ClientVolume::GetRootNodeRef() const
217{
218	return fRootNodeRef;
219}
220
221// GetSharePermissions
222Permissions
223ClientVolume::GetSharePermissions() const
224{
225	return fSharePermissions;
226}
227
228// GetNodePermissions
229Permissions
230ClientVolume::GetNodePermissions(dev_t volumeID, ino_t nodeID)
231{
232	return fSharePermissions;
233}
234
235// GetNodePermissions
236Permissions
237ClientVolume::GetNodePermissions(Node* node)
238{
239// TODO: We should also check whether the node is located on the client volume
240// in the first place. Otherwise someone with access to a low-security share
241// could get access to arbitrary nodes on the server.
242	return fSharePermissions;
243}
244
245// GetNode
246Node*
247ClientVolume::GetNode(dev_t volumeID, ino_t nodeID)
248{
249	VolumeManager* volumeManager = VolumeManager::GetDefault();
250
251	// get the node
252	Node* node = volumeManager->GetNode(volumeID, nodeID);
253	if (!node)
254		return NULL;
255
256	// check, if the node is contained by the root dir of the client volume
257	if (volumeManager->DirectoryContains(GetRootDirectory(), node, true))
258		return node;
259
260	return NULL;
261}
262
263// GetNode
264Node*
265ClientVolume::GetNode(NodeID nodeID)
266{
267	return GetNode(nodeID.volumeID, nodeID.nodeID);
268}
269
270// GetNode
271Node*
272ClientVolume::GetNode(const node_ref& nodeRef)
273{
274	return GetNode(nodeRef.device, nodeRef.node);
275}
276
277// GetDirectory
278Directory*
279ClientVolume::GetDirectory(dev_t volumeID, ino_t nodeID)
280{
281	VolumeManager* volumeManager = VolumeManager::GetDefault();
282
283	// get the directory
284	Directory* dir = GetDirectory(volumeID, nodeID);
285	if (!dir)
286		return NULL;
287
288	// check, if the dir is contained by the root dir of the client volume
289	if (volumeManager->DirectoryContains(GetRootDirectory(), dir, true))
290		return dir;
291
292	return NULL;
293}
294
295// GetDirectory
296Directory*
297ClientVolume::GetDirectory(NodeID nodeID)
298{
299	return GetDirectory(nodeID.volumeID, nodeID.nodeID);
300}
301
302// LoadDirectory
303status_t
304ClientVolume::LoadDirectory(dev_t volumeID, ino_t nodeID,
305	Directory** _directory)
306{
307	if (!_directory)
308		return B_BAD_VALUE;
309
310	VolumeManager* volumeManager = VolumeManager::GetDefault();
311
312	// load the directory
313	Directory* dir;
314	status_t error = volumeManager->LoadDirectory(volumeID, nodeID, &dir);
315	if (error != B_OK)
316		return error;
317
318	// check, if the dir is contained by the root dir of the client volume
319	if (!volumeManager->DirectoryContains(GetRootDirectory(), dir, true))
320		return B_ENTRY_NOT_FOUND;
321
322	*_directory = dir;
323	return B_OK;
324}
325
326// GetEntry
327Entry*
328ClientVolume::GetEntry(dev_t volumeID, ino_t dirID, const char* name)
329{
330	VolumeManager* volumeManager = VolumeManager::GetDefault();
331
332	// get the entry
333	Entry* entry = volumeManager->GetEntry(volumeID, dirID, name);
334	if (!entry)
335		return NULL;
336
337	// check, if the entry is contained by the root dir of the client volume
338	if (volumeManager->DirectoryContains(GetRootDirectory(), entry))
339		return entry;
340
341	return NULL;
342}
343
344// GetEntry
345Entry*
346ClientVolume::GetEntry(Directory* directory, const char* name)
347{
348	if (!directory)
349		return NULL;
350
351	return GetEntry(directory->GetVolumeID(), directory->GetID(), name);
352}
353
354// LoadEntry
355status_t
356ClientVolume::LoadEntry(dev_t volumeID, ino_t dirID, const char* name,
357	Entry** _entry)
358{
359	if (!name || !_entry)
360		return B_BAD_VALUE;
361
362	VolumeManager* volumeManager = VolumeManager::GetDefault();
363
364	// get the entry
365	Entry* entry;
366	status_t error = VolumeManager::GetDefault()->LoadEntry(volumeID, dirID,
367		name, true, &entry);
368	if (error != B_OK)
369		return error;
370
371	// check, if the entry is contained by the root dir of the client volume
372	if (!volumeManager->DirectoryContains(GetRootDirectory(), entry))
373		return B_ENTRY_NOT_FOUND;
374
375	*_entry = entry;
376	return B_OK;
377}
378
379// LoadEntry
380status_t
381ClientVolume::LoadEntry(Directory* directory, const char* name, Entry** entry)
382{
383	if (!directory)
384		return B_BAD_VALUE;
385
386	return LoadEntry(directory->GetVolumeID(), directory->GetID(), name, entry);
387}
388
389// Open
390//
391// The caller gets a lock to the returned node handle.
392status_t
393ClientVolume::Open(Node* node, int openMode, FileHandle** _handle)
394{
395	if (!node || !_handle)
396		return B_BAD_VALUE;
397
398	// open the node
399	FileHandle* handle = NULL;
400	status_t error = node->Open(openMode, &handle);
401	if (error != B_OK)
402		return error;
403	BReference<NodeHandle> handleReference(handle, true);
404
405	// lock the handle
406	handle->Lock();
407
408	// add the handle
409	error = fNodeHandles->AddNodeHandle(handle);
410	if (error != B_OK)
411		return error;
412
413	handleReference.Detach();
414	*_handle = handle;
415	return B_OK;
416}
417
418// OpenDir
419//
420// The caller gets a lock to the returned node handle.
421status_t
422ClientVolume::OpenDir(Directory* directory, DirIterator** _iterator)
423{
424	if (!directory || !_iterator)
425		return B_BAD_VALUE;
426
427	// open the directory
428	DirIterator* iterator = NULL;
429	status_t error = directory->OpenDir(&iterator);
430	if (error != B_OK)
431		return error;
432	BReference<NodeHandle> handleReference(iterator, true);
433
434	// lock the handle
435	iterator->Lock();
436
437	// add the handle
438	error = fNodeHandles->AddNodeHandle(iterator);
439	if (error != B_OK)
440		return error;
441
442	handleReference.Detach();
443	*_iterator = iterator;
444	return B_OK;
445}
446
447// OpenAttrDir
448//
449// The caller gets a lock to the returned node handle.
450status_t
451ClientVolume::OpenAttrDir(Node* node, AttrDirIterator** _iterator)
452{
453	if (!node || !_iterator)
454		return B_BAD_VALUE;
455
456	// open the attribut directory
457	AttrDirIterator* iterator = NULL;
458	status_t error = node->OpenAttrDir(&iterator);
459	if (error != B_OK)
460		return error;
461	BReference<NodeHandle> handleReference(iterator, true);
462
463	// lock the handle
464	iterator->Lock();
465
466	// add the handle
467	error = fNodeHandles->AddNodeHandle(iterator);
468	if (error != B_OK)
469		return error;
470
471	handleReference.Detach();
472	*_iterator = iterator;
473	return B_OK;
474}
475
476// Close
477//
478// VolumeManager MUST be locked. After closing the handle must still be
479// unlocked. When the last reference is surrendered it will finally be deleted.
480status_t
481ClientVolume::Close(NodeHandle* handle)
482{
483	if (!handle || !fNodeHandles->RemoveNodeHandle(handle))
484		return B_BAD_VALUE;
485
486	return B_OK;
487}
488
489// LockNodeHandle
490//
491// VolumeManager must NOT be locked.
492status_t
493ClientVolume::LockNodeHandle(int32 cookie, NodeHandle** _handle)
494{
495	return fNodeHandles->LockNodeHandle(cookie, _handle);
496}
497
498// UnlockNodeHandle
499//
500// VolumeManager may or may not be locked.
501void
502ClientVolume::UnlockNodeHandle(NodeHandle* nodeHandle)
503{
504	fNodeHandles->UnlockNodeHandle(nodeHandle);
505}
506
507// ProcessNodeMonitoringEvent
508void
509ClientVolume::ProcessNodeMonitoringEvent(NodeMonitoringEvent* event)
510{
511	if (fNodeMonitoringProcessor)
512		fNodeMonitoringProcessor->ProcessNodeMonitoringEvent(fID, event);
513}
514
515// _NextVolumeID
516int32
517ClientVolume::_NextVolumeID()
518{
519	return atomic_add(&sNextVolumeID, 1);
520}
521
522// sNextVolumeID
523int32 ClientVolume::sNextVolumeID = 0;
524
525
526// #pragma -
527
528// destructor
529ClientVolume::NodeMonitoringProcessor::~NodeMonitoringProcessor()
530{
531}
532
533