⛏️ index : haiku.git

// netfs.cpp

#include <new>

#include <KernelExport.h>
#include <fsproto.h>

#include "DebugSupport.h"
#include "Node.h"
#include "ObjectTracker.h"
#include "QueryManager.h"
#include "RootVolume.h"
#include "VolumeManager.h"

// #pragma mark -
// #pragma mark ----- prototypes -----

extern "C" {

// fs
static int netfs_mount(nspace_id nsid, const char *device, ulong flags,
				void *parameters, size_t len, void **data, vnode_id *rootID);
static int netfs_unmount(void *ns);
//static int netfs_sync(void *ns);
static int netfs_read_fs_stat(void *ns, struct fs_info *info);
//static int netfs_write_fs_stat(void *ns, struct fs_info *info, long mask);

// vnodes
static int netfs_read_vnode(void *ns, vnode_id vnid, char reenter,
				void **node);
static int netfs_write_vnode(void *ns, void *node, char reenter);
static int netfs_remove_vnode(void *ns, void *node, char reenter);

// nodes
//static int netfs_fsync(void *ns, void *node);
static int netfs_read_stat(void *ns, void *node, struct stat *st);
static int netfs_write_stat(void *ns, void *node, struct stat *st,
				long mask);
static int netfs_access(void *ns, void *node, int mode);

// files
static int netfs_create(void *ns, void *dir, const char *name,
				int openMode, int mode, vnode_id *vnid, void **cookie);
static int netfs_open(void *ns, void *node, int openMode, void **cookie);
static int netfs_close(void *ns, void *node, void *cookie);
static int netfs_free_cookie(void *ns, void *node, void *cookie);
static int netfs_read(void *ns, void *node, void *cookie, off_t pos,
				void *buffer, size_t *bufferSize);
static int netfs_write(void *ns, void *node, void *cookie, off_t pos,
				const void *buffer, size_t *bufferSize);
static int netfs_ioctl(void *ns, void *node, void *cookie, int cmd,
				void *buffer, size_t bufferSize);
//static int netfs_setflags(void *ns, void *node, void *cookie, int flags);

// hard links / symlinks
static int netfs_link(void *ns, void *dir, const char *name, void *node);
static int netfs_unlink(void *ns, void *dir, const char *name);
static int netfs_symlink(void *ns, void *dir, const char *name,
				const char *path);
static int netfs_read_link(void *ns, void *node, char *buffer,
				size_t *bufferSize);
static int netfs_rename(void *ns, void *oldDir, const char *oldName,
				void *newDir, const char *newName);

// directories
static int netfs_mkdir(void *ns, void *dir, const char *name, int mode);
static int netfs_rmdir(void *ns, void *dir, const char *name);
static int netfs_open_dir(void *ns, void *node, void **cookie);
static int netfs_close_dir(void *ns, void *node, void *cookie);
static int netfs_free_dir_cookie(void *ns, void *node, void *cookie);
static int netfs_read_dir(void *ns, void *node, void *cookie,
				long *count, struct dirent *buffer, size_t bufferSize);
static int netfs_rewind_dir(void *ns, void *node, void *cookie);
static int netfs_walk(void *ns, void *dir, const char *entryName,
				char **resolvedPath, vnode_id *vnid);

// attributes
static int netfs_open_attrdir(void *ns, void *node, void **cookie);
static int netfs_close_attrdir(void *ns, void *node, void *cookie);
static int netfs_free_attrdir_cookie(void *ns, void *node, void *cookie);
static int netfs_read_attrdir(void *ns, void *node, void *cookie,
				long *count, struct dirent *buffer, size_t bufferSize);
static int netfs_read_attr(void *ns, void *node, const char *name,
				int type, void *buffer, size_t *bufferSize, off_t pos);
static int netfs_rewind_attrdir(void *ns, void *node, void *cookie);
static int netfs_write_attr(void *ns, void *node, const char *name,
				int type, const void *buffer, size_t *bufferSize, off_t pos);
static int netfs_remove_attr(void *ns, void *node, const char *name);
static int netfs_rename_attr(void *ns, void *node, const char *oldName,
				const char *newName);
static int netfs_stat_attr(void *ns, void *node, const char *name,
				struct attr_info *attrInfo);

// queries
static int netfs_open_query(void *ns, const char *queryString, ulong flags,
				port_id port, long token, void **cookie);
static int netfs_close_query(void *ns, void *cookie);
static int netfs_free_query_cookie(void *ns, void *node, void *cookie);
static int netfs_read_query(void *ns, void *cookie, long *count,
				struct dirent *buffer, size_t bufferSize);

} // extern "C"

