1// VolumeManager.cpp
2
3#include <HashMap.h>
4#include <HashSet.h>
5#include <util/DoublyLinkedList.h>
6
7#include "DebugSupport.h"
8#include "QueryManager.h"
9#include "RootVolume.h"
10#include "VolumeEvent.h"
11#include "VolumeManager.h"
12
13// VolumeSet
14struct VolumeManager::VolumeSet : HashSet<HashKeyPointer<Volume*> > {
15};
16
17// NodeIDVolumeMap
18struct VolumeManager::NodeIDVolumeMap : HashMap<HashKey64<vnode_id>, Volume*> {
19};
20
21// VolumeEventQueue
22class VolumeManager::VolumeEventQueue {
23public:
24	VolumeEventQueue()
25		: fLock("volume event queue"),
26		  fCounterSem(-1)
27	{
28		fCounterSem = create_sem(0, "volume event count");
29		#if !USER
30			if (fCounterSem >= 0)
31				set_sem_owner(fCounterSem, B_SYSTEM_TEAM);
32		#endif
33	}
34
35	~VolumeEventQueue()
36	{
37		Close();
38	}
39
40	status_t InitCheck() const
41	{
42		if (fCounterSem < 0)
43			return fCounterSem;
44		return B_OK;
45	}
46
47	void Close()
48	{
49		AutoLocker<Locker> _(fLock);
50		if (fCounterSem >= 0) {
51			delete_sem(fCounterSem);
52			fCounterSem = -1;
53		}
54
55		while (VolumeEvent* event = fEvents.First()) {
56			fEvents.Remove(event);
57			event->ReleaseReference();
58		}
59	}
60
61	void Push(VolumeEvent* event)
62	{
63		if (!event)
64			return;
65
66		AutoLocker<Locker> _(fLock);
67		if (fCounterSem < 0)
68			return;
69		fEvents.Insert(event);
70		event->AcquireReference();
71		release_sem(fCounterSem);
72	}
73
74	VolumeEvent* Pop()
75	{
76		status_t error;
77		do {
78			error = acquire_sem(fCounterSem);
79		} while (error == B_INTERRUPTED);
80		if (error != B_OK)
81			return NULL;
82
83		AutoLocker<Locker> _(fLock);
84		if (VolumeEvent* event = fEvents.First()) {
85			fEvents.Remove(event);
86			return event;
87		}
88
89		return NULL;
90	}
91
92private:
93	Locker				fLock;
94	sem_id				fCounterSem;
95	DoublyLinkedList<VolumeEvent> fEvents;
96};
97
98
99// constructor
100VolumeManager::VolumeManager(nspace_id id, uint32 flags)
101	: Locker("volume manager"),
102	  fID(id),
103	  fMountFlags(flags),
104	  fMountUID(0),
105	  fMountGID(0),
106	  fRootID(-1),
107	  fNextNodeID(2),
108	  fQueryManager(NULL),
109	  fVolumes(NULL),
110	  fNodeIDs2Volumes(NULL),
111	  fVolumeEvents(NULL),
112	  fEventDeliverer(-1)
113{
114}
115
116// destructor
117VolumeManager::~VolumeManager()
118{
119	if (fVolumeEvents)
120		fVolumeEvents->Close();
121	if (fEventDeliverer >= 0) {
122		int32 result;
123		wait_for_thread(fEventDeliverer, &result);
124	}
125	delete fVolumeEvents;
126	delete fVolumes;
127	delete fNodeIDs2Volumes;
128	delete fQueryManager;
129}
130
131// MountRootVolume
132status_t
133VolumeManager::MountRootVolume(const char* device,
134	const char* parameters, int32 len, Volume** volume)
135{
136	// Store the uid/gid of the mounting user -- when running in userland
137	// this is always the owner of the UserlandFS server, but we can't help
138	// that.
139	fMountUID = geteuid();
140	fMountGID = getegid();
141
142	// create the query manager
143	fQueryManager = new(std::nothrow) QueryManager(this);
144	if (!fQueryManager)
145		return B_NO_MEMORY;
146	status_t error = fQueryManager->Init();
147	if (error != B_OK)
148		return error;
149
150	// create volumes set
151	fVolumes = new(std::nothrow) VolumeSet;
152	if (!fVolumes)
153		return B_NO_MEMORY;
154	error = fVolumes->InitCheck();
155	if (error != B_OK)
156		return error;
157
158	// create node ID to volumes map
159	fNodeIDs2Volumes = new(std::nothrow) NodeIDVolumeMap;
160	if (!fNodeIDs2Volumes)
161		return B_NO_MEMORY;
162	error = fNodeIDs2Volumes->InitCheck();
163	if (error != B_OK)
164		return error;
165
166	// create the volume event queue
167	fVolumeEvents = new VolumeEventQueue;
168	if (!fVolumeEvents)
169		return B_NO_MEMORY;
170	error = fVolumeEvents->InitCheck();
171	if (error != B_OK)
172		return error;
173
174	// spawn the event deliverer
175	#if USER
176		fEventDeliverer = spawn_thread(&_EventDelivererEntry,
177			"volume event deliverer", B_NORMAL_PRIORITY, this);
178	#else
179		fEventDeliverer = spawn_kernel_thread(&_EventDelivererEntry,
180			"volume event deliverer", B_NORMAL_PRIORITY, this);
181	#endif
182	if (fEventDeliverer < 0)
183		return fEventDeliverer;
184
185	// create the root volume
186	RootVolume* rootVolume = new(std::nothrow) RootVolume(this);
187	if (!rootVolume)
188		return B_NO_MEMORY;
189	error = rootVolume->Init();
190	if (error != B_OK) {
191		delete rootVolume;
192		return error;
193	}
194	fRootID = rootVolume->GetRootID();
195
196	// add the root volume
197	error = AddVolume(rootVolume);
198	if (error != B_OK) {
199		delete rootVolume;
200		return error;
201	}
202
203	// mount the root volume
204	error = rootVolume->Mount(device, fMountFlags, (const char*)parameters,
205		len);
206	if (error != B_OK) {
207		rootVolume->SetUnmounting(true);
208		PutVolume(rootVolume);
209		return error;
210	}
211	rootVolume->AcquireReference();
212	*volume = rootVolume;
213
214	// run the event deliverer
215	resume_thread(fEventDeliverer);
216
217	return B_OK;
218}
219
220// UnmountRootVolume
221void
222VolumeManager::UnmountRootVolume()
223{
224	if (Volume* rootVolume = GetRootVolume()) {
225		rootVolume->SetUnmounting(true);
226		PutVolume(rootVolume);
227	} else {
228		ERROR(("VolumeManager::UnmountRootVolume(): ERROR: Couldn't get "
229			"root volume!\n"));
230	}
231}
232
233// GetQueryManager
234QueryManager*
235VolumeManager::GetQueryManager() const
236{
237	return fQueryManager;
238}
239
240// GetRootVolume
241Volume*
242VolumeManager::GetRootVolume()
243{
244	return GetVolume(fRootID);
245}
246
247// AddVolume
248//
249// The caller must have a reference to the volume and retains it.
250status_t
251VolumeManager::AddVolume(Volume* volume)
252{
253	if (!volume)
254		return B_BAD_VALUE;
255
256	// check, if it already exists
257	AutoLocker<Locker> _(this);
258	if (fVolumes->Contains(volume))
259		return B_BAD_VALUE;
260
261	// add the volume
262	return fVolumes->Add(volume);
263}
264
265// GetVolume
266Volume*
267VolumeManager::GetVolume(vnode_id nodeID)
268{
269	AutoLocker<Locker> _(this);
270	Volume* volume = fNodeIDs2Volumes->Get(nodeID);
271	if (volume && GetVolume(volume))
272		return volume;
273	return NULL;
274}
275
276// GetVolume
277Volume*
278VolumeManager::GetVolume(Volume* volume)
279{
280	if (!volume)
281		return NULL;
282
283	AutoLocker<Locker> _(this);
284	if (fVolumes->Contains(volume)) {
285// TODO: Any restrictions regarding volumes about to be removed?
286		volume->AcquireReference();
287		return volume;
288	}
289	return NULL;
290}
291
292// PutVolume
293//
294// The VolumeManager must not be locked, when this method is invoked.
295void
296VolumeManager::PutVolume(Volume* volume)
297{
298	if (!volume)
299		return;
300
301	// If the volume is marked unmounting and is not yet marked removed, we
302	// initiate the removal process.
303	{
304		AutoLocker<Locker> locker(this);
305//PRINT(("VolumeManager::PutVolume(%p): reference count before: %ld\n",
306//volume, volume->CountReferences()));
307		if (volume->IsUnmounting() && !volume->IsRemoved()) {
308//PRINT(("VolumeManager::PutVolume(%p): Volume connection broken, marking "
309//"removed and removing all nodes.\n", volume));
310			// mark removed
311			volume->MarkRemoved();
312
313			// get parent volume
314			Volume* parentVolume = volume->GetParentVolume();
315			if (parentVolume && !GetVolume(parentVolume))
316				parentVolume = NULL;
317
318			locker.Unlock();
319
320			// prepare to unmount
321			volume->PrepareToUnmount();
322
323			// remove from parent volume
324			if (parentVolume) {
325				parentVolume->RemoveChildVolume(volume);
326				PutVolume(parentVolume);
327			}
328		}
329	}
330
331	// If the volume is marked removed and it's reference count drops to 0,
332	// we unmount and delete it.
333	{
334		AutoLocker<Locker> locker(this);
335		if (volume->ReleaseReference() && volume->IsRemoved()) {
336			PRINT("VolumeManager::PutVolume(%p): Removed volume "
337				"unreferenced. Unmounting...\n", volume);
338			// remove from volume set -- now noone can get a reference to it
339			// anymore
340			fVolumes->Remove(volume);
341
342			locker.Unlock();
343
344			// unmount and delete the volume
345// TODO: At some point all the volume's node IDs have to be removed from
346// fNodeIDs2Volumes. For the time being we expect the volume to do that itself
347// in Unmount().
348			volume->Unmount();
349			delete volume;
350		}
351	}
352}
353
354// NewNodeID
355vnode_id
356VolumeManager::NewNodeID(Volume* volume)
357{
358	if (!volume)
359		return B_BAD_VALUE;
360
361	AutoLocker<Locker> _(this);
362	vnode_id nodeID = fNextNodeID;
363	status_t error = fNodeIDs2Volumes->Put(nodeID, volume);
364	if (error != B_OK)
365		return error;
366	return fNextNodeID++;
367}
368
369// RemoveNodeID
370void
371VolumeManager::RemoveNodeID(vnode_id nodeID)
372{
373	AutoLocker<Locker> _(this);
374	fNodeIDs2Volumes->Remove(nodeID);
375}
376
377// SendVolumeEvent
378void
379VolumeManager::SendVolumeEvent(VolumeEvent* event)
380{
381	if (!event)
382		return;
383
384	fVolumeEvents->Push(event);
385}
386
387// _EventDelivererEntry
388int32
389VolumeManager::_EventDelivererEntry(void* data)
390{
391	return ((VolumeManager*)data)->_EventDeliverer();
392}
393
394// _EventDeliverer
395int32
396VolumeManager::_EventDeliverer()
397{
398	while (VolumeEvent* event = fVolumeEvents->Pop()) {
399		if (Volume* volume = GetVolume(event->GetTarget())) {
400			volume->HandleEvent(event);
401			PutVolume(volume);
402		}
403		event->ReleaseReference();
404	}
405	return 0;
406}
407
408