aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/buffer.c60
-rw-r--r--fs/fat/file.c31
2 files changed, 74 insertions, 17 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 5287be18633b..55023231e460 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2160,11 +2160,12 @@ int block_read_full_page(struct page *page, get_block_t *get_block)
2160 * truncates. Uses prepare/commit_write to allow the filesystem to 2160 * truncates. Uses prepare/commit_write to allow the filesystem to
2161 * deal with the hole. 2161 * deal with the hole.
2162 */ 2162 */
2163int generic_cont_expand(struct inode *inode, loff_t size) 2163static int __generic_cont_expand(struct inode *inode, loff_t size,
2164 pgoff_t index, unsigned int offset)
2164{ 2165{
2165 struct address_space *mapping = inode->i_mapping; 2166 struct address_space *mapping = inode->i_mapping;
2166 struct page *page; 2167 struct page *page;
2167 unsigned long index, offset, limit; 2168 unsigned long limit;
2168 int err; 2169 int err;
2169 2170
2170 err = -EFBIG; 2171 err = -EFBIG;
@@ -2176,24 +2177,24 @@ int generic_cont_expand(struct inode *inode, loff_t size)
2176 if (size > inode->i_sb->s_maxbytes) 2177 if (size > inode->i_sb->s_maxbytes)
2177 goto out; 2178 goto out;
2178 2179
2179 offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
2180
2181 /* ugh. in prepare/commit_write, if from==to==start of block, we
2182 ** skip the prepare. make sure we never send an offset for the start
2183 ** of a block
2184 */
2185 if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
2186 offset++;
2187 }
2188 index = size >> PAGE_CACHE_SHIFT;
2189 err = -ENOMEM; 2180 err = -ENOMEM;
2190 page = grab_cache_page(mapping, index); 2181 page = grab_cache_page(mapping, index);
2191 if (!page) 2182 if (!page)
2192 goto out; 2183 goto out;
2193 err = mapping->a_ops->prepare_write(NULL, page, offset, offset); 2184 err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
2194 if (!err) { 2185 if (err) {
2195 err = mapping->a_ops->commit_write(NULL, page, offset, offset); 2186 /*
2187 * ->prepare_write() may have instantiated a few blocks
2188 * outside i_size. Trim these off again.
2189 */
2190 unlock_page(page);
2191 page_cache_release(page);
2192 vmtruncate(inode, inode->i_size);
2193 goto out;
2196 } 2194 }
2195
2196 err = mapping->a_ops->commit_write(NULL, page, offset, offset);
2197
2197 unlock_page(page); 2198 unlock_page(page);
2198 page_cache_release(page); 2199 page_cache_release(page);
2199 if (err > 0) 2200 if (err > 0)
@@ -2202,6 +2203,36 @@ out:
2202 return err; 2203 return err;
2203} 2204}
2204 2205
2206int generic_cont_expand(struct inode *inode, loff_t size)
2207{
2208 pgoff_t index;
2209 unsigned int offset;
2210
2211 offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
2212
2213 /* ugh. in prepare/commit_write, if from==to==start of block, we
2214 ** skip the prepare. make sure we never send an offset for the start
2215 ** of a block
2216 */
2217 if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
2218 /* caller must handle this extra byte. */
2219 offset++;
2220 }
2221 index = size >> PAGE_CACHE_SHIFT;
2222
2223 return __generic_cont_expand(inode, size, index, offset);
2224}
2225
2226int generic_cont_expand_simple(struct inode *inode, loff_t size)
2227{
2228 loff_t pos = size - 1;
2229 pgoff_t index = pos >> PAGE_CACHE_SHIFT;
2230 unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
2231
2232 /* prepare/commit_write can handle even if from==to==start of block. */
2233 return __generic_cont_expand(inode, size, index, offset);
2234}
2235
2205/* 2236/*
2206 * For moronic filesystems that do not allow holes in file. 2237 * For moronic filesystems that do not allow holes in file.
2207 * We may have to extend the file. 2238 * We may have to extend the file.
@@ -3145,6 +3176,7 @@ EXPORT_SYMBOL(fsync_bdev);
3145EXPORT_SYMBOL(generic_block_bmap); 3176EXPORT_SYMBOL(generic_block_bmap);
3146EXPORT_SYMBOL(generic_commit_write); 3177EXPORT_SYMBOL(generic_commit_write);
3147EXPORT_SYMBOL(generic_cont_expand); 3178EXPORT_SYMBOL(generic_cont_expand);
3179EXPORT_SYMBOL(generic_cont_expand_simple);
3148EXPORT_SYMBOL(init_buffer); 3180EXPORT_SYMBOL(init_buffer);
3149EXPORT_SYMBOL(invalidate_bdev); 3181EXPORT_SYMBOL(invalidate_bdev);
3150EXPORT_SYMBOL(ll_rw_block); 3182EXPORT_SYMBOL(ll_rw_block);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 15229fe569c3..9b07c328a6fc 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -11,6 +11,7 @@
11#include <linux/msdos_fs.h> 11#include <linux/msdos_fs.h>
12#include <linux/smp_lock.h> 12#include <linux/smp_lock.h>
13#include <linux/buffer_head.h> 13#include <linux/buffer_head.h>
14#include <linux/writeback.h>
14 15
15int fat_generic_ioctl(struct inode *inode, struct file *filp, 16int fat_generic_ioctl(struct inode *inode, struct file *filp,
16 unsigned int cmd, unsigned long arg) 17 unsigned int cmd, unsigned long arg)
@@ -124,6 +125,24 @@ struct file_operations fat_file_operations = {
124 .sendfile = generic_file_sendfile, 125 .sendfile = generic_file_sendfile,
125}; 126};
126 127
128static int fat_cont_expand(struct inode *inode, loff_t size)
129{
130 struct address_space *mapping = inode->i_mapping;
131 loff_t start = inode->i_size, count = size - inode->i_size;
132 int err;
133
134 err = generic_cont_expand_simple(inode, size);
135 if (err)
136 goto out;
137
138 inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
139 mark_inode_dirty(inode);
140 if (IS_SYNC(inode))
141 err = sync_page_range_nolock(inode, mapping, start, count);
142out:
143 return err;
144}
145
127int fat_notify_change(struct dentry *dentry, struct iattr *attr) 146int fat_notify_change(struct dentry *dentry, struct iattr *attr)
128{ 147{
129 struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); 148 struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
@@ -132,11 +151,17 @@ int fat_notify_change(struct dentry *dentry, struct iattr *attr)
132 151
133 lock_kernel(); 152 lock_kernel();
134 153
135 /* FAT cannot truncate to a longer file */ 154 /*
155 * Expand the file. Since inode_setattr() updates ->i_size
156 * before calling the ->truncate(), but FAT needs to fill the
157 * hole before it.
158 */
136 if (attr->ia_valid & ATTR_SIZE) { 159 if (attr->ia_valid & ATTR_SIZE) {
137 if (attr->ia_size > inode->i_size) { 160 if (attr->ia_size > inode->i_size) {
138 error = -EPERM; 161 error = fat_cont_expand(inode, attr->ia_size);
139 goto out; 162 if (error || attr->ia_valid == ATTR_SIZE)
163 goto out;
164 attr->ia_valid &= ~ATTR_SIZE;
140 } 165 }
141 } 166 }
142 167