diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fat/fat.h | 3 | ||||
-rw-r--r-- | fs/fat/file.c | 34 | ||||
-rw-r--r-- | fs/fat/inode.c | 35 |
3 files changed, 57 insertions, 15 deletions
diff --git a/fs/fat/fat.h b/fs/fat/fat.h index a1c8c4b44fdb..27ac25725954 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h | |||
@@ -306,7 +306,8 @@ extern long fat_generic_ioctl(struct file *filp, unsigned int cmd, | |||
306 | extern const struct file_operations fat_file_operations; | 306 | extern const struct file_operations fat_file_operations; |
307 | extern const struct inode_operations fat_file_inode_operations; | 307 | extern const struct inode_operations fat_file_inode_operations; |
308 | extern int fat_setattr(struct dentry * dentry, struct iattr * attr); | 308 | extern int fat_setattr(struct dentry * dentry, struct iattr * attr); |
309 | extern void fat_truncate(struct inode *inode); | 309 | extern int fat_setsize(struct inode *inode, loff_t offset); |
310 | extern void fat_truncate_blocks(struct inode *inode, loff_t offset); | ||
310 | extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, | 311 | extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, |
311 | struct kstat *stat); | 312 | struct kstat *stat); |
312 | extern int fat_file_fsync(struct file *file, int datasync); | 313 | extern int fat_file_fsync(struct file *file, int datasync); |
diff --git a/fs/fat/file.c b/fs/fat/file.c index 8c13b8acfd2f..990dfae022e5 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c | |||
@@ -283,7 +283,7 @@ static int fat_free(struct inode *inode, int skip) | |||
283 | return fat_free_clusters(inode, free_start); | 283 | return fat_free_clusters(inode, free_start); |
284 | } | 284 | } |
285 | 285 | ||
286 | void fat_truncate(struct inode *inode) | 286 | void fat_truncate_blocks(struct inode *inode, loff_t offset) |
287 | { | 287 | { |
288 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | 288 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); |
289 | const unsigned int cluster_size = sbi->cluster_size; | 289 | const unsigned int cluster_size = sbi->cluster_size; |
@@ -293,10 +293,10 @@ void fat_truncate(struct inode *inode) | |||
293 | * This protects against truncating a file bigger than it was then | 293 | * This protects against truncating a file bigger than it was then |
294 | * trying to write into the hole. | 294 | * trying to write into the hole. |
295 | */ | 295 | */ |
296 | if (MSDOS_I(inode)->mmu_private > inode->i_size) | 296 | if (MSDOS_I(inode)->mmu_private > offset) |
297 | MSDOS_I(inode)->mmu_private = inode->i_size; | 297 | MSDOS_I(inode)->mmu_private = offset; |
298 | 298 | ||
299 | nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits; | 299 | nr_clusters = (offset + (cluster_size - 1)) >> sbi->cluster_bits; |
300 | 300 | ||
301 | fat_free(inode, nr_clusters); | 301 | fat_free(inode, nr_clusters); |
302 | fat_flush_inodes(inode->i_sb, inode, NULL); | 302 | fat_flush_inodes(inode->i_sb, inode, NULL); |
@@ -364,6 +364,18 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode) | |||
364 | return 0; | 364 | return 0; |
365 | } | 365 | } |
366 | 366 | ||
367 | int fat_setsize(struct inode *inode, loff_t offset) | ||
368 | { | ||
369 | int error; | ||
370 | |||
371 | error = simple_setsize(inode, offset); | ||
372 | if (error) | ||
373 | return error; | ||
374 | fat_truncate_blocks(inode, offset); | ||
375 | |||
376 | return error; | ||
377 | } | ||
378 | |||
367 | #define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET) | 379 | #define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET) |
368 | /* valid file mode bits */ | 380 | /* valid file mode bits */ |
369 | #define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO) | 381 | #define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO) |
@@ -378,7 +390,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) | |||
378 | /* | 390 | /* |
379 | * Expand the file. Since inode_setattr() updates ->i_size | 391 | * Expand the file. Since inode_setattr() updates ->i_size |
380 | * before calling the ->truncate(), but FAT needs to fill the | 392 | * before calling the ->truncate(), but FAT needs to fill the |
381 | * hole before it. | 393 | * hole before it. XXX: this is no longer true with new truncate |
394 | * sequence. | ||
382 | */ | 395 | */ |
383 | if (attr->ia_valid & ATTR_SIZE) { | 396 | if (attr->ia_valid & ATTR_SIZE) { |
384 | if (attr->ia_size > inode->i_size) { | 397 | if (attr->ia_size > inode->i_size) { |
@@ -427,15 +440,20 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) | |||
427 | attr->ia_valid &= ~ATTR_MODE; | 440 | attr->ia_valid &= ~ATTR_MODE; |
428 | } | 441 | } |
429 | 442 | ||
430 | if (attr->ia_valid) | 443 | if (attr->ia_valid & ATTR_SIZE) { |
431 | error = inode_setattr(inode, attr); | 444 | error = fat_setsize(inode, attr->ia_size); |
445 | if (error) | ||
446 | goto out; | ||
447 | } | ||
448 | |||
449 | generic_setattr(inode, attr); | ||
450 | mark_inode_dirty(inode); | ||
432 | out: | 451 | out: |
433 | return error; | 452 | return error; |
434 | } | 453 | } |
435 | EXPORT_SYMBOL_GPL(fat_setattr); | 454 | EXPORT_SYMBOL_GPL(fat_setattr); |
436 | 455 | ||
437 | const struct inode_operations fat_file_inode_operations = { | 456 | const struct inode_operations fat_file_inode_operations = { |
438 | .truncate = fat_truncate, | ||
439 | .setattr = fat_setattr, | 457 | .setattr = fat_setattr, |
440 | .getattr = fat_getattr, | 458 | .getattr = fat_getattr, |
441 | }; | 459 | }; |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index ed33904926ee..7bf45aee56d7 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c | |||
@@ -142,14 +142,29 @@ static int fat_readpages(struct file *file, struct address_space *mapping, | |||
142 | return mpage_readpages(mapping, pages, nr_pages, fat_get_block); | 142 | return mpage_readpages(mapping, pages, nr_pages, fat_get_block); |
143 | } | 143 | } |
144 | 144 | ||
145 | static void fat_write_failed(struct address_space *mapping, loff_t to) | ||
146 | { | ||
147 | struct inode *inode = mapping->host; | ||
148 | |||
149 | if (to > inode->i_size) { | ||
150 | truncate_pagecache(inode, to, inode->i_size); | ||
151 | fat_truncate_blocks(inode, inode->i_size); | ||
152 | } | ||
153 | } | ||
154 | |||
145 | static int fat_write_begin(struct file *file, struct address_space *mapping, | 155 | static int fat_write_begin(struct file *file, struct address_space *mapping, |
146 | loff_t pos, unsigned len, unsigned flags, | 156 | loff_t pos, unsigned len, unsigned flags, |
147 | struct page **pagep, void **fsdata) | 157 | struct page **pagep, void **fsdata) |
148 | { | 158 | { |
159 | int err; | ||
160 | |||
149 | *pagep = NULL; | 161 | *pagep = NULL; |
150 | return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, | 162 | err = cont_write_begin_newtrunc(file, mapping, pos, len, flags, |
151 | fat_get_block, | 163 | pagep, fsdata, fat_get_block, |
152 | &MSDOS_I(mapping->host)->mmu_private); | 164 | &MSDOS_I(mapping->host)->mmu_private); |
165 | if (err < 0) | ||
166 | fat_write_failed(mapping, pos + len); | ||
167 | return err; | ||
153 | } | 168 | } |
154 | 169 | ||
155 | static int fat_write_end(struct file *file, struct address_space *mapping, | 170 | static int fat_write_end(struct file *file, struct address_space *mapping, |
@@ -159,6 +174,8 @@ static int fat_write_end(struct file *file, struct address_space *mapping, | |||
159 | struct inode *inode = mapping->host; | 174 | struct inode *inode = mapping->host; |
160 | int err; | 175 | int err; |
161 | err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); | 176 | err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); |
177 | if (err < len) | ||
178 | fat_write_failed(mapping, pos + len); | ||
162 | if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) { | 179 | if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) { |
163 | inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; | 180 | inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; |
164 | MSDOS_I(inode)->i_attrs |= ATTR_ARCH; | 181 | MSDOS_I(inode)->i_attrs |= ATTR_ARCH; |
@@ -172,7 +189,9 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, | |||
172 | loff_t offset, unsigned long nr_segs) | 189 | loff_t offset, unsigned long nr_segs) |
173 | { | 190 | { |
174 | struct file *file = iocb->ki_filp; | 191 | struct file *file = iocb->ki_filp; |
175 | struct inode *inode = file->f_mapping->host; | 192 | struct address_space *mapping = file->f_mapping; |
193 | struct inode *inode = mapping->host; | ||
194 | ssize_t ret; | ||
176 | 195 | ||
177 | if (rw == WRITE) { | 196 | if (rw == WRITE) { |
178 | /* | 197 | /* |
@@ -193,8 +212,12 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, | |||
193 | * FAT need to use the DIO_LOCKING for avoiding the race | 212 | * FAT need to use the DIO_LOCKING for avoiding the race |
194 | * condition of fat_get_block() and ->truncate(). | 213 | * condition of fat_get_block() and ->truncate(). |
195 | */ | 214 | */ |
196 | return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, | 215 | ret = blockdev_direct_IO_newtrunc(rw, iocb, inode, inode->i_sb->s_bdev, |
197 | offset, nr_segs, fat_get_block, NULL); | 216 | iov, offset, nr_segs, fat_get_block, NULL); |
217 | if (ret < 0 && (rw & WRITE)) | ||
218 | fat_write_failed(mapping, offset + iov_length(iov, nr_segs)); | ||
219 | |||
220 | return ret; | ||
198 | } | 221 | } |
199 | 222 | ||
200 | static sector_t _fat_bmap(struct address_space *mapping, sector_t block) | 223 | static sector_t _fat_bmap(struct address_space *mapping, sector_t block) |
@@ -429,7 +452,7 @@ static void fat_delete_inode(struct inode *inode) | |||
429 | { | 452 | { |
430 | truncate_inode_pages(&inode->i_data, 0); | 453 | truncate_inode_pages(&inode->i_data, 0); |
431 | inode->i_size = 0; | 454 | inode->i_size = 0; |
432 | fat_truncate(inode); | 455 | fat_truncate_blocks(inode, 0); |
433 | clear_inode(inode); | 456 | clear_inode(inode); |
434 | } | 457 | } |
435 | 458 | ||