Patch from: Oleg Drokin 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. linux/fs.h | 2 linux/pagemap.h | 0 filemap.c | 173 +++++++++++++++++++++++++++++--------------------------- 3 files changed, 93 insertions(+), 82 deletions(-) diff -puN mm/filemap.c~generic_write_checks mm/filemap.c --- 25/mm/filemap.c~generic_write_checks 2003-02-14 18:23:57.000000000 -0800 +++ 25-akpm/mm/filemap.c 2003-02-14 18:23:57.000000000 -0800 @@ -1486,6 +1486,91 @@ filemap_set_next_iovec(const struct iove *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. @@ -1506,12 +1591,11 @@ generic_file_aio_write_nolock(struct kio size_t ocount; /* original count */ size_t count; /* after file limit checks */ 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; loff_t pos; struct page *page; struct page *cached_page = NULL; + const int isblk = S_ISBLK(inode->i_mode); ssize_t written; ssize_t err; size_t bytes; @@ -1540,95 +1624,20 @@ generic_file_aio_write_nolock(struct kio ocount -= iv->iov_len; /* This segment is no good */ break; } - count = ocount; + count = ocount; pos = *ppos; - if (unlikely(pos < 0)) - return -EINVAL; + pagevec_init(&lru_pvec, 0); /* We can write back this queue in page reclaim */ 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; - 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); - 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; - } - } + err = generic_write_checks(inode, file, &pos, &count, isblk); + if (err) + goto out; - if (pos + count > inode->i_size) - count = inode->i_size - pos; - } - err = 0; if (count == 0) goto out; diff -puN include/linux/pagemap.h~generic_write_checks include/linux/pagemap.h diff -puN include/linux/fs.h~generic_write_checks include/linux/fs.h --- 25/include/linux/fs.h~generic_write_checks 2003-02-14 18:23:57.000000000 -0800 +++ 25-akpm/include/linux/fs.h 2003-02-14 18:23:57.000000000 -0800 @@ -1195,6 +1195,8 @@ extern int generic_file_readonly_mmap(st 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 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_aio_read(struct kiocb *, char *, size_t, loff_t); extern ssize_t generic_file_aio_write(struct kiocb *, const char *, size_t, loff_t); _