Index: uspace/srv/fs/exfat/Makefile
===================================================================
--- uspace/srv/fs/exfat/Makefile	(revision fcc3cd810dbe367d24b2ef35689ccd02cec2f405)
+++ uspace/srv/fs/exfat/Makefile	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
@@ -35,5 +35,7 @@
 SOURCES = \
 	exfat.c \
+	exfat_fat.c \
 	exfat_ops.c \
+	exfat_idx.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/fs/exfat/exfat.c
===================================================================
--- uspace/srv/fs/exfat/exfat.c	(revision fcc3cd810dbe367d24b2ef35689ccd02cec2f405)
+++ uspace/srv/fs/exfat/exfat.c	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
@@ -158,9 +158,9 @@
 
 	printf(NAME ": HelenOS exFAT file system server\n");
-/*
+
 	rc = exfat_idx_init();
 	if (rc != EOK)
 		goto err;
-*/
+
 	vfs_phone = service_connect_blocking(SERVICE_VFS, 0, 0);
 	if (vfs_phone < EOK) {
@@ -171,5 +171,5 @@
 	rc = fs_register(vfs_phone, &exfat_reg, &exfat_vfs_info, exfat_connection);
 	if (rc != EOK) {
-		/* exfat_idx_fini(); */
+		exfat_idx_fini();
 		goto err;
 	}
Index: uspace/srv/fs/exfat/exfat.h
===================================================================
--- uspace/srv/fs/exfat/exfat.h	(revision fcc3cd810dbe367d24b2ef35689ccd02cec2f405)
+++ uspace/srv/fs/exfat/exfat.h	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
@@ -35,4 +35,5 @@
 #define EXFAT_FAT_H_
 
+#include "exfat_fat.h"
 #include <fibril_synch.h>
 #include <libfs.h>
@@ -58,5 +59,5 @@
 #define DATA_CNT(bs)	uint32_t_le2host(bs->data_clusters)
 #define ROOT_ST(bs)		uint32_t_le2host(bs->rootdir_cluster)
-#define VOL_FLAGS		uint16_t_le2host(bs->volume_flags)
+#define VOL_FLAGS(bs)	uint16_t_le2host(bs->volume_flags)
 
 
@@ -88,4 +89,67 @@
 } __attribute__((__packed__)) exfat_bs_t;
 
+typedef enum {
+	EXFAT_DIRECTORY,
+	EXFAT_FILE
+} exfat_node_type_t;
+
+struct exfat_node;
+
+typedef struct {
+	/** Used indices (position) hash table link. */
+	link_t		uph_link;
+	/** Used indices (index) hash table link. */
+	link_t		uih_link;
+
+	fibril_mutex_t	lock;
+	devmap_handle_t	devmap_handle;
+	fs_index_t	index;
+	/**
+	 * Parent node's first cluster.
+	 * Zero is used if this node is not linked, in which case nodep must
+	 * contain a pointer to the in-core node structure.
+	 * One is used when the parent is the root directory.
+	 */
+	exfat_cluster_t	pfc;
+	/** Directory entry index within the parent node. */
+	unsigned	pdi;
+	/** Pointer to in-core node instance. */
+	struct exfat_node	*nodep;
+} exfat_idx_t;
+
+/** exFAT in-core node. */
+typedef struct exfat_node {
+	/** Back pointer to the FS node. */
+	fs_node_t		*bp;
+	
+	fibril_mutex_t		lock;
+	exfat_node_type_t	type;
+	exfat_idx_t			*idx;
+	/**
+	 *  Node's first cluster.
+	 *  Zero is used for zero-length nodes.
+	 *  One is used to mark root directory.
+	 */
+	exfat_cluster_t		firstc;
+	/** exFAT in-core node free list link. */
+	link_t			ffn_link;
+	aoff64_t		size;
+	unsigned		lnkcnt;
+	unsigned		refcnt;
+	bool			dirty;
+
+	/*
+	 * Cache of the node's last and "current" cluster to avoid some
+	 * unnecessary FAT walks.
+	 */
+	/* Node's last cluster in FAT. */
+	bool		lastc_cached_valid;
+	exfat_cluster_t	lastc_cached_value;
+	/* Node's "current" cluster, i.e. where the last I/O took place. */
+	bool		currc_cached_valid;
+	aoff64_t	currc_cached_bn;
+	exfat_cluster_t	currc_cached_value;
+} exfat_node_t;
+
 
 extern fs_reg_t exfat_reg;
@@ -95,4 +159,26 @@
 extern void exfat_unmounted(ipc_callid_t, ipc_call_t *);
 extern void exfat_unmount(ipc_callid_t, ipc_call_t *);
