Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ 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;
