X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fread_write.c;h=6be4b1a1956b21004c2b03382f6f4b08ecc957d3;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=a30ab9df41e2391abce52c3ebf08271eea308245;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/read_write.c b/fs/read_write.c index a30ab9df4..6be4b1a19 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -13,8 +13,10 @@ #include #include #include +#include #include +#include struct file_operations generic_ro_fops = { .llseek = generic_file_llseek, @@ -75,14 +77,12 @@ loff_t remote_llseek(struct file *file, loff_t offset, int origin) unlock_kernel(); return retval; } - EXPORT_SYMBOL(remote_llseek); loff_t no_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } - EXPORT_SYMBOL(no_llseek); loff_t default_llseek(struct file *file, loff_t offset, int origin) @@ -108,18 +108,21 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin) unlock_kernel(); return retval; } - EXPORT_SYMBOL(default_llseek); -static inline loff_t llseek(struct file *file, loff_t offset, int origin) +loff_t vfs_llseek(struct file *file, loff_t offset, int origin) { loff_t (*fn)(struct file *, loff_t, int); - fn = default_llseek; - if (file->f_op && file->f_op->llseek) - fn = file->f_op->llseek; + fn = no_llseek; + if (file->f_mode & FMODE_LSEEK) { + fn = default_llseek; + if (file->f_op && file->f_op->llseek) + fn = file->f_op->llseek; + } return fn(file, offset, origin); } +EXPORT_SYMBOL(vfs_llseek); asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin) { @@ -134,7 +137,7 @@ asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin) retval = -EINVAL; if (origin <= 2) { - loff_t res = llseek(file, offset, origin); + loff_t res = vfs_llseek(file, offset, origin); retval = res; if (res != (loff_t)retval) retval = -EOVERFLOW; /* LFS: should only happen on 32 bit platforms */ @@ -143,9 +146,8 @@ asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin) bad: return retval; } -EXPORT_SYMBOL_GPL(sys_lseek); -#if !defined(__alpha__) +#ifdef __ARCH_WANT_SYS_LLSEEK asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, loff_t __user * result, unsigned int origin) @@ -164,7 +166,7 @@ asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high, if (origin > 2) goto out_putf; - offset = llseek(file, ((loff_t) offset_high << 32) | offset_low, + offset = vfs_llseek(file, ((loff_t) offset_high << 32) | offset_low, origin); retval = (int)offset; @@ -180,6 +182,27 @@ bad: } #endif + +int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count) +{ + struct inode *inode; + loff_t pos; + + if (unlikely(count > file->f_maxcount)) + goto Einval; + pos = *ppos; + if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) + goto Einval; + + inode = file->f_dentry->d_inode; + if (inode->i_flock && MANDATORY_LOCK(inode)) + return locks_mandatory_area(read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, pos, count); + return 0; + +Einval: + return -EINVAL; +} + ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { struct kiocb kiocb; @@ -198,15 +221,16 @@ EXPORT_SYMBOL(do_sync_read); ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { - struct inode *inode = file->f_dentry->d_inode; ssize_t ret; if (!(file->f_mode & FMODE_READ)) return -EBADF; if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read)) return -EINVAL; + if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) + return -EFAULT; - ret = locks_verify_area(FLOCK_VERIFY_READ, inode, file, *pos, count); + ret = rw_verify_area(READ, file, pos, count); if (!ret) { ret = security_file_permission (file, MAY_READ); if (!ret) { @@ -214,8 +238,11 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) ret = file->f_op->read(file, buf, count, pos); else ret = do_sync_read(file, buf, count, pos); - if (ret > 0) + if (ret > 0) { dnotify_parent(file->f_dentry, DN_ACCESS); + current->rchar += ret; + } + current->syscr++; } } @@ -242,15 +269,16 @@ EXPORT_SYMBOL(do_sync_write); ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { - struct inode *inode = file->f_dentry->d_inode; ssize_t ret; if (!(file->f_mode & FMODE_WRITE)) return -EBADF; if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write)) return -EINVAL; + if (unlikely(!access_ok(VERIFY_READ, buf, count))) + return -EFAULT; - ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, *pos, count); + ret = rw_verify_area(WRITE, file, pos, count); if (!ret) { ret = security_file_permission (file, MAY_WRITE); if (!ret) { @@ -258,8 +286,11 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ ret = file->f_op->write(file, buf, count, pos); else ret = do_sync_write(file, buf, count, pos); - if (ret > 0) + if (ret > 0) { dnotify_parent(file->f_dentry, DN_MODIFY); + current->wchar += ret; + } + current->syscw++; } } @@ -268,6 +299,16 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ EXPORT_SYMBOL(vfs_write); +static inline loff_t file_pos_read(struct file *file) +{ + return file->f_pos; +} + +static inline void file_pos_write(struct file *file, loff_t pos) +{ + file->f_pos = pos; +} + asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count) { struct file *file; @@ -276,7 +317,9 @@ asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count) file = fget_light(fd, &fput_needed); if (file) { - ret = vfs_read(file, buf, count, &file->f_pos); + loff_t pos = file_pos_read(file); + ret = vfs_read(file, buf, count, &pos); + file_pos_write(file, pos); fput_light(file, fput_needed); } @@ -292,7 +335,9 @@ asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t co file = fget_light(fd, &fput_needed); if (file) { - ret = vfs_write(file, buf, count, &file->f_pos); + loff_t pos = file_pos_read(file); + ret = vfs_write(file, buf, count, &pos); + file_pos_write(file, pos); fput_light(file, fput_needed); } @@ -311,7 +356,9 @@ asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf, file = fget_light(fd, &fput_needed); if (file) { - ret = vfs_read(file, buf, count, &pos); + ret = -ESPIPE; + if (file->f_mode & FMODE_PREAD) + ret = vfs_read(file, buf, count, &pos); fput_light(file, fput_needed); } @@ -330,7 +377,9 @@ asmlinkage ssize_t sys_pwrite64(unsigned int fd, const char __user *buf, file = fget_light(fd, &fput_needed); if (file) { - ret = vfs_write(file, buf, count, &pos); + ret = -ESPIPE; + if (file->f_mode & FMODE_PWRITE) + ret = vfs_write(file, buf, count, &pos); fput_light(file, fput_needed); } @@ -359,6 +408,9 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to) EXPORT_SYMBOL(iov_shorten); +/* A write operation does a read from user space and vice versa */ +#define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) + static ssize_t do_readv_writev(int type, struct file *file, const struct iovec __user * uvector, unsigned long nr_segs, loff_t *pos) @@ -373,7 +425,6 @@ static ssize_t do_readv_writev(int type, struct file *file, int seg; io_fn_t fn; iov_fn_t fnv; - struct inode *inode; /* * SuS says "The readv() function *may* fail if the iovcnt argument @@ -413,8 +464,11 @@ static ssize_t do_readv_writev(int type, struct file *file, tot_len = 0; ret = -EINVAL; for (seg = 0; seg < nr_segs; seg++) { + void __user *buf = iov[seg].iov_base; ssize_t len = (ssize_t)iov[seg].iov_len; + if (unlikely(!access_ok(vrfy_dir(type), buf, len))) + goto Efault; if (len < 0) /* size_t not fitting an ssize_t .. */ goto out; tot_len += len; @@ -426,11 +480,7 @@ static ssize_t do_readv_writev(int type, struct file *file, goto out; } - inode = file->f_dentry->d_inode; - /* VERIFY_WRITE actually means a read, as we write to user space */ - ret = locks_verify_area((type == READ - ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), - inode, file, *pos, tot_len); + ret = rw_verify_area(type, file, pos, tot_len); if (ret) goto out; @@ -477,6 +527,9 @@ out: dnotify_parent(file->f_dentry, (type == READ) ? DN_ACCESS : DN_MODIFY); return ret; +Efault: + ret = -EFAULT; + goto out; } ssize_t vfs_readv(struct file *file, const struct iovec __user *vec, @@ -514,10 +567,15 @@ sys_readv(unsigned long fd, const struct iovec __user *vec, unsigned long vlen) file = fget_light(fd, &fput_needed); if (file) { - ret = vfs_readv(file, vec, vlen, &file->f_pos); + loff_t pos = file_pos_read(file); + ret = vfs_readv(file, vec, vlen, &pos); + file_pos_write(file, pos); fput_light(file, fput_needed); } + if (ret > 0) + current->rchar += ret; + current->syscr++; return ret; } @@ -530,10 +588,15 @@ sys_writev(unsigned long fd, const struct iovec __user *vec, unsigned long vlen) file = fget_light(fd, &fput_needed); if (file) { - ret = vfs_writev(file, vec, vlen, &file->f_pos); + loff_t pos = file_pos_read(file); + ret = vfs_writev(file, vec, vlen, &pos); + file_pos_write(file, pos); fput_light(file, fput_needed); } + if (ret > 0) + current->wchar += ret; + current->syscw++; return ret; } @@ -561,9 +624,13 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, goto fput_in; if (!in_file->f_op || !in_file->f_op->sendfile) goto fput_in; + retval = -ESPIPE; if (!ppos) ppos = &in_file->f_pos; - retval = locks_verify_area(FLOCK_VERIFY_READ, in_inode, in_file, *ppos, count); + else + if (!(in_file->f_mode & FMODE_PREAD)) + goto fput_in; + retval = rw_verify_area(READ, in_file, ppos, count); if (retval) goto fput_in; @@ -584,7 +651,7 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, if (!out_file->f_op || !out_file->f_op->sendpage) goto fput_out; out_inode = out_file->f_dentry->d_inode; - retval = locks_verify_area(FLOCK_VERIFY_WRITE, out_inode, out_file, out_file->f_pos, count); + retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count); if (retval) goto fput_out; @@ -608,6 +675,13 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, retval = in_file->f_op->sendfile(in_file, ppos, count, file_send_actor, out_file); + if (retval > 0) { + current->rchar += retval; + current->wchar += retval; + } + current->syscr++; + current->syscw++; + if (*ppos > max) retval = -EOVERFLOW;