Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision 54e4479f46415540ebc300de736a40054d783148)
+++ uspace/srv/fs/fat/fat_ops.c	(revision 073f550421083dc6e7d47b683ff6fdb70f820273)
@@ -250,16 +250,16 @@
  * Forward declarations of FAT libfs operations.
  */
-static fs_node_t *fat_node_get(dev_handle_t, fs_index_t);
-static void fat_node_put(fs_node_t *);
-static fs_node_t *fat_create_node(dev_handle_t, int);
+static int fat_root_get(fs_node_t **, dev_handle_t);
+static int fat_match(fs_node_t **, fs_node_t *, const char *);
+static int fat_node_get(fs_node_t **, dev_handle_t, fs_index_t);
+static int fat_node_put(fs_node_t *);
+static int fat_create_node(fs_node_t **, dev_handle_t, int);
 static int fat_destroy_node(fs_node_t *);
 static int fat_link(fs_node_t *, fs_node_t *, const char *);
 static int fat_unlink(fs_node_t *, fs_node_t *, const char *);
-static fs_node_t *fat_match(fs_node_t *, const char *);
+static int fat_has_children(bool *, fs_node_t *);
 static fs_index_t fat_index_get(fs_node_t *);
 static size_t fat_size_get(fs_node_t *);
 static unsigned fat_lnkcnt_get(fs_node_t *);
-static bool fat_has_children(fs_node_t *);
-static fs_node_t *fat_root_get(dev_handle_t);
 static char fat_plb_get_char(unsigned);
 static bool fat_is_directory(fs_node_t *);
@@ -270,372 +270,10 @@
  */
 
