/* * linux/fs/read_write.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include <linux/slab.h> #include <linux/stat.h>#include <linux/fcntl.h>#include <linux/file.h>#include <linux/uio.h>#include <linux/smp_lock.h>#include <linux/fsnotify.h>#include <linux/security.h>#include <linux/module.h>#include <linux/syscalls.h>#include <linux/pagemap.h>#include "read_write.h" #include <asm/uaccess.h>#include <asm/unistd.h> const struct file_operations generic_ro_fops = { .llseek = generic_file_llseek, .read = do_sync_read, .aio_read = generic_file_aio_read, .mmap = generic_file_readonly_mmap, .sendfile = generic_file_sendfile,}; EXPORT_SYMBOL(generic_ro_fops); loff_t generic_file_llseek(struct file *file, loff_t offset, int origin){ long long retval; struct inode *inode = file->f_mapping->host; mutex_lock(&inode->i_mutex); switch (origin) { case 2: offset += inode->i_size; break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset>=0 && offset<=inode->i_sb->s_maxbytes) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } mutex_unlock(&inode->i_mutex); return retval;} EXPORT_SYMBOL(generic_file_llseek); loff_t remote_llseek(struct file *file, loff_t offset, int origin){ long long retval; lock_kernel(); switch (origin) { case 2: offset += i_size_read(file->f_path.dentry->d_inode); break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset>=0 && offset<=file->f_path.dentry->d_inode->i_sb->s_maxbytes) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } 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){ long long retval; lock_kernel(); switch (origin) { case 2: offset += i_size_read(file->f_path.dentry->d_inode); break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset >= 0) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } unlock_kernel(); return retval;}EXPORT_SYMBOL(default_llseek); loff_t vfs_llseek(struct file *file, loff_t offset, int origin){ loff_t (*fn)(struct file *, loff_t, int); 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){ off_t retval; struct file * file; int fput_needed; retval = -EBADF; file = fget_light(fd, &fput_needed); if (!file) goto bad; retval = -EINVAL; if (origin <= 2) { 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 */ } fput_light(file, fput_needed);bad: return retval;} #ifdef __ARCH_WANT_SYS_LLSEEKasmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, loff_t __user * result, unsigned int origin){ int retval; struct file * file; loff_t offset; int fput_needed; retval = -EBADF; file = fget_light(fd, &fput_needed); if (!file) goto bad; retval = -EINVAL; if (origin > 2) goto out_putf; offset = vfs_llseek(file, ((loff_t) offset_high << 32) | offset_low, origin); retval = (int)offset; if (offset >= 0) { retval = -EFAULT; if (!copy_to_user(result, &offset, sizeof(offset))) retval = 0; }out_putf: fput_light(file, fput_needed);bad: return retval;}#endif /* * rw_verify_area doesn't like huge counts. We limit * them to something that fits in "int" so that others * won't have to do range checks all the time. */#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK) int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count){ struct inode *inode; loff_t pos; inode = file->f_path.dentry->d_inode; if (unlikely((ssize_t) count < 0)) goto Einval; pos = *ppos; if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) goto Einval; if (unlikely(inode->i_flock && MANDATORY_LOCK(inode))) { int retval = locks_mandatory_area( read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, pos, count); if (retval < 0) return retval; } return count > MAX_RW_COUNT ? MAX_RW_COUNT : count; Einval: return -EINVAL;} static void wait_on_retry_sync_kiocb(struct kiocb *iocb){ set_current_state(TASK_UNINTERRUPTIBLE); if (!kiocbIsKicked(iocb)) schedule(); else kiocbClearKicked(iocb); __set_current_state(TASK_RUNNING);} ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos){ struct iovec iov = { .iov_base = buf, .iov_len = len }; struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_left = len; for (;;) { ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); if (ret != -EIOCBRETRY) break; wait_on_retry_sync_kiocb(&kiocb); } if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret;} EXPORT_SYMBOL(do_sync_read); ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos){ 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 = rw_verify_area(READ, file, pos, count); if (ret >= 0) { count = ret; ret = security_file_permission (file, MAY_READ); if (!ret) { if (file->f_op->read) ret = file->f_op->read(file, buf, count, pos); else ret = do_sync_read(file, buf, count, pos); if (ret > 0) { fsnotify_access(file->f_path.dentry); add_rchar(current, ret); } inc_syscr(current); } } return ret;} EXPORT_SYMBOL(vfs_read); ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos){ struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_left = len; for (;;) { ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); if (ret != -EIOCBRETRY) break; wait_on_retry_sync_kiocb(&kiocb); } if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret;} EXPORT_SYMBOL(do_sync_write); ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos){ 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 = rw_verify_area(WRITE, file, pos, count); if (ret >= 0) { count = ret; ret = security_file_permission (file, MAY_WRITE); if (!ret) { if (file->f_op->write) ret = file->f_op->write(file, buf, count, pos); else ret = do_sync_write(file, buf, count, pos); if (ret > 0) { fsnotify_modify(file->f_path.dentry); add_wchar(current, ret); } inc_syscw(current); } } return ret;} 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; ssize_t ret = -EBADF; int fput_needed; file = fget_light(fd, &fput_needed); if (file) { loff_t pos = file_pos_read(file); ret = vfs_read(file, buf, count, &pos); file_pos_write(file, pos); fput_light(file, fput_needed); } return ret;}EXPORT_SYMBOL_GPL(sys_read); asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count){ struct file *file; ssize_t ret = -EBADF; int fput_needed; file = fget_light(fd, &fput_needed); if (file) { loff_t pos = file_pos_read(file); ret = vfs_write(file, buf, count, &pos); file_pos_write(file, pos); fput_light(file, fput_needed); } return ret;} asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf, size_t count, loff_t pos){ struct file *file; ssize_t ret = -EBADF; int fput_needed; if (pos < 0) return -EINVAL; file = fget_light(fd, &fput_needed); if (file) { ret = -ESPIPE; if (file->f_mode & FMODE_PREAD) ret = vfs_read(file, buf, count, &pos); fput_light(file, fput_needed); } return ret;} asmlinkage ssize_t sys_pwrite64(unsigned int fd, const char __user *buf, size_t count, loff_t pos){ struct file *file; ssize_t ret = -EBADF; int fput_needed; if (pos < 0) return -EINVAL; file = fget_light(fd, &fput_needed); if (file) { ret = -ESPIPE; if (file->f_mode & FMODE_PWRITE) ret = vfs_write(file, buf, count, &pos); fput_light(file, fput_needed); } return ret;} /* * Reduce an iovec's length in-place. Return the resulting number of segments */unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to){ unsigned long seg = 0; size_t len = 0; while (seg < nr_segs) { seg++; if (len + iov->iov_len >= to) { iov->iov_len = to - len; break; } len += iov->iov_len; iov++; } return seg;} ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn){ struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_left = len; kiocb.ki_nbytes = len; for (;;) { ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); if (ret != -EIOCBRETRY) break; wait_on_retry_sync_kiocb(&kiocb); } if (ret == -EIOCBQUEUED) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret;} /* Do it by hand, with file-ops */ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov, unsigned long nr_segs, loff_t *ppos, io_fn_t fn){ struct iovec *vector = iov; ssize_t ret = 0; while (nr_segs > 0) { void __user *base; size_t len; ssize_t nr; base = vector->iov_base; len = vector->iov_len; vector++; nr_segs--; nr = fn(filp, base, len, ppos); if (nr < 0) { if (!ret) ret = nr; break; }Lines 1–500 of 833. Load more
/* * linux/fs/read_write.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include <linux/slab.h> #include <linux/stat.h> #include <linux/fcntl.h> #include <linux/file.h> #include <linux/uio.h> #include <linux/smp_lock.h> #include <linux/fsnotify.h> #include <linux/security.h> #include <linux/module.h> #include <linux/syscalls.h> #include <linux/pagemap.h> #include "read_write.h" #include <asm/uaccess.h> #include <asm/unistd.h> const struct file_operations generic_ro_fops = { .llseek = generic_file_llseek, .read = do_sync_read, .aio_read = generic_file_aio_read, .mmap = generic_file_readonly_mmap, .sendfile = generic_file_sendfile, }; EXPORT_SYMBOL(generic_ro_fops); loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) { long long retval; struct inode *inode = file->f_mapping->host; mutex_lock(&inode->i_mutex); switch (origin) { case 2: offset += inode->i_size; break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset>=0 && offset<=inode->i_sb->s_maxbytes) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } mutex_unlock(&inode->i_mutex); return retval; } EXPORT_SYMBOL(generic_file_llseek); loff_t remote_llseek(struct file *file, loff_t offset, int origin) { long long retval; lock_kernel(); switch (origin) { case 2: offset += i_size_read(file->f_path.dentry->d_inode); break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset>=0 && offset<=file->f_path.dentry->d_inode->i_sb->s_maxbytes) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } 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) { long long retval; lock_kernel(); switch (origin) { case 2: offset += i_size_read(file->f_path.dentry->d_inode); break; case 1: offset += file->f_pos; } retval = -EINVAL; if (offset >= 0) { if (offset != file->f_pos) { file->f_pos = offset; file->f_version = 0; } retval = offset; } unlock_kernel(); return retval; } EXPORT_SYMBOL(default_llseek); loff_t vfs_llseek(struct file *file, loff_t offset, int origin) { loff_t (*fn)(struct file *, loff_t, int); 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) { off_t retval; struct file * file; int fput_needed; retval = -EBADF; file = fget_light(fd, &fput_needed); if (!file) goto bad; retval = -EINVAL; if (origin <= 2) { 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 */ } fput_light(file, fput_needed); bad: return retval; } #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) { int retval; struct file * file; loff_t offset; int fput_needed; retval = -EBADF; file = fget_light(fd, &fput_needed); if (!file) goto bad; retval = -EINVAL; if (origin > 2) goto out_putf; offset = vfs_llseek(file, ((loff_t) offset_high << 32) | offset_low, origin); retval = (int)offset; if (offset >= 0) { retval = -EFAULT; if (!copy_to_user(result, &offset, sizeof(offset))) retval = 0; } out_putf: fput_light(file, fput_needed); bad: return retval; } #endif /* * rw_verify_area doesn't like huge counts. We limit * them to something that fits in "int" so that others * won't have to do range checks all the time. */ #define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK) int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count) { struct inode *inode; loff_t pos; inode = file->f_path.dentry->d_inode; if (unlikely((ssize_t) count < 0)) goto Einval; pos = *ppos; if (unlikely((pos < 0) || (loff_t) (pos + count) < 0)) goto Einval; if (unlikely(inode->i_flock && MANDATORY_LOCK(inode))) { int retval = locks_mandatory_area( read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, pos, count); if (retval < 0) return retval; } return count > MAX_RW_COUNT ? MAX_RW_COUNT : count; Einval: return -EINVAL; } static void wait_on_retry_sync_kiocb(struct kiocb *iocb) { set_current_state(TASK_UNINTERRUPTIBLE); if (!kiocbIsKicked(iocb)) schedule(); else kiocbClearKicked(iocb); __set_current_state(TASK_RUNNING); } ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = buf, .iov_len = len }; struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_left = len; for (;;) { ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); if (ret != -EIOCBRETRY) break; wait_on_retry_sync_kiocb(&kiocb); } if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } EXPORT_SYMBOL(do_sync_read); ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { 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 = rw_verify_area(READ, file, pos, count); if (ret >= 0) { count = ret; ret = security_file_permission (file, MAY_READ); if (!ret) { if (file->f_op->read) ret = file->f_op->read(file, buf, count, pos); else ret = do_sync_read(file, buf, count, pos); if (ret > 0) { fsnotify_access(file->f_path.dentry); add_rchar(current, ret); } inc_syscr(current); } } return ret; } EXPORT_SYMBOL(vfs_read); ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_left = len; for (;;) { ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); if (ret != -EIOCBRETRY) break; wait_on_retry_sync_kiocb(&kiocb); } if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } EXPORT_SYMBOL(do_sync_write); ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { 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 = rw_verify_area(WRITE, file, pos, count); if (ret >= 0) { count = ret; ret = security_file_permission (file, MAY_WRITE); if (!ret) { if (file->f_op->write) ret = file->f_op->write(file, buf, count, pos); else ret = do_sync_write(file, buf, count, pos); if (ret > 0) { fsnotify_modify(file->f_path.dentry); add_wchar(current, ret); } inc_syscw(current); } } return ret; } 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; ssize_t ret = -EBADF; int fput_needed; file = fget_light(fd, &fput_needed); if (file) { loff_t pos = file_pos_read(file); ret = vfs_read(file, buf, count, &pos); file_pos_write(file, pos); fput_light(file, fput_needed); } return ret; } EXPORT_SYMBOL_GPL(sys_read); asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count) { struct file *file; ssize_t ret = -EBADF; int fput_needed; file = fget_light(fd, &fput_needed); if (file) { loff_t pos = file_pos_read(file); ret = vfs_write(file, buf, count, &pos); file_pos_write(file, pos); fput_light(file, fput_needed); } return ret; } asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf, size_t count, loff_t pos) { struct file *file; ssize_t ret = -EBADF; int fput_needed; if (pos < 0) return -EINVAL; file = fget_light(fd, &fput_needed); if (file) { ret = -ESPIPE; if (file->f_mode & FMODE_PREAD) ret = vfs_read(file, buf, count, &pos); fput_light(file, fput_needed); } return ret; } asmlinkage ssize_t sys_pwrite64(unsigned int fd, const char __user *buf, size_t count, loff_t pos) { struct file *file; ssize_t ret = -EBADF; int fput_needed; if (pos < 0) return -EINVAL; file = fget_light(fd, &fput_needed); if (file) { ret = -ESPIPE; if (file->f_mode & FMODE_PWRITE) ret = vfs_write(file, buf, count, &pos); fput_light(file, fput_needed); } return ret; } /* * Reduce an iovec's length in-place. Return the resulting number of segments */ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to) { unsigned long seg = 0; size_t len = 0; while (seg < nr_segs) { seg++; if (len + iov->iov_len >= to) { iov->iov_len = to - len; break; } len += iov->iov_len; iov++; } return seg; } ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn) { struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; kiocb.ki_left = len; kiocb.ki_nbytes = len; for (;;) { ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); if (ret != -EIOCBRETRY) break; wait_on_retry_sync_kiocb(&kiocb); } if (ret == -EIOCBQUEUED) ret = wait_on_sync_kiocb(&kiocb); *ppos = kiocb.ki_pos; return ret; } /* Do it by hand, with file-ops */ ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov, unsigned long nr_segs, loff_t *ppos, io_fn_t fn) { struct iovec *vector = iov; ssize_t ret = 0; while (nr_segs > 0) { void __user *base; size_t len; ssize_t nr; base = vector->iov_base; len = vector->iov_len; vector++; nr_segs--; nr = fn(filp, base, len, ppos); if (nr < 0) { if (!ret) ret = nr; break; }