+extern void exfat_lookup(ipc_callid_t, ipc_call_t *);
+extern void exfat_read(ipc_callid_t, ipc_call_t *);
+extern void exfat_write(ipc_callid_t, ipc_call_t *);
+extern void exfat_truncate(ipc_callid_t, ipc_call_t *);
+extern void exfat_stat(ipc_callid_t, ipc_call_t *);
+extern void exfat_close(ipc_callid_t, ipc_call_t *);
+extern void exfat_destroy(ipc_callid_t, ipc_call_t *);
+extern void exfat_open_node(ipc_callid_t, ipc_call_t *);
+extern void exfat_stat(ipc_callid_t, ipc_call_t *);
+extern void exfat_sync(ipc_callid_t, ipc_call_t *);
+
+extern int exfat_idx_get_new(exfat_idx_t **, devmap_handle_t);
+extern exfat_idx_t *exfat_idx_get_by_pos(devmap_handle_t, exfat_cluster_t, unsigned);
+extern exfat_idx_t *exfat_idx_get_by_index(devmap_handle_t, fs_index_t);
+extern void exfat_idx_destroy(exfat_idx_t *);
+extern void exfat_idx_hashin(exfat_idx_t *);
+extern void exfat_idx_hashout(exfat_idx_t *);
+
+extern int exfat_idx_init(void);
+extern void exfat_idx_fini(void);
+extern int exfat_idx_init_by_devmap_handle(devmap_handle_t);
+extern void exfat_idx_fini_by_devmap_handle(devmap_handle_t);
 
 #endif
