[RFC][PATCH] FAT: Add support for FAT32 with non-mirrored FATs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I've got an USB FAT32-formatted external drive that developed bad blocks
in the area of the 0th FAT. So, I've developed a quick patch for
that adds support for non-mirrored FATs in case of FAT32 (as per FS
spec). If FAT mirroring is on, the behavior is unchanged: read/write the
0th FAT, and mirror changes to the remaining FATs.

I've tested this patch on the said external drive as well as on some
loop devices.

Best regards,
Roma

--- cut here ---

>From 869fb1b0ed81396c0a0d1a42f557ea5be2c39797 Mon Sep 17 00:00:00 2001
From: Roman Dubtsov <dubtsov@xxxxxxxxx>
Date: Sat, 6 Nov 2010 12:21:49 +0100
Subject: [PATCH] fat: add support for FAT32 with non-mirrored FATs

Use BPB_ExtFlags member of the FAT32 boot sector to detect volumes with only a
single active FAT.

Signed-off-by: Roman Dubtsov <dubtsov@xxxxxxxxx>
---
 fs/fat/fat.h    |    6 ++++++
 fs/fat/fatent.c |    6 +++++-
 fs/fat/inode.c  |   17 +++++++++++++++++
 3 files changed, 28 insertions(+), 1 deletions(-)

diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index d75a77f..d09191a 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -52,6 +52,9 @@ struct fat_mount_options {
 #define FAT_HASH_BITS	8
 #define FAT_HASH_SIZE	(1UL << FAT_HASH_BITS)
 
+#define FAT_FLAG_DONT_MIRROR	0x0080
+#define FAT_FLAG_USE_FAT_NR	0x000F
+
 /*
  * MS-DOS file system in-core superblock data
  */
@@ -87,6 +90,9 @@ struct msdos_sb_info {
 
 	spinlock_t inode_hash_lock;
 	struct hlist_head inode_hashtable[FAT_HASH_SIZE];
+
+	int active_fat;              /* Active FAT number (FAT32 only) */
+	int mirror_fat;              /* Fat mirroring flag (FAT32 only) */
 };
 
 #define FAT_CACHE_VALID	0	/* special case for valid cache */
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index b47d2c9..7932921 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -36,9 +36,10 @@ static void fat_ent_blocknr(struct super_block *sb, int entry,
 {
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 	int bytes = (entry << sbi->fatent_shift);
+	int fat_start = sbi->fat_start + sbi->active_fat * sbi->fat_length;
 	WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry);
 	*offset = bytes & (sb->s_blocksize - 1);
-	*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
+	*blocknr = fat_start + (bytes >> sb->s_blocksize_bits);
 }
 
 static void fat12_ent_set_ptr(struct fat_entry *fatent, int offset)
@@ -372,6 +373,9 @@ static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
 	struct buffer_head *c_bh;
 	int err, n, copy;
 
+	if (!sbi->mirror_fat)
+		return 0;
+
 	err = 0;
 	for (copy = 1; copy < sbi->fats; copy++) {
 		sector_t backup_fat = sbi->fat_length * copy;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index ad6998a..a3401f8 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -1361,10 +1361,13 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
 	sbi->free_clusters = -1;	/* Don't know yet */
 	sbi->free_clus_valid = 0;
 	sbi->prev_free = FAT_START_ENT;
+	sbi->mirror_fat = 1;		/* FAT mirroring is ON by default, */
+	sbi->active_fat = 0;		/* and the default active FAT # is 0 */
 
 	if (!sbi->fat_length && b->fat32_length) {
 		struct fat_boot_fsinfo *fsinfo;
 		struct buffer_head *fsinfo_bh;
+		u16 flags;
 
 		/* Must be FAT32 */
 		sbi->fat_bits = 32;
@@ -1378,6 +1381,20 @@ int fat_fill_super(struct super_block *sb, void *data, int silent,
 		if (sbi->fsinfo_sector == 0)
 			sbi->fsinfo_sector = 1;
 
+		flags = le16_to_cpu(b->flags);
+		if (flags & FAT_FLAG_DONT_MIRROR) {
+			sbi->active_fat = flags & FAT_FLAG_USE_FAT_NR;
+			sbi->mirror_fat = 0;
+			printk(KERN_INFO "FAT: FAT mirroring is off."
+					" Using FAT #%d\n",
+					sbi->active_fat);
+			if (sbi->active_fat >= sbi->fats) {
+				printk(KERN_ERR "FAT: invalid active fat number\n");
+				brelse(bh);
+				goto out_fail;
+			}
+		}
+
 		fsinfo_bh = sb_bread(sb, sbi->fsinfo_sector);
 		if (fsinfo_bh == NULL) {
 			printk(KERN_ERR "FAT: bread failed, FSINFO block"
-- 
1.7.3.2



--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux