lkml.org 
[lkml]   [2010]   [May]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH -V11 3/9] vfs: Add open by file handle support
    Date
    Acked-by: Serge Hallyn <serue@us.ibm.com>
    Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
    ---
    fs/exportfs/expfs.c | 2 +
    fs/namei.c | 50 +++++++++++++
    fs/open.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++
    include/linux/fs.h | 3 +-
    include/linux/syscalls.h | 2 +
    5 files changed, 233 insertions(+), 1 deletions(-)

    diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
    index d103c31..e73c0ab 100644
    --- a/fs/exportfs/expfs.c
    +++ b/fs/exportfs/expfs.c
    @@ -373,6 +373,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
    /*
    * Try to get any dentry for the given file handle from the filesystem.
    */
    + if (!nop || !nop->fh_to_dentry)
    + return ERR_PTR(-ESTALE);
    result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
    if (!result)
    result = ERR_PTR(-ESTALE);
    diff --git a/fs/namei.c b/fs/namei.c
    index b86b96f..21cf0a5 100644
    --- a/fs/namei.c
    +++ b/fs/namei.c
    @@ -1556,6 +1556,56 @@ static int open_will_truncate(int flag, struct inode *inode)
    return (flag & O_TRUNC);
    }

    +struct file *finish_open_handle(struct path *path,
    + int open_flag, int acc_mode)
    +{
    + int error;
    + struct file *filp;
    + int will_truncate;
    +
    + will_truncate = open_will_truncate(open_flag, path->dentry->d_inode);
    + if (will_truncate) {
    + error = mnt_want_write(path->mnt);
    + if (error)
    + goto exit;
    + }
    + error = may_open(path, acc_mode, open_flag);
    + if (error) {
    + if (will_truncate)
    + mnt_drop_write(path->mnt);
    + goto exit;
    + }
    + filp = dentry_open(path->dentry, path->mnt, open_flag, current_cred());
    + if (!IS_ERR(filp)) {
    + error = ima_file_check(filp, acc_mode);
    + if (error) {
    + fput(filp);
    + filp = ERR_PTR(error);
    + }
    + }
    + if (!IS_ERR(filp)) {
    + if (will_truncate) {
    + error = handle_truncate(path);
    + if (error) {
    + fput(filp);
    + filp = ERR_PTR(error);
    + }
    + }
    + }
    + /*
    + * It is now safe to drop the mnt write
    + * because the filp has had a write taken
    + * on its behalf.
    + */
    + if (will_truncate)
    + mnt_drop_write(path->mnt);
    + return filp;
    +
    +exit:
    + path_put(path);
    + return ERR_PTR(error);
    +}
    +
    static struct file *finish_open(struct nameidata *nd,
    int open_flag, int acc_mode)
    {
    diff --git a/fs/open.c b/fs/open.c
    index c4c2577..9f8a09a 100644
    --- a/fs/open.c
    +++ b/fs/open.c
    @@ -1306,3 +1306,180 @@ SYSCALL_DEFINE4(name_to_handle_at, int, dfd, const char __user *, name,
    return -ENOSYS;
    }
    #endif
    +
    +#ifdef CONFIG_EXPORTFS
    +static struct vfsmount *get_vfsmount_from_fd(int fd)
    +{
    + int fput_needed;
    + struct path *path;
    + struct file *filep;
    +
    + if (fd == AT_FDCWD) {
    + struct fs_struct *fs = current->fs;
    + read_lock(&fs->lock);
    + path = &fs->pwd;
    + mntget(path->mnt);
    + read_unlock(&fs->lock);
    + } else {
    + filep = fget_light(fd, &fput_needed);
    + if (!filep)
    + return ERR_PTR(-EBADF);
    + path = &filep->f_path;
    + mntget(path->mnt);
    + fput_light(filep, fput_needed);
    + }
    + return path->mnt;
    +}
    +
    +static int vfs_dentry_acceptable(void *context, struct dentry *dentry)
    +{
    + return 1;
    +}
    +
    +static struct path *handle_to_path(int mountdirfd, struct file_handle *handle)
    +{
    + int retval;
    + int handle_size;
    + struct path *path;
    +
    + path = kmalloc(sizeof(struct path), GFP_KERNEL);
    + if (!path)
    + return ERR_PTR(-ENOMEM);
    +
    + path->mnt = get_vfsmount_from_fd(mountdirfd);
    + if (IS_ERR(path->mnt)) {
    + retval = PTR_ERR(path->mnt);
    + goto out_err;
    + }
    + /* change the handle size to multiple of sizeof(u32) */
    + handle_size = handle->handle_size >> 2;
    + path->dentry = exportfs_decode_fh(path->mnt,
    + (struct fid *)handle->f_handle,
    + handle_size, handle->handle_type,
    + vfs_dentry_acceptable, NULL);
    + if (IS_ERR(path->dentry)) {
    + retval = PTR_ERR(path->dentry);
    + goto out_mnt;
    + }
    + return path;
    +out_mnt:
    + mntput(path->mnt);
    +out_err:
    + kfree(path);
    + return ERR_PTR(retval);
    +}
    +
    +static long do_sys_open_by_handle(int mountdirfd,
    + struct file_handle __user *ufh, int open_flag)
    +{
    + int acc_mode;
    + int fd, retval = 0;
    + struct file *filp;
    + struct path *path;
    + struct file_handle f_handle;
    + struct file_handle *handle = NULL;
    +
    + /* can't use O_CREATE with open_by_handle */
    + if (open_flag & O_CREAT) {
    + retval = -EINVAL;
    + goto out_err;
    + }
    + if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
    + retval = -EFAULT;
    + goto out_err;
    + }
    + if ((f_handle.handle_size > MAX_HANDLE_SZ) ||
    + (f_handle.handle_size <= 0)) {
    + retval = -EINVAL;
    + goto out_err;
    + }
    + if (!capable(CAP_DAC_OVERRIDE)) {
    + retval = -EPERM;
    + goto out_err;
    + }
    + handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_size,
    + GFP_KERNEL);
    + if (!handle) {
    + retval = -ENOMEM;
    + goto out_err;
    + }
    + /* copy the full handle */
    + if (copy_from_user(handle, ufh,
    + sizeof(struct file_handle) +
    + f_handle.handle_size)) {
    + retval = -EFAULT;
    + goto out_handle;
    + }
    + path = handle_to_path(mountdirfd, handle);
    + if (IS_ERR(path)) {
    + retval = PTR_ERR(path);
    + goto out_handle;
    + }
    + /*
    + * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only
    + * check for O_DSYNC if the need any syncing at all we enforce it's
    + * always set instead of having to deal with possibly weird behaviour
    + * for malicious applications setting only __O_SYNC.
    + */
    + if (open_flag & __O_SYNC)
    + open_flag |= O_DSYNC;
    +
    + acc_mode = MAY_OPEN | ACC_MODE(open_flag);
    +
    + /* O_TRUNC implies we need access checks for write permissions */
    + if (open_flag & O_TRUNC)
    + acc_mode |= MAY_WRITE;
    + /*
    + * Allow the LSM permission hook to distinguish append
    + * access from general write access.
    + */
    + if (open_flag & O_APPEND)
    + acc_mode |= MAY_APPEND;
    +
    + fd = get_unused_fd_flags(open_flag);
    + if (fd < 0) {
    + retval = fd;
    + goto out_path;
    + }
    + filp = finish_open_handle(path, open_flag, acc_mode);
    + if (IS_ERR(filp)) {
    + put_unused_fd(fd);
    + retval = PTR_ERR(filp);
    + } else {
    + retval = fd;
    + fsnotify_open(filp->f_path.dentry);
    + fd_install(fd, filp);
    + }
    + kfree(path);
    + kfree(handle);
    + return retval;
    +
    +out_path:
    + path_put(path);
    + kfree(path);
    +out_handle:
    + kfree(handle);
    +out_err:
    + return retval;
    +}
    +
    +SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
    + struct file_handle __user *, handle,
    + int, flags)
    +{
    + long ret;
    +
    + if (force_o_largefile())
    + flags |= O_LARGEFILE;
    +
    + ret = do_sys_open_by_handle(mountdirfd, handle, flags);
    + return ret;
    +}
    +#else
    +SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
    + struct file_handle __user *, handle,
    + int, flags)
    +{
    + return -ENOSYS;
    +}
    +#endif
    diff --git a/include/linux/fs.h b/include/linux/fs.h
    index d428b1a..c30940c 100644
    --- a/include/linux/fs.h
    +++ b/include/linux/fs.h
    @@ -2140,7 +2140,8 @@ extern int may_open(struct path *, int, int);

    extern int kernel_read(struct file *, loff_t, char *, unsigned long);
    extern struct file * open_exec(const char *);
    -
    +extern struct file *finish_open_handle(struct path *, int, int);
    +
    /* fs/dcache.c -- generic fs support functions */
    extern int is_subdir(struct dentry *, struct dentry *);
    extern int path_is_under(struct path *, struct path *);
    diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
    index d0deef0..58b9702 100644
    --- a/include/linux/syscalls.h
    +++ b/include/linux/syscalls.h
    @@ -849,5 +849,7 @@ asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len,
    asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg);
    asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
    struct file_handle __user *handle, int flag);
    +asmlinkage long sys_open_by_handle_at(int mountdirfd,
    + struct file_handle __user *handle, int flags);

    #endif
    --
    1.7.1.78.g212f0


    \
     
     \ /
      Last update: 2010-05-20 09:39    [W:2.274 / U:0.080 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site