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