Repository: mborgerson/fatx (language: python)

Issue:
Writing over a deleted file with the same name may write garbage data
Sometimes, after writing a large file, and then deleting the file, writing over the file again will result in garbage data being read back.

Reproduced on a clean drive image by copying a 256MB file, deleting it, and then copying a smaller file with the same name. The MD5 sum of the second file is incorrect (it matches neither the first nor second file copied).


Discussion / hints:

Fix patch:
diff --git a/libfatx/fatx_fat.c b/libfatx/fatx_fat.c
index bf74d62..0d94a18 100644
--- a/libfatx/fatx_fat.c
+++ b/libfatx/fatx_fat.c
@@ -327,6 +327,7 @@ int fatx_alloc_cluster(struct fatx_fs *fs, size_t *cluster)
     int status;
     fatx_fat_entry fat_entry;
     size_t i;
+    void *zero;
 
     fatx_debug(fs, "fatx_alloc_cluster()\n");
 
@@ -350,6 +351,16 @@ int fatx_alloc_cluster(struct fatx_fs *fs, size_t *cluster)
     status = fatx_mark_cluster_end(fs, i);
     if (status != FATX_STATUS_SUCCESS) return status;
 
+    status = fatx_dev_seek_cluster(fs, i, 0);
+    if (status != FATX_STATUS_SUCCESS) return status;
+
+    zero = calloc(1, fs->bytes_per_cluster);
+    if (!zero) return FATX_STATUS_ERROR;
+
+    status = fatx_dev_write(fs, zero, fs->bytes_per_cluster, 1);
+    free(zero);
+    if (status != 1) return status;
+
     *cluster = i;
 
     return FATX_STATUS_SUCCESS;


Test patch:
diff --git a/tests/test.py b/tests/test.py
index 13d8e31..b597760 100755
--- a/tests/test.py
+++ b/tests/test.py
@@ -83,5 +83,33 @@ def test_write_large_file(self):
 
 		assert d == b
 
+	
+	def test_write_offset(self):
+		test_file_path = '/offsetfile'
+		fs = Fatx('xbox_hdd.img')
+
+		rng = random.Random()
+		rng.seed(12345)
+
+		# Write 1KB of random data
+		b = bytes([rng.getrandbits(8) for _ in range(1024)])
+		fs.write(test_file_path, b)
+
+		# Delete the data
+		d = fs.read(test_file_path)
+		fs.unlink(test_file_path)
+		assert d == b
+
+		# Write 512KB of random data at offset 128
+		b = bytes([rng.getrandbits(8) for _ in range(1024 * 512)])
+		fs.write(test_file_path, b, offset=128)
+
+		# Read from offset zero
+		d = fs.read(test_file_path)
+		fs.unlink(test_file_path)
+
+		assert d == bytes([0] * 128) + b
+
+
 if __name__ == '__main__':
 	unittest.main()