Commit becfd1f37544798cbdfd788f32c827160fab98c1

Authored by Aneesh Kumar K.V
Committed by Al Viro
1 parent 990d6c2d7a

vfs: Add open by file handle support

[AV: duplicate of open() guts removed; file_open_root() used instead]

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Showing 6 changed files with 181 additions and 0 deletions Side-by-side Diff

... ... @@ -2284,4 +2284,17 @@
2284 2284 }
2285 2285  
2286 2286 #endif /* CONFIG_TIMERFD */
  2287 +
  2288 +#ifdef CONFIG_FHANDLE
  2289 +/*
  2290 + * Exactly like fs/open.c:sys_open_by_handle_at(), except that it
  2291 + * doesn't set the O_LARGEFILE flag.
  2292 + */
  2293 +asmlinkage long
  2294 +compat_sys_open_by_handle_at(int mountdirfd,
  2295 + struct file_handle __user *handle, int flags)
  2296 +{
  2297 + return do_handle_open(mountdirfd, handle, flags);
  2298 +}
  2299 +#endif
... ... @@ -374,6 +374,8 @@
374 374 /*
375 375 * Try to get any dentry for the given file handle from the filesystem.
376 376 */
  377 + if (!nop || !nop->fh_to_dentry)
  378 + return ERR_PTR(-ESTALE);
377 379 result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
378 380 if (!result)
379 381 result = ERR_PTR(-ESTALE);
... ... @@ -5,6 +5,8 @@
5 5 #include <linux/mount.h>
6 6 #include <linux/namei.h>
7 7 #include <linux/exportfs.h>
  8 +#include <linux/fs_struct.h>
  9 +#include <linux/fsnotify.h>
