1// netfs.cpp
2
3#include <new>
4
5#include <KernelExport.h>
6#include <fsproto.h>
7
8#include "DebugSupport.h"
9#include "Node.h"
10#include "ObjectTracker.h"
11#include "QueryManager.h"
12#include "RootVolume.h"
13#include "VolumeManager.h"
14
15// #pragma mark -
16// #pragma mark ----- prototypes -----
17
18extern "C" {
19
20// fs
21static int netfs_mount(nspace_id nsid, const char *device, ulong flags,
22				void *parameters, size_t len, void **data, vnode_id *rootID);
23static int netfs_unmount(void *ns);
24//static int netfs_sync(void *ns);
25static int netfs_read_fs_stat(void *ns, struct fs_info *info);
26//static int netfs_write_fs_stat(void *ns, struct fs_info *info, long mask);
27
28// vnodes
29static int netfs_read_vnode(void *ns, vnode_id vnid, char reenter,
30				void **node);
31static int netfs_write_vnode(void *ns, void *node, char reenter);
32static int netfs_remove_vnode(void *ns, void *node, char reenter);
33
34// nodes
35//static int netfs_fsync(void *ns, void *node);
36static int netfs_read_stat(void *ns, void *node, struct stat *st);
37static int netfs_write_stat(void *ns, void *node, struct stat *st,
38				long mask);
39static int netfs_access(void *ns, void *node, int mode);
40
41// files
42static int netfs_create(void *ns, void *dir, const char *name,
43				int openMode, int mode, vnode_id *vnid, void **cookie);
44static int netfs_open(void *ns, void *node, int openMode, void **cookie);
45static int netfs_close(void *ns, void *node, void *cookie);
46static int netfs_free_cookie(void *ns, void *node, void *cookie);
47static int netfs_read(void *ns, void *node, void *cookie, off_t pos,
48				void *buffer, size_t *bufferSize);
49static int netfs_write(void *ns, void *node, void *cookie, off_t pos,
50				const void *buffer, size_t *bufferSize);
51static int netfs_ioctl(void *ns, void *node, void *cookie, int cmd,
52				void *buffer, size_t bufferSize);
53//static int netfs_setflags(void *ns, void *node, void *cookie, int flags);
54
55// hard links / symlinks
56static int netfs_link(void *ns, void *dir, const char *name, void *node);
57static int netfs_unlink(void *ns, void *dir, const char *name);
58static int netfs_symlink(void *ns, void *dir, const char *name,
59				const char *path);
60static int netfs_read_link(void *ns, void *node, char *buffer,
61				size_t *bufferSize);
62static int netfs_rename(void *ns, void *oldDir, const char *oldName,
63				void *newDir, const char *newName);
64
65// directories
66static int netfs_mkdir(void *ns, void *dir, const char *name, int mode);
67static int netfs_rmdir(void *ns, void *dir, const char *name);
68static int netfs_open_dir(void *ns, void *node, void **cookie);
69static int netfs_close_dir(void *ns, void *node, void *cookie);
70static int netfs_free_dir_cookie(void *ns, void *node, void *cookie);
71static int netfs_read_dir(void *ns, void *node, void *cookie,
72				long *count, struct dirent *buffer, size_t bufferSize);
73static int netfs_rewind_dir(void *ns, void *node, void *cookie);
74static int netfs_walk(void *ns, void *dir, const char *entryName,
75				char **resolvedPath, vnode_id *vnid);
76
77// attributes
78static int netfs_open_attrdir(void *ns, void *node, void **cookie);
79static int netfs_close_attrdir(void *ns, void *node, void *cookie);
80static int netfs_free_attrdir_cookie(void *ns, void *node, void *cookie);
81static int netfs_read_attrdir(void *ns, void *node, void *cookie,
82				long *count, struct dirent *buffer, size_t bufferSize);
83static int netfs_read_attr(void *ns, void *node, const char *name,
84				int type, void *buffer, size_t *bufferSize, off_t pos);
85static int netfs_rewind_attrdir(void *ns, void *node, void *cookie);
86static int netfs_write_attr(void *ns, void *node, const char *name,
87				int type, const void *buffer, size_t *bufferSize, off_t pos);
88static int netfs_remove_attr(void *ns, void *node, const char *name);
89static int netfs_rename_attr(void *ns, void *node, const char *oldName,
90				const char *newName);
91static int netfs_stat_attr(void *ns, void *node, const char *name,
92				struct attr_info *attrInfo);
93
94// queries
95static int netfs_open_query(void *ns, const char *queryString, ulong flags,
96				port_id port, long token, void **cookie);
97static int netfs_close_query(void *ns, void *cookie);
98static int netfs_free_query_cookie(void *ns, void *node, void *cookie);
99static int netfs_read_query(void *ns, void *cookie, long *count,
100				struct dirent *buffer, size_t bufferSize);
101
102} // extern "C"
103
104/* vnode_ops struct. Fill this in to tell the kernel how to call
105	functions in your driver.
106*/
107vnode_ops fs_entry = {
108	&netfs_read_vnode,				// read_vnode
109	&netfs_write_vnode,				// write_vnode
110	&netfs_remove_vnode,			// remove_vnode
111	NULL,							// secure_vnode (not needed)
112	&netfs_walk,					// walk
113	&netfs_access,					// access
114	&netfs_create,					// create
115	&netfs_mkdir,					// mkdir
116	&netfs_symlink,					// symlink
117	&netfs_link,					// link
118	&netfs_rename,					// rename
119	&netfs_unlink,					// unlink
120	&netfs_rmdir,					// rmdir
121	&netfs_read_link,				// readlink
122	&netfs_open_dir,				// opendir
123	&netfs_close_dir,				// closedir
124	&netfs_free_dir_cookie,			// free_dircookie
125	&netfs_rewind_dir,				// rewinddir
126	&netfs_read_dir,				// readdir
127	&netfs_open,					// open file
128	&netfs_close,					// close file
129	&netfs_free_cookie,				// free cookie
130	&netfs_read,					// read file
131	&netfs_write,					// write file
132	NULL,							// readv
133	NULL,							// writev
134	&netfs_ioctl,					// ioctl
135	NULL,							// setflags file
136	&netfs_read_stat,				// read stat
137	&netfs_write_stat,				// write stat
138	NULL,							// fsync
139	NULL,							// initialize
140	&netfs_mount,					// mount
141	&netfs_unmount,					// unmount
142	NULL,							// sync
143	&netfs_read_fs_stat,			// read fs stat
144	NULL,							// write fs stat
145	NULL,							// select
146	NULL,							// deselect
147
148	NULL,							// open index dir
149	NULL,							// close index dir
150	NULL,							// free index dir cookie
151	NULL,							// rewind index dir
152	NULL,							// read index dir
153	NULL,							// create index
154	NULL,							// remove index
155	NULL,							// rename index
156	NULL,							// stat index
157
158	&netfs_open_attrdir,			// open attr dir
159	&netfs_close_attrdir,			// close attr dir
160	&netfs_free_attrdir_cookie,		// free attr dir cookie
161	&netfs_rewind_attrdir,			// rewind attr dir
162	&netfs_read_attrdir,			// read attr dir
163	&netfs_write_attr,				// write attr
164	&netfs_read_attr,				// read attr
165	&netfs_remove_attr,				// remove attr
166	&netfs_rename_attr,				// rename attr
167	&netfs_stat_attr,				// stat attr
168
169	&netfs_open_query,				// open query
170	&netfs_close_query,				// close query
171	&netfs_free_query_cookie,		// free query cookie
172	&netfs_read_query,				// read query
173};
174
175int32 api_version = B_CUR_FS_API_VERSION;
176
177// #pragma mark -
178// #pragma mark ----- fs -----
179
180// netfs_mount
181static
182int
183netfs_mount(nspace_id nsid, const char *device, ulong flags,
184	void *parameters, size_t len, void **data, vnode_id *rootID)
185{
186	status_t error = B_OK;
187	init_debugging();
188
189	#ifdef DEBUG_OBJECT_TRACKING
190		ObjectTracker::InitDefault();
191	#endif
192
193	// create and init the volume manager
194	VolumeManager* volumeManager = new(std::nothrow) VolumeManager(nsid, flags);
195	Volume* rootVolume = NULL;
196	if (volumeManager) {
197		error = volumeManager->MountRootVolume(device,
198			(const char*)parameters, len, &rootVolume);
199		if (error != B_OK) {
200			delete volumeManager;
201			volumeManager = NULL;
202		}
203	} else
204		error = B_NO_MEMORY;
205	VolumePutter _(rootVolume);
206
207	// set results
208	if (error == B_OK) {
209		*data = volumeManager;
210		*rootID = rootVolume->GetRootID();
211	} else {
212		#ifdef DEBUG_OBJECT_TRACKING
213			ObjectTracker::ExitDefault();
214		#endif
215		exit_debugging();
216	}
217	return error;
218}
219
220// netfs_unmount
221static
222int
223netfs_unmount(void *ns)
224{
225	VolumeManager* volumeManager = (VolumeManager*)ns;
226
227	PRINT("netfs_unmount()\n");
228
229	volumeManager->UnmountRootVolume();
230	delete volumeManager;
231
232	#ifdef DEBUG_OBJECT_TRACKING
233		ObjectTracker::ExitDefault();
234	#endif
235
236	PRINT("netfs_unmount() done\n");
237
238	exit_debugging();
239	return B_OK;
240}
241
242#if 0 // not used
243
244// netfs_sync
245static
246int
247netfs_sync(void *ns)
248{
249	VolumeManager* volumeManager = (VolumeManager*)ns;
250	Volume* volume = volumeManager->GetRootVolume();
251	VolumePutter _(volume);
252
253	PRINT("netfs_sync(%p)\n", ns);
254
255	status_t error = B_BAD_VALUE;
256	if (volume)
257		error = volume->Sync();
258
259	PRINT("netfs_sync() done: %" B_PRIx32 " \n", error);
260
261	return error;
262}
263
264#endif
265
266// netfs_read_fs_stat
267static
268int
269netfs_read_fs_stat(void *ns, struct fs_info *info)
270{
271	VolumeManager* volumeManager = (VolumeManager*)ns;
272	Volume* volume = volumeManager->GetRootVolume();
273	VolumePutter _(volume);
274
275	PRINT("netfs_read_fs_stat(%p, %p)\n", ns, info);
276
277	status_t error = B_BAD_VALUE;
278	if (volume)
279		error = volume->ReadFSStat(info);
280
281	PRINT("netfs_read_fs_stat() done: %" B_PRIx32 " \n", error);
282
283	return error;
284}
285
286#if 0 // not used
287
288// netfs_write_fs_stat
289static
290int
291netfs_write_fs_stat(void *ns, struct fs_info *info, long mask)
292{
293	VolumeManager* volumeManager = (VolumeManager*)ns;
294	Volume* volume = volumeManager->GetRootVolume();
295	VolumePutter _(volume);
296
297	PRINT("netfs_write_fs_stat(%p, %p, %ld)\n", ns, info, mask);
298
299	status_t error = B_BAD_VALUE;
300	if (volume)
301		error = volume->WriteFSStat(info, mask);
302
303	PRINT("netfs_write_fs_stat() done: %" B_PRIx32 " \n", error);
304
305	return error;
306}
307
308#endif
309
310// #pragma mark -
311// #pragma mark ----- vnodes -----
312
313// netfs_read_vnode
314static
315int
316netfs_read_vnode(void *ns, vnode_id vnid, char reenter, void **node)
317{
318	VolumeManager* volumeManager = (VolumeManager*)ns;
319	Volume* volume = volumeManager->GetVolume(vnid);
320	VolumePutter _(volume);
321
322	PRINT("netfs_read_vnode(%p, %" B_PRIdINO ", %d, %p)\n", ns, vnid, reenter,
323		node);
324
325	status_t error = B_BAD_VALUE;
326	if (volume)
327		error = volume->ReadVNode(vnid, reenter, (Node**)node);
328
329	PRINT("netfs_read_vnode() done: (%" B_PRIx32 ", %p)\n", error, *node);
330
331	return error;
332}
333
334// netfs_write_vnode
335static
336int
337netfs_write_vnode(void *ns, void *_node, char reenter)
338{
339	Node* node = (Node*)_node;
340// DANGER: If dbg_printf() is used, this thread will enter another FS and
341// even perform a write operation. The is dangerous here, since this hook
342// may be called out of the other FSs, since, for instance a put_vnode()
343// called from another FS may cause the VFS layer to free vnodes and thus
344// invoke this hook.
345//	PRINT(("netfs_write_vnode(%p, %p, %d)\n", ns, node, reenter));
346	status_t error = node->GetVolume()->WriteVNode(node, reenter);
347//	PRINT(("netfs_write_vnode() done: %" B_PRIx32 "\n", error));
348	return error;
349}
350
351// netfs_remove_vnode
352static
353int
354netfs_remove_vnode(void *ns, void *_node, char reenter)
355{
356	Node* node = (Node*)_node;
357// DANGER: See netfs_write_vnode().
358//	PRINT(("netfs_remove_vnode(%p, %p, %d)\n", ns, node, reenter));
359	status_t error = node->GetVolume()->RemoveVNode(node, reenter);
360//	PRINT(("netfs_remove_vnode() done: %" B_PRIx32 "\n", error));
361	return error;
362}
363
364// #pragma mark -
365// #pragma mark ----- nodes -----
366
367#if 0 // not used
368
369// netfs_fsync
370static
371int
372netfs_fsync(void *ns, void *_node)
373{
374	Node* node = (Node*)_node;
375	PRINT("netfs_fsync(%p, %p)\n", ns, node);
376	status_t error = node->GetVolume()->FSync(node);
377	PRINT("netfs_fsync() done: %" B_PRIx32 "\n", error);
378	return error;
379}
380
381#endif
382
383// netfs_read_stat
384static
385int
386netfs_read_stat(void *ns, void *_node, struct stat *st)
387{
388	Node* node = (Node*)_node;
389	PRINT("netfs_read_stat(%p, %p, %p)\n", ns, node, st);
390	status_t error = node->GetVolume()->ReadStat(node, st);
391	PRINT("netfs_read_stat() done: %" B_PRIx32 "\n", error);
392	return error;
393}
394
395// netfs_write_stat
396static
397int
398netfs_write_stat(void *ns, void *_node, struct stat *st, long mask)
399{
400	Node* node = (Node*)_node;
401	PRINT("netfs_write_stat(%p, %p, %p, %ld)\n", ns, node, st, mask);
402	status_t error = node->GetVolume()->WriteStat(node, st, mask);
403	PRINT("netfs_write_stat() done: %" B_PRIx32 "\n", error);
404	return error;
405}
406
407// netfs_access
408static
409int
410netfs_access(void *ns, void *_node, int mode)
411{
412	Node* node = (Node*)_node;
413	PRINT("netfs_access(%p, %p, %d)\n", ns, node, mode);
414	status_t error = node->GetVolume()->Access(node, mode);
415	PRINT("netfs_access() done: %" B_PRIx32 "\n", error);
416	return error;
417}
418
419// #pragma mark -
420// #pragma mark ----- files -----
421
422// netfs_create
423static
424int
425netfs_create(void *ns, void *_dir, const char *name, int openMode, int mode,
426	vnode_id *vnid, void **cookie)
427{
428	Node* dir = (Node*)_dir;
429	PRINT("netfs_create(%p, %p, `%s', %d, %d, %p, %p)\n", ns, dir,
430		name, openMode, mode, vnid, cookie);
431	status_t error = dir->GetVolume()->Create(dir, name, openMode, mode, vnid,
432		cookie);
433	PRINT("netfs_create() done: (%" B_PRIx32 ", %" B_PRIdINO ", %p)\n", error, *vnid,
434		*cookie);
435	return error;
436}
437
438// netfs_open
439static
440int
441netfs_open(void *ns, void *_node, int openMode, void **cookie)
442{
443	Node* node = (Node*)_node;
444	PRINT("netfs_open(%p, %p, %d)\n", ns, node, openMode);
445	status_t error = node->GetVolume()->Open(node, openMode, cookie);
446	PRINT("netfs_open() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
447	return error;
448}
449
450// netfs_close
451static
452int
453netfs_close(void *ns, void *_node, void *cookie)
454{
455	Node* node = (Node*)_node;
456	PRINT("netfs_close(%p, %p, %p)\n", ns, node, cookie);
457	status_t error = node->GetVolume()->Close(node, cookie);
458	PRINT("netfs_close() done: %" B_PRIx32 "\n", error);
459	return error;
460}
461
462// netfs_free_cookie
463static
464int
465netfs_free_cookie(void *ns, void *_node, void *cookie)
466{
467	Node* node = (Node*)_node;
468	PRINT("netfs_free_cookie(%p, %p, %p)\n", ns, node, cookie);
469	status_t error = node->GetVolume()->FreeCookie(node, cookie);
470	PRINT("netfs_free_cookie() done: %" B_PRIx32 "\n", error);
471	return error;
472}
473
474// netfs_read
475static
476int
477netfs_read(void *ns, void *_node, void *cookie, off_t pos, void *buffer,
478	size_t *bufferSize)
479{
480	Node* node = (Node*)_node;
481	PRINT("netfs_read(%p, %p, %p, %" B_PRIdOFF ", %p, %lu)\n", ns, node,
482		cookie, pos, buffer, *bufferSize);
483	status_t error = node->GetVolume()->Read(node, cookie, pos, buffer,
484		*bufferSize, bufferSize);
485	PRINT("netfs_read() done: (%" B_PRIx32 ", %lu)\n", error, *bufferSize);
486	return error;
487}
488
489// netfs_write
490static
491int
492netfs_write(void *ns, void *_node, void *cookie, off_t pos,
493	const void *buffer, size_t *bufferSize)
494{
495	Node* node = (Node*)_node;
496	PRINT("netfs_write(%p, %p, %p, %" B_PRIdOFF ", %p, %lu)\n", ns, node,
497		cookie, pos, buffer, *bufferSize);
498	status_t error = node->GetVolume()->Write(node, cookie, pos, buffer,
499		*bufferSize, bufferSize);
500	PRINT("netfs_write() done: (%" B_PRIx32 ", %lu)\n", error, *bufferSize);
501	return error;
502}
503
504// netfs_ioctl
505static
506int
507netfs_ioctl(void *ns, void *_node, void *cookie, int cmd, void *buffer,
508	size_t bufferSize)
509{
510	Node* node = (Node*)_node;
511	PRINT("netfs_ioctl(%p, %p, %p, %d, %p, %lu)\n", ns, node, cookie, cmd,
512		buffer, bufferSize);
513	status_t error = node->GetVolume()->IOCtl(node, cookie, cmd, buffer,
514		bufferSize);
515	PRINT("netfs_ioctl() done: (%" B_PRIx32 ")\n", error);
516	return error;
517}
518
519// netfs_setflags
520//static
521//int
522//netfs_setflags(void *ns, void *_node, void *cookie, int flags)
523//{
524//	Node* node = (Node*)_node;
525//	PRINT(("netfs_setflags(%p, %p, %p, %d)\n", ns, node, cookie, flags));
526//	status_t error = node->GetVolume()->SetFlags(node, cookie, flags);
527//	PRINT(("netfs_setflags() done: (%lx)\n", error));
528//	return error;
529//}
530
531// #pragma mark -
532// #pragma mark ----- hard links / symlinks -----
533
534// netfs_link
535static
536int
537netfs_link(void *ns, void *_dir, const char *name, void *_node)
538{
539	Node* dir = (Node*)_dir;
540	Node* node = (Node*)_node;
541	PRINT("netfs_link(%p, %p, `%s', %p)\n", ns, dir, name, node);
542	status_t error = dir->GetVolume()->Link(dir, name, node);
543	PRINT("netfs_link() done: (%" B_PRIx32 ")\n", error);
544	return error;
545}
546
547// netfs_unlink
548static
549int
550netfs_unlink(void *ns, void *_dir, const char *name)
551{
552	Node* dir = (Node*)_dir;
553	PRINT("netfs_unlink(%p, %p, `%s')\n", ns, dir, name);
554	status_t error = dir->GetVolume()->Unlink(dir, name);
555	PRINT("netfs_unlink() done: (%" B_PRIx32 ")\n", error);
556	return error;
557}
558
559// netfs_symlink
560static
561int
562netfs_symlink(void *ns, void *_dir, const char *name, const char *path)
563{
564	Node* dir = (Node*)_dir;
565	PRINT("netfs_symlink(%p, %p, `%s', `%s')\n", ns, dir, name, path);
566	status_t error = dir->GetVolume()->Symlink(dir, name, path);
567	PRINT("netfs_symlink() done: (%" B_PRIx32 ")\n", error);
568	return error;
569}
570
571// netfs_read_link
572static
573int
574netfs_read_link(void *ns, void *_node, char *buffer, size_t *bufferSize)
575{
576	Node* node = (Node*)_node;
577	PRINT("netfs_read_link(%p, %p, %p, %lu)\n", ns, node, buffer,
578		*bufferSize);
579
580	// TODO: If this were to be implemented (which it isn't, it currently just
581	// returns B_BAD_VALUE) then this will need to be changed to return the
582	// length of the node and not the number of bytes read into buffer.
583	status_t error = node->GetVolume()->ReadLink(node, buffer, *bufferSize,
584		bufferSize);
585	PRINT("netfs_read_link() done: (%" B_PRIx32 ", %lu)\n", error,
586		*bufferSize);
587	return error;
588}
589
590// netfs_rename
591static
592int
593netfs_rename(void *ns, void *_oldDir, const char *oldName, void *_newDir,
594	const char *newName)
595{
596	Node* oldDir = (Node*)_oldDir;
597	Node* newDir = (Node*)_newDir;
598	PRINT("netfs_rename(%p, %p, `%s', %p, `%s')\n", ns, oldDir, oldName,
599		newDir, newName);
600	status_t error = oldDir->GetVolume()->Rename(oldDir, oldName,
601		newDir, newName);
602	PRINT("netfs_rename() done: (%" B_PRIx32 ")\n", error);
603	return error;
604}
605
606// #pragma mark -
607// #pragma mark ----- directories -----
608
609// netfs_mkdir
610static
611int
612netfs_mkdir(void *ns, void *_dir, const char *name, int mode)
613{
614	Node* dir = (Node*)_dir;
615	PRINT("netfs_mkdir(%p, %p, `%s', %d)\n", ns, dir, name, mode);
616	status_t error = dir->GetVolume()->MkDir(dir, name, mode);
617	PRINT("netfs_mkdir() done: (%" B_PRIx32 ")\n", error);
618	return error;
619}
620
621// netfs_rmdir
622static
623int
624netfs_rmdir(void *ns, void *_dir, const char *name)
625{
626	Node* dir = (Node*)_dir;
627	PRINT("netfs_rmdir(%p, %p, `%s')\n", ns, dir, name);
628	status_t error = dir->GetVolume()->RmDir(dir, name);
629	PRINT("netfs_rmdir() done: (%" B_PRIx32 ")\n", error);
630	return error;
631}
632
633// netfs_open_dir
634static
635int
636netfs_open_dir(void *ns, void *_node, void **cookie)
637{
638	Node* node = (Node*)_node;
639	PRINT("netfs_open_dir(%p, %p)\n", ns, node);
640	status_t error = node->GetVolume()->OpenDir(node, cookie);
641	PRINT("netfs_open_dir() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
642	return error;
643}
644
645// netfs_close_dir
646static
647int
648netfs_close_dir(void *ns, void *_node, void *cookie)
649{
650	Node* node = (Node*)_node;
651	PRINT("netfs_close_dir(%p, %p, %p)\n", ns, node, cookie);
652	status_t error = node->GetVolume()->CloseDir(node, cookie);
653	PRINT("netfs_close_dir() done: %" B_PRIx32 "\n", error);
654	return error;
655}
656
657// netfs_free_dir_cookie
658static
659int
660netfs_free_dir_cookie(void *ns, void *_node, void *cookie)
661{
662	Node* node = (Node*)_node;
663	PRINT("netfs_free_dir_cookie(%p, %p, %p)\n", ns, node, cookie);
664	status_t error = node->GetVolume()->FreeDirCookie(node, cookie);
665	PRINT("netfs_free_dir_cookie() done: %" B_PRIx32 " \n", error);
666	return error;
667}
668
669// netfs_read_dir
670static
671int
672netfs_read_dir(void *ns, void *_node, void *cookie, long *count,
673	struct dirent *buffer, size_t bufferSize)
674{
675	Node* node = (Node*)_node;
676	PRINT("netfs_read_dir(%p, %p, %p, %ld, %p, %lu)\n", ns, node, cookie,
677		*count, buffer, bufferSize);
678	int32 _count = *count;
679	status_t error = node->GetVolume()->ReadDir(node, cookie, buffer,
680		bufferSize, _count, &_count);
681	*count = _count;
682	PRINT("netfs_read_dir() done: (%" B_PRIx32 ", %ld)\n", error, *count);
683	#if DEBUG
684		dirent* entry = buffer;
685		for (int32 i = 0; i < *count; i++) {
686			// R5's kernel vsprintf() doesn't seem to know `%.<number>s', so
687			// we need to work around.
688			char name[B_FILE_NAME_LENGTH];
689			int nameLen = strnlen(entry->d_name, B_FILE_NAME_LENGTH - 1);
690			strncpy(name, entry->d_name, nameLen);
691			name[nameLen] = '\0';
692			PRINT("  entry: d_dev: %" B_PRIdDEV ", d_pdev: %" B_PRIdDEV
693				", d_ino: %" B_PRIdINO ", d_pino: %" B_PRIdINO
694				", d_reclen: %hu, d_name: `%s'\n",
695				entry->d_dev, entry->d_pdev, entry->d_ino,
696				entry->d_pino, entry->d_reclen, name);
697			entry = (dirent*)((char*)entry + entry->d_reclen);
698		}
699	#endif
700
701	return error;
702}
703
704// netfs_rewind_dir
705static
706int
707netfs_rewind_dir(void *ns, void *_node, void *cookie)
708{
709	Node* node = (Node*)_node;
710	PRINT("netfs_rewind_dir(%p, %p, %p)\n", ns, node, cookie);
711	status_t error = node->GetVolume()->RewindDir(node, cookie);
712	PRINT("netfs_rewind_dir() done: %" B_PRIx32 "\n", error);
713	return error;
714}
715
716// netfs_walk
717static
718int
719netfs_walk(void *ns, void *_dir, const char *entryName,
720	char **resolvedPath, vnode_id *vnid)
721{
722	Node* dir = (Node*)_dir;
723	PRINT("netfs_walk(%p, %p, `%s', %p, %p)\n", ns, dir,
724		entryName, resolvedPath, vnid);
725	status_t error = dir->GetVolume()->Walk(dir, entryName, resolvedPath, vnid);
726	PRINT("netfs_walk() done: (%" B_PRIx32 ", `%s', %" B_PRIdINO ")\n", error,
727		(resolvedPath ? *resolvedPath : NULL), *vnid);
728	return error;
729}
730
731// #pragma mark -
732// #pragma mark ----- attributes -----
733
734// netfs_open_attrdir
735static
736int
737netfs_open_attrdir(void *ns, void *_node, void **cookie)
738{
739	Node* node = (Node*)_node;
740	PRINT("netfs_open_attrdir(%p, %p)\n", ns, node);
741	status_t error = node->GetVolume()->OpenAttrDir(node, cookie);
742	PRINT("netfs_open_attrdir() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
743	return error;
744}
745
746// netfs_close_attrdir
747static
748int
749netfs_close_attrdir(void *ns, void *_node, void *cookie)
750{
751	Node* node = (Node*)_node;
752	PRINT("netfs_close_attrdir(%p, %p, %p)\n", ns, node, cookie);
753	status_t error = node->GetVolume()->CloseAttrDir(node, cookie);
754	PRINT("netfs_close_attrdir() done: (%" B_PRIx32 ")\n", error);
755	return error;
756}
757
758// netfs_free_attrdir_cookie
759static
760int
761netfs_free_attrdir_cookie(void *ns, void *_node, void *cookie)
762{
763	Node* node = (Node*)_node;
764	PRINT("netfs_free_attrdir_cookie(%p, %p, %p)\n", ns, node, cookie);
765	status_t error = node->GetVolume()->FreeAttrDirCookie(node, cookie);
766	PRINT("netfs_free_attrdir_cookie() done: (%" B_PRIx32 ")\n", error);
767	return error;
768}
769
770// netfs_read_attrdir
771static
772int
773netfs_read_attrdir(void *ns, void *_node, void *cookie, long *count,
774	struct dirent *buffer, size_t bufferSize)
775{
776	Node* node = (Node*)_node;
777	PRINT("netfs_read_attrdir(%p, %p, %p, %ld, %p, %lu)\n", ns, node,
778		cookie, *count, buffer, bufferSize);
779	int32 _count = *count;
780	status_t error = node->GetVolume()->ReadAttrDir(node, cookie, buffer,
781		bufferSize, _count, &_count);
782	*count = _count;
783	PRINT("netfs_read_attrdir() done: (%" B_PRIx32 ", %ld)\n", error, *count);
784	return error;
785}
786
787// netfs_rewind_attrdir
788static
789int
790netfs_rewind_attrdir(void *ns, void *_node, void *cookie)
791{
792	Node* node = (Node*)_node;
793	PRINT("netfs_rewind_attrdir(%p, %p, %p)\n", ns, node, cookie);
794	status_t error = node->GetVolume()->RewindAttrDir(node, cookie);
795	PRINT("netfs_rewind_attrdir() done: (%" B_PRIx32 ")\n", error);
796	return error;
797}
798
799// netfs_read_attr
800static
801int
802netfs_read_attr(void *ns, void *_node, const char *name, int type,
803	void *buffer, size_t *bufferSize, off_t pos)
804{
805	Node* node = (Node*)_node;
806	PRINT("netfs_read_attr(%p, %p, `%s', %d, %p, %lu, %" B_PRIdOFF ")\n", ns,
807		node, name, type, buffer, *bufferSize, pos);
808	status_t error = node->GetVolume()->ReadAttr(node, name, type, pos, buffer,
809		*bufferSize, bufferSize);
810	PRINT("netfs_read_attr() done: (%" B_PRIx32 ", %ld)\n", error,
811		*bufferSize);
812	return error;
813}
814
815// netfs_write_attr
816static
817int
818netfs_write_attr(void *ns, void *_node, const char *name, int type,
819	const void *buffer, size_t *bufferSize, off_t pos)
820{
821	Node* node = (Node*)_node;
822	PRINT("netfs_write_attr(%p, %p, `%s', %d, %p, %lu, %" B_PRIdOFF ")\n", ns,
823		node, name, type, buffer, *bufferSize, pos);
824	status_t error = node->GetVolume()->WriteAttr(node, name, type, pos, buffer,
825		*bufferSize, bufferSize);
826	PRINT("netfs_write_attr() done: (%" B_PRIx32 ", %ld)\n", error,
827		*bufferSize);
828	return error;
829}
830
831// netfs_remove_attr
832static
833int
834netfs_remove_attr(void *ns, void *_node, const char *name)
835{
836	Node* node = (Node*)_node;
837	PRINT("netfs_remove_attr(%p, %p, `%s')\n", ns, node, name);
838	status_t error = node->GetVolume()->RemoveAttr(node, name);
839	PRINT("netfs_remove_attr() done: (%" B_PRIx32 ")\n", error);
840	return error;
841}
842
843// netfs_rename_attr
844static
845int
846netfs_rename_attr(void *ns, void *_node, const char *oldName,
847	const char *newName)
848{
849	Node* node = (Node*)_node;
850	PRINT("netfs_rename_attr(%p, %p, `%s', `%s')\n", ns, node, oldName,
851		newName);
852	status_t error = node->GetVolume()->RenameAttr(node, oldName, newName);
853	PRINT("netfs_rename_attr() done: (%" B_PRIx32 ")\n", error);
854	return error;
855}
856
857// netfs_stat_attr
858static
859int
860netfs_stat_attr(void *ns, void *_node, const char *name,
861	struct attr_info *attrInfo)
862{
863	Node* node = (Node*)_node;
864	PRINT("netfs_stat_attr(%p, %p, `%s', %p)\n", ns, node, name,
865		attrInfo);
866	status_t error = node->GetVolume()->StatAttr(node, name, attrInfo);
867	PRINT("netfs_stat_attr() done: (%" B_PRIx32 ")\n", error);
868	return error;
869}
870
871// #pragma mark -
872// #pragma mark ----- queries -----
873
874// netfs_open_query
875static
876int
877netfs_open_query(void *ns, const char *queryString, ulong flags,
878	port_id port, long token, void **cookie)
879{
880	VolumeManager* volumeManager = (VolumeManager*)ns;
881	Volume* volume = volumeManager->GetRootVolume();
882	VolumePutter _(volume);
883
884	PRINT("netfs_open_query(%p, `%s', %lu, %" B_PRId32 ", %ld, %p)\n", ns,
885		queryString, flags, port, token, cookie);
886
887	status_t error = B_BAD_VALUE;
888	if (volume) {
889		error = volume->OpenQuery(queryString, flags, port, token,
890			(QueryIterator**)cookie);
891	}
892
893	PRINT("netfs_open_query() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
894	return error;
895}
896
897// netfs_close_query
898static
899int
900netfs_close_query(void *ns, void *cookie)
901{
902	PRINT("netfs_close_query(%p, %p)\n", ns, cookie);
903
904	status_t error = B_OK;
905	// no-op: we don't use this hook
906
907	PRINT("netfs_close_query() done: (%" B_PRIx32 ")\n", error);
908	return error;
909}
910
911// netfs_free_query_cookie
912static
913int
914netfs_free_query_cookie(void *ns, void *node, void *cookie)
915{
916	VolumeManager* volumeManager = (VolumeManager*)ns;
917	QueryIterator* iterator = (QueryIterator*)cookie;
918
919	PRINT("netfs_free_query_cookie(%p, %p)\n", ns, cookie);
920
921	status_t error = B_OK;
922	volumeManager->GetQueryManager()->PutIterator(iterator);
923
924	PRINT("netfs_free_query_cookie() done: (%" B_PRIx32 ")\n", error);
925	return error;
926}
927
928// netfs_read_query
929static
930int
931netfs_read_query(void *ns, void *cookie, long *count,
932	struct dirent *buffer, size_t bufferSize)
933{
934	VolumeManager* volumeManager = (VolumeManager*)ns;
935	Volume* volume = volumeManager->GetRootVolume();
936	QueryIterator* iterator = (QueryIterator*)cookie;
937	VolumePutter _(volume);
938
939	PRINT("netfs_read_query(%p, %p, %ld, %p, %lu)\n", ns, cookie,
940		*count, buffer, bufferSize);
941
942	status_t error = B_BAD_VALUE;
943	if (volume) {
944		int32 _count = *count;
945		error = volume->ReadQuery(iterator, buffer, bufferSize,
946			_count, &_count);
947		*count = _count;
948	}
949
950	PRINT("netfs_read_query() done: (%" B_PRIx32 ", %ld)\n", error, *count);
951	return error;
952}
953
954