-/** Instantiate a FAT in-core node. */
-fs_node_t *fat_node_get(dev_handle_t dev_handle, fs_index_t index)
-{
-	fat_node_t *nodep;
-	fat_idx_t *idxp;
-
-	idxp = fat_idx_get_by_index(dev_handle, index);
-	if (!idxp)
-		return NULL;
-	/* idxp->lock held */
-	nodep = fat_node_get_core(idxp);
-	fibril_mutex_unlock(&idxp->lock);
-	return FS_NODE(nodep);
-}
-
-void fat_node_put(fs_node_t *fn)
-{
-	fat_node_t *nodep = FAT_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_head);
-			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);
-	}
-}
-
-fs_node_t *fat_create_node(dev_handle_t dev_handle, int flags)
-{
-	fat_idx_t *idxp;
-	fat_node_t *nodep;
-	fat_bs_t *bs;
-	fat_cluster_t mcl, lcl;
-	uint16_t bps;
-	int rc;
-
-	bs = block_bb_get(dev_handle);
-	bps = uint16_t_le2host(bs->bps);
-	if (flags & L_DIRECTORY) {
-		/* allocate a cluster */
-		rc = fat_alloc_clusters(bs, dev_handle, 1, &mcl, &lcl);
-		if (rc != EOK) 
-			return NULL;
-	}
-
-	nodep = fat_node_get_new();
-	if (!nodep) {
-		(void) fat_free_clusters(bs, dev_handle, mcl);
-		return NULL;
-	}
-	idxp = fat_idx_get_new(dev_handle);
-	if (!idxp) {
-		(void) fat_free_clusters(bs, dev_handle, mcl);	
-		fat_node_put(FS_NODE(nodep));
-		return NULL;
-	}
-	/* idxp->lock held */
-	if (flags & L_DIRECTORY) {
-		/* Populate the new cluster with unused dentries. */
-		rc = fat_zero_cluster(bs, dev_handle, mcl);
-		assert(rc == EOK);
-		nodep->type = FAT_DIRECTORY;
-		nodep->firstc = mcl;
-		nodep->size = bps * bs->spc;
-	} else {
-		nodep->type = FAT_FILE;
-		nodep->firstc = FAT_CLST_RES0;
-		nodep->size = 0;
-	}
-	nodep->lnkcnt = 0;	/* not linked anywhere */
-	nodep->refcnt = 1;
-	nodep->dirty = true;
-
-	nodep->idx = idxp;
-	idxp->nodep = nodep;
-
-	fibril_mutex_unlock(&idxp->lock);
-	return FS_NODE(nodep);
-}
-
-int fat_destroy_node(fs_node_t *fn)
-{
-	fat_node_t *nodep = FAT_NODE(fn);
-	fat_bs_t *bs;
-	int rc = EOK;
-
-	/*
-	 * 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.
-	 */
-	assert(fat_has_children(fn) == false);
-
-	bs = block_bb_get(nodep->idx->dev_handle);
-	if (nodep->firstc != FAT_CLST_RES0) {
-		assert(nodep->size);
-		/* Free all clusters allocated to the node. */
-		rc = fat_free_clusters(bs, nodep->idx->dev_handle,
-		    nodep->firstc);
-	}
-
-	fat_idx_destroy(nodep->idx);
-	free(nodep->bp);
-	free(nodep);
-	return rc;
-}
-
-int fat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
-{
-	fat_node_t *parentp = FAT_NODE(pfn);
-	fat_node_t *childp = FAT_NODE(cfn);
-	fat_dentry_t *d;
-	fat_bs_t *bs;
-	block_t *b;
-	unsigned i, j;
-	uint16_t bps;
-	unsigned dps;
-	unsigned blocks;
-	fat_cluster_t mcl, lcl;
-	int rc;
-
-	fibril_mutex_lock(&childp->lock);
-	if (childp->lnkcnt == 1) {
-		/*
-		 * On FAT, we don't support multiple hard links.
-		 */
-		fibril_mutex_unlock(&childp->lock);
-		return EMLINK;
-	}
-	assert(childp->lnkcnt == 0);
-	fibril_mutex_unlock(&childp->lock);
-
-	if (!fat_dentry_name_verify(name)) {
-		/*
-		 * Attempt to create unsupported 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->dev_handle);
-	bps = uint16_t_le2host(bs->bps);
-	dps = bps / sizeof(fat_dentry_t);
-
-	blocks = parentp->size / bps;
-
-	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; 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->dev_handle, 1, &mcl, &lcl);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return rc;
-	}
-	rc = fat_zero_cluster(bs, parentp->idx->dev_handle, mcl);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return rc;
-	}
-	rc = fat_append_clusters(bs, parentp, mcl);
-	if (rc != EOK) {
-		fibril_mutex_unlock(&parentp->idx->lock);
-		return rc;
-	}
-	parentp->size += bps * bs->spc;
-	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:
-	/*
-	 * 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.
-	 */
-	memset(d, 0, sizeof(fat_dentry_t));
-	fat_dentry_name_set(d, name);
-	b->dirty = true;		/* need to sync block */
-	rc = block_put(b);
-	fibril_mutex_unlock(&parentp->idx->lock);
-	if (rc != EOK) 
-		return rc;
-
-	fibril_mutex_lock(&childp->idx->lock);
-	
-	/*
-	 * If possible, create the Sub-directory Identifier Entry and the
-	 * Sub-directory Parent Pointer Entry (i.e. "." and ".."). These entries
-	 * are not mandatory according to Standard ECMA-107 and HelenOS VFS does
-	 * not use them anyway, so this is rather a sign of our good will.
-	 */
-	rc = fat_block_get(&b, bs, childp, 0, BLOCK_FLAGS_NONE);
-	if (rc != EOK) {
-		/*
-		 * Rather than returning an error, simply skip the creation of
-		 * these two entries.
-		 */
-		goto skip_dots;
-	}
-	d = (fat_dentry_t *)b->data;
-	if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
-	    str_cmp(d->name, FAT_NAME_DOT) == 0) {
-	   	memset(d, 0, sizeof(fat_dentry_t));
-	   	str_cpy(d->name, 8, FAT_NAME_DOT);
-		str_cpy(d->ext, 3, FAT_EXT_PAD);
-		d->attr = FAT_ATTR_SUBDIR;
-		d->firstc = host2uint16_t_le(childp->firstc);
-		/* TODO: initialize also the date/time members. */
-	}
-	d++;
-	if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
-	    str_cmp(d->name, FAT_NAME_DOT_DOT) == 0) {
-		memset(d, 0, sizeof(fat_dentry_t));
-		str_cpy(d->name, 8, FAT_NAME_DOT_DOT);
-		str_cpy(d->ext, 3, FAT_EXT_PAD);
-		d->attr = FAT_ATTR_SUBDIR;
-		d->firstc = (parentp->firstc == FAT_CLST_ROOT) ?
-		    host2uint16_t_le(FAT_CLST_RES0) :
-		    host2uint16_t_le(parentp->firstc);
-		/* TODO: initialize also the date/time members. */
-	}
-	b->dirty = true;		/* need to sync block */
-	/*
-	 * Ignore the return value as we would have fallen through on error
-	 * anyway.
-	 */
-	(void) block_put(b);
-skip_dots:
-
-	childp->idx->pfc = parentp->firstc;
-	childp->idx->pdi = i * dps + j;
-	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.
-	 */
-	fat_idx_hashin(childp->idx);
-
-	return EOK;
-}
-
-int fat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
-{
-	fat_node_t *parentp = FAT_NODE(pfn);
-	fat_node_t *childp = FAT_NODE(cfn);
-	fat_bs_t *bs;
-	fat_dentry_t *d;
-	uint16_t bps;
-	block_t *b;
-	int rc;
-
-	if (!parentp)
-		return EBUSY;
-	
-	if (fat_has_children(cfn))
-		return ENOTEMPTY;
-
-	fibril_mutex_lock(&parentp->lock);
-	fibril_mutex_lock(&childp->lock);
-	assert(childp->lnkcnt == 1);
-	fibril_mutex_lock(&childp->idx->lock);
-	bs = block_bb_get(childp->idx->dev_handle);
-	bps = uint16_t_le2host(bs->bps);
-
-	rc = _fat_block_get(&b, bs, childp->idx->dev_handle, childp->idx->pfc,
-	    (childp->idx->pdi * sizeof(fat_dentry_t)) / bps,
-	    BLOCK_FLAGS_NONE);
-	if (rc != EOK) 
-		goto error;
-	d = (fat_dentry_t *)b->data +
-	    (childp->idx->pdi % (bps / 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);
-	if (rc != EOK)
-		goto error;
-
-	/* remove the index structure from the position hash */
-	fat_idx_hashout(childp->idx);
-	/* clear position information */
-	childp->idx->pfc = FAT_CLST_RES0;
-	childp->idx->pdi = 0;
-	fibril_mutex_unlock(&childp->idx->lock);
-	childp->lnkcnt = 0;
-	childp->dirty = true;
-	fibril_mutex_unlock(&childp->lock);
-	fibril_mutex_unlock(&parentp->lock);
-
-	return EOK;
-
-error:
-	fibril_mutex_unlock(&parentp->idx->lock);
-	fibril_mutex_unlock(&childp->lock);
-	fibril_mutex_unlock(&childp->idx->lock);
-	return rc;
-}
-
-fs_node_t *fat_match(fs_node_t *pfn, const char *component)
+int fat_root_get(fs_node_t **rfn, dev_handle_t dev_handle)
+{
+	return fat_node_get(rfn, dev_handle, 0);
+}
+
+int fat_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
 {
 	fat_bs_t *bs;
@@ -657,5 +295,8 @@
 	for (i = 0; i < blocks; i++) {
 		rc = fat_block_get(&b, bs, parentp, i, BLOCK_FLAGS_NONE);
-		assert(rc == EOK);
+		if (rc != EOK) {
+			fibril_mutex_unlock(&parentp->idx->lock);
+			return rc;
+		}
 		for (j = 0; j < dps; j++) { 
 			d = ((fat_dentry_t *)b->data) + j;
@@ -666,7 +307,9 @@
 			case FAT_DENTRY_LAST:
 				rc = block_put(b);
-				assert(rc == EOK);
+				/* expect EOK as b was not dirty */
+				assert(rc == EOK);	
 				fibril_mutex_unlock(&parentp->idx->lock);
-				return NULL;
+				*rfn = NULL;
+				return EOK;
 			default:
 			case FAT_DENTRY_VALID:
@@ -693,38 +336,412 @@
 					 */
 					rc = block_put(b);
-					assert(rc == EOK);
-					return NULL;
+					/* expect EOK as b was not dirty */
+					assert(rc == EOK);	
+					return ENOMEM;
 				}
 				nodep = fat_node_get_core(idx);
 				fibril_mutex_unlock(&idx->lock);
 				rc = block_put(b);
+				/* expect EOK as b was not dirty */
 				assert(rc == EOK);
-				return FS_NODE(nodep);
+				*rfn = FS_NODE(nodep);
+				return EOK;
 			}
 		}
 		rc = block_put(b);
-		assert(rc == EOK);
+		assert(rc == EOK);	/* expect EOK as b was not dirty */
 	}
 
 	fibril_mutex_unlock(&parentp->idx->lock);