/* vnode_ops struct. Fill this in to tell the kernel how to call
	functions in your driver.
*/
vnode_ops fs_entry = {
	&netfs_read_vnode,				// read_vnode
	&netfs_write_vnode,				// write_vnode
	&netfs_remove_vnode,			// remove_vnode
	NULL,							// secure_vnode (not needed)
	&netfs_walk,					// walk
	&netfs_access,					// access
	&netfs_create,					// create
	&netfs_mkdir,					// mkdir
	&netfs_symlink,					// symlink
	&netfs_link,					// link
	&netfs_rename,					// rename
	&netfs_unlink,					// unlink
	&netfs_rmdir,					// rmdir
	&netfs_read_link,				// readlink
	&netfs_open_dir,				// opendir
	&netfs_close_dir,				// closedir
	&netfs_free_dir_cookie,			// free_dircookie
	&netfs_rewind_dir,				// rewinddir
	&netfs_read_dir,				// readdir
	&netfs_open,					// open file
	&netfs_close,					// close file
	&netfs_free_cookie,				// free cookie
	&netfs_read,					// read file
	&netfs_write,					// write file
	NULL,							// readv
	NULL,							// writev
	&netfs_ioctl,					// ioctl
	NULL,							// setflags file
	&netfs_read_stat,				// read stat
	&netfs_write_stat,				// write stat
	NULL,							// fsync
	NULL,							// initialize
	&netfs_mount,					// mount
	&netfs_unmount,					// unmount
	NULL,							// sync
	&netfs_read_fs_stat,			// read fs stat
	NULL,							// write fs stat
	NULL,							// select
	NULL,							// deselect

	NULL,							// open index dir
	NULL,							// close index dir
	NULL,							// free index dir cookie
	NULL,							// rewind index dir
	NULL,							// read index dir
	NULL,							// create index
	NULL,							// remove index
	NULL,							// rename index
	NULL,							// stat index

	&netfs_open_attrdir,			// open attr dir
	&netfs_close_attrdir,			// close attr dir
	&netfs_free_attrdir_cookie,		// free attr dir cookie
	&netfs_rewind_attrdir,			// rewind attr dir
	&netfs_read_attrdir,			// read attr dir
	&netfs_write_attr,				// write attr
	&netfs_read_attr,				// read attr
	&netfs_remove_attr,				// remove attr
	&netfs_rename_attr,				// rename attr
	&netfs_stat_attr,				// stat attr

	&netfs_open_query,				// open query
	&netfs_close_query,				// close query
	&netfs_free_query_cookie,		// free query cookie
	&netfs_read_query,				// read query
};

int32 api_version = B_CUR_FS_API_VERSION;

// #pragma mark -
// #pragma mark ----- fs -----

// netfs_mount
static
int
netfs_mount(nspace_id nsid, const char *device, ulong flags,
	void *parameters, size_t len, void **data, vnode_id *rootID)
{
	status_t error = B_OK;
	init_debugging();

	#ifdef DEBUG_OBJECT_TRACKING
		ObjectTracker::InitDefault();
	#endif

	// create and init the volume manager
	VolumeManager* volumeManager = new(std::nothrow) VolumeManager(nsid, flags);
	Volume* rootVolume = NULL;
	if (volumeManager) {
		error = volumeManager->MountRootVolume(device,
			(const char*)parameters, len, &rootVolume);
		if (error != B_OK) {
			delete volumeManager;
			volumeManager = NULL;
		}
	} else
		error = B_NO_MEMORY;
	VolumePutter _(rootVolume);

	// set results
	if (error == B_OK) {
		*data = volumeManager;
		*rootID = rootVolume->GetRootID();
	} else {
		#ifdef DEBUG_OBJECT_TRACKING
			ObjectTracker::ExitDefault();
		#endif
		exit_debugging();
	}
	return error;
}

// netfs_unmount
static
int
netfs_unmount(void *ns)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;

	PRINT("netfs_unmount()\n");

	volumeManager->UnmountRootVolume();
	delete volumeManager;

	#ifdef DEBUG_OBJECT_TRACKING
		ObjectTracker::ExitDefault();
	#endif

	PRINT("netfs_unmount() done\n");

	exit_debugging();
	return B_OK;
}

