fs/fat: Do not write unmodified fat entries to disk

The code caches 6 sectors of the FAT. On FAT traversal, the old contents
needs to be flushed to disk, but only if any FAT entries had been modified.
Explicitly flag the buffer on modification.

Currently, creating a new file traverses the whole FAT up to the first
free cluster and rewrites the on-disk blocks.

Signed-off-by: Stefan Brüns <stefan.bruens@rwth-aachen.de>
Reviewed-by: Lukasz Majewski <l.majewski@samsung.com>
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 826bd85..df9f2b5 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -876,6 +876,7 @@
 	}
 
 	mydata->fatbufnum = -1;
+	mydata->fat_dirty = 0;
 	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
 	if (mydata->fatbuf == NULL) {
 		debug("Error: allocating memory\n");
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index 961ccd8..babe9c8 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -104,13 +104,19 @@
 /*
  * Write fat buffer into block device
  */
-static int flush_fat_buffer(fsdata *mydata)
+static int flush_dirty_fat_buffer(fsdata *mydata)
 {
 	int getsize = FATBUFBLOCKS;
 	__u32 fatlength = mydata->fatlength;
 	__u8 *bufptr = mydata->fatbuf;
 	__u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
 
+	debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
+	      (int)mydata->fat_dirty);
+
+	if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
+		return 0;
+
 	startblock += mydata->fat_sect;
 
 	if (getsize > fatlength)
@@ -130,6 +136,7 @@
 			return -1;
 		}
 	}
+	mydata->fat_dirty = 0;
 
 	return 0;
 }
@@ -186,10 +193,8 @@
 		startblock += mydata->fat_sect;	/* Offset from start of disk */
 
 		/* Write back the fatbuf to the disk */
-		if (mydata->fatbufnum != -1) {
-			if (flush_fat_buffer(mydata) < 0)
-				return -1;
-		}
+		if (flush_dirty_fat_buffer(mydata) < 0)
+			return -1;
 
 		if (disk_read(startblock, getsize, bufptr) < 0) {
 			debug("Error reading FAT blocks\n");
@@ -494,10 +499,8 @@
 		if (getsize > fatlength)
 			getsize = fatlength;
 
-		if (mydata->fatbufnum != -1) {
-			if (flush_fat_buffer(mydata) < 0)
-				return -1;
-		}
+		if (flush_dirty_fat_buffer(mydata) < 0)
+			return -1;
 
 		if (disk_read(startblock, getsize, bufptr) < 0) {
 			debug("Error reading FAT blocks\n");
@@ -506,6 +509,9 @@
 		mydata->fatbufnum = bufnum;
 	}
 
+	/* Mark as dirty */
+	mydata->fat_dirty = 1;
+
 	/* Set the actual entry */
 	switch (mydata->fatsize) {
 	case 32:
@@ -645,7 +651,7 @@
 
 	dir_curclust = dir_newclust;
 
-	if (flush_fat_buffer(mydata) < 0)
+	if (flush_dirty_fat_buffer(mydata) < 0)
 		return;
 
 	memset(get_dentfromdir_block, 0x00,
@@ -675,7 +681,7 @@
 	}
 
 	/* Flush fat buffer */
-	if (flush_fat_buffer(mydata) < 0)
+	if (flush_dirty_fat_buffer(mydata) < 0)
 		return -1;
 
 	return 0;
@@ -1011,6 +1017,7 @@
 	}
 
 	mydata->fatbufnum = -1;
+	mydata->fat_dirty = 0;
 	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
 	if (mydata->fatbuf == NULL) {
 		debug("Error: allocating memory\n");
@@ -1111,7 +1118,7 @@
 	debug("attempt to write 0x%llx bytes\n", *actwrite);
 
 	/* Flush fat buffer */
-	ret = flush_fat_buffer(mydata);
+	ret = flush_dirty_fat_buffer(mydata);
 	if (ret) {
 		printf("Error: flush fat buffer\n");
 		goto exit;
diff --git a/include/fat.h b/include/fat.h
index 9d053e6..8ec91cd 100644
--- a/include/fat.h
+++ b/include/fat.h
@@ -169,6 +169,7 @@
 	int	fatsize;	/* Size of FAT in bits */
 	__u32	fatlength;	/* Length of FAT in sectors */
 	__u16	fat_sect;	/* Starting sector of the FAT */
+	__u8	fat_dirty;      /* Set if fatbuf has been modified */
 	__u32	rootdir_sect;	/* Start sector of root directory */
 	__u16	sect_size;	/* Size of sectors in bytes */
 	__u16	clust_size;	/* Size of clusters in sectors */