-	return NULL;
-}
-
-fs_index_t fat_index_get(fs_node_t *fn)
-{
-	return FAT_NODE(fn)->idx->index;
-}
-
-size_t fat_size_get(fs_node_t *fn)
-{
-	return FAT_NODE(fn)->size;
-}
-
-unsigned fat_lnkcnt_get(fs_node_t *fn)
-{
-	return FAT_NODE(fn)->lnkcnt;
-}
-
-bool fat_has_children(fs_node_t *fn)
+	*rfn = NULL;
+	return EOK;
+}
+
+/** Instantiate a FAT in-core node. */
+int fat_node_get(fs_node_t **rfn, dev_handle_t dev_handle, fs_index_t index)
+{
+	fat_node_t *nodep;
+	fat_idx_t *idxp;
+
+	idxp = fat_idx_get_by_index(dev_handle, index);
+	if (!idxp) {
+		*rfn = NULL;
+		return EOK;
+	}
+	/* idxp->lock held */
+	nodep = fat_node_get_core(idxp);
+	fibril_mutex_unlock(&idxp->lock);
+	*rfn = FS_NODE(nodep);
+	return EOK;
+}
+
+int fat_node_put(fs_node_t *fn)
+{
+	fat_node_t *nodep = FAT_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_head);
+			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 fat_create_node(fs_node_t **rfn, dev_handle_t dev_handle, int flags)
+{
+	fat_idx_t *idxp;
+	fat_node_t *nodep;
+	fat_bs_t *bs;
+	fat_cluster_t mcl, lcl;
+	uint16_t bps;
+	int rc;
+
+	bs = block_bb_get(dev_handle);
+	bps = uint16_t_le2host(bs->bps);
+	if (flags & L_DIRECTORY) {
+		/* allocate a cluster */
+		rc = fat_alloc_clusters(bs, dev_handle, 1, &mcl, &lcl);
+		if (rc != EOK)
+			return rc;
+		/* populate the new cluster with unused dentries */
+		rc = fat_zero_cluster(bs, dev_handle, mcl);
+		if (rc != EOK) {
+			(void) fat_free_clusters(bs, dev_handle, mcl);
+			return rc;
+		}
+	}
+
+	nodep = fat_node_get_new();
+	if (!nodep) {
+		(void) fat_free_clusters(bs, dev_handle, mcl);
+		return ENOMEM;	/* FIXME: determine the true error code */
+	}
+	idxp = fat_idx_get_new(dev_handle);
+	if (!idxp) {
+		(void) fat_free_clusters(bs, dev_handle, mcl);	
+		(void) fat_node_put(FS_NODE(nodep));
+		return ENOMEM;	/* FIXME: determine the true error code */
+	}
+	/* idxp->lock held */
+	if (flags & L_DIRECTORY) {
+		nodep->type = FAT_DIRECTORY;
+		nodep->firstc = mcl;
+		nodep->size = bps * bs->spc;
+	} else {
+		nodep->type = FAT_FILE;
+		nodep->firstc = FAT_CLST_RES0;
+		nodep->size = 0;
+	}
+	nodep->lnkcnt = 0;	/* not linked anywhere */
+	nodep->refcnt = 1;
+	nodep->dirty = true;
+
+	nodep->idx = idxp;
+	idxp->nodep = nodep;
+
+	fibril_mutex_unlock(&idxp->lock);
+	*rfn = FS_NODE(nodep);
+	return EOK;
+}
+
+int fat_destroy_node(fs_node_t *fn)
+{
+	fat_node_t *nodep = FAT_NODE(fn);
+	fat_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 = fat_has_children(&has_children, fn);
+	if (rc != EOK)
+		return rc;
+	assert(!has_children);
+
+	bs = block_bb_get(nodep->idx->dev_handle);
+	if (nodep->firstc != FAT_CLST_RES0) {
+		assert(nodep->size);
+		/* Free all clusters allocated to the node. */
+		rc = fat_free_clusters(bs, nodep->idx->dev_handle,
+		    nodep->firstc);
+	}
+
+	fat_idx_destroy(nodep->idx);
+	free(nodep->bp);
+	free(nodep);
+	return rc;
+}
+
+int fat_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
+{
+	fat_node_t *parentp = FAT_NODE(pfn);
+	fat_node_t *childp = FAT_NODE(cfn);
+	fat_dentry_t *d;
+	fat_bs_t *bs;
+	block_t *b;
+	unsigned i, j;
+	uint16_t bps;
+	unsigned dps;
+	unsigned blocks;
+	fat_cluster_t mcl, lcl;
+	int rc;
+
+	fibril_mutex_lock(&childp->lock);
+	if (childp->lnkcnt == 1) {
+		/*
+		 * On FAT, we don't support multiple hard links.
+		 */
+		fibril_mutex_unlock(&childp->lock);
+		return EMLINK;
+	}
+	assert(childp->lnkcnt == 0);
+	fibril_mutex_unlock(&childp->lock);
+
+	if (!fat_dentry_name_verify(name)) {
+		/*
+		 * Attempt to create unsupported 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->dev_handle);
+	bps = uint16_t_le2host(bs->bps);
+	dps = bps / sizeof(fat_dentry_t);
+
+	blocks = parentp->size / bps;
+
+	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; 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->dev_handle, 1, &mcl, &lcl);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&parentp->idx->lock);
+		return rc;
+	}
+	rc = fat_zero_cluster(bs, parentp->idx->dev_handle, mcl);
+	if (rc != EOK) {
+		(void) fat_free_clusters(bs, parentp->idx->dev_handle, mcl);
+		fibril_mutex_unlock(&parentp->idx->lock);
+		return rc;
+	}
+	rc = fat_append_clusters(bs, parentp, mcl);
+	if (rc != EOK) {
+		(void) fat_free_clusters(bs, parentp->idx->dev_handle, mcl);
+		fibril_mutex_unlock(&parentp->idx->lock);
+		return rc;
+	}
+	parentp->size += bps * bs->spc;
+	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:
+	/*
+	 * 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.
+	 */
+	memset(d, 0, sizeof(fat_dentry_t));
+	fat_dentry_name_set(d, name);
+	b->dirty = true;		/* need to sync block */
+	rc = block_put(b);
+	fibril_mutex_unlock(&parentp->idx->lock);
+	if (rc != EOK) 
+		return rc;
+
+	fibril_mutex_lock(&childp->idx->lock);
+	
+	/*
+	 * If possible, create the Sub-directory Identifier Entry and the
+	 * Sub-directory Parent Pointer Entry (i.e. "." and ".."). These entries
+	 * are not mandatory according to Standard ECMA-107 and HelenOS VFS does
+	 * not use them anyway, so this is rather a sign of our good will.
+	 */
+	rc = fat_block_get(&b, bs, childp, 0, BLOCK_FLAGS_NONE);
+	if (rc != EOK) {
+		/*
+		 * Rather than returning an error, simply skip the creation of
+		 * these two entries.
+		 */
+		goto skip_dots;
+	}
+	d = (fat_dentry_t *)b->data;
+	if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
+	    str_cmp(d->name, FAT_NAME_DOT) == 0) {
+	   	memset(d, 0, sizeof(fat_dentry_t));
+	   	str_cpy(d->name, 8, FAT_NAME_DOT);
+		str_cpy(d->ext, 3, FAT_EXT_PAD);
+		d->attr = FAT_ATTR_SUBDIR;
+		d->firstc = host2uint16_t_le(childp->firstc);
+		/* TODO: initialize also the date/time members. */
+	}
+	d++;
+	if (fat_classify_dentry(d) == FAT_DENTRY_LAST ||
+	    str_cmp(d->name, FAT_NAME_DOT_DOT) == 0) {
+		memset(d, 0, sizeof(fat_dentry_t));
+		str_cpy(d->name, 8, FAT_NAME_DOT_DOT);
+		str_cpy(d->ext, 3, FAT_EXT_PAD);
+		d->attr = FAT_ATTR_SUBDIR;
+		d->firstc = (parentp->firstc == FAT_CLST_ROOT) ?
+		    host2uint16_t_le(FAT_CLST_RES0) :
+		    host2uint16_t_le(parentp->firstc);
+		/* TODO: initialize also the date/time members. */
+	}
+	b->dirty = true;		/* need to sync block */
+	/*
+	 * Ignore the return value as we would have fallen through on error
+	 * anyway.
+	 */
+	(void) block_put(b);
+skip_dots:
+
+	childp->idx->pfc = parentp->firstc;
+	childp->idx->pdi = i * dps + j;
+	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.
+	 */
+	fat_idx_hashin(childp->idx);
+
+	return EOK;
+}
+
+int fat_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
+{
+	fat_node_t *parentp = FAT_NODE(pfn);
+	fat_node_t *childp = FAT_NODE(cfn);
+	fat_bs_t *bs;
+	fat_dentry_t *d;
+	uint16_t bps;
+	block_t *b;
+	bool has_children;
+	int rc;
+
+	if (!parentp)
+		return EBUSY;
+	
+	rc = fat_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);
+	bs = block_bb_get(childp->idx->dev_handle);
+	bps = uint16_t_le2host(bs->bps);
+
+	rc = _fat_block_get(&b, bs, childp->idx->dev_handle, childp->idx->pfc,
+	    (childp->idx->pdi * sizeof(fat_dentry_t)) / bps,
+	    BLOCK_FLAGS_NONE);
+	if (rc != EOK) 
+		goto error;
+	d = (fat_dentry_t *)b->data +
+	    (childp->idx->pdi % (bps / 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);
+	if (rc != EOK)
+		goto error;
+
+	/* remove the index structure from the position hash */
+	fat_idx_hashout(childp->idx);
+	/* clear position information */
+	childp->idx->pfc = FAT_CLST_RES0;
+	childp->idx->pdi = 0;
+	fibril_mutex_unlock(&childp->idx->lock);
+	childp->lnkcnt = 0;
+	childp->dirty = true;
+	fibril_mutex_unlock(&childp->lock);
+	fibril_mutex_unlock(&parentp->lock);
+
+	return EOK;
+
+error:
+	fibril_mutex_unlock(&parentp->idx->lock);
+	fibril_mutex_unlock(&childp->lock);
+	fibril_mutex_unlock(&childp->idx->lock);
+	return rc;
+}
+
+int fat_has_children(bool *has_children, fs_node_t *fn)
 {
 	fat_bs_t *bs;
@@ -737,6 +754,8 @@
 	int rc;
 
-	if (nodep->type != FAT_DIRECTORY)
-		return false;
+	if (nodep->type != FAT_DIRECTORY) {
+		*has_children = false;
+		return EOK;
+	}
 	
 	fibril_mutex_lock(&nodep->idx->lock);
@@ -751,5 +770,8 @@
 	
 		rc = fat_block_get(&b, bs, nodep, i, BLOCK_FLAGS_NONE);
-		assert(rc == EOK);
+		if (rc != EOK) {
+			fibril_mutex_unlock(&nodep->idx->lock);
+			return rc;
+		}
 		for (j = 0; j < dps; j++) {
 			d = ((fat_dentry_t *)b->data) + j;
@@ -760,30 +782,48 @@
 			case FAT_DENTRY_LAST:
 				rc = block_put(b);
+				/* expect EOK as b was not dirty */
 				assert(rc == EOK);
 				fibril_mutex_unlock(&nodep->idx->lock);
-				return false;
+				*has_children = false;
+				return EOK;
 			default:
 			case FAT_DENTRY_VALID:
 				rc = block_put(b);
+				/* expect EOK as b was not dirty */
 				assert(rc == EOK);
 				fibril_mutex_unlock(&nodep->idx->lock);
-				return true;
+				*has_children = true;
+				return EOK;
 			}
 			rc = block_put(b);
+			/* expect EOK as b was not dirty */
 			assert(rc == EOK);
 			fibril_mutex_unlock(&nodep->idx->lock);
-			return true;
+			*has_children = true;
+			return EOK;
 		}
 		rc = block_put(b);