#if 0 // not used

// netfs_sync
static
int
netfs_sync(void *ns)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;
	Volume* volume = volumeManager->GetRootVolume();
	VolumePutter _(volume);

	PRINT("netfs_sync(%p)\n", ns);

	status_t error = B_BAD_VALUE;
	if (volume)
		error = volume->Sync();

	PRINT("netfs_sync() done: %" B_PRIx32 " \n", error);

	return error;
}

#endif

// netfs_read_fs_stat
static
int
netfs_read_fs_stat(void *ns, struct fs_info *info)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;
	Volume* volume = volumeManager->GetRootVolume();
	VolumePutter _(volume);

	PRINT("netfs_read_fs_stat(%p, %p)\n", ns, info);

	status_t error = B_BAD_VALUE;
	if (volume)
		error = volume->ReadFSStat(info);

	PRINT("netfs_read_fs_stat() done: %" B_PRIx32 " \n", error);

	return error;
}

#if 0 // not used

// netfs_write_fs_stat
static
int
netfs_write_fs_stat(void *ns, struct fs_info *info, long mask)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;
	Volume* volume = volumeManager->GetRootVolume();
	VolumePutter _(volume);

	PRINT("netfs_write_fs_stat(%p, %p, %ld)\n", ns, info, mask);

	status_t error = B_BAD_VALUE;
	if (volume)
		error = volume->WriteFSStat(info, mask);

	PRINT("netfs_write_fs_stat() done: %" B_PRIx32 " \n", error);

	return error;
}

#endif

// #pragma mark -
// #pragma mark ----- vnodes -----

// netfs_read_vnode
static
int
netfs_read_vnode(void *ns, vnode_id vnid, char reenter, void **node)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;
	Volume* volume = volumeManager->GetVolume(vnid);
	VolumePutter _(volume);

	PRINT("netfs_read_vnode(%p, %" B_PRIdINO ", %d, %p)\n", ns, vnid, reenter,
		node);

	status_t error = B_BAD_VALUE;
	if (volume)
		error = volume->ReadVNode(vnid, reenter, (Node**)node);

	PRINT("netfs_read_vnode() done: (%" B_PRIx32 ", %p)\n", error, *node);

	return error;
}

// netfs_write_vnode
static
int
netfs_write_vnode(void *ns, void *_node, char reenter)
{
	Node* node = (Node*)_node;
// DANGER: If dbg_printf() is used, this thread will enter another FS and
// even perform a write operation. The is dangerous here, since this hook
// may be called out of the other FSs, since, for instance a put_vnode()
// called from another FS may cause the VFS layer to free vnodes and thus
// invoke this hook.
//	PRINT(("netfs_write_vnode(%p, %p, %d)\n", ns, node, reenter));
	status_t error = node->GetVolume()->WriteVNode(node, reenter);
//	PRINT(("netfs_write_vnode() done: %" B_PRIx32 "\n", error));
	return error;
}

// netfs_remove_vnode
static
int
netfs_remove_vnode(void *ns, void *_node, char reenter)
{
	Node* node = (Node*)_node;
// DANGER: See netfs_write_vnode().
//	PRINT(("netfs_remove_vnode(%p, %p, %d)\n", ns, node, reenter));
	status_t error = node->GetVolume()->RemoveVNode(node, reenter);
//	PRINT(("netfs_remove_vnode() done: %" B_PRIx32 "\n", error));
	return error;
}

// #pragma mark -
// #pragma mark ----- nodes -----

#if 0 // not used

// netfs_fsync
static
int
netfs_fsync(void *ns, void *_node)
{
	Node* node = (Node*)_node;
	PRINT("netfs_fsync(%p, %p)\n", ns, node);
	status_t error = node->GetVolume()->FSync(node);
	PRINT("netfs_fsync() done: %" B_PRIx32 "\n", error);
	return error;
}

#endif

// netfs_read_stat
static
int
netfs_read_stat(void *ns, void *_node, struct stat *st)
{
	Node* node = (Node*)_node;
	PRINT("netfs_read_stat(%p, %p, %p)\n", ns, node, st);
	status_t error = node->GetVolume()->ReadStat(node, st);
	PRINT("netfs_read_stat() done: %" B_PRIx32 "\n", error);
	return error;
}