Index: uspace/srv/fs/exfat/exfat_fat.c
===================================================================
--- uspace/srv/fs/exfat/exfat_fat.c	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
+++ uspace/srv/fs/exfat/exfat_fat.c	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+/**
+ * @file	exfat_fat.c
+ * @brief	Functions that manipulate the File Allocation Tables.
+ */
+
+#include "exfat_fat.h"
+#include "exfat.h"
+#include "../../vfs/vfs.h"
+#include <libfs.h>
+#include <libblock.h>
+#include <errno.h>
+#include <byteorder.h>
+#include <align.h>
+#include <assert.h>
+#include <fibril_synch.h>
+#include <malloc.h>
+#include <mem.h>
+
+
+/**
+ * The fat_alloc_lock mutex protects all copies of the File Allocation Table
+ * during allocation of clusters. The lock does not have to be held durring
+ * deallocation of clusters.
+ */
+static FIBRIL_MUTEX_INITIALIZE(fat_alloc_lock);
+
+
+
+/** Perform basic sanity checks on the file system.
+ *
+ * Verify if values of boot sector fields are sane. Also verify media
+ * descriptor. This is used to rule out cases when a device obviously
+ * does not contain a exfat file system.
+ */
+int exfat_sanity_check(exfat_bs_t *bs, devmap_handle_t devmap_handle)
+{
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_fat.h
===================================================================
--- uspace/srv/fs/exfat/exfat_fat.h	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
+++ uspace/srv/fs/exfat/exfat_fat.h	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */
+
+#ifndef EXFAT_FAT_FAT_H_
+#define EXFAT_FAT_FAT_H_
+
+#include "../../vfs/vfs.h"
+#include <stdint.h>
+#include <libblock.h>
+
+
+/* forward declarations */
+struct block;
+struct exfat_node;
+struct exfat_bs;
+
+typedef uint32_t exfat_cluster_t;
+
+
+extern int exfat_sanity_check(struct exfat_bs *, devmap_handle_t);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_idx.c
===================================================================
--- uspace/srv/fs/exfat/exfat_idx.c	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
+++ uspace/srv/fs/exfat/exfat_idx.c	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2008 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup fs
+ * @{
+ */ 
+
+/**
+ * @file	exfat_idx.c
+ * @brief	Layer for translating exFAT entities to VFS node indices.
+ */
+
+#include "exfat.h"
+#include "../../vfs/vfs.h"
+#include <errno.h>
+#include <str.h>
+#include <adt/hash_table.h>
+#include <adt/list.h>
+#include <assert.h>
+#include <fibril_synch.h>
+#include <malloc.h>
+
+/** Each instance of this type describes one interval of freed VFS indices. */
+typedef struct {
+	link_t		link;
+	fs_index_t	first;
+	fs_index_t	last;
+} freed_t;
+
+/**
+ * Each instance of this type describes state of all VFS indices that
+ * are currently unused.
+ */
+typedef struct {
+	link_t		link;
+	devmap_handle_t	devmap_handle;
+
+	/** Next unassigned index. */
+	fs_index_t	next;
+	/** Number of remaining unassigned indices. */
+	uint64_t	remaining;
+
+	/** Sorted list of intervals of freed indices. */
+	link_t		freed_head;
+} unused_t;
+
+/** Mutex protecting the list of unused structures. */
+static FIBRIL_MUTEX_INITIALIZE(unused_lock);
+
+/** List of unused structures. */
+static LIST_INITIALIZE(unused_head);
+
+static void unused_initialize(unused_t *u, devmap_handle_t devmap_handle)
+{
+	link_initialize(&u->link);
+	u->devmap_handle = devmap_handle;
+	u->next = 0;
+	u->remaining = ((uint64_t)((fs_index_t)-1)) + 1;
+	list_initialize(&u->freed_head);
+}
+
+static unused_t *unused_find(devmap_handle_t devmap_handle, bool lock)
+{
+	unused_t *u;
+	link_t *l;
+
+	if (lock)
+		fibril_mutex_lock(&unused_lock);
+	for (l = unused_head.next; l != &unused_head; l = l->next) {
+		u = list_get_instance(l, unused_t, link);
+		if (u->devmap_handle == devmap_handle) 
+			return u;
+	}
+	if (lock)
+		fibril_mutex_unlock(&unused_lock);
+	return NULL;
+}
+
+/** Mutex protecting the up_hash and ui_hash. */
+static FIBRIL_MUTEX_INITIALIZE(used_lock);
+
+/**
+ * Global hash table of all used exfat_idx_t structures.
+ * The index structures are hashed by the devmap_handle, parent node's first
+ * cluster and index within the parent directory.
+ */ 
+static hash_table_t up_hash;
+
+#define UPH_BUCKETS_LOG	12
+#define UPH_BUCKETS	(1 << UPH_BUCKETS_LOG)
+
+#define UPH_DH_KEY	0
+#define UPH_PFC_KEY	1
+#define UPH_PDI_KEY	2
+
+static hash_index_t pos_hash(unsigned long key[])
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t)key[UPH_DH_KEY];
+	exfat_cluster_t pfc = (exfat_cluster_t)key[UPH_PFC_KEY];
+	unsigned pdi = (unsigned)key[UPH_PDI_KEY];
+
+	hash_index_t h;
+
+	/*
+	 * The least significant half of all bits are the least significant bits
+	 * of the parent node's first cluster.
+	 *
+	 * The least significant half of the most significant half of all bits
+	 * are the least significant bits of the node's dentry index within the
+	 * parent directory node.
+	 *
+	 * The most significant half of the most significant half of all bits
+	 * are the least significant bits of the device handle.
+	 */
+	h = pfc & ((1 << (UPH_BUCKETS_LOG / 2)) - 1);
+	h |= (pdi & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
+	    (UPH_BUCKETS_LOG / 2); 
+	h |= (devmap_handle & ((1 << (UPH_BUCKETS_LOG / 4)) - 1)) <<
+	    (3 * (UPH_BUCKETS_LOG / 4));
+
+	return h;
+}
+
+static int pos_compare(unsigned long key[], hash_count_t keys, link_t *item)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t)key[UPH_DH_KEY];
+	exfat_cluster_t pfc;
+	unsigned pdi;
+	exfat_idx_t *fidx = list_get_instance(item, exfat_idx_t, uph_link);
+
+	switch (keys) {
+	case 1:
+		return (devmap_handle == fidx->devmap_handle);
+	case 3:
+		pfc = (exfat_cluster_t) key[UPH_PFC_KEY];
+		pdi = (unsigned) key[UPH_PDI_KEY];
+		return (devmap_handle == fidx->devmap_handle) && (pfc == fidx->pfc) &&
+		    (pdi == fidx->pdi);
+	default:
+		assert((keys == 1) || (keys == 3));
+	}
+
+	return 0;
+}
+
+static void pos_remove_callback(link_t *item)
+{
+	/* nothing to do */
+}
+
+static hash_table_operations_t uph_ops = {
+	.hash = pos_hash,
+	.compare = pos_compare,
+	.remove_callback = pos_remove_callback,
+};
+
+/**
+ * Global hash table of all used fat_idx_t structures.
+ * The index structures are hashed by the devmap_handle and index.
+ */
+static hash_table_t ui_hash;
+
+#define UIH_BUCKETS_LOG	12
+#define UIH_BUCKETS	(1 << UIH_BUCKETS_LOG)
+
+#define UIH_DH_KEY	0
+#define UIH_INDEX_KEY	1
+
+static hash_index_t idx_hash(unsigned long key[])
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t)key[UIH_DH_KEY];
+	fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
+
+	hash_index_t h;
+
+	h = devmap_handle & ((1 << (UIH_BUCKETS_LOG / 2)) - 1);
+	h |= (index & ((1 << (UIH_BUCKETS_LOG / 2)) - 1)) <<
+	    (UIH_BUCKETS_LOG / 2);
+
+	return h;
+}
+
+static int idx_compare(unsigned long key[], hash_count_t keys, link_t *item)
+{
+	devmap_handle_t devmap_handle = (devmap_handle_t)key[UIH_DH_KEY];
+	fs_index_t index;
+	exfat_idx_t *fidx = list_get_instance(item, exfat_idx_t, uih_link);
+
+	switch (keys) {
+	case 1:
+		return (devmap_handle == fidx->devmap_handle);
+	case 2:
+		index = (fs_index_t) key[UIH_INDEX_KEY];
+		return (devmap_handle == fidx->devmap_handle) &&
+		    (index == fidx->index);
+	default:
+		assert((keys == 1) || (keys == 2));
+	}
+
+	return 0;
+}
+
+static void idx_remove_callback(link_t *item)
+{
+	exfat_idx_t *fidx = list_get_instance(item, exfat_idx_t, uih_link);
+
+	free(fidx);
+}
+
+static hash_table_operations_t uih_ops = {
+	.hash = idx_hash,
+	.compare = idx_compare,
+	.remove_callback = idx_remove_callback,
+};
+
+/** Allocate a VFS index which is not currently in use. */
+static bool exfat_index_alloc(devmap_handle_t devmap_handle, fs_index_t *index)
+{
+	unused_t *u;
+	
+	assert(index);
+	u = unused_find(devmap_handle, true);
+	if (!u)
+		return false;	
+
+	if (list_empty(&u->freed_head)) {
+		if (u->remaining) { 
+			/*
+			 * There are no freed indices, allocate one directly
+			 * from the counter.
+			 */
+			*index = u->next++;
+			--u->remaining;
+			fibril_mutex_unlock(&unused_lock);
+			return true;
+		}
+	} else {
+		/* There are some freed indices which we can reuse. */
+		freed_t *f = list_get_instance(u->freed_head.next, freed_t,
+		    link);
+		*index = f->first;
+		if (f->first++ == f->last) {
+			/* Destroy the interval. */
+			list_remove(&f->link);
+			free(f);
+		}
+		fibril_mutex_unlock(&unused_lock);
+		return true;
+	}
+	/*
+	 * We ran out of indices, which is extremely unlikely with FAT16, but
+	 * theoretically still possible (e.g. too many open unlinked nodes or
+	 * too many zero-sized nodes).
+	 */
+	fibril_mutex_unlock(&unused_lock);
+	return false;
+}
+
+/** If possible, coalesce two intervals of freed indices. */
+static void try_coalesce_intervals(link_t *l, link_t *r, link_t *cur)
+{
+	freed_t *fl = list_get_instance(l, freed_t, link);
+	freed_t *fr = list_get_instance(r, freed_t, link);
+
+	if (fl->last + 1 == fr->first) {
+		if (cur == l) {
+			fl->last = fr->last;
+			list_remove(r);
+			free(r);
+		} else {
+			fr->first = fl->first;
+			list_remove(l);
+			free(l);
+		}
+	}
+}
+
+/** Free a VFS index, which is no longer in use. */
+static void exfat_index_free(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	unused_t *u;
+
+	u = unused_find(devmap_handle, true);
+	assert(u);
+
+	if (u->next == index + 1) {
+		/* The index can be returned directly to the counter. */
+		u->next--;
+		u->remaining++;
+	} else {
+		/*
+		 * The index must be returned either to an existing freed
+		 * interval or a new interval must be created.
+		 */
+		link_t *lnk;
+		freed_t *n;
+		for (lnk = u->freed_head.next; lnk != &u->freed_head;
+		    lnk = lnk->next) {
+			freed_t *f = list_get_instance(lnk, freed_t, link);
+			if (f->first == index + 1) {
+				f->first--;
+				if (lnk->prev != &u->freed_head)
+					try_coalesce_intervals(lnk->prev, lnk,
+					    lnk);
+				fibril_mutex_unlock(&unused_lock);
+				return;
+			}
+			if (f->last == index - 1) {
+				f->last++;
+				if (lnk->next != &u->freed_head)
+					try_coalesce_intervals(lnk, lnk->next,
+					    lnk);
+				fibril_mutex_unlock(&unused_lock);
+				return;
+			}
+			if (index > f->first) {
+				n = malloc(sizeof(freed_t));
+				/* TODO: sleep until allocation succeeds */
+				assert(n);
+				link_initialize(&n->link);
+				n->first = index;
+				n->last = index;
+				list_insert_before(&n->link, lnk);
+				fibril_mutex_unlock(&unused_lock);
+				return;
+			}
+
+		}
+		/* The index will form the last interval. */
+		n = malloc(sizeof(freed_t));
+		/* TODO: sleep until allocation succeeds */
+		assert(n);
+		link_initialize(&n->link);
+		n->first = index;
+		n->last = index;
+		list_append(&n->link, &u->freed_head);
+	}
+	fibril_mutex_unlock(&unused_lock);
+}
+
+static int exfat_idx_create(exfat_idx_t **fidxp, devmap_handle_t devmap_handle)
+{
+	exfat_idx_t *fidx;
+
+	fidx = (exfat_idx_t *) malloc(sizeof(exfat_idx_t));
+	if (!fidx) 
+		return ENOMEM;
+	if (!exfat_index_alloc(devmap_handle, &fidx->index)) {
+		free(fidx);
+		return ENOSPC;
+	}
+		
+	link_initialize(&fidx->uph_link);
+	link_initialize(&fidx->uih_link);
+	fibril_mutex_initialize(&fidx->lock);
+	fidx->devmap_handle = devmap_handle;
+	fidx->pfc = 0;	/* no parent yet */
+	fidx->pdi = 0;
+	fidx->nodep = NULL;
+
+	*fidxp = fidx;
+	return EOK;
+}
+
+int exfat_idx_get_new(exfat_idx_t **fidxp, devmap_handle_t devmap_handle)
+{
+	exfat_idx_t *fidx;
+	int rc;
+
+	fibril_mutex_lock(&used_lock);
+	rc = exfat_idx_create(&fidx, devmap_handle);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&used_lock);
+		return rc;
+	}
+		
+	unsigned long ikey[] = {
+		[UIH_DH_KEY] = devmap_handle,
+		[UIH_INDEX_KEY] = fidx->index,
+	};
+	
+	hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
+	fibril_mutex_lock(&fidx->lock);
+	fibril_mutex_unlock(&used_lock);
+
+	*fidxp = fidx;
+	return EOK;
+}
+
+exfat_idx_t *
+exfat_idx_get_by_pos(devmap_handle_t devmap_handle, exfat_cluster_t pfc, unsigned pdi)
+{
+	exfat_idx_t *fidx;
+	link_t *l;
+	unsigned long pkey[] = {
+		[UPH_DH_KEY] = devmap_handle,
+		[UPH_PFC_KEY] = pfc,
+		[UPH_PDI_KEY] = pdi,
+	};
+
+	fibril_mutex_lock(&used_lock);
+	l = hash_table_find(&up_hash, pkey);
+	if (l) {
+		fidx = hash_table_get_instance(l, exfat_idx_t, uph_link);
+	} else {
+		int rc;
+
+		rc = exfat_idx_create(&fidx, devmap_handle);
+		if (rc != EOK) {
+			fibril_mutex_unlock(&used_lock);
+			return NULL;
+		}
+		
+		unsigned long ikey[] = {
+			[UIH_DH_KEY] = devmap_handle,
+			[UIH_INDEX_KEY] = fidx->index,
+		};
+	
+		fidx->pfc = pfc;
+		fidx->pdi = pdi;
+
+		hash_table_insert(&up_hash, pkey, &fidx->uph_link);
+		hash_table_insert(&ui_hash, ikey, &fidx->uih_link);
+	}
+	fibril_mutex_lock(&fidx->lock);
+	fibril_mutex_unlock(&used_lock);
+
+	return fidx;
+}
+
+void exfat_idx_hashin(exfat_idx_t *idx)
+{
+	unsigned long pkey[] = {
+		[UPH_DH_KEY] = idx->devmap_handle,
+		[UPH_PFC_KEY] = idx->pfc,
+		[UPH_PDI_KEY] = idx->pdi,
+	};
+
+	fibril_mutex_lock(&used_lock);
+	hash_table_insert(&up_hash, pkey, &idx->uph_link);
+	fibril_mutex_unlock(&used_lock);
+}
+
+void exfat_idx_hashout(exfat_idx_t *idx)
+{
+	unsigned long pkey[] = {
+		[UPH_DH_KEY] = idx->devmap_handle,
+		[UPH_PFC_KEY] = idx->pfc,
+		[UPH_PDI_KEY] = idx->pdi,
+	};
+
+	fibril_mutex_lock(&used_lock);
+	hash_table_remove(&up_hash, pkey, 3);
+	fibril_mutex_unlock(&used_lock);
+}
+
+exfat_idx_t *
+exfat_idx_get_by_index(devmap_handle_t devmap_handle, fs_index_t index)
+{
+	exfat_idx_t *fidx = NULL;
+	link_t *l;
+	unsigned long ikey[] = {
+		[UIH_DH_KEY] = devmap_handle,
+		[UIH_INDEX_KEY] = index,
+	};
+
+	fibril_mutex_lock(&used_lock);
+	l = hash_table_find(&ui_hash, ikey);
+	if (l) {
+		fidx = hash_table_get_instance(l, exfat_idx_t, uih_link);
+		fibril_mutex_lock(&fidx->lock);
+	}
+	fibril_mutex_unlock(&used_lock);
+
+	return fidx;
+}
+
+/** Destroy the index structure.
+ *
+ * @param idx		The index structure to be destroyed.
+ */
+void exfat_idx_destroy(exfat_idx_t *idx)
+{
+	unsigned long ikey[] = {
+		[UIH_DH_KEY] = idx->devmap_handle,
+		[UIH_INDEX_KEY] = idx->index,
+	};
+	devmap_handle_t devmap_handle = idx->devmap_handle;
+	fs_index_t index = idx->index;
+
+	/* TODO: assert(idx->pfc == FAT_CLST_RES0); */
+	assert(idx->pfc == 0);
+
+	fibril_mutex_lock(&used_lock);
+	/*
+	 * Since we can only free unlinked nodes, the index structure is not
+	 * present in the position hash (uph). We therefore hash it out from
+	 * the index hash only.
+	 */
+	hash_table_remove(&ui_hash, ikey, 2);
+	fibril_mutex_unlock(&used_lock);
+	/* Release the VFS index. */
+	exfat_index_free(devmap_handle, index);
+	/* The index structure itself is freed in idx_remove_callback(). */
+}
+
+int exfat_idx_init(void)
+{
+	if (!hash_table_create(&up_hash, UPH_BUCKETS, 3, &uph_ops)) 
+		return ENOMEM;
+	if (!hash_table_create(&ui_hash, UIH_BUCKETS, 2, &uih_ops)) {
+		hash_table_destroy(&up_hash);
+		return ENOMEM;
+	}
+	return EOK;
+}
+
+void exfat_idx_fini(void)
+{
+	/* We assume the hash tables are empty. */
+	hash_table_destroy(&up_hash);
+	hash_table_destroy(&ui_hash);
+}
+
+int exfat_idx_init_by_devmap_handle(devmap_handle_t devmap_handle)
+{
+	unused_t *u;
+	int rc = EOK;
+
+	u = (unused_t *) malloc(sizeof(unused_t));
+	if (!u)
+		return ENOMEM;
+	unused_initialize(u, devmap_handle);
+	fibril_mutex_lock(&unused_lock);
+	if (!unused_find(devmap_handle, false)) {
+		list_append(&u->link, &unused_head);
+	} else {
+		free(u);
+		rc = EEXIST;
+	}
+	fibril_mutex_unlock(&unused_lock);
+	return rc;
+}
+
+void exfat_idx_fini_by_devmap_handle(devmap_handle_t devmap_handle)
+{
+	unsigned long ikey[] = {
+		[UIH_DH_KEY] = devmap_handle
+	};
+	unsigned long pkey[] = {
+		[UPH_DH_KEY] = devmap_handle
+	};
+
+	/*
+	 * Remove this instance's index structure from up_hash and ui_hash.
+	 * Process up_hash first and ui_hash second because the index structure
+	 * is actually removed in idx_remove_callback(). 
+	 */
+	fibril_mutex_lock(&used_lock);
+	hash_table_remove(&up_hash, pkey, 1);
+	hash_table_remove(&ui_hash, ikey, 1);
+	fibril_mutex_unlock(&used_lock);
+
+	/*
+	 * Free the unused and freed structures for this instance.
+	 */
+	unused_t *u = unused_find(devmap_handle, true);
+	assert(u);
+	list_remove(&u->link);
+	fibril_mutex_unlock(&unused_lock);
+
+	while (!list_empty(&u->freed_head)) {
+		freed_t *f;
+		f = list_get_instance(u->freed_head.next, freed_t, link);
+		list_remove(&f->link);
+		free(f);
+	}
+	free(u); 
+}
+
+/**
+ * @}
+ */ 
Index: uspace/srv/fs/exfat/exfat_ops.c
===================================================================
--- uspace/srv/fs/exfat/exfat_ops.c	(revision fcc3cd810dbe367d24b2ef35689ccd02cec2f405)
+++ uspace/srv/fs/exfat/exfat_ops.c	(revision 4c3c4a59feab5e527616518e61e77606aae07ba6)
@@ -38,4 +38,5 @@
 
 #include "exfat.h"
