diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/buffer.c | 60 | ||||
-rw-r--r-- | fs/fat/file.c | 31 |
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 | */ |
2163 | int generic_cont_expand(struct inode *inode, loff_t size) | 2163 | static 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 | ||
2206 | int 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 | |||
2226 | int 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); | |||
3145 | EXPORT_SYMBOL(generic_block_bmap); | 3176 | EXPORT_SYMBOL(generic_block_bmap); |
3146 | EXPORT_SYMBOL(generic_commit_write); | 3177 | EXPORT_SYMBOL(generic_commit_write); |
3147 | EXPORT_SYMBOL(generic_cont_expand); | 3178 | EXPORT_SYMBOL(generic_cont_expand); |
3179 | EXPORT_SYMBOL(generic_cont_expand_simple); | ||
3148 | EXPORT_SYMBOL(init_buffer); | 3180 | EXPORT_SYMBOL(init_buffer); |
3149 | EXPORT_SYMBOL(invalidate_bdev); | 3181 | EXPORT_SYMBOL(invalidate_bdev); |
3150 | EXPORT_SYMBOL(ll_rw_block); | 3182 | EXPORT_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 | ||
15 | int fat_generic_ioctl(struct inode *inode, struct file *filp, | 16 | int 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 | ||
128 | static 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); | ||
142 | out: | ||
143 | return err; | ||
144 | } | ||
145 | |||
127 | int fat_notify_change(struct dentry *dentry, struct iattr *attr) | 146 | int 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 | ||