// netfs_write_stat
static
int
netfs_write_stat(void *ns, void *_node, struct stat *st, long mask)
{
	Node* node = (Node*)_node;
	PRINT("netfs_write_stat(%p, %p, %p, %ld)\n", ns, node, st, mask);
	status_t error = node->GetVolume()->WriteStat(node, st, mask);
	PRINT("netfs_write_stat() done: %" B_PRIx32 "\n", error);
	return error;
}

// netfs_access
static
int
netfs_access(void *ns, void *_node, int mode)
{
	Node* node = (Node*)_node;
	PRINT("netfs_access(%p, %p, %d)\n", ns, node, mode);
	status_t error = node->GetVolume()->Access(node, mode);
	PRINT("netfs_access() done: %" B_PRIx32 "\n", error);
	return error;
}

// #pragma mark -
// #pragma mark ----- files -----

// netfs_create
static
int
netfs_create(void *ns, void *_dir, const char *name, int openMode, int mode,
	vnode_id *vnid, void **cookie)
{
	Node* dir = (Node*)_dir;
	PRINT("netfs_create(%p, %p, `%s', %d, %d, %p, %p)\n", ns, dir,
		name, openMode, mode, vnid, cookie);
	status_t error = dir->GetVolume()->Create(dir, name, openMode, mode, vnid,
		cookie);
	PRINT("netfs_create() done: (%" B_PRIx32 ", %" B_PRIdINO ", %p)\n", error, *vnid,
		*cookie);
	return error;
}

