xref: /haiku/src/add-ons/kernel/file_systems/netfs/client/VolumeManager.cpp (revision 837b16251d4b2b6249ebcaa19bb319cbe82c6126)
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 struct VolumeManager::VolumeSet : HashSet<HashKey32<Volume*> > {
15 };
16 
17 // NodeIDVolumeMap
18 struct VolumeManager::NodeIDVolumeMap : HashMap<HashKey64<vnode_id>, Volume*> {
19 };
20 
21 // VolumeEventQueue
22 class VolumeManager::VolumeEventQueue {
23 public:
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 
92 private:
93 	Locker				fLock;
94 	sem_id				fCounterSem;
95 	DoublyLinkedList<VolumeEvent> fEvents;
96 };
97 
98 
99 // constructor
100 VolumeManager::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
117 VolumeManager::~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
132 status_t
133 VolumeManager::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
221 void
222 VolumeManager::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
234 QueryManager*
235 VolumeManager::GetQueryManager() const
236 {
237 	return fQueryManager;
238 }
239 
240 // GetRootVolume
241 Volume*
242 VolumeManager::GetRootVolume()
243 {
244 	return GetVolume(fRootID);
245 }
246 
247 // AddVolume
248 //
249 // The caller must have a reference to the volume and retains it.
250 status_t
251 VolumeManager::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
266 Volume*
267 VolumeManager::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
277 Volume*
278 VolumeManager::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.
295 void
296 VolumeManager::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 unreferenced. "
337 "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
355 vnode_id
356 VolumeManager::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
370 void
371 VolumeManager::RemoveNodeID(vnode_id nodeID)
372 {
373 	AutoLocker<Locker> _(this);
374 	fNodeIDs2Volumes->Remove(nodeID);
375 }
376 
377 // SendVolumeEvent
378 void
379 VolumeManager::SendVolumeEvent(VolumeEvent* event)
380 {
381 	if (!event)
382 		return;
383 
384 	fVolumeEvents->Push(event);
385 }
386 
387 // _EventDelivererEntry
388 int32
389 VolumeManager::_EventDelivererEntry(void* data)
390 {
391 	return ((VolumeManager*)data)->_EventDeliverer();
392 }
393 
394 // _EventDeliverer
395 int32
396 VolumeManager::_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