xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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.
32 static const bigtime_t kNotificationRequestTimeout = 50000;	// 50 ms
33 
34 
35 // #pragma mark - SelectSyncMap
36 
37 
38 struct FileSystem::SelectSyncMap
39 	: public SynchronizedHashMap<HashKeyPointer<selectsync*>, int32*, Locker> {
40 };
41 
42 
43 // #pragma mark - NodeListenerKey
44 
45 
46 struct 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 
82 protected:
83 	void*	fClientListener;
84 	dev_t	fDevice;
85 	ino_t	fNode;
86 };
87 
88 
89 // #pragma mark - NodeListenerProxy
90 
91 
92 struct 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 
122 private:
123 	FileSystem*			fFileSystem;
124 	NodeListenerProxy*	fHashTableLink;
125 };
126 
127 
128 // #pragma mark - NodeListenerHashDefinition
129 
130 
131 struct 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
162 FileSystem::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
182 FileSystem::~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
234 status_t
235 FileSystem::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
340 const char*
341 FileSystem::GetName() const
342 {
343 	return fName.GetString();
344 }
345 
346 // GetCapabilities
347 const FSCapabilities&
348 FileSystem::GetCapabilities() const
349 {
350 	return fCapabilities;
351 }
352 
353 // GetPortPool
354 RequestPortPool*
355 FileSystem::GetPortPool()
356 {
357 	return &fPortPool;
358 }
359 
360 // Mount
361 status_t
362 FileSystem::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
397 FileSystem::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
430 void
431 FileSystem::VolumeUnmounted(Volume* volume)
432 {
433 	MutexLocker locker(fVolumeLock);
434 	fVolumes.Remove(volume);
435 }
436 
437 // GetVolume
438 Volume*
439 FileSystem::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
455 const IOCtlInfo*
456 FileSystem::GetIOCtlInfo(int command) const
457 {
458 	return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL);
459 }
460 
461 // AddSelectSyncEntry
462 status_t
463 FileSystem::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
482 void
483 FileSystem::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
496 bool
497 FileSystem::KnowsSelectSyncEntry(selectsync* sync)
498 {
499 	return fSelectSyncs->ContainsKey(sync);
500 }
501 
502 
503 // AddNodeListener
504 status_t
505 FileSystem::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
534 status_t
535 FileSystem::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
555 VNodeOps*
556 FileSystem::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
589 void
590 FileSystem::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
602 bool
603 FileSystem::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
612 void
613 FileSystem::_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
715 void
716 FileSystem::_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
751 int32
752 FileSystem::_NotificationThreadEntry(void* data)
753 {
754 	return ((FileSystem*)data)->_NotificationThread();
755 }
756 
757 
758 // _NotificationThread
759 int32
760 FileSystem::_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