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