diff options
author | OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> | 2008-11-06 15:53:57 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-06 18:41:21 -0500 |
commit | 2bdf67eb1631f30e2f3f5d49e4007c76e88877a8 (patch) | |
tree | d3d337e52516b3b61cdf4508cf445ef8dcaae2ed /fs/fat | |
parent | 0e75f5da06c05425f4b375eb981c4489fb2d9787 (diff) |
fat: mmu_private race fix
mmu_private is 64bits value, hence it's not atomic to update.
So, the access rule for mmu_private is we must hold ->i_mutex. But,
fat_get_block() path doesn't follow the rule on non-allocation path.
This fixes by using i_size instead if non-allocation path.
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/cache.c | 23 | ||||
-rw-r--r-- | fs/fat/dir.c | 2 | ||||
-rw-r--r-- | fs/fat/fat.h | 6 | ||||
-rw-r--r-- | fs/fat/inode.c | 4 |
4 files changed, 25 insertions, 10 deletions
diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 589edde9053c..b42602298087 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c | |||
@@ -293,10 +293,12 @@ static int fat_bmap_cluster(struct inode *inode, int cluster) | |||
293 | } | 293 | } |
294 | 294 | ||
295 | int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, | 295 | int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, |
296 | unsigned long *mapped_blocks) | 296 | unsigned long *mapped_blocks, int create) |
297 | { | 297 | { |
298 | struct super_block *sb = inode->i_sb; | 298 | struct super_block *sb = inode->i_sb; |
299 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 299 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
300 | const unsigned long blocksize = sb->s_blocksize; | ||
301 | const unsigned char blocksize_bits = sb->s_blocksize_bits; | ||
300 | sector_t last_block; | 302 | sector_t last_block; |
301 | int cluster, offset; | 303 | int cluster, offset; |
302 | 304 | ||
@@ -309,10 +311,21 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, | |||
309 | } | 311 | } |
310 | return 0; | 312 | return 0; |
311 | } | 313 | } |
312 | last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1)) | 314 | |
313 | >> sb->s_blocksize_bits; | 315 | last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; |
314 | if (sector >= last_block) | 316 | if (sector >= last_block) { |
315 | return 0; | 317 | if (!create) |
318 | return 0; | ||
319 | |||
320 | /* | ||
321 | * ->mmu_private can access on only allocation path. | ||
322 | * (caller must hold ->i_mutex) | ||
323 | */ | ||
324 | last_block = (MSDOS_I(inode)->mmu_private + (blocksize - 1)) | ||
325 | >> blocksize_bits; | ||
326 | if (sector >= last_block) | ||
327 | return 0; | ||
328 | } | ||
316 | 329 | ||
317 | cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits); | 330 | cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits); |
318 | offset = sector & (sbi->sec_per_clus - 1); | 331 | offset = sector & (sbi->sec_per_clus - 1); |
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 140fc39e2307..2ecaa17acdb5 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c | |||
@@ -77,7 +77,7 @@ next: | |||
77 | 77 | ||
78 | *bh = NULL; | 78 | *bh = NULL; |
79 | iblock = *pos >> sb->s_blocksize_bits; | 79 | iblock = *pos >> sb->s_blocksize_bits; |
80 | err = fat_bmap(dir, iblock, &phys, &mapped_blocks); | 80 | err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0); |
81 | if (err || !phys) | 81 | if (err || !phys) |
82 | return -1; /* beyond EOF or error */ | 82 | return -1; /* beyond EOF or error */ |
83 | 83 | ||
diff --git a/fs/fat/fat.h b/fs/fat/fat.h index a69f7f9757c0..4efc5038ed29 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h | |||
@@ -91,7 +91,9 @@ struct msdos_inode_info { | |||
91 | /* for avoiding the race between fat_free() and fat_get_cluster() */ | 91 | /* for avoiding the race between fat_free() and fat_get_cluster() */ |
92 | unsigned int cache_valid_id; | 92 | unsigned int cache_valid_id; |
93 | 93 | ||
94 | loff_t mmu_private; | 94 | /* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */ |
95 | loff_t mmu_private; /* physically allocated size */ | ||
96 | |||
95 | int i_start; /* first cluster or 0 */ | 97 | int i_start; /* first cluster or 0 */ |
96 | int i_logstart; /* logical first cluster */ | 98 | int i_logstart; /* logical first cluster */ |
97 | int i_attrs; /* unused attribute bits */ | 99 | int i_attrs; /* unused attribute bits */ |
@@ -222,7 +224,7 @@ extern void fat_cache_inval_inode(struct inode *inode); | |||
222 | extern int fat_get_cluster(struct inode *inode, int cluster, | 224 | extern int fat_get_cluster(struct inode *inode, int cluster, |
223 | int *fclus, int *dclus); | 225 | int *fclus, int *dclus); |
224 | extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, | 226 | extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, |
225 | unsigned long *mapped_blocks); | 227 | unsigned long *mapped_blocks, int create); |
226 | 228 | ||
227 | /* fat/dir.c */ | 229 | /* fat/dir.c */ |
228 | extern const struct file_operations fat_dir_operations; | 230 | extern const struct file_operations fat_dir_operations; |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index be88208b83a6..9e37ad93c730 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c | |||
@@ -64,7 +64,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock, | |||
64 | sector_t phys; | 64 | sector_t phys; |
65 | int err, offset; | 65 | int err, offset; |
66 | 66 | ||
67 | err = fat_bmap(inode, iblock, &phys, &mapped_blocks); | 67 | err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create); |
68 | if (err) | 68 | if (err) |
69 | return err; | 69 | return err; |
70 | if (phys) { | 70 | if (phys) { |
@@ -94,7 +94,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock, | |||
94 | *max_blocks = min(mapped_blocks, *max_blocks); | 94 | *max_blocks = min(mapped_blocks, *max_blocks); |
95 | MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits; | 95 | MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits; |
96 | 96 | ||
97 | err = fat_bmap(inode, iblock, &phys, &mapped_blocks); | 97 | err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create); |
98 | if (err) | 98 | if (err) |
99 | return err; | 99 | return err; |
100 | 100 | ||