1/*
2 * Copyright 2001-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "Volume.h"
8
9#include <util/AutoLock.h>
10
11#include <thread.h>
12#include <fs/node_monitor.h>
13#include <Notifications.h>
14
15#include "AutoLocker.h"
16#include "Compatibility.h"
17#include "Debug.h"
18#include "FileSystem.h"
19#include "HashMap.h"
20#include "kernel_interface.h"
21#include "KernelRequestHandler.h"
22#include "PortReleaser.h"
23#include "RequestAllocator.h"
24#include "Requests.h"
25#include "Settings.h"
26#include "SingleReplyRequestHandler.h"
27
28
29// The time after which the notification thread times out at the port and
30// restarts the loop. Of interest only when the FS is deleted. It is the
31// maximal time the destructor has to wait for the thread.
32static const bigtime_t kNotificationRequestTimeout = 50000;	// 50 ms
33
34
35// #pragma mark - SelectSyncMap
36
37
38struct FileSystem::SelectSyncMap
39	: public SynchronizedHashMap<HashKeyPointer<selectsync*>, int32*, Locker> {
40};
41
42
43// #pragma mark - NodeListenerKey
44
45
46struct FileSystem::NodeListenerKey {
47	NodeListenerKey(void* clientListener, dev_t device, ino_t node)
48		:
49		fClientListener(clientListener),
50		fDevice(device),
51		fNode(node)
52	{
53	}
54
55	void* ClientListener() const
56	{
57		return fClientListener;
58	}
59
60	dev_t Device() const
61	{
62		return fDevice;
63	}
64
65	ino_t Node() const
66	{
67		return fNode;
68	}
69
70	uint32 HashValue() const
71	{
72		return (uint32)(addr_t)fClientListener ^ (uint32)fDevice
73			^ (uint32)fNode ^ (uint32)(fNode >> 32);
74	}
75
76	bool operator==(const NodeListenerKey& other) const
77	{
78		return fClientListener == other.fClientListener
79			&& fDevice == other.fDevice && fNode == other.fNode;
80	}
81
82protected:
83	void*	fClientListener;
84	dev_t	fDevice;
85	ino_t	fNode;
86};
87
88
89// #pragma mark - NodeListenerProxy
90
91
92struct FileSystem::NodeListenerProxy : NodeListenerKey, NotificationListener {
93	NodeListenerProxy(FileSystem* fileSystem, void* clientListener,
94		dev_t device, ino_t node)
95		:
96		NodeListenerKey(clientListener, device, node),
97		fFileSystem(fileSystem)
98	{
99	}
100
101	virtual void EventOccurred(NotificationService& service,
102		const KMessage* event)
103	{
104		fFileSystem->_NodeListenerEventOccurred(this, event);
105	}
106
107	NodeListenerProxy*& HashTableLink()
108	{
109		return fHashTableLink;
110	}
111
112	status_t StartListening(uint32 flags)
113	{
114		return add_node_listener(fDevice, fNode, flags, *this);
115	}
116
117	status_t StopListening()
118	{
119		return remove_node_listener(fDevice, fNode, *this);
120	}
121
122private:
123	FileSystem*			fFileSystem;
124	NodeListenerProxy*	fHashTableLink;
125};
126
127
128// #pragma mark - NodeListenerHashDefinition
129
130
131struct FileSystem::NodeListenerHashDefinition {
132	typedef NodeListenerKey		KeyType;
133	typedef	NodeListenerProxy	ValueType;
134
135	size_t HashKey(const NodeListenerKey& key) const
136	{
137		return key.HashValue();
138	}
139
140	size_t Hash(const NodeListenerProxy* value) const
141	{
142		return value->HashValue();
143	}
144
145	bool Compare(const NodeListenerKey& key,
146		const NodeListenerProxy* value) const
147	{
148		return key == *value;
149	}
150
151	NodeListenerProxy*& GetLink(NodeListenerProxy* value) const
152	{
153		return value->HashTableLink();
154	}
155};
156
157
158// #pragma mark - FileSystem
159
160
161// constructor
162FileSystem::FileSystem()
163	:
164	fVolumes(),
165	fName(),
166	fTeam(-1),
167	fNotificationPort(NULL),
168	fNotificationThread(-1),
169	fPortPool(),
170	fSelectSyncs(NULL),
171	fSettings(NULL),
172	fUserlandServerTeam(-1),
173	fInitialized(false),
174	fTerminating(false)
175{
176	mutex_init(&fVolumeLock, "userlandfs volumes");
177	mutex_init(&fVNodeOpsLock, "userlandfs vnode ops");
178	mutex_init(&fNodeListenersLock, "userlandfs node listeners");
179}
180
181// destructor
182FileSystem::~FileSystem()
183{
184	fTerminating = true;
185
186	// wait for the notification thread to terminate
187	if (fNotificationThread >= 0) {
188		int32 result;
189		wait_for_thread(fNotificationThread, &result);
190	}
191
192	// delete our data structures
193	if (fNodeListeners != NULL) {
194		MutexLocker nodeListenersLocker(fNodeListenersLock);
195		NodeListenerProxy* proxy = fNodeListeners->Clear(true);
196		while (proxy != NULL) {
197			NodeListenerProxy* next = proxy->HashTableLink();
198			proxy->StopListening();
199			delete proxy;
200			proxy = next;
201		}
202	}
203
204	if (fSelectSyncs) {
205		for (SelectSyncMap::Iterator it = fSelectSyncs->GetIterator();
206				it.HasNext();) {
207			SelectSyncMap::Entry entry = it.Next();
208			delete entry.value;
209		}
210		delete fSelectSyncs;
211	}
212
213	delete fSettings;
214
215	// delete vnode ops vectors -- there shouldn't be any left, though
216	VNodeOps* ops = fVNodeOps.Clear();
217	int32 count = 0;
218	while (ops != NULL) {
219		count++;
220		VNodeOps* next = ops->hash_link;
221		free(ops);
222		ops = next;
223	}
224	if (count > 0)
225		WARN(("Deleted %" B_PRId32 " vnode ops vectors!\n", count));
226
227
228	mutex_destroy(&fVolumeLock);
229	mutex_destroy(&fVNodeOpsLock);
230	mutex_destroy(&fNodeListenersLock);
231}
232
233// Init
234status_t
235FileSystem::Init(const char* name, team_id team, Port::Info* infos, int32 count,
236	const FSCapabilities& capabilities)
237{
238	PRINT(("FileSystem::Init(\"%s\", %p, %" B_PRId32 ")\n", name, infos,
239		count));
240	capabilities.Dump();
241
242	// check parameters
243	if (!name || !infos || count < 2)
244		RETURN_ERROR(B_BAD_VALUE);
245
246	// set the name
247	if (!fName.SetTo(name))
248		return B_NO_MEMORY;
249
250	// init VNodeOps map
251	status_t error = fVNodeOps.Init();
252	if (error != B_OK)
253		return error;
254
255	fTeam = team;
256	fCapabilities = capabilities;
257
258	// create the select sync entry map
259	fSelectSyncs = new(nothrow) SelectSyncMap;
260	if (!fSelectSyncs)
261		return B_NO_MEMORY;
262
263	// create the node listener proxy map
264	fNodeListeners = new(std::nothrow) NodeListenerMap;
265	if (fNodeListeners == NULL || fNodeListeners->Init() != B_OK)
266		return B_NO_MEMORY;
267
268	// create the request ports
269	// the notification port
270	fNotificationPort = new(nothrow) RequestPort(infos);
271	if (!fNotificationPort)
272		RETURN_ERROR(B_NO_MEMORY);
273	error = fNotificationPort->InitCheck();
274	if (error != B_OK)
275		return error;
276
277	// the other request ports
278	for (int32 i = 1; i < count; i++) {
279		RequestPort* port = new(nothrow) RequestPort(infos + i);
280		if (!port)
281			RETURN_ERROR(B_NO_MEMORY);
282		error = port->InitCheck();
283		if (error == B_OK)
284			error = fPortPool.AddPort(port);
285		if (error != B_OK) {
286			delete port;
287			RETURN_ERROR(error);
288		}
289	}
290
291	// get the userland team
292	port_info portInfo;
293	error = get_port_info(infos[0].owner_port, &portInfo);
294	if (error != B_OK)
295		RETURN_ERROR(error);
296	fUserlandServerTeam = portInfo.team;
297
298	// print some info about the userland team
299	D(
300		PRINT(("  userland team is: %" B_PRId32 "\n", fUserlandServerTeam));
301		int32 cookie = 0;
302		thread_info threadInfo;
303		while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo)
304			   == B_OK) {
305			PRINT(("    userland thread: %" B_PRId32 ": `%s'\n",
306				threadInfo.thread, threadInfo.name));
307		}
308	);
309
310	// load the settings
311	fSettings = new(nothrow) Settings;
312	if (fSettings) {
313		status_t settingsError = fSettings->SetTo(fName.GetString());
314		if (settingsError != B_OK) {
315			PRINT(("Failed to load settings: %s\n", strerror(settingsError)));
316			delete fSettings;
317			fSettings = NULL;
318		} else
319			fSettings->Dump();
320	} else
321		ERROR(("Failed to allocate settings.\n"));
322
323	// spawn the notification thread
324	#if USER
325		fNotificationThread = spawn_thread(_NotificationThreadEntry,
326			"UFS notification thread", B_NORMAL_PRIORITY, this);
327	#else
328		fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry,
329			"UFS notification thread", B_NORMAL_PRIORITY, this);
330	#endif
331	if (fNotificationThread < 0)
332		RETURN_ERROR(fNotificationThread);
333	resume_thread(fNotificationThread);
334
335	fInitialized = (error == B_OK);
336	RETURN_ERROR(error);
337}
338
339// GetName
340const char*
341FileSystem::GetName() const
342{
343	return fName.GetString();
344}
345
346// GetCapabilities
347const FSCapabilities&
348FileSystem::GetCapabilities() const
349{
350	return fCapabilities;
351}
352
353// GetPortPool
354RequestPortPool*
355FileSystem::GetPortPool()
356{
357	return &fPortPool;
358}
359
360// Mount
361status_t
362FileSystem::Mount(fs_volume* fsVolume, const char* device, uint32 flags,
363	const char* parameters, Volume** _volume)
364{
365	// check initialization and parameters
366	if (!fInitialized || !_volume)
367		return B_BAD_VALUE;
368
369	// create volume
370	Volume* volume = new(nothrow) Volume(this, fsVolume);
371	if (!volume)
372		return B_NO_MEMORY;
373
374	// add volume to the volume list
375	MutexLocker locker(fVolumeLock);
376	status_t error = fVolumes.PushBack(volume);
377	locker.Unlock();
378	if (error != B_OK)
379		return error;
380
381	// mount volume
382	error = volume->Mount(device, flags, parameters);
383	if (error != B_OK) {
384		MutexLocker locker(fVolumeLock);
385		fVolumes.Remove(volume);
386		locker.Unlock();
387		volume->ReleaseReference();
388		return error;
389	}
390
391	*_volume = volume;
392	return error;
393}
394
395// Initialize
396/*status_t
397FileSystem::Initialize(const char* deviceName, const char* parameters,
398	size_t len)
399{
400	// get a free port
401	RequestPort* port = fPortPool.AcquirePort();
402	if (!port)
403		return B_ERROR;
404	PortReleaser _(&fPortPool, port);
405	// prepare the request
406	RequestAllocator allocator(port->GetPort());
407	MountVolumeRequest* request;
408	status_t error = AllocateRequest(allocator, &request);
409	if (error != B_OK)
410		return error;
411	error = allocator.AllocateString(request->device, deviceName);
412	if (error == B_OK)
413		error = allocator.AllocateData(request->parameters, parameters, len, 1);
414	if (error != B_OK)
415		return error;
416	// send the request
417	SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY);
418	InitializeVolumeReply* reply;
419	error = port->SendRequest(&allocator, &handler, (Request**)&reply);
420	if (error != B_OK)
421		return error;
422	RequestReleaser requestReleaser(port, reply);
423	// process the reply
424	if (reply->error != B_OK)
425		return reply->error;
426	return error;
427}*/
428
429// VolumeUnmounted
430void
431FileSystem::VolumeUnmounted(Volume* volume)
432{
433	MutexLocker locker(fVolumeLock);
434	fVolumes.Remove(volume);
435}
436
437// GetVolume
438Volume*
439FileSystem::GetVolume(dev_t id)
440{
441	MutexLocker _(fVolumeLock);
442	for (Vector<Volume*>::Iterator it = fVolumes.Begin();
443		 it != fVolumes.End();
444		 it++) {
445		 Volume* volume = *it;
446		 if (volume->GetID() == id) {
447		 	volume->AcquireReference();
448		 	return volume;
449		 }
450	}
451	return NULL;
452}
453
454// GetIOCtlInfo
455const IOCtlInfo*
456FileSystem::GetIOCtlInfo(int command) const
457{
458	return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL);
459}
460
461// AddSelectSyncEntry
462status_t
463FileSystem::AddSelectSyncEntry(selectsync* sync)
464{
465	AutoLocker<SelectSyncMap> _(fSelectSyncs);
466	int32* count = fSelectSyncs->Get(sync);
467	if (!count) {
468		count = new(nothrow) int32(0);
469		if (!count)
470			return B_NO_MEMORY;
471		status_t error = fSelectSyncs->Put(sync, count);
472		if (error != B_OK) {
473			delete count;
474			return error;
475		}
476	}
477	(*count)++;
478	return B_OK;
479}
480
481// RemoveSelectSyncEntry
482void
483FileSystem::RemoveSelectSyncEntry(selectsync* sync)
484{
485	AutoLocker<SelectSyncMap> _(fSelectSyncs);
486	if (int32* count = fSelectSyncs->Get(sync)) {
487		if (--(*count) <= 0) {
488			fSelectSyncs->Remove(sync);
489			delete count;
490		}
491	}
492}
493
494
495// KnowsSelectSyncEntry
496bool
497FileSystem::KnowsSelectSyncEntry(selectsync* sync)
498{
499	return fSelectSyncs->ContainsKey(sync);
500}
501
502
503// AddNodeListener
504status_t
505FileSystem::AddNodeListener(dev_t device, ino_t node, uint32 flags,
506	void* listener)
507{
508	MutexLocker nodeListenersLocker(fNodeListenersLock);
509
510	// lookup the proxy
511	NodeListenerProxy* proxy = fNodeListeners->Lookup(
512		NodeListenerKey(listener, device, node));
513	if (proxy != NULL)
514		return proxy->StartListening(flags);
515
516	// it doesn't exist yet -- create it
517	proxy = new(std::nothrow) NodeListenerProxy(this, listener, device, node);
518	if (proxy == NULL)
519		return B_NO_MEMORY;
520
521	// start listening
522	status_t error = proxy->StartListening(flags);
523	if (error != B_OK) {
524		delete proxy;
525		return error;
526	}
527
528	fNodeListeners->Insert(proxy);
529	return B_OK;
530}
531
532
533// RemoveNodeListener
534status_t
535FileSystem::RemoveNodeListener(dev_t device, ino_t node, void* listener)
536{
537	MutexLocker nodeListenersLocker(fNodeListenersLock);
538
539	// lookup the proxy
540	NodeListenerProxy* proxy = fNodeListeners->Lookup(
541		NodeListenerKey(listener, device, node));
542	if (proxy == NULL)
543		return B_BAD_VALUE;
544
545	status_t error = proxy->StopListening();
546
547	fNodeListeners->Remove(proxy);
548	delete proxy;
549
550	return error;
551}
552
553
554// GetVNodeOps
555VNodeOps*
556FileSystem::GetVNodeOps(const FSVNodeCapabilities& capabilities)
557{
558	MutexLocker locker(fVNodeOpsLock);
559
560	// do we already have ops for those capabilities
561	VNodeOps* ops = fVNodeOps.Lookup(capabilities);
562	if (ops != NULL) {
563		ops->refCount++;
564		return ops;
565	}
566
567	// no, create a new object
568	fs_vnode_ops* opsVector = new(std::nothrow) fs_vnode_ops;
569	if (opsVector == NULL)
570		return NULL;
571
572	// set the operations
573	_InitVNodeOpsVector(opsVector, capabilities);
574
575	// create the VNodeOps object
576	ops = new(std::nothrow) VNodeOps(capabilities, opsVector);
577	if (ops == NULL) {
578		delete opsVector;
579		return NULL;
580	}
581
582	fVNodeOps.Insert(ops);
583
584	return ops;
585}
586
587
588// PutVNodeOps
589void
590FileSystem::PutVNodeOps(VNodeOps* ops)
591{
592	MutexLocker locker(fVNodeOpsLock);
593
594	if (--ops->refCount == 0) {
595		fVNodeOps.Remove(ops);
596		delete ops;
597	}
598}
599
600
601// IsUserlandServerThread
602bool
603FileSystem::IsUserlandServerThread() const
604{
605	thread_info info;
606	get_thread_info(find_thread(NULL), &info);
607	return (info.team == fUserlandServerTeam);
608}
609
610
611// _InitVNodeOpsVector
612void
613FileSystem::_InitVNodeOpsVector(fs_vnode_ops* ops,
614	const FSVNodeCapabilities& capabilities)
615{
616	memcpy(ops, &gUserlandFSVnodeOps, sizeof(fs_vnode_ops));
617
618	#undef CLEAR_UNSUPPORTED
619	#define CLEAR_UNSUPPORTED(capability, op) 	\
620		if (!capabilities.Get(capability))				\
621			ops->op = NULL
622
623	// vnode operations
624	// FS_VNODE_CAPABILITY_LOOKUP: lookup
625	// FS_VNODE_CAPABILITY_GET_VNODE_NAME: get_vnode_name
626		// emulated in userland
627	// FS_VNODE_CAPABILITY_PUT_VNODE: put_vnode
628	// FS_VNODE_CAPABILITY_REMOVE_VNODE: remove_vnode
629		// needed by Volume to clean up
630
631	// asynchronous I/O
632	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_IO, io);
633	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CANCEL_IO, cancel_io);
634
635	// cache file access
636	ops->get_file_map = NULL;	// never used
637
638	// common operations
639	// FS_VNODE_CAPABILITY_IOCTL: ioctl
640		// needed by Volume
641	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SET_FLAGS, set_flags);
642	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SELECT, select);
643	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_DESELECT, deselect);
644	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_FSYNC, fsync);
645
646	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_SYMLINK, read_symlink);
647	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SYMLINK, create_symlink);
648
649	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_LINK, link);
650	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_UNLINK, unlink);
651	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME, rename);
652
653	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_ACCESS, access);
654	// FS_VNODE_CAPABILITY_READ_STAT: read_stat
655		// needed by Volume
656	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_STAT, write_stat);
657
658	// file operations
659	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE, create);
660	// FS_VNODE_CAPABILITY_OPEN: open
661		// mandatory
662	// FS_VNODE_CAPABILITY_CLOSE: close
663		// needed by Volume
664	// FS_VNODE_CAPABILITY_FREE_COOKIE: free_cookie
665		// needed by Volume
666	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ, read);
667	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE, write);
668
669	// directory operations
670	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_DIR, create_dir);
671	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_DIR, remove_dir);
672	// FS_VNODE_CAPABILITY_OPEN_DIR: open_dir
673		// mandatory
674	// FS_VNODE_CAPABILITY_CLOSE_DIR: close_dir
675		// needed by Volume
676	// FS_VNODE_CAPABILITY_FREE_DIR_COOKIE: free_dir_cookie
677		// needed by Volume
678	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_DIR, read_dir);
679	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_DIR, rewind_dir);
680
681	// attribute directory operations
682	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, open_attr_dir);
683	// FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR: close_attr_dir
684		// needed by Volume
685	// FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE: free_attr_dir_cookie
686		// needed by Volume
687	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_DIR, read_attr_dir);
688	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, rewind_attr_dir);
689
690	// attribute operations
691	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_ATTR, create_attr);
692	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR, open_attr);
693	// FS_VNODE_CAPABILITY_CLOSE_ATTR: close_attr
694		// needed by Volume
695	// FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE: free_attr_cookie
696		// needed by Volume
697	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR, read_attr);
698	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR, write_attr);
699
700	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_STAT, read_attr_stat);
701	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR_STAT, write_attr_stat);
702	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME_ATTR, rename_attr);
703	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_ATTR, remove_attr);
704
705	// support for node and FS layers
706	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SPECIAL_NODE,
707		create_special_node);
708	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_GET_SUPER_VNODE, get_super_vnode);
709
710	#undef CLEAR_UNSUPPORTED
711}
712
713
714// _NodeListenerEventOccurred
715void
716FileSystem::_NodeListenerEventOccurred(NodeListenerProxy* proxy,
717	const KMessage* event)
718{
719	// get a free port
720	RequestPort* port = fPortPool.AcquirePort();
721	if (port == NULL)
722		return;
723	PortReleaser _(&fPortPool, port);
724
725	// prepare the request
726	RequestAllocator allocator(port->GetPort());
727	NodeMonitoringEventRequest* request;
728	status_t error = AllocateRequest(allocator, &request);
729	if (error != B_OK)
730		return;
731
732	error = allocator.AllocateData(request->event, event->Buffer(),
733		event->ContentSize(), 1);
734	if (error != B_OK)
735		return;
736
737	Thread* thread = thread_get_current_thread();
738	request->team = thread->team->id;
739	request->thread = thread->id;
740	request->user = geteuid();
741	request->group = getegid();
742	request->listener = proxy->ClientListener();
743
744	// send the request
745	KernelRequestHandler handler(this, NODE_MONITORING_EVENT_REPLY);
746	port->SendRequest(&allocator, &handler);
747}
748
749
750// _NotificationThreadEntry
751int32
752FileSystem::_NotificationThreadEntry(void* data)
753{
754	return ((FileSystem*)data)->_NotificationThread();
755}
756
757
758// _NotificationThread
759int32
760FileSystem::_NotificationThread()
761{
762	// process the notification requests until the FS is deleted
763	while (!fTerminating) {
764		if (fNotificationPort->InitCheck() != B_OK)
765			return fNotificationPort->InitCheck();
766		KernelRequestHandler handler(this, NO_REQUEST);
767		fNotificationPort->HandleRequests(&handler, NULL,
768			kNotificationRequestTimeout);
769	}
770	// We eat all remaining notification requests, so that they aren't
771	// presented to the file system, when it is mounted next time.
772	// TODO: We should probably use a special handler that sends an ack reply,
773	// but ignores the requests otherwise.
774	KernelRequestHandler handler(this, NO_REQUEST);
775	fNotificationPort->HandleRequests(&handler, NULL, 0);
776	return 0;
777}
778
779