fs: fat: flush a directory cluster properly

When a long name directory entry is created, multiple directory entries
may be occupied across a directory cluster boundary. Since only one
directory cluster is cached in a directory iterator, a first cluster must
be written back to device before switching over a second cluster.

Without this patch, some added files may be lost even if you don't see
any failures on write operation.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Tested-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index 11b85d3..5ea15fa 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -209,7 +209,8 @@
 	return 1;
 }
 
-static int flush_dir_table(fat_itr *itr);
+static int new_dir_table(fat_itr *itr);
+static int flush_dir(fat_itr *itr);
 
 /*
  * Fill dir_slot entries with appropriate name, id, and attr
@@ -242,19 +243,15 @@
 		memcpy(itr->dent, slotptr, sizeof(dir_slot));
 		slotptr--;
 		counter--;
+
+		if (itr->remaining == 0)
+			flush_dir(itr);
+
 		if (!fat_itr_next(itr))
-			if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+			if (!itr->dent && !itr->is_root && new_dir_table(itr))
 				return -1;
 	}
 
-	if (!itr->dent && !itr->is_root)
-		/*
-		 * don't care return value here because we have already
-		 * finished completing an entry with name, only ending up
-		 * no more entry left
-		 */
-		flush_dir_table(itr);
-
 	return 0;
 }
 
@@ -621,18 +618,14 @@
 }
 
 /*
- * Write directory entries in itr's buffer to block device
+ * Allocate a cluster for additional directory entries
  */
-static int flush_dir_table(fat_itr *itr)
+static int new_dir_table(fat_itr *itr)
 {
 	fsdata *mydata = itr->fsdata;
 	int dir_newclust = 0;
 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 
-	if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) {
-		printf("error: writing directory entry\n");
-		return -1;
-	}
 	dir_newclust = find_empty_cluster(mydata);
 	set_fatent_value(mydata, itr->clust, dir_newclust);
 	if (mydata->fatsize == 32)
@@ -987,7 +980,7 @@
 			return itr->dent;
 	}
 
-	if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+	if (!itr->dent && !itr->is_root && new_dir_table(itr))
 		/* indicate that allocating dent failed */
 		itr->dent = NULL;
 
@@ -1164,14 +1157,16 @@
 
 		memset(itr->dent, 0, sizeof(*itr->dent));
 
-		/* Set short name to set alias checksum field in dir_slot */
+		/* Calculate checksum for short name */
 		set_name(itr->dent, filename);
+
+		/* Set long name entries */
 		if (fill_dir_slot(itr, filename)) {
 			ret = -EIO;
 			goto exit;
 		}
 
-		/* Set attribute as archive for regular file */
+		/* Set short name entry */
 		fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
 
 		retdent = itr->dent;