shmem_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
- unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
loff_t pos;
unsigned long written;
- long status;
int err;
loff_t maxpos;
down(&inode->i_sem);
pos = *ppos;
- err = -EINVAL;
- if (pos < 0)
- goto out_nc;
-
- err = file->f_error;
- if (err) {
- file->f_error = 0;
- goto out_nc;
- }
-
written = 0;
- if (file->f_flags & O_APPEND)
- pos = inode->i_size;
+ err = generic_write_checks(inode, file, &pos, &count, 0);
+ if (err || !count)
+ goto out;
maxpos = inode->i_size;
- if (pos + count > inode->i_size) {
+ if (maxpos < pos + count) {
maxpos = pos + count;
- if (maxpos > SHMEM_MAX_BYTES)
- maxpos = SHMEM_MAX_BYTES;
if (!vm_enough_memory(VM_ACCT(maxpos) - VM_ACCT(inode->i_size))) {
err = -ENOMEM;
- goto out_nc;
- }
- }
-
- /*
- * Check whether we've reached the file size limit.
- */
- err = -EFBIG;
- if (limit != RLIM_INFINITY) {
- if (pos >= limit) {
- send_sig(SIGXFSZ, current, 0);
goto out;
}
- if (pos > 0xFFFFFFFFULL || count > limit - (u32)pos) {
- /* send_sig(SIGXFSZ, current, 0); */
- count = limit - (u32)pos;
- }
}
- /*
- * LFS rule
- */
- if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) {
- if (pos >= MAX_NON_LFS) {
- send_sig(SIGXFSZ, current, 0);
- 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 give them an EFBIG.
- *
- * Linus frestrict idea will clean these up nicely..
- */
- if (pos >= SHMEM_MAX_BYTES) {
- if (count || pos > SHMEM_MAX_BYTES) {
- send_sig(SIGXFSZ, current, 0);
- err = -EFBIG;
- goto out;
- }
- /* zero-length writes at ->s_maxbytes are OK */
- }
- if (pos + count > SHMEM_MAX_BYTES)
- count = SHMEM_MAX_BYTES - pos;
-
- status = 0;
- if (count) {
- remove_suid(file->f_dentry);
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
- }
+ remove_suid(file->f_dentry);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
- while (count) {
+ do {
struct page *page = NULL;
unsigned long bytes, index, offset;
char *kaddr;
* But it still may be a good idea to prefault below.
*/
- status = shmem_getpage(inode, index, &page, SGP_WRITE);
- if (status)
+ err = shmem_getpage(inode, index, &page, SGP_WRITE);
+ if (err)
break;
left = bytes;
flush_dcache_page(page);
if (left) {
page_cache_release(page);
- status = -EFAULT;
+ err = -EFAULT;
break;
}
page_cache_release(page);
/*
- * Balance dirty pages??
+ * Our dirty pages are not counted in nr_dirty,
+ * and we do not attempt to balance dirty pages.
*/
written += bytes;
buf += bytes;
if (pos > inode->i_size)
inode->i_size = pos;
- }
+ } while (count);
*ppos = pos;
- err = written ? written : status;
-out:
+ if (written)
+ err = written;
+
/* Short writes give back address space */
if (inode->i_size != maxpos)
vm_unacct_memory(VM_ACCT(maxpos) - VM_ACCT(inode->i_size));
-out_nc:
+out:
up(&inode->i_sem);
return err;
}