diff options
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/cache.c | 14 | ||||
-rw-r--r-- | fs/fat/dir.c | 6 | ||||
-rw-r--r-- | fs/fat/inode.c | 82 |
3 files changed, 87 insertions, 15 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 | ||
298 | int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys) | 298 | int 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 | ||
74 | next: | 74 | next: |
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 | ||
52 | static int fat_get_block(struct inode *inode, sector_t iblock, | 53 | static 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 | ||
103 | static 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 | |||
117 | static 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 | |||
89 | static int fat_writepage(struct page *page, struct writeback_control *wbc) | 124 | static 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 | ||
166 | static 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 | |||
131 | static sector_t _fat_bmap(struct address_space *mapping, sector_t block) | 194 | static 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 | ||