aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/fat/cache.c14
-rw-r--r--fs/fat/dir.c6
-rw-r--r--fs/fat/inode.c82
-rw-r--r--include/linux/msdos_fs.h3
4 files changed, 89 insertions, 16 deletions
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index 77c24fcf712a..1acc941245fb 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -295,7 +295,8 @@ static int fat_bmap_cluster(struct inode *inode, int cluster)
295 return dclus; 295 return dclus;
296} 296}
297 297
298int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) 298int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
299 unsigned long *mapped_blocks)
299{ 300{
300 struct super_block *sb = inode->i_sb; 301 struct super_block *sb = inode->i_sb;
301 struct msdos_sb_info *sbi = MSDOS_SB(sb); 302 struct msdos_sb_info *sbi = MSDOS_SB(sb);
@@ -303,9 +304,12 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys)
303 int cluster, offset; 304 int cluster, offset;
304 305
305 *phys = 0; 306 *phys = 0;
307 *mapped_blocks = 0;
306 if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) { 308 if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) {
307 if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) 309 if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) {
308 *phys = sector + sbi->dir_start; 310 *phys = sector + sbi->dir_start;
311 *mapped_blocks = 1;
312 }
309 return 0; 313 return 0;
310 } 314 }
311 last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1)) 315 last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
@@ -318,7 +322,11 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys)
318 cluster = fat_bmap_cluster(inode, cluster); 322 cluster = fat_bmap_cluster(inode, cluster);
319 if (cluster < 0) 323 if (cluster < 0)
320 return cluster; 324 return cluster;
321 else if (cluster) 325 else if (cluster) {
322 *phys = fat_clus_to_blknr(sbi, cluster) + offset; 326 *phys = fat_clus_to_blknr(sbi, cluster) + offset;
327 *mapped_blocks = sbi->sec_per_clus - offset;
328 if (*mapped_blocks > last_block - sector)
329 *mapped_blocks = last_block - sector;
330 }
323 return 0; 331 return 0;
324} 332}
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 4ce77475aed3..eef1b81aa294 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -68,8 +68,8 @@ static int fat__get_entry(struct inode *dir, loff_t *pos,
68{ 68{
69 struct super_block *sb = dir->i_sb; 69 struct super_block *sb = dir->i_sb;
70 sector_t phys, iblock; 70 sector_t phys, iblock;
71 int offset; 71 unsigned long mapped_blocks;
72 int err; 72 int err, offset;
73 73
74next: 74next:
75 if (*bh) 75 if (*bh)
@@ -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); 80 err = fat_bmap(dir, iblock, &phys, &mapped_blocks);
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/inode.c b/fs/fat/inode.c
index 932c8d6d1f54..e7f4aa7fc686 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -23,6 +23,7 @@
23#include <linux/mount.h> 23#include <linux/mount.h>
24#include <linux/vfs.h> 24#include <linux/vfs.h>
25#include <linux/parser.h> 25#include <linux/parser.h>
26#include <linux/uio.h>
26#include <asm/unaligned.h> 27#include <asm/unaligned.h>
27 28
28#ifndef CONFIG_FAT_DEFAULT_IOCHARSET 29#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
@@ -49,43 +50,77 @@ static int fat_add_cluster(struct inode *inode)
49 return err; 50 return err;
50} 51}
51 52
52static int fat_get_block(struct inode *inode, sector_t iblock, 53static int __fat_get_blocks(struct inode *inode, sector_t iblock,
53 struct buffer_head *bh_result, int create) 54 unsigned long *max_blocks,
55 struct buffer_head *bh_result, int create)
54{ 56{
55 struct super_block *sb = inode->i_sb; 57 struct super_block *sb = inode->i_sb;
58 struct msdos_sb_info *sbi = MSDOS_SB(sb);
56 sector_t phys; 59 sector_t phys;
57 int err; 60 unsigned long mapped_blocks;
61 int err, offset;
58 62
59 err = fat_bmap(inode, iblock, &phys); 63 err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
60 if (err) 64 if (err)
61 return err; 65 return err;
62 if (phys) { 66 if (phys) {
63 map_bh(bh_result, sb, phys); 67 map_bh(bh_result, sb, phys);
68 *max_blocks = min(mapped_blocks, *max_blocks);
64 return 0; 69 return 0;
65 } 70 }
66 if (!create) 71 if (!create)
67 return 0; 72 return 0;
73
68 if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) { 74 if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
69 fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)", 75 fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)",
70 MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private); 76 MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
71 return -EIO; 77 return -EIO;
72 } 78 }
73 if (!((unsigned long)iblock & (MSDOS_SB(sb)->sec_per_clus - 1))) { 79
80 offset = (unsigned long)iblock & (sbi->sec_per_clus - 1);
81 if (!offset) {
82 /* TODO: multiple cluster allocation would be desirable. */
74 err = fat_add_cluster(inode); 83 err = fat_add_cluster(inode);
75 if (err) 84 if (err)
76 return err; 85 return err;
77 } 86 }
78 MSDOS_I(inode)->mmu_private += sb->s_blocksize; 87 /* available blocks on this cluster */
79 err = fat_bmap(inode, iblock, &phys); 88 mapped_blocks = sbi->sec_per_clus - offset;
89
90 *max_blocks = min(mapped_blocks, *max_blocks);
91 MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits;
92
93 err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
80 if (err) 94 if (err)
81 return err; 95 return err;
82 if (!phys) 96 BUG_ON(!phys);
83 BUG(); 97 BUG_ON(*max_blocks != mapped_blocks);
84 set_buffer_new(bh_result); 98 set_buffer_new(bh_result);
85 map_bh(bh_result, sb, phys); 99 map_bh(bh_result, sb, phys);
86 return 0; 100 return 0;
87} 101}
88 102
103static int fat_get_blocks(struct inode *inode, sector_t iblock,
104 unsigned long max_blocks,
105 struct buffer_head *bh_result, int create)
106{
107 struct super_block *sb = inode->i_sb;
108 int err;
109
110 err = __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
111 if (err)
112 return err;
113 bh_result->b_size = max_blocks << sb->s_blocksize_bits;
114 return 0;
115}
116
117static int fat_get_block(struct inode *inode, sector_t iblock,
118 struct buffer_head *bh_result, int create)
119{
120 unsigned long max_blocks = 1;
121 return __fat_get_blocks(inode, iblock, &max_blocks, bh_result, create);
122}
123
89static int fat_writepage(struct page *page, struct writeback_control *wbc) 124static int fat_writepage(struct page *page, struct writeback_control *wbc)
90{ 125{
91 return block_write_full_page(page, fat_get_block, wbc); 126 return block_write_full_page(page, fat_get_block, wbc);
@@ -128,6 +163,34 @@ static int fat_commit_write(struct file *file, struct page *page,
128 return err; 163 return err;
129} 164}
130 165
166static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
167 const struct iovec *iov,
168 loff_t offset, unsigned long nr_segs)
169{
170 struct file *file = iocb->ki_filp;
171 struct inode *inode = file->f_mapping->host;
172
173 if (rw == WRITE) {
174 /*
175 * FIXME: blockdev_direct_IO() doesn't use ->prepare_write(),
176 * so we need to update the ->mmu_private to block boundary.
177 *
178 * But we must fill the remaining area or hole by nul for
179 * updating ->mmu_private.
180 */
181 loff_t size = offset + iov_length(iov, nr_segs);
182 if (MSDOS_I(inode)->mmu_private < size)
183 return -EINVAL;
184 }
185
186 /*
187 * FAT need to use the DIO_LOCKING for avoiding the race
188 * condition of fat_get_block() and ->truncate().
189 */
190 return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
191 offset, nr_segs, fat_get_blocks, NULL);
192}
193
131static sector_t _fat_bmap(struct address_space *mapping, sector_t block) 194static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
132{ 195{
133 return generic_block_bmap(mapping, block, fat_get_block); 196 return generic_block_bmap(mapping, block, fat_get_block);
@@ -141,6 +204,7 @@ static struct address_space_operations fat_aops = {
141 .sync_page = block_sync_page, 204 .sync_page = block_sync_page,
142 .prepare_write = fat_prepare_write, 205 .prepare_write = fat_prepare_write,
143 .commit_write = fat_commit_write, 206 .commit_write = fat_commit_write,
207 .direct_IO = fat_direct_IO,
144 .bmap = _fat_bmap 208 .bmap = _fat_bmap
145}; 209};
146 210
diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h
index 941da5c016a0..e933e2a355ad 100644
--- a/include/linux/msdos_fs.h
+++ b/include/linux/msdos_fs.h
@@ -329,7 +329,8 @@ static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len)
329extern void fat_cache_inval_inode(struct inode *inode); 329extern void fat_cache_inval_inode(struct inode *inode);
330extern int fat_get_cluster(struct inode *inode, int cluster, 330extern int fat_get_cluster(struct inode *inode, int cluster,
331 int *fclus, int *dclus); 331 int *fclus, int *dclus);
332extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys); 332extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
333 unsigned long *mapped_blocks);
333 334
334/* fat/dir.c */ 335/* fat/dir.c */
335extern struct file_operations fat_dir_operations; 336extern struct file_operations fat_dir_operations;