Commit 0ceb3314 authored by Dmitriy Monakhov's avatar Dmitriy Monakhov Committed by Linus Torvalds

mm: move common segment checks to separate helper function

[akpm@linux-foundation.org: cleanup]
Signed-off-by: default avatarMonakhov Dmitriy <dmonakhov@openvz.org>
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: default avatarAnton Altaparmakov <aia21@cam.ac.uk>
Acked-by: default avatarDavid Chinner <dgc@sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b46b8f19
...@@ -2129,28 +2129,13 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb, ...@@ -2129,28 +2129,13 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
loff_t pos; loff_t pos;
unsigned long seg;
size_t count; /* after file limit checks */ size_t count; /* after file limit checks */
ssize_t written, err; ssize_t written, err;
count = 0; count = 0;
for (seg = 0; seg < nr_segs; seg++) { err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
const struct iovec *iv = &iov[seg]; if (err)
/* return err;
* If any segment has a negative length, or the cumulative
* length ever wraps negative then return -EINVAL.
*/
count += iv->iov_len;
if (unlikely((ssize_t)(count|iv->iov_len) < 0))
return -EINVAL;
if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
continue;
if (!seg)
return -EFAULT;
nr_segs = seg;
count -= iv->iov_len; /* This segment is no good */
break;
}
pos = *ppos; pos = *ppos;
vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
/* We can write back this queue in page reclaim. */ /* We can write back this queue in page reclaim. */
......
...@@ -639,7 +639,6 @@ xfs_write( ...@@ -639,7 +639,6 @@ xfs_write(
xfs_fsize_t isize, new_size; xfs_fsize_t isize, new_size;
xfs_iocore_t *io; xfs_iocore_t *io;
bhv_vnode_t *vp; bhv_vnode_t *vp;
unsigned long seg;
int iolock; int iolock;
int eventsent = 0; int eventsent = 0;
bhv_vrwlock_t locktype; bhv_vrwlock_t locktype;
...@@ -652,24 +651,9 @@ xfs_write( ...@@ -652,24 +651,9 @@ xfs_write(
vp = BHV_TO_VNODE(bdp); vp = BHV_TO_VNODE(bdp);
xip = XFS_BHVTOI(bdp); xip = XFS_BHVTOI(bdp);
for (seg = 0; seg < segs; seg++) { error = generic_segment_checks(iovp, &segs, &ocount, VERIFY_READ);
const struct iovec *iv = &iovp[seg]; if (error)
return error;
/*
* If any segment has a negative length, or the cumulative
* length ever wraps negative then return -EINVAL.
*/
ocount += iv->iov_len;
if (unlikely((ssize_t)(ocount|iv->iov_len) < 0))
return -EINVAL;
if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
continue;
if (seg == 0)
return -EFAULT;
segs = seg;
ocount -= iv->iov_len; /* This segment is no good */
break;
}
count = ocount; count = ocount;
pos = *offset; pos = *offset;
......
...@@ -1735,6 +1735,8 @@ extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor ...@@ -1735,6 +1735,8 @@ extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor
extern void do_generic_mapping_read(struct address_space *mapping, extern void do_generic_mapping_read(struct address_space *mapping,
struct file_ra_state *, struct file *, struct file_ra_state *, struct file *,
loff_t *, read_descriptor_t *, read_actor_t); loff_t *, read_descriptor_t *, read_actor_t);
extern int generic_segment_checks(const struct iovec *iov,
unsigned long *nr_segs, size_t *count, int access_flags);
/* fs/splice.c */ /* fs/splice.c */
extern ssize_t generic_file_splice_read(struct file *, loff_t *, extern ssize_t generic_file_splice_read(struct file *, loff_t *,
......
...@@ -1110,6 +1110,45 @@ success: ...@@ -1110,6 +1110,45 @@ success:
return size; return size;
} }
/*
* Performs necessary checks before doing a write
* @iov: io vector request
* @nr_segs: number of segments in the iovec
* @count: number of bytes to write
* @access_flags: type of access: %VERIFY_READ or %VERIFY_WRITE
*
* Adjust number of segments and amount of bytes to write (nr_segs should be
* properly initialized first). Returns appropriate error code that caller
* should return or zero in case that write should be allowed.
*/
int generic_segment_checks(const struct iovec *iov,
unsigned long *nr_segs, size_t *count, int access_flags)
{
unsigned long seg;
size_t cnt = 0;
for (seg = 0; seg < *nr_segs; seg++) {
const struct iovec *iv = &iov[seg];
/*
* If any segment has a negative length, or the cumulative
* length ever wraps negative then return -EINVAL.
*/
cnt += iv->iov_len;
if (unlikely((ssize_t)(cnt|iv->iov_len) < 0))
return -EINVAL;
if (access_ok(access_flags, iv->iov_base, iv->iov_len))
continue;
if (seg == 0)
return -EFAULT;
*nr_segs = seg;
cnt -= iv->iov_len; /* This segment is no good */
break;
}
*count = cnt;
return 0;
}
EXPORT_SYMBOL(generic_segment_checks);
/** /**
* generic_file_aio_read - generic filesystem read routine * generic_file_aio_read - generic filesystem read routine
* @iocb: kernel I/O control block * @iocb: kernel I/O control block
...@@ -1131,24 +1170,9 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1131,24 +1170,9 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
loff_t *ppos = &iocb->ki_pos; loff_t *ppos = &iocb->ki_pos;
count = 0; count = 0;
for (seg = 0; seg < nr_segs; seg++) { retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
const struct iovec *iv = &iov[seg]; if (retval)
return retval;
/*
* If any segment has a negative length, or the cumulative
* length ever wraps negative then return -EINVAL.
*/
count += iv->iov_len;
if (unlikely((ssize_t)(count|iv->iov_len) < 0))
return -EINVAL;
if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len))
continue;
if (seg == 0)
return -EFAULT;
nr_segs = seg;
count -= iv->iov_len; /* This segment is no good */
break;
}
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
if (filp->f_flags & O_DIRECT) { if (filp->f_flags & O_DIRECT) {
...@@ -2218,30 +2242,14 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, ...@@ -2218,30 +2242,14 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov,
size_t ocount; /* original count */ size_t ocount; /* original count */
size_t count; /* after file limit checks */ size_t count; /* after file limit checks */
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
unsigned long seg;
loff_t pos; loff_t pos;
ssize_t written; ssize_t written;
ssize_t err; ssize_t err;
ocount = 0; ocount = 0;
for (seg = 0; seg < nr_segs; seg++) { err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
const struct iovec *iv = &iov[seg]; if (err)
return err;
/*
* If any segment has a negative length, or the cumulative
* length ever wraps negative then return -EINVAL.
*/
ocount += iv->iov_len;
if (unlikely((ssize_t)(ocount|iv->iov_len) < 0))
return -EINVAL;
if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
continue;
if (seg == 0)
return -EFAULT;
nr_segs = seg;
ocount -= iv->iov_len; /* This segment is no good */
break;
}
count = ocount; count = ocount;
pos = *ppos; pos = *ppos;
......
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