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. I made the new function `static inline', because nothing is actually using it yet. Later, we can remove the `static', so it is still inlined for the current filesystems. filemap.c | 172 ++++++++++++++++++++++++++++++++------------------------------ 1 files changed, 90 insertions(+), 82 deletions(-) diff -puN mm/filemap.c~generic_write_checks mm/filemap.c --- 25/mm/filemap.c~generic_write_checks 2003-02-13 22:08:50.000000000 -0800 +++ 25-akpm/mm/filemap.c 2003-02-13 23:01:40.000000000 -0800 @@ -1486,6 +1486,90 @@ 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. + */ +static 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; +} /* * Write to a file through the page cache. @@ -1506,12 +1590,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 +1623,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; _