1008 lines · 22.4 KB · C
RawDownload
/* *  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/fsnotify.h>#include <linux/security.h>#include <linux/export.h>#include <linux/syscalls.h>#include <linux/pagemap.h>#include <linux/splice.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,	.splice_read	= generic_file_splice_read,}; EXPORT_SYMBOL(generic_ro_fops); static inline int unsigned_offsets(struct file *file){	return file->f_mode & FMODE_UNSIGNED_OFFSET;} static loff_t lseek_execute(struct file *file, struct inode *inode,		loff_t offset, loff_t maxsize){	if (offset < 0 && !unsigned_offsets(file))		return -EINVAL;	if (offset > maxsize)		return -EINVAL; 	if (offset != file->f_pos) {		file->f_pos = offset;		file->f_version = 0;	}	return offset;} /** * generic_file_llseek_size - generic llseek implementation for regular files * @file:	file structure to seek on * @offset:	file offset to seek to * @origin:	type of seek * @size:	max size of file system * * This is a variant of generic_file_llseek that allows passing in a custom * file size. * * Synchronization: * SEEK_SET and SEEK_END are unsynchronized (but atomic on 64bit platforms) * SEEK_CUR is synchronized against other SEEK_CURs, but not read/writes. * read/writes behave like SEEK_SET against seeks. */loff_tgeneric_file_llseek_size(struct file *file, loff_t offset, int origin,		loff_t maxsize){	struct inode *inode = file->f_mapping->host; 	switch (origin) {	case SEEK_END:		offset += i_size_read(inode);		break;	case SEEK_CUR:		/*		 * Here we special-case the lseek(fd, 0, SEEK_CUR)		 * position-querying operation.  Avoid rewriting the "same"		 * f_pos value back to the file because a concurrent read(),		 * write() or lseek() might have altered it		 */		if (offset == 0)			return file->f_pos;		/*		 * f_lock protects against read/modify/write race with other		 * SEEK_CURs. Note that parallel writes and reads behave		 * like SEEK_SET.		 */		spin_lock(&file->f_lock);		offset = lseek_execute(file, inode, file->f_pos + offset,				       maxsize);		spin_unlock(&file->f_lock);		return offset;	case SEEK_DATA:		/*		 * In the generic case the entire file is data, so as long as		 * offset isn't at the end of the file then the offset is data.		 */		if (offset >= i_size_read(inode))			return -ENXIO;		break;	case SEEK_HOLE:		/*		 * There is a virtual hole at the end of the file, so as long as		 * offset isn't i_size or larger, return i_size.		 */		if (offset >= i_size_read(inode))			return -ENXIO;		offset = i_size_read(inode);		break;	} 	return lseek_execute(file, inode, offset, maxsize);}EXPORT_SYMBOL(generic_file_llseek_size); /** * generic_file_llseek - generic llseek implementation for regular files * @file:	file structure to seek on * @offset:	file offset to seek to * @origin:	type of seek * * This is a generic implemenation of ->llseek useable for all normal local * filesystems.  It just updates the file offset to the value specified by * @offset and @origin under i_mutex. */loff_t generic_file_llseek(struct file *file, loff_t offset, int origin){	struct inode *inode = file->f_mapping->host; 	return generic_file_llseek_size(file, offset, origin,					inode->i_sb->s_maxbytes);}EXPORT_SYMBOL(generic_file_llseek); /** * noop_llseek - No Operation Performed llseek implementation * @file:	file structure to seek on * @offset:	file offset to seek to * @origin:	type of seek * * This is an implementation of ->llseek useable for the rare special case when * userspace expects the seek to succeed but the (device) file is actually not * able to perform the seek. In this case you use noop_llseek() instead of * falling back to the default implementation of ->llseek. */loff_t noop_llseek(struct file *file, loff_t offset, int origin){	return file->f_pos;}EXPORT_SYMBOL(noop_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){	struct inode *inode = file->f_path.dentry->d_inode;	loff_t retval; 	mutex_lock(&inode->i_mutex);	switch (origin) {		case SEEK_END:			offset += i_size_read(inode);			break;		case SEEK_CUR:			if (offset == 0) {				retval = file->f_pos;				goto out;			}			offset += file->f_pos;			break;		case SEEK_DATA:			/*			 * In the generic case the entire file is data, so as			 * long as offset isn't at the end of the file then the			 * offset is data.			 */			if (offset >= inode->i_size) {				retval = -ENXIO;				goto out;			}			break;		case SEEK_HOLE:			/*			 * There is a virtual hole at the end of the file, so			 * as long as offset isn't i_size or larger, return			 * i_size.			 */			if (offset >= inode->i_size) {				retval = -ENXIO;				goto out;			}			offset = inode->i_size;			break;	}	retval = -EINVAL;	if (offset >= 0 || unsigned_offsets(file)) {		if (offset != file->f_pos) {			file->f_pos = offset;			file->f_version = 0;		}		retval = offset;	}out:	mutex_unlock(&inode->i_mutex);	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) {		if (file->f_op && file->f_op->llseek)			fn = file->f_op->llseek;	}	return fn(file, offset, origin);}EXPORT_SYMBOL(vfs_llseek); SYSCALL_DEFINE3(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 <= SEEK_MAX) {		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_LLSEEKSYSCALL_DEFINE5(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 > SEEK_MAX)		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. */int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count){	struct inode *inode;	loff_t pos;	int retval = -EINVAL; 	inode = file->f_path.dentry->d_inode;	if (unlikely((ssize_t) count < 0))		return retval;	pos = *ppos;	if (unlikely(pos < 0)) {		if (!unsigned_offsets(file))			return retval;		if (count >= -pos) /* both values are in 0..LLONG_MAX */			return -EOVERFLOW;	} else if (unlikely((loff_t) (pos + count) < 0)) {		if (!unsigned_offsets(file))			return retval;	} 	if (unlikely(inode->i_flock && mandatory_lock(inode))) {		retval = locks_mandatory_area(			read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,			inode, file, pos, count);		if (retval < 0)			return retval;	}	retval = security_file_permission(file,				read_write == READ ? MAY_READ : MAY_WRITE);	if (retval)		return retval;	return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;} 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;	kiocb.ki_nbytes = 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;		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);			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;	kiocb.ki_nbytes = 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;		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);			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;} SYSCALL_DEFINE3(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;} SYSCALL_DEFINE3(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;} SYSCALL_DEFINE(pread64)(unsigned int fd, char __user *buf,			size_t count, loff_t pos){	struct file *file;	ssize_t ret = -EBADF;	int fput_needed;

Lines 1–500 of 1008.