/*
* 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;