// netfs_open
static
int
netfs_open(void *ns, void *_node, int openMode, void **cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_open(%p, %p, %d)\n", ns, node, openMode);
	status_t error = node->GetVolume()->Open(node, openMode, cookie);
	PRINT("netfs_open() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
	return error;
}

// netfs_close
static
int
netfs_close(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_close(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->Close(node, cookie);
	PRINT("netfs_close() done: %" B_PRIx32 "\n", error);
	return error;
}

// netfs_free_cookie
static
int
netfs_free_cookie(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_free_cookie(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->FreeCookie(node, cookie);
	PRINT("netfs_free_cookie() done: %" B_PRIx32 "\n", error);
	return error;
}

// netfs_read
static
int
netfs_read(void *ns, void *_node, void *cookie, off_t pos, void *buffer,
	size_t *bufferSize)
{
	Node* node = (Node*)_node;
	PRINT("netfs_read(%p, %p, %p, %" B_PRIdOFF ", %p, %lu)\n", ns, node,
		cookie, pos, buffer, *bufferSize);
	status_t error = node->GetVolume()->Read(node, cookie, pos, buffer,
		*bufferSize, bufferSize);
	PRINT("netfs_read() done: (%" B_PRIx32 ", %lu)\n", error, *bufferSize);
	return error;
}

// netfs_write
static
int
netfs_write(void *ns, void *_node, void *cookie, off_t pos,
	const void *buffer, size_t *bufferSize)
{
	Node* node = (Node*)_node;
	PRINT("netfs_write(%p, %p, %p, %" B_PRIdOFF ", %p, %lu)\n", ns, node,
		cookie, pos, buffer, *bufferSize);
	status_t error = node->GetVolume()->Write(node, cookie, pos, buffer,
		*bufferSize, bufferSize);
	PRINT("netfs_write() done: (%" B_PRIx32 ", %lu)\n", error, *bufferSize);
	return error;
}

// netfs_ioctl
static
int
netfs_ioctl(void *ns, void *_node, void *cookie, int cmd, void *buffer,
	size_t bufferSize)
{
	Node* node = (Node*)_node;
	PRINT("netfs_ioctl(%p, %p, %p, %d, %p, %lu)\n", ns, node, cookie, cmd,
		buffer, bufferSize);
	status_t error = node->GetVolume()->IOCtl(node, cookie, cmd, buffer,
		bufferSize);
	PRINT("netfs_ioctl() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_setflags
//static
//int
//netfs_setflags(void *ns, void *_node, void *cookie, int flags)
//{
//	Node* node = (Node*)_node;
//	PRINT(("netfs_setflags(%p, %p, %p, %d)\n", ns, node, cookie, flags));
//	status_t error = node->GetVolume()->SetFlags(node, cookie, flags);
//	PRINT(("netfs_setflags() done: (%lx)\n", error));
//	return error;
//}

// #pragma mark -
// #pragma mark ----- hard links / symlinks -----

// netfs_link
static
int
netfs_link(void *ns, void *_dir, const char *name, void *_node)
{
	Node* dir = (Node*)_dir;
	Node* node = (Node*)_node;
	PRINT("netfs_link(%p, %p, `%s', %p)\n", ns, dir, name, node);
	status_t error = dir->GetVolume()->Link(dir, name, node);
	PRINT("netfs_link() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_unlink
static
int
netfs_unlink(void *ns, void *_dir, const char *name)
{
	Node* dir = (Node*)_dir;
	PRINT("netfs_unlink(%p, %p, `%s')\n", ns, dir, name);
	status_t error = dir->GetVolume()->Unlink(dir, name);
	PRINT("netfs_unlink() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_symlink
static
int
netfs_symlink(void *ns, void *_dir, const char *name, const char *path)
{
	Node* dir = (Node*)_dir;
	PRINT("netfs_symlink(%p, %p, `%s', `%s')\n", ns, dir, name, path);
	status_t error = dir->GetVolume()->Symlink(dir, name, path);
	PRINT("netfs_symlink() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_read_link
static
int
netfs_read_link(void *ns, void *_node, char *buffer, size_t *bufferSize)
{
	Node* node = (Node*)_node;
	PRINT("netfs_read_link(%p, %p, %p, %lu)\n", ns, node, buffer,
		*bufferSize);

	// TODO: If this were to be implemented (which it isn't, it currently just
	// returns B_BAD_VALUE) then this will need to be changed to return the
	// length of the node and not the number of bytes read into buffer.
	status_t error = node->GetVolume()->ReadLink(node, buffer, *bufferSize,
		bufferSize);
	PRINT("netfs_read_link() done: (%" B_PRIx32 ", %lu)\n", error,
		*bufferSize);
	return error;
}

// netfs_rename
static
int
netfs_rename(void *ns, void *_oldDir, const char *oldName, void *_newDir,
	const char *newName)
{
	Node* oldDir = (Node*)_oldDir;
	Node* newDir = (Node*)_newDir;
	PRINT("netfs_rename(%p, %p, `%s', %p, `%s')\n", ns, oldDir, oldName,
		newDir, newName);
	status_t error = oldDir->GetVolume()->Rename(oldDir, oldName,
		newDir, newName);
	PRINT("netfs_rename() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// #pragma mark -
// #pragma mark ----- directories -----

// netfs_mkdir
static
int
netfs_mkdir(void *ns, void *_dir, const char *name, int mode)
{
	Node* dir = (Node*)_dir;
	PRINT("netfs_mkdir(%p, %p, `%s', %d)\n", ns, dir, name, mode);
	status_t error = dir->GetVolume()->MkDir(dir, name, mode);
	PRINT("netfs_mkdir() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_rmdir
static
int
netfs_rmdir(void *ns, void *_dir, const char *name)
{
	Node* dir = (Node*)_dir;
	PRINT("netfs_rmdir(%p, %p, `%s')\n", ns, dir, name);
	status_t error = dir->GetVolume()->RmDir(dir, name);
	PRINT("netfs_rmdir() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_open_dir
static
int
netfs_open_dir(void *ns, void *_node, void **cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_open_dir(%p, %p)\n", ns, node);
	status_t error = node->GetVolume()->OpenDir(node, cookie);
	PRINT("netfs_open_dir() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
	return error;
}

// netfs_close_dir
static
int
netfs_close_dir(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_close_dir(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->CloseDir(node, cookie);
	PRINT("netfs_close_dir() done: %" B_PRIx32 "\n", error);
	return error;
}

// netfs_free_dir_cookie
static
int
netfs_free_dir_cookie(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_free_dir_cookie(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->FreeDirCookie(node, cookie);
	PRINT("netfs_free_dir_cookie() done: %" B_PRIx32 " \n", error);
	return error;
}

// netfs_read_dir
static
int
netfs_read_dir(void *ns, void *_node, void *cookie, long *count,
	struct dirent *buffer, size_t bufferSize)
{
	Node* node = (Node*)_node;
	PRINT("netfs_read_dir(%p, %p, %p, %ld, %p, %lu)\n", ns, node, cookie,
		*count, buffer, bufferSize);
	int32 _count = *count;
	status_t error = node->GetVolume()->ReadDir(node, cookie, buffer,
		bufferSize, _count, &_count);
	*count = _count;
	PRINT("netfs_read_dir() done: (%" B_PRIx32 ", %ld)\n", error, *count);
	#if DEBUG
		dirent* entry = buffer;
		for (int32 i = 0; i < *count; i++) {
			// R5's kernel vsprintf() doesn't seem to know `%.<number>s', so
			// we need to work around.
			char name[B_FILE_NAME_LENGTH];
			int nameLen = strnlen(entry->d_name, B_FILE_NAME_LENGTH - 1);
			strncpy(name, entry->d_name, nameLen);
			name[nameLen] = '\0';
			PRINT("  entry: d_dev: %" B_PRIdDEV ", d_pdev: %" B_PRIdDEV
				", d_ino: %" B_PRIdINO ", d_pino: %" B_PRIdINO
				", d_reclen: %hu, d_name: `%s'\n",
				entry->d_dev, entry->d_pdev, entry->d_ino,
				entry->d_pino, entry->d_reclen, name);
			entry = (dirent*)((char*)entry + entry->d_reclen);
		}
	#endif

	return error;
}

// netfs_rewind_dir
static
int
netfs_rewind_dir(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_rewind_dir(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->RewindDir(node, cookie);
	PRINT("netfs_rewind_dir() done: %" B_PRIx32 "\n", error);
	return error;
}

// netfs_walk
static
int
netfs_walk(void *ns, void *_dir, const char *entryName,
	char **resolvedPath, vnode_id *vnid)
{
	Node* dir = (Node*)_dir;
	PRINT("netfs_walk(%p, %p, `%s', %p, %p)\n", ns, dir,
		entryName, resolvedPath, vnid);
	status_t error = dir->GetVolume()->Walk(dir, entryName, resolvedPath, vnid);
	PRINT("netfs_walk() done: (%" B_PRIx32 ", `%s', %" B_PRIdINO ")\n", error,
		(resolvedPath ? *resolvedPath : NULL), *vnid);
	return error;
}

// #pragma mark -
// #pragma mark ----- attributes -----

// netfs_open_attrdir
static
int
netfs_open_attrdir(void *ns, void *_node, void **cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_open_attrdir(%p, %p)\n", ns, node);
	status_t error = node->GetVolume()->OpenAttrDir(node, cookie);
	PRINT("netfs_open_attrdir() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
	return error;
}

// netfs_close_attrdir
static
int
netfs_close_attrdir(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_close_attrdir(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->CloseAttrDir(node, cookie);
	PRINT("netfs_close_attrdir() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_free_attrdir_cookie
static
int
netfs_free_attrdir_cookie(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_free_attrdir_cookie(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->FreeAttrDirCookie(node, cookie);
	PRINT("netfs_free_attrdir_cookie() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_read_attrdir
static
int
netfs_read_attrdir(void *ns, void *_node, void *cookie, long *count,
	struct dirent *buffer, size_t bufferSize)
{
	Node* node = (Node*)_node;
	PRINT("netfs_read_attrdir(%p, %p, %p, %ld, %p, %lu)\n", ns, node,
		cookie, *count, buffer, bufferSize);
	int32 _count = *count;
	status_t error = node->GetVolume()->ReadAttrDir(node, cookie, buffer,
		bufferSize, _count, &_count);
	*count = _count;
	PRINT("netfs_read_attrdir() done: (%" B_PRIx32 ", %ld)\n", error, *count);
	return error;
}

// netfs_rewind_attrdir
static
int
netfs_rewind_attrdir(void *ns, void *_node, void *cookie)
{
	Node* node = (Node*)_node;
	PRINT("netfs_rewind_attrdir(%p, %p, %p)\n", ns, node, cookie);
	status_t error = node->GetVolume()->RewindAttrDir(node, cookie);
	PRINT("netfs_rewind_attrdir() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_read_attr
static
int
netfs_read_attr(void *ns, void *_node, const char *name, int type,
	void *buffer, size_t *bufferSize, off_t pos)
{
	Node* node = (Node*)_node;
	PRINT("netfs_read_attr(%p, %p, `%s', %d, %p, %lu, %" B_PRIdOFF ")\n", ns,
		node, name, type, buffer, *bufferSize, pos);
	status_t error = node->GetVolume()->ReadAttr(node, name, type, pos, buffer,
		*bufferSize, bufferSize);
	PRINT("netfs_read_attr() done: (%" B_PRIx32 ", %ld)\n", error,
		*bufferSize);
	return error;
}

// netfs_write_attr
static
int
netfs_write_attr(void *ns, void *_node, const char *name, int type,
	const void *buffer, size_t *bufferSize, off_t pos)
{
	Node* node = (Node*)_node;
	PRINT("netfs_write_attr(%p, %p, `%s', %d, %p, %lu, %" B_PRIdOFF ")\n", ns,
		node, name, type, buffer, *bufferSize, pos);
	status_t error = node->GetVolume()->WriteAttr(node, name, type, pos, buffer,
		*bufferSize, bufferSize);
	PRINT("netfs_write_attr() done: (%" B_PRIx32 ", %ld)\n", error,
		*bufferSize);
	return error;
}

// netfs_remove_attr
static
int
netfs_remove_attr(void *ns, void *_node, const char *name)
{
	Node* node = (Node*)_node;
	PRINT("netfs_remove_attr(%p, %p, `%s')\n", ns, node, name);
	status_t error = node->GetVolume()->RemoveAttr(node, name);
	PRINT("netfs_remove_attr() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_rename_attr
static
int
netfs_rename_attr(void *ns, void *_node, const char *oldName,
	const char *newName)
{
	Node* node = (Node*)_node;
	PRINT("netfs_rename_attr(%p, %p, `%s', `%s')\n", ns, node, oldName,
		newName);
	status_t error = node->GetVolume()->RenameAttr(node, oldName, newName);
	PRINT("netfs_rename_attr() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_stat_attr
static
int
netfs_stat_attr(void *ns, void *_node, const char *name,
	struct attr_info *attrInfo)
{
	Node* node = (Node*)_node;
	PRINT("netfs_stat_attr(%p, %p, `%s', %p)\n", ns, node, name,
		attrInfo);
	status_t error = node->GetVolume()->StatAttr(node, name, attrInfo);
	PRINT("netfs_stat_attr() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// #pragma mark -
// #pragma mark ----- queries -----

// netfs_open_query
static
int
netfs_open_query(void *ns, const char *queryString, ulong flags,
	port_id port, long token, void **cookie)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;
	Volume* volume = volumeManager->GetRootVolume();
	VolumePutter _(volume);

	PRINT("netfs_open_query(%p, `%s', %lu, %" B_PRId32 ", %ld, %p)\n", ns,
		queryString, flags, port, token, cookie);

	status_t error = B_BAD_VALUE;
	if (volume) {
		error = volume->OpenQuery(queryString, flags, port, token,
			(QueryIterator**)cookie);
	}

	PRINT("netfs_open_query() done: (%" B_PRIx32 ", %p)\n", error, *cookie);
	return error;
}

// netfs_close_query
static
int
netfs_close_query(void *ns, void *cookie)
{
	PRINT("netfs_close_query(%p, %p)\n", ns, cookie);

	status_t error = B_OK;
	// no-op: we don't use this hook

	PRINT("netfs_close_query() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_free_query_cookie
static
int
netfs_free_query_cookie(void *ns, void *node, void *cookie)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;
	QueryIterator* iterator = (QueryIterator*)cookie;

	PRINT("netfs_free_query_cookie(%p, %p)\n", ns, cookie);

	status_t error = B_OK;
	volumeManager->GetQueryManager()->PutIterator(iterator);

	PRINT("netfs_free_query_cookie() done: (%" B_PRIx32 ")\n", error);
	return error;
}

// netfs_read_query
static
int
netfs_read_query(void *ns, void *cookie, long *count,
	struct dirent *buffer, size_t bufferSize)
{
	VolumeManager* volumeManager = (VolumeManager*)ns;
	Volume* volume = volumeManager->GetRootVolume();
	QueryIterator* iterator = (QueryIterator*)cookie;
	VolumePutter _(volume);

	PRINT("netfs_read_query(%p, %p, %ld, %p, %lu)\n", ns, cookie,
		*count, buffer, bufferSize);

	status_t error = B_BAD_VALUE;
	if (volume) {
		int32 _count = *count;
		error = volume->ReadQuery(iterator, buffer, bufferSize,
			_count, &_count);
		*count = _count;
	}

	PRINT("netfs_read_query() done: (%" B_PRIx32 ", %ld)\n", error, *count);
	return error;
}