-		assert(rc == EOK);
+		assert(rc == EOK);	/* expect EOK as b was not dirty */
 	}
 
 	fibril_mutex_unlock(&nodep->idx->lock);
-	return false;
-}
-
-fs_node_t *fat_root_get(dev_handle_t dev_handle)
-{
-	return fat_node_get(dev_handle, 0);
+	*has_children = false;
+	return EOK;
+}
+
+
+fs_index_t fat_index_get(fs_node_t *fn)
+{
+	return FAT_NODE(fn)->idx->index;
+}
+
+size_t fat_size_get(fs_node_t *fn)
+{
+	return FAT_NODE(fn)->size;
+}
+
+unsigned fat_lnkcnt_get(fs_node_t *fn)
+{
+	return FAT_NODE(fn)->lnkcnt;
 }
 
@@ -805,4 +845,5 @@
 /** libfs operations */
 libfs_ops_t fat_libfs_ops = {
+	.root_get = fat_root_get,
 	.match = fat_match,
 	.node_get = fat_node_get,
@@ -812,9 +853,8 @@
 	.link = fat_link,
 	.unlink = fat_unlink,
+	.has_children = fat_has_children,
 	.index_get = fat_index_get,
 	.size_get = fat_size_get,
 	.lnkcnt_get = fat_lnkcnt_get,
-	.has_children = fat_has_children,
-	.root_get = fat_root_get,
 	.plb_get_char =	fat_plb_get_char,
 	.is_directory = fat_is_directory,
@@ -967,5 +1007,5 @@
 	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
 	off_t pos = (off_t)IPC_GET_ARG3(*request);
-	fs_node_t *fn = fat_node_get(dev_handle, index);
+	fs_node_t *fn;
 	fat_node_t *nodep;
 	fat_bs_t *bs;
@@ -975,4 +1015,9 @@
 	int rc;
 
+	rc = fat_node_get(&fn, dev_handle, index);
+	if (rc != EOK) {
+		ipc_answer_0(rid, rc);
+		return;
+	}
 	if (!fn) {
 		ipc_answer_0(rid, ENOENT);
@@ -1052,5 +1097,5 @@
 				case FAT_DENTRY_VALID:
 					fat_dentry_name_get(d, name);
-					rc == block_put(b);
+					rc = block_put(b);
 					assert(rc == EOK);
 					goto hit;
@@ -1080,5 +1125,5 @@
 	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
 	off_t pos = (off_t)IPC_GET_ARG3(*request);
-	fs_node_t *fn = fat_node_get(dev_handle, index);
+	fs_node_t *fn;
 	fat_node_t *nodep;
 	fat_bs_t *bs;
@@ -1092,4 +1137,9 @@
 	int rc;
 	
+	rc = fat_node_get(&fn, dev_handle, index);
+	if (rc != EOK) {
+		ipc_answer_0(rid, rc);
+		return;
+	}
 	if (!fn) {
 		ipc_answer_0(rid, ENOENT);
@@ -1196,5 +1246,5 @@
 	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
 	size_t size = (off_t)IPC_GET_ARG3(*request);
-	fs_node_t *fn = fat_node_get(dev_handle, index);
+	fs_node_t *fn;
 	fat_node_t *nodep;
 	fat_bs_t *bs;
@@ -1204,4 +1254,9 @@
 	int rc;
 
+	rc = fat_node_get(&fn, dev_handle, index);
+	if (rc != EOK) {
+		ipc_answer_0(rid, rc);
+		return;
+	}
 	if (!fn) {
 		ipc_answer_0(rid, ENOENT);
@@ -1267,7 +1322,12 @@
 	dev_handle_t dev_handle = (dev_handle_t)IPC_GET_ARG1(*request);
 	fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
+	fs_node_t *fn;
 	int rc;
 
-	fs_node_t *fn = fat_node_get(dev_handle, index);
+	rc = fat_node_get(&fn, dev_handle, index);
+	if (rc != EOK) {
+		ipc_answer_0(rid, rc);
+		return;
+	}
 	if (!fn) {
 		ipc_answer_0(rid, ENOENT);
