From 22c6186ecea0be9eff1c399298ad36e94a59995f Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Mon, 16 Mar 2015 04:33:53 -0700 Subject: direct_IO: remove rw from a_ops->direct_IO() Now that no one is using rw, remove it completely. Signed-off-by: Omar Sandoval Signed-off-by: Al Viro --- mm/filemap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'mm/filemap.c') diff --git a/mm/filemap.c b/mm/filemap.c index 876f4e6f3ed6..9920db455f05 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1707,7 +1707,7 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) pos + count - 1); if (!retval) { struct iov_iter data = *iter; - retval = mapping->a_ops->direct_IO(READ, iocb, &data, pos); + retval = mapping->a_ops->direct_IO(iocb, &data, pos); } if (retval > 0) { @@ -2395,7 +2395,7 @@ generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos) } data = *from; - written = mapping->a_ops->direct_IO(WRITE, iocb, &data, pos); + written = mapping->a_ops->direct_IO(iocb, &data, pos); /* * Finally, try again to invalidate clean pages which might have been -- cgit v1.2.3 From 0b8def9d6dfa6b2a9a2740cf81d8d2c134688d39 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 7 Apr 2015 10:22:53 -0400 Subject: __generic_file_write_iter: keep ->ki_pos and return value consistent A side effect worth noting: in O_APPEND case we set ->ki_pos early, so if it turns out to be an error or a zero-length write, we'll end up with ->ki_pos modified. Safe, since all callers never look at the ->ki_pos after the call of __generic_file_write_iter() returning non-positive, all the way to caller of ->write_iter() and those discard ->ki_pos when getting that. Signed-off-by: Al Viro --- mm/filemap.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'mm/filemap.c') diff --git a/mm/filemap.c b/mm/filemap.c index 9920db455f05..353f82e09e63 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2557,7 +2557,6 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct address_space * mapping = file->f_mapping; struct inode *inode = mapping->host; - loff_t pos = iocb->ki_pos; ssize_t written = 0; ssize_t err; ssize_t status; @@ -2565,7 +2564,7 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); + err = generic_write_checks(file, &iocb->ki_pos, &count, S_ISBLK(inode->i_mode)); if (err) goto out; @@ -2583,9 +2582,9 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) goto out; if (io_is_direct(file)) { - loff_t endbyte; + loff_t pos, endbyte; - written = generic_file_direct_write(iocb, from, pos); + written = generic_file_direct_write(iocb, from, iocb->ki_pos); /* * If the write stopped short of completing, fall back to * buffered writes. Some filesystems do this for writes to @@ -2593,13 +2592,10 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) * not succeed (even if it did, DAX does not handle dirty * page-cache pages correctly). */ - if (written < 0 || written == count || IS_DAX(inode)) + if (written < 0 || !iov_iter_count(from) || IS_DAX(inode)) goto out; - pos += written; - count -= written; - - status = generic_perform_write(file, from, pos); + status = generic_perform_write(file, from, pos = iocb->ki_pos); /* * If generic_perform_write() returned a synchronous error * then we want to return the number of bytes which were @@ -2611,15 +2607,15 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) err = status; goto out; } - iocb->ki_pos = pos + status; /* * We need to ensure that the page cache pages are written to * disk and invalidated to preserve the expected O_DIRECT * semantics. */ endbyte = pos + status - 1; - err = filemap_write_and_wait_range(file->f_mapping, pos, endbyte); + err = filemap_write_and_wait_range(mapping, pos, endbyte); if (err == 0) { + iocb->ki_pos = endbyte + 1; written += status; invalidate_mapping_pages(mapping, pos >> PAGE_CACHE_SHIFT, @@ -2631,9 +2627,9 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) */ } } else { - written = generic_perform_write(file, from, pos); - if (likely(written >= 0)) - iocb->ki_pos = pos + written; + written = generic_perform_write(file, from, iocb->ki_pos); + if (likely(written > 0)) + iocb->ki_pos += written; } out: current->backing_dev_info = NULL; -- cgit v1.2.3 From 5f380c7fa7e01f15ca0816bd241ece9a64a73192 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 7 Apr 2015 11:28:12 -0400 Subject: lift generic_write_checks() into callers of __generic_file_write_iter() Signed-off-by: Al Viro --- fs/block_dev.c | 10 ++++++++++ fs/cifs/file.c | 39 +++++++++++++++++++++++---------------- fs/ext4/file.c | 14 +++++++++++--- fs/udf/file.c | 10 ++++++++++ mm/filemap.c | 17 ++++++----------- 5 files changed, 60 insertions(+), 30 deletions(-) (limited to 'mm/filemap.c') diff --git a/fs/block_dev.c b/fs/block_dev.c index 6e3de63c3055..bcd7f97beab9 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1597,6 +1597,16 @@ ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct blk_plug plug; ssize_t ret; + size_t count = iov_iter_count(from); + + ret = generic_write_checks(file, &iocb->ki_pos, &count, 1); + if (ret) + return ret; + + if (count == 0) + return 0; + + iov_iter_truncate(from, count); blk_start_plug(&plug); ret = __generic_file_write_iter(iocb, from); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 3cb04129ddb1..3c5c9bc5cbaf 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2673,8 +2673,8 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = file->f_mapping->host; struct cifsInodeInfo *cinode = CIFS_I(inode); struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; - ssize_t rc = -EACCES; - loff_t lock_pos = iocb->ki_pos; + ssize_t rc; + size_t count; /* * We need to hold the sem to be sure nobody modifies lock list @@ -2682,23 +2682,30 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) */ down_read(&cinode->lock_sem); mutex_lock(&inode->i_mutex); - if (file->f_flags & O_APPEND) - lock_pos = i_size_read(inode); - if (!cifs_find_lock_conflict(cfile, lock_pos, iov_iter_count(from), + + count = iov_iter_count(from); + rc = generic_write_checks(file, &iocb->ki_pos, &count, 0); + if (rc) + goto out; + + if (count == 0) + goto out; + + iov_iter_truncate(from, count); + + if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from), server->vals->exclusive_lock_type, NULL, - CIFS_WRITE_OP)) { + CIFS_WRITE_OP)) rc = __generic_file_write_iter(iocb, from); - mutex_unlock(&inode->i_mutex); - - if (rc > 0) { - ssize_t err; + else + rc = -EACCES; +out: + mutex_unlock(&inode->i_mutex); - err = generic_write_sync(file, iocb->ki_pos - rc, rc); - if (err < 0) - rc = err; - } - } else { - mutex_unlock(&inode->i_mutex); + if (rc > 0) { + ssize_t err = generic_write_sync(file, iocb->ki_pos - rc, rc); + if (err < 0) + rc = err; } up_read(&cinode->lock_sem); return rc; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 9ad03036d9f5..f7cca423dded 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -132,9 +132,8 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ret = -EFBIG; goto errout; } - - if (pos + length > sbi->s_bitmap_maxbytes) - iov_iter_truncate(from, sbi->s_bitmap_maxbytes - pos); + iov_iter_truncate(from, sbi->s_bitmap_maxbytes - pos); + length = iov_iter_count(from); } iocb->private = &overwrite; @@ -172,7 +171,16 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) } } + ret = generic_write_checks(file, &iocb->ki_pos, &length, 0); + if (ret) + goto out; + + if (length == 0) + goto out; + + iov_iter_truncate(from, length); ret = __generic_file_write_iter(iocb, from); +out: mutex_unlock(&inode->i_mutex); if (ret > 0) { diff --git a/fs/udf/file.c b/fs/udf/file.c index 78d42548b260..35e81ed99405 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -151,7 +151,17 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) } else up_write(&iinfo->i_data_sem); + retval = generic_write_checks(file, &iocb->ki_pos, &count, 0); + if (retval) + goto out; + + if (count == 0) + goto out; + + iov_iter_truncate(from, count); + retval = __generic_file_write_iter(iocb, from); +out: mutex_unlock(&inode->i_mutex); if (retval > 0) { diff --git a/mm/filemap.c b/mm/filemap.c index 353f82e09e63..a794a7f98743 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2560,19 +2560,9 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ssize_t written = 0; ssize_t err; ssize_t status; - size_t count = iov_iter_count(from); /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &iocb->ki_pos, &count, S_ISBLK(inode->i_mode)); - if (err) - goto out; - - if (count == 0) - goto out; - - iov_iter_truncate(from, count); - err = file_remove_suid(file); if (err) goto out; @@ -2651,9 +2641,14 @@ ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; ssize_t ret; + size_t count = iov_iter_count(from); mutex_lock(&inode->i_mutex); - ret = __generic_file_write_iter(iocb, from); + ret = generic_write_checks(file, &iocb->ki_pos, &count, 0); + if (!ret && count) { + iov_iter_truncate(from, count); + ret = __generic_file_write_iter(iocb, from); + } mutex_unlock(&inode->i_mutex); if (ret > 0) { -- cgit v1.2.3 From 0fa6b005afdb3152ce85df963302e59b61115f9b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 4 Apr 2015 04:05:48 -0400 Subject: generic_write_checks(): drop isblk argument all remaining callers are passing 0; some just obscure that fact. Signed-off-by: Al Viro --- fs/9p/vfs_file.c | 2 +- fs/btrfs/file.c | 2 +- fs/ceph/file.c | 2 +- fs/cifs/file.c | 4 ++-- fs/ext4/file.c | 2 +- fs/fuse/file.c | 6 +++--- fs/ncpfs/file.c | 2 +- fs/nfs/direct.c | 2 +- fs/ntfs/file.c | 2 +- fs/ocfs2/file.c | 3 +-- fs/udf/file.c | 2 +- fs/xfs/xfs_file.c | 2 +- include/linux/fs.h | 2 +- mm/filemap.c | 63 +++++++++++++++++------------------------------------- 14 files changed, 36 insertions(+), 60 deletions(-) (limited to 'mm/filemap.c') diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index d7fcb775311e..b5b020ace1b3 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -409,7 +409,7 @@ v9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) size_t count = iov_iter_count(from); int err = 0; - retval = generic_write_checks(file, &origin, &count, 0); + retval = generic_write_checks(file, &origin, &count); if (retval) return retval; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index cdc801c85105..691a84a81e09 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1747,7 +1747,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, mutex_lock(&inode->i_mutex); current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); + err = generic_write_checks(file, &pos, &count); if (err) { mutex_unlock(&inode->i_mutex); goto out; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 56237ea5fc22..761841903160 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -953,7 +953,7 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); + err = generic_write_checks(file, &pos, &count); if (err) goto out; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 3c5c9bc5cbaf..4202e74b2db5 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2580,7 +2580,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) */ len = iov_iter_count(from); - rc = generic_write_checks(file, &iocb->ki_pos, &len, 0); + rc = generic_write_checks(file, &iocb->ki_pos, &len); if (rc) return rc; @@ -2684,7 +2684,7 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) mutex_lock(&inode->i_mutex); count = iov_iter_count(from); - rc = generic_write_checks(file, &iocb->ki_pos, &count, 0); + rc = generic_write_checks(file, &iocb->ki_pos, &count); if (rc) goto out; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index f7cca423dded..1f0afc181b7b 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -171,7 +171,7 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) } } - ret = generic_write_checks(file, &iocb->ki_pos, &length, 0); + ret = generic_write_checks(file, &iocb->ki_pos, &length); if (ret) goto out; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 3d355e946991..4c04a8144a75 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1167,7 +1167,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); + err = generic_write_checks(file, &pos, &count); if (err) goto out; @@ -1420,7 +1420,7 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) /* Don't allow parallel writes to the same file */ mutex_lock(&inode->i_mutex); - res = generic_write_checks(file, &iocb->ki_pos, &count, 0); + res = generic_write_checks(file, &iocb->ki_pos, &count); if (!res) { iov_iter_truncate(from, count); res = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE); @@ -2841,7 +2841,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset) io->done = &wait; if (iov_iter_rw(iter) == WRITE) { - ret = generic_write_checks(file, &pos, &count, 0); + ret = generic_write_checks(file, &pos, &count); if (!ret) { iov_iter_truncate(iter, count); ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE); diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 479bf8db264e..ab6363b16556 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -177,7 +177,7 @@ ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) void *bouncebuffer; ncp_dbg(1, "enter %pD2\n", file); - errno = generic_write_checks(file, &pos, &count, 0); + errno = generic_write_checks(file, &pos, &count); if (errno) return errno; iov_iter_truncate(from, count); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 06503bc604e1..5ddd77acb3f7 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -977,7 +977,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, dfprintk(FILE, "NFS: direct write(%pD2, %zd@%Ld)\n", file, count, (long long) pos); - result = generic_write_checks(file, &pos, &count, 0); + result = generic_write_checks(file, &pos, &count); if (result) goto out; diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 77087d5ad458..cec4ec3c1ede 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -345,7 +345,7 @@ static ssize_t ntfs_prepare_file_for_write(struct kiocb *iocb, "0x%llx, count 0x%zx.", vi->i_ino, (unsigned)le32_to_cpu(ni->type), (unsigned long long)iocb->ki_pos, count); - err = generic_write_checks(file, &iocb->ki_pos, &count, S_ISBLK(vi->i_mode)); + err = generic_write_checks(file, &iocb->ki_pos, &count); if (unlikely(err)) goto out; iov_iter_truncate(from, count); diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 0a6ec7e6efd8..1c11314946cb 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2374,8 +2374,7 @@ relock: /* communicate with ocfs2_dio_end_io */ ocfs2_iocb_set_rw_locked(iocb, rw_level); - ret = generic_write_checks(file, ppos, &count, - S_ISBLK(inode->i_mode)); + ret = generic_write_checks(file, ppos, &count); if (ret) goto out_dio; diff --git a/fs/udf/file.c b/fs/udf/file.c index 35e81ed99405..6834509a7e5a 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -151,7 +151,7 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) } else up_write(&iinfo->i_data_sem); - retval = generic_write_checks(file, &iocb->ki_pos, &count, 0); + retval = generic_write_checks(file, &iocb->ki_pos, &count); if (retval) goto out; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 44856c3b9617..43c0e6686c47 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -554,7 +554,7 @@ xfs_file_aio_write_checks( int error = 0; restart: - error = generic_write_checks(file, pos, count, S_ISBLK(inode->i_mode)); + error = generic_write_checks(file, pos, count); if (error) return error; diff --git a/include/linux/fs.h b/include/linux/fs.h index 72e3759de8c3..492948ea4c9b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2566,7 +2566,7 @@ extern int sb_min_blocksize(struct super_block *, int); extern int generic_file_mmap(struct file *, struct vm_area_struct *); extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *); -int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk); +int generic_write_checks(struct file *file, loff_t *pos, size_t *count); extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); diff --git a/mm/filemap.c b/mm/filemap.c index a794a7f98743..dfc573c6ec25 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2260,7 +2260,7 @@ EXPORT_SYMBOL(read_cache_page_gfp); * Returns appropriate error code that caller should return or * zero in case that write should be allowed. */ -inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk) +inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count) { struct inode *inode = file->f_mapping->host; unsigned long limit = rlimit(RLIMIT_FSIZE); @@ -2268,20 +2268,17 @@ inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, i if (unlikely(*pos < 0)) return -EINVAL; - if (!isblk) { - /* FIXME: this is for backwards compatibility with 2.4 */ - if (file->f_flags & O_APPEND) - *pos = i_size_read(inode); + /* FIXME: this is for backwards compatibility with 2.4 */ + if (file->f_flags & O_APPEND) + *pos = i_size_read(inode); - if (limit != RLIM_INFINITY) { - if (*pos >= limit) { - send_sig(SIGXFSZ, current, 0); - return -EFBIG; - } - if (*count > limit - (typeof(limit))*pos) { - *count = limit - (typeof(limit))*pos; - } + if (limit != RLIM_INFINITY) { + if (*pos >= limit) { + send_sig(SIGXFSZ, current, 0); + return -EFBIG; } + if (*count > limit - (typeof(limit))*pos) + *count = limit - (typeof(limit))*pos; } /* @@ -2289,12 +2286,10 @@ inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, i */ if (unlikely(*pos + *count > MAX_NON_LFS && !(file->f_flags & O_LARGEFILE))) { - if (*pos >= MAX_NON_LFS) { + if (*pos >= MAX_NON_LFS) return -EFBIG; - } - if (*count > MAX_NON_LFS - (unsigned long)*pos) { + if (*count > MAX_NON_LFS - (unsigned long)*pos) *count = MAX_NON_LFS - (unsigned long)*pos; - } } /* @@ -2304,33 +2299,15 @@ inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count, i * exceeded without writing data we send a signal and return EFBIG. * Linus frestrict idea will clean these up nicely.. */ - if (likely(!isblk)) { - if (unlikely(*pos >= inode->i_sb->s_maxbytes)) { - if (*count || *pos > inode->i_sb->s_maxbytes) { - return -EFBIG; - } - /* zero-length writes at ->s_maxbytes are OK */ - } - - if (unlikely(*pos + *count > inode->i_sb->s_maxbytes)) - *count = inode->i_sb->s_maxbytes - *pos; - } else { -#ifdef CONFIG_BLOCK - loff_t isize; - if (bdev_read_only(I_BDEV(inode))) - return -EPERM; - isize = i_size_read(inode); - if (*pos >= isize) { - if (*count || *pos > isize) - return -ENOSPC; + if (unlikely(*pos >= inode->i_sb->s_maxbytes)) { + if (*count || *pos > inode->i_sb->s_maxbytes) { + return -EFBIG; } - - if (*pos + *count > isize) - *count = isize - *pos; -#else - return -EPERM; -#endif + /* zero-length writes at ->s_maxbytes are OK */ } + + if (unlikely(*pos + *count > inode->i_sb->s_maxbytes)) + *count = inode->i_sb->s_maxbytes - *pos; return 0; } EXPORT_SYMBOL(generic_write_checks); @@ -2644,7 +2621,7 @@ ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) size_t count = iov_iter_count(from); mutex_lock(&inode->i_mutex); - ret = generic_write_checks(file, &iocb->ki_pos, &count, 0); + ret = generic_write_checks(file, &iocb->ki_pos, &count); if (!ret && count) { iov_iter_truncate(from, count); ret = __generic_file_write_iter(iocb, from); -- cgit v1.2.3 From 3309dd04cbcd2cdad168485af5cf3576b5051e49 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 9 Apr 2015 12:55:47 -0400 Subject: switch generic_write_checks() to iocb and iter ... returning -E... upon error and amount of data left in iter after (possible) truncation upon success. Note, that normal case gives a non-zero (positive) return value, so any tests for != 0 _must_ be updated. Signed-off-by: Al Viro Conflicts: fs/ext4/file.c --- fs/9p/vfs_file.c | 26 ++++++++++---------------- fs/btrfs/file.c | 24 +++++++++--------------- fs/ceph/file.c | 14 ++++++-------- fs/cifs/file.c | 26 ++++++-------------------- fs/ext4/file.c | 20 ++++++-------------- fs/fuse/file.c | 22 +++++++--------------- fs/ncpfs/file.c | 14 +++++--------- fs/nfs/direct.c | 25 ++++++++++--------------- fs/ntfs/file.c | 11 ++++------- fs/ocfs2/file.c | 19 ++++++++++--------- fs/udf/file.c | 10 ++-------- fs/xfs/xfs_file.c | 8 ++++---- include/linux/fs.h | 2 +- mm/filemap.c | 44 +++++++++++++++++++------------------------- 14 files changed, 99 insertions(+), 166 deletions(-) (limited to 'mm/filemap.c') diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index b5b020ace1b3..2a9dd37dc426 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -404,21 +404,16 @@ static ssize_t v9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; - ssize_t retval = 0; - loff_t origin = iocb->ki_pos; - size_t count = iov_iter_count(from); + ssize_t retval; + loff_t origin; int err = 0; - retval = generic_write_checks(file, &origin, &count); - if (retval) + retval = generic_write_checks(iocb, from); + if (retval <= 0) return retval; - iov_iter_truncate(from, count); - - if (!count) - return 0; - - retval = p9_client_write(file->private_data, origin, from, &err); + origin = iocb->ki_pos; + retval = p9_client_write(file->private_data, iocb->ki_pos, from, &err); if (retval > 0) { struct inode *inode = file_inode(file); loff_t i_size; @@ -428,12 +423,11 @@ v9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (inode->i_mapping && inode->i_mapping->nrpages) invalidate_inode_pages2_range(inode->i_mapping, pg_start, pg_end); - origin += retval; + iocb->ki_pos += retval; i_size = i_size_read(inode); - iocb->ki_pos = origin; - if (origin > i_size) { - inode_add_bytes(inode, origin - i_size); - i_size_write(inode, origin); + if (iocb->ki_pos > i_size) { + inode_add_bytes(inode, iocb->ki_pos - i_size); + i_size_write(inode, iocb->ki_pos); } return retval; } diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 691a84a81e09..c64d11c41eeb 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1739,27 +1739,19 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, u64 start_pos; u64 end_pos; ssize_t num_written = 0; - ssize_t err = 0; - size_t count = iov_iter_count(from); bool sync = (file->f_flags & O_DSYNC) || IS_SYNC(file->f_mapping->host); - loff_t pos = iocb->ki_pos; + ssize_t err; + loff_t pos; + size_t count; mutex_lock(&inode->i_mutex); - - current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &pos, &count); - if (err) { + err = generic_write_checks(iocb, from); + if (err <= 0) { mutex_unlock(&inode->i_mutex); - goto out; - } - - if (count == 0) { - mutex_unlock(&inode->i_mutex); - goto out; + return err; } - iov_iter_truncate(from, count); - + current->backing_dev_info = inode_to_bdi(inode); err = file_remove_suid(file); if (err) { mutex_unlock(&inode->i_mutex); @@ -1786,6 +1778,8 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, */ update_time_for_write(inode); + pos = iocb->ki_pos; + count = iov_iter_count(from); start_pos = round_down(pos, root->sectorsize); if (start_pos > i_size_read(inode)) { /* Expand hole size to cover write data, preventing empty gap */ diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 761841903160..3f0b9339d823 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -941,9 +941,9 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_osd_client *osdc = &ceph_sb_to_client(inode->i_sb)->client->osdc; - ssize_t count = iov_iter_count(from), written = 0; + ssize_t count, written = 0; int err, want, got; - loff_t pos = iocb->ki_pos; + loff_t pos; if (ceph_snap(inode) != CEPH_NOSNAP) return -EROFS; @@ -953,14 +953,12 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &pos, &count); - if (err) - goto out; - - if (count == 0) + err = generic_write_checks(iocb, from); + if (err <= 0) goto out; - iov_iter_truncate(from, count); + pos = iocb->ki_pos; + count = iov_iter_count(from); err = file_remove_suid(file); if (err) goto out; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 4202e74b2db5..ca2bc5406306 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2563,7 +2563,6 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from, ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; - size_t len; ssize_t total_written = 0; struct cifsFileInfo *open_file; struct cifs_tcon *tcon; @@ -2579,16 +2578,10 @@ ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) * write request. */ - len = iov_iter_count(from); - rc = generic_write_checks(file, &iocb->ki_pos, &len); - if (rc) + rc = generic_write_checks(iocb, from); + if (rc <= 0) return rc; - if (!len) - return 0; - - iov_iter_truncate(from, len); - INIT_LIST_HEAD(&wdata_list); cifs_sb = CIFS_FILE_SB(file); open_file = file->private_data; @@ -2599,8 +2592,8 @@ ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from) memcpy(&saved_from, from, sizeof(struct iov_iter)); - rc = cifs_write_from_iter(iocb->ki_pos, len, from, open_file, cifs_sb, - &wdata_list); + rc = cifs_write_from_iter(iocb->ki_pos, iov_iter_count(from), from, + open_file, cifs_sb, &wdata_list); /* * If at least one write was successfully sent, then discard any rc @@ -2674,7 +2667,6 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) struct cifsInodeInfo *cinode = CIFS_I(inode); struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; ssize_t rc; - size_t count; /* * We need to hold the sem to be sure nobody modifies lock list @@ -2683,16 +2675,10 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) down_read(&cinode->lock_sem); mutex_lock(&inode->i_mutex); - count = iov_iter_count(from); - rc = generic_write_checks(file, &iocb->ki_pos, &count); - if (rc) + rc = generic_write_checks(iocb, from); + if (rc <= 0) goto out; - if (count == 0) - goto out; - - iov_iter_truncate(from, count); - if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(from), server->vals->exclusive_lock_type, NULL, CIFS_WRITE_OP)) diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 42b1fa33a17a..c10785f10d1d 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -97,9 +97,7 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct blk_plug plug; int o_direct = io_is_direct(file); int overwrite = 0; - size_t length = iov_iter_count(from); ssize_t ret; - loff_t pos; /* * Unaligned direct AIO must be serialized; see comment above @@ -116,16 +114,10 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) } mutex_lock(&inode->i_mutex); - ret = generic_write_checks(file, &iocb->ki_pos, &length); - if (ret) + ret = generic_write_checks(iocb, from); + if (ret <= 0) goto out; - if (length == 0) - goto out; - - iov_iter_truncate(from, length); - pos = iocb->ki_pos; - /* * If we have encountered a bitmap-format file, the size limit * is smaller than s_maxbytes, which is for extent-mapped files. @@ -133,19 +125,19 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); - if (pos >= sbi->s_bitmap_maxbytes) { + if (iocb->ki_pos >= sbi->s_bitmap_maxbytes) { ret = -EFBIG; goto out; } - iov_iter_truncate(from, sbi->s_bitmap_maxbytes - pos); + iov_iter_truncate(from, sbi->s_bitmap_maxbytes - iocb->ki_pos); } iocb->private = &overwrite; if (o_direct) { - length = iov_iter_count(from); + size_t length = iov_iter_count(from); + loff_t pos = iocb->ki_pos; blk_start_plug(&plug); - /* check whether we do a DIO overwrite or not */ if (ext4_should_dioread_nolock(inode) && !aio_mutex && !file->f_mapping->nrpages && pos + length <= i_size_read(inode)) { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 8c15d0a077e8..b86c8e08399a 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1145,13 +1145,11 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; - size_t count = iov_iter_count(from); ssize_t written = 0; ssize_t written_buffered = 0; struct inode *inode = mapping->host; ssize_t err; loff_t endbyte = 0; - loff_t pos = iocb->ki_pos; if (get_fuse_conn(inode)->writeback_cache) { /* Update size (EOF optimization) and mode (SUID clearing) */ @@ -1167,14 +1165,10 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(inode); - err = generic_write_checks(file, &pos, &count); - if (err) - goto out; - - if (count == 0) + err = generic_write_checks(iocb, from); + if (err <= 0) goto out; - iov_iter_truncate(from, count); err = file_remove_suid(file); if (err) goto out; @@ -1184,6 +1178,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) goto out; if (file->f_flags & O_DIRECT) { + loff_t pos = iocb->ki_pos; written = generic_file_direct_write(iocb, from, pos); if (written < 0 || !iov_iter_count(from)) goto out; @@ -1209,9 +1204,9 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) written += written_buffered; iocb->ki_pos = pos + written_buffered; } else { - written = fuse_perform_write(file, mapping, from, pos); + written = fuse_perform_write(file, mapping, from, iocb->ki_pos); if (written >= 0) - iocb->ki_pos = pos + written; + iocb->ki_pos += written; } out: current->backing_dev_info = NULL; @@ -1412,7 +1407,6 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); struct fuse_io_priv io = { .async = 0, .file = file }; - size_t count = iov_iter_count(from); ssize_t res; if (is_bad_inode(inode)) @@ -1420,11 +1414,9 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) /* Don't allow parallel writes to the same file */ mutex_lock(&inode->i_mutex); - res = generic_write_checks(file, &iocb->ki_pos, &count); - if (!res) { - iov_iter_truncate(from, count); + res = generic_write_checks(iocb, from); + if (res > 0) res = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE); - } fuse_invalidate_attr(inode); if (res > 0) fuse_write_update_size(inode, iocb->ki_pos); diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index ab6363b16556..011324ce9df2 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -170,20 +170,15 @@ ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); size_t already_written = 0; - loff_t pos = iocb->ki_pos; - size_t count = iov_iter_count(from); size_t bufsize; int errno; void *bouncebuffer; + off_t pos; ncp_dbg(1, "enter %pD2\n", file); - errno = generic_write_checks(file, &pos, &count); - if (errno) + errno = generic_write_checks(iocb, from); + if (errno <= 0) return errno; - iov_iter_truncate(from, count); - - if (!count) - return 0; errno = ncp_make_open(inode, O_WRONLY); if (errno) { @@ -201,10 +196,11 @@ ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) errno = -EIO; /* -ENOMEM */ goto outrel; } + pos = iocb->ki_pos; while (iov_iter_count(from)) { int written_this_time; size_t to_write = min_t(size_t, - bufsize - ((off_t)pos % bufsize), + bufsize - (pos % bufsize), iov_iter_count(from)); if (copy_from_iter(bouncebuffer, to_write, from) != to_write) { diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 5ddd77acb3f7..9634189b8545 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -969,24 +969,19 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, struct nfs_direct_req *dreq; struct nfs_lock_context *l_ctx; loff_t end; - size_t count = iov_iter_count(iter); - end = (pos + count - 1) >> PAGE_CACHE_SHIFT; - - nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count); dfprintk(FILE, "NFS: direct write(%pD2, %zd@%Ld)\n", - file, count, (long long) pos); + file, iov_iter_count(iter), (long long) iocb->ki_pos); - result = generic_write_checks(file, &pos, &count); - if (result) + result = generic_write_checks(iocb, iter); + if (result <= 0) goto out; - result = -EINVAL; - if ((ssize_t) count < 0) - goto out; - result = 0; - if (!count) - goto out; + nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, + iov_iter_count(iter)); + + pos = iocb->ki_pos; + end = (pos + iov_iter_count(iter) - 1) >> PAGE_CACHE_SHIFT; mutex_lock(&inode->i_mutex); @@ -1001,7 +996,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, goto out_unlock; } - task_io_account_write(count); + task_io_account_write(iov_iter_count(iter)); result = -ENOMEM; dreq = nfs_direct_req_alloc(); @@ -1009,7 +1004,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struct iov_iter *iter, goto out_unlock; dreq->inode = inode; - dreq->bytes_left = count; + dreq->bytes_left = iov_iter_count(iter); dreq->io_start = pos; dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); l_ctx = nfs_get_lock_context(dreq->ctx); diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index cec4ec3c1ede..7bb487e663b4 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -339,17 +339,14 @@ static ssize_t ntfs_prepare_file_for_write(struct kiocb *iocb, struct inode *vi = file_inode(file); ntfs_inode *base_ni, *ni = NTFS_I(vi); ntfs_volume *vol = ni->vol; - size_t count = iov_iter_count(from); ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, pos " "0x%llx, count 0x%zx.", vi->i_ino, (unsigned)le32_to_cpu(ni->type), - (unsigned long long)iocb->ki_pos, count); - err = generic_write_checks(file, &iocb->ki_pos, &count); - if (unlikely(err)) - goto out; - iov_iter_truncate(from, count); - if (count == 0) + (unsigned long long)iocb->ki_pos, + iov_iter_count(from)); + err = generic_write_checks(iocb, from); + if (unlikely(err <= 0)) goto out; /* * All checks have passed. Before we start doing any writing we want diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index fc53ff065364..b93919f50f0f 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2250,9 +2250,10 @@ out: static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { - int ret, direct_io, appending, rw_level, have_alloc_sem = 0; + int direct_io, appending, rw_level, have_alloc_sem = 0; int can_do_direct, has_refcount = 0; ssize_t written = 0; + ssize_t ret; size_t count = iov_iter_count(from), orig_count; loff_t old_size; u32 old_clusters; @@ -2319,13 +2320,14 @@ relock: ocfs2_inode_unlock(inode, 1); } - orig_count = count; - ret = generic_write_checks(file, &iocb->ki_pos, &count); - if (ret < 0) { - mlog_errno(ret); + orig_count = iov_iter_count(from); + ret = generic_write_checks(iocb, from); + if (ret <= 0) { + if (ret) + mlog_errno(ret); goto out; } - iov_iter_truncate(from, count); + count = ret; can_do_direct = direct_io; ret = ocfs2_prepare_inode_for_write(file, iocb->ki_pos, count, appending, @@ -2349,7 +2351,7 @@ relock: rw_level = -1; direct_io = 0; - iov_iter_reexpand(from, count = orig_count); + iov_iter_reexpand(from, orig_count); goto relock; } @@ -2377,7 +2379,7 @@ relock: loff_t endbyte; ssize_t written_buffered; written = generic_file_direct_write(iocb, from, iocb->ki_pos); - if (written < 0 || written == count) { + if (written < 0 || !iov_iter_count(from)) { ret = written; goto out_dio; } @@ -2385,7 +2387,6 @@ relock: /* * for completing the rest of the request. */ - count -= written; written_buffered = generic_perform_write(file, from, iocb->ki_pos); /* * If generic_file_buffered_write() returned a synchronous error diff --git a/fs/udf/file.c b/fs/udf/file.c index ccab8b78e363..3de2edafff73 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -120,21 +120,15 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ssize_t retval; struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); - size_t count = iov_iter_count(from); struct udf_inode_info *iinfo = UDF_I(inode); int err; mutex_lock(&inode->i_mutex); - retval = generic_write_checks(file, &iocb->ki_pos, &count); - if (retval) + retval = generic_write_checks(iocb, from); + if (retval <= 0) goto out; - if (count == 0) - goto out; - - iov_iter_truncate(from, count); - down_write(&iinfo->i_data_sem); if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { loff_t end = iocb->ki_pos + iov_iter_count(from); diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index ebde43e15dd9..28d157807b42 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -551,12 +551,12 @@ xfs_file_aio_write_checks( struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); - int error = 0; + ssize_t error = 0; size_t count = iov_iter_count(from); restart: - error = generic_write_checks(file, &iocb->ki_pos, &count); - if (error) + error = generic_write_checks(iocb, from); + if (error <= 0) return error; error = xfs_break_layouts(inode, iolock); @@ -577,13 +577,13 @@ restart: xfs_rw_iunlock(ip, *iolock); *iolock = XFS_IOLOCK_EXCL; xfs_rw_ilock(ip, *iolock); + iov_iter_reexpand(from, count); goto restart; } error = xfs_zero_eof(ip, iocb->ki_pos, i_size_read(inode), &zero); if (error) return error; } - iov_iter_truncate(from, count); /* * Updating the timestamps will grab the ilock again from diff --git a/include/linux/fs.h b/include/linux/fs.h index c7b21db7782f..b4aa400ac723 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2566,7 +2566,7 @@ extern int sb_min_blocksize(struct super_block *, int); extern int generic_file_mmap(struct file *, struct vm_area_struct *); extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *); -int generic_write_checks(struct file *file, loff_t *pos, size_t *count); +extern ssize_t generic_write_checks(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *); extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); diff --git a/mm/filemap.c b/mm/filemap.c index dfc573c6ec25..243997a26e7c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2260,36 +2260,38 @@ EXPORT_SYMBOL(read_cache_page_gfp); * Returns appropriate error code that caller should return or * zero in case that write should be allowed. */ -inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count) +inline ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from) { + struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; unsigned long limit = rlimit(RLIMIT_FSIZE); + loff_t pos; - if (unlikely(*pos < 0)) - return -EINVAL; + if (!iov_iter_count(from)) + return 0; /* FIXME: this is for backwards compatibility with 2.4 */ if (file->f_flags & O_APPEND) - *pos = i_size_read(inode); + iocb->ki_pos = i_size_read(inode); + + pos = iocb->ki_pos; if (limit != RLIM_INFINITY) { - if (*pos >= limit) { + if (iocb->ki_pos >= limit) { send_sig(SIGXFSZ, current, 0); return -EFBIG; } - if (*count > limit - (typeof(limit))*pos) - *count = limit - (typeof(limit))*pos; + iov_iter_truncate(from, limit - (unsigned long)pos); } /* * LFS rule */ - if (unlikely(*pos + *count > MAX_NON_LFS && + if (unlikely(pos + iov_iter_count(from) > MAX_NON_LFS && !(file->f_flags & O_LARGEFILE))) { - if (*pos >= MAX_NON_LFS) + if (pos >= MAX_NON_LFS) return -EFBIG; - if (*count > MAX_NON_LFS - (unsigned long)*pos) - *count = MAX_NON_LFS - (unsigned long)*pos; + iov_iter_truncate(from, MAX_NON_LFS - (unsigned long)pos); } /* @@ -2299,16 +2301,11 @@ inline int generic_write_checks(struct file *file, loff_t *pos, size_t *count) * exceeded without writing data we send a signal and return EFBIG. * Linus frestrict idea will clean these up nicely.. */ - if (unlikely(*pos >= inode->i_sb->s_maxbytes)) { - if (*count || *pos > inode->i_sb->s_maxbytes) { - return -EFBIG; - } - /* zero-length writes at ->s_maxbytes are OK */ - } + if (unlikely(pos >= inode->i_sb->s_maxbytes)) + return -EFBIG; - if (unlikely(*pos + *count > inode->i_sb->s_maxbytes)) - *count = inode->i_sb->s_maxbytes - *pos; - return 0; + iov_iter_truncate(from, inode->i_sb->s_maxbytes - pos); + return iov_iter_count(from); } EXPORT_SYMBOL(generic_write_checks); @@ -2618,14 +2615,11 @@ ssize_t generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; ssize_t ret; - size_t count = iov_iter_count(from); mutex_lock(&inode->i_mutex); - ret = generic_write_checks(file, &iocb->ki_pos, &count); - if (!ret && count) { - iov_iter_truncate(from, count); + ret = generic_write_checks(iocb, from); + if (ret > 0) ret = __generic_file_write_iter(iocb, from); - } mutex_unlock(&inode->i_mutex); if (ret > 0) { -- cgit v1.2.3 From 2ba48ce513c4e545318d22b138861d5876edf906 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 9 Apr 2015 13:52:01 -0400 Subject: mirror O_APPEND and O_DIRECT into iocb->ki_flags ... avoiding write_iter/fcntl races. Signed-off-by: Al Viro --- fs/aio.c | 2 +- fs/btrfs/file.c | 2 +- fs/ceph/file.c | 8 ++++---- fs/ext4/file.c | 4 ++-- fs/fuse/file.c | 2 +- fs/gfs2/file.c | 2 +- fs/nfs/file.c | 6 +++--- fs/ocfs2/file.c | 10 +++++----- fs/xfs/xfs_file.c | 4 ++-- include/linux/fs.h | 15 +++++++++++++++ mm/filemap.c | 6 +++--- 11 files changed, 38 insertions(+), 23 deletions(-) (limited to 'mm/filemap.c') diff --git a/fs/aio.c b/fs/aio.c index 5785c4b58fea..e976185c8e5b 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1502,7 +1502,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, } req->common.ki_pos = iocb->aio_offset; req->common.ki_complete = aio_complete; - req->common.ki_flags = 0; + req->common.ki_flags = iocb_flags(req->common.ki_filp); if (iocb->aio_flags & IOCB_FLAG_RESFD) { /* diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index c64d11c41eeb..faa7d390841b 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1794,7 +1794,7 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb, if (sync) atomic_inc(&BTRFS_I(inode)->sync_writers); - if (file->f_flags & O_DIRECT) { + if (iocb->ki_flags & IOCB_DIRECT) { num_written = __btrfs_direct_write(iocb, from, pos); } else { num_written = __btrfs_buffered_write(file, from, pos); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 3f0b9339d823..b9b8eb225f66 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -457,7 +457,7 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *i, if (ret < 0) return ret; - if (file->f_flags & O_DIRECT) { + if (iocb->ki_flags & IOCB_DIRECT) { while (iov_iter_count(i)) { size_t start; ssize_t n; @@ -828,7 +828,7 @@ again: return ret; if ((got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0 || - (iocb->ki_filp->f_flags & O_DIRECT) || + (iocb->ki_flags & IOCB_DIRECT) || (fi->flags & CEPH_F_SYNC)) { dout("aio_sync_read %p %llx.%llx %llu~%u got cap refs on %s\n", @@ -995,12 +995,12 @@ retry_snap: inode, ceph_vinop(inode), pos, count, ceph_cap_string(got)); if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 || - (file->f_flags & O_DIRECT) || (fi->flags & CEPH_F_SYNC)) { + (iocb->ki_flags & IOCB_DIRECT) || (fi->flags & CEPH_F_SYNC)) { struct iov_iter data; mutex_unlock(&inode->i_mutex); /* we might need to revert back to that point */ data = *from; - if (file->f_flags & O_DIRECT) + if (iocb->ki_flags & IOCB_DIRECT) written = ceph_sync_direct_write(iocb, &data, pos); else written = ceph_sync_write(iocb, &data, pos); diff --git a/fs/ext4/file.c b/fs/ext4/file.c index c10785f10d1d..53bbc0b1995f 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -95,7 +95,7 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct inode *inode = file_inode(iocb->ki_filp); struct mutex *aio_mutex = NULL; struct blk_plug plug; - int o_direct = io_is_direct(file); + int o_direct = iocb->ki_flags & IOCB_DIRECT; int overwrite = 0; ssize_t ret; @@ -106,7 +106,7 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (o_direct && ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS) && !is_sync_kiocb(iocb) && - (file->f_flags & O_APPEND || + (iocb->ki_flags & IOCB_APPEND || ext4_unaligned_aio(inode, from, iocb->ki_pos))) { aio_mutex = ext4_aio_mutex(inode); mutex_lock(aio_mutex); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index b86c8e08399a..5ef05b5c4cff 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1177,7 +1177,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (err) goto out; - if (file->f_flags & O_DIRECT) { + if (iocb->ki_flags & IOCB_DIRECT) { loff_t pos = iocb->ki_pos; written = generic_file_direct_write(iocb, from, pos); if (written < 0 || !iov_iter_count(from)) diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 614bb42cb7e1..08329afa1339 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -709,7 +709,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) gfs2_size_hint(file, iocb->ki_pos, iov_iter_count(from)); - if (file->f_flags & O_APPEND) { + if (iocb->ki_flags & IOCB_APPEND) { struct gfs2_holder gh; ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh); diff --git a/fs/nfs/file.c b/fs/nfs/file.c index f6a3adedf027..14364dc001f7 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -170,7 +170,7 @@ nfs_file_read(struct kiocb *iocb, struct iov_iter *to) struct inode *inode = file_inode(iocb->ki_filp); ssize_t result; - if (iocb->ki_filp->f_flags & O_DIRECT) + if (iocb->ki_flags & IOCB_DIRECT) return nfs_file_direct_read(iocb, to, iocb->ki_pos); dprintk("NFS: read(%pD2, %zu@%lu)\n", @@ -680,7 +680,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from) if (result) return result; - if (file->f_flags & O_DIRECT) + if (iocb->ki_flags & IOCB_DIRECT) return nfs_file_direct_write(iocb, from, pos); dprintk("NFS: write(%pD2, %zu@%Ld)\n", @@ -692,7 +692,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from) /* * O_APPEND implies that we must revalidate the file length. */ - if (file->f_flags & O_APPEND) { + if (iocb->ki_flags & IOCB_APPEND) { result = nfs_revalidate_file_size(inode, file); if (result) goto out; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index b93919f50f0f..cd37f6cd4d51 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2274,8 +2274,8 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, if (count == 0) return 0; - appending = file->f_flags & O_APPEND ? 1 : 0; - direct_io = file->f_flags & O_DIRECT ? 1 : 0; + appending = iocb->ki_flags & IOCB_APPEND ? 1 : 0; + direct_io = iocb->ki_flags & IOCB_DIRECT ? 1 : 0; mutex_lock(&inode->i_mutex); @@ -2429,7 +2429,7 @@ relock: out_dio: /* buffered aio wouldn't have proper lock coverage today */ - BUG_ON(ret == -EIOCBQUEUED && !(file->f_flags & O_DIRECT)); + BUG_ON(ret == -EIOCBQUEUED && !(iocb->ki_flags & IOCB_DIRECT)); if (unlikely(written <= 0)) goto no_sync; @@ -2546,7 +2546,7 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb, * buffered reads protect themselves in ->readpage(). O_DIRECT reads * need locks to protect pending reads from racing with truncate. */ - if (filp->f_flags & O_DIRECT) { + if (iocb->ki_flags & IOCB_DIRECT) { have_alloc_sem = 1; ocfs2_iocb_set_sem_locked(iocb); @@ -2580,7 +2580,7 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb, trace_generic_file_aio_read_ret(ret); /* buffered aio wouldn't have proper lock coverage today */ - BUG_ON(ret == -EIOCBQUEUED && !(filp->f_flags & O_DIRECT)); + BUG_ON(ret == -EIOCBQUEUED && !(iocb->ki_flags & IOCB_DIRECT)); /* see ocfs2_file_write_iter */ if (ret == -EIOCBQUEUED || !ocfs2_iocb_is_rw_locked(iocb)) { diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 28d157807b42..1f12ad0a8585 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -279,7 +279,7 @@ xfs_file_read_iter( XFS_STATS_INC(xs_read_calls); - if (unlikely(file->f_flags & O_DIRECT)) + if (unlikely(iocb->ki_flags & IOCB_DIRECT)) ioflags |= XFS_IO_ISDIRECT; if (file->f_mode & FMODE_NOCMTIME) ioflags |= XFS_IO_INVIS; @@ -804,7 +804,7 @@ xfs_file_write_iter( if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return -EIO; - if (unlikely(file->f_flags & O_DIRECT)) + if (unlikely(iocb->ki_flags & IOCB_DIRECT)) ret = xfs_file_dio_aio_write(iocb, from); else ret = xfs_file_buffered_aio_write(iocb, from); diff --git a/include/linux/fs.h b/include/linux/fs.h index b4aa400ac723..b1d7db28c13c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -315,6 +315,8 @@ struct address_space; struct writeback_control; #define IOCB_EVENTFD (1 << 0) +#define IOCB_APPEND (1 << 1) +#define IOCB_DIRECT (1 << 2) struct kiocb { struct file *ki_filp; @@ -329,10 +331,13 @@ static inline bool is_sync_kiocb(struct kiocb *kiocb) return kiocb->ki_complete == NULL; } +static inline int iocb_flags(struct file *file); + static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) { *kiocb = (struct kiocb) { .ki_filp = filp, + .ki_flags = iocb_flags(filp), }; } @@ -2779,6 +2784,16 @@ static inline bool io_is_direct(struct file *filp) return (filp->f_flags & O_DIRECT) || IS_DAX(file_inode(filp)); } +static inline int iocb_flags(struct file *file) +{ + int res = 0; + if (file->f_flags & O_APPEND) + res |= IOCB_APPEND; + if (io_is_direct(file)) + res |= IOCB_DIRECT; + return res; +} + static inline ino_t parent_ino(struct dentry *dentry) { ino_t res; diff --git a/mm/filemap.c b/mm/filemap.c index 243997a26e7c..405de370e657 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1694,7 +1694,7 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) loff_t *ppos = &iocb->ki_pos; loff_t pos = *ppos; - if (io_is_direct(file)) { + if (iocb->ki_flags & IOCB_DIRECT) { struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; size_t count = iov_iter_count(iter); @@ -2271,7 +2271,7 @@ inline ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from) return 0; /* FIXME: this is for backwards compatibility with 2.4 */ - if (file->f_flags & O_APPEND) + if (iocb->ki_flags & IOCB_APPEND) iocb->ki_pos = i_size_read(inode); pos = iocb->ki_pos; @@ -2545,7 +2545,7 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (err) goto out; - if (io_is_direct(file)) { + if (iocb->ki_flags & IOCB_DIRECT) { loff_t pos, endbyte; written = generic_file_direct_write(iocb, from, iocb->ki_pos); -- cgit v1.2.3