Commit 02cc4540 authored by Nick Piggin's avatar Nick Piggin Committed by James Toy

Signed-off-by: Nick Piggin <npiggin@suse.de>

Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Jan Kara <jack@suse.cz>
Cc: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
Cc: Chris Mason <chris.mason@oracle.com>
Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Cc: Miklos Szeredi <miklos@szeredi.hu>
Cc: Trond Myklebust <trond.myklebust@fys.uio.no>
Cc: Steven French <sfrench@us.ibm.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 6c0f9796
......@@ -302,7 +302,7 @@ extern int fat_generic_ioctl(struct inode *inode, struct file *filp,
extern const struct file_operations fat_file_operations;
extern const struct inode_operations fat_file_inode_operations;
extern int fat_setattr(struct dentry * dentry, struct iattr * attr);
extern void fat_truncate(struct inode *inode);
extern void fat_truncate_blocks(struct inode *inode, loff_t offset);
extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
extern int fat_file_fsync(struct file *file, struct dentry *dentry,
......
......@@ -252,7 +252,7 @@ static int fat_free(struct inode *inode, int skip)
return fat_free_clusters(inode, free_start);
}
void fat_truncate(struct inode *inode)
void fat_truncate_blocks(struct inode *inode, loff_t offset)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
const unsigned int cluster_size = sbi->cluster_size;
......@@ -262,10 +262,10 @@ void fat_truncate(struct inode *inode)
* This protects against truncating a file bigger than it was then
* trying to write into the hole.
*/
if (MSDOS_I(inode)->mmu_private > inode->i_size)
MSDOS_I(inode)->mmu_private = inode->i_size;
if (MSDOS_I(inode)->mmu_private > offset)
MSDOS_I(inode)->mmu_private = offset;
nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;
nr_clusters = (offset + (cluster_size - 1)) >> sbi->cluster_bits;
fat_free(inode, nr_clusters);
fat_flush_inodes(inode->i_sb, inode, NULL);
......@@ -333,6 +333,18 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
return 0;
}
static int fat_setsize(struct inode *inode, loff_t offset)
{
int error;
error = simple_setsize(inode, offset);
if (error)
return error;
fat_truncate_blocks(inode, offset);
return error;
}
#define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
/* valid file mode bits */
#define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO)
......@@ -347,7 +359,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
/*
* Expand the file. Since inode_setattr() updates ->i_size
* before calling the ->truncate(), but FAT needs to fill the
* hole before it.
* hole before it. XXX: this is no longer true with new truncate
* sequence.
*/
if (attr->ia_valid & ATTR_SIZE) {
if (attr->ia_size > inode->i_size) {
......@@ -396,15 +409,21 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
attr->ia_valid &= ~ATTR_MODE;
}
if (attr->ia_valid)
error = inode_setattr(inode, attr);
if (attr->ia_valid & ATTR_SIZE) {
error = fat_setsize(inode, attr->ia_size);
if (error)
goto out;
}
generic_setattr(inode, attr);
mark_inode_dirty(inode);
out:
return error;
}
EXPORT_SYMBOL_GPL(fat_setattr);
const struct inode_operations fat_file_inode_operations = {
.truncate = fat_truncate,
.new_truncate = 1,
.setattr = fat_setattr,
.getattr = fat_getattr,
};
......@@ -146,10 +146,19 @@ static int fat_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
int err;
*pagep = NULL;
return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
err = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
fat_get_block,
&MSDOS_I(mapping->host)->mmu_private);
if (err < 0) {
struct inode *inode = mapping->host;
loff_t isize = inode->i_size;
if (pos + len > isize)
fat_truncate_blocks(inode, isize);
}
return err;
}
static int fat_write_end(struct file *file, struct address_space *mapping,
......@@ -159,6 +168,11 @@ static int fat_write_end(struct file *file, struct address_space *mapping,
struct inode *inode = mapping->host;
int err;
err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
if (err < len) {
loff_t isize = inode->i_size;
if (pos + len > isize)
fat_truncate_blocks(inode, isize);
}
if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
......@@ -173,6 +187,7 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
ssize_t ret;
if (rw == WRITE) {
/*
......@@ -193,8 +208,12 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
* FAT need to use the DIO_LOCKING for avoiding the race
* condition of fat_get_block() and ->truncate().
*/
return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
offset, nr_segs, fat_get_block, NULL);
if (ret < 0 && (rw & WRITE))
fat_truncate_blocks(inode, inode->i_size);
return ret;
}
static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
......@@ -429,7 +448,7 @@ static void fat_delete_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
inode->i_size = 0;
fat_truncate(inode);
fat_truncate_blocks(inode, 0);
clear_inode(inode);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment