X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fread_write.c;h=c4c2bee373eddeab7b0d3bb9e110453a63ccd436;hb=65da6b7c3bf0bd6a149128079565e5f4efec28ac;hp=f765a22b49cb50205acabb2fd9abffb2161d9974;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/read_write.c b/fs/read_write.c index f765a22b4..c4c2bee37 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -76,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) @@ -109,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) { @@ -135,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 */ @@ -144,7 +146,6 @@ asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin) bad: return retval; } -EXPORT_SYMBOL_GPL(sys_lseek); #ifdef __ARCH_WANT_SYS_LLSEEK asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high, @@ -165,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; @@ -181,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; @@ -199,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) { @@ -215,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++; } } @@ -243,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) { @@ -259,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++; } } @@ -269,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; @@ -277,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); } @@ -293,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); } @@ -312,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); } @@ -331,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); } @@ -360,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) @@ -374,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 @@ -414,10 +464,13 @@ 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 (len < 0) /* size_t not fitting an ssize_t .. */ goto out; + if (unlikely(!access_ok(vrfy_dir(type), buf, len))) + goto Efault; tot_len += len; if ((ssize_t)tot_len < 0) /* maths overflow on the ssize_t */ goto out; @@ -427,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; @@ -478,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, @@ -515,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; } @@ -531,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; } @@ -562,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; @@ -585,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; @@ -609,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;