blob: f8eb0e7b043a7117808c11916035b22afea774cf [file] [log] [blame]
/*
* linux/fs/compat.c
*
* Kernel compatibililty routines for e.g. 32 bit syscall support
* on 64 bit kernels.
*
* Copyright (C) 2002 Stephen Rothwell, IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <linux/compat.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/namei.h>
#include <linux/file.h>
#include <linux/vfs.h>
#include <asm/uaccess.h>
/*
* Not all architectures have sys_utime, so implement this in terms
* of sys_utimes.
*/
asmlinkage long compat_sys_utime(char *filename, struct compat_utimbuf *t)
{
struct timeval tv[2];
if (t) {
if (get_user(tv[0].tv_sec, &t->actime) ||
get_user(tv[1].tv_sec, &t->modtime))
return -EFAULT;
tv[0].tv_usec = 0;
tv[1].tv_usec = 0;
}
return do_utimes(filename, t ? tv : NULL);
}
asmlinkage long compat_sys_newstat(char * filename,
struct compat_stat *statbuf)
{
struct kstat stat;
int error = vfs_stat(filename, &stat);
if (!error)
error = cp_compat_stat(&stat, statbuf);
return error;
}
asmlinkage long compat_sys_newlstat(char * filename,
struct compat_stat *statbuf)
{
struct kstat stat;
int error = vfs_lstat(filename, &stat);
if (!error)
error = cp_compat_stat(&stat, statbuf);
return error;
}
asmlinkage long compat_sys_newfstat(unsigned int fd,
struct compat_stat * statbuf)
{
struct kstat stat;
int error = vfs_fstat(fd, &stat);
if (!error)
error = cp_compat_stat(&stat, statbuf);
return error;
}
int get_compat_flock(struct flock *kfl, struct compat_flock *ufl)
{
int err;
if (!access_ok(VERIFY_READ, ufl, sizeof(*ufl)))
return -EFAULT;
err = __get_user(kfl->l_type, &ufl->l_type);
err |= __get_user(kfl->l_whence, &ufl->l_whence);
err |= __get_user(kfl->l_start, &ufl->l_start);
err |= __get_user(kfl->l_len, &ufl->l_len);
err |= __get_user(kfl->l_pid, &ufl->l_pid);
return err;
}
int put_compat_flock(struct flock *kfl, struct compat_flock *ufl)
{
int err;
if (!access_ok(VERIFY_WRITE, ufl, sizeof(*ufl)))
return -EFAULT;
err = __put_user(kfl->l_type, &ufl->l_type);
err |= __put_user(kfl->l_whence, &ufl->l_whence);
err |= __put_user(kfl->l_start, &ufl->l_start);
err |= __put_user(kfl->l_len, &ufl->l_len);
err |= __put_user(kfl->l_pid, &ufl->l_pid);
return err;
}
static int put_compat_statfs(struct compat_statfs *ubuf, struct statfs *kbuf)
{
if (verify_area(VERIFY_WRITE, ubuf, sizeof(*ubuf)) ||
__put_user(kbuf->f_type, &ubuf->f_type) ||
__put_user(kbuf->f_bsize, &ubuf->f_bsize) ||
__put_user(kbuf->f_blocks, &ubuf->f_blocks) ||
__put_user(kbuf->f_bfree, &ubuf->f_bfree) ||
__put_user(kbuf->f_bavail, &ubuf->f_bavail) ||
__put_user(kbuf->f_files, &ubuf->f_files) ||
__put_user(kbuf->f_ffree, &ubuf->f_ffree) ||
__put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
__put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
__put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]))
return -EFAULT;
return 0;
}
/*
* The following statfs calls are copies of code from fs/open.c and
* should be checked against those from time to time
*/
asmlinkage long compat_sys_statfs(const char *path, struct compat_statfs *buf)
{
struct nameidata nd;
int error;
error = user_path_walk(path, &nd);
if (!error) {
struct statfs tmp;
error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp);
if (!error && put_compat_statfs(buf, &tmp))
error = -EFAULT;
path_release(&nd);
}
return error;
}
asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs *buf)
{
struct file * file;
struct statfs tmp;
int error;
error = -EBADF;
file = fget(fd);
if (!file)
goto out;
error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);
if (!error && put_compat_statfs(buf, &tmp))
error = -EFAULT;
fput(file);
out:
return error;
}