xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/kernel_add_on/FileSystem.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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<HashKey32<selectsync*>, int32*> {
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 %ld 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, %ld)\n", name, infos, count));
238 	capabilities.Dump();
239 
240 	// check parameters
241 	if (!name || !infos || count < 2)
242 		RETURN_ERROR(B_BAD_VALUE);
243 
244 	// set the name
245 	if (!fName.SetTo(name))
246 		return B_NO_MEMORY;
247 
248 	// init VNodeOps map
249 	status_t error = fVNodeOps.Init();
250 	if (error != B_OK)
251 		return error;
252 
253 	fTeam = team;
254 	fCapabilities = capabilities;
255 
256 	// create the select sync entry map
257 	fSelectSyncs = new(nothrow) SelectSyncMap;
258 	if (!fSelectSyncs)
259 		return B_NO_MEMORY;
260 
261 	// create the node listener proxy map
262 	fNodeListeners = new(std::nothrow) NodeListenerMap;
263 	if (fNodeListeners == NULL || fNodeListeners->Init() != B_OK)
264 		return B_NO_MEMORY;
265 
266 	// create the request ports
267 	// the notification port
268 	fNotificationPort = new(nothrow) RequestPort(infos);
269 	if (!fNotificationPort)
270 		RETURN_ERROR(B_NO_MEMORY);
271 	error = fNotificationPort->InitCheck();
272 	if (error != B_OK)
273 		return error;
274 
275 	// the other request ports
276 	for (int32 i = 1; i < count; i++) {
277 		RequestPort* port = new(nothrow) RequestPort(infos + i);
278 		if (!port)
279 			RETURN_ERROR(B_NO_MEMORY);
280 		error = port->InitCheck();
281 		if (error == B_OK)
282 			error = fPortPool.AddPort(port);
283 		if (error != B_OK) {
284 			delete port;
285 			RETURN_ERROR(error);
286 		}
287 	}
288 
289 	// get the userland team
290 	port_info portInfo;
291 	error = get_port_info(infos[0].owner_port, &portInfo);
292 	if (error != B_OK)
293 		RETURN_ERROR(error);
294 	fUserlandServerTeam = portInfo.team;
295 
296 	// print some info about the userland team
297 	D(
298 		PRINT(("  userland team is: %ld\n", fUserlandServerTeam));
299 		int32 cookie = 0;
300 		thread_info threadInfo;
301 		while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo)
302 			   == B_OK) {
303 			PRINT(("    userland thread: %ld: `%s'\n", threadInfo.thread,
304 				threadInfo.name));
305 		}
306 	);
307 
308 	// load the settings
309 	fSettings = new(nothrow) Settings;
310 	if (fSettings) {
311 		status_t settingsError = fSettings->SetTo(fName.GetString());
312 		if (settingsError != B_OK) {
313 			PRINT(("Failed to load settings: %s\n", strerror(settingsError)));
314 			delete fSettings;
315 			fSettings = NULL;
316 		} else
317 			fSettings->Dump();
318 	} else
319 		ERROR(("Failed to allocate settings.\n"));
320 
321 	// spawn the notification thread
322 	#if USER
323 		fNotificationThread = spawn_thread(_NotificationThreadEntry,
324 			"UFS notification thread", B_NORMAL_PRIORITY, this);
325 	#else
326 		fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry,
327 			"UFS notification thread", B_NORMAL_PRIORITY, this);
328 	#endif
329 	if (fNotificationThread < 0)
330 		RETURN_ERROR(fNotificationThread);
331 	resume_thread(fNotificationThread);
332 
333 	fInitialized = (error == B_OK);
334 	RETURN_ERROR(error);
335 }
336 
337 // GetName
338 const char*
339 FileSystem::GetName() const
340 {
341 	return fName.GetString();
342 }
343 
344 // GetCapabilities
345 const FSCapabilities&
346 FileSystem::GetCapabilities() const
347 {
348 	return fCapabilities;
349 }
350 
351 // GetPortPool
352 RequestPortPool*
353 FileSystem::GetPortPool()
354 {
355 	return &fPortPool;
356 }
357 
358 // Mount
359 status_t
360 FileSystem::Mount(fs_volume* fsVolume, const char* device, uint32 flags,
361 	const char* parameters, Volume** _volume)
362 {
363 	// check initialization and parameters
364 	if (!fInitialized || !_volume)
365 		return B_BAD_VALUE;
366 
367 	// create volume
368 	Volume* volume = new(nothrow) Volume(this, fsVolume);
369 	if (!volume)
370 		return B_NO_MEMORY;
371 
372 	// add volume to the volume list
373 	MutexLocker locker(fVolumeLock);
374 	status_t error = fVolumes.PushBack(volume);
375 	locker.Unlock();
376 	if (error != B_OK)
377 		return error;
378 
379 	// mount volume
380 	error = volume->Mount(device, flags, parameters);
381 	if (error != B_OK) {
382 		MutexLocker locker(fVolumeLock);
383 		fVolumes.Remove(volume);
384 		locker.Unlock();
385 		volume->ReleaseReference();
386 		return error;
387 	}
388 
389 	*_volume = volume;
390 	return error;
391 }
392 
393 // Initialize
394 /*status_t
395 FileSystem::Initialize(const char* deviceName, const char* parameters,
396 	size_t len)
397 {
398 	// get a free port
399 	RequestPort* port = fPortPool.AcquirePort();
400 	if (!port)
401 		return B_ERROR;
402 	PortReleaser _(&fPortPool, port);
403 	// prepare the request
404 	RequestAllocator allocator(port->GetPort());
405 	MountVolumeRequest* request;
406 	status_t error = AllocateRequest(allocator, &request);
407 	if (error != B_OK)
408 		return error;
409 	error = allocator.AllocateString(request->device, deviceName);
410 	if (error == B_OK)
411 		error = allocator.AllocateData(request->parameters, parameters, len, 1);
412 	if (error != B_OK)
413 		return error;
414 	// send the request
415 	SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY);
416 	InitializeVolumeReply* reply;
417 	error = port->SendRequest(&allocator, &handler, (Request**)&reply);
418 	if (error != B_OK)
419 		return error;
420 	RequestReleaser requestReleaser(port, reply);
421 	// process the reply
422 	if (reply->error != B_OK)
423 		return reply->error;
424 	return error;
425 }*/
426 
427 // VolumeUnmounted
428 void
429 FileSystem::VolumeUnmounted(Volume* volume)
430 {
431 	MutexLocker locker(fVolumeLock);
432 	fVolumes.Remove(volume);
433 }
434 
435 // GetVolume
436 Volume*
437 FileSystem::GetVolume(dev_t id)
438 {
439 	MutexLocker _(fVolumeLock);
440 	for (Vector<Volume*>::Iterator it = fVolumes.Begin();
441 		 it != fVolumes.End();
442 		 it++) {
443 		 Volume* volume = *it;
444 		 if (volume->GetID() == id) {
445 		 	volume->AcquireReference();
446 		 	return volume;
447 		 }
448 	}
449 	return NULL;
450 }
451 
452 // GetIOCtlInfo
453 const IOCtlInfo*
454 FileSystem::GetIOCtlInfo(int command) const
455 {
456 	return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL);
457 }
458 
459 // AddSelectSyncEntry
460 status_t
461 FileSystem::AddSelectSyncEntry(selectsync* sync)
462 {
463 	AutoLocker<SelectSyncMap> _(fSelectSyncs);
464 	int32* count = fSelectSyncs->Get(sync);
465 	if (!count) {
466 		count = new(nothrow) int32(0);
467 		if (!count)
468 			return B_NO_MEMORY;
469 		status_t error = fSelectSyncs->Put(sync, count);
470 		if (error != B_OK) {
471 			delete count;
472 			return error;
473 		}
474 	}
475 	(*count)++;
476 	return B_OK;
477 }
478 
479 // RemoveSelectSyncEntry
480 void
481 FileSystem::RemoveSelectSyncEntry(selectsync* sync)
482 {
483 	AutoLocker<SelectSyncMap> _(fSelectSyncs);
484 	if (int32* count = fSelectSyncs->Get(sync)) {
485 		if (--(*count) <= 0) {
486 			fSelectSyncs->Remove(sync);
487 			delete count;
488 		}
489 	}
490 }
491 
492 
493 // KnowsSelectSyncEntry
494 bool
495 FileSystem::KnowsSelectSyncEntry(selectsync* sync)
496 {
497 	return fSelectSyncs->ContainsKey(sync);
498 }
499 
500 
501 // AddNodeListener
502 status_t
503 FileSystem::AddNodeListener(dev_t device, ino_t node, uint32 flags,
504 	void* listener)
505 {
506 	MutexLocker nodeListenersLocker(fNodeListenersLock);
507 
508 	// lookup the proxy
509 	NodeListenerProxy* proxy = fNodeListeners->Lookup(
510 		NodeListenerKey(listener, device, node));
511 	if (proxy != NULL)
512 		return proxy->StartListening(flags);
513 
514 	// it doesn't exist yet -- create it
515 	proxy = new(std::nothrow) NodeListenerProxy(this, listener, device, node);
516 	if (proxy == NULL)
517 		return B_NO_MEMORY;
518 
519 	// start listening
520 	status_t error = proxy->StartListening(flags);
521 	if (error != B_OK) {
522 		delete proxy;
523 		return error;
524 	}
525 
526 	fNodeListeners->Insert(proxy);
527 	return B_OK;
528 }
529 
530 
531 // RemoveNodeListener
532 status_t
533 FileSystem::RemoveNodeListener(dev_t device, ino_t node, void* listener)
534 {
535 	MutexLocker nodeListenersLocker(fNodeListenersLock);
536 
537 	// lookup the proxy
538 	NodeListenerProxy* proxy = fNodeListeners->Lookup(
539 		NodeListenerKey(listener, device, node));
540 	if (proxy == NULL)
541 		return B_BAD_VALUE;
542 
543 	status_t error = proxy->StopListening();
544 
545 	fNodeListeners->Remove(proxy);
546 	delete proxy;
547 
548 	return error;
549 }
550 
551 
552 // GetVNodeOps
553 VNodeOps*
554 FileSystem::GetVNodeOps(const FSVNodeCapabilities& capabilities)
555 {
556 	MutexLocker locker(fVNodeOpsLock);
557 
558 	// do we already have ops for those capabilities
559 	VNodeOps* ops = fVNodeOps.Lookup(capabilities);
560 	if (ops != NULL) {
561 		ops->refCount++;
562 		return ops;
563 	}
564 
565 	// no, create a new object
566 	fs_vnode_ops* opsVector = new(std::nothrow) fs_vnode_ops;
567 	if (opsVector == NULL)
568 		return NULL;
569 
570 	// set the operations
571 	_InitVNodeOpsVector(opsVector, capabilities);
572 
573 	// create the VNodeOps object
574 	ops = new(std::nothrow) VNodeOps(capabilities, opsVector);
575 	if (ops == NULL) {
576 		delete opsVector;
577 		return NULL;
578 	}
579 
580 	fVNodeOps.Insert(ops);
581 
582 	return ops;
583 }
584 
585 
586 // PutVNodeOps
587 void
588 FileSystem::PutVNodeOps(VNodeOps* ops)
589 {
590 	MutexLocker locker(fVNodeOpsLock);
591 
592 	if (--ops->refCount == 0) {
593 		fVNodeOps.Remove(ops);
594 		delete ops;
595 	}
596 }
597 
598 
599 // IsUserlandServerThread
600 bool
601 FileSystem::IsUserlandServerThread() const
602 {
603 	thread_info info;
604 	get_thread_info(find_thread(NULL), &info);
605 	return (info.team == fUserlandServerTeam);
606 }
607 
608 
609 // _InitVNodeOpsVector
610 void
611 FileSystem::_InitVNodeOpsVector(fs_vnode_ops* ops,
612 	const FSVNodeCapabilities& capabilities)
613 {
614 	memcpy(ops, &gUserlandFSVnodeOps, sizeof(fs_vnode_ops));
615 
616 	#undef CLEAR_UNSUPPORTED
617 	#define CLEAR_UNSUPPORTED(capability, op) 	\
618 		if (!capabilities.Get(capability))				\
619 			ops->op = NULL
620 
621 	// vnode operations
622 	// FS_VNODE_CAPABILITY_LOOKUP: lookup
623 	// FS_VNODE_CAPABILITY_GET_VNODE_NAME: get_vnode_name
624 		// emulated in userland
625 	// FS_VNODE_CAPABILITY_PUT_VNODE: put_vnode
626 	// FS_VNODE_CAPABILITY_REMOVE_VNODE: remove_vnode
627 		// needed by Volume to clean up
628 
629 	// asynchronous I/O
630 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_IO, io);
631 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CANCEL_IO, cancel_io);
632 
633 	// cache file access
634 	ops->get_file_map = NULL;	// never used
635 
636 	// common operations
637 	// FS_VNODE_CAPABILITY_IOCTL: ioctl
638 		// needed by Volume
639 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SET_FLAGS, set_flags);
640 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SELECT, select);
641 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_DESELECT, deselect);
642 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_FSYNC, fsync);
643 
644 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_SYMLINK, read_symlink);
645 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SYMLINK, create_symlink);
646 
647 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_LINK, link);
648 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_UNLINK, unlink);
649 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME, rename);
650 
651 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_ACCESS, access);
652 	// FS_VNODE_CAPABILITY_READ_STAT: read_stat
653 		// needed by Volume
654 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_STAT, write_stat);
655 
656 	// file operations
657 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE, create);
658 	// FS_VNODE_CAPABILITY_OPEN: open
659 		// mandatory
660 	// FS_VNODE_CAPABILITY_CLOSE: close
661 		// needed by Volume
662 	// FS_VNODE_CAPABILITY_FREE_COOKIE: free_cookie
663 		// needed by Volume
664 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ, read);
665 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE, write);
666 
667 	// directory operations
668 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_DIR, create_dir);
669 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_DIR, remove_dir);
670 	// FS_VNODE_CAPABILITY_OPEN_DIR: open_dir
671 		// mandatory
672 	// FS_VNODE_CAPABILITY_CLOSE_DIR: close_dir
673 		// needed by Volume
674 	// FS_VNODE_CAPABILITY_FREE_DIR_COOKIE: free_dir_cookie
675 		// needed by Volume
676 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_DIR, read_dir);
677 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_DIR, rewind_dir);
678 
679 	// attribute directory operations
680 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, open_attr_dir);
681 	// FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR: close_attr_dir
682 		// needed by Volume
683 	// FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE: free_attr_dir_cookie
684 		// needed by Volume
685 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_DIR, read_attr_dir);
686 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, rewind_attr_dir);
687 
688 	// attribute operations
689 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_ATTR, create_attr);
690 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR, open_attr);
691 	// FS_VNODE_CAPABILITY_CLOSE_ATTR: close_attr
692 		// needed by Volume
693 	// FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE: free_attr_cookie
694 		// needed by Volume
695 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR, read_attr);
696 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR, write_attr);
697 
698 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_STAT, read_attr_stat);
699 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR_STAT, write_attr_stat);
700 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME_ATTR, rename_attr);
701 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_ATTR, remove_attr);
702 
703 	// support for node and FS layers
704 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SPECIAL_NODE,
705 		create_special_node);
706 	CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_GET_SUPER_VNODE, get_super_vnode);
707 
708 	#undef CLEAR_UNSUPPORTED
709 }
710 
711 
712 // _NodeListenerEventOccurred
713 void
714 FileSystem::_NodeListenerEventOccurred(NodeListenerProxy* proxy,
715 	const KMessage* event)
716 {
717 	// get a free port
718 	RequestPort* port = fPortPool.AcquirePort();
719 	if (port == NULL)
720 		return;
721 	PortReleaser _(&fPortPool, port);
722 
723 	// prepare the request
724 	RequestAllocator allocator(port->GetPort());
725 	NodeMonitoringEventRequest* request;
726 	status_t error = AllocateRequest(allocator, &request);
727 	if (error != B_OK)
728 		return;
729 
730 	error = allocator.AllocateData(request->event, event->Buffer(),
731 		event->ContentSize(), 1);
732 	if (error != B_OK)
733 		return;
734 
735 	Thread* thread = thread_get_current_thread();
736 	request->team = thread->team->id;
737 	request->thread = thread->id;
738 	request->user = geteuid();
739 	request->group = getegid();
740 	request->listener = proxy->ClientListener();
741 
742 	// send the request
743 	KernelRequestHandler handler(this, NODE_MONITORING_EVENT_REPLY);
744 	port->SendRequest(&allocator, &handler);
745 }
746 
747 
748 // _NotificationThreadEntry
749 int32
750 FileSystem::_NotificationThreadEntry(void* data)
751 {
752 	return ((FileSystem*)data)->_NotificationThread();
753 }
754 
755 
756 // _NotificationThread
757 int32
758 FileSystem::_NotificationThread()
759 {
760 	// process the notification requests until the FS is deleted
761 	while (!fTerminating) {
762 		if (fNotificationPort->InitCheck() != B_OK)
763 			return fNotificationPort->InitCheck();
764 		KernelRequestHandler handler(this, NO_REQUEST);
765 		fNotificationPort->HandleRequests(&handler, NULL,
766 			kNotificationRequestTimeout);
767 	}
768 	// We eat all remaining notification requests, so that they aren't
769 	// presented to the file system, when it is mounted next time.
770 	// TODO: We should probably use a special handler that sends an ack reply,
771 	// but ignores the requests otherwise.
772 	KernelRequestHandler handler(this, NO_REQUEST);
773 	fNotificationPort->HandleRequests(&handler, NULL, 0);
774 	return 0;
775 }
776 
777