

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.



 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);

_
