Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ boot/Makefile.common	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -102,4 +102,5 @@
 	$(USPACE_PATH)/srv/fs/tmpfs/tmpfs \
 	$(USPACE_PATH)/srv/fs/fat/fat \
+	$(USPACE_PATH)/srv/fs/exfat/exfat \
 	$(USPACE_PATH)/srv/fs/ext2fs/ext2fs \
 	$(USPACE_PATH)/srv/taskmon/taskmon \
@@ -148,4 +149,6 @@
 	$(USPACE_PATH)/app/dload/dload \
 	$(USPACE_PATH)/app/edit/edit \
+	$(USPACE_PATH)/app/filecrc/filecrc \
+	$(USPACE_PATH)/app/filegen/filegen \
 	$(USPACE_PATH)/app/ext2info/ext2info \
 	$(USPACE_PATH)/app/kill/kill \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -39,4 +39,6 @@
 	app/devctl \
 	app/edit \
+	app/filecrc \
+	app/filegen \
 	app/ext2info \
 	app/getterm \
@@ -79,4 +81,5 @@
 	srv/bd/part/guid_part \
 	srv/bd/part/mbr_part \
+	srv/fs/exfat \
 	srv/fs/fat \
 	srv/fs/tmpfs \
Index: uspace/app/filecrc/Makefile
===================================================================
--- uspace/app/filecrc/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filecrc/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2010 Jiri Svoboda
+# 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.
+#
+
+USPACE_PREFIX = ../..
+EXTRA_CFLAGS = -Iinclude
+BINARY = filecrc
+
+SOURCES = \
+	filecrc.c \
+	crc32.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/filecrc/crc32.c
===================================================================
--- uspace/app/filecrc/crc32.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filecrc/crc32.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2000 Bryan Call <bc@fodder.org>
+ * Copyright (c) 2011 Oleg Romanenko
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "crc32.h"
+
+#define BUFFERSIZE 16384	/* (16k) buffer size for reading from the file */
+
+static const uint32_t crctable[256] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+
+int crc32(int fd, uint32_t * main_val)
+{
+    char buf[BUFFERSIZE];
+    char *p;
+    int nr;
+    uint32_t crc = ~0;
+
+    while (1) {
+		if ((nr = read(fd, buf, sizeof(buf))) < 0)
+			break;
+
+		if (nr == 0)
+			break;
+		for (p = buf; nr--; ++p)
+			crc = (crc >> 8) ^ crctable[(crc ^ *p) & 0xff];
+    }
+    if (nr < 0)
+	return 1;
+
+    *main_val = ~crc;
+    return 0;
+}
Index: uspace/app/filecrc/crc32.h
===================================================================
--- uspace/app/filecrc/crc32.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filecrc/crc32.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+
+int crc32(int fd, uint32_t * main_val);
Index: uspace/app/filecrc/filecrc.c
===================================================================
--- uspace/app/filecrc/filecrc.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filecrc/filecrc.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,91 @@
+/*
+ * 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 filecheck
+ * @{
+ */
+
+/**
+ * @file	filecrc.c
+ * @brief	Tool for calculating CRC32 checksum for a file(s)
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include "crc32.h"
+
+#define NAME	"filecrc"
+#define VERSION "0.0.2"
+
+static void print_help(void);
+
+
+int main(int argc, char **argv) {
+
+	if (argc < 2) {
+		print_help();
+		return 0;
+	}
+	
+	int i;
+	for (i = 1; argv[i] != NULL && i < argc; i++) {
+		uint32_t hash = 0;		
+		int fd = open(argv[i], O_RDONLY);
+		if (fd < 0) {
+			printf("Unable to open %s\n", argv[i]);
+			continue;
+		}
+		
+		if (crc32(fd, &hash) == 0) {
+			printf("%s : %x\n", argv[i], hash);
+		}
+		
+		close(fd);
+	}
+
+	return 0;
+}
+
+
+/* Displays help for filecrc */
+static void print_help(void)
+{
+	printf(
+	   "Usage:  %s <file1> [file2] [...]\n",
+	   NAME);
+}
+
+
+/**
+ * @}
+ */
Index: uspace/app/filegen/Makefile
===================================================================
--- uspace/app/filegen/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filegen/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2010 Jiri Svoboda
+# 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.
+#
+
+USPACE_PREFIX = ../..
+EXTRA_CFLAGS = -Iinclude
+BINARY = filegen
+
+SOURCES = \
+	filegen.c \
+	crc32.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/filegen/crc32.c
===================================================================
--- uspace/app/filegen/crc32.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filegen/crc32.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2000 Bryan Call <bc@fodder.org>
+ * Copyright (c) 2011 Oleg Romanenko
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "crc32.h"
+
+static const uint32_t crctable[256] = {
+    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+
+void crc32(char *buf, size_t size, uint32_t * crc)
+{
+    char *p;
+
+	for (p = buf; size--; ++p)
+		*crc = (*crc >> 8) ^ crctable[(*crc ^ *p) & 0xff];
+}
Index: uspace/app/filegen/crc32.h
===================================================================
--- uspace/app/filegen/crc32.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filegen/crc32.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+
+void crc32(char *buf, size_t size, uint32_t * crc);
Index: uspace/app/filegen/filegen.c
===================================================================
--- uspace/app/filegen/filegen.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/app/filegen/filegen.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,119 @@
+/*
+ * 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 filecheck
+ * @{
+ */
+
+/**
+ * @file	filecrc.c
+ * @brief	Tool for generating file with random data
+ *
+ */
+
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include "crc32.h"
+
+#define NAME	"filegen"
+#define VERSION "0.0.1"
+
+#define BUFFERSIZE 256
+
+
+static void print_help(void);
+
+
+int main(int argc, char **argv) {
+	int rc;
+	uint64_t size = 0;
+	
+	if (argc < 3) {
+		print_help();
+		return 0;
+	}
+	
+	int fd = open(argv[1], O_WRONLY | O_CREAT);
+	if (fd < 0) {
+		printf("Unable to open %s for writing\n", argv[1]);
+		return 1;
+	}
+	
+	rc = str_uint64(argv[2], NULL, 10, true, &size);
+	if (rc != EOK) {
+		printf("Cannot convert size to number\n");
+		return 1;
+	}
+	
+	struct timeval tv;	
+	gettimeofday(&tv, NULL);
+	srandom(tv.tv_sec + tv.tv_usec / 100000);	
+
+	uint64_t i=0, pbuf=0;
+	uint32_t crc=~0;
+	char buf[BUFFERSIZE];
+	
+	while (i<size) {
+		pbuf=0;
+		while (i<size && pbuf<BUFFERSIZE) {
+			buf[pbuf] = rand() % 255;
+			i++;
+			pbuf++;
+		}
+		if (pbuf) {
+			crc32(buf, pbuf, &crc);
+			write(fd, buf, pbuf);
+		}
+	}
+	
+	close(fd);
+	crc = ~crc;
+	printf("%s : %x\n", argv[1], crc);
+
+	return 0;
+}
+
+
+/* Displays help for filegen */
+static void print_help(void)
+{
+	printf(
+	   "Usage:  %s <file> <size in bytes>\n",
+	   NAME);
+}
+
+
+/**
+ * @}
+ */
Index: uspace/app/mkfat/fat.h
===================================================================
--- uspace/app/mkfat/fat.h	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/app/mkfat/fat.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -38,6 +38,14 @@
 #define BS_BLOCK		0
 #define BS_SIZE			512
+#define DIRENT_SIZE		32
 
-#define DIRENT_SIZE		32
+#define FAT12_CLST_MAX    4085
+#define FAT16_CLST_MAX    65525
+
+#define FAT12	12
+#define FAT16	16
+#define FAT32	32
+
+#define FAT_SIZE(a) ((a==FAT12)? 1.5 : ( (a==FAT16)? 2 : 4  ) ) 
 
 typedef struct fat_bs {
Index: uspace/app/mkfat/mkfat.c
===================================================================
--- uspace/app/mkfat/mkfat.c	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/app/mkfat/mkfat.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -35,5 +35,5 @@
  * @brief	Tool for creating new FAT file systems.
  *
- * Currently we can only create 16-bit FAT.
+ * Currently we can create 12/16/32-bit FAT.
  */
 
@@ -55,40 +55,37 @@
 #define div_round_up(a, b) (((a) + (b) - 1) / (b))
 
-/** Predefined file-system parameters */
+/** Default file-system parameters */
 enum {
-	sector_size		= 512,
-	sectors_per_cluster	= 8,
-	fat_count		= 2,
-	reserved_clusters	= 2,
-	media_descriptor	= 0xF8 /**< fixed disk */
+	default_sector_size		= 512,
+	default_sectors_per_cluster	= 4,
+	default_fat_count		= 2,
+	default_reserved_clusters	= 2,
+	default_media_descriptor	= 0xF8 /**< fixed disk */
 };
 
 /** Configurable file-system parameters */
 typedef struct fat_cfg {
+	int fat_type; /* FAT12 = 12, FAT16 = 16, FAT32 = 32 */
+	size_t sector_size;
 	uint32_t total_sectors;
 	uint16_t root_ent_max;
-	uint16_t addt_res_sectors;
+	uint32_t addt_res_sectors;
+	uint8_t sectors_per_cluster;
+
+	uint16_t reserved_sectors;
+	uint32_t rootdir_sectors;
+	uint32_t fat_sectors;
+	uint32_t total_clusters;
+	uint8_t fat_count;
 } fat_cfg_t;
 
-/** Derived file-system parameters */
-typedef struct fat_params {
-	struct fat_cfg cfg;
-	uint16_t reserved_sectors;
-	uint16_t rootdir_sectors;
-	uint32_t fat_sectors;
-	uint16_t total_clusters;
-} fat_params_t;
-
 static void syntax_print(void);
 
-static int fat_params_compute(struct fat_cfg const *cfg,
-    struct fat_params *par);
-static int fat_blocks_write(struct fat_params const *par,
-    service_id_t service_id);
-static void fat_bootsec_create(struct fat_params const *par, struct fat_bs *bs);
+static int fat_params_compute(struct fat_cfg *cfg);
+static int fat_blocks_write(struct fat_cfg const *cfg, service_id_t service_id);
+static void fat_bootsec_create(struct fat_cfg const *cfg, struct fat_bs *bs);
 
 int main(int argc, char **argv)
 {
-	struct fat_params par;
 	struct fat_cfg cfg;
 
@@ -96,11 +93,14 @@
 	char *dev_path;
 	service_id_t service_id;
-	size_t block_size;
 	char *endptr;
 	aoff64_t dev_nblocks;
 
+	cfg.sector_size = default_sector_size;
+	cfg.sectors_per_cluster = default_sectors_per_cluster;
+	cfg.fat_count = default_fat_count;
 	cfg.total_sectors = 0;
 	cfg.addt_res_sectors = 0;
 	cfg.root_ent_max = 128;
+	cfg.fat_type = FAT16;
 
 	if (argc < 2) {
@@ -111,5 +111,4 @@
 
 	--argc; ++argv;
-
 	if (str_cmp(*argv, "--size") == 0) {
 		--argc; ++argv;
@@ -130,4 +129,22 @@
 	}
 
+	if (str_cmp(*argv, "--type") == 0) {
+		--argc; ++argv;
+		if (*argv == NULL) {
+			printf(NAME ": Error, argument missing.\n");
+			syntax_print();
+			return 1;
+		}
+
+		cfg.fat_type = strtol(*argv, &endptr, 10);
+		if (*endptr != '\0') {
+			printf(NAME ": Error, invalid argument.\n");
+			syntax_print();
+			return 1;
+		}
+
+		--argc; ++argv;
+	}
+
 	if (argc != 1) {
 		printf(NAME ": Error, unexpected argument.\n");
@@ -137,4 +154,5 @@
 
 	dev_path = *argv;
+	printf("Device: %s\n", dev_path);
 
 	rc = loc_service_get_id(dev_path, &service_id, 0);
@@ -150,5 +168,5 @@
 	}
 
-	rc = block_get_bsize(service_id, &block_size);
+	rc = block_get_bsize(service_id, &cfg.sector_size);
 	if (rc != EOK) {
 		printf(NAME ": Error determining device block size.\n");
@@ -165,6 +183,6 @@
 	}
 
-	if (block_size != 512) {
-		printf(NAME ": Error. Device block size is not 512 bytes.\n");
+	if (cfg.fat_type == FAT12 && cfg.sector_size != 512) {
+		printf(NAME ": Error. Device block size is not 512 bytes for FAT12 file system.\n");
 		return 2;
 	}
@@ -175,7 +193,7 @@
 	}
 
-	printf(NAME ": Creating FAT filesystem on device %s.\n", dev_path);
-
-	rc = fat_params_compute(&cfg, &par);
+	printf(NAME ": Creating FAT%d filesystem on device %s.\n", cfg.fat_type, dev_path);
+
+	rc = fat_params_compute(&cfg);
 	if (rc != EOK) {
 		printf(NAME ": Invalid file-system parameters.\n");
@@ -183,5 +201,5 @@
 	}
 
-	rc = fat_blocks_write(&par, service_id);
+	rc = fat_blocks_write(&cfg, service_id);
 	if (rc != EOK) {
 		printf(NAME ": Error writing device.\n");
@@ -197,5 +215,5 @@
 static void syntax_print(void)
 {
-	printf("syntax: mkfat [--size <num_blocks>] <device_name>\n");
+	printf("syntax: mkfat32 [--size <sectors>] [--type 12|16|32] <device_name>\n");
 }
 
@@ -205,5 +223,5 @@
  * file system params.
  */
-static int fat_params_compute(struct fat_cfg const *cfg, struct fat_params *par)
+static int fat_params_compute(struct fat_cfg *cfg)
 {
 	uint32_t fat_bytes;
@@ -211,22 +229,33 @@
 
 	/*
-         * Make a conservative guess on the FAT size needed for the file
-         * system. The optimum could be potentially smaller since we
-         * do not subtract size of the FAT itself when computing the
-         * size of the data region.
-         */
-
-	par->reserved_sectors = 1 + cfg->addt_res_sectors;
-	par->rootdir_sectors = div_round_up(cfg->root_ent_max * DIRENT_SIZE,
-	    sector_size);
-	non_data_sectors_lb = par->reserved_sectors + par->rootdir_sectors;
-
-	par->total_clusters = div_round_up(cfg->total_sectors - non_data_sectors_lb,
-	    sectors_per_cluster);
-
-	fat_bytes = (par->total_clusters + 2) * 2;
-	par->fat_sectors = div_round_up(fat_bytes, sector_size);
-
-	par->cfg = *cfg;
+     * Make a conservative guess on the FAT size needed for the file
+     * system. The optimum could be potentially smaller since we
+     * do not subtract size of the FAT itself when computing the
+     * size of the data region.
+     */
+
+	if (cfg->fat_type == FAT12)
+		cfg->sectors_per_cluster = 1;
+
+	cfg->reserved_sectors = 1 + cfg->addt_res_sectors;
+	if (cfg->fat_type != FAT32) {
+		cfg->rootdir_sectors = div_round_up(cfg->root_ent_max * DIRENT_SIZE,
+			cfg->sector_size);
+	}
+	else
+		cfg->rootdir_sectors = 0;
+	non_data_sectors_lb = cfg->reserved_sectors + cfg->rootdir_sectors;
+
+	cfg->total_clusters = div_round_up(cfg->total_sectors - non_data_sectors_lb,
+	    cfg->sectors_per_cluster);
+
+	if ((cfg->fat_type == FAT12 && cfg->total_clusters > FAT12_CLST_MAX) ||
+		(cfg->fat_type == FAT16 && (cfg->total_clusters <= FAT12_CLST_MAX ||
+		cfg->total_clusters > FAT16_CLST_MAX)) ||
+	    (cfg->fat_type == FAT32 && cfg->total_clusters <= FAT16_CLST_MAX))
+		return ENOSPC;
+
+	fat_bytes = (cfg->total_clusters + 2) * FAT_SIZE(cfg->fat_type);
+	cfg->fat_sectors = div_round_up(fat_bytes, cfg->sector_size);
 
 	return EOK;
@@ -234,5 +263,5 @@
 
 /** Create file system with the given parameters. */
-static int fat_blocks_write(struct fat_params const *par, service_id_t service_id)
+static int fat_blocks_write(struct fat_cfg const *cfg, service_id_t service_id)
 {
 	aoff64_t addr;
@@ -243,5 +272,5 @@
 	struct fat_bs bs;
 
-	fat_bootsec_create(par, &bs);
+	fat_bootsec_create(cfg, &bs);
 
 	rc = block_write_direct(service_id, BS_BLOCK, 1, &bs);
@@ -251,10 +280,11 @@
 	addr = BS_BLOCK + 1;
 
-	buffer = calloc(sector_size, 1);
+	buffer = calloc(cfg->sector_size, 1);
 	if (buffer == NULL)
 		return ENOMEM;
+	memset(buffer, 0, cfg->sector_size);
 
 	/* Reserved sectors */
-	for (i = 0; i < par->reserved_sectors - 1; ++i) {
+	for (i = 0; i < cfg->reserved_sectors - 1; ++i) {
 		rc = block_write_direct(service_id, addr, 1, buffer);
 		if (rc != EOK)
@@ -265,14 +295,26 @@
 
 	/* File allocation tables */
-	for (i = 0; i < fat_count; ++i) {
+	for (i = 0; i < cfg->fat_count; ++i) {
 		printf("Writing allocation table %d.\n", i + 1);
 
-		for (j = 0; j < par->fat_sectors; ++j) {
-			memset(buffer, 0, sector_size);
+		for (j = 0; j < cfg->fat_sectors; ++j) {
+			memset(buffer, 0, cfg->sector_size);
 			if (j == 0) {
-				buffer[0] = media_descriptor;
+				buffer[0] = default_media_descriptor;
 				buffer[1] = 0xFF;
 				buffer[2] = 0xFF;
-				buffer[3] = 0xFF;
+				if (cfg->fat_type == FAT16) {
+					buffer[3] = 0xFF;
+				} else if (cfg->fat_type == FAT32) {
+					buffer[3] = 0x0F;
+					buffer[4] = 0xFF;
+					buffer[5] = 0xFF;
+					buffer[6] = 0xFF;
+					buffer[7] = 0x0F;
+					buffer[8] = 0xF8;
+					buffer[9] = 0xFF;
+					buffer[10] = 0xFF;
+					buffer[11] = 0x0F;
+				}
 			}
 
@@ -285,15 +327,24 @@
 	}
 
+	/* Root directory */
 	printf("Writing root directory.\n");
-
-	memset(buffer, 0, sector_size);
-
-	/* Root directory */
-	for (i = 0; i < par->rootdir_sectors; ++i) {
-		rc = block_write_direct(service_id, addr, 1, buffer);
-		if (rc != EOK)
-			return EIO;
-
-		++addr;
+	memset(buffer, 0, cfg->sector_size);
+	if (cfg->fat_type != FAT32) {
+		size_t idx;
+		for (idx = 0; idx < cfg->rootdir_sectors; ++idx) {
+			rc = block_write_direct(service_id, addr, 1, buffer);
+			if (rc != EOK)
+				return EIO;
+
+			++addr;
+		}
+	} else {
+		for (i=0; i<cfg->sectors_per_cluster; i++) {
+			rc = block_write_direct(service_id, addr, 1, buffer);
+			if (rc != EOK)
+				return EIO;
+
+			++addr;
+		}	
 	}
 
@@ -304,5 +355,5 @@
 
 /** Construct boot sector with the given parameters. */
-static void fat_bootsec_create(struct fat_params const *par, struct fat_bs *bs)
+static void fat_bootsec_create(struct fat_cfg const *cfg, struct fat_bs *bs)
 {
 	memset(bs, 0, sizeof(*bs));
@@ -315,34 +366,44 @@
 
 	/* BIOS Parameter Block */
-	bs->bps = host2uint16_t_le(sector_size);
-	bs->spc = sectors_per_cluster;
-	bs->rscnt = host2uint16_t_le(par->reserved_sectors);
-	bs->fatcnt = fat_count;
-	bs->root_ent_max = host2uint16_t_le(par->cfg.root_ent_max);
-
-	if (par->cfg.total_sectors < 0x10000)
-		bs->totsec16 = host2uint16_t_le(par->cfg.total_sectors);
-	else
-		bs->totsec16 = host2uint16_t_le(0);
-
-	bs->mdesc = media_descriptor;
-	bs->sec_per_fat = host2uint16_t_le(par->fat_sectors);
+	bs->bps = host2uint16_t_le(cfg->sector_size);
+	bs->spc = cfg->sectors_per_cluster;
+	bs->rscnt = host2uint16_t_le(cfg->reserved_sectors);
+	bs->fatcnt = cfg->fat_count;
+	bs->root_ent_max = host2uint16_t_le(cfg->root_ent_max);
+
+	if (cfg->total_sectors < 0x10000) {
+		bs->totsec16 = host2uint16_t_le(cfg->total_sectors);
+		bs->totsec32 = 0;
+	} else {
+		bs->totsec16 = 0;
+		bs->totsec32 = host2uint32_t_le(cfg->total_sectors);
+	}
+
+	bs->mdesc = default_media_descriptor;
 	bs->sec_per_track = host2uint16_t_le(63);
+	bs->signature = host2uint16_t_be(0x55AA);
 	bs->headcnt = host2uint16_t_le(6);
 	bs->hidden_sec = host2uint32_t_le(0);
 
-	if (par->cfg.total_sectors >= 0x10000)
-		bs->totsec32 = host2uint32_t_le(par->cfg.total_sectors);
-	else
-		bs->totsec32 = host2uint32_t_le(0);
-
-	/* Extended BPB */
-	bs->pdn = 0x80;
-	bs->ebs = 0x29;
-	bs->id = host2uint32_t_be(0x12345678);
-
-	memcpy(bs->label, "HELENOS_NEW", 11);
-	memcpy(bs->type, "FAT16   ", 8);
-	bs->signature = host2uint16_t_be(0x55AA);
+	if (cfg->fat_type == FAT32) {
+		bs->sec_per_fat = 0;
+		bs->fat32.sectors_per_fat = host2uint32_t_le(cfg->fat_sectors);
+
+		bs->fat32.pdn = 0x80;
+		bs->fat32.ebs = 0x29;
+		bs->fat32.id = host2uint32_t_be(0x12345678);
+		bs->fat32.root_cluster = 2;
+
+		memcpy(bs->fat32.label, "HELENOS_NEW", 11);
+		memcpy(bs->fat32.type, "FAT32   ", 8);
+	} else {
+		bs->sec_per_fat = host2uint16_t_le(cfg->fat_sectors);
+		bs->pdn = 0x80;
+		bs->ebs = 0x29;
+		bs->id = host2uint32_t_be(0x12345678);
+
+		memcpy(bs->label, "HELENOS_NEW", 11);
+		memcpy(bs->type, "FAT   ", 8);
+	}
 }
 
Index: uspace/lib/c/generic/str.c
===================================================================
--- uspace/lib/c/generic/str.c	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/lib/c/generic/str.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -3,4 +3,5 @@
  * Copyright (c) 2008 Jiri Svoboda
  * Copyright (c) 2011 Martin Sucha
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -367,4 +368,16 @@
 }
 
+/** Check whether wide string is plain ASCII.
+ *
+ * @return True if wide string is plain ASCII.
+ *
+ */
+bool wstr_is_ascii(const wchar_t *wstr)
+{
+	while (*wstr && ascii_check(*wstr))
+		wstr++;
+	return *wstr == 0;
+}
+
 /** Check whether character is valid
  *
@@ -619,7 +632,10 @@
  * @param size	Size of the destination buffer.
  * @param src	Source wide string.
- */
-void wstr_to_str(char *dest, size_t size, const wchar_t *src)
-{
+ *
+ * @return EOK, if success, negative otherwise.
+ */
+int wstr_to_str(char *dest, size_t size, const wchar_t *src)
+{
+	int rc;
 	wchar_t ch;
 	size_t src_idx;
@@ -633,10 +649,90 @@
 
 	while ((ch = src[src_idx++]) != 0) {
-		if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
+		rc = chr_encode(ch, dest, &dest_off, size - 1);
+		if (rc != EOK)
 			break;
 	}
 
 	dest[dest_off] = '\0';
-}
+	return rc;
+}
+
+/** Convert UTF16 string to string.
+ *
+ * Convert utf16 string @a src to string. The output is written to the buffer
+ * specified by @a dest and @a size. @a size must be non-zero and the string
+ * written will always be well-formed. Surrogate pairs also supported.
+ *
+ * @param dest	Destination buffer.
+ * @param size	Size of the destination buffer.
+ * @param src	Source utf16 string.
+ *
+ * @return EOK, if success, negative otherwise.
+ */
+int utf16_to_str(char *dest, size_t size, const uint16_t *src)
+{
+	size_t idx=0, dest_off=0;
+	wchar_t ch;
+	int rc = EOK;
+
+	/* There must be space for a null terminator in the buffer. */
+	assert(size > 0);
+
+	while (src[idx]) {
+		if ((src[idx] & 0xfc00) == 0xd800) {
+			if (src[idx+1] && (src[idx+1] & 0xfc00) == 0xdc00) {
+				ch = 0x10000;
+				ch += (src[idx] & 0x03FF) << 10;
+				ch += (src[idx+1] & 0x03FF);
+				idx += 2;
+			}
+			else
+				break;
+		} else {
+			ch = src[idx];
+			idx++;
+		}
+		rc = chr_encode(ch, dest, &dest_off, size-1);
+		if (rc != EOK)
+			break;
+	}
+	dest[dest_off] = '\0';
+	return rc;
+}
+
+int str_to_utf16(uint16_t *dest, size_t size, const char *src)
+{
+	int rc=EOK;
+	size_t offset=0;
+	size_t idx=0;
+	wchar_t c;
+
+	assert(size > 0);
+	
+	while ((c = str_decode(src, &offset, STR_NO_LIMIT)) != 0) {
+		if (c > 0x10000) {
+			if (idx+2 >= size-1) {
+				rc=EOVERFLOW;
+				break;
+			}
+			c = (c - 0x10000);
+			dest[idx] = 0xD800 | (c >> 10);
+			dest[idx+1] = 0xDC00 | (c & 0x3FF);
+			idx++;
+		} else {
+			 dest[idx] = c;
+		}
+
+		idx++;
+		if (idx >= size-1) {
+			rc=EOVERFLOW;
+			break;
+		}
+	}
+
+	dest[idx] = '\0';
+	return rc;
+}
+
 
 /** Convert wide string to new string.
@@ -698,7 +794,10 @@
  * @param dlen	Length of destination buffer (number of wchars).
  * @param src	Source string.
- */
-void str_to_wstr(wchar_t *dest, size_t dlen, const char *src)
-{
+ *
+ * @return EOK, if success, negative otherwise.
+ */
+int str_to_wstr(wchar_t *dest, size_t dlen, const char *src)
+{
+	int rc=EOK;
 	size_t offset;
 	size_t di;
@@ -711,6 +810,8 @@
 
 	do {
-		if (di >= dlen - 1)
+		if (di >= dlen - 1) {
+			rc = EOVERFLOW;
 			break;
+		}
 
 		c = str_decode(src, &offset, STR_NO_LIMIT);
@@ -719,4 +820,5 @@
 
 	dest[dlen - 1] = '\0';
+	return rc;
 }
 
@@ -783,4 +885,39 @@
 	
 	return (char *) res;
+}
+
+/** Find first occurence of character in wide string.
+ *
+ * @param wstr String to search.
+ * @param ch  Character to look for.
+ *
+ * @return Pointer to character in @a wstr or NULL if not found.
+ */
+wchar_t *wstr_chr(const wchar_t *wstr, wchar_t ch)
+{
+	while (*wstr && *wstr != ch)
+		wstr++;
+	if (*wstr)
+		return (wchar_t *) wstr;
+	else
+		return NULL;
+}
+
+/** Find last occurence of character in wide string.
+ *
+ * @param wstr String to search.
+ * @param ch  Character to look for.
+ *
+ * @return Pointer to character in @a wstr or NULL if not found.
+ */
+wchar_t *wstr_rchr(const wchar_t *wstr, wchar_t ch)
+{
+	const wchar_t *res = NULL;
+	while (*wstr) {
+		if (*wstr == ch)
+			res = wstr;
+		wstr++;
+	}
+	return (wchar_t *) res;
 }
 
@@ -1037,4 +1174,34 @@
 }
 
+void str_reverse(char* begin, char* end) 
+{
+    char aux;
+    while(end>begin)
+        aux=*end, *end--=*begin, *begin++=aux;
+}
+
+int size_t_str(size_t value, int base, char* str, size_t size) 
+{
+    static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+    char* wstr=str;
+	
+	if (size == 0) 
+		return EINVAL;
+    if (base<2 || base>35) {
+        *str='\0';
+        return EINVAL;
+    }
+
+    do {
+        *wstr++ = num[value % base];
+		if (--size == 0)
+			return EOVERFLOW;
+    } while(value /= base);
+    *wstr='\0';
+
+    // Reverse string
+    str_reverse(str,wstr-1);
+	return EOK;
+}
 
 /** Convert initial part of string to unsigned long according to given base.
Index: uspace/lib/c/include/str.h
===================================================================
--- uspace/lib/c/include/str.h	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/lib/c/include/str.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2005 Martin Decky
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -71,4 +72,5 @@
 extern bool ascii_check(wchar_t ch);
 extern bool chr_check(wchar_t ch);
+extern bool wstr_is_ascii(const wchar_t *wstr);
 
 extern int str_cmp(const char *s1, const char *s2);
@@ -79,12 +81,16 @@
 extern void str_append(char *dest, size_t size, const char *src);
 
+extern int wstr_to_str(char *dest, size_t size, const wchar_t *src);
 extern int spascii_to_str(char *dest, size_t size, const uint8_t *src, size_t n);
-extern void wstr_to_str(char *dest, size_t size, const wchar_t *src);
 extern char *wstr_to_astr(const wchar_t *src);
-extern void str_to_wstr(wchar_t *dest, size_t dlen, const char *src);
 extern wchar_t *str_to_awstr(const char *src);
+extern int str_to_wstr(wchar_t *dest, size_t dlen, const char *src);
+extern int utf16_to_str(char *dest, size_t size, const uint16_t *src);
+extern int str_to_utf16(uint16_t *dest, size_t size, const char *src);
 
 extern char *str_chr(const char *str, wchar_t ch);
 extern char *str_rchr(const char *str, wchar_t ch);
+extern wchar_t *wstr_chr(const wchar_t *wstr, wchar_t ch);
+extern wchar_t *wstr_rchr(const wchar_t *wstr, wchar_t ch);
 
 extern bool wstr_linsert(wchar_t *str, wchar_t ch, size_t pos, size_t max_pos);
@@ -93,4 +99,7 @@
 extern char *str_dup(const char *);
 extern char *str_ndup(const char *, size_t max_size);
+
+extern void str_reverse(char* begin, char* end);
+extern int size_t_str(size_t value, int base, char* str, size_t size);
 
 extern int str_uint64(const char *, char **, unsigned int, bool, uint64_t *);
Index: uspace/srv/fs/exfat/Makefile
===================================================================
--- uspace/srv/fs/exfat/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2005 Martin Decky
+# Copyright (c) 2007 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.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBBLOCK_PREFIX)/libblock.a $(LIBFS_PREFIX)/libfs.a
+EXTRA_CFLAGS += -I$(LIBBLOCK_PREFIX) -I$(LIBFS_PREFIX)
+BINARY = exfat
+
+SOURCES = \
+	exfat.c \
+	exfat_fat.c \
+	exfat_bitmap.c \
+	exfat_ops.c \
+	exfat_idx.c \
+	exfat_dentry.c \
+	exfat_directory.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/fs/exfat/exfat.c
===================================================================
--- uspace/srv/fs/exfat/exfat.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2006 Martin Decky
+ * 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.c
+ * @brief	FAT file system driver for HelenOS.
+ */
+
+#include "exfat.h"
+#include <ipc/services.h>
+#include <ns.h>
+#include <async.h>
+#include <errno.h>
+#include <unistd.h>
+#include <task.h>
+#include <stdio.h>
+#include <libfs.h>
+#include "../../vfs/vfs.h"
+
+#define NAME	"exfat"
+
+vfs_info_t exfat_vfs_info = {
+	.name = NAME,
+	.concurrent_read_write = false,
+	.write_retains_size = false,	
+};
+
+int main(int argc, char **argv)
+{
+	printf(NAME ": HelenOS exFAT file system server\n");
+
+	int rc = exfat_idx_init();
+	if (rc != EOK)
+		goto err;
+
+	async_sess_t *vfs_sess = service_connect_blocking(EXCHANGE_SERIALIZE,
+	    SERVICE_VFS, 0, 0);
+	if (!vfs_sess) {
+		printf(NAME ": failed to connect to VFS\n");
+		return -1;
+	}
+	
+	rc = fs_register(vfs_sess, &exfat_vfs_info, &exfat_ops, &exfat_libfs_ops);
+	if (rc != EOK) {
+		exfat_idx_fini();
+		goto err;
+	}
+	
+	printf(NAME ": Accepting connections\n");
+	task_retval(0);
+	async_manager();
+
+	/* not reached */
+	return 0;
+
+err:
+	printf(NAME ": Failed to register file system (%d)\n", rc);
+	return rc;
+}
+
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat.h
===================================================================
--- uspace/srv/fs/exfat/exfat.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,194 @@
+/*
+ * 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_EXFAT_H_
+#define EXFAT_EXFAT_H_
+
+#include "exfat_fat.h"
+#include <fibril_synch.h>
+#include <libfs.h>
+#include <atomic.h>
+#include <sys/types.h>
+#include <bool.h>
+#include "../../vfs/vfs.h"
+
+#ifndef dprintf
+#define dprintf(...)	printf(__VA_ARGS__)
+#endif
+
+#define BS_BLOCK		0
+#define BS_SIZE			512
+
+#define BPS(bs)			((uint32_t) (1 << (bs->bytes_per_sector)))
+#define SPC(bs)			((uint32_t)(1 << (bs->sec_per_cluster)))
+#define BPC(bs)			((uint32_t)(BPS(bs)*SPC(bs)))
+#define VOL_FS(bs)		uint64_t_le2host(bs->volume_start)
+#define VOL_CNT(bs)		uint64_t_le2host(bs->volume_count)
+#define FAT_FS(bs)		uint32_t_le2host(bs->fat_sector_start)
+#define FAT_CNT(bs)		uint32_t_le2host(bs->fat_sector_count)
+#define DATA_FS(bs)		uint32_t_le2host(bs->data_start_sector)
+#define DATA_CNT(bs)	uint32_t_le2host(bs->data_clusters)
+#define ROOT_FC(bs)		uint32_t_le2host(bs->rootdir_cluster)
+#define VOL_FLAGS(bs)	uint16_t_le2host(bs->volume_flags)
+
+#define EXFAT_NODE(node)	((node) ? (exfat_node_t *) (node)->data : NULL)
+#define FS_NODE(node)	((node) ? (node)->bp : NULL)
+#define DPS(bs) (BPS((bs)) / sizeof(exfat_dentry_t))
+
+typedef struct exfat_bs {
+	uint8_t jump[3];				/* 0x00 jmp and nop instructions */
+	uint8_t oem_name[8];			/* 0x03 "EXFAT   " */
+	uint8_t	__reserved[53];			/* 0x0B always 0 */
+	uint64_t volume_start;			/* 0x40 partition first sector */
+	uint64_t volume_count;			/* 0x48 partition sectors count */
+	uint32_t fat_sector_start;		/* 0x50 FAT first sector */
+	uint32_t fat_sector_count;		/* 0x54 FAT sectors count */
+	uint32_t data_start_sector;		/* 0x58 Data region first cluster sector */
+	uint32_t data_clusters;			/* 0x5C total clusters count */
+	uint32_t rootdir_cluster;		/* 0x60 first cluster of the root dir */
+	uint32_t volume_serial;			/* 0x64 volume serial number */
+	struct {						/* 0x68 FS version */
+		uint8_t minor;
+		uint8_t major;
+	} __attribute__ ((packed)) version;
+	uint16_t volume_flags;			/* 0x6A volume state flags */
+	uint8_t bytes_per_sector;		/* 0x6C sector size as (1 << n) */
+	uint8_t sec_per_cluster;		/* 0x6D sectors per cluster as (1 << n) */
+	uint8_t fat_count;				/* 0x6E always 1 */
+	uint8_t drive_no;				/* 0x6F always 0x80 */
+	uint8_t allocated_percent;		/* 0x70 percentage of allocated space */
+	uint8_t _reserved2[7];			/* 0x71 reserved */
+	uint8_t bootcode[390];			/* Boot code */
+	uint16_t signature;				/* the value of 0xAA55 */
+} __attribute__((__packed__)) exfat_bs_t;
+
+typedef enum {
+	EXFAT_UNKNOW,
+	EXFAT_DIRECTORY,
+	EXFAT_FILE,
+	EXFAT_BITMAP,
+	EXFAT_UCTABLE
+} exfat_node_type_t;
+
+struct exfat_node;
+struct exfat_idx_t;
+
+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;
+	service_id_t	service_id;
+	fs_index_t	index;
+
+	/* Does parent node fragmented or not? */
+	bool parent_fragmented;
+	/**
+	 * 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;
+	/* Should we do walk-on-FAT or not */
+	bool			fragmented;
+
+	/*
+	 * 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 vfs_out_ops_t exfat_ops;
+extern libfs_ops_t exfat_libfs_ops;
+
+extern int exfat_idx_get_new(exfat_idx_t **, service_id_t);
+extern exfat_idx_t *exfat_idx_get_by_pos(service_id_t, exfat_cluster_t, unsigned);
+extern exfat_idx_t *exfat_idx_get_by_index(service_id_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_service_id(service_id_t);
+extern void exfat_idx_fini_by_service_id(service_id_t);
+
+extern int exfat_node_expand(service_id_t service_id, exfat_node_t *nodep,
+    exfat_cluster_t clusters);
+extern int exfat_node_put(fs_node_t *);
+extern int exfat_bitmap_get(fs_node_t **, service_id_t);
+extern int exfat_uctable_get(fs_node_t **, service_id_t);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_bitmap.c
===================================================================
--- uspace/srv/fs/exfat/exfat_bitmap.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_bitmap.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,274 @@
+/*
+ * 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_bitmap.c
+ * @brief	Functions that manipulate the Bitmap Table.
+ */
+
+#include "exfat_bitmap.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>
+
+
+int bitmap_is_free(exfat_bs_t *bs, service_id_t service_id, 
+    exfat_cluster_t clst)
+{
+	fs_node_t *fn;
+	block_t *b=NULL;
+	exfat_node_t *bitmapp;
+	uint8_t *bitmap;
+	int rc;
+	bool alloc;
+
+	clst -= EXFAT_CLST_FIRST;
+	
+	rc = exfat_bitmap_get(&fn, service_id);
+	if (rc != EOK)
+		return rc;
+	bitmapp = EXFAT_NODE(fn);
+	
+	aoff64_t offset = clst / 8;
+	rc = exfat_block_get(&b, bs, bitmapp, offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		return rc;
+	}
+	bitmap = (uint8_t *)b->data;
+	alloc = bitmap[offset % BPS(bs)] & (1 << (clst % 8));
+
+	rc = block_put(b);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		return rc;
+	}
+	rc = exfat_node_put(fn);
+	if (rc != EOK)
+		return rc;
+
+	if (alloc)
+		return ENOENT;
+
+	return EOK;
+}
+
+int bitmap_set_cluster(exfat_bs_t *bs, service_id_t service_id, 
+    exfat_cluster_t clst)
+{
+	fs_node_t *fn;
+	block_t *b=NULL;
+	exfat_node_t *bitmapp;
+	uint8_t *bitmap;
+	int rc;
+
+	clst -= EXFAT_CLST_FIRST;
+	
+	rc = exfat_bitmap_get(&fn, service_id);
+	if (rc != EOK)
+		return rc;
+	bitmapp = EXFAT_NODE(fn);
+	
+	aoff64_t offset = clst / 8;
+	rc = exfat_block_get(&b, bs, bitmapp, offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		return rc;
+	}
+	bitmap = (uint8_t *)b->data;
+	bitmap[offset % BPS(bs)] |= (1 << (clst % 8));
+
+	b->dirty = true;
+	rc = block_put(b);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		return rc;
+	}
+	
+	return exfat_node_put(fn);
+}
+
+int bitmap_clear_cluster(exfat_bs_t *bs, service_id_t service_id, 
+    exfat_cluster_t clst)
+{
+	fs_node_t *fn;
+	block_t *b=NULL;
+	exfat_node_t *bitmapp;
+	uint8_t *bitmap;
+	int rc;
+
+	clst -= EXFAT_CLST_FIRST;
+	
+	rc = exfat_bitmap_get(&fn, service_id);
+	if (rc != EOK)
+		return rc;
+	bitmapp = EXFAT_NODE(fn);
+	
+	aoff64_t offset = clst / 8;
+	rc = exfat_block_get(&b, bs, bitmapp, offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		return rc;
+	}
+	bitmap = (uint8_t *)b->data;
+	bitmap[offset % BPS(bs)] &= ~(1 << (clst % 8));
+
+	b->dirty = true;
+	rc = block_put(b);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		return rc;
+	}
+	
+	return exfat_node_put(fn);
+}
+
+int bitmap_set_clusters(exfat_bs_t *bs, service_id_t service_id, 
+    exfat_cluster_t firstc, exfat_cluster_t count)
+{
+	int rc;
+	exfat_cluster_t clst;
+	clst = firstc;
+
+	while (clst < firstc+count ) {
+		rc = bitmap_set_cluster(bs, service_id, clst);
+		if (rc != EOK) {
+			if ((clst-firstc) > 0)
+				(void) bitmap_clear_clusters(bs, service_id, firstc, clst-firstc);
+			return rc;
+		}
+		clst++;
+	}
+	return EOK;
+}
+
+int bitmap_clear_clusters(exfat_bs_t *bs, service_id_t service_id, 
+    exfat_cluster_t firstc, exfat_cluster_t count)
+{
+	int rc;
+	exfat_cluster_t clst;
+	clst = firstc;
+
+	while (clst < firstc+count ) {
+		rc = bitmap_clear_cluster(bs, service_id, clst);
+		if (rc != EOK)
+			return rc;
+		clst++;
+	}
+	return EOK;
+}
+
+int bitmap_alloc_clusters(exfat_bs_t *bs, service_id_t service_id, 
+    exfat_cluster_t *firstc, exfat_cluster_t count)
+{
+	exfat_cluster_t startc, endc;
+	startc = EXFAT_CLST_FIRST;
+
+	while (startc < DATA_CNT(bs)+2) {
+		endc = startc;
+		while (bitmap_is_free(bs, service_id, endc) == EOK) {
+			if ((endc - startc)+1 == count){
+				*firstc = startc;
+				return bitmap_set_clusters(bs, service_id, startc, count);
+			}
+			else
+				endc++;
+		}
+		startc = endc+1;
+	}
+	return ENOSPC;
+}
+
+
+int bitmap_append_clusters(exfat_bs_t *bs, exfat_node_t *nodep, 
+    exfat_cluster_t count)
+{
+	if (nodep->firstc == 0) {
+		return bitmap_alloc_clusters(bs, nodep->idx->service_id, 
+		    &nodep->firstc, count);
+	} else {
+		exfat_cluster_t lastc, clst;
+		lastc = nodep->firstc + ROUND_UP(nodep->size, BPC(bs)) / BPC(bs) - 1;
+
+		clst = lastc+1;
+		while (bitmap_is_free(bs, nodep->idx->service_id, clst) == EOK) {
+			if ((clst - lastc) == count){
+				return bitmap_set_clusters(bs, nodep->idx->service_id, 
+				    lastc+1, count);
+			}
+			else
+				clst++;
+		}
+		return ENOSPC;
+	}
+}
+
+
+int bitmap_free_clusters(exfat_bs_t *bs, exfat_node_t *nodep, 
+    exfat_cluster_t count)
+{
+	exfat_cluster_t lastc;
+	lastc = nodep->firstc + ROUND_UP(nodep->size, BPC(bs)) / BPC(bs) - 1;
+	lastc -= count;
+
+	return bitmap_clear_clusters(bs, nodep->idx->service_id, lastc+1, count);
+}
+
+
+int bitmap_replicate_clusters(exfat_bs_t *bs, exfat_node_t *nodep)
+{
+	int rc;
+	exfat_cluster_t lastc, clst;
+	service_id_t service_id = nodep->idx->service_id;
+	lastc = nodep->firstc + ROUND_UP(nodep->size, BPC(bs)) / BPC(bs) - 1;
+
+	for (clst = nodep->firstc; clst < lastc; clst++) {
+		rc = exfat_set_cluster(bs, service_id, clst, clst+1);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return exfat_set_cluster(bs, service_id, lastc, EXFAT_CLST_EOF);
+}
+
+
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_bitmap.h
===================================================================
--- uspace/srv/fs/exfat/exfat_bitmap.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_bitmap.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,69 @@
+/*
+ * 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_EXFAT_BITMAP_H_
+#define EXFAT_EXFAT_BITMAP_H_
+
+#include <stdint.h>
+#include "exfat.h"
+#include "exfat_fat.h"
+
+/* forward declarations */
+struct exfat_node;
+struct exfat_bs;
+
+extern int bitmap_alloc_clusters(struct exfat_bs *bs, service_id_t service_id, 
+    exfat_cluster_t *firstc, exfat_cluster_t count);
+extern int bitmap_append_clusters(struct exfat_bs *bs, struct exfat_node *nodep, 
+    exfat_cluster_t count);
+extern int bitmap_free_clusters(struct exfat_bs *bs, struct exfat_node *nodep, 
+    exfat_cluster_t count);
+extern int bitmap_replicate_clusters(struct exfat_bs *bs, struct exfat_node *nodep); 
+
+extern int bitmap_is_free(struct exfat_bs *bs, service_id_t service_id, 
+    exfat_cluster_t clst);
+extern int bitmap_set_cluster(struct exfat_bs *bs, service_id_t service_id, 
+    exfat_cluster_t clst);
+extern int bitmap_clear_cluster(struct exfat_bs *bs, service_id_t service_id, 
+    exfat_cluster_t clst);
+
+extern int bitmap_set_clusters(struct exfat_bs *bs, service_id_t service_id, 
+    exfat_cluster_t firstc, exfat_cluster_t count);
+extern int bitmap_clear_clusters(struct exfat_bs *bs, service_id_t service_id, 
+    exfat_cluster_t firstc, exfat_cluster_t count);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_dentry.c
===================================================================
--- uspace/srv/fs/exfat/exfat_dentry.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_dentry.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,125 @@
+/*
+ * 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_dentry.c
+ * @brief	Functions that work with exFAT directory entries.
+ */
+
+#include "exfat_dentry.h"
+#include <ctype.h>
+#include <str.h>
+#include <errno.h>
+#include <byteorder.h>
+#include <assert.h>
+
+exfat_dentry_clsf_t exfat_classify_dentry(const exfat_dentry_t *d)
+{
+	switch (d->type) {
+	case EXFAT_TYPE_VOLLABEL:
+		return EXFAT_DENTRY_VOLLABEL;
+	case EXFAT_TYPE_BITMAP:
+		return EXFAT_DENTRY_BITMAP;
+	case EXFAT_TYPE_UCTABLE:
+		return EXFAT_DENTRY_UCTABLE;
+	case EXFAT_TYPE_GUID:
+		return EXFAT_DENTRY_GUID;
+	case EXFAT_TYPE_FILE:
+		return EXFAT_DENTRY_FILE;
+	case EXFAT_TYPE_STREAM:
+		return EXFAT_DENTRY_STREAM;
+	case EXFAT_TYPE_NAME:
+		return EXFAT_DENTRY_NAME;
+	case EXFAT_TYPE_UNUSED:
+		return EXFAT_DENTRY_LAST;
+	default:
+		if (d->type & EXFAT_TYPE_USED)
+			return EXFAT_DENTRY_SKIP;
+		else
+			return EXFAT_DENTRY_FREE;
+	}
+}
+
+uint16_t exfat_name_hash(const uint16_t *name, const uint16_t *uctable, size_t chars)
+{
+	uint16_t hash = 0;
+	uint16_t ch;
+	while (*name) {
+		if (*name < chars)
+			ch = uint16_t_le2host(uctable[*name]);
+		else
+			ch = *name;
+		name++;
+
+		hash = ((hash << 15) | (hash >> 1)) + (ch & 0xff);
+		hash = ((hash << 15) | (hash >> 1)) + (ch >> 8);
+	}
+
+	return hash;
+}
+
+void exfat_dentry_get_name(const exfat_name_dentry_t *name, size_t size, uint16_t *dst, size_t *offset)
+{
+	size_t i=0; 
+	while(i<EXFAT_NAME_PART_LEN && *offset < size) {
+		dst[*offset] = uint16_t_le2host(name->name[i]);
+		i++;
+		(*offset)++;
+	}
+	dst[*offset] = '\0';
+}
+
+bool exfat_valid_char(wchar_t ch)
+{
+	/* TODO */
+	return true;
+}
+
+bool exfat_valid_name(const char *name)
+{
+	/* TODO */
+	return true;
+}
+
+size_t utf16_length(const uint16_t *wstr)
+{
+	size_t len = 0;
+	
+	while (*wstr++ != 0)
+		len++;
+	
+	return len;
+}
+
+/**
+ * @}
+ */ 
Index: uspace/srv/fs/exfat/exfat_dentry.h
===================================================================
--- uspace/srv/fs/exfat/exfat_dentry.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_dentry.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,170 @@
+/*
+ * 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_EXFAT_DENTRY_H_
+#define EXFAT_EXFAT_DENTRY_H_
+
+#include <stdint.h>
+#include <bool.h>
+
+#define EXFAT_FILENAME_LEN	255
+#define EXFAT_NAME_PART_LEN	15
+
+#define EXFAT_TYPE_UNUSED	0x00
+#define EXFAT_TYPE_USED		0x80
+#define EXFAT_TYPE_VOLLABEL	0x83
+#define EXFAT_TYPE_BITMAP	0x81
+#define EXFAT_TYPE_UCTABLE	0x82
+#define EXFAT_TYPE_GUID		0xA0
+#define EXFAT_TYPE_FILE		0x85
+#define EXFAT_TYPE_STREAM	0xC0
+#define EXFAT_TYPE_NAME		0xC1
+
+#define EXFAT_ATTR_RDONLY	0x01
+#define EXFAT_ATTR_HIDDEN	0x02
+#define EXFAT_ATTR_SYSTEM	0x04
+#define EXFAT_ATTR_SUBDIR	0x10
+#define EXFAT_ATTR_ARCHIVE	0x20
+
+
+/* All dentry structs should have 31 byte size */
+typedef struct {
+	uint8_t 	size;
+	uint16_t 	label[11];
+	uint8_t 	_reserved[8];
+} __attribute__ ((packed)) exfat_vollabel_dentry_t;
+
+typedef struct {
+	uint8_t 	flags;
+	uint8_t 	_reserved[18];
+	uint32_t 	firstc;
+	uint64_t 	size;
+} __attribute__ ((packed)) exfat_bitmap_dentry_t;
+
+typedef struct {
+	uint8_t 	_reserved1[3];
+	uint32_t 	checksum;
+	uint8_t 	_reserved2[12];
+	uint32_t 	firstc;
+	uint64_t 	size;
+} __attribute__ ((packed)) exfat_uctable_dentry_t;
+
+typedef struct {
+	uint8_t 	count; /* Always zero */ 
+	uint16_t 	checksum;
+	uint16_t 	flags;
+	uint8_t 	guid[16];
+	uint8_t 	_reserved[10];
+} __attribute__ ((packed)) exfat_guid_dentry_t;
+
+typedef struct {
+	uint8_t 	count;
+	uint16_t 	checksum;
+	uint16_t 	attr;
+	uint8_t 	_reserved1[2];
+	uint32_t 	ctime;
+	uint32_t 	mtime;
+	uint32_t 	atime;
+	uint8_t 	ctime_fine;
+	uint8_t 	mtime_fine;
+	uint8_t 	ctime_tz;
+	uint8_t 	mtime_tz;
+	uint8_t 	atime_tz;
+	uint8_t 	_reserved2[7];
+} __attribute__ ((packed)) exfat_file_dentry_t;
+
+typedef struct {
+	uint8_t 	flags;
+	uint8_t 	_reserved1[1];
+	uint8_t 	name_size;
+	uint16_t 	hash;
+	uint8_t 	_reserved2[2];
+	uint64_t 	valid_data_size;
+	uint8_t 	_reserved3[4];
+	uint32_t 	firstc;
+	uint64_t 	data_size;
+} __attribute__ ((packed)) exfat_stream_dentry_t;
+
+typedef struct {
+	uint8_t 	flags;
+	uint16_t 	name[EXFAT_NAME_PART_LEN];
+} __attribute__ ((packed)) exfat_name_dentry_t;
+
+
+typedef struct {
+	uint8_t type;
+	union {
+		exfat_vollabel_dentry_t	vollabel;
+		exfat_bitmap_dentry_t 	bitmap;
+		exfat_uctable_dentry_t 	uctable;
+		exfat_guid_dentry_t 	guid;
+		exfat_file_dentry_t 	file;
+		exfat_stream_dentry_t 	stream;
+		exfat_name_dentry_t 	name;
+	};
+} __attribute__ ((packed)) exfat_dentry_t;
+
+
+typedef enum {
+	EXFAT_DENTRY_SKIP,
+	EXFAT_DENTRY_LAST,
+	EXFAT_DENTRY_FREE,
+	EXFAT_DENTRY_VOLLABEL,
+	EXFAT_DENTRY_BITMAP,
+	EXFAT_DENTRY_UCTABLE,
+	EXFAT_DENTRY_GUID,
+	EXFAT_DENTRY_FILE,
+	EXFAT_DENTRY_STREAM,
+	EXFAT_DENTRY_NAME
+} exfat_dentry_clsf_t;
+
+
+extern exfat_dentry_clsf_t exfat_classify_dentry(const exfat_dentry_t *d);
+
+extern uint16_t exfat_name_hash(const uint16_t *name, const uint16_t *uctable, 
+    size_t chars);
+
+extern void exfat_dentry_get_name(const exfat_name_dentry_t *name, size_t size, 
+    uint16_t *dst, size_t *offset);
+
+
+extern bool exfat_valid_char(wchar_t ch);
+extern bool exfat_valid_name(const char *name);
+
+extern size_t utf16_length(const uint16_t *wstr);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_directory.c
===================================================================
--- uspace/srv/fs/exfat/exfat_directory.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_directory.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,511 @@
+/*
+ * 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_directory.c
+ * @brief	Functions that work with FAT directory.
+ */
+
+#include "exfat.h"
+#include "exfat_directory.h"
+#include "exfat_fat.h"
+#include <libblock.h>
+#include <errno.h>
+#include <byteorder.h>
+#include <mem.h>
+#include <malloc.h>
+#include <str.h>
+#include <align.h>
+
+void exfat_directory_init(exfat_directory_t *di)
+{
+	di->b = NULL;
+	di->nodep = NULL;
+	di->bs = NULL;
+	di->blocks = 0;
+	di->pos = 0;
+	di->bnum = 0;
+	di->last = false;
+	di->fragmented = false;
+	di->firstc = 0;
+}
+
+int exfat_directory_open(exfat_node_t *nodep, exfat_directory_t *di)
+{
+	exfat_directory_init(di);
+	di->nodep = nodep;	
+	if (di->nodep->type != EXFAT_DIRECTORY)
+		return EINVAL;
+	di->service_id = nodep->idx->service_id;
+	di->fragmented = nodep->fragmented;
+	di->firstc = nodep->firstc;
+
+	di->bs = block_bb_get(di->service_id);
+/*	di->blocks = nodep->size / BPS(di->bs); */
+	di->blocks = ROUND_UP(nodep->size, BPS(di->bs))/BPS(di->bs);
+	return EOK;
+}
+
+int exfat_directory_open_parent(exfat_directory_t *di, 
+    service_id_t service_id, exfat_cluster_t firstc, bool fragmented)
+{
+	exfat_directory_init(di);
+	di->service_id = service_id;
+	di->fragmented = fragmented;
+	di->firstc = firstc;
+	di->bs = block_bb_get(service_id);
+	di->blocks = 0;
+	return EOK;
+}
+
+int exfat_directory_close(exfat_directory_t *di)
+{
+	int rc=EOK;
+	
+	if (di->b)
+		rc = block_put(di->b);
+	
+	return rc;
+}
+
+static int exfat_directory_block_load(exfat_directory_t *di)
+{
+	uint32_t i;
+	int rc;
+
+	i = (di->pos * sizeof(exfat_dentry_t)) / BPS(di->bs);
+	if (di->nodep && (i >= di->blocks))
+		return ENOENT;
+
+	if (di->b && di->bnum != i) {
+		block_put(di->b);
+		di->b = NULL;
+	}
+	if (!di->b) {
+		if (di->nodep) {
+			rc = exfat_block_get(&di->b, di->bs, di->nodep, i, BLOCK_FLAGS_NONE);
+		} else {
+			rc = exfat_block_get_by_clst(&di->b, di->bs, di->service_id,
+			    di->fragmented, di->firstc, NULL, i, BLOCK_FLAGS_NONE);
+		}
+		if (rc != EOK) {
+			di->b = NULL;
+			return rc;
+		}
+		di->bnum = i;
+	}
+	return EOK;
+}
+
+int exfat_directory_next(exfat_directory_t *di)
+{
+	int rc;
+
+	di->pos += 1;
+	rc = exfat_directory_block_load(di);
+	if (rc!=EOK)
+		di->pos -= 1;
+	
+	return rc;
+}
+
+int exfat_directory_prev(exfat_directory_t *di)
+{
+	int rc=EOK;
+	
+	if (di->pos > 0) {
+		di->pos -= 1;
+		rc=exfat_directory_block_load(di);
+	}
+	else
+		return ENOENT;
+	
+	if (rc!=EOK)
+		di->pos += 1;
+	
+	return rc;
+}
+
+int exfat_directory_seek(exfat_directory_t *di, aoff64_t pos)
+{
+	aoff64_t _pos = di->pos;
+	int rc;
+
+	di->pos = pos;
+	rc = exfat_directory_block_load(di);
+	if (rc!=EOK)
+		di->pos = _pos;
+	
+	return rc;
+}
+
+int exfat_directory_get(exfat_directory_t *di, exfat_dentry_t **d)
+{
+	int rc;
+	
+	rc = exfat_directory_block_load(di);
+	if (rc == EOK) {
+		aoff64_t o = di->pos % (BPS(di->bs) / sizeof(exfat_dentry_t));
+		*d = ((exfat_dentry_t *)di->b->data) + o;
+	}
+	
+	return rc;
+}
+
+int exfat_directory_find(exfat_directory_t *di, exfat_dentry_clsf_t type, exfat_dentry_t **d)
+{
+	do {
+		if (exfat_directory_get(di, d) == EOK) {
+			if (exfat_classify_dentry(*d) == type)
+				return EOK;
+		} else
+			return ENOENT;
+	} while (exfat_directory_next(di) == EOK);
+	
+	return ENOENT;
+}
+
+int exfat_directory_find_continue(exfat_directory_t *di, exfat_dentry_clsf_t type, exfat_dentry_t **d)
+{
+	int rc;
+	rc = exfat_directory_next(di);
+	if (rc != EOK)
+		return rc;
+	return exfat_directory_find(di, type, d);
+}
+
+
+int exfat_directory_read_file(exfat_directory_t *di, char *name, size_t size, 
+    exfat_file_dentry_t *df, exfat_stream_dentry_t *ds)
+{
+	uint16_t wname[EXFAT_FILENAME_LEN+1];
+	exfat_dentry_t *d = NULL;
+	int rc, i;
+	size_t offset = 0;
+	aoff64_t start_pos = 0;
+	
+	rc = exfat_directory_find(di, EXFAT_DENTRY_FILE, &d);
+	if (rc != EOK)
+		return rc;
+	start_pos = di->pos;
+	*df = d->file;
+
+	rc = exfat_directory_next(di);
+	if (rc != EOK)
+		return rc;
+	rc = exfat_directory_get(di, &d); 
+	if (rc != EOK)
+		return rc;
+	if (exfat_classify_dentry(d) != EXFAT_DENTRY_STREAM)
+		return ENOENT;
+	*ds  = d->stream;
+	
+	if (ds->name_size > size)
+		return EOVERFLOW;
+
+	for (i=0; i<df->count-1; i++) {
+		rc = exfat_directory_next(di);
+		if (rc != EOK)
+			return rc;
+		rc = exfat_directory_get(di, &d); 
+		if (rc != EOK)
+			return rc;
+		if (exfat_classify_dentry(d) != EXFAT_DENTRY_NAME)
+			return ENOENT;
+		exfat_dentry_get_name(&d->name, ds->name_size, wname, &offset);
+	}
+	rc = utf16_to_str(name, size, wname);
+	if (rc != EOK)
+		return rc;
+
+	exfat_directory_seek(di, start_pos);
+	return EOK;
+}
+
+static uint16_t exfat_directory_set_checksum(const uint8_t *bytes, size_t count)
+{
+	uint16_t checksum = 0;
+	size_t idx;
+
+	for (idx = 0; idx < count; idx++) {
+		if (idx == 2 || idx == 3)
+			continue;
+		checksum = ((checksum << 15) | (checksum >> 1)) + (uint16_t)bytes[idx];
+	}
+	return checksum;
+}
+
+int exfat_directory_sync_file(exfat_directory_t *di, exfat_file_dentry_t *df, 
+    exfat_stream_dentry_t *ds)
+{
+	int rc, i, count;
+	exfat_dentry_t *array=NULL, *de;
+	aoff64_t pos = di->pos;
+
+	rc = exfat_directory_get(di, &de);
+	if (rc != EOK)
+		return rc;
+	count = de->file.count+1;
+	array = (exfat_dentry_t *) malloc(count*sizeof(exfat_dentry_t));
+	if (!array)
+		return ENOMEM;
+	for (i=0; i<count; i++) {
+		rc = exfat_directory_get(di, &de);
+		if (rc != EOK)
+			return rc;
+		array[i] = *de;
+		rc = exfat_directory_next(di);
+		if (rc!=EOK) {
+			free(array);
+			return rc;
+		}
+	}
+	rc = exfat_directory_seek(di, pos);
+	if (rc!=EOK) {
+		free(array);
+		return rc;
+	}
+
+	/* Sync */
+	array[0].file.attr = host2uint16_t_le(df->attr);
+	array[1].stream.firstc = host2uint32_t_le(ds->firstc);
+	array[1].stream.flags = ds->flags;
+	array[1].stream.valid_data_size = host2uint64_t_le(ds->valid_data_size);
+	array[1].stream.data_size = host2uint64_t_le(ds->data_size);
+	array[0].file.checksum = host2uint16_t_le(exfat_directory_set_checksum((uint8_t *)array,
+	    count*sizeof(exfat_dentry_t)));
+
+	/* Store */
+	for (i=0; i<count; i++) {
+		rc = exfat_directory_get(di, &de);
+		if (rc != EOK)
+			return rc;
+		*de = array[i];
+		di->b->dirty = true;
+		rc = exfat_directory_next(di);
+		if (rc!=EOK) {
+			free(array);
+			return rc;
+		}
+	}
+	free(array);
+
+	return EOK;
+}
+
+int exfat_directory_write_file(exfat_directory_t *di, const char *name)
+{
+	fs_node_t *fn;
+	exfat_node_t *uctablep;
+	uint16_t *uctable;
+	exfat_dentry_t df, ds, *de;
+	uint16_t wname[EXFAT_FILENAME_LEN+1];
+	int rc, i;
+	size_t uctable_chars, j;
+	aoff64_t pos;
+
+	rc = str_to_utf16(wname, EXFAT_FILENAME_LEN, name);
+	if (rc != EOK)
+		return rc;
+	rc = exfat_uctable_get(&fn, di->service_id);
+	if (rc != EOK)
+		return rc;
+	uctablep = EXFAT_NODE(fn);
+
+	uctable_chars = ALIGN_DOWN(uctablep->size, sizeof(uint16_t)) / sizeof(uint16_t); 
+	uctable = (uint16_t *) malloc(uctable_chars * sizeof(uint16_t));
+	rc = exfat_read_uctable(di->bs, uctablep, (uint8_t *)uctable);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		free(uctable);
+		return rc;
+	}
+
+	/* Fill stream entry */
+	ds.type = EXFAT_TYPE_STREAM;
+	ds.stream.flags = 0;
+	ds.stream.valid_data_size = 0;
+	ds.stream.data_size = 0;
+	ds.stream.name_size = utf16_length(wname);
+	ds.stream.hash = host2uint16_t_le(exfat_name_hash(wname, uctable, 
+	    uctable_chars));
+
+	/* Fill file entry */
+	df.type = EXFAT_TYPE_FILE;
+	df.file.attr = 0;
+	df.file.count = ROUND_UP(ds.stream.name_size, EXFAT_NAME_PART_LEN) / 
+	    EXFAT_NAME_PART_LEN + 1;
+	df.file.checksum = 0;
+
+	free(uctable);
+	rc = exfat_node_put(fn);
+	if (rc != EOK)
+		return rc;
+
+	/* Looking for set of free entries */
+	rc = exfat_directory_lookup_free(di, df.file.count+1);
+	if (rc != EOK)
+		return rc;
+	pos = di->pos;
+
+	/* Write file entry */
+	rc = exfat_directory_get(di, &de);
+	if (rc != EOK)
+		return rc;
+	*de = df;
+	di->b->dirty = true;
+	rc = exfat_directory_next(di);
+	if (rc != EOK)
+		return rc;
+
+	/* Write stream entry */
+	rc = exfat_directory_get(di, &de);
+	if (rc != EOK)
+		return rc;
+	*de = ds;
+	di->b->dirty = true;
+
+	/* Write file name */
+	size_t chars = EXFAT_NAME_PART_LEN;
+	uint16_t *sname = wname;
+
+	for (i=0; i<ds.stream.name_size; i++)
+		wname[i] = host2uint16_t_le(wname[i]);
+
+	for (i=0; i < df.file.count-1; i++) {
+		rc = exfat_directory_next(di);
+		if (rc != EOK)
+			return rc;
+
+		if (i == df.file.count-2)
+			chars = ds.stream.name_size - EXFAT_NAME_PART_LEN*(df.file.count-2);
+		rc = exfat_directory_get(di, &de);
+		if (rc != EOK)
+			return rc;
+		de->type = EXFAT_TYPE_NAME;
+		/* test */
+		for (j=0; j<chars; j++){
+			de->name.name[j] = *sname;
+			sname++;
+		}
+
+		di->b->dirty = true;
+		sname += chars;
+	}
+	
+	return exfat_directory_seek(di, pos);
+}
+
+int exfat_directory_erase_file(exfat_directory_t *di, aoff64_t pos)
+{
+	int rc, count;
+	exfat_dentry_t *de;
+
+	rc = exfat_directory_get(di, &de);
+	if (rc != EOK)
+		return rc;
+	count = de->file.count+1;
+	
+	while (count) {
+		rc = exfat_directory_get(di, &de);
+		if (rc != EOK)
+			return rc;
+		de->type &= (~EXFAT_TYPE_USED);
+		di->b->dirty = true;
+
+		rc = exfat_directory_next(di);
+		if (rc!=EOK)
+			return rc;
+		count--;
+	}
+	return EOK;
+}
+
+int exfat_directory_expand(exfat_directory_t *di)
+{
+	int rc;
+
+	if (!di->nodep)
+		return ENOSPC;
+
+	rc = exfat_node_expand(di->nodep->idx->service_id, di->nodep, 1);
+	if (rc != EOK)
+		return rc;
+
+	di->fragmented = di->nodep->fragmented;
+	di->nodep->size += BPC(di->bs);
+	di->nodep->dirty = true;		/* need to sync node */
+	di->blocks = di->nodep->size / BPS(di->bs);
+	
+	return EOK;
+}
+
+int exfat_directory_lookup_free(exfat_directory_t *di, size_t count)
+{
+	int rc;
+	exfat_dentry_t *d;
+	size_t found;
+	aoff64_t pos;
+
+	rc = exfat_directory_seek(di, 0);
+	if (rc != EOK)
+		return rc;
+
+	do {
+		found = 0;
+		pos = 0;
+		do {
+			if (exfat_directory_get(di, &d) == EOK) {
+				switch (exfat_classify_dentry(d)) {
+				case EXFAT_DENTRY_LAST:
+				case EXFAT_DENTRY_FREE:
+					if (found==0) pos = di->pos;
+					found++;
+					if (found == count) {
+						exfat_directory_seek(di, pos);
+						return EOK;
+					}
+					break;
+				default:
+					found = 0;
+					break;
+				}
+			}
+		} while (exfat_directory_next(di) == EOK);	
+	} while (exfat_directory_expand(di) == EOK);
+	return ENOSPC;
+}
+
+
+/**
+ * @}
+ */ 
Index: uspace/srv/fs/exfat/exfat_directory.h
===================================================================
--- uspace/srv/fs/exfat/exfat_directory.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_directory.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,87 @@
+/*
+ * 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_EXFAT_DIRECTORY_H_
+#define EXFAT_EXFAT_DIRECTORY_H_
+
+#include <stdint.h>
+#include "exfat.h"
+#include "exfat_fat.h"
+#include "exfat_dentry.h"
+
+typedef struct {
+	/* Directory data */
+	exfat_bs_t *bs;
+	exfat_node_t *nodep;
+	service_id_t service_id;
+	uint32_t blocks;
+	uint32_t bnum;
+	aoff64_t pos;
+	block_t *b;
+	bool last;
+	bool fragmented;
+	exfat_cluster_t firstc;
+} exfat_directory_t;
+
+
+extern void exfat_directory_init(exfat_directory_t *di);
+extern int exfat_directory_open(exfat_node_t *nodep, exfat_directory_t *di);
+extern int exfat_directory_open_parent(exfat_directory_t *di, 
+    service_id_t service_id, exfat_cluster_t firstc, bool fragmented);
+extern int exfat_directory_close(exfat_directory_t *di);
+
+extern int exfat_directory_next(exfat_directory_t *di);
+extern int exfat_directory_prev(exfat_directory_t *di);
+extern int exfat_directory_seek(exfat_directory_t *di, aoff64_t pos);
+extern int exfat_directory_get(exfat_directory_t *di, exfat_dentry_t **de);
+extern int exfat_directory_find(exfat_directory_t *di, 
+    exfat_dentry_clsf_t type, exfat_dentry_t **d);
+extern int exfat_directory_find_continue(exfat_directory_t *di, 
+    exfat_dentry_clsf_t type, exfat_dentry_t **d);
+
+extern int exfat_directory_read_file(exfat_directory_t *di, char *name, 
+    size_t size, exfat_file_dentry_t *df, exfat_stream_dentry_t *ds);
+extern int exfat_directory_sync_file(exfat_directory_t *di, 
+    exfat_file_dentry_t *df, exfat_stream_dentry_t *ds);
+extern int exfat_directory_write_file(exfat_directory_t *di, const char *name);
+extern int exfat_directory_erase_file(exfat_directory_t *di, aoff64_t pos);
+
+extern int exfat_directory_expand(exfat_directory_t *di);
+extern int exfat_directory_lookup_free(exfat_directory_t *di, size_t count);
+extern int exfat_directory_print(exfat_directory_t *di);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_fat.c
===================================================================
--- uspace/srv/fs/exfat/exfat_fat.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_fat.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,546 @@
+/*
+ * 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 Table.
+ */
+
+#include "exfat_fat.h"
+#include "exfat_bitmap.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(exfat_alloc_lock);
+
+/** Walk the cluster chain.
+ *
+ * @param bs		Buffer holding the boot sector for the file.
+ * @param service_id	Service ID of the device with the file.
+ * @param firstc	First cluster to start the walk with.
+ * @param lastc		If non-NULL, output argument hodling the last cluster
+ *			number visited.
+ * @param numc		If non-NULL, output argument holding the number of
+ *			clusters seen during the walk.
+ * @param max_clusters	Maximum number of clusters to visit.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+exfat_cluster_walk(exfat_bs_t *bs, service_id_t service_id, 
+    exfat_cluster_t firstc, exfat_cluster_t *lastc, uint32_t *numc,
+    uint32_t max_clusters)
+{
+	uint32_t clusters = 0;
+	exfat_cluster_t clst = firstc;
+	int rc;
+
+	if (firstc < EXFAT_CLST_FIRST) {
+		/* No space allocated to the file. */
+		if (lastc)
+			*lastc = firstc;
+		if (numc)
+			*numc = 0;
+		return EOK;
+	}
+
+	while (clst != EXFAT_CLST_EOF && clusters < max_clusters) {
+		assert(clst >= EXFAT_CLST_FIRST);
+		if (lastc)
+			*lastc = clst;	/* remember the last cluster number */
+
+		rc = exfat_get_cluster(bs, service_id, clst, &clst);
+		if (rc != EOK)
+			return rc;
+
+		assert(clst != EXFAT_CLST_BAD);
+		clusters++;
+	}
+
+	if (lastc && clst != EXFAT_CLST_EOF)
+		*lastc = clst;
+	if (numc)
+		*numc = clusters;
+
+	return EOK;
+}
+
+/** Read block from file located on a exFAT file system.
+ *
+ * @param block		Pointer to a block pointer for storing result.
+ * @param bs		Buffer holding the boot sector of the file system.
+ * @param nodep		FAT node.
+ * @param bn		Block number.
+ * @param flags		Flags passed to libblock.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+exfat_block_get(block_t **block, exfat_bs_t *bs, exfat_node_t *nodep,
+    aoff64_t bn, int flags)
+{
+	exfat_cluster_t firstc = nodep->firstc;
+	exfat_cluster_t currc;
+	aoff64_t relbn = bn;
+	int rc;
+
+	if (!nodep->size)
+		return ELIMIT;
+
+	if (nodep->fragmented) {
+		if (((((nodep->size - 1) / BPS(bs)) / SPC(bs)) == bn / SPC(bs)) &&
+			nodep->lastc_cached_valid) {
+				/*
+			* This is a request to read a block within the last cluster
+			* when fortunately we have the last cluster number cached.
+			*/
+			return block_get(block, nodep->idx->service_id, DATA_FS(bs) + 
+		        (nodep->lastc_cached_value-EXFAT_CLST_FIRST)*SPC(bs) + 
+			    (bn % SPC(bs)), flags);
+		}
+
+		if (nodep->currc_cached_valid && bn >= nodep->currc_cached_bn) {
+			/*
+			* We can start with the cluster cached by the previous call to
+			* fat_block_get().
+			*/
+			firstc = nodep->currc_cached_value;
+			relbn -= (nodep->currc_cached_bn / SPC(bs)) * SPC(bs);
+		}
+	}
+
+	rc = exfat_block_get_by_clst(block, bs, nodep->idx->service_id,
+	    nodep->fragmented, firstc, &currc, relbn, flags);
+	if (rc != EOK)
+		return rc;
+
+	/*
+	 * Update the "current" cluster cache.
+	 */
+	nodep->currc_cached_valid = true;
+	nodep->currc_cached_bn = bn;
+	nodep->currc_cached_value = currc;
+
+	return rc;
+}
+
+/** Read block from file located on a exFAT file system.
+ *
+ * @param block		Pointer to a block pointer for storing result.
+ * @param bs		Buffer holding the boot sector of the file system.
+ * @param service_id	Service ID of the file system.
+ * @param fcl		First cluster used by the file. Can be zero if the file
+ *			is empty.
+ * @param clp		If not NULL, address where the cluster containing bn
+ *			will be stored.
+ *			stored
+ * @param bn		Block number.
+ * @param flags		Flags passed to libblock.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+exfat_block_get_by_clst(block_t **block, exfat_bs_t *bs, 
+    service_id_t service_id, bool fragmented, exfat_cluster_t fcl,
+    exfat_cluster_t *clp, aoff64_t bn, int flags)
+{
+	uint32_t clusters;
+	uint32_t max_clusters;
+	exfat_cluster_t c;
+	int rc;
+
+	if (fcl < EXFAT_CLST_FIRST || fcl > DATA_CNT(bs)+2)
+		return ELIMIT;
+
+	if (!fragmented) {
+		rc = block_get(block, service_id, DATA_FS(bs) + 
+		    (fcl-EXFAT_CLST_FIRST)*SPC(bs) + bn, flags);
+	} else {
+		max_clusters = bn / SPC(bs);
+		rc = exfat_cluster_walk(bs, service_id, fcl, &c, &clusters, max_clusters);
+		if (rc != EOK)
+			return rc;
+		assert(clusters == max_clusters);
+
+		rc = block_get(block, service_id, DATA_FS(bs) + 
+		    (c-EXFAT_CLST_FIRST)*SPC(bs) + (bn % SPC(bs)), flags);
+
+		if (clp)
+			*clp = c;
+	}
+
+	return rc;
+}
+
+
+/** Get cluster from the FAT.
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param clst		Cluster which to get.
+ * @param value		Output argument holding the value of the cluster.
+ *
+ * @return		EOK or a negative error code.
+ */
+int
+exfat_get_cluster(exfat_bs_t *bs, service_id_t service_id,
+    exfat_cluster_t clst, exfat_cluster_t *value)
+{
+	block_t *b;
+	aoff64_t offset;
+	int rc;
+
+	offset = clst * sizeof(exfat_cluster_t);
+
+	rc = block_get(&b, service_id, FAT_FS(bs) + offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK)
+		return rc;
+
+	*value = uint32_t_le2host(*(uint32_t *)(b->data + offset % BPS(bs)));
+
+	rc = block_put(b);
+
+	return rc;
+}
+
+/** Set cluster in FAT.
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param clst		Cluster which is to be set.
+ * @param value		Value to set the cluster with.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+exfat_set_cluster(exfat_bs_t *bs, service_id_t service_id,
+    exfat_cluster_t clst, exfat_cluster_t value)
+{
+	block_t *b;
+	aoff64_t offset;
+	int rc;
+
+	offset = clst * sizeof(exfat_cluster_t);
+
+	rc = block_get(&b, service_id, FAT_FS(bs) + offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK)
+		return rc;
+
+	*(uint32_t *)(b->data + offset % BPS(bs)) = host2uint32_t_le(value);
+
+	b->dirty = true;	/* need to sync block */
+	rc = block_put(b);
+	return rc;
+}
+
+/** Allocate clusters in FAT.
+ *
+ * This function will attempt to allocate the requested number of clusters in
+ * the FAT.  The FAT will be altered so that the allocated
+ * clusters form an independent chain (i.e. a chain which does not belong to any
+ * file yet).
+ *
+ * @param bs		Buffer holding the boot sector of the file system.
+ * @param service_id	Service ID of the file system.
+ * @param nclsts	Number of clusters to allocate.
+ * @param mcl		Output parameter where the first cluster in the chain
+ *			will be returned.
+ * @param lcl		Output parameter where the last cluster in the chain
+ *			will be returned.
+ *
+ * @return		EOK on success, a negative error code otherwise.
+ */
+int
+exfat_alloc_clusters(exfat_bs_t *bs, service_id_t service_id, unsigned nclsts,
+    exfat_cluster_t *mcl, exfat_cluster_t *lcl)
+{
+	exfat_cluster_t *lifo;    /* stack for storing free cluster numbers */
+	unsigned found = 0;     /* top of the free cluster number stack */
+	exfat_cluster_t clst;
+	int rc = EOK;
+
+	lifo = (exfat_cluster_t *) malloc(nclsts * sizeof(exfat_cluster_t));
+	if (!lifo)
+		return ENOMEM;
+
+	fibril_mutex_lock(&exfat_alloc_lock);
+	for (clst=EXFAT_CLST_FIRST; clst < DATA_CNT(bs)+2 && found < nclsts; clst++) {
+		/* Need to rewrite because of multiple exfat_bitmap_get calls */
+		if (bitmap_is_free(bs, service_id, clst)==EOK) {
+		   /*
+			* The cluster is free. Put it into our stack
+			* of found clusters and mark it as non-free.
+			*/
+			lifo[found] = clst;
+			rc = exfat_set_cluster(bs, service_id, clst,
+				(found == 0) ?  EXFAT_CLST_EOF : lifo[found - 1]);
+			if (rc != EOK)
+				break;
+			found++;
+			rc = bitmap_set_cluster(bs, service_id, clst);
+			if (rc != EOK)
+				break;
+
+		}
+	}
+
+	if (rc == EOK && found == nclsts) {
+		*mcl = lifo[found - 1];
+		*lcl = lifo[0];
+		free(lifo);
+		fibril_mutex_unlock(&exfat_alloc_lock);
+		return EOK;
+	}
+
+	/* If something wrong - free the clusters */
+	if (found > 0) {
+		while (found--) {
+			(void) bitmap_clear_cluster(bs, service_id, lifo[found]);
+			(void) exfat_set_cluster(bs, service_id, lifo[found], 0);
+		}
+	}
+
+	free(lifo);
+	fibril_mutex_unlock(&exfat_alloc_lock);
+	return ENOSPC;
+}
+
+/** Free clusters forming a cluster chain in FAT.
+ *
+ * @param bs		Buffer hodling the boot sector of the file system.
+ * @param service_id	Service ID of the file system.
+ * @param firstc	First cluster in the chain which is to be freed.
+ *
+ * @return		EOK on success or a negative return code.
+ */
+int
+exfat_free_clusters(exfat_bs_t *bs, service_id_t service_id, exfat_cluster_t firstc)
+{
+	exfat_cluster_t nextc;
+	int rc;
+
+	/* Mark all clusters in the chain as free */
+	while (firstc != EXFAT_CLST_EOF) {
+		assert(firstc >= EXFAT_CLST_FIRST && firstc < EXFAT_CLST_BAD);
+		rc = exfat_get_cluster(bs, service_id, firstc, &nextc);
+		if (rc != EOK)
+			return rc;
+		rc = exfat_set_cluster(bs, service_id, firstc, 0);
+		if (rc != EOK)
+			return rc;
+		rc = bitmap_clear_cluster(bs, service_id, firstc);
+		if (rc != EOK)
+			return rc;
+		firstc = nextc;
+	}
+
+	return EOK;
+}
+
+/** Append a cluster chain to the last file cluster in FAT.
+ *
+ * @param bs		Buffer holding the boot sector of the file system.
+ * @param nodep		Node representing the file.
+ * @param mcl		First cluster of the cluster chain to append.
+ * @param lcl		Last cluster of the cluster chain to append.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+exfat_append_clusters(exfat_bs_t *bs, exfat_node_t *nodep, exfat_cluster_t mcl,
+    exfat_cluster_t lcl)
+{
+	service_id_t service_id = nodep->idx->service_id;
+	exfat_cluster_t lastc;
+	int rc;
+
+	if (nodep->firstc == 0) {
+		/* No clusters allocated to the node yet. */
+		nodep->firstc = mcl;
+		nodep->dirty = true;	/* need to sync node */
+	} else {
+		if (nodep->lastc_cached_valid) {
+			lastc = nodep->lastc_cached_value;
+			nodep->lastc_cached_valid = false;
+		} else {
+			rc = exfat_cluster_walk(bs, service_id, nodep->firstc,
+			    &lastc, NULL, (uint16_t) -1);
+			if (rc != EOK)
+				return rc;
+		}
+
+		rc = exfat_set_cluster(bs, nodep->idx->service_id, lastc, mcl);
+		if (rc != EOK)
+			return rc;
+	}
+
+	nodep->lastc_cached_valid = true;
+	nodep->lastc_cached_value = lcl;
+
+	return EOK;
+}
+
+/** Chop off node clusters in FAT.
+ *
+ * @param bs		Buffer holding the boot sector of the file system.
+ * @param nodep		FAT node where the chopping will take place.
+ * @param lcl		Last cluster which will remain in the node. If this
+ *			argument is FAT_CLST_RES0, then all clusters will
+ *			be chopped off.
+ *
+ * @return		EOK on success or a negative return code.
+ */
+int exfat_chop_clusters(exfat_bs_t *bs, exfat_node_t *nodep, exfat_cluster_t lcl)
+{
+	int rc;
+	service_id_t service_id = nodep->idx->service_id;
+
+	/*
+	 * Invalidate cached cluster numbers.
+	 */
+	nodep->lastc_cached_valid = false;
+	if (nodep->currc_cached_value != lcl)
+		nodep->currc_cached_valid = false;
+
+	if (lcl == 0) {
+		/* The node will have zero size and no clusters allocated. */
+		rc = exfat_free_clusters(bs, service_id, nodep->firstc);
+		if (rc != EOK)
+			return rc;
+		nodep->firstc = 0;
+		nodep->dirty = true;		/* need to sync node */
+	} else {
+		exfat_cluster_t nextc;
+
+		rc = exfat_get_cluster(bs, service_id, lcl, &nextc);
+		if (rc != EOK)
+			return rc;
+
+		/* Terminate the cluster chain */
+		rc = exfat_set_cluster(bs, service_id, lcl, EXFAT_CLST_EOF);
+		if (rc != EOK)
+			return rc;
+
+		/* Free all following clusters. */
+		rc = exfat_free_clusters(bs, service_id, nextc);
+		if (rc != EOK)
+			return rc;
+	}
+
+	/*
+	 * Update and re-enable the last cluster cache.
+	 */
+	nodep->lastc_cached_valid = true;
+	nodep->lastc_cached_value = lcl;
+
+	return EOK;
+}
+
+int
+exfat_zero_cluster(exfat_bs_t *bs, service_id_t service_id, exfat_cluster_t c)
+{
+	size_t i;
+	block_t *b;
+	int rc;
+
+	for (i = 0; i < SPC(bs); i++) {
+		rc = exfat_block_get_by_clst(&b, bs, service_id, false, c, NULL, i,
+		    BLOCK_FLAGS_NOREAD);
+		if (rc != EOK)
+			return rc;
+		memset(b->data, 0, BPS(bs));
+		b->dirty = true;
+		rc = block_put(b);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+int
+exfat_read_uctable(exfat_bs_t *bs, exfat_node_t *nodep, uint8_t *uctable)
+{
+	size_t i, blocks, count;
+	block_t *b;
+	int rc;
+	blocks = ROUND_UP(nodep->size, BPS(bs))/BPS(bs);
+	count = BPS(bs);
+	
+	for (i = 0; i < blocks; i++) {
+		rc = exfat_block_get(&b, bs, nodep, i, BLOCK_FLAGS_NOREAD);
+		if (rc != EOK)
+			return rc;
+		if (i == blocks-1)
+			count = nodep->size - i*BPS(bs);
+		memcpy(uctable, b->data, count);
+		uctable += count;
+		rc = block_put(b);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+
+/** 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, service_id_t service_id)
+{
+	/* TODO */
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_fat.h
===================================================================
--- uspace/srv/fs/exfat/exfat_fat.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_fat.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,95 @@
+/*
+ * 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>
+
+#define EXFAT_ROOT_IDX		0
+#define EXFAT_BITMAP_IDX	1
+#define EXFAT_UCTABLE_IDX	2
+
+#define EXFAT_ROOT_PAR	0
+#define EXFAT_ROOT_POS	0
+
+#define EXFAT_CLST_FIRST	0x00000002
+#define EXFAT_CLST_LAST		0xfffffff6
+#define EXFAT_CLST_BAD		0xfffffff7
+#define EXFAT_CLST_EOF		0xffffffff
+
+/* forward declarations */
+struct block;
+struct exfat_node;
+struct exfat_bs;
+
+typedef uint32_t exfat_cluster_t;
+
+
+#define exfat_clusters_get(numc, bs, sid, fc) \
+    exfat_cluster_walk((bs), (sid), (fc), NULL, (numc), (uint32_t) -1)
+extern int exfat_cluster_walk(struct exfat_bs *bs, service_id_t service_id, 
+    exfat_cluster_t firstc, exfat_cluster_t *lastc, uint32_t *numc,
+    uint32_t max_clusters);
+extern int exfat_block_get(block_t **block, struct exfat_bs *bs,
+    struct exfat_node *nodep, aoff64_t bn, int flags);
+extern int exfat_block_get_by_clst(block_t **block, struct exfat_bs *bs, 
+    service_id_t service_id, bool fragmented, exfat_cluster_t fcl,
+    exfat_cluster_t *clp, aoff64_t bn, int flags);
+
+extern int exfat_get_cluster(struct exfat_bs *bs, service_id_t service_id,
+    exfat_cluster_t clst, exfat_cluster_t *value);
+extern int exfat_set_cluster(struct exfat_bs *bs, service_id_t service_id,
+    exfat_cluster_t clst, exfat_cluster_t value);
+extern int exfat_sanity_check(struct exfat_bs *, service_id_t);
+
+extern int exfat_append_clusters(struct exfat_bs *, struct exfat_node *,
+    exfat_cluster_t, exfat_cluster_t);
+extern int exfat_chop_clusters(struct exfat_bs *, struct exfat_node *,
+    exfat_cluster_t);
+extern int exfat_alloc_clusters(struct exfat_bs *, service_id_t, unsigned,
+    exfat_cluster_t *, exfat_cluster_t *);
+extern int exfat_free_clusters(struct exfat_bs *, service_id_t, exfat_cluster_t);
+extern int exfat_zero_cluster(struct exfat_bs * bs, service_id_t service_id, 
+    exfat_cluster_t mcl);
+
+extern int exfat_read_uctable(struct exfat_bs *bs, struct exfat_node *nodep, 
+    uint8_t *uctable);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/exfat/exfat_idx.c
===================================================================
--- uspace/srv/fs/exfat/exfat_idx.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_idx.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -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;
+	service_id_t service_id;
+
+	/** Next unassigned index. */
+	fs_index_t next;
+	/** Number of remaining unassigned indices. */
+	uint64_t remaining;
+
+	/** Sorted list of intervals of freed indices. */
+	list_t freed_list;
+} unused_t;
+
+/** Mutex protecting the list of unused structures. */
+static FIBRIL_MUTEX_INITIALIZE(unused_lock);
+
+/** List of unused structures. */
+static LIST_INITIALIZE(unused_list);
+
+static void unused_initialize(unused_t *u, service_id_t service_id)
+{
+	link_initialize(&u->link);
+	u->service_id = service_id;
+	u->next = 0;
+	u->remaining = ((uint64_t)((fs_index_t)-1)) + 1;
+	list_initialize(&u->freed_list);
+}
+
+static unused_t *unused_find(service_id_t service_id, bool lock)
+{
+	unused_t *u;
+
+	if (lock)
+		fibril_mutex_lock(&unused_lock);
+	list_foreach(unused_list, l) {
+		u = list_get_instance(l, unused_t, link);
+		if (u->service_id == service_id) 
+			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 service_id, 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_SID_KEY	0
+#define UPH_PFC_KEY	1
+#define UPH_PDI_KEY	2
+
+static hash_index_t pos_hash(unsigned long key[])
+{
+	service_id_t service_id = (service_id_t)key[UPH_SID_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 |= (service_id & ((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)
+{
+	service_id_t service_id = (service_id_t)key[UPH_SID_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 (service_id == fidx->service_id);
+	case 3:
+		pfc = (exfat_cluster_t) key[UPH_PFC_KEY];
+		pdi = (unsigned) key[UPH_PDI_KEY];
+		return (service_id == fidx->service_id) && (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 service_id and index.
+ */
+static hash_table_t ui_hash;
+
+#define UIH_BUCKETS_LOG	12
+#define UIH_BUCKETS	(1 << UIH_BUCKETS_LOG)
+
+#define UIH_SID_KEY	0
+#define UIH_INDEX_KEY	1
+
+static hash_index_t idx_hash(unsigned long key[])
+{
+	service_id_t service_id = (service_id_t)key[UIH_SID_KEY];
+	fs_index_t index = (fs_index_t)key[UIH_INDEX_KEY];
+
+	hash_index_t h;
+
+	h = service_id & ((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)
+{
+	service_id_t service_id = (service_id_t)key[UIH_SID_KEY];
+	fs_index_t index;
+	exfat_idx_t *fidx = list_get_instance(item, exfat_idx_t, uih_link);
+
+	switch (keys) {
+	case 1:
+		return (service_id == fidx->service_id);
+	case 2:
+		index = (fs_index_t) key[UIH_INDEX_KEY];
+		return (service_id == fidx->service_id) &&
+		    (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(service_id_t service_id, fs_index_t *index)
+{
+	unused_t *u;
+	
+	assert(index);
+	u = unused_find(service_id, true);
+	if (!u)
+		return false;	
+
+	if (list_empty(&u->freed_list)) {
+		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(list_first(&u->freed_list),
+		    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(service_id_t service_id, fs_index_t index)
+{
+	unused_t *u;
+
+	u = unused_find(service_id, 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_list.head.next; lnk != &u->freed_list.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_list.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_list.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_list);
+	}
+	fibril_mutex_unlock(&unused_lock);
+}
+
+static int exfat_idx_create(exfat_idx_t **fidxp, service_id_t service_id)
+{
+	exfat_idx_t *fidx;
+
+	fidx = (exfat_idx_t *) malloc(sizeof(exfat_idx_t));
+	if (!fidx) 
+		return ENOMEM;
+	if (!exfat_index_alloc(service_id, &fidx->index)) {
+		free(fidx);
+		return ENOSPC;
+	}
+		
+	link_initialize(&fidx->uph_link);
+	link_initialize(&fidx->uih_link);
+	fibril_mutex_initialize(&fidx->lock);
+	fidx->service_id = service_id;
+	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, service_id_t service_id)
+{
+	exfat_idx_t *fidx;
+	int rc;
+
+	fibril_mutex_lock(&used_lock);
+	rc = exfat_idx_create(&fidx, service_id);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&used_lock);
+		return rc;
+	}
+		
+	unsigned long ikey[] = {
+		[UIH_SID_KEY] = service_id,
+		[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(service_id_t service_id, exfat_cluster_t pfc, unsigned pdi)
+{
+	exfat_idx_t *fidx;
+	link_t *l;
+	unsigned long pkey[] = {
+		[UPH_SID_KEY] = service_id,
+		[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, service_id);
+		if (rc != EOK) {
+			fibril_mutex_unlock(&used_lock);
+			return NULL;
+		}
+		
+		unsigned long ikey[] = {
+			[UIH_SID_KEY] = service_id,
+			[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_SID_KEY] = idx->service_id,
+		[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_SID_KEY] = idx->service_id,
+		[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(service_id_t service_id, fs_index_t index)
+{
+	exfat_idx_t *fidx = NULL;
+	link_t *l;
+	unsigned long ikey[] = {
+		[UIH_SID_KEY] = service_id,
+		[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_SID_KEY] = idx->service_id,
+		[UIH_INDEX_KEY] = idx->index,
+	};
+	service_id_t service_id = idx->service_id;
+	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(service_id, 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_service_id(service_id_t service_id)
+{
+	unused_t *u;
+	int rc = EOK;
+
+	u = (unused_t *) malloc(sizeof(unused_t));
+	if (!u)
+		return ENOMEM;
+	unused_initialize(u, service_id);
+	fibril_mutex_lock(&unused_lock);
+	if (!unused_find(service_id, false)) {
+		list_append(&u->link, &unused_list);
+	} else {
+		free(u);
+		rc = EEXIST;
+	}
+	fibril_mutex_unlock(&unused_lock);
+	return rc;
+}
+
+void exfat_idx_fini_by_service_id(service_id_t service_id)
+{
+	unsigned long ikey[] = {
+		[UIH_SID_KEY] = service_id 
+	};
+	unsigned long pkey[] = {
+		[UPH_SID_KEY] = service_id 
+	};
+
+	/*
+	 * 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(service_id, true);
+	assert(u);
+	list_remove(&u->link);
+	fibril_mutex_unlock(&unused_lock);
+
+	while (!list_empty(&u->freed_list)) {
+		freed_t *f;
+		f = list_get_instance(list_first(&u->freed_list), 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 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/exfat/exfat_ops.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,1482 @@
+/*
+ * 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_ops.c
+ * @brief	Implementation of VFS operations for the exFAT file system server.
+ */
+
+#include "exfat.h"
+#include "exfat_fat.h"
+#include "exfat_dentry.h"
+#include "exfat_directory.h"
+#include "exfat_bitmap.h"
+#include "../../vfs/vfs.h"
+#include <libfs.h>
+#include <libblock.h>
+#include <ipc/services.h>
+#include <ipc/loc.h>
+#include <macros.h>
+#include <async.h>
+#include <errno.h>
+#include <str.h>
+#include <byteorder.h>
+#include <adt/hash_table.h>
+#include <adt/list.h>
+#include <assert.h>
+#include <fibril_synch.h>
+#include <sys/mman.h>
+#include <align.h>
+#include <malloc.h>
+#include <stdio.h>
+
+/** 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_list);
+
+/*
+ * Forward declarations of FAT libfs operations.
+ */
+
+static int exfat_root_get(fs_node_t **, service_id_t);
+static int exfat_match(fs_node_t **, fs_node_t *, const char *);
+static int exfat_node_get(fs_node_t **, service_id_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 **, service_id_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 bool exfat_is_directory(fs_node_t *);
+static bool exfat_is_file(fs_node_t *node);
+static service_id_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 = EXFAT_UNKNOW;
+	link_initialize(&node->ffn_link);
+	node->size = 0;
+	node->lnkcnt = 0;
+	node->refcnt = 0;
+	node->dirty = false;
+	node->fragmented = 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)
+{
+	int rc;
+	exfat_directory_t di;
+	exfat_file_dentry_t df;
+	exfat_stream_dentry_t ds;
+
+	if (!(node->type == EXFAT_DIRECTORY || node->type == EXFAT_FILE))
+		return EOK;
+
+	if (node->type == EXFAT_DIRECTORY)
+		df.attr = EXFAT_ATTR_SUBDIR;
+	else
+		df.attr = 0;
+
+	ds.firstc = node->firstc;
+	if (node->size == 0 && node->firstc == 0) {
+		ds.flags = 0;
+	} else {
+		ds.flags = 1;
+		ds.flags |= (!node->fragmented << 1);
+	}
+	ds.valid_data_size = node->size;
+	ds.data_size = node->size;
+
+	exfat_directory_open_parent(&di, node->idx->service_id, node->idx->pfc, 
+	    node->idx->parent_fragmented);
+	rc = exfat_directory_seek(&di, node->idx->pdi);
+	if (rc != EOK) {
+		(void) exfat_directory_close(&di);
+		return rc;
+	}
+
+	rc = exfat_directory_sync_file(&di, &df, &ds);
+	if (rc != EOK) {
+		(void) exfat_directory_close(&di);
+		return rc;
+	}
+	return exfat_directory_close(&di);
+}
+
+static int exfat_node_fini_by_service_id(service_id_t service_id)
+{
+	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);
+	list_foreach(ffn_list, lnk) {
+		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->service_id != service_id) {
+			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_list. */
+		goto restart;
+	}
+	fibril_mutex_unlock(&ffn_mutex);
+
+	return EOK;
+}
+
+static int exfat_node_get_new(exfat_node_t **nodepp)
+{
+	fs_node_t *fn;
+	exfat_node_t *nodep;
+	int rc;
+
+	fibril_mutex_lock(&ffn_mutex);
+	if (!list_empty(&ffn_list)) {
+		/* Try to use a cached free node structure. */
+		exfat_idx_t *idxp_tmp;
+		nodep = list_get_instance(list_first(&ffn_list), exfat_node_t,
+		    ffn_link);
+		if (!fibril_mutex_trylock(&nodep->lock))
+			goto skip_cache;
+		idxp_tmp = nodep->idx;
+		if (!fibril_mutex_trylock(&idxp_tmp->lock)) {
+			fibril_mutex_unlock(&nodep->lock);
+			goto skip_cache;
+		}
+		list_remove(&nodep->ffn_link);
+		fibril_mutex_unlock(&ffn_mutex);
+		if (nodep->dirty) {
+			rc = exfat_node_sync(nodep);
+			if (rc != EOK) {
+				idxp_tmp->nodep = NULL;
+				fibril_mutex_unlock(&nodep->lock);
+				fibril_mutex_unlock(&idxp_tmp->lock);
+				free(nodep->bp);
+				free(nodep);
+				return rc;
+			}
+		}
+		idxp_tmp->nodep = NULL;
+		fibril_mutex_unlock(&nodep->lock);
+		fibril_mutex_unlock(&idxp_tmp->lock);
+		fn = FS_NODE(nodep);
+	} else {
+skip_cache:
+		/* Try to allocate a new node structure. */
+		fibril_mutex_unlock(&ffn_mutex);
+		fn = (fs_node_t *)malloc(sizeof(fs_node_t));
+		if (!fn)
+			return ENOMEM;
+		nodep = (exfat_node_t *)malloc(sizeof(exfat_node_t));
+		if (!nodep) {
+			free(fn);
+			return ENOMEM;
+		}
+	}
+	exfat_node_initialize(nodep);
+	fs_node_initialize(fn);
+	fn->data = nodep;
+	nodep->bp = fn;
+
+	*nodepp = nodep;
+	return EOK;
+}
+
+static int exfat_node_get_new_by_pos(exfat_node_t **nodepp, 
+    service_id_t service_id, exfat_cluster_t pfc, unsigned pdi)
+{
+	exfat_idx_t *idxp = exfat_idx_get_by_pos(service_id, pfc, pdi);
+	if (!idxp)
+		return ENOMEM;
+	if (exfat_node_get_new(nodepp) != EOK)
+		return ENOMEM;
+	(*nodepp)->idx = idxp;
+	idxp->nodep = *nodepp;
+	return EOK;
+}
+
+
+/** Internal version of exfat_node_get().
+ *
+ * @param idxp		Locked index structure.
+ */
+static int exfat_node_get_core(exfat_node_t **nodepp, exfat_idx_t *idxp)
+{
+	exfat_dentry_t *d;
+	exfat_node_t *nodep = NULL;
+	exfat_directory_t di;
+	int rc;
+
+	if (idxp->nodep) {
+		/*
+		 * We are lucky.
+		 * The node is already instantiated in memory.
+		 */
+		fibril_mutex_lock(&idxp->nodep->lock);
+		if (!idxp->nodep->refcnt++) {
+			fibril_mutex_lock(&ffn_mutex);
+			list_remove(&idxp->nodep->ffn_link);
+			fibril_mutex_unlock(&ffn_mutex);
+		}
+		fibril_mutex_unlock(&idxp->nodep->lock);
+		*nodepp = idxp->nodep;
+		return EOK;
+	}
+
+	/*
+	 * We must instantiate the node from the file system.
+	 */
+
+	assert(idxp->pfc);
+
+	rc = exfat_node_get_new(&nodep);
+	if (rc != EOK)
+		return rc;
+
+	exfat_directory_open_parent(&di, idxp->service_id, idxp->pfc, 
+	    idxp->parent_fragmented);
+	rc = exfat_directory_seek(&di, idxp->pdi);
+	if (rc != EOK) {
+		(void) exfat_directory_close(&di);
+		(void) exfat_node_put(FS_NODE(nodep));
+		return rc;
+	}
+	rc = exfat_directory_get(&di, &d);
+	if (rc != EOK) {
+		(void) exfat_directory_close(&di);
+		(void) exfat_node_put(FS_NODE(nodep));
+		return rc;
+	}
+
+	switch (exfat_classify_dentry(d)) {
+	case EXFAT_DENTRY_FILE:
+		nodep->type = (uint16_t_le2host(d->file.attr) & EXFAT_ATTR_SUBDIR)? 
+		    EXFAT_DIRECTORY : EXFAT_FILE;
+		rc = exfat_directory_next(&di);
+		if (rc != EOK) {
+			(void) exfat_directory_close(&di);
+			(void) exfat_node_put(FS_NODE(nodep));
+			return rc;
+		}
+		rc = exfat_directory_get(&di, &d);
+		if (rc != EOK) {
+			(void) exfat_directory_close(&di);
+			(void) exfat_node_put(FS_NODE(nodep));
+			return rc;
+		}
+		nodep->firstc = uint32_t_le2host(d->stream.firstc);
+		nodep->size = uint64_t_le2host(d->stream.data_size);
+		nodep->fragmented = (d->stream.flags & 0x02) == 0;
+		break;
+	case EXFAT_DENTRY_BITMAP:
+		nodep->type = EXFAT_BITMAP;
+		nodep->firstc = uint32_t_le2host(d->bitmap.firstc);
+		nodep->size = uint64_t_le2host(d->bitmap.size);
+		nodep->fragmented = true;
+		break;
+	case EXFAT_DENTRY_UCTABLE:
+		nodep->type = EXFAT_UCTABLE;
+		nodep->firstc = uint32_t_le2host(d->uctable.firstc);
+		nodep->size = uint64_t_le2host(d->uctable.size);
+		nodep->fragmented = true;
+		break;
+	default:
+	case EXFAT_DENTRY_SKIP:
+	case EXFAT_DENTRY_LAST:
+	case EXFAT_DENTRY_FREE:
+	case EXFAT_DENTRY_VOLLABEL:
+	case EXFAT_DENTRY_GUID:
+	case EXFAT_DENTRY_STREAM:
+	case EXFAT_DENTRY_NAME:
+		(void) exfat_directory_close(&di);
+		(void) exfat_node_put(FS_NODE(nodep));
+		return ENOENT;
+	}
+
+	nodep->lnkcnt = 1;
+	nodep->refcnt = 1;
+
+	rc = exfat_directory_close(&di);
+	if (rc != EOK) {
+		(void) exfat_node_put(FS_NODE(nodep));
+		return rc;
+	}
+
+	/* Link the idx structure with the node structure. */
+	nodep->idx = idxp;
+	idxp->nodep = nodep;
+
+	*nodepp = nodep;
+	return EOK;
+}
+
+int exfat_node_expand(service_id_t service_id, exfat_node_t *nodep, exfat_cluster_t clusters)
+{
+	exfat_bs_t *bs;
+	int rc;
+	bs = block_bb_get(service_id);
+
+	if (!nodep->fragmented) {
+		rc = bitmap_append_clusters(bs, nodep, clusters);
+		if (rc != ENOSPC)
+			return rc;
+		if (rc == ENOSPC) {
+			nodep->fragmented = true;
+			nodep->dirty = true;		/* need to sync node */
+			rc = bitmap_replicate_clusters(bs, nodep);
+			if (rc != EOK)
+				return rc;
+		}
+	}
+
+	/* If we cant linear expand the node, we should use FAT instead */
+	exfat_cluster_t mcl, lcl;
+
+	/* create an independent chain of nclsts clusters in all FATs */
+	rc = exfat_alloc_clusters(bs, service_id, clusters, &mcl, &lcl);
+	if (rc != EOK)
+		return rc;
+	rc = exfat_zero_cluster(bs, service_id, mcl);
+	if (rc != EOK) {
+		(void) exfat_free_clusters(bs, service_id, mcl);
+		return rc;
+	}
+	/*
+	 * Append the cluster chain starting in mcl to the end of the
+	 * node's cluster chain.
+	 */
+	rc = exfat_append_clusters(bs, nodep, mcl, lcl);
+	if (rc != EOK) {
+		(void) exfat_free_clusters(bs, service_id, mcl);
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int exfat_node_shrink(service_id_t service_id, exfat_node_t *nodep, aoff64_t size)
+{
+	exfat_bs_t *bs;
+	int rc;
+	bs = block_bb_get(service_id);
+
+	if (!nodep->fragmented) {
+		exfat_cluster_t clsts, prev_clsts, new_clsts;
+		prev_clsts = ROUND_UP(nodep->size, BPC(bs)) / BPC(bs);
+		new_clsts =  ROUND_UP(size, BPC(bs)) / BPC(bs);
+
+		assert(new_clsts < prev_clsts);
+
+		clsts = prev_clsts - new_clsts;
+		rc = bitmap_free_clusters(bs, nodep, clsts);
+		if (rc != EOK)
+			return rc;
+	} else {
+		/*
+		 * The node will be shrunk, clusters will be deallocated.
+		 */
+		if (size == 0) {
+			rc = exfat_chop_clusters(bs, nodep, 0);
+			if (rc != EOK)
+				return rc;
+		} else {
+			exfat_cluster_t lastc;
+			rc = exfat_cluster_walk(bs, service_id, nodep->firstc,
+			    &lastc, NULL, (size - 1) / BPC(bs));
+			if (rc != EOK)
+				return rc;
+			rc = exfat_chop_clusters(bs, nodep, lastc);
+			if (rc != EOK)
+				return rc;
+		}
+	}
+
+	nodep->size = size;
+	nodep->dirty = true;		/* need to sync node */
+	return EOK;
+}
+
+
+/*
+ * EXFAT libfs operations.
+ */
+
+int exfat_root_get(fs_node_t **rfn, service_id_t service_id)
+{
+	return exfat_node_get(rfn, service_id, EXFAT_ROOT_IDX);
+}
+
+int exfat_bitmap_get(fs_node_t **rfn, service_id_t service_id)
+{
+	return exfat_node_get(rfn, service_id, EXFAT_BITMAP_IDX);
+}
+
+int exfat_uctable_get(fs_node_t **rfn, service_id_t service_id)
+{
+	return exfat_node_get(rfn, service_id, EXFAT_UCTABLE_IDX);
+}
+
+
+int exfat_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
+{
+	exfat_node_t *parentp = EXFAT_NODE(pfn);
+	char name[EXFAT_FILENAME_LEN+1];
+	exfat_file_dentry_t df;
+	exfat_stream_dentry_t ds;
+	service_id_t service_id;
+	int rc;
+
+	fibril_mutex_lock(&parentp->idx->lock);
+	service_id = parentp->idx->service_id;
+	fibril_mutex_unlock(&parentp->idx->lock);
+	
+	exfat_directory_t di;
+	rc = exfat_directory_open(parentp, &di);
+	if (rc != EOK)
+		return rc;
+
+	while (exfat_directory_read_file(&di, name, EXFAT_FILENAME_LEN, 
+	    &df, &ds) == EOK) {
+		if (stricmp(name, component) == 0) {
+			/* hit */
+			exfat_node_t *nodep;
+			aoff64_t o = di.pos % (BPS(di.bs) / sizeof(exfat_dentry_t));
+			exfat_idx_t *idx = exfat_idx_get_by_pos(service_id,
+				parentp->firstc, di.bnum * DPS(di.bs) + o);
+			if (!idx) {
+				/*
+				 * Can happen if memory is low or if we
+				 * run out of 32-bit indices.
+				 */
+				rc = exfat_directory_close(&di);
+				return (rc == EOK) ? ENOMEM : rc;
+			}
+			rc = exfat_node_get_core(&nodep, idx);
+			fibril_mutex_unlock(&idx->lock);
+			if (rc != EOK) {
+				(void) exfat_directory_close(&di);
+				return rc;
+			}
+			*rfn = FS_NODE(nodep);
+			rc = exfat_directory_close(&di);
+			if (rc != EOK)
+				(void) exfat_node_put(*rfn);
+			return rc;
+		} else {
+			rc = exfat_directory_next(&di);
+			if (rc != EOK)
+				break;
+		}
+	}
+	(void) exfat_directory_close(&di);
+	*rfn = NULL;
+	return EOK;
+}
+
+/** Instantiate a exFAT in-core node. */
+int exfat_node_get(fs_node_t **rfn, service_id_t service_id, fs_index_t index)
+{
+	exfat_node_t *nodep;
+	exfat_idx_t *idxp;
+	int rc;
+
+	idxp = exfat_idx_get_by_index(service_id, index);
+	if (!idxp) {
+		*rfn = NULL;
+		return EOK;
+	}
+	/* idxp->lock held */
+	rc = exfat_node_get_core(&nodep, idxp);
+	fibril_mutex_unlock(&idxp->lock);
+	if (rc == EOK)
+		*rfn = FS_NODE(nodep);
+	return rc;
+}
+
+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)
+{
+	exfat_node_t *nodep = EXFAT_NODE(fn);
+	bool destroy = false;
+
+	fibril_mutex_lock(&nodep->lock);
+	if (!--nodep->refcnt) {
+		if (nodep->idx) {
+			fibril_mutex_lock(&ffn_mutex);
+			list_append(&nodep->ffn_link, &ffn_list);
+			fibril_mutex_unlock(&ffn_mutex);
+		} else {
+			/*
+			 * The node does not have any index structure associated
+			 * with itself. This can only mean that we are releasing
+			 * the node after a failed attempt to allocate the index
+			 * structure for it.
+			 */
+			destroy = true;
+		}
+	}
+	fibril_mutex_unlock(&nodep->lock);
+	if (destroy) {
+		free(nodep->bp);
+		free(nodep);
+	}
+	return EOK;
+}
+
+int exfat_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
+{
+	exfat_idx_t *idxp;
+	exfat_node_t *nodep;
+	exfat_bs_t *bs;
+	int rc;
+
+	bs = block_bb_get(service_id);
+	rc = exfat_node_get_new(&nodep);
+	if (rc != EOK)
+		return rc;
+
+	rc = exfat_idx_get_new(&idxp, service_id);
+	if (rc != EOK) {
+		(void) exfat_node_put(FS_NODE(nodep));
+		return rc;
+	}
+
+	nodep->firstc = 0;
+	nodep->size = 0;
+	nodep->fragmented = false;
+	nodep->lnkcnt = 0;	/* not linked anywhere */
+	nodep->refcnt = 1;
+	nodep->dirty = true;
+
+	nodep->idx = idxp;
+	idxp->nodep = nodep;
+	fibril_mutex_unlock(&idxp->lock);
+
+	if (flags & L_DIRECTORY) {
+		nodep->type = EXFAT_DIRECTORY;
+		rc = exfat_node_expand(service_id, nodep, 1);
+		if (rc != EOK) {
+			(void) exfat_node_put(FS_NODE(nodep));
+			return rc;
+		}
+		nodep->size = BPC(bs);
+	} else {
+		nodep->type = EXFAT_FILE;
+	}
+
+	*rfn = FS_NODE(nodep);
+	return EOK;
+}
+
+int exfat_destroy_node(fs_node_t *fn)
+{
+	exfat_node_t *nodep = EXFAT_NODE(fn);
+	exfat_bs_t *bs;
+	bool has_children;
+	int rc;
+
+	/*
+	 * The node is not reachable from the file system. This means that the
+	 * link count should be zero and that the index structure cannot be
+	 * found in the position hash. Obviously, we don't need to lock the node
+	 * nor its index structure.
+	 */
+	assert(nodep->lnkcnt == 0);
+
+	/*
+	 * The node may not have any children.
+	 */
+	rc = exfat_has_children(&has_children, fn);
+	if (rc != EOK)
+		return rc;
+	assert(!has_children);
+
+	bs = block_bb_get(nodep->idx->service_id);
+	if (nodep->firstc != 0) {
+		assert(nodep->size);
+		/* Free all clusters allocated to the node. */
+		if (nodep->fragmented)
+			rc = exfat_free_clusters(bs, nodep->idx->service_id,
+				nodep->firstc);
+		else
+			rc = bitmap_free_clusters(bs, nodep, 
+			    ROUND_UP(nodep->size, BPC(bs)) / BPC(bs));
+	} 
+
+	exfat_idx_destroy(nodep->idx);
+	free(nodep->bp);
+	free(nodep);
+	return rc;
+}
+
+int exfat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
+{
+	exfat_node_t *parentp = EXFAT_NODE(pfn);
+	exfat_node_t *childp = EXFAT_NODE(cfn);
+	exfat_directory_t di;
+	int rc;
+
+	fibril_mutex_lock(&childp->lock);
+	if (childp->lnkcnt == 1) {
+		/*
+		 * We don't support multiple hard links.
+		 */
+		fibril_mutex_unlock(&childp->lock);
+		return EMLINK;
+	}
+	assert(childp->lnkcnt == 0);
+	fibril_mutex_unlock(&childp->lock);
+
+	if (!exfat_valid_name(name))
+		return ENOTSUP;
+
+	fibril_mutex_lock(&parentp->idx->lock);
+	rc = exfat_directory_open(parentp, &di);
+	if (rc != EOK)
+		return rc;
+	/*
+	 * At this point we only establish the link between the parent and the
+	 * child.  The dentry, except of the name and the extension, will remain
+	 * uninitialized until the corresponding node is synced. Thus the valid
+	 * dentry data is kept in the child node structure.
+	 */
+	rc = exfat_directory_write_file(&di, name);
+	if (rc!=EOK)
+		return rc;
+	rc = exfat_directory_close(&di);
+	if (rc!=EOK)
+		return rc;
+
+	fibril_mutex_unlock(&parentp->idx->lock);
+	if (rc != EOK)
+		return rc;
+
+	fibril_mutex_lock(&childp->idx->lock);
+
+
+	childp->idx->pfc = parentp->firstc;
+	childp->idx->parent_fragmented = parentp->fragmented;
+	childp->idx->pdi = di.pos;
+	fibril_mutex_unlock(&childp->idx->lock);
+
+	fibril_mutex_lock(&childp->lock);
+	childp->lnkcnt = 1;
+	childp->dirty = true;		/* need to sync node */
+	fibril_mutex_unlock(&childp->lock);
+
+	/*
+	 * Hash in the index structure into the position hash.
+	 */
+	exfat_idx_hashin(childp->idx);
+
+	return EOK;
+
+}
+
+int exfat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
+{
+	exfat_node_t *parentp = EXFAT_NODE(pfn);
+	exfat_node_t *childp = EXFAT_NODE(cfn);
+	bool has_children;
+	int rc;
+
+	if (!parentp)
+		return EBUSY;
+
+	rc = exfat_has_children(&has_children, cfn);
+	if (rc != EOK)
+		return rc;
+	if (has_children)
+		return ENOTEMPTY;
+
+	fibril_mutex_lock(&parentp->lock);
+	fibril_mutex_lock(&childp->lock);
+	assert(childp->lnkcnt == 1);
+	fibril_mutex_lock(&childp->idx->lock);
+	
+	exfat_directory_t di;
+	rc = exfat_directory_open(parentp,&di);
+	if (rc != EOK)
+		goto error;
+	rc = exfat_directory_erase_file(&di, childp->idx->pdi);
+	if (rc != EOK)
+		goto error;
+	rc = exfat_directory_close(&di);
+	if (rc != EOK)
+		goto error;
+
+	/* remove the index structure from the position hash */
+	exfat_idx_hashout(childp->idx);
+	/* clear position information */
+	childp->idx->pfc = 0;
+	childp->idx->pdi = 0;
+	fibril_mutex_unlock(&childp->idx->lock);
+	childp->lnkcnt = 0;
+	childp->refcnt++;	/* keep the node in memory until destroyed */
+	childp->dirty = true;
+	fibril_mutex_unlock(&childp->lock);
+	fibril_mutex_unlock(&parentp->lock);
+
+	return EOK;
+
+error:
+	(void) exfat_directory_close(&di);
+	fibril_mutex_unlock(&childp->idx->lock);
+	fibril_mutex_unlock(&childp->lock);
+	fibril_mutex_unlock(&parentp->lock);
+	return rc;
+
+}
+
+int exfat_has_children(bool *has_children, fs_node_t *fn)
+{
+	exfat_directory_t di;
+	exfat_dentry_t *d;
+	exfat_node_t *nodep = EXFAT_NODE(fn);
+	int rc;
+
+	*has_children = false;
+
+	if (nodep->type != EXFAT_DIRECTORY)
+		return EOK;
+
+	fibril_mutex_lock(&nodep->idx->lock);
+
+	rc = exfat_directory_open(nodep, &di);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&nodep->idx->lock);
+		return rc;
+	}
+
+	do {
+		rc = exfat_directory_get(&di, &d);
+		if (rc != EOK) {
+			(void) exfat_directory_close(&di);
+			fibril_mutex_unlock(&nodep->idx->lock);
+			return rc;
+		}
+		switch (exfat_classify_dentry(d)) {
+		case EXFAT_DENTRY_SKIP:
+		case EXFAT_DENTRY_FREE:
+			continue;
+		case EXFAT_DENTRY_LAST:
+			*has_children = false;
+			goto exit;
+		default:
+			*has_children = true;
+			goto exit;
+		}
+	} while (exfat_directory_next(&di) == EOK);
+
+exit:
+	rc = exfat_directory_close(&di);
+	fibril_mutex_unlock(&nodep->idx->lock);
+	return rc;
+}
+
+
+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;
+}
+
+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;
+}
+
+service_id_t exfat_device_get(fs_node_t *node)
+{
+	return 0;
+}
+
+
+/** libfs operations */
+libfs_ops_t exfat_libfs_ops = {
+	.root_get = exfat_root_get,
+	.match = exfat_match,
+	.node_get = exfat_node_get,
+	.node_open = exfat_node_open,
+	.node_put = exfat_node_put,
+	.create = exfat_create_node,
+	.destroy = exfat_destroy_node,
+	.link = exfat_link,
+	.unlink = exfat_unlink,
+	.has_children = exfat_has_children,
+	.index_get = exfat_index_get,
+	.size_get = exfat_size_get,
+	.lnkcnt_get = exfat_lnkcnt_get,
+	.is_directory = exfat_is_directory,
+	.is_file = exfat_is_file,
+	.device_get = exfat_device_get
+};
+
+
+/*
+ * VFS_OUT operations.
+ */
+
+/* Print debug info */
+/*
+static void exfat_fsinfo(exfat_bs_t *bs, service_id_t service_id)
+{
+	printf("exFAT file system mounted\n");
+	printf("Version: %d.%d\n", bs->version.major, bs->version.minor);
+	printf("Volume serial: %d\n", uint32_t_le2host(bs->volume_serial));
+	printf("Volume first sector: %lld\n", VOL_FS(bs));
+	printf("Volume sectors: %lld\n", VOL_CNT(bs));
+	printf("FAT first sector: %d\n", FAT_FS(bs));
+	printf("FAT sectors: %d\n", FAT_CNT(bs));
+	printf("Data first sector: %d\n", DATA_FS(bs));
+	printf("Data sectors: %d\n", DATA_CNT(bs));
+	printf("Root dir first cluster: %d\n", ROOT_FC(bs));
+	printf("Bytes per sector: %d\n", BPS(bs));
+	printf("Sectors per cluster: %d\n", SPC(bs));
+	printf("KBytes per cluster: %d\n", SPC(bs)*BPS(bs)/1024);
+
+	int i, rc;
+	exfat_cluster_t clst;
+	for (i=0; i<=7; i++) {
+		rc = exfat_get_cluster(bs, service_id, i, &clst);
+		if (rc != EOK)
+			return;
+		printf("Clst %d: %x", i, clst);
+		if (i>=2)
+			printf(", Bitmap: %d\n", bitmap_is_free(bs, service_id, i)!=EOK);
+		else
+			printf("\n");
+	}
+}
+*/
+ 
+static int
+exfat_mounted(service_id_t service_id, const char *opts, fs_index_t *index,
+    aoff64_t *size, unsigned *linkcnt)
+{
+	int rc;
+	exfat_node_t *rootp=NULL, *bitmapp=NULL, *uctablep=NULL;
+	enum cache_mode cmode;
+	exfat_bs_t *bs;
+
+	/* Check for option enabling write through. */
+	if (str_cmp(opts, "wtcache") == 0)
+		cmode = CACHE_MODE_WT;
+	else
+		cmode = CACHE_MODE_WB;
+
+	/* initialize libblock */
+	rc = block_init(EXCHANGE_SERIALIZE, service_id, BS_SIZE);
+	if (rc != EOK)
+		return rc;
+
+	/* prepare the boot block */
+	rc = block_bb_read(service_id, BS_BLOCK);
+	if (rc != EOK) {
+		block_fini(service_id);
+		return rc;
+	}
+
+	/* get the buffer with the boot sector */
+	bs = block_bb_get(service_id);
+
+	/* Initialize the block cache */
+	rc = block_cache_init(service_id, BPS(bs), 0 /* XXX */, cmode);
+	if (rc != EOK) {
+		block_fini(service_id);
+		return rc;
+	}
+
+	/* Do some simple sanity checks on the file system. */
+	rc = exfat_sanity_check(bs, service_id);
+	if (rc != EOK) {
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		return rc;
+	}
+
+	rc = exfat_idx_init_by_service_id(service_id);
+	if (rc != EOK) {
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		return rc;
+	}
+
+	/* Initialize the root node. */
+	rc = exfat_node_get_new_by_pos(&rootp, service_id, EXFAT_ROOT_PAR, 
+	    EXFAT_ROOT_POS);
+	if (rc!=EOK) {
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOMEM;
+	}
+	assert(rootp->idx->index == EXFAT_ROOT_IDX);
+
+	rootp->type = EXFAT_DIRECTORY;
+	rootp->firstc = ROOT_FC(bs);
+	rootp->fragmented = true;
+	rootp->refcnt = 1;
+	rootp->lnkcnt = 0;	/* FS root is not linked */
+
+	uint32_t clusters;
+	rc = exfat_clusters_get(&clusters, bs, service_id, rootp->firstc);
+	if (rc != EOK) {
+		free(rootp);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOTSUP;
+	}
+	rootp->size = BPS(bs) * SPC(bs) * clusters;
+	fibril_mutex_unlock(&rootp->idx->lock);
+
+	/* Open root directory and looking for Bitmap and UC-Table */
+	exfat_directory_t di;
+	exfat_dentry_t *de;
+	rc = exfat_directory_open(rootp, &di);
+	if (rc != EOK) {
+		free(rootp);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOTSUP;
+	}
+
+	/* Initialize the bitmap node. */
+	rc = exfat_directory_find(&di, EXFAT_DENTRY_BITMAP, &de);
+	if (rc != EOK) {
+		free(rootp);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOTSUP;
+	}
+
+	rc = exfat_node_get_new_by_pos(&bitmapp, service_id, rootp->firstc, 
+	    di.pos);
+	if (rc!=EOK) {
+		free(rootp);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOMEM;
+	}
+	assert(bitmapp->idx->index == EXFAT_BITMAP_IDX);
+	fibril_mutex_unlock(&bitmapp->idx->lock);
+
+	bitmapp->type = EXFAT_BITMAP;
+	bitmapp->firstc = uint32_t_le2host(de->bitmap.firstc);
+	bitmapp->fragmented = true;
+	bitmapp->idx->parent_fragmented = true;
+	bitmapp->refcnt = 1;
+	bitmapp->lnkcnt = 0;
+	bitmapp->size = uint64_t_le2host(de->bitmap.size);
+
+	/* Initialize the uctable node. */
+	rc = exfat_directory_seek(&di, 0);
+	if (rc != EOK) {
+		free(rootp);
+		free(bitmapp);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOTSUP;
+	}
+
+	rc = exfat_directory_find(&di, EXFAT_DENTRY_UCTABLE, &de);
+	if (rc != EOK) {
+		free(rootp);
+		free(bitmapp);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOTSUP;
+	}
+
+	rc = exfat_node_get_new_by_pos(&uctablep, service_id, rootp->firstc, 
+	    di.pos);
+	if (rc!=EOK) {
+		free(rootp);
+		free(bitmapp);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOMEM;
+	}
+	assert(uctablep->idx->index == EXFAT_UCTABLE_IDX);
+	fibril_mutex_unlock(&uctablep->idx->lock);
+
+	uctablep->type = EXFAT_UCTABLE;
+	uctablep->firstc = uint32_t_le2host(de->uctable.firstc);
+	uctablep->fragmented = true;
+	uctablep->idx->parent_fragmented = true;
+	uctablep->refcnt = 1;
+	uctablep->lnkcnt = 0;
+	uctablep->size = uint64_t_le2host(de->uctable.size);
+
+	rc = exfat_directory_close(&di);
+	if (rc!=EOK) {
+		free(rootp);
+		free(bitmapp);
+		free(uctablep);
+		(void) block_cache_fini(service_id);
+		block_fini(service_id);
+		exfat_idx_fini_by_service_id(service_id);
+		return ENOMEM;
+	}
+
+	/* exfat_fsinfo(bs, service_id); */
+
+	*index = rootp->idx->index;
+	*size = rootp->size;
+	*linkcnt = rootp->lnkcnt;
+	
+	return EOK;
+}
+
+static int exfat_unmounted(service_id_t service_id)
+{
+	fs_node_t *fn;
+	exfat_node_t *nodep;
+	int rc;
+
+	rc = exfat_root_get(&fn, service_id);
+	if (rc != EOK)
+		return rc;
+	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);
+		return EBUSY;
+	}
+
+	/*
+	 * Put the root node and force it to the FAT free node list.
+	 */
+	(void) exfat_node_put(fn);
+	(void) exfat_node_put(fn);
+
+	/*
+	 * Perform cleanup of the node structures, index structures and
+	 * associated data. Write back this file system's dirty blocks and
+	 * stop using libblock for this instance.
+	 */
+	(void) exfat_node_fini_by_service_id(service_id);
+	exfat_idx_fini_by_service_id(service_id);
+	(void) block_cache_fini(service_id);
+	block_fini(service_id);
+
+	return EOK;
+}
+
+static int
+exfat_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
+    size_t *rbytes)
+{
+	fs_node_t *fn;
+	exfat_node_t *nodep;
+	exfat_bs_t *bs;
+	size_t bytes=0;
+	block_t *b;
+	int rc;
+
+	rc = exfat_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
+	nodep = EXFAT_NODE(fn);
+
+	ipc_callid_t callid;
+	size_t len;
+	if (!async_data_read_receive(&callid, &len)) {
+		exfat_node_put(fn);
+		async_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+
+	bs = block_bb_get(service_id);
+
+	if (nodep->type == EXFAT_FILE) {
+		/*
+		 * Our strategy for regular file reads is to read one block at
+		 * most and make use of the possibility to return less data than
+		 * requested. This keeps the code very simple.
+		 */
+		if (pos >= nodep->size) {
+			/* reading beyond the EOF */
+			bytes = 0;
+			(void) async_data_read_finalize(callid, NULL, 0);
+		} else {
+			bytes = min(len, BPS(bs) - pos % BPS(bs));
+			bytes = min(bytes, nodep->size - pos);
+			rc = exfat_block_get(&b, bs, nodep, pos / BPS(bs),
+			    BLOCK_FLAGS_NONE);
+			if (rc != EOK) {
+				exfat_node_put(fn);
+				async_answer_0(callid, rc);
+				return rc;
+			}
+			(void) async_data_read_finalize(callid,
+			    b->data + pos % BPS(bs), bytes);
+			rc = block_put(b);
+			if (rc != EOK) {
+				exfat_node_put(fn);
+				return rc;
+			}
+		}
+	} else {
+		if (nodep->type != EXFAT_DIRECTORY) {
+			async_answer_0(callid, ENOTSUP);
+			return ENOTSUP;
+		}
+			
+		aoff64_t spos = pos;
+		char name[EXFAT_FILENAME_LEN+1];
+		exfat_file_dentry_t df;
+		exfat_stream_dentry_t ds;
+
+		assert(nodep->size % BPS(bs) == 0);
+		assert(BPS(bs) % sizeof(exfat_dentry_t) == 0);
+
+		exfat_directory_t di;
+		rc = exfat_directory_open(nodep, &di);
+		if (rc != EOK) goto err;
+		rc = exfat_directory_seek(&di, pos);
+		if (rc != EOK) {
+			(void) exfat_directory_close(&di);
+			goto err;
+		}
+
+		rc = exfat_directory_read_file(&di, name, EXFAT_FILENAME_LEN, &df, &ds);
+		if (rc == EOK) goto hit;
+		if (rc == ENOENT) goto miss;
+
+err:
+		(void) exfat_node_put(fn);
+		async_answer_0(callid, rc);
+		return rc;
+
+miss:
+		rc = exfat_directory_close(&di);
+		if (rc!=EOK)
+			goto err;
+		rc = exfat_node_put(fn);
+		async_answer_0(callid, rc != EOK ? rc : ENOENT);
+		*rbytes = 0;
+		return rc != EOK ? rc : ENOENT;
+
+hit:
+		pos = di.pos;
+		rc = exfat_directory_close(&di);
+		if (rc!=EOK)
+			goto err;
+		(void) async_data_read_finalize(callid, name, str_size(name) + 1);
+		bytes = (pos - spos)+1;
+	}
+
+	rc = exfat_node_put(fn);
+	*rbytes = bytes;
+	return rc;
+}
+
+static int exfat_close(service_id_t service_id, fs_index_t index)
+{
+	return EOK;
+}
+
+static int exfat_sync(service_id_t service_id, fs_index_t index)
+{
+	fs_node_t *fn;
+	int rc = exfat_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
+
+	exfat_node_t *nodep = EXFAT_NODE(fn);
+
+	nodep->dirty = true;
+	rc = exfat_node_sync(nodep);
+
+	exfat_node_put(fn);
+	return rc;
+}
+
+static int
+exfat_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
+    size_t *wbytes, aoff64_t *nsize)
+{
+	fs_node_t *fn;
+	exfat_node_t *nodep;
+	exfat_bs_t *bs;
+	size_t bytes;
+	block_t *b;
+	aoff64_t boundary;
+	int flags = BLOCK_FLAGS_NONE;
+	int rc;
+
+	rc = exfat_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
+	nodep = EXFAT_NODE(fn);
+
+	ipc_callid_t callid;
+	size_t len;
+	if (!async_data_write_receive(&callid, &len)) {
+		(void) exfat_node_put(fn);
+		async_answer_0(callid, EINVAL);
+		return EINVAL;
+	}
+
+	bs = block_bb_get(service_id);
+
+	/*
+	 * In all scenarios, we will attempt to write out only one block worth
+	 * of data at maximum. There might be some more efficient approaches,
+	 * but this one greatly simplifies fat_write(). Note that we can afford
+	 * to do this because the client must be ready to handle the return
+	 * value signalizing a smaller number of bytes written.
+	 */
+	bytes = min(len, BPS(bs) - pos % BPS(bs));
+	if (bytes == BPS(bs))
+		flags |= BLOCK_FLAGS_NOREAD;
+
+	boundary = ROUND_UP(nodep->size, BPC(bs));
+	if (pos >= boundary) {
+		unsigned nclsts;
+		nclsts = (ROUND_UP(pos + bytes, BPC(bs)) - boundary) / BPC(bs);
+		rc = exfat_node_expand(service_id, nodep, nclsts);
+		if (rc != EOK) {
+			/* could not expand node */
+			(void) exfat_node_put(fn);
+			async_answer_0(callid, rc);
+			return rc;
+		}
+	}
+
+	if (pos + bytes > nodep->size) {
+		nodep->size = pos + bytes;
+		nodep->dirty = true;	/* need to sync node */
+	}
+
+	/*
+	 * This is the easier case - we are either overwriting already
+	 * existing contents or writing behind the EOF, but still within
+	 * the limits of the last cluster. The node size may grow to the
+	 * next block size boundary.
+	 */
+	rc = exfat_block_get(&b, bs, nodep, pos / BPS(bs), flags);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		async_answer_0(callid, rc);
+		return rc;
+	}
+
+	(void) async_data_write_finalize(callid,
+		b->data + pos % BPS(bs), bytes);
+	b->dirty = true;		/* need to sync block */
+	rc = block_put(b);
+	if (rc != EOK) {
+		(void) exfat_node_put(fn);
+		return rc;
+	}
+
+
+	*wbytes = bytes;
+	*nsize = nodep->size;
+	rc = exfat_node_put(fn);
+	return rc;
+}
+
+static int
+exfat_truncate(service_id_t service_id, fs_index_t index, aoff64_t size)
+{
+	fs_node_t *fn;
+	exfat_node_t *nodep;
+	exfat_bs_t *bs;
+	int rc;
+
+	rc = exfat_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
+	nodep = EXFAT_NODE(fn);
+
+	bs = block_bb_get(service_id);
+
+	if (nodep->size == size) {
+		rc = EOK;
+	} else if (nodep->size < size) {
+		/*
+		 * The standard says we have the freedom to grow the node.
+		 * For now, we simply return an error.
+		 */
+		rc = EINVAL;
+	} else if (ROUND_UP(nodep->size, BPC(bs)) == ROUND_UP(size, BPC(bs))) {
+		/*
+		 * The node will be shrunk, but no clusters will be deallocated.
+		 */
+		nodep->size = size;
+		nodep->dirty = true;		/* need to sync node */
+		rc = EOK;
+	} else {
+		rc = exfat_node_shrink(service_id, nodep, size);
+	}
+
+	(void) exfat_node_put(fn);
+	return rc;
+}
+
+static int exfat_destroy(service_id_t service_id, fs_index_t index)
+{
+	fs_node_t *fn;
+	exfat_node_t *nodep;
+	int rc;
+
+	rc = exfat_node_get(&fn, service_id, index);
+	if (rc != EOK)
+		return rc;
+	if (!fn)
+		return ENOENT;
+
+	nodep = EXFAT_NODE(fn);
+	/*
+	 * We should have exactly two references. One for the above
+	 * call to fat_node_get() and one from fat_unlink().
+	 */
+	assert(nodep->refcnt == 2);
+
+	rc = exfat_destroy_node(fn);
+	return rc;
+}
+
+vfs_out_ops_t exfat_ops = {
+	.mounted = exfat_mounted,
+	.unmounted = exfat_unmounted,
+	.read = exfat_read,
+	.write = exfat_write,
+	.truncate = exfat_truncate,
+	.close = exfat_close,
+	.destroy = exfat_destroy,
+	.sync = exfat_sync,
+};
+
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/fat/Makefile
===================================================================
--- uspace/srv/fs/fat/Makefile	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/Makefile	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -39,4 +39,5 @@
 	fat_idx.c \
 	fat_dentry.c \
+	fat_directory.c \
 	fat_fat.c
 
Index: uspace/srv/fs/fat/fat.c
===================================================================
--- uspace/srv/fs/fat/fat.c	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/fat.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -2,4 +2,5 @@
  * Copyright (c) 2006 Martin Decky
  * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
Index: uspace/srv/fs/fat/fat.h
===================================================================
--- uspace/srv/fs/fat/fat.h	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/fat.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -55,5 +56,7 @@
 #define RSCNT(bs)	uint16_t_le2host((bs)->rscnt)
 #define FATCNT(bs)	(bs)->fatcnt
-#define SF(bs)		uint16_t_le2host((bs)->sec_per_fat)
+#define SF(bs)		(uint16_t_le2host((bs)->sec_per_fat) !=0 ? \
+    uint16_t_le2host((bs)->sec_per_fat) : \
+    uint32_t_le2host(bs->fat32.sectors_per_fat))
 #define RDE(bs)		uint16_t_le2host((bs)->root_ent_max)
 #define TS(bs)		(uint16_t_le2host((bs)->totsec16) != 0 ? \
Index: uspace/srv/fs/fat/fat_dentry.c
===================================================================
--- uspace/srv/fs/fat/fat_dentry.c	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/fat_dentry.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -39,12 +40,7 @@
 #include <ctype.h>
 #include <str.h>
-
-static bool is_d_char(const char ch)
-{
-	if (isalnum(ch) || ch == '_')
-		return true;
-	else
-		return false;
-}
+#include <errno.h>
+#include <byteorder.h>
+#include <assert.h>
 
 /** Compare path component with the name read from the dentry.
@@ -80,10 +76,297 @@
 }
 
-bool fat_dentry_name_verify(const char *name)
+void fat_dentry_name_get(const fat_dentry_t *d, char *buf)
+{
+	unsigned int i;
+	
+	for (i = 0; i < FAT_NAME_LEN; i++) {
+		if (d->name[i] == FAT_PAD)
+			break;
+		
+		if (d->name[i] == FAT_DENTRY_E5_ESC)
+			*buf++ = 0xe5;
+		else {
+			if (d->lcase & FAT_LCASE_LOWER_NAME)
+				*buf++ = tolower(d->name[i]);
+			else
+				*buf++ = d->name[i];
+		}
+	}
+	
+	if (d->ext[0] != FAT_PAD)
+		*buf++ = '.';
+	
+	for (i = 0; i < FAT_EXT_LEN; i++) {
+		if (d->ext[i] == FAT_PAD) {
+			*buf = '\0';
+			return;
+		}
+		
+		if (d->ext[i] == FAT_DENTRY_E5_ESC)
+			*buf++ = 0xe5;
+		else {
+			if (d->lcase & FAT_LCASE_LOWER_EXT)
+				*buf++ = tolower(d->ext[i]);
+			else
+				*buf++ = d->ext[i];
+		}
+	}
+	
+	*buf = '\0';
+}
+
+void fat_dentry_name_set(fat_dentry_t *d, const char *name)
+{
+	unsigned int i;
+	const char fake_ext[] = "   ";
+	bool lower_name = true;
+	bool lower_ext = true;
+	
+	for (i = 0; i < FAT_NAME_LEN; i++) {
+		switch ((uint8_t) *name) {
+		case 0xe5:
+			d->name[i] = FAT_DENTRY_E5_ESC;
+			name++;
+			break;
+		case '\0':
+		case '.':
+			d->name[i] = FAT_PAD;
+			break;
+		default:
+			if (isalpha(*name)) {
+				if (!islower(*name))
+					lower_name = false;
+			}
+			
+			d->name[i] = toupper(*name++);
+			break;
+		}
+	}
+	
+	if (*name++ != '.')
+		name = fake_ext;
+	
+	for (i = 0; i < FAT_EXT_LEN; i++) {
+		switch ((uint8_t) *name) {
+		case 0xe5:
+			d->ext[i] = FAT_DENTRY_E5_ESC;
+			name++;
+			break;
+		case '\0':
+			d->ext[i] = FAT_PAD;
+			break;
+		default:
+			if (isalpha(*name)) {
+				if (!islower(*name))
+					lower_ext = false;
+			}
+			
+			d->ext[i] = toupper(*name++);
+			break;
+		}
+	}
+	
+	if (lower_name)
+		d->lcase |= FAT_LCASE_LOWER_NAME;
+	else
+		d->lcase &= ~FAT_LCASE_LOWER_NAME;
+	
+	if (lower_ext)
+		d->lcase |= FAT_LCASE_LOWER_EXT;
+	else
+		d->lcase &= ~FAT_LCASE_LOWER_EXT;
+}
+
+fat_dentry_clsf_t fat_classify_dentry(const fat_dentry_t *d)
+{
+	if (d->attr == FAT_ATTR_LFN) {
+		/* long name entry */
+		if (FAT_LFN_ORDER(d) & FAT_LFN_ERASED)
+			return FAT_DENTRY_FREE;
+		else
+			return FAT_DENTRY_LFN;
+	}
+	if (d->attr & FAT_ATTR_VOLLABEL) {
+		/* volume label entry */
+		return FAT_DENTRY_SKIP;
+	}
+	if (d->name[0] == FAT_DENTRY_ERASED) {
+		/* not-currently-used entry */
+		return FAT_DENTRY_FREE;
+	}
+	if (d->name[0] == FAT_DENTRY_UNUSED) {
+		/* never used entry */
+		return FAT_DENTRY_LAST;
+	}
+	if (d->name[0] == FAT_DENTRY_DOT) {
+		/*
+		 * Most likely '.' or '..'.
+		 * It cannot occur in a regular file name.
+		 */
+		return FAT_DENTRY_SKIP;
+	}
+	return FAT_DENTRY_VALID;
+}
+
+/** Compute checksum of Node name.
+ *
+ * Returns an unsigned byte checksum computed on an unsigned byte
+ * array. The array must be 11 bytes long and is assumed to contain
+ * a name stored in the format of a MS-DOS directory entry.
+ *
+ * @param name		Node name read from the dentry.
+ *
+ * @return		An 8-bit unsigned checksum of the name.
+ */
+uint8_t fat_dentry_chksum(uint8_t *name)
+{
+	uint8_t i, sum=0;
+	for (i=0; i<(FAT_NAME_LEN+FAT_EXT_LEN); i++) {
+		sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + name[i];
+	}
+	return sum;
+}
+
+/** Get number of bytes in a string with size limit.
+ *
+ * @param str  NULL-terminated (or not) string.
+ * @param size Maximum number of bytes to consider.
+ *
+ * @return Number of bytes in string (without 0 and ff).
+ *
+ */
+size_t fat_lfn_str_nlength(const uint16_t *str, size_t size)
+{
+	size_t offset = 0;
+
+	while (offset < size) {
+		if (str[offset] == 0 || str[offset] == FAT_LFN_PAD) 
+			break;
+		offset++;
+	}
+	return offset;
+}
+
+/** Get number of bytes in a FAT long entry occuped by characters.
+ *
+ * @param d FAT long entry.
+ *
+ * @return Number of bytes.
+ *
+ */
+size_t fat_lfn_size(const fat_dentry_t *d)
+{
+	size_t size = 0;
+	
+	size += fat_lfn_str_nlength(FAT_LFN_PART1(d), FAT_LFN_PART1_SIZE);
+	size += fat_lfn_str_nlength(FAT_LFN_PART2(d), FAT_LFN_PART2_SIZE);
+	size += fat_lfn_str_nlength(FAT_LFN_PART3(d), FAT_LFN_PART3_SIZE);	
+	
+	return size;
+}
+
+size_t fat_lfn_get_entry(const fat_dentry_t *d, uint16_t *dst, size_t *offset)
+{
+	int i;
+	for (i=FAT_LFN_PART3_SIZE-1; i>=0 && *offset>0; i--) {
+		if (d->lfn.part3[i] == 0 || d->lfn.part3[i] == FAT_LFN_PAD)
+			continue;
+		(*offset)--;
+		dst[(*offset)] = uint16_t_le2host(d->lfn.part3[i]);
+	}
+	for (i=FAT_LFN_PART2_SIZE-1; i>=0 && *offset>0; i--) {
+		if (d->lfn.part2[i] == 0 || d->lfn.part2[i] == FAT_LFN_PAD)
+			continue;
+		(*offset)--;
+		dst[(*offset)] = uint16_t_le2host(d->lfn.part2[i]);
+	}
+	for (i=FAT_LFN_PART1_SIZE-1; i>=0 && *offset>0; i--) {
+		if (d->lfn.part1[i] == 0 || d->lfn.part1[i] == FAT_LFN_PAD)
+			continue;
+		(*offset)--;
+		dst[(*offset)] = uint16_t_le2host(d->lfn.part1[i]);
+	}
+	return *offset;
+}
+
+size_t fat_lfn_set_entry(const uint16_t *src, size_t *offset, size_t size, fat_dentry_t *d)
+{
+	size_t idx;
+	for (idx=0; idx < FAT_LFN_PART1_SIZE; idx++) {
+		if (*offset < size) {
+			d->lfn.part1[idx] = host2uint16_t_le(src[*offset]);
+			(*offset)++;
+		}
+		else
+			d->lfn.part1[idx] = FAT_LFN_PAD;
+	}
+	for (idx=0; idx < FAT_LFN_PART2_SIZE; idx++) {
+		if (*offset < size) {
+			d->lfn.part2[idx] = host2uint16_t_le(src[*offset]);
+			(*offset)++;
+		}
+		else
+			d->lfn.part2[idx] = FAT_LFN_PAD;
+	}
+	for (idx=0; idx < FAT_LFN_PART3_SIZE; idx++) {
+		if (*offset < size) {
+			d->lfn.part3[idx] = host2uint16_t_le(src[*offset]);
+			(*offset)++;
+		}
+		else
+			d->lfn.part3[idx] = FAT_LFN_PAD;
+	}
+
+	if (src[*offset] == 0)
+		offset++;
+	FAT_LFN_ATTR(d) = FAT_ATTR_LFN;
+	d->lfn.type = 0;
+	d->lfn.firstc_lo = 0;
+	
+	return *offset;
+}
+
+void str_to_ascii(char *dst, const char *src, size_t count, uint8_t pad)
+{
+	wchar_t ch;
+	size_t off = 0;
+	size_t i = 0;
+	
+	while (i < count) {
+		if ((ch = str_decode(src, &off, STR_NO_LIMIT)) != 0) {
+			if (ascii_check(ch) & IS_D_CHAR(ch))
+				*dst = toupper(ch);
+			else
+				*dst = pad;
+		}
+		else
+			break;
+
+		dst++;
+		i++;
+	}
+	*dst = '\0';
+}
+
+bool fat_valid_name(const char *name)
+{
+	wchar_t ch;
+	size_t offset=0;
+	bool result = true;
+	
+	while ((ch = str_decode(name, &offset, STR_NO_LIMIT)) != 0) {
+		if (wstr_chr(FAT_STOP_CHARS, ch) != NULL) {
+			result = false;
+			break;
+		}
+	}
+	return result;
+}
+
+bool fat_valid_short_name(const char *name)
 {
 	unsigned int i;
 	unsigned int dot = 0;
 	bool dot_found = false;
-	
 
 	for (i = 0; name[i]; i++) {
@@ -96,5 +379,5 @@
 			}
 		} else {
-			if (!is_d_char(name[i]))
+			if (!IS_D_CHAR(name[i]))
 				return false;
 		}
@@ -114,128 +397,12 @@
 }
 
-void fat_dentry_name_get(const fat_dentry_t *d, char *buf)
-{
-	unsigned int i;
-	
-	for (i = 0; i < FAT_NAME_LEN; i++) {
-		if (d->name[i] == FAT_PAD)
-			break;
-		
-		if (d->name[i] == FAT_DENTRY_E5_ESC)
-			*buf++ = 0xe5;
-		else {
-			if (d->lcase & FAT_LCASE_LOWER_NAME)
-				*buf++ = tolower(d->name[i]);
-			else
-				*buf++ = d->name[i];
-		}
-	}
-	
-	if (d->ext[0] != FAT_PAD)
-		*buf++ = '.';
-	
-	for (i = 0; i < FAT_EXT_LEN; i++) {
-		if (d->ext[i] == FAT_PAD) {
-			*buf = '\0';
-			return;
-		}
-		
-		if (d->ext[i] == FAT_DENTRY_E5_ESC)
-			*buf++ = 0xe5;
-		else {
-			if (d->lcase & FAT_LCASE_LOWER_EXT)
-				*buf++ = tolower(d->ext[i]);
-			else
-				*buf++ = d->ext[i];
-		}
-	}
-	
-	*buf = '\0';
-}
-
-void fat_dentry_name_set(fat_dentry_t *d, const char *name)
-{
-	unsigned int i;
-	const char fake_ext[] = "   ";
-	bool lower_name = true;
-	bool lower_ext = true;
-	
-	for (i = 0; i < FAT_NAME_LEN; i++) {
-		switch ((uint8_t) *name) {
-		case 0xe5:
-			d->name[i] = FAT_DENTRY_E5_ESC;
-			name++;
-			break;
-		case '\0':
-		case '.':
-			d->name[i] = FAT_PAD;
-			break;
-		default:
-			if (isalpha(*name)) {
-				if (!islower(*name))
-					lower_name = false;
-			}
-			
-			d->name[i] = toupper(*name++);
-			break;
-		}
-	}
-	
-	if (*name++ != '.')
-		name = fake_ext;
-	
-	for (i = 0; i < FAT_EXT_LEN; i++) {
-		switch ((uint8_t) *name) {
-		case 0xe5:
-			d->ext[i] = FAT_DENTRY_E5_ESC;
-			name++;
-			break;
-		case '\0':
-			d->ext[i] = FAT_PAD;
-			break;
-		default:
-			if (isalpha(*name)) {
-				if (!islower(*name))
-					lower_ext = false;
-			}
-			
-			d->ext[i] = toupper(*name++);
-			break;
-		}
-	}
-	
-	if (lower_name)
-		d->lcase |= FAT_LCASE_LOWER_NAME;
-	else
-		d->lcase &= ~FAT_LCASE_LOWER_NAME;
-	
-	if (lower_ext)
-		d->lcase |= FAT_LCASE_LOWER_EXT;
-	else
-		d->lcase &= ~FAT_LCASE_LOWER_EXT;
-}
-
-fat_dentry_clsf_t fat_classify_dentry(const fat_dentry_t *d)
-{
-	if (d->attr & FAT_ATTR_VOLLABEL) {
-		/* volume label entry */
-		return FAT_DENTRY_SKIP;
-	}
-	if (d->name[0] == FAT_DENTRY_ERASED) {
-		/* not-currently-used entry */
-		return FAT_DENTRY_FREE;
-	}
-	if (d->name[0] == FAT_DENTRY_UNUSED) {
-		/* never used entry */
-		return FAT_DENTRY_LAST;
-	}
-	if (d->name[0] == FAT_DENTRY_DOT) {
-		/*
-		 * Most likely '.' or '..'.
-		 * It cannot occur in a regular file name.
-		 */
-		return FAT_DENTRY_SKIP;
-	}
-	return FAT_DENTRY_VALID;
+size_t utf16_length(const uint16_t *wstr)
+{
+	size_t len = 0;
+	
+	while (*wstr++ != 0)
+		len++;
+	
+	return len;
 }
 
Index: uspace/srv/fs/fat/fat_dentry.h
===================================================================
--- uspace/srv/fs/fat/fat_dentry.h	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/fat_dentry.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -37,4 +38,7 @@
 #include <bool.h>
 
+#define IS_D_CHAR(ch) (isalnum(ch) || ch == '_')
+#define FAT_STOP_CHARS L"*?/\\\n\t|'"
+
 #define FAT_NAME_LEN		8
 #define FAT_EXT_LEN		3
@@ -44,12 +48,19 @@
 #define FAT_EXT_PAD		"   "
 
-#define FAT_ATTR_RDONLY		(1 << 0)
-#define FAT_ATTR_VOLLABEL	(1 << 3)
-#define FAT_ATTR_SUBDIR		(1 << 4)
-
+#define FAT_ATTR_RDONLY   0x01
+#define FAT_ATTR_HIDDEN   0x02
+#define FAT_ATTR_SYSTEM   0x04
+#define FAT_ATTR_VOLLABEL 0x08
+#define FAT_ATTR_SUBDIR   0x10
+#define FAT_ATTR_ARCHIVE  0x20
+#define FAT_ATTR_LFN \
+    (FAT_ATTR_RDONLY | FAT_ATTR_HIDDEN | FAT_ATTR_SYSTEM | FAT_ATTR_VOLLABEL)
+    
 #define FAT_LCASE_LOWER_NAME	0x08
 #define FAT_LCASE_LOWER_EXT	0x10
 
-#define FAT_PAD			' ' 
+#define FAT_PAD			' '
+#define FAT_LFN_PAD	0xffff
+#define FAT_SFN_CHAR '_'
 
 #define FAT_DENTRY_UNUSED	0x00
@@ -57,4 +68,25 @@
 #define FAT_DENTRY_DOT		0x2e
 #define FAT_DENTRY_ERASED	0xe5
+#define FAT_LFN_LAST		0x40
+#define FAT_LFN_ERASED		0x80
+
+#define FAT_LFN_ORDER(d) (d->lfn.order)
+#define FAT_IS_LFN(d) \
+    ((FAT_LFN_ORDER(d) & FAT_LFN_LAST) == FAT_LFN_LAST)
+#define FAT_LFN_COUNT(d) \
+    (FAT_LFN_ORDER(d) ^ FAT_LFN_LAST)
+#define FAT_LFN_PART1(d) (d->lfn.part1)
+#define FAT_LFN_PART2(d) (d->lfn.part2)
+#define FAT_LFN_PART3(d) (d->lfn.part3)
+#define FAT_LFN_ATTR(d) (d->lfn.attr)
+#define FAT_LFN_CHKSUM(d) (d->lfn.check_sum)
+
+#define FAT_LFN_NAME_SIZE   260
+#define FAT_LFN_MAX_COUNT   20
+#define FAT_LFN_PART1_SIZE  5
+#define FAT_LFN_PART2_SIZE  6
+#define FAT_LFN_PART3_SIZE  2
+#define FAT_LFN_ENTRY_SIZE \
+    (FAT_LFN_PART1_SIZE + FAT_LFN_PART2_SIZE + FAT_LFN_PART3_SIZE)
 
 typedef enum {
@@ -62,34 +94,62 @@
 	FAT_DENTRY_LAST,
 	FAT_DENTRY_FREE,
-	FAT_DENTRY_VALID
+	FAT_DENTRY_VALID,
+	FAT_DENTRY_LFN
 } fat_dentry_clsf_t;
 
 typedef struct {
-	uint8_t		name[8];
-	uint8_t		ext[3];
-	uint8_t		attr;
-	uint8_t		lcase;
-	uint8_t		ctime_fine;
-	uint16_t	ctime;
-	uint16_t	cdate;
-	uint16_t	adate;
 	union {
-		uint16_t	eaidx;		/* FAT12/FAT16 */
-		uint16_t	firstc_hi;	/* FAT32 */
-	} __attribute__ ((packed));
-	uint16_t	mtime;
-	uint16_t	mdate;
-	union {
-		uint16_t	firstc;		/* FAT12/FAT16 */
-		uint16_t	firstc_lo;	/* FAT32 */
-	} __attribute__ ((packed));
-	uint32_t	size;
+		struct {
+			uint8_t		name[8];
+			uint8_t		ext[3];
+			uint8_t		attr;
+			uint8_t		lcase;
+			uint8_t		ctime_fine;
+			uint16_t	ctime;
+			uint16_t	cdate;
+			uint16_t	adate;
+			union {
+				uint16_t	eaidx;		/* FAT12/FAT16 */
+				uint16_t	firstc_hi;	/* FAT32 */
+			} __attribute__ ((packed));
+			uint16_t	mtime;
+			uint16_t	mdate;
+			union {
+				uint16_t	firstc;		/* FAT12/FAT16 */
+				uint16_t	firstc_lo;	/* FAT32 */
+			} __attribute__ ((packed));
+			uint32_t	size;
+		} __attribute__ ((packed));
+		struct {
+			uint8_t		order;
+			uint16_t	part1[FAT_LFN_PART1_SIZE];
+			uint8_t		attr;
+			uint8_t		type;
+			uint8_t		check_sum;
+			uint16_t	part2[FAT_LFN_PART2_SIZE];
+			uint16_t	firstc_lo; /* MUST be 0 */
+			uint16_t	part3[FAT_LFN_PART3_SIZE];
+		} __attribute__ ((packed)) lfn;
+	};
 } __attribute__ ((packed)) fat_dentry_t;
 
+
 extern int fat_dentry_namecmp(char *, const char *);
-extern bool fat_dentry_name_verify(const char *);
 extern void fat_dentry_name_get(const fat_dentry_t *, char *);
 extern void fat_dentry_name_set(fat_dentry_t *, const char *);
 extern fat_dentry_clsf_t fat_classify_dentry(const fat_dentry_t *);
+extern uint8_t fat_dentry_chksum(uint8_t *);
+
+extern size_t fat_lfn_str_nlength(const uint16_t *, size_t);
+extern size_t fat_lfn_size(const fat_dentry_t *);
+extern size_t fat_lfn_get_entry(const fat_dentry_t *, uint16_t *, size_t *);
+extern size_t fat_lfn_set_entry(const uint16_t *, size_t *, size_t, fat_dentry_t *);
+
+extern void str_to_ascii(char *dst, const char *src, size_t count, uint8_t pad);
+extern size_t utf16_length(const uint16_t *wstr);
+
+extern bool fat_valid_name(const char *name);
+extern bool fat_valid_short_name(const char *name);
+
 
 #endif
Index: uspace/srv/fs/fat/fat_directory.c
===================================================================
--- uspace/srv/fs/fat/fat_directory.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/fat/fat_directory.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,495 @@
+/*
+ * 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	fat_directory.c
+ * @brief	Functions that work with FAT directory.
+ */
+
+#include "fat_directory.h"
+#include "fat_fat.h"
+#include <libblock.h>
+#include <errno.h>
+#include <byteorder.h>
+#include <mem.h>
+#include <str.h>
+#include <align.h>
+
+int fat_directory_block_load(fat_directory_t *);
+
+
+int fat_directory_open(fat_node_t *nodep, fat_directory_t *di)
+{
+	di->b = NULL;
+	di->nodep = nodep;	
+	if (di->nodep->type != FAT_DIRECTORY)
+		return EINVAL;
+
+	di->bs = block_bb_get(di->nodep->idx->service_id);
+	di->blocks = ROUND_UP(nodep->size, BPS(di->bs))/BPS(di->bs);
+	di->pos = 0;
+	di->bnum = 0;
+	di->last = false;
+	return EOK;
+}
+
+int fat_directory_close(fat_directory_t *di)
+{
+	int rc=EOK;
+	
+	if (di->b)
+		rc = block_put(di->b);
+	
+	return rc;
+}
+
+int fat_directory_block_load(fat_directory_t *di)
+{
+	uint32_t i;
+	int rc;
+
+	i = (di->pos * sizeof(fat_dentry_t)) / BPS(di->bs);
+	if (i < di->blocks) {
+		if (di->b && di->bnum != i) {
+			block_put(di->b);
+			di->b = NULL;
+		}
+		if (!di->b) {
+			rc = fat_block_get(&di->b, di->bs, di->nodep, i, BLOCK_FLAGS_NONE);
+			if (rc != EOK) {
+				di->b = NULL;
+				return rc;
+			}
+			di->bnum = i;
+		}
+		return EOK;
+	}
+	return ENOENT;
+}
+
+int fat_directory_next(fat_directory_t *di)
+{
+	int rc;
+
+	di->pos += 1;
+	rc = fat_directory_block_load(di);
+	if (rc!=EOK)
+		di->pos -= 1;
+	
+	return rc;
+}
+
+int fat_directory_prev(fat_directory_t *di)
+{
+	int rc=EOK;
+	
+	if (di->pos > 0) {
+		di->pos -= 1;
+		rc=fat_directory_block_load(di);
+	}
+	else
+		return ENOENT;
+	
+	if (rc!=EOK)
+		di->pos += 1;
+	
+	return rc;
+}
+
+int fat_directory_seek(fat_directory_t *di, aoff64_t pos)
+{
+	aoff64_t _pos = di->pos;
+	int rc;
+
+	di->pos = pos;
+	rc = fat_directory_block_load(di);
+	if (rc!=EOK)
+		di->pos = _pos;
+	
+	return rc;
+}
+
+int fat_directory_get(fat_directory_t *di, fat_dentry_t **d)
+{
+	int rc;
+	
+	rc = fat_directory_block_load(di);
+	if (rc == EOK) {
+		aoff64_t o = di->pos % (BPS(di->bs) / sizeof(fat_dentry_t));
+		*d = ((fat_dentry_t *)di->b->data) + o;
+	}
+	
+	return rc;
+}
+
+int fat_directory_read(fat_directory_t *di, char *name, fat_dentry_t **de)
+{
+	fat_dentry_t *d = NULL;
+	uint16_t wname[FAT_LFN_NAME_SIZE];
+	size_t lfn_offset, lfn_size;
+	bool long_entry = false;
+	int long_entry_count = 0;
+	uint8_t checksum = 0;
+
+	do {
+		if (fat_directory_get(di, &d) == EOK) {
+			switch (fat_classify_dentry(d)) {
+			case FAT_DENTRY_LAST:
+				long_entry_count = 0;
+				long_entry = false;
+				return ENOENT;
+			case FAT_DENTRY_LFN:
+				if (long_entry) {
+					/* We found long entry */
+					long_entry_count--;
+					if ((FAT_LFN_ORDER(d) == long_entry_count) && 
+						(checksum == FAT_LFN_CHKSUM(d))) {
+						/* Right order! */
+						fat_lfn_get_entry(d, wname, &lfn_offset);
+					} else {
+						/* Something wrong with order. Skip this long entries set */
+						long_entry_count = 0;
+						long_entry = false;
+					}
+				} else {
+					if (FAT_IS_LFN(d)) {
+						/* We found Last long entry! */
+						if (FAT_LFN_COUNT(d) <= FAT_LFN_MAX_COUNT) {
+							long_entry = true;
+							long_entry_count = FAT_LFN_COUNT(d);
+							lfn_size = (FAT_LFN_ENTRY_SIZE * 
+								(FAT_LFN_COUNT(d) - 1)) + fat_lfn_size(d);
+							lfn_offset = lfn_size;
+							fat_lfn_get_entry(d, wname, &lfn_offset);
+							checksum = FAT_LFN_CHKSUM(d);
+						}
+					}
+				}
+				break;
+			case FAT_DENTRY_VALID:
+				if (long_entry && 
+					(checksum == fat_dentry_chksum(d->name))) {
+					wname[lfn_size] = '\0';
+					if (utf16_to_str(name, FAT_LFN_NAME_SIZE, wname) != EOK)
+						fat_dentry_name_get(d, name);
+				}
+				else
+					fat_dentry_name_get(d, name);
+				
+				*de = d;
+				return EOK;
+			default:
+			case FAT_DENTRY_SKIP:
+			case FAT_DENTRY_FREE:
+				long_entry_count = 0;
+				long_entry = false;
+				break;
+			}
+		}
+	} while (fat_directory_next(di) == EOK);
+	
+	return ENOENT;
+}
+
+int fat_directory_erase(fat_directory_t *di)
+{
+	int rc;
+	fat_dentry_t *d;
+	bool flag = false;
+	uint8_t checksum;
+
+	rc = fat_directory_get(di, &d);
+	if (rc != EOK)
+		return rc;
+	checksum = fat_dentry_chksum(d->name);
+
+	d->name[0] = FAT_DENTRY_ERASED;
+	di->b->dirty = true;
+	
+	while (!flag && fat_directory_prev(di) == EOK) {
+		if (fat_directory_get(di, &d) == EOK &&
+			fat_classify_dentry(d) == FAT_DENTRY_LFN &&			
+			checksum == FAT_LFN_CHKSUM(d)) {
+				if (FAT_IS_LFN(d))
+					flag = true;
+				memset(d, 0, sizeof(fat_dentry_t));
+				d->name[0] = FAT_DENTRY_ERASED;
+				di->b->dirty = true;
+		}
+		else
+			break;
+	}
+
+	return EOK;
+}
+
+int fat_directory_write(fat_directory_t *di, const char *name, fat_dentry_t *de)
+{
+	int rc;
+	bool enable_lfn = true; /* We can use this variable to switch off LFN support */
+	
+	if (fat_valid_short_name(name)) {
+		/* NAME could be directly stored in dentry without creating LFN */
+		fat_dentry_name_set(de, name);
+		if (fat_directory_is_sfn_exist(di, de))
+			return EEXIST;
+		rc = fat_directory_lookup_free(di, 1);
+		if (rc != EOK)
+			return rc;
+		rc = fat_directory_write_dentry(di, de);
+		return rc;
+	} else if (enable_lfn && fat_valid_name(name)) {
+		/* We should create long entries to store name */
+		int long_entry_count;
+		uint8_t checksum;
+		uint16_t wname[FAT_LFN_NAME_SIZE];
+		size_t lfn_size, lfn_offset;
+		
+		rc = str_to_utf16(wname, FAT_LFN_NAME_SIZE, name);
+		if (rc != EOK)
+			return rc;
+		
+		lfn_size = utf16_length(wname);
+		long_entry_count = lfn_size / FAT_LFN_ENTRY_SIZE;
+		if (lfn_size % FAT_LFN_ENTRY_SIZE)
+			long_entry_count++;
+		rc = fat_directory_lookup_free(di, long_entry_count+1);
+		if (rc != EOK)
+			return rc;
+		aoff64_t start_pos = di->pos;
+
+		/* Write Short entry */
+		rc = fat_directory_create_sfn(di, de, name);
+		if (rc != EOK)
+			return rc;
+		checksum = fat_dentry_chksum(de->name);
+
+		rc = fat_directory_seek(di, start_pos+long_entry_count);
+		if (rc != EOK)
+			return rc;
+		rc = fat_directory_write_dentry(di, de);
+		if (rc != EOK)
+			return rc;
+
+		/* Write Long entry by parts */
+		lfn_offset = 0;
+		fat_dentry_t *d;
+		size_t idx = 0;
+		do {
+			rc = fat_directory_prev(di);
+			if (rc != EOK)
+				return rc;
+			rc = fat_directory_get(di, &d);
+			if (rc != EOK)
+				return rc;
+			fat_lfn_set_entry(wname, &lfn_offset, lfn_size+1, d);
+			FAT_LFN_CHKSUM(d) = checksum;
+			FAT_LFN_ORDER(d) = ++idx;
+			di->b->dirty = true;
+		} while (lfn_offset < lfn_size);
+		FAT_LFN_ORDER(d) |= FAT_LFN_LAST;
+
+		rc = fat_directory_seek(di, start_pos+long_entry_count);
+		return rc;
+	}
+
+	return ENOTSUP;
+}
+
+int fat_directory_create_sfn(fat_directory_t *di, fat_dentry_t *de, const char *lname)
+{
+	char name[FAT_NAME_LEN+1];
+	char ext[FAT_EXT_LEN+1];
+	char number[FAT_NAME_LEN+1];
+	memset(name, FAT_PAD, FAT_NAME_LEN);
+	memset(ext, FAT_PAD, FAT_EXT_LEN);
+	memset(number, FAT_PAD, FAT_NAME_LEN);
+
+	size_t name_len = str_size(lname);
+	char *pdot = str_rchr(lname, '.');
+	ext[FAT_EXT_LEN] = '\0';
+	if (pdot) {
+		pdot++;
+		str_to_ascii(ext, pdot, FAT_EXT_LEN, FAT_SFN_CHAR);
+		name_len = (pdot - lname - 1);
+	}
+	if (name_len > FAT_NAME_LEN)
+		name_len = FAT_NAME_LEN;
+	str_to_ascii(name, lname, name_len, FAT_SFN_CHAR);
+
+	size_t idx;
+	for (idx=1; idx <= FAT_MAX_SFN; idx++) {
+		if (size_t_str(idx, 10, number, FAT_NAME_LEN-2)!=EOK)
+			return EOVERFLOW;
+
+		/* Fill de->name with FAT_PAD */
+		memset(de->name, FAT_PAD, FAT_NAME_LEN+FAT_EXT_LEN);
+		/* Copy ext */
+		memcpy(de->ext, ext, str_size(ext));
+		/* Copy name */
+		memcpy(de->name, name, str_size(name));
+
+		/* Copy number */
+		size_t offset;
+		if (str_size(name)+str_size(number)+1 >FAT_NAME_LEN)
+			offset = FAT_NAME_LEN - str_size(number)-1;
+		else
+			offset = str_size(name);
+		de->name[offset] = '~';
+		offset++;
+		memcpy(de->name+offset, number, str_size(number));
+
+		if (!fat_directory_is_sfn_exist(di, de))
+			return EOK;
+	}
+	return ERANGE;
+}
+
+int fat_directory_write_dentry(fat_directory_t *di, fat_dentry_t *de)
+{
+	fat_dentry_t *d;
+	int rc;
+
+	rc = fat_directory_get(di, &d);
+	if (rc!=EOK)
+		return rc;
+	memcpy(d, de, sizeof(fat_dentry_t));
+	di->b->dirty = true;
+	return EOK;
+}
+
+int fat_directory_expand(fat_directory_t *di)
+{
+	int rc;
+	fat_cluster_t mcl, lcl;
+
+	if (!FAT_IS_FAT32(di->bs) && di->nodep->firstc == FAT_CLST_ROOT) {
+		/* Can't grow the root directory on FAT12/16. */
+		return ENOSPC;
+	}
+	rc = fat_alloc_clusters(di->bs, di->nodep->idx->service_id, 1, &mcl, &lcl);
+	if (rc != EOK)
+		return rc;
+	rc = fat_zero_cluster(di->bs, di->nodep->idx->service_id, mcl);
+	if (rc != EOK) {
+		(void) fat_free_clusters(di->bs, di->nodep->idx->service_id, mcl);
+		return rc;
+	}
+	rc = fat_append_clusters(di->bs, di->nodep, mcl, lcl);
+	if (rc != EOK) {
+		(void) fat_free_clusters(di->bs, di->nodep->idx->service_id, mcl);
+		return rc;
+	}
+	di->nodep->size += BPS(di->bs) * SPC(di->bs);
+	di->nodep->dirty = true;		/* need to sync node */
+	di->blocks = di->nodep->size / BPS(di->bs);
+	
+	return EOK;
+}
+
+int fat_directory_lookup_free(fat_directory_t *di, size_t count)
+{
+	fat_dentry_t *d;
+	size_t found;
+	aoff64_t pos;
+	
+	do {
+		found = 0;
+		pos=0;
+		fat_directory_seek(di, 0);
+		do {
+			if (fat_directory_get(di, &d) == EOK) {
+				switch (fat_classify_dentry(d)) {
+				case FAT_DENTRY_LAST:
+				case FAT_DENTRY_FREE:
+					if (found==0) pos = di->pos;
+					found++;
+					if (found == count) {
+						fat_directory_seek(di, pos);
+						return EOK;
+					}
+					break;
+				case FAT_DENTRY_VALID:
+				case FAT_DENTRY_LFN:
+				case FAT_DENTRY_SKIP:
+				default:
+					found = 0;
+					break;
+				}
+			}
+		} while (fat_directory_next(di) == EOK);	
+	} while (fat_directory_expand(di) == EOK);
+	return ENOSPC;
+}
+
+int fat_directory_lookup_name(fat_directory_t *di, const char *name, fat_dentry_t **de)
+{
+	char entry[FAT_LFN_NAME_SIZE];
+	fat_directory_seek(di, 0);
+	while (fat_directory_read(di, entry, de) == EOK) {
+		if (fat_dentry_namecmp(entry, name) == 0) {
+			return EOK;
+		} else {
+			if (fat_directory_next(di) != EOK)
+				break;
+		}
+	}
+	return ENOENT;
+}
+
+bool fat_directory_is_sfn_exist(fat_directory_t *di, fat_dentry_t *de)
+{
+	fat_dentry_t *d;
+	fat_directory_seek(di, 0);
+	do {
+		if (fat_directory_get(di, &d) == EOK) {
+			switch (fat_classify_dentry(d)) {
+			case FAT_DENTRY_LAST:
+				return false;
+			case FAT_DENTRY_VALID:
+					if (bcmp(de->name, d->name, FAT_NAME_LEN+FAT_EXT_LEN)==0)
+						return true;
+					break;
+			default:
+			case FAT_DENTRY_LFN:
+			case FAT_DENTRY_SKIP:
+			case FAT_DENTRY_FREE:
+				break;
+			}
+		}
+	} while (fat_directory_next(di) == EOK);	
+	return false;
+}
+
+/**
+ * @}
+ */ 
Index: uspace/srv/fs/fat/fat_directory.h
===================================================================
--- uspace/srv/fs/fat/fat_directory.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
+++ uspace/srv/fs/fat/fat_directory.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -0,0 +1,78 @@
+/*
+ * 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 FAT_FAT_DIRECTORY_H_
+#define FAT_FAT_DIRECTORY_H_
+
+#include <stdint.h>
+#include "fat.h"
+#include "fat_dentry.h"
+
+#define FAT_MAX_SFN 9999
+
+typedef struct {
+	/* Directory data */
+	fat_bs_t *bs;
+	fat_node_t *nodep;
+	uint32_t blocks;
+	uint32_t bnum;
+	aoff64_t pos;
+	block_t *b;
+	bool last;
+} fat_directory_t;
+
+
+extern int fat_directory_open(fat_node_t *, fat_directory_t *);
+extern int fat_directory_close(fat_directory_t *);
+
+extern int fat_directory_next(fat_directory_t *);
+extern int fat_directory_prev(fat_directory_t *);
+extern int fat_directory_seek(fat_directory_t *, aoff64_t);
+extern int fat_directory_get(fat_directory_t *, fat_dentry_t **);
+
+extern int fat_directory_read(fat_directory_t *, char *, fat_dentry_t **);
+extern int fat_directory_write(fat_directory_t *, const char *, fat_dentry_t *);
+extern int fat_directory_erase(fat_directory_t *);
+extern int fat_directory_lookup_name(fat_directory_t *, const char *, fat_dentry_t **);
+extern bool fat_directory_is_sfn_exist(fat_directory_t *, fat_dentry_t *);
+
+extern int fat_directory_lookup_free(fat_directory_t *di, size_t count);
+extern int fat_directory_write_dentry(fat_directory_t *di, fat_dentry_t *de);
+extern int fat_directory_create_sfn(fat_directory_t *di, fat_dentry_t *de, const char *lname);
+extern int fat_directory_expand(fat_directory_t *di);
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/fs/fat/fat_fat.c
===================================================================
--- uspace/srv/fs/fat/fat_fat.c	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/fat_fat.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -29,5 +30,5 @@
 /** @addtogroup fs
  * @{
- */ 
+ */
 
 /**
@@ -54,10 +55,8 @@
  * primitive boot sector members.
  */
-#define RDS(bs)		((sizeof(fat_dentry_t) * RDE((bs))) / BPS((bs))) + \
-			(((sizeof(fat_dentry_t) * RDE((bs))) % BPS((bs))) != 0)
-#define SSA(bs)		(RSCNT((bs)) + FATCNT((bs)) * SF((bs)) + RDS(bs))
-
 #define CLBN2PBN(bs, cl, bn) \
 	(SSA((bs)) + ((cl) - FAT_CLST_FIRST) * SPC((bs)) + (bn) % SPC((bs)))
+
+#define IS_ODD(number)	(number & 0x1)
 
 /**
@@ -65,5 +64,5 @@
  * during allocation of clusters. The lock does not have to be held durring
  * deallocation of clusters.
- */  
+ */
 static FIBRIL_MUTEX_INITIALIZE(fat_alloc_lock);
 
@@ -77,15 +76,15 @@
  * @param numc		If non-NULL, output argument holding the number of
  *			clusters seen during the walk.
- * @param max_clusters	Maximum number of clusters to visit.	
+ * @param max_clusters	Maximum number of clusters to visit.
  *
  * @return		EOK on success or a negative error code.
  */
-int 
+int
 fat_cluster_walk(fat_bs_t *bs, service_id_t service_id, fat_cluster_t firstc,
-    fat_cluster_t *lastc, uint16_t *numc, uint16_t max_clusters)
-{
-	block_t *b;
-	uint16_t clusters = 0;
-	fat_cluster_t clst = firstc;
+    fat_cluster_t *lastc, uint32_t *numc, uint32_t max_clusters)
+{
+	uint32_t clusters = 0;
+	fat_cluster_t clst = firstc, clst_last1 = FAT_CLST_LAST1(bs);
+	fat_cluster_t clst_bad = FAT_CLST_BAD(bs);
 	int rc;
 
@@ -99,27 +98,19 @@
 	}
 
-	while (clst < FAT_CLST_LAST1 && clusters < max_clusters) {
-		aoff64_t fsec;	/* sector offset relative to FAT1 */
-		unsigned fidx;	/* FAT1 entry index */
-
+	while (clst < clst_last1 && clusters < max_clusters) {
 		assert(clst >= FAT_CLST_FIRST);
 		if (lastc)
 			*lastc = clst;	/* remember the last cluster number */
-		fsec = (clst * sizeof(fat_cluster_t)) / BPS(bs);
-		fidx = clst % (BPS(bs) / sizeof(fat_cluster_t));
+
 		/* read FAT1 */
-		rc = block_get(&b, service_id, RSCNT(bs) + fsec,
-		    BLOCK_FLAGS_NONE);
-		if (rc != EOK)
-			return rc;
-		clst = uint16_t_le2host(((fat_cluster_t *)b->data)[fidx]);
-		assert(clst != FAT_CLST_BAD);
-		rc = block_put(b);
-		if (rc != EOK)
-			return rc;
+		rc = fat_get_cluster(bs, service_id, FAT1, clst, &clst);
+		if (rc != EOK)
+			return rc;
+
+		assert(clst != clst_bad);
 		clusters++;
 	}
 
-	if (lastc && clst < FAT_CLST_LAST1)
+	if (lastc && clst < clst_last1)
 		*lastc = clst;
 	if (numc)
@@ -151,5 +142,5 @@
 		return ELIMIT;
 
-	if (nodep->firstc == FAT_CLST_ROOT) 
+	if (!FAT_IS_FAT32(bs) && nodep->firstc == FAT_CLST_ROOT)
 		goto fall_through;
 
@@ -178,5 +169,5 @@
 	if (rc != EOK)
 		return rc;
-	
+
 	/*
 	 * Update the "current" cluster cache.
@@ -198,5 +189,5 @@
  * @param clp		If not NULL, address where the cluster containing bn
  *			will be stored.
- *			stored 
+ *			stored
  * @param bn		Block number.
  * @param flags		Flags passed to libblock.
@@ -208,6 +199,6 @@
     fat_cluster_t fcl, fat_cluster_t *clp, aoff64_t bn, int flags)
 {
-	uint16_t clusters;
-	unsigned max_clusters;
+	uint32_t clusters;
+	uint32_t max_clusters;
 	fat_cluster_t c;
 	int rc;
@@ -219,5 +210,5 @@
 		return ELIMIT;
 
-	if (fcl == FAT_CLST_ROOT) {
+	if (!FAT_IS_FAT32(bs) && fcl == FAT_CLST_ROOT) {
 		/* root directory special case */
 		assert(bn < RDS(bs));
@@ -275,8 +266,8 @@
 			return rc;
 	}
-	
+
 	if (o >= pos)
 		return EOK;
-	
+
 	/* zero out the initial part of the new cluster chain */
 	for (o = boundary; o < pos; o += BPS(bs)) {
@@ -295,5 +286,5 @@
 }
 
-/** Get cluster from the first FAT.
+/** Get cluster from the first FAT. FAT12 version
  *
  * @param bs		Buffer holding the boot sector for the file system.
@@ -305,20 +296,310 @@
  */
 int
+fat_get_cluster_fat12(fat_bs_t *bs, service_id_t service_id, unsigned fatno,
+    fat_cluster_t clst, fat_cluster_t *value)
+{
+	block_t *b, *b1;
+	uint16_t byte1, byte2;
+	aoff64_t offset;
+	int rc;
+
+	offset = (clst + clst/2);
+	if (offset / BPS(bs) >= SF(bs))
+		return ERANGE;
+
+	rc = block_get(&b, service_id, RSCNT(bs) + SF(bs) * fatno +
+	    offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK)
+		return rc;
+
+	byte1 = ((uint8_t*) b->data)[offset % BPS(bs)];
+	/* This cluster access spans a sector boundary. Check only for FAT12 */
+	if ((offset % BPS(bs)) + 1 == BPS(bs)) {
+		/* Is it last sector of FAT? */
+		if (offset / BPS(bs) < SF(bs)) {
+			/* No. Reading next sector */
+			rc = block_get(&b1, service_id, 1 + RSCNT(bs) +
+				SF(bs)*fatno + offset / BPS(bs), BLOCK_FLAGS_NONE);
+			if (rc != EOK) {
+				block_put(b);
+				return rc;
+			}
+			/*
+			* Combining value with last byte of current sector and
+			* first byte of next sector
+			*/
+			byte2 = ((uint8_t*) b1->data)[0];
+
+			rc = block_put(b1);
+			if (rc != EOK) {
+				block_put(b);
+				return rc;
+			}
+		}
+		else {
+			/* Yes. It is last sector of FAT */
+			block_put(b);
+			return ERANGE;
+		}
+	}
+	else
+		byte2 = ((uint8_t*) b->data)[(offset % BPS(bs))+1];
+
+	*value = uint16_t_le2host(byte1 | (byte2 << 8));
+	if (IS_ODD(clst))
+		*value = (*value) >> 4;
+	else
+		*value = (*value) & FAT12_MASK;
+	
+	rc = block_put(b);
+	return rc;
+}
+
+/** Get cluster from the first FAT. FAT16 version
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param clst		Cluster which to get.
+ * @param value		Output argument holding the value of the cluster.
+ *
+ * @return		EOK or a negative error code.
+ */
+int
+fat_get_cluster_fat16(fat_bs_t *bs, service_id_t service_id, unsigned fatno,
+    fat_cluster_t clst, fat_cluster_t *value)
+{
+	block_t *b;
+	aoff64_t offset;
+	int rc;
+
+	offset = (clst * FAT16_CLST_SIZE);
+
+	rc = block_get(&b, service_id, RSCNT(bs) + SF(bs) * fatno +
+	    offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK)
+		return rc;
+
+	*value = uint16_t_le2host(*(uint16_t *)(b->data + offset % BPS(bs)));
+
+	rc = block_put(b);
+
+	return rc;
+}
+
+/** Get cluster from the first FAT. FAT32 version
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param clst		Cluster which to get.
+ * @param value		Output argument holding the value of the cluster.
+ *
+ * @return		EOK or a negative error code.
+ */
+int
+fat_get_cluster_fat32(fat_bs_t *bs, service_id_t service_id, unsigned fatno,
+    fat_cluster_t clst, fat_cluster_t *value)
+{
+	block_t *b;
+	aoff64_t offset;
+	int rc;
+
+	offset = (clst * FAT32_CLST_SIZE);
+
+	rc = block_get(&b, service_id, RSCNT(bs) + SF(bs) * fatno +
+	    offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK)
+		return rc;
+
+	*value = uint32_t_le2host(*(uint32_t *)(b->data + offset % BPS(bs))) & FAT32_MASK;
+
+	rc = block_put(b);
+
+	return rc;
+}
+
+
+/** Get cluster from the first FAT.
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param clst		Cluster which to get.
+ * @param value		Output argument holding the value of the cluster.
+ *
+ * @return		EOK or a negative error code.
+ */
+int
 fat_get_cluster(fat_bs_t *bs, service_id_t service_id, unsigned fatno,
     fat_cluster_t clst, fat_cluster_t *value)
 {
-	block_t *b;
-	fat_cluster_t *cp;
-	int rc;
-
+	int rc;
+
+	assert(fatno < FATCNT(bs));
+
+	if (FAT_IS_FAT12(bs)) {
+		rc = fat_get_cluster_fat12(bs, service_id, fatno, clst, value);
+	}
+	else {
+		if (FAT_IS_FAT32(bs))
+			rc = fat_get_cluster_fat32(bs, service_id, fatno, clst, value);
+		else
+			rc = fat_get_cluster_fat16(bs, service_id, fatno, clst, value);
+	}
+
+	return rc;
+}
+
+/** Set cluster in one instance of FAT. FAT12 version.
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param fatno		Number of the FAT instance where to make the change.
+ * @param clst		Cluster which is to be set.
+ * @param value		Value to set the cluster with.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+fat_set_cluster_fat12(fat_bs_t *bs, service_id_t service_id, unsigned fatno,
+    fat_cluster_t clst, fat_cluster_t value)
+{
+	block_t *b, *b1=NULL;
+	aoff64_t offset;
+	uint16_t byte1, byte2;
+	int rc;
+
+	offset = (clst + clst/2);
+	if (offset / BPS(bs) >= SF(bs))
+		return ERANGE;
+	
 	rc = block_get(&b, service_id, RSCNT(bs) + SF(bs) * fatno +
-	    (clst * sizeof(fat_cluster_t)) / BPS(bs), BLOCK_FLAGS_NONE);
+	    offset / BPS(bs), BLOCK_FLAGS_NONE);
 	if (rc != EOK)
 		return rc;
-	cp = (fat_cluster_t *)b->data +
-	    clst % (BPS(bs) / sizeof(fat_cluster_t));
-	*value = uint16_t_le2host(*cp);
+
+	byte1 = ((uint8_t*) b->data)[offset % BPS(bs)];
+	bool border = false;
+	/* This cluster access spans a sector boundary. Check only for FAT12 */
+	if ((offset % BPS(bs))+1 == BPS(bs)) {
+		/* Is it last sector of FAT? */
+		if (offset / BPS(bs) < SF(bs)) {
+			/* No. Reading next sector */
+			rc = block_get(&b1, service_id, 1 + RSCNT(bs) +
+				SF(bs)*fatno + offset / BPS(bs), BLOCK_FLAGS_NONE);
+			if (rc != EOK) {
+				block_put(b);
+				return rc;
+			}
+			/*
+			 * Combining value with last byte of current sector and
+			 * first byte of next sector
+			 */
+			byte2 = ((uint8_t*) b1->data)[0];
+			border = true;
+		}
+		else {
+			/* Yes. It is last sector of fat */
+			block_put(b);
+			return ERANGE;
+		}
+	}
+	else
+		byte2 = ((uint8_t*) b->data)[(offset % BPS(bs))+1];
+
+	if (IS_ODD(clst)) {
+		byte1 &= 0x0f;
+		byte2 = 0;
+		value = (value << 4);
+	} else {
+		byte1 = 0;
+		byte2 &= 0xf0;
+		value &= FAT12_MASK;
+	}
+
+	byte1 = byte1 | (value & 0xff);
+	byte2 = byte2 | (value >> 8);
+
+	((uint8_t*) b->data)[(offset % BPS(bs))] = byte1;
+	if (border) {
+		((uint8_t*) b1->data)[0] = byte2;
+
+		b1->dirty = true;
+		rc = block_put(b1);
+		if (rc != EOK) {
+			block_put(b);
+			return rc;
+		}
+	} else 
+		((uint8_t*) b->data)[(offset % BPS(bs))+1] = byte2;
+
+	b->dirty = true;	/* need to sync block */
 	rc = block_put(b);
-	
+	return rc;
+}
+
+/** Set cluster in one instance of FAT. FAT16 version.
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param fatno		Number of the FAT instance where to make the change.
+ * @param clst		Cluster which is to be set.
+ * @param value		Value to set the cluster with.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+fat_set_cluster_fat16(fat_bs_t *bs, service_id_t service_id, unsigned fatno,
+    fat_cluster_t clst, fat_cluster_t value)
+{
+	block_t *b;
+	aoff64_t offset;
+	int rc;
+
+	offset = (clst * FAT16_CLST_SIZE);
+
+	rc = block_get(&b, service_id, RSCNT(bs) + SF(bs) * fatno +
+	    offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK)
+		return rc;
+
+	*(uint16_t *)(b->data + offset % BPS(bs)) = host2uint16_t_le(value);
+
+	b->dirty = true;	/* need to sync block */
+	rc = block_put(b);
+	return rc;
+}
+
+/** Set cluster in one instance of FAT. FAT32 version.
+ *
+ * @param bs		Buffer holding the boot sector for the file system.
+ * @param service_id	Service ID for the file system.
+ * @param fatno		Number of the FAT instance where to make the change.
+ * @param clst		Cluster which is to be set.
+ * @param value		Value to set the cluster with.
+ *
+ * @return		EOK on success or a negative error code.
+ */
+int
+fat_set_cluster_fat32(fat_bs_t *bs, service_id_t service_id, unsigned fatno,
+    fat_cluster_t clst, fat_cluster_t value)
+{
+	block_t *b;
+	aoff64_t offset;
+	int rc;
+	fat_cluster_t temp;
+
+	offset = (clst * FAT32_CLST_SIZE);
+
+	rc = block_get(&b, service_id, RSCNT(bs) + SF(bs) * fatno +
+	    offset / BPS(bs), BLOCK_FLAGS_NONE);
+	if (rc != EOK)
+		return rc;
+
+	temp = uint32_t_le2host(*(uint32_t *)(b->data + offset % BPS(bs)));
+	temp &= 0xf0000000;
+	temp |= (value & FAT32_MASK);
+	*(uint32_t *)(b->data + offset % BPS(bs)) = host2uint32_t_le(temp);
+
+	b->dirty = true;	/* need to sync block */
+	rc = block_put(b);
 	return rc;
 }
@@ -338,18 +619,15 @@
     fat_cluster_t clst, fat_cluster_t value)
 {
-	block_t *b;
-	fat_cluster_t *cp;
 	int rc;
 
 	assert(fatno < FATCNT(bs));
-	rc = block_get(&b, service_id, RSCNT(bs) + SF(bs) * fatno +
-	    (clst * sizeof(fat_cluster_t)) / BPS(bs), BLOCK_FLAGS_NONE);
-	if (rc != EOK)
-		return rc;
-	cp = (fat_cluster_t *)b->data +
-	    clst % (BPS(bs) / sizeof(fat_cluster_t));
-	*cp = host2uint16_t_le(value);
-	b->dirty = true;		/* need to sync block */
-	rc = block_put(b);
+
+	if (FAT_IS_FAT12(bs))
+		rc = fat_set_cluster_fat12(bs, service_id, fatno, clst, value);
+	else if (FAT_IS_FAT32(bs))
+		rc = fat_set_cluster_fat32(bs, service_id, fatno, clst, value);
+	else
+		rc = fat_set_cluster_fat16(bs, service_id, fatno, clst, value);
+
 	return rc;
 }
@@ -369,10 +647,11 @@
 	uint8_t fatno;
 	unsigned c;
-	int rc;
-
-	for (fatno = FAT1 + 1; fatno < bs->fatcnt; fatno++) {
+	fat_cluster_t clst_last1 = FAT_CLST_LAST1(bs);
+	int rc;
+
+	for (fatno = FAT1 + 1; fatno < FATCNT(bs); fatno++) {
 		for (c = 0; c < nclsts; c++) {
 			rc = fat_set_cluster(bs, service_id, fatno, lifo[c],
-			    c == 0 ? FAT_CLST_LAST1 : lifo[c - 1]);
+			    c == 0 ? clst_last1 : lifo[c - 1]);
 			if (rc != EOK)
 				return rc;
@@ -404,94 +683,57 @@
     fat_cluster_t *mcl, fat_cluster_t *lcl)
 {
-	block_t *blk;
-	fat_cluster_t *lifo;	/* stack for storing free cluster numbers */ 
-	unsigned found = 0;	/* top of the free cluster number stack */
-	unsigned b, c, cl; 
-	int rc;
+	fat_cluster_t *lifo;    /* stack for storing free cluster numbers */
+	unsigned found = 0;     /* top of the free cluster number stack */
+	fat_cluster_t clst, value, clst_last1 = FAT_CLST_LAST1(bs);
+	int rc = EOK;
 
 	lifo = (fat_cluster_t *) malloc(nclsts * sizeof(fat_cluster_t));
 	if (!lifo)
 		return ENOMEM;
-	
 	/*
 	 * Search FAT1 for unused clusters.
 	 */
 	fibril_mutex_lock(&fat_alloc_lock);
-	for (b = 0, cl = 0; b < SF(bs); b++) {
-		rc = block_get(&blk, service_id, RSCNT(bs) + b,
-		    BLOCK_FLAGS_NONE);
-		if (rc != EOK)
-			goto error;
-		for (c = 0; c < BPS(bs) / sizeof(fat_cluster_t); c++, cl++) {
-			/*
-			 * Check if the entire cluster is physically there.
-			 * This check becomes necessary when the file system is
-			 * created with fewer total sectors than how many is
-			 * inferred from the size of the file allocation table
-			 * or when the last cluster ends beyond the end of the
-			 * device.
-			 */
-			if ((cl >= FAT_CLST_FIRST) &&
-			    CLBN2PBN(bs, cl, SPC(bs) - 1) >= TS(bs)) {
-				rc = block_put(blk);
-				if (rc != EOK)
-					goto error;
-				goto out;
-			}
-
-			fat_cluster_t *clst = (fat_cluster_t *)blk->data + c;
-			if (uint16_t_le2host(*clst) == FAT_CLST_RES0) {
-				/*
-				 * The cluster is free. Put it into our stack
-				 * of found clusters and mark it as non-free.
-				 */
-				lifo[found] = cl;
-				*clst = (found == 0) ?
-				    host2uint16_t_le(FAT_CLST_LAST1) :
-				    host2uint16_t_le(lifo[found - 1]);
-				blk->dirty = true;	/* need to sync block */
-				if (++found == nclsts) {
-					/* we are almost done */
-					rc = block_put(blk);
-					if (rc != EOK)
-						goto error;
-					/* update the shadow copies of FAT */
-					rc = fat_alloc_shadow_clusters(bs,
-					    service_id, lifo, nclsts);
-					if (rc != EOK)
-						goto error;
-					*mcl = lifo[found - 1];
-					*lcl = lifo[0];
-					free(lifo);
-					fibril_mutex_unlock(&fat_alloc_lock);
-					return EOK;
-				}
-			}
-		}
-		rc = block_put(blk);
-		if (rc != EOK) {
-error:
+	for (clst=FAT_CLST_FIRST; clst < CC(bs)+2 && found < nclsts; clst++) {
+		rc = fat_get_cluster(bs, service_id, FAT1, clst, &value);
+		if (rc != EOK)
+		break;
+
+		if (value == FAT_CLST_RES0) {
+		/*
+		 * The cluster is free. Put it into our stack
+		 * of found clusters and mark it as non-free.
+		 */
+		lifo[found] = clst;
+		rc = fat_set_cluster(bs, service_id, FAT1, clst,
+		    (found == 0) ?  clst_last1 : lifo[found - 1]);
+		if (rc != EOK)
+			break;
+
+		found++;
+		}
+	}
+
+	if (rc == EOK && found == nclsts) {
+		rc = fat_alloc_shadow_clusters(bs, service_id, lifo, nclsts);
+		if (rc == EOK) {
+			*mcl = lifo[found - 1];
+			*lcl = lifo[0];
+			free(lifo);
 			fibril_mutex_unlock(&fat_alloc_lock);
-			free(lifo);
-			return rc;
-		}
-	}
-out:
-	fibril_mutex_unlock(&fat_alloc_lock);
-
-	/*
-	 * We could not find enough clusters. Now we need to free the clusters
-	 * we have allocated so far.
-	 */
-	while (found--) {
+			return EOK;
+		}
+	}
+
+	/* If something wrong - free the clusters */
+	if (found > 0) {
+		while (found--) {
 		rc = fat_set_cluster(bs, service_id, FAT1, lifo[found],
 		    FAT_CLST_RES0);
-		if (rc != EOK) {
-			free(lifo);
-			return rc;
-		}
-	}
-	
+		}
+	}
+
 	free(lifo);
+	fibril_mutex_unlock(&fat_alloc_lock);
 	return ENOSPC;
 }
@@ -509,14 +751,14 @@
 {
 	unsigned fatno;
-	fat_cluster_t nextc;
+	fat_cluster_t nextc, clst_bad = FAT_CLST_BAD(bs);
 	int rc;
 
 	/* Mark all clusters in the chain as free in all copies of FAT. */
-	while (firstc < FAT_CLST_LAST1) {
-		assert(firstc >= FAT_CLST_FIRST && firstc < FAT_CLST_BAD);
+	while (firstc < FAT_CLST_LAST1(bs)) {
+		assert(firstc >= FAT_CLST_FIRST && firstc < clst_bad);
 		rc = fat_get_cluster(bs, service_id, FAT1, firstc, &nextc);
 		if (rc != EOK)
 			return rc;
-		for (fatno = FAT1; fatno < bs->fatcnt; fatno++) {
+		for (fatno = FAT1; fatno < FATCNT(bs); fatno++) {
 			rc = fat_set_cluster(bs, service_id, fatno, firstc,
 			    FAT_CLST_RES0);
@@ -564,7 +806,7 @@
 		}
 
-		for (fatno = FAT1; fatno < bs->fatcnt; fatno++) {
-			rc = fat_set_cluster(bs, nodep->idx->service_id, fatno,
-			    lastc, mcl);
+		for (fatno = FAT1; fatno < FATCNT(bs); fatno++) {
+			rc = fat_set_cluster(bs, nodep->idx->service_id,
+			    fatno, lastc, mcl);
 			if (rc != EOK)
 				return rc;
@@ -590,4 +832,5 @@
 int fat_chop_clusters(fat_bs_t *bs, fat_node_t *nodep, fat_cluster_t lcl)
 {
+	fat_cluster_t clst_last1 = FAT_CLST_LAST1(bs);
 	int rc;
 	service_id_t service_id = nodep->idx->service_id;
@@ -616,7 +859,7 @@
 
 		/* Terminate the cluster chain in all copies of FAT. */
-		for (fatno = FAT1; fatno < bs->fatcnt; fatno++) {
+		for (fatno = FAT1; fatno < FATCNT(bs); fatno++) {
 			rc = fat_set_cluster(bs, service_id, fatno, lcl,
-			    FAT_CLST_LAST1);
+			    clst_last1);
 			if (rc != EOK)
 				return rc;
@@ -673,14 +916,13 @@
 
 	/* Check number of FATs. */
-	if (bs->fatcnt == 0)
+	if (FATCNT(bs) == 0)
 		return ENOTSUP;
 
 	/* Check total number of sectors. */
-
-	if (bs->totsec16 == 0 && bs->totsec32 == 0)
+	if (TS(bs) == 0)
 		return ENOTSUP;
 
 	if (bs->totsec16 != 0 && bs->totsec32 != 0 &&
-	    bs->totsec16 != bs->totsec32) 
+	    bs->totsec16 != bs->totsec32)
 		return ENOTSUP;
 
@@ -690,5 +932,5 @@
 
 	/* Check number of sectors per FAT. */
-	if (bs->sec_per_fat == 0)
+	if (SF(bs) == 0)
 		return ENOTSUP;
 
@@ -700,11 +942,9 @@
 	 * sanitized to support file systems with this property.
 	 */
-	if ((uint16_t_le2host(bs->root_ent_max) * sizeof(fat_dentry_t)) %
-	    uint16_t_le2host(bs->bps) != 0)
+	if (!FAT_IS_FAT32(bs) && (RDE(bs) * sizeof(fat_dentry_t)) % BPS(bs) != 0)
 		return ENOTSUP;
 
 	/* Check signature of each FAT. */
-
-	for (fat_no = 0; fat_no < bs->fatcnt; fat_no++) {
+	for (fat_no = 0; fat_no < FATCNT(bs); fat_no++) {
 		rc = fat_get_cluster(bs, service_id, fat_no, 0, &e0);
 		if (rc != EOK)
@@ -723,5 +963,6 @@
 		 * set to one.
 		 */
-		if ((e0 >> 8) != 0xff || e1 != 0xffff)
+		if (!FAT_IS_FAT12(bs) && 
+			((e0 >> 8) != (FAT_MASK(bs) >> 8) || e1 != FAT_MASK(bs)))
 			return ENOTSUP;
 	}
@@ -732,3 +973,3 @@
 /**
  * @}
- */ 
+ */
Index: uspace/srv/fs/fat/fat_fat.h
===================================================================
--- uspace/srv/fs/fat/fat_fat.h	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/fat_fat.h	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -29,5 +30,5 @@
 /** @addtogroup fs
  * @{
- */ 
+ */
 
 #ifndef FAT_FAT_FAT_H_
@@ -40,10 +41,23 @@
 #define FAT1		0
 
-#define FAT_CLST_RES0	0x0000
-#define FAT_CLST_RES1	0x0001
-#define FAT_CLST_FIRST	0x0002
-#define FAT_CLST_BAD	0xfff7
-#define FAT_CLST_LAST1	0xfff8
-#define FAT_CLST_LAST8  0xffff
+#define FAT_CLST_RES0	  0
+#define FAT_CLST_RES1	  1
+#define FAT_CLST_FIRST	  2
+
+#define FAT32_CLST_BAD    0x0ffffff7
+#define FAT32_CLST_LAST1  0x0ffffff8
+#define FAT32_CLST_LAST8  0x0fffffff
+
+#define FAT12_MASK        0x0fff
+#define FAT16_MASK        0xffff
+#define FAT32_MASK        0x0fffffff
+
+#define FAT12_CLST_MAX    4085
+#define FAT16_CLST_MAX    65525
+
+/* Size in bytes for cluster value of FAT */
+#define FAT12_CLST_SIZE   2
+#define FAT16_CLST_SIZE   2
+#define FAT32_CLST_SIZE   4
 
 /* internally used to mark root directory's parent */
@@ -52,4 +66,34 @@
 #define FAT_CLST_ROOT		FAT_CLST_RES1
 
+/*
+ * Convenience macros for computing some frequently used values from the
+ * primitive boot sector members.
+ */
+#define RDS(bs)	  ((sizeof(fat_dentry_t) * RDE((bs))) / BPS((bs))) + \
+		   (((sizeof(fat_dentry_t) * RDE((bs))) % BPS((bs))) != 0)
+#define SSA(bs)	  (RSCNT((bs)) + FATCNT((bs)) * SF((bs)) + RDS(bs))
+#define DS(bs)	  (TS(bs) - SSA(bs))
+#define CC(bs)	  (DS(bs) / SPC(bs))
+
+#define FAT_IS_FAT12(bs)	(CC(bs) < FAT12_CLST_MAX)
+#define FAT_IS_FAT16(bs) \
+    ((CC(bs) >= FAT12_CLST_MAX) && (CC(bs) < FAT16_CLST_MAX))
+#define FAT_IS_FAT32(bs)	(CC(bs) >= FAT16_CLST_MAX)
+
+#define FAT_CLST_SIZE(bs) \
+    (FAT_IS_FAT32(bs) ? FAT32_CLST_SIZE : FAT16_CLST_SIZE)
+
+#define FAT_MASK(bs) \
+    (FAT_IS_FAT12(bs) ? FAT12_MASK : \
+    (FAT_IS_FAT32(bs) ? FAT32_MASK : FAT16_MASK))
+
+#define FAT_CLST_LAST1(bs)      (FAT32_CLST_LAST1 & FAT_MASK((bs)))
+#define FAT_CLST_LAST8(bs)      (FAT32_CLST_LAST8 & FAT_MASK((bs)))
+#define FAT_CLST_BAD(bs)        (FAT32_CLST_BAD & FAT_MASK((bs)))
+
+#define FAT_ROOT_CLST(bs) \
+    (FAT_IS_FAT32(bs) ? uint32_t_le2host(bs->fat32.root_cluster) : \
+    FAT_CLST_ROOT)
+
 /* forward declarations */
 struct block;
@@ -57,10 +101,10 @@
 struct fat_bs;
 
-typedef uint16_t fat_cluster_t;
+typedef uint32_t fat_cluster_t;
 
-#define fat_clusters_get(numc, bs, dh, fc) \
-    fat_cluster_walk((bs), (dh), (fc), NULL, (numc), (uint16_t) -1)
+#define fat_clusters_get(numc, bs, sid, fc) \
+    fat_cluster_walk((bs), (sid), (fc), NULL, (numc), (uint32_t) -1)
 extern int fat_cluster_walk(struct fat_bs *, service_id_t, fat_cluster_t,
-    fat_cluster_t *, uint16_t *, uint16_t);
+    fat_cluster_t *, uint32_t *, uint32_t);
 
 extern int fat_block_get(block_t **, struct fat_bs *, struct fat_node *,
@@ -78,6 +122,18 @@
 extern int fat_alloc_shadow_clusters(struct fat_bs *, service_id_t,
     fat_cluster_t *, unsigned);
+extern int fat_get_cluster_fat12(struct fat_bs *, service_id_t, unsigned,
+    fat_cluster_t, fat_cluster_t *);
+extern int fat_get_cluster_fat16(struct fat_bs *, service_id_t, unsigned,
+    fat_cluster_t, fat_cluster_t *);
+extern int fat_get_cluster_fat32(struct fat_bs *, service_id_t, unsigned,
+    fat_cluster_t, fat_cluster_t *);
 extern int fat_get_cluster(struct fat_bs *, service_id_t, unsigned,
     fat_cluster_t, fat_cluster_t *);
+extern int fat_set_cluster_fat12(struct fat_bs *, service_id_t, unsigned,
+    fat_cluster_t, fat_cluster_t);
+extern int fat_set_cluster_fat16(struct fat_bs *, service_id_t, unsigned,
+    fat_cluster_t, fat_cluster_t);
+extern int fat_set_cluster_fat32(struct fat_bs *, service_id_t, unsigned,
+    fat_cluster_t, fat_cluster_t);
 extern int fat_set_cluster(struct fat_bs *, service_id_t, unsigned,
     fat_cluster_t, fat_cluster_t);
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision 7fadb6526b88d8a8d0bbeb5d5997969a53e7e66d)
+++ uspace/srv/fs/fat/fat_ops.c	(revision 375ab5e571cc53eeb2aa803a8e86052b51d7ad00)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2008 Jakub Jermar
+ * Copyright (c) 2011 Oleg Romanenko
  * All rights reserved.
  *
@@ -29,5 +30,5 @@
 /** @addtogroup fs
  * @{
- */ 
+ */
 
 /**
@@ -39,4 +40,5 @@
 #include "fat_dentry.h"
 #include "fat_fat.h"
+#include "fat_directory.h"
 #include "../../vfs/vfs.h"
 #include <libfs.h>
@@ -56,4 +58,5 @@
 #include <align.h>
 #include <malloc.h>
+#include <str.h>
 
 #define FAT_NODE(node)	((node) ? (fat_node_t *) (node)->data : NULL)
@@ -104,8 +107,8 @@
 	node->dirty = false;
 	node->lastc_cached_valid = false;
-	node->lastc_cached_value = FAT_CLST_LAST1;
+	node->lastc_cached_value = 0;
 	node->currc_cached_valid = false;
 	node->currc_cached_bn = 0;
-	node->currc_cached_value = FAT_CLST_LAST1;
+	node->currc_cached_value = 0;
 }
 
@@ -116,9 +119,9 @@
 	fat_dentry_t *d;
 	int rc;
-	
+
 	assert(node->dirty);
 
 	bs = block_bb_get(node->idx->service_id);
-	
+
 	/* Read the block that contains the dentry of interest. */
 	rc = _fat_block_get(&b, bs, node->idx->service_id, node->idx->pfc,
@@ -136,7 +139,7 @@
 		d->attr = FAT_ATTR_SUBDIR;
 	}
-	
+
 	/* TODO: update other fields? (e.g time fields) */
-	
+
 	b->dirty = true;		/* need to sync block */
 	rc = block_put(b);
@@ -255,5 +258,5 @@
 	fn->data = nodep;
 	nodep->bp = fn;
-	
+
 	*nodepp = nodep;
 	return EOK;
@@ -291,5 +294,5 @@
 	 * We must instantiate the node from the file system.
 	 */
-	
+
 	assert(idxp->pfc);
 
@@ -309,6 +312,13 @@
 
 	d = ((fat_dentry_t *)b->data) + (idxp->pdi % DPS(bs));
+	if (FAT_IS_FAT32(bs)) {
+		nodep->firstc = uint16_t_le2host(d->firstc_lo) | 
+		    (uint16_t_le2host(d->firstc_hi) << 16);
+	} 
+	else
+		nodep->firstc = uint16_t_le2host(d->firstc);
+
 	if (d->attr & FAT_ATTR_SUBDIR) {
-		/* 
+		/*
 		 * The only directory which does not have this bit set is the
 		 * root directory itself. The root directory node is handled
@@ -316,4 +326,5 @@
 		 */
 		nodep->type = FAT_DIRECTORY;
+
 		/*
 		 * Unfortunately, the 'size' field of the FAT dentry is not
@@ -321,7 +332,6 @@
 		 * size of the directory by walking the FAT.
 		 */
-		uint16_t clusters;
-		rc = fat_clusters_get(&clusters, bs, idxp->service_id,
-		    uint16_t_le2host(d->firstc));
+		uint32_t clusters;
+		rc = fat_clusters_get(&clusters, bs, idxp->service_id, nodep->firstc);
 		if (rc != EOK) {
 			(void) block_put(b);
@@ -334,5 +344,5 @@
 		nodep->size = uint32_t_le2host(d->size);
 	}
-	nodep->firstc = uint16_t_le2host(d->firstc);
+
 	nodep->lnkcnt = 1;
 	nodep->refcnt = 1;
@@ -363,12 +373,8 @@
 int fat_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
 {
-	fat_bs_t *bs;
 	fat_node_t *parentp = FAT_NODE(pfn);
-	char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
-	unsigned i, j;
-	unsigned blocks;
+	char name[FAT_LFN_NAME_SIZE];
 	fat_dentry_t *d;
 	service_id_t service_id;
-	block_t *b;
 	int rc;
 
@@ -376,58 +382,43 @@
 	service_id = parentp->idx->service_id;
 	fibril_mutex_unlock(&parentp->idx->lock);
-
-	bs = block_bb_get(service_id);
-	blocks = parentp->size / BPS(bs);
-	for (i = 0; i < blocks; i++) {
-		rc = fat_block_get(&b, bs, parentp, i, BLOCK_FLAGS_NONE);
-		if (rc != EOK)
-			return rc;
-		for (j = 0; j < DPS(bs); j++) { 
-			d = ((fat_dentry_t *)b->data) + j;
-			switch (fat_classify_dentry(d)) {
-			case FAT_DENTRY_SKIP:
-			case FAT_DENTRY_FREE:
-				continue;
-			case FAT_DENTRY_LAST:
-				/* miss */
-				rc = block_put(b);
-				*rfn = NULL;
-				return rc;
-			default:
-			case FAT_DENTRY_VALID:
-				fat_dentry_name_get(d, name);
-				break;
+	
+	fat_directory_t di;
+	rc = fat_directory_open(parentp, &di);
+	if (rc != EOK)
+		return rc;
+
+	while (fat_directory_read(&di, name, &d) == EOK) {
+		if (fat_dentry_namecmp(name, component) == 0) {
+			/* hit */
+			fat_node_t *nodep;
+			aoff64_t o = di.pos % (BPS(di.bs) / sizeof(fat_dentry_t));
+			fat_idx_t *idx = fat_idx_get_by_pos(service_id,
+				parentp->firstc, di.bnum * DPS(di.bs) + o);
+			if (!idx) {
+				/*
+				 * Can happen if memory is low or if we
+				 * run out of 32-bit indices.
+				 */
+				rc = fat_directory_close(&di);
+				return (rc == EOK) ? ENOMEM : rc;
 			}
-			if (fat_dentry_namecmp(name, component) == 0) {
-				/* hit */
-				fat_node_t *nodep;
-				fat_idx_t *idx = fat_idx_get_by_pos(service_id,
-				    parentp->firstc, i * DPS(bs) + j);
-				if (!idx) {
-					/*
-					 * Can happen if memory is low or if we
-					 * run out of 32-bit indices.
-					 */
-					rc = block_put(b);
-					return (rc == EOK) ? ENOMEM : rc;
-				}
-				rc = fat_node_get_core(&nodep, idx);
-				fibril_mutex_unlock(&idx->lock);
-				if (rc != EOK) {
-					(void) block_put(b);
-					return rc;
-				}
-				*rfn = FS_NODE(nodep);
-				rc = block_put(b);
-				if (rc != EOK)
-					(void) fat_node_put(*rfn);
+			rc = fat_node_get_core(&nodep, idx);
+			fibril_mutex_unlock(&idx->lock);
+			if (rc != EOK) {
+				(void) fat_directory_close(&di);
 				return rc;
 			}
-		}
-		rc = block_put(b);
-		if (rc != EOK)
+			*rfn = FS_NODE(nodep);
+			rc = fat_directory_close(&di);
+			if (rc != EOK)
+				(void) fat_node_put(*rfn);
 			return rc;
-	}
-
+		} else {
+			rc = fat_directory_next(&di);
+			if (rc != EOK)
+				break;
+		}
+	}
+	(void) fat_directory_close(&di);
 	*rfn = NULL;
 	return EOK;
@@ -591,7 +582,6 @@
 	fat_bs_t *bs;
 	block_t *b;
-	unsigned i, j;
-	unsigned blocks;
-	fat_cluster_t mcl, lcl;
+	fat_directory_t di;
+	fat_dentry_t de;
 	int rc;
 
@@ -607,83 +597,13 @@
 	fibril_mutex_unlock(&childp->lock);
 
-	if (!fat_dentry_name_verify(name)) {
-		/*
-		 * Attempt to create unsupported name.
-		 */
+	if (!fat_valid_name(name))
 		return ENOTSUP;
-	}
-
-	/*
-	 * Get us an unused parent node's dentry or grow the parent and allocate
-	 * a new one.
-	 */
-	
+
 	fibril_mutex_lock(&parentp->idx->lock);
 	bs = block_bb_get(parentp->idx->service_id);
-
-	blocks = parentp->size / BPS(bs);
-
-	for (i = 0; i < blocks; i++) {
-		rc = fat_block_get(&b, bs, parentp, i, BLOCK_FLAGS_NONE);
-		if (rc != EOK) {
-			fibril_mutex_unlock(&parentp->idx->lock);
-			return rc;
-		}
-		for (j = 0; j < DPS(bs); j++) {
-			d = ((fat_dentry_t *)b->data) + j;
-			switch (fat_classify_dentry(d)) {
-			case FAT_DENTRY_SKIP:
-			case FAT_DENTRY_VALID:
-				/* skipping used and meta entries */
-				continue;
-			case FAT_DENTRY_FREE:
-			case FAT_DENTRY_LAST:
-				/* found an empty slot */
-				goto hit;
-			}
-		}
-		rc = block_put(b);
-		if (rc != EOK) {
-			fibril_mutex_unlock(&parentp->idx->lock);
-			return rc;
-		}
-	}
-	j = 0;
-	
-	/*
-	 * We need to grow the parent in order to create a new unused dentry.
-	 */
-	if (parentp->firstc == FAT_CLST_ROOT) {
-		/* Can't grow the root directory. */
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return ENOSPC;
-	}
-	rc = fat_alloc_clusters(bs, parentp->idx->service_id, 1, &mcl, &lcl);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return rc;
-	}
-	rc = fat_zero_cluster(bs, parentp->idx->service_id, mcl);
-	if (rc != EOK) {
-		(void) fat_free_clusters(bs, parentp->idx->service_id, mcl);
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return rc;
-	}
-	rc = fat_append_clusters(bs, parentp, mcl, lcl);
-	if (rc != EOK) {
-		(void) fat_free_clusters(bs, parentp->idx->service_id, mcl);
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return rc;
-	}
-	parentp->size += BPS(bs) * SPC(bs);
-	parentp->dirty = true;		/* need to sync node */
-	rc = fat_block_get(&b, bs, parentp, i, BLOCK_FLAGS_NONE);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return rc;
-	}
-	d = (fat_dentry_t *)b->data;
-
-hit:
+	rc = fat_directory_open(parentp, &di);
+	if (rc != EOK)
+		return rc;
+
 	/*
 	 * At this point we only establish the link between the parent and the
@@ -692,14 +612,19 @@
 	 * dentry data is kept in the child node structure.
 	 */
-	memset(d, 0, sizeof(fat_dentry_t));
-	fat_dentry_name_set(d, name);
-	b->dirty = true;		/* need to sync block */
-	rc = block_put(b);
+	memset(&de, 0, sizeof(fat_dentry_t));
+
+	rc = fat_directory_write(&di, name, &de);
+	if (rc!=EOK)
+		return rc;
+	rc = fat_directory_close(&di);
+	if (rc!=EOK)
+		return rc;
+
 	fibril_mutex_unlock(&parentp->idx->lock);
-	if (rc != EOK) 
+	if (rc != EOK)
 		return rc;
 
 	fibril_mutex_lock(&childp->idx->lock);
-	
+
 	if (childp->type == FAT_DIRECTORY) {
 		/*
@@ -720,5 +645,5 @@
 		d = (fat_dentry_t *) b->data;
 		if ((fat_classify_dentry(d) == FAT_DENTRY_LAST) ||
-		    (str_cmp((char *) d->name, FAT_NAME_DOT)) == 0) {
+		    (bcmp(d->name, FAT_NAME_DOT, FAT_NAME_LEN)) == 0) {
 			memset(d, 0, sizeof(fat_dentry_t));
 			memcpy(d->name, FAT_NAME_DOT, FAT_NAME_LEN);
@@ -730,11 +655,11 @@
 		d++;
 		if ((fat_classify_dentry(d) == FAT_DENTRY_LAST) ||
-		    (str_cmp((char *) d->name, FAT_NAME_DOT_DOT) == 0)) {
+		    (bcmp(d->name, FAT_NAME_DOT_DOT, FAT_NAME_LEN) == 0)) {
 			memset(d, 0, sizeof(fat_dentry_t));
 			memcpy(d->name, FAT_NAME_DOT_DOT, FAT_NAME_LEN);
 			memcpy(d->ext, FAT_EXT_PAD, FAT_EXT_LEN);
 			d->attr = FAT_ATTR_SUBDIR;
-			d->firstc = (parentp->firstc == FAT_CLST_ROOT) ?
-			    host2uint16_t_le(FAT_CLST_RES0) :
+			d->firstc = (parentp->firstc == FAT_ROOT_CLST(bs)) ?
+			    host2uint16_t_le(FAT_CLST_ROOTPAR) :
 			    host2uint16_t_le(parentp->firstc);
 			/* TODO: initialize also the date/time members. */
@@ -750,5 +675,5 @@
 
 	childp->idx->pfc = parentp->firstc;
-	childp->idx->pdi = i * DPS(bs) + j;
+	childp->idx->pdi = di.pos;	/* di.pos holds absolute position of SFN entry */
 	fibril_mutex_unlock(&childp->idx->lock);
 
@@ -770,7 +695,4 @@
 	fat_node_t *parentp = FAT_NODE(pfn);
 	fat_node_t *childp = FAT_NODE(cfn);
-	fat_bs_t *bs;
-	fat_dentry_t *d;
-	block_t *b;
 	bool has_children;
 	int rc;
@@ -778,5 +700,5 @@
 	if (!parentp)
 		return EBUSY;
-	
+
 	rc = fat_has_children(&has_children, cfn);
 	if (rc != EOK)
@@ -789,17 +711,16 @@
 	assert(childp->lnkcnt == 1);
 	fibril_mutex_lock(&childp->idx->lock);
-	bs = block_bb_get(childp->idx->service_id);
-
-	rc = _fat_block_get(&b, bs, childp->idx->service_id, childp->idx->pfc,
-	    NULL, (childp->idx->pdi * sizeof(fat_dentry_t)) / BPS(bs),
-	    BLOCK_FLAGS_NONE);
-	if (rc != EOK) 
+	
+	fat_directory_t di;
+	rc = fat_directory_open(parentp,&di);
+	if (rc != EOK)
 		goto error;
-	d = (fat_dentry_t *)b->data +
-	    (childp->idx->pdi % (BPS(bs) / sizeof(fat_dentry_t)));
-	/* mark the dentry as not-currently-used */
-	d->name[0] = FAT_DENTRY_ERASED;
-	b->dirty = true;		/* need to sync block */
-	rc = block_put(b);
+	rc = fat_directory_seek(&di, childp->idx->pdi);
+	if (rc != EOK)
+		goto error;
+	rc = fat_directory_erase(&di);
+	if (rc != EOK)
+		goto error;
+	rc = fat_directory_close(&di);
 	if (rc != EOK)
 		goto error;
@@ -820,7 +741,8 @@
 
 error:
-	fibril_mutex_unlock(&parentp->idx->lock);
+	(void) fat_directory_close(&di);
+	fibril_mutex_unlock(&childp->idx->lock);
 	fibril_mutex_unlock(&childp->lock);
-	fibril_mutex_unlock(&childp->idx->lock);
+	fibril_mutex_unlock(&parentp->lock);
 	return rc;
 }
@@ -839,5 +761,5 @@
 		return EOK;
 	}
-	
+
 	fibril_mutex_lock(&nodep->idx->lock);
 	bs = block_bb_get(nodep->idx->service_id);
@@ -847,5 +769,5 @@
 	for (i = 0; i < blocks; i++) {
 		fat_dentry_t *d;
-	
+
 		rc = fat_block_get(&b, bs, nodep, i, BLOCK_FLAGS_NONE);
 		if (rc != EOK) {
@@ -875,5 +797,5 @@
 		if (rc != EOK) {
 			fibril_mutex_unlock(&nodep->idx->lock);
-			return rc;	
+			return rc;
 		}
 	}
@@ -946,5 +868,5 @@
 	fat_bs_t *bs;
 	int rc;
-	
+
 	/* Check for option enabling write through. */
 	if (str_cmp(opts, "wtcache") == 0)
@@ -1003,4 +925,5 @@
 		return ENOMEM;
 	}
+
 	fs_node_initialize(rfn);
 	fat_node_t *rootp = (fat_node_t *)malloc(sizeof(fat_node_t));
@@ -1027,13 +950,28 @@
 
 	rootp->type = FAT_DIRECTORY;
-	rootp->firstc = FAT_CLST_ROOT;
+	rootp->firstc = FAT_ROOT_CLST(bs);
 	rootp->refcnt = 1;
 	rootp->lnkcnt = 0;	/* FS root is not linked */
-	rootp->size = RDE(bs) * sizeof(fat_dentry_t);
+
+	if (FAT_IS_FAT32(bs)) {
+		uint32_t clusters;
+		rc = fat_clusters_get(&clusters, bs, service_id, rootp->firstc);
+		if (rc != EOK) {
+			free(rfn);
+			free(rootp);
+			(void) block_cache_fini(service_id);
+			block_fini(service_id);
+			fat_idx_fini_by_service_id(service_id);
+			return ENOTSUP;
+		}
+		rootp->size = BPS(bs) * SPC(bs) * clusters;
+	} else
+		rootp->size = RDE(bs) * sizeof(fat_dentry_t);
+
 	rootp->idx = ridxp;
 	ridxp->nodep = rootp;
 	rootp->bp = rfn;
 	rfn->data = rootp;
-	
+
 	fibril_mutex_unlock(&ridxp->lock);
 
@@ -1064,5 +1002,5 @@
 		return EBUSY;
 	}
-	
+
 	/*
 	 * Put the root node and force it to the FAT free node list.
@@ -1141,7 +1079,6 @@
 		}
 	} else {
-		unsigned bnum;
 		aoff64_t spos = pos;
-		char name[FAT_NAME_LEN + 1 + FAT_EXT_LEN + 1];
+		char name[FAT_LFN_NAME_SIZE];
 		fat_dentry_t *d;
 
@@ -1150,46 +1087,26 @@
 		assert(BPS(bs) % sizeof(fat_dentry_t) == 0);
 
-		/*
-		 * Our strategy for readdir() is to use the position pointer as
-		 * an index into the array of all dentries. On entry, it points
-		 * to the first unread dentry. If we skip any dentries, we bump
-		 * the position pointer accordingly.
-		 */
-		bnum = (pos * sizeof(fat_dentry_t)) / BPS(bs);
-		while (bnum < nodep->size / BPS(bs)) {
-			aoff64_t o;
-
-			rc = fat_block_get(&b, bs, nodep, bnum,
-			    BLOCK_FLAGS_NONE);
-			if (rc != EOK)
-				goto err;
-			for (o = pos % (BPS(bs) / sizeof(fat_dentry_t));
-			    o < BPS(bs) / sizeof(fat_dentry_t);
-			    o++, pos++) {
-				d = ((fat_dentry_t *)b->data) + o;
-				switch (fat_classify_dentry(d)) {
-				case FAT_DENTRY_SKIP:
-				case FAT_DENTRY_FREE:
-					continue;
-				case FAT_DENTRY_LAST:
-					rc = block_put(b);
-					if (rc != EOK)
-						goto err;
-					goto miss;
-				default:
-				case FAT_DENTRY_VALID:
-					fat_dentry_name_get(d, name);
-					rc = block_put(b);
-					if (rc != EOK)
-						goto err;
-					goto hit;
-				}
-			}
-			rc = block_put(b);
-			if (rc != EOK)
-				goto err;
-			bnum++;
-		}
+		fat_directory_t di;
+		rc = fat_directory_open(nodep, &di);
+		if (rc != EOK) goto err;
+		rc = fat_directory_seek(&di, pos);
+		if (rc != EOK) {
+			(void) fat_directory_close(&di);
+			goto err;
+		}
+
+		rc = fat_directory_read(&di, name, &d);
+		if (rc == EOK) goto hit;
+		if (rc == ENOENT) goto miss;
+
+err:
+		(void) fat_node_put(fn);
+		async_answer_0(callid, rc);
+		return rc;
+
 miss:
+		rc = fat_directory_close(&di);
+		if (rc!=EOK)
+			goto err;
 		rc = fat_node_put(fn);
 		async_answer_0(callid, rc != EOK ? rc : ENOENT);
@@ -1197,12 +1114,11 @@
 		return rc != EOK ? rc : ENOENT;
 
-err:
-		(void) fat_node_put(fn);
-		async_answer_0(callid, rc);
-		return rc;
-
 hit:
+		pos = di.pos;
+		rc = fat_directory_close(&di);
+		if (rc!=EOK)
+			goto err;
 		(void) async_data_read_finalize(callid, name, str_size(name) + 1);
-		bytes = (pos - spos) + 1;
+		bytes = (pos - spos)+1;
 	}
 
@@ -1231,5 +1147,5 @@
 		return ENOENT;
 	nodep = FAT_NODE(fn);
-	
+
 	ipc_callid_t callid;
 	size_t len;
@@ -1247,10 +1163,10 @@
 	 * but this one greatly simplifies fat_write(). Note that we can afford
 	 * to do this because the client must be ready to handle the return
-	 * value signalizing a smaller number of bytes written. 
-	 */ 
+	 * value signalizing a smaller number of bytes written.
+	 */
 	bytes = min(len, BPS(bs) - pos % BPS(bs));
 	if (bytes == BPS(bs))
 		flags |= BLOCK_FLAGS_NOREAD;
-	
+
 	boundary = ROUND_UP(nodep->size, BPC(bs));
 	if (pos < boundary) {
@@ -1295,6 +1211,6 @@
 		 */
 		unsigned nclsts;
-		fat_cluster_t mcl, lcl; 
- 
+		fat_cluster_t mcl, lcl;
+
 		nclsts = (ROUND_UP(pos + bytes, BPC(bs)) - boundary) / BPC(bs);
 		/* create an independent chain of nclsts clusters in all FATs */
@@ -1380,5 +1296,5 @@
 		nodep->size = size;
 		nodep->dirty = true;		/* need to sync node */
-		rc = EOK;	
+		rc = EOK;
 	} else {
 		/*
@@ -1401,5 +1317,5 @@
 		nodep->size = size;
 		nodep->dirty = true;		/* need to sync node */
-		rc = EOK;	
+		rc = EOK;
 	}
 out:
@@ -1444,10 +1360,10 @@
 	if (!fn)
 		return ENOENT;
-	
+
 	fat_node_t *nodep = FAT_NODE(fn);
-	
+
 	nodep->dirty = true;
 	rc = fat_node_sync(nodep);
-	
+
 	fat_node_put(fn);
 	return rc;
