aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat
diff options
context:
space:
mode:
authorOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>2007-07-15 20:40:05 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-16 13:31:01 -0400
commit98283bb49c6c8c070ebde9f47489d3e9a83c1323 (patch)
treef124325ac1768bee61b82e7689bc48ea102efe56 /fs/fat
parent347e03df1696ab22963f6b8c4f2220c41ec17f82 (diff)
fat: Fix the race of read/write the FAT12 entry
FAT12 entry is 12bits, so it needs 2 phase to update the value. And writer and reader access it without any lock, so reader can get the half updated value. This fixes the long standing race condition by adding a global spinlock to only FAT12 for avoiding any impact against FAT16/32. Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/fat')
-rw-r--r--fs/fat/fatent.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index ab171ea8e869..2c1b73fb82ae 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -17,6 +17,8 @@ struct fatent_operations {
17 int (*ent_next)(struct fat_entry *); 17 int (*ent_next)(struct fat_entry *);
18}; 18};
19 19
20static DEFINE_SPINLOCK(fat12_entry_lock);
21
20static void fat12_ent_blocknr(struct super_block *sb, int entry, 22static void fat12_ent_blocknr(struct super_block *sb, int entry,
21 int *offset, sector_t *blocknr) 23 int *offset, sector_t *blocknr)
22{ 24{
@@ -116,10 +118,13 @@ static int fat12_ent_get(struct fat_entry *fatent)
116 u8 **ent12_p = fatent->u.ent12_p; 118 u8 **ent12_p = fatent->u.ent12_p;
117 int next; 119 int next;
118 120
121 spin_lock(&fat12_entry_lock);
119 if (fatent->entry & 1) 122 if (fatent->entry & 1)
120 next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4); 123 next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4);
121 else 124 else
122 next = (*ent12_p[1] << 8) | *ent12_p[0]; 125 next = (*ent12_p[1] << 8) | *ent12_p[0];
126 spin_unlock(&fat12_entry_lock);
127
123 next &= 0x0fff; 128 next &= 0x0fff;
124 if (next >= BAD_FAT12) 129 if (next >= BAD_FAT12)
125 next = FAT_ENT_EOF; 130 next = FAT_ENT_EOF;
@@ -151,6 +156,7 @@ static void fat12_ent_put(struct fat_entry *fatent, int new)
151 if (new == FAT_ENT_EOF) 156 if (new == FAT_ENT_EOF)
152 new = EOF_FAT12; 157 new = EOF_FAT12;
153 158
159 spin_lock(&fat12_entry_lock);
154 if (fatent->entry & 1) { 160 if (fatent->entry & 1) {
155 *ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f); 161 *ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f);
156 *ent12_p[1] = new >> 4; 162 *ent12_p[1] = new >> 4;
@@ -158,6 +164,7 @@ static void fat12_ent_put(struct fat_entry *fatent, int new)
158 *ent12_p[0] = new & 0xff; 164 *ent12_p[0] = new & 0xff;
159 *ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8); 165 *ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8);
160 } 166 }
167 spin_unlock(&fat12_entry_lock);
161 168
162 mark_buffer_dirty(fatent->bhs[0]); 169 mark_buffer_dirty(fatent->bhs[0]);
163 if (fatent->nr_bhs == 2) 170 if (fatent->nr_bhs == 2)