Commit 7ac1de5d authored by Andrew Morton's avatar Andrew Morton Committed by Jens Axboe

[PATCH] separate checks from generic_file_aio_write

Patch from: Oleg Drokin <green@namesys.com>

It moves all the arg checking code from the start of generic_file_aio_write()
into a standalone function, so other filesystems can avoid having to
cut-n-paste them.

The new function is exported to modules, and also inlined in filemap.c so
that the current filesystems are unaffected.  If someone is using ext2 and
reiserfs at the same time, they lose a bit of icache.
parent 3172a7c4
...@@ -1195,6 +1195,8 @@ extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *); ...@@ -1195,6 +1195,8 @@ extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size); extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
extern int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size); extern int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *); extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
int generic_write_checks(struct inode *inode, struct file *file,
loff_t *pos, size_t *count, int isblk);
extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *); extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *);
extern ssize_t generic_file_aio_read(struct kiocb *, char *, size_t, loff_t); extern ssize_t generic_file_aio_read(struct kiocb *, char *, size_t, loff_t);
extern ssize_t generic_file_aio_write(struct kiocb *, const char *, size_t, loff_t); extern ssize_t generic_file_aio_write(struct kiocb *, const char *, size_t, loff_t);
......
...@@ -1486,6 +1486,91 @@ filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes) ...@@ -1486,6 +1486,91 @@ filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes)
*basep = base; *basep = base;
} }
/*
* Performs necessary checks before doing a write
*
* Can adjust writing position aor amount of bytes to write.
* Returns appropriate error code that caller should return or
* zero in case that write should be allowed.
*/
inline int generic_write_checks(struct inode *inode,
struct file *file, loff_t *pos, size_t *count, int isblk)
{
unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
if (unlikely(*pos < 0))
return -EINVAL;
if (unlikely(file->f_error)) {
int err = file->f_error;
file->f_error = 0;
return err;
}
if (!isblk) {
/* FIXME: this is for backwards compatibility with 2.4 */
if (file->f_flags & O_APPEND)
*pos = inode->i_size;
if (limit != RLIM_INFINITY) {
if (*pos >= limit) {
send_sig(SIGXFSZ, current, 0);
return -EFBIG;
}
if (*pos > 0xFFFFFFFFULL || *count > limit-(u32)*pos) {
/* send_sig(SIGXFSZ, current, 0); */
*count = limit - (u32)*pos;
}
}
}
/*
* LFS rule
*/
if (unlikely(*pos + *count > MAX_NON_LFS &&
!(file->f_flags & O_LARGEFILE))) {
if (*pos >= MAX_NON_LFS) {
send_sig(SIGXFSZ, current, 0);
return -EFBIG;
}
if (*count > MAX_NON_LFS - (u32)*pos) {
/* send_sig(SIGXFSZ, current, 0); */
*count = MAX_NON_LFS - (u32)*pos;
}
}
/*
* Are we about to exceed the fs block limit ?
*
* If we have written data it becomes a short write. If we have
* 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) {
send_sig(SIGXFSZ, current, 0);
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 {
if (bdev_read_only(inode->i_bdev))
return -EPERM;
if (*pos >= inode->i_size) {
if (*count || *pos > inode->i_size)
return -ENOSPC;
}
if (*pos + *count > inode->i_size)
*count = inode->i_size - *pos;
}
return 0;
}
EXPORT_SYMBOL(generic_write_checks);
/* /*
* Write to a file through the page cache. * Write to a file through the page cache.
...@@ -1506,12 +1591,11 @@ generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, ...@@ -1506,12 +1591,11 @@ 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 limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
const int isblk = S_ISBLK(inode->i_mode);
long status = 0; long status = 0;
loff_t pos; loff_t pos;
struct page *page; struct page *page;
struct page *cached_page = NULL; struct page *cached_page = NULL;
const int isblk = S_ISBLK(inode->i_mode);
ssize_t written; ssize_t written;
ssize_t err; ssize_t err;
size_t bytes; size_t bytes;
...@@ -1540,95 +1624,20 @@ generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, ...@@ -1540,95 +1624,20 @@ generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov,
ocount -= iv->iov_len; /* This segment is no good */ ocount -= iv->iov_len; /* This segment is no good */
break; break;
} }
count = ocount;
count = ocount;
pos = *ppos; pos = *ppos;
if (unlikely(pos < 0)) pagevec_init(&lru_pvec, 0);
return -EINVAL;
/* We can write back this queue in page reclaim */ /* We can write back this queue in page reclaim */
current->backing_dev_info = mapping->backing_dev_info; current->backing_dev_info = mapping->backing_dev_info;
pagevec_init(&lru_pvec, 0);
if (unlikely(file->f_error)) {
err = file->f_error;
file->f_error = 0;
goto out;
}
written = 0; written = 0;
if (!isblk) { err = generic_write_checks(inode, file, &pos, &count, isblk);
/* FIXME: this is for backwards compatibility with 2.4 */ if (err)
if (file->f_flags & O_APPEND)
pos = inode->i_size;
if (limit != RLIM_INFINITY) {
if (pos >= limit) {
send_sig(SIGXFSZ, current, 0);
err = -EFBIG;
goto out;
}
if (pos > 0xFFFFFFFFULL || count > limit - (u32)pos) {
/* send_sig(SIGXFSZ, current, 0); */
count = limit - (u32)pos;
}
}
}
/*
* LFS rule
*/
if (unlikely(pos + count > MAX_NON_LFS &&
!(file->f_flags & O_LARGEFILE))) {
if (pos >= MAX_NON_LFS) {
send_sig(SIGXFSZ, current, 0);
err = -EFBIG;
goto out;
}
if (count > MAX_NON_LFS - (u32)pos) {
/* send_sig(SIGXFSZ, current, 0); */
count = MAX_NON_LFS - (u32)pos;
}
}
/*
* Are we about to exceed the fs block limit ?
*
* If we have written data it becomes a short write. If we have
* 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) {
send_sig(SIGXFSZ, current, 0);
err = -EFBIG;
goto out;
}
/* 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 {
if (bdev_read_only(inode->i_bdev)) {
err = -EPERM;
goto out;
}
if (pos >= inode->i_size) {
if (count || pos > inode->i_size) {
err = -ENOSPC;
goto out; goto out;
}
}
if (pos + count > inode->i_size)
count = inode->i_size - pos;
}
err = 0;
if (count == 0) if (count == 0)
goto out; goto out;
......
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