diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /fs/read_write.c | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/read_write.c')
-rw-r--r-- | fs/read_write.c | 324 |
1 files changed, 236 insertions, 88 deletions
diff --git a/fs/read_write.c b/fs/read_write.c index 1cd19d57a..6c28b8f59 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -9,133 +9,281 @@ #include <linux/stat.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/fcntl.h> +#include <linux/file.h> #include <linux/mm.h> +#include <linux/uio.h> -#include <asm/segment.h> +#include <asm/uaccess.h> -asmlinkage int sys_lseek(unsigned int fd, off_t offset, unsigned int origin) +static long long default_llseek(struct inode *inode, + struct file *file, + long long offset, + int origin) { - struct file * file; - int tmp = -1; - - if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode)) - return -EBADF; - if (origin > 2) - return -EINVAL; - if (file->f_op && file->f_op->lseek) - return file->f_op->lseek(file->f_inode,file,offset,origin); + long long retval; -/* this is the default handler if no lseek handler is present */ switch (origin) { - case 0: - tmp = offset; - break; - case 1: - tmp = file->f_pos + offset; - break; case 2: - if (!file->f_inode) - return -EINVAL; - tmp = file->f_inode->i_size + offset; + offset += inode->i_size; break; + case 1: + offset += file->f_pos; } - if (tmp < 0) - return -EINVAL; - if (tmp != file->f_pos) { - file->f_pos = tmp; - file->f_reada = 0; - file->f_version = ++event; + retval = -EINVAL; + if (offset >= 0) { + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_reada = 0; + file->f_version = ++event; + } + retval = offset; } - return file->f_pos; + return retval; +} + +static inline long long llseek(struct inode * inode, struct file *file, + long long offset, unsigned int origin) +{ + long long (*fn)(struct inode *, struct file *, long long, int); + + fn = default_llseek; + if (file->f_op && file->f_op->llseek) + fn = file->f_op->llseek; + return fn(inode, file, offset, origin); +} + +asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin) +{ + long retval; + struct file * file; + struct inode * inode; + + retval = -EBADF; + if (fd >= NR_OPEN || + !(file = current->files->fd[fd]) || + !(inode = file->f_inode)) + goto bad; + retval = -EINVAL; + if (origin > 2) + goto bad; + retval = llseek(inode, file, offset, origin); +bad: + return retval; } asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, loff_t * result, unsigned int origin) { + long retval; struct file * file; - loff_t tmp = -1; - loff_t offset; - int err; + struct inode * inode; + long long offset; - if (fd >= NR_OPEN || !(file=current->files->fd[fd]) || !(file->f_inode)) - return -EBADF; + retval = -EBADF; + if (fd >= NR_OPEN || + !(file = current->files->fd[fd]) || + !(inode = file->f_inode)) + goto bad; + retval = -EINVAL; if (origin > 2) - return -EINVAL; - if ((err = verify_area(VERIFY_WRITE, result, sizeof(loff_t)))) - return err; - offset = (loff_t) (((unsigned long long) offset_high << 32) | offset_low); -/* there is no fs specific llseek handler */ - switch (origin) { - case 0: - tmp = offset; - break; - case 1: - tmp = file->f_pos + offset; - break; - case 2: - if (!file->f_inode) - return -EINVAL; - tmp = file->f_inode->i_size + offset; - break; + goto bad; + + offset = llseek(inode, file, + (((unsigned long long) offset_high << 32) | offset_low), + origin); + + retval = offset; + if (offset >= 0) { + retval = copy_to_user(result, &offset, sizeof(offset)); + if (retval) + retval = -EFAULT; } - if (tmp < 0) - return -EINVAL; - file->f_pos = tmp; - file->f_reada = 0; - file->f_version = ++event; - memcpy_tofs(result, &file->f_pos, sizeof(loff_t)); - return 0; + +bad: + return retval; } -asmlinkage int sys_read(unsigned int fd,char * buf,unsigned int count) +asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count) { int error; struct file * file; struct inode * inode; + long (*read)(struct inode *, struct file *, char *, unsigned long); - if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode)) - return -EBADF; + error = -EBADF; + file = fget(fd); + if (!file) + goto bad_file; + inode = file->f_inode; + if (!inode) + goto out; + error = -EBADF; if (!(file->f_mode & 1)) - return -EBADF; - if (!file->f_op || !file->f_op->read) - return -EINVAL; - if (!count) - return 0; - error = verify_area(VERIFY_WRITE,buf,count); + goto out; + error = locks_verify_area(FLOCK_VERIFY_READ,inode,file,file->f_pos,count); if (error) - return error; - return file->f_op->read(inode,file,buf,count); + goto out; + error = -EINVAL; + if (!file->f_op || !(read = file->f_op->read)) + goto out; + error = read(inode,file,buf,count); +out: + fput(file, inode); +bad_file: + return error; } -asmlinkage int sys_write(unsigned int fd,char * buf,unsigned int count) +asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count) { int error; struct file * file; struct inode * inode; - int written; - - if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode)) - return -EBADF; + long (*write)(struct inode *, struct file *, const char *, unsigned long); + + error = -EBADF; + file = fget(fd); + if (!file) + goto bad_file; + inode = file->f_inode; + if (!inode) + goto out; if (!(file->f_mode & 2)) - return -EBADF; - if (!file->f_op || !file->f_op->write) - return -EINVAL; + goto out; + error = locks_verify_area(FLOCK_VERIFY_WRITE,inode,file,file->f_pos,count); + if (error) + goto out; + error = -EINVAL; + if (!file->f_op || !(write = file->f_op->write)) + goto out; + down(&inode->i_sem); + error = write(inode,file,buf,count); + up(&inode->i_sem); +out: + fput(file, inode); +bad_file: + return error; +} + +static long sock_readv_writev(int type, struct inode * inode, struct file * file, + const struct iovec * iov, long count, long size) +{ + struct msghdr msg; + struct socket *sock; + + sock = &inode->u.socket_i; + if (!sock->ops) + return -EOPNOTSUPP; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_iov = (struct iovec *) iov; + msg.msg_iovlen = count; + + /* read() does a VERIFY_WRITE */ + if (type == VERIFY_WRITE) { + if (!sock->ops->recvmsg) + return -EOPNOTSUPP; + return sock->ops->recvmsg(sock, &msg, size, + (file->f_flags & O_NONBLOCK), 0, NULL); + } + if (!sock->ops->sendmsg) + return -EOPNOTSUPP; + return sock->ops->sendmsg(sock, &msg, size, + (file->f_flags & O_NONBLOCK), 0); +} + +typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long); + +static long do_readv_writev(int type, struct inode * inode, struct file * file, + const struct iovec * vector, unsigned long count) +{ + unsigned long tot_len; + struct iovec iov[UIO_MAXIOV]; + long retval, i; + IO_fn_t fn; + + /* + * First get the "struct iovec" from user memory and + * verify all the pointers + */ if (!count) return 0; - error = verify_area(VERIFY_READ,buf,count); - if (error) - return error; - written = file->f_op->write(inode,file,buf,count); + if (count > UIO_MAXIOV) + return -EINVAL; + if (copy_from_user(iov, vector, count*sizeof(*vector))) + return -EFAULT; + tot_len = 0; + for (i = 0 ; i < count ; i++) + tot_len += iov[i].iov_len; + + retval = locks_verify_area(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, + inode, file, file->f_pos, tot_len); + if (retval) + return retval; + /* - * If data has been written to the file, remove the setuid and - * the setgid bits + * Then do the actual IO. Note that sockets need to be handled + * specially as they have atomicity guarantees and can handle + * iovec's natively */ - if (written > 0 && !suser() && (inode->i_mode & (S_ISUID | S_ISGID))) { - struct iattr newattrs; - newattrs.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID); - newattrs.ia_valid = ATTR_MODE; - notify_change(inode, &newattrs); + if (inode->i_sock) + return sock_readv_writev(type, inode, file, iov, count, tot_len); + + if (!file->f_op) + return -EINVAL; + /* VERIFY_WRITE actually means a read, as we write to user space */ + fn = file->f_op->read; + if (type == VERIFY_READ) + fn = (IO_fn_t) file->f_op->write; + vector = iov; + while (count > 0) { + void * base; + int len, nr; + + base = vector->iov_base; + len = vector->iov_len; + vector++; + count--; + nr = fn(inode, file, base, len); + if (nr < 0) { + if (retval) + break; + retval = nr; + break; + } + retval += nr; + if (nr != len) + break; } - return written; + return retval; +} + +asmlinkage long sys_readv(unsigned long fd, const struct iovec * vector, unsigned long count) +{ + struct file * file; + struct inode * inode; + + if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode)) + return -EBADF; + if (!(file->f_mode & 1)) + return -EBADF; + return do_readv_writev(VERIFY_WRITE, inode, file, vector, count); +} + +asmlinkage long sys_writev(unsigned long fd, const struct iovec * vector, unsigned long count) +{ + int error; + struct file * file; + struct inode * inode; + + if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode = file->f_inode)) + return -EBADF; + if (!(file->f_mode & 2)) + return -EBADF; + down(&inode->i_sem); + error = do_readv_writev(VERIFY_READ, inode, file, vector, count); + up(&inode->i_sem); + return error; } |