diff options
Diffstat (limited to 'fs/fat/file.c')
-rw-r--r-- | fs/fat/file.c | 31 |
1 files changed, 28 insertions, 3 deletions
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 | ||