Index: uspace/srv/fs/devfs/devfs_ops.c
===================================================================
--- uspace/srv/fs/devfs/devfs_ops.c	(revision 1e4cadad49fbe94a308f6f74c331537fbfc59425)
+++ uspace/srv/fs/devfs/devfs_ops.c	(revision 1313ee92979dd133fd0dd0717213410aada3df10)
@@ -44,9 +44,15 @@
 #include <fibril_synch.h>
 #include <adt/hash_table.h>
+#include <ipc/devmap.h>
 #include <sys/stat.h>
+#include <libfs.h>
+#include <assert.h>
 #include "devfs.h"
 #include "devfs_ops.h"
 
-#define PLB_GET_CHAR(pos)  (devfs_reg.plb_ro[pos % PLB_SIZE])
+typedef struct {
+	devmap_handle_type_t type;
+	dev_handle_t handle;
+} devfs_node_t;
 
 /** Opened devices structure */
@@ -91,4 +97,309 @@
 };
 
+static int devfs_node_get_internal(fs_node_t **rfn, devmap_handle_type_t type,
+    dev_handle_t handle)
+{
+	devfs_node_t *node = (devfs_node_t *) malloc(sizeof(devfs_node_t));
+	if (node == NULL) {
+		*rfn = NULL;
+		return ENOMEM;
+	}
+	
+	*rfn = (fs_node_t *) malloc(sizeof(fs_node_t));
+	if (*rfn == NULL) {
+		free(node);
+		*rfn = NULL;
+		return ENOMEM;
+	}
+	
+	fs_node_initialize(*rfn);
+	node->type = type;
+	node->handle = handle;
+	
+	(*rfn)->data = node;
+	return EOK;
+}
+
+static int devfs_root_get(fs_node_t **rfn, dev_handle_t dev_handle)
+{
+	return devfs_node_get_internal(rfn, DEV_HANDLE_NONE, 0);
+}
+
+static int devfs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
+{
+	devfs_node_t *node = (devfs_node_t *) pfn->data;
+	
+	if (node->handle == 0) {
+		/* Root directory */
+		
+		dev_desc_t *devs;
+		size_t count = devmap_get_namespaces(&devs);
+		
+		if (count > 0) {
+			size_t pos;
+			for (pos = 0; pos < count; pos++) {
+				/* Ignore root namespace */
+				if (str_cmp(devs[pos].name, "") == 0)
+					continue;
+				
+				if (str_cmp(devs[pos].name, component) == 0) {
+					free(devs);
+					return devfs_node_get_internal(rfn, DEV_HANDLE_NAMESPACE, devs[pos].handle);
+				}
+			}
+			
+			free(devs);
+		}
+		
+		/* Search root namespace */
+		dev_handle_t namespace;
+		if (devmap_namespace_get_handle("", &namespace, 0) == EOK) {
+			count = devmap_get_devices(namespace, &devs);
+			
+			if (count > 0) {
+				size_t pos;
+				for (pos = 0; pos < count; pos++) {
+					if (str_cmp(devs[pos].name, component) == 0) {
+						free(devs);
+						return devfs_node_get_internal(rfn, DEV_HANDLE_DEVICE, devs[pos].handle);
+					}
+				}
+				
+				free(devs);
+			}
+		}
+		
+		*rfn = NULL;
+		return EOK;
+	}
+	
+	if (node->type == DEV_HANDLE_NAMESPACE) {
+		/* Namespace directory */
+		
+		dev_desc_t *devs;
+		size_t count = devmap_get_devices(node->handle, &devs);
+		if (count > 0) {
+			size_t pos;
+			for (pos = 0; pos < count; pos++) {
+				if (str_cmp(devs[pos].name, component) == 0) {
+					free(devs);
+					return devfs_node_get_internal(rfn, DEV_HANDLE_DEVICE, devs[pos].handle);
+				}
+			}
+			
+			free(devs);
+		}
+		
+		*rfn = NULL;
+		return EOK;
+	}
+	
+	*rfn = NULL;
+	return EOK;
+}
+
+static int devfs_node_get(fs_node_t **rfn, dev_handle_t dev_handle, fs_index_t index)
+{
+	return devfs_node_get_internal(rfn, devmap_handle_probe(index), index);
+}
+
+static int devfs_node_open(fs_node_t *fn)
+{
+	devfs_node_t *node = (devfs_node_t *) fn->data;
+	
+	if (node->handle == 0) {
+		/* Root directory */
+		return EOK;
+	}
+	
+	devmap_handle_type_t type = devmap_handle_probe(node->handle);
+	
+	if (type == DEV_HANDLE_NAMESPACE) {
+		/* Namespace directory */
+		return EOK;
+	}
+	
+	if (type == DEV_HANDLE_DEVICE) {
+		/* Device node */
+		
+		unsigned long key[] = {
+			[DEVICES_KEY_HANDLE] = (unsigned long) node->handle
+		};
+		
+		fibril_mutex_lock(&devices_mutex);
+		link_t *lnk = hash_table_find(&devices, key);
+		if (lnk == NULL) {
+			device_t *dev = (device_t *) malloc(sizeof(device_t));
+			if (dev == NULL) {
+				fibril_mutex_unlock(&devices_mutex);
+				return ENOMEM;
+			}
+			
+			int phone = devmap_device_connect(node->handle, 0);
+			if (phone < 0) {
+				fibril_mutex_unlock(&devices_mutex);
+				free(dev);
+				return ENOENT;
+			}
+			
+			dev->handle = node->handle;
+			dev->phone = phone;
+			dev->refcount = 1;
+			
+			hash_table_insert(&devices, key, &dev->link);
+		} else {
+			device_t *dev = hash_table_get_instance(lnk, device_t, link);
+			dev->refcount++;
+		}
+		
+		fibril_mutex_unlock(&devices_mutex);
+		
+		return EOK;
+	}
+	
+	return ENOENT;
+}
+
+static int devfs_node_put(fs_node_t *fn)
+{
+	free(fn->data);
+	free(fn);
+	return EOK;
+}
+
+static int devfs_create_node(fs_node_t **rfn, dev_handle_t dev_handle, int lflag)
+{
+	assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
+	
+	*rfn = NULL;
+	return ENOTSUP;
+}
+
+static int devfs_destroy_node(fs_node_t *fn)
+{
+	return ENOTSUP;
+}
+
+static int devfs_link_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
+{
+	return ENOTSUP;
+}
+
+static int devfs_unlink_node(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
+{
+	return ENOTSUP;
+}
+
+static int devfs_has_children(bool *has_children, fs_node_t *fn)
+{
+	devfs_node_t *node = (devfs_node_t *) fn->data;
+	
+	if (node->handle == 0) {
+		size_t count = devmap_count_namespaces();
+		if (count > 0) {
+			*has_children = true;
+			return EOK;
+		}
+		
+		/* Root namespace */
+		dev_handle_t namespace;
+		if (devmap_namespace_get_handle("", &namespace, 0) == EOK) {
+			count = devmap_count_devices(namespace);
+			if (count > 0) {
+				*has_children = true;
+				return EOK;
+			}
+		}
+		
+		*has_children = false;
+		return EOK;
+	}
+	
+	if (node->type == DEV_HANDLE_NAMESPACE) {
+		size_t count = devmap_count_devices(node->handle);
+		if (count > 0) {
+			*has_children = true;
+			return EOK;
+		}
+		
+		*has_children = false;
+		return EOK;
+	}
+	
+	*has_children = false;
+	return EOK;
+}
+
+static fs_index_t devfs_index_get(fs_node_t *fn)
+{
+	devfs_node_t *node = (devfs_node_t *) fn->data;
+	return node->handle;
+}
+
+static size_t devfs_size_get(fs_node_t *fn)
+{
+	return 0;
+}
+
+static unsigned int devfs_lnkcnt_get(fs_node_t *fn)
+{
+	devfs_node_t *node = (devfs_node_t *) fn->data;
+	
+	if (node->handle == 0)
+		return 0;
+	
+	return 1;
+}
+
+static char devfs_plb_get_char(unsigned pos)
+{
+	return devfs_reg.plb_ro[pos % PLB_SIZE];
+}
+
+static bool devfs_is_directory(fs_node_t *fn)
+{
+	devfs_node_t *node = (devfs_node_t *) fn->data;
+	
+	return ((node->type == DEV_HANDLE_NONE) || (node->type == DEV_HANDLE_NAMESPACE));
+}
+
+static bool devfs_is_file(fs_node_t *fn)
+{
+	devfs_node_t *node = (devfs_node_t *) fn->data;
+	
+	return (node->type == DEV_HANDLE_DEVICE);
+}
+
+static dev_handle_t devfs_device_get(fs_node_t *fn)
+{
+	devfs_node_t *node = (devfs_node_t *) fn->data;
+	
+	if (node->type == DEV_HANDLE_DEVICE)
+		return node->handle;
+	
+	return 0;
+}
+
+/** libfs operations */
+libfs_ops_t devfs_libfs_ops = {
+	.root_get = devfs_root_get,
+	.match = devfs_match,
+	.node_get = devfs_node_get,
+	.node_open = devfs_node_open,
+	.node_put = devfs_node_put,
+	.create = devfs_create_node,
+	.destroy = devfs_destroy_node,
+	.link = devfs_link_node,
+	.unlink = devfs_unlink_node,
+	.has_children = devfs_has_children,
+	.index_get = devfs_index_get,
+	.size_get = devfs_size_get,
+	.lnkcnt_get = devfs_lnkcnt_get,
+	.plb_get_char = devfs_plb_get_char,
+	.is_directory = devfs_is_directory,
+	.is_file = devfs_is_file,
+	.device_get = devfs_device_get
+};
+
 bool devfs_init(void)
 {
@@ -105,29 +416,14 @@
 void devfs_mounted(ipc_callid_t rid, ipc_call_t *request)
 {
+	char *opts;
+	
 	/* Accept the mount options */
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_write_receive(&callid, &size)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-	
-	char *opts = malloc(size + 1);
-	if (!opts) {
-		ipc_answer_0(callid, ENOMEM);
-		ipc_answer_0(rid, ENOMEM);
-		return;
-	}
-	
-	ipcarg_t retval = async_data_write_finalize(callid, opts, size);
+	ipcarg_t retval = async_data_string_receive(&opts, 0);
 	if (retval != EOK) {
 		ipc_answer_0(rid, retval);
-		free(opts);
 		return;
 	}
 	
 	free(opts);
-	
 	ipc_answer_3(rid, EOK, 0, 0, 0);
 }
@@ -135,194 +431,111 @@
 void devfs_mount(ipc_callid_t rid, ipc_call_t *request)
 {
-	ipc_answer_0(rid, ENOTSUP);
+	libfs_mount(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
 }
 
 void devfs_lookup(ipc_callid_t rid, ipc_call_t *request)
 {
-	ipcarg_t first = IPC_GET_ARG1(*request);
-	ipcarg_t last = IPC_GET_ARG2(*request);
-	dev_handle_t dev_handle = IPC_GET_ARG3(*request);
-	ipcarg_t lflag = IPC_GET_ARG4(*request);
-	fs_index_t index = IPC_GET_ARG5(*request);
-	
-	/* Hierarchy is flat, no altroot is supported */
-	if (index != 0) {
-		ipc_answer_0(rid, ENOENT);
-		return;
-	}
-	
-	if ((lflag & L_LINK) || (lflag & L_UNLINK)) {
-		ipc_answer_0(rid, ENOTSUP);
-		return;
-	}
-	
-	/* Eat slash */
-	if (PLB_GET_CHAR(first) == '/') {
-		first++;
-		first %= PLB_SIZE;
-	}
-	
-	if (first >= last) {
-		/* Root entry */
-		if (!(lflag & L_FILE))
-			ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, 0, 0, 0);
-		else
-			ipc_answer_0(rid, ENOENT);
-	} else {
-		if (!(lflag & L_DIRECTORY)) {
-			size_t len;
-			if (last >= first)
-				len = last - first + 1;
-			else
-				len = first + PLB_SIZE - last + 1;
+	libfs_lookup(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
+}
+
+void devfs_open_node(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_open_node(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
+}
+
+void devfs_stat(ipc_callid_t rid, ipc_call_t *request)
+{
+	libfs_stat(&devfs_libfs_ops, devfs_reg.fs_handle, rid, request);
+}
+
+void devfs_read(ipc_callid_t rid, ipc_call_t *request)
+{
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
+	off_t pos = (off_t) IPC_GET_ARG3(*request);
+	
+	if (index == 0) {
+		ipc_callid_t callid;
+		size_t size;
+		if (!async_data_read_receive(&callid, &size)) {
+			ipc_answer_0(callid, EINVAL);
+			ipc_answer_0(rid, EINVAL);
+			return;
+		}
+		
+		dev_desc_t *desc;
+		size_t count = devmap_get_namespaces(&desc);
+		
+		/* Get rid of root namespace */
+		size_t i;
+		for (i = 0; i < count; i++) {
+			if (str_cmp(desc[i].name, "") == 0) {
+				if (pos >= i)
+					pos++;
+				
+				break;
+			}
+		}
+		
+		if (pos < count) {
+			async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
+			free(desc);
+			ipc_answer_1(rid, EOK, 1);
+			return;
+		}
+		
+		free(desc);
+		pos -= count;
+		
+		/* Search root namespace */
+		dev_handle_t namespace;
+		if (devmap_namespace_get_handle("", &namespace, 0) == EOK) {
+			count = devmap_get_devices(namespace, &desc);
 			
-			char *name = (char *) malloc(len + 1);
-			if (name == NULL) {
-				ipc_answer_0(rid, ENOMEM);
+			if (pos < count) {
+				async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
+				free(desc);
+				ipc_answer_1(rid, EOK, 1);
 				return;
 			}
 			
-			size_t i;
-			for (i = 0; i < len; i++)
-				name[i] = PLB_GET_CHAR(first + i);
-			
-			name[len] = 0;
-			
-			dev_handle_t handle;
-			if (devmap_device_get_handle(name, &handle, 0) != EOK) {
-				free(name);
-				ipc_answer_0(rid, ENOENT);
-				return;
-			}
-			
-			if (lflag & L_OPEN) {
-				unsigned long key[] = {
-					[DEVICES_KEY_HANDLE] = (unsigned long) handle
-				};
-				
-				fibril_mutex_lock(&devices_mutex);
-				link_t *lnk = hash_table_find(&devices, key);
-				if (lnk == NULL) {
-					int phone = devmap_device_connect(handle, 0);
-					if (phone < 0) {
-						fibril_mutex_unlock(&devices_mutex);
-						free(name);
-						ipc_answer_0(rid, ENOENT);
-						return;
-					}
-					
-					device_t *dev = (device_t *) malloc(sizeof(device_t));
-					if (dev == NULL) {
-						fibril_mutex_unlock(&devices_mutex);
-						free(name);
-						ipc_answer_0(rid, ENOMEM);
-						return;
-					}
-					
-					dev->handle = handle;
-					dev->phone = phone;
-					dev->refcount = 1;
-					
-					hash_table_insert(&devices, key, &dev->link);
-				} else {
-					device_t *dev = hash_table_get_instance(lnk, device_t, link);
-					dev->refcount++;
-				}
-				fibril_mutex_unlock(&devices_mutex);
-			}
-			
-			free(name);
-			
-			ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, handle, 0, 1);
-		} else
-			ipc_answer_0(rid, ENOENT);
-	}
-}
-
-void devfs_open_node(ipc_callid_t rid, ipc_call_t *request)
-{
-	dev_handle_t handle = IPC_GET_ARG2(*request);
-	
-	unsigned long key[] = {
-		[DEVICES_KEY_HANDLE] = (unsigned long) handle
-	};
-	
-	fibril_mutex_lock(&devices_mutex);
-	link_t *lnk = hash_table_find(&devices, key);
-	if (lnk == NULL) {
-		int phone = devmap_device_connect(handle, 0);
-		if (phone < 0) {
-			fibril_mutex_unlock(&devices_mutex);
-			ipc_answer_0(rid, ENOENT);
-			return;
-		}
-		
-		device_t *dev = (device_t *) malloc(sizeof(device_t));
-		if (dev == NULL) {
-			fibril_mutex_unlock(&devices_mutex);
-			ipc_answer_0(rid, ENOMEM);
-			return;
-		}
-		
-		dev->handle = handle;
-		dev->phone = phone;
-		dev->refcount = 1;
-		
-		hash_table_insert(&devices, key, &dev->link);
-	} else {
-		device_t *dev = hash_table_get_instance(lnk, device_t, link);
-		dev->refcount++;
-	}
-	fibril_mutex_unlock(&devices_mutex);
-	
-	ipc_answer_3(rid, EOK, 0, 1, L_FILE);
-}
-
-void devfs_stat(ipc_callid_t rid, ipc_call_t *request)
-{
-	dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_read_receive(&callid, &size) ||
-	    size != sizeof(struct stat)) {
-		ipc_answer_0(callid, EINVAL);
-		ipc_answer_0(rid, EINVAL);
-		return;
-	}
-
-	struct stat stat;
-	memset(&stat, 0, sizeof(struct stat));
-
-	stat.fs_handle = devfs_reg.fs_handle;
-	stat.dev_handle = dev_handle;
-	stat.index = index;
-	stat.lnkcnt = 1;
-	stat.is_file = (index != 0);
-	stat.size = 0;
-	
-	if (index != 0) {
-		unsigned long key[] = {
-			[DEVICES_KEY_HANDLE] = (unsigned long) index
-		};
-		
-		fibril_mutex_lock(&devices_mutex);
-		link_t *lnk = hash_table_find(&devices, key);
-		if (lnk != NULL) 
-			stat.devfs_stat.device = (dev_handle_t)index;
-		fibril_mutex_unlock(&devices_mutex);
-	}
-
-	async_data_read_finalize(callid, &stat, sizeof(struct stat));
-	ipc_answer_0(rid, EOK);
-}
-
-void devfs_read(ipc_callid_t rid, ipc_call_t *request)
-{
-	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
-	off_t pos = (off_t) IPC_GET_ARG3(*request);
-	
-	if (index != 0) {
+			free(desc);
+		}
+		
+		ipc_answer_0(callid, ENOENT);
+		ipc_answer_1(rid, ENOENT, 0);
+		return;
+	}
+	
+	devmap_handle_type_t type = devmap_handle_probe(index);
+	
+	if (type == DEV_HANDLE_NAMESPACE) {
+		/* Namespace directory */
+		ipc_callid_t callid;
+		size_t size;
+		if (!async_data_read_receive(&callid, &size)) {
+			ipc_answer_0(callid, EINVAL);
+			ipc_answer_0(rid, EINVAL);
+			return;
+		}
+		
+		dev_desc_t *desc;
+		size_t count = devmap_get_devices(index, &desc);
+		
+		if (pos < count) {
+			async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
+			free(desc);
+			ipc_answer_1(rid, EOK, 1);
+			return;
+		}
+		
+		free(desc);
+		ipc_answer_0(callid, ENOENT);
+		ipc_answer_1(rid, ENOENT, 0);
+		return;
+	}
+	
+	if (type == DEV_HANDLE_DEVICE) {
+		/* Device node */
+		
 		unsigned long key[] = {
 			[DEVICES_KEY_HANDLE] = (unsigned long) index
@@ -364,35 +577,8 @@
 		/* Driver reply is the final result of the whole operation */
 		ipc_answer_1(rid, rc, bytes);
-	} else {
-		ipc_callid_t callid;
-		size_t size;
-		if (!async_data_read_receive(&callid, &size)) {
-			ipc_answer_0(callid, EINVAL);
-			ipc_answer_0(rid, EINVAL);
-			return;
-		}
-		
-		size_t count = devmap_device_get_count();
-		dev_desc_t *desc = malloc(count * sizeof(dev_desc_t));
-		if (desc == NULL) {
-			ipc_answer_0(callid, ENOMEM);
-			ipc_answer_1(rid, ENOMEM, 0);
-			return;
-		}
-		
-		size_t max = devmap_device_get_devices(count, desc);
-		
-		if (pos < max) {
-			async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
-		} else {
-			ipc_answer_0(callid, ENOENT);
-			ipc_answer_1(rid, ENOENT, 0);
-			return;
-		}
-		
-		free(desc);
-		
-		ipc_answer_1(rid, EOK, 1);
-	}
+		return;
+	}
+	
+	ipc_answer_0(rid, ENOENT);
 }
 
@@ -402,5 +588,19 @@
 	off_t pos = (off_t) IPC_GET_ARG3(*request);
 	
-	if (index != 0) {
+	if (index == 0) {
+		ipc_answer_0(rid, ENOTSUP);
+		return;
+	}
+	
+	devmap_handle_type_t type = devmap_handle_probe(index);
+	
+	if (type == DEV_HANDLE_NAMESPACE) {
+		/* Namespace directory */
+		ipc_answer_0(rid, ENOTSUP);
+		return;
+	}
+	
+	if (type == DEV_HANDLE_DEVICE) {
+		/* Device node */
 		unsigned long key[] = {
 			[DEVICES_KEY_HANDLE] = (unsigned long) index
@@ -443,8 +643,8 @@
 		/* Driver reply is the final result of the whole operation */
 		ipc_answer_1(rid, rc, bytes);
-	} else {
-		/* Read-only filesystem */
-		ipc_answer_0(rid, ENOTSUP);
-	}
+		return;
+	}
+	
+	ipc_answer_0(rid, ENOENT);
 }
 
@@ -458,5 +658,18 @@
 	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
 	
-	if (index != 0) {
+	if (index == 0) {
+		ipc_answer_0(rid, EOK);
+		return;
+	}
+	
+	devmap_handle_type_t type = devmap_handle_probe(index);
+	
+	if (type == DEV_HANDLE_NAMESPACE) {
+		/* Namespace directory */
+		ipc_answer_0(rid, EOK);
+		return;
+	}
+	
+	if (type == DEV_HANDLE_DEVICE) {
 		unsigned long key[] = {
 			[DEVICES_KEY_HANDLE] = (unsigned long) index
@@ -482,6 +695,8 @@
 		
 		ipc_answer_0(rid, EOK);
-	} else
-		ipc_answer_0(rid, ENOTSUP);
+		return;
+	}
+	
+	ipc_answer_0(rid, ENOENT);
 }
 
@@ -490,5 +705,18 @@
 	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
 	
-	if (index != 0) {
+	if (index == 0) {
+		ipc_answer_0(rid, EOK);
+		return;
+	}
+	
+	devmap_handle_type_t type = devmap_handle_probe(index);
+	
+	if (type == DEV_HANDLE_NAMESPACE) {
+		/* Namespace directory */
+		ipc_answer_0(rid, EOK);
+		return;
+	}
+	
+	if (type == DEV_HANDLE_DEVICE) {
 		unsigned long key[] = {
 			[DEVICES_KEY_HANDLE] = (unsigned long) index
@@ -518,6 +746,8 @@
 		/* Driver reply is the final result of the whole operation */
 		ipc_answer_0(rid, rc);
-	} else
-		ipc_answer_0(rid, ENOTSUP);
+		return;
+	}
+	
+	ipc_answer_0(rid, ENOENT);
 }
 
