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