8 10 #include <asm/uaccess.h>
9 11 #include "internal.h"
10 12  
... ... @@ -104,5 +106,161 @@
104 106 path_put(&path);
105 107 }
106 108 return err;
  109 +}
  110 +
  111 +static struct vfsmount *get_vfsmount_from_fd(int fd)
  112 +{
  113 + struct path path;
  114 +
  115 + if (fd == AT_FDCWD) {
  116 + struct fs_struct *fs = current->fs;
  117 + spin_lock(&fs->lock);
  118 + path = fs->pwd;
  119 + mntget(path.mnt);
  120 + spin_unlock(&fs->lock);
  121 + } else {
  122 + int fput_needed;
  123 + struct file *file = fget_light(fd, &fput_needed);
  124 + if (!file)
  125 + return ERR_PTR(-EBADF);
  126 + path = file->f_path;
  127 + mntget(path.mnt);
  128 + fput_light(file, fput_needed);
  129 + }
  130 + return path.mnt;
  131 +}
  132 +
  133 +static int vfs_dentry_acceptable(void *context, struct dentry *dentry)
  134 +{
  135 + return 1;
  136 +}
  137 +
  138 +static int do_handle_to_path(int mountdirfd, struct file_handle *handle,
  139 + struct path *path)
  140 +{
  141 + int retval = 0;
  142 + int handle_dwords;
  143 +
  144 + path->mnt = get_vfsmount_from_fd(mountdirfd);
  145 + if (IS_ERR(path->mnt)) {
  146 + retval = PTR_ERR(path->mnt);
  147 + goto out_err;
  148 + }
  149 + /* change the handle size to multiple of sizeof(u32) */
  150 + handle_dwords = handle->handle_bytes >> 2;
  151 + path->dentry = exportfs_decode_fh(path->mnt,
  152 + (struct fid *)handle->f_handle,
  153 + handle_dwords, handle->handle_type,
  154 + vfs_dentry_acceptable, NULL);
  155 + if (IS_ERR(path->dentry)) {
  156 + retval = PTR_ERR(path->dentry);
  157 + goto out_mnt;
  158 + }
  159 + return 0;
  160 +out_mnt:
  161 + mntput(path->mnt);
  162 +out_err:
  163 + return retval;
  164 +}
  165 +
  166 +static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
  167 + struct path *path)
  168 +{
  169 + int retval = 0;
  170 + struct file_handle f_handle;
  171 + struct file_handle *handle = NULL;
  172 +
  173 + /*
  174 + * With handle we don't look at the execute bit on the
  175 + * the directory. Ideally we would like CAP_DAC_SEARCH.
  176 + * But we don't have that
  177 + */
  178 + if (!capable(CAP_DAC_READ_SEARCH)) {
  179 + retval = -EPERM;
  180 + goto out_err;
  181 + }
  182 + if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
  183 + retval = -EFAULT;
  184 + goto out_err;
  185 + }
  186 + if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
  187 + (f_handle.handle_bytes == 0)) {
  188 + retval = -EINVAL;
  189 + goto out_err;
  190 + }
  191 + handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
  192 + GFP_KERNEL);
  193 + if (!handle) {
  194 + retval = -ENOMEM;
  195 + goto out_err;
  196 + }
  197 + /* copy the full handle */
  198 + if (copy_from_user(handle, ufh,
  199 + sizeof(struct file_handle) +
  200 + f_handle.handle_bytes)) {
  201 + retval = -EFAULT;
  202 + goto out_handle;
  203 + }
  204 +
  205 + retval = do_handle_to_path(mountdirfd, handle, path);
  206 +
  207 +out_handle:
  208 + kfree(handle);
  209 +out_err:
  210 + return retval;
  211 +}
  212 +
  213 +long do_handle_open(int mountdirfd,
  214 + struct file_handle __user *ufh, int open_flag)
  215 +{
  216 + long retval = 0;
  217 + struct path path;
  218 + struct file *file;
  219 + int fd;
  220 +
  221 + retval = handle_to_path(mountdirfd, ufh, &path);
  222 + if (retval)
  223 + return retval;
  224 +
  225 + fd = get_unused_fd_flags(open_flag);
  226 + if (fd < 0) {
  227 + path_put(&path);
  228 + return fd;
  229 + }
  230 + file = file_open_root(path.dentry, path.mnt, "", open_flag);
  231 + if (IS_ERR(file)) {
  232 + put_unused_fd(fd);
  233 + retval = PTR_ERR(file);
  234 + } else {
  235 + retval = fd;
  236 + fsnotify_open(file);
  237 + fd_install(fd, file);
  238 + }
  239 + path_put(&path);
  240 + return retval;
  241 +}
  242 +
  243 +/**
  244 + * sys_open_by_handle_at: Open the file handle
  245 + * @mountdirfd: directory file descriptor
  246 + * @handle: file handle to be opened
  247 + * @flag: open flags.
  248 + *
  249 + * @mountdirfd indicate the directory file descriptor
  250 + * of the mount point. file handle is decoded relative
  251 + * to the vfsmount pointed by the @mountdirfd. @flags
  252 + * value is same as the open(2) flags.
  253 + */
  254 +SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
  255 + struct file_handle __user *, handle,
  256 + int, flags)
  257 +{
  258 + long ret;
  259 +
  260 + if (force_o_largefile())
  261 + flags |= O_LARGEFILE;
  262 +
  263 + ret = do_handle_open(mountdirfd, handle, flags);
  264 + return ret;
107 265 }
... ... @@ -117,6 +117,9 @@
117 117 extern struct file *do_file_open_root(struct dentry *, struct vfsmount *,
118 118 const char *, const struct open_flags *, int lookup_flags);
119 119  
  120 +extern long do_handle_open(int mountdirfd,
  121 + struct file_handle __user *ufh, int open_flag);
  122 +
120 123 /*
121 124 * inode.c
122 125 */
include/linux/syscalls.h
... ... @@ -836,5 +836,8 @@
836 836 asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
837 837 struct file_handle __user *handle,
838 838 int __user *mnt_id, int flag);
  839 +asmlinkage long sys_open_by_handle_at(int mountdirfd,
  840 + struct file_handle __user *handle,
  841 + int flags);
839 842 #endif
... ... @@ -189,4 +189,6 @@
189 189  
190 190 /* open by handle */
191 191 cond_syscall(sys_name_to_handle_at);
  192 +cond_syscall(sys_open_by_handle_at);
  193 +cond_syscall(compat_sys_open_by_handle_at);