+#include "exfat_fat.h"
 #include "../../vfs/vfs.h"
 #include <libfs.h>
@@ -55,9 +56,224 @@
 #include <align.h>
 #include <malloc.h>
+#include <stdio.h>
+
+#define EXFAT_NODE(node)	((node) ? (exfat_node_t *) (node)->data : NULL)
+#define FS_NODE(node)	((node) ? (node)->bp : NULL)
+
+
+/** Mutex protecting the list of cached free FAT nodes. */
+static FIBRIL_MUTEX_INITIALIZE(ffn_mutex);
+
+/** List of cached free FAT nodes. */
+static LIST_INITIALIZE(ffn_head);
+
+/*
+ * Forward declarations of FAT libfs operations.
+ */
+static int exfat_root_get(fs_node_t **, devmap_handle_t);
+static int exfat_match(fs_node_t **, fs_node_t *, const char *);
+static int exfat_node_get(fs_node_t **, devmap_handle_t, fs_index_t);
+static int exfat_node_open(fs_node_t *);
+static int exfat_node_put(fs_node_t *);
+static int exfat_create_node(fs_node_t **, devmap_handle_t, int);
+static int exfat_destroy_node(fs_node_t *);
+static int exfat_link(fs_node_t *, fs_node_t *, const char *);
+static int exfat_unlink(fs_node_t *, fs_node_t *, const char *);
+static int exfat_has_children(bool *, fs_node_t *);
+static fs_index_t exfat_index_get(fs_node_t *);
+static aoff64_t exfat_size_get(fs_node_t *);
+static unsigned exfat_lnkcnt_get(fs_node_t *);
+static char exfat_plb_get_char(unsigned);
+static bool exfat_is_directory(fs_node_t *);
+static bool exfat_is_file(fs_node_t *node);
+static devmap_handle_t exfat_device_get(fs_node_t *node);
+
+/*
+ * Helper functions.
+ */
+static void exfat_node_initialize(exfat_node_t *node)
+{
+	fibril_mutex_initialize(&node->lock);
+	node->bp = NULL;
+	node->idx = NULL;
+	node->type = 0;
+	link_initialize(&node->ffn_link);
+	node->size = 0;
+	node->lnkcnt = 0;
+	node->refcnt = 0;
+	node->dirty = false;
+	node->lastc_cached_valid = false;
+	node->lastc_cached_value = 0;
+	node->currc_cached_valid = false;
+	node->currc_cached_bn = 0;
+	node->currc_cached_value = 0;
+}
+
+static int exfat_node_sync(exfat_node_t *node)
+{
+	return EOK;
+}
+
+static int exfat_node_fini_by_devmap_handle(devmap_handle_t devmap_handle)
+{
+	link_t *lnk;
+	exfat_node_t *nodep;
+	int rc;
+
+	/*
+	 * We are called from fat_unmounted() and assume that there are already
+	 * no nodes belonging to this instance with non-zero refcount. Therefore
+	 * it is sufficient to clean up only the FAT free node list.
+	 */
+
+restart:
+	fibril_mutex_lock(&ffn_mutex);
+	for (lnk = ffn_head.next; lnk != &ffn_head; lnk = lnk->next) {
+		nodep = list_get_instance(lnk, exfat_node_t, ffn_link);
+		if (!fibril_mutex_trylock(&nodep->lock)) {
+			fibril_mutex_unlock(&ffn_mutex);
+			goto restart;
+		}
+		if (!fibril_mutex_trylock(&nodep->idx->lock)) {
+			fibril_mutex_unlock(&nodep->lock);
+			fibril_mutex_unlock(&ffn_mutex);
+			goto restart;
+		}
+		if (nodep->idx->devmap_handle != devmap_handle) {
+			fibril_mutex_unlock(&nodep->idx->lock);
+			fibril_mutex_unlock(&nodep->lock);
+			continue;
+		}
+
+		list_remove(&nodep->ffn_link);
+		fibril_mutex_unlock(&ffn_mutex);
+
+		/*
+		 * We can unlock the node and its index structure because we are
+		 * the last player on this playground and VFS is preventing new
+		 * players from entering.
+		 */
+		fibril_mutex_unlock(&nodep->idx->lock);
+		fibril_mutex_unlock(&nodep->lock);
+
+		if (nodep->dirty) {
+			rc = exfat_node_sync(nodep);
+			if (rc != EOK)
+				return rc;
+		}
+		nodep->idx->nodep = NULL;
+		free(nodep->bp);
+		free(nodep);
+
+		/* Need to restart because we changed the ffn_head list. */
+		goto restart;
+	}
+	fibril_mutex_unlock(&ffn_mutex);
+
+	return EOK;
+}
+
+
+/*
+ * FAT libfs operations.
+ */
+
+int exfat_root_get(fs_node_t **rfn, devmap_handle_t devmap_handle)
+{
+	return exfat_node_get(rfn, devmap_handle, 0);
+}
+
+int exfat_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
+{
+	*rfn = NULL;
+	return EOK;
+}
+
+/** Instantiate a exFAT in-core node. */
+int exfat_node_get(fs_node_t **rfn, devmap_handle_t devmap_handle, fs_index_t index)
+{
+	*rfn = NULL;
+	return EOK;
+}
+
+int exfat_node_open(fs_node_t *fn)
+{
+	/*
+	 * Opening a file is stateless, nothing
+	 * to be done here.
+	 */
+	return EOK;
+}
+
+int exfat_node_put(fs_node_t *fn)
+{
+	return EOK;
+}
+
+int exfat_create_node(fs_node_t **rfn, devmap_handle_t devmap_handle, int flags)
+{
+	*rfn = NULL;
+	return EOK;
+}
+
+int exfat_destroy_node(fs_node_t *fn)
+{
+	return EOK;
+}
+
+int exfat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
+{
+	return EOK;
+}
+
+int exfat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
+{
+	return EOK;
+}
+
+int exfat_has_children(bool *has_children, fs_node_t *fn)
+{
+	*has_children = false;
+	return EOK;
+}
+
+
+fs_index_t exfat_index_get(fs_node_t *fn)
+{
+	return EXFAT_NODE(fn)->idx->index;
+}
+
+aoff64_t exfat_size_get(fs_node_t *fn)
+{
+	return EXFAT_NODE(fn)->size;
+}
+
+unsigned exfat_lnkcnt_get(fs_node_t *fn)
+{
+	return EXFAT_NODE(fn)->lnkcnt;
+}
+
+char exfat_plb_get_char(unsigned pos)
+{
+	return exfat_reg.plb_ro[pos % PLB_SIZE];
+}
+
+bool exfat_is_directory(fs_node_t *fn)
+{
+	return EXFAT_NODE(fn)->type == EXFAT_DIRECTORY;
+}
+
+bool exfat_is_file(fs_node_t *fn)
+{
+	return EXFAT_NODE(fn)->type == EXFAT_FILE;
+}
+
+devmap_handle_t exfat_device_get(fs_node_t *node)
+{
+	return 0;
+}
+
 
 /** libfs operations */
-
-libfs_ops_t exfat_libfs_ops;
-/*
 libfs_ops_t exfat_libfs_ops = {
 	.root_get = exfat_root_get,
@@ -79,5 +295,5 @@
 	.device_get = exfat_device_get
 };
-*/
+
 
 /*
@@ -136,6 +352,71 @@
 	}
 
-	async_answer_0(rid, EOK);
-/*	async_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt); */
+	/* Do some simple sanity checks on the file system. */
+	rc = exfat_sanity_check(bs, devmap_handle);
+	if (rc != EOK) {
+		(void) block_cache_fini(devmap_handle);
+		block_fini(devmap_handle);
+		async_answer_0(rid, rc);
+		return;
+	}
+
+	rc = exfat_idx_init_by_devmap_handle(devmap_handle);
+	if (rc != EOK) {
+		(void) block_cache_fini(devmap_handle);
+		block_fini(devmap_handle);
+		async_answer_0(rid, rc);
+		return;
+	}
+
+	/* Initialize the root node. */
+	fs_node_t *rfn = (fs_node_t *)malloc(sizeof(fs_node_t));
+	if (!rfn) {
+		(void) block_cache_fini(devmap_handle);
+		block_fini(devmap_handle);
+		exfat_idx_fini_by_devmap_handle(devmap_handle);
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	fs_node_initialize(rfn);
+	exfat_node_t *rootp = (exfat_node_t *)malloc(sizeof(exfat_node_t));
+	if (!rootp) {
+		free(rfn);
+		(void) block_cache_fini(devmap_handle);
+		block_fini(devmap_handle);
+		exfat_idx_fini_by_devmap_handle(devmap_handle);
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	exfat_node_initialize(rootp);
+
+	/* exfat_idx_t *ridxp = exfat_idx_get_by_pos(devmap_handle, FAT_CLST_ROOTPAR, 0); */
+	exfat_idx_t *ridxp = exfat_idx_get_by_pos(devmap_handle, 0, 0);
+	if (!ridxp) {
+		free(rfn);
+		free(rootp);
+		(void) block_cache_fini(devmap_handle);
+		block_fini(devmap_handle);
+		exfat_idx_fini_by_devmap_handle(devmap_handle);
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+	assert(ridxp->index == 0);
+	/* ridxp->lock held */
+
+	rootp->type = EXFAT_DIRECTORY;
+	rootp->firstc = ROOT_ST(bs);
+	rootp->refcnt = 1;
+	rootp->lnkcnt = 0;	/* FS root is not linked */
+	rootp->idx = ridxp;
+	ridxp->nodep = rootp;
+	rootp->bp = rfn;
+	rfn->data = rootp;
+
+	fibril_mutex_unlock(&ridxp->lock);
+
+	/* async_answer_0(rid, EOK); */
+	async_answer_3(rid, EOK, ridxp->index, rootp->size, rootp->lnkcnt);
 }
 
@@ -148,4 +429,30 @@
 {
 	devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
+	fs_node_t *fn;
+	exfat_node_t *nodep;
+	int rc;
+
+	rc = exfat_root_get(&fn, devmap_handle);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		return;
+	}
+	nodep = EXFAT_NODE(fn);
+
+	/*
+	 * We expect exactly two references on the root node. One for the
+	 * fat_root_get() above and one created in fat_mounted().
+	 */
+	if (nodep->refcnt != 2) {
+		(void) exfat_node_put(fn);
+		async_answer_0(rid, EBUSY);
+		return;
+	}
+
+	/*
+	 * Put the root node and force it to the FAT free node list.
+	 */
+	(void) exfat_node_put(fn);
+	(void) exfat_node_put(fn);
 
 	/*
@@ -154,4 +461,6 @@
 	 * stop using libblock for this instance.
 	 */
+	(void) exfat_node_fini_by_devmap_handle(devmap_handle);
+	exfat_idx_fini_by_devmap_handle(devmap_handle);
 	(void) block_cache_fini(devmap_handle);
 	block_fini(devmap_handle);
