diff options
| author | Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | 2011-01-29 08:13:26 -0500 |
|---|---|---|
| committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-15 02:21:44 -0400 |
| commit | becfd1f37544798cbdfd788f32c827160fab98c1 (patch) | |
| tree | 80353bdc7e59c661e841d2d0029bb308c0ed4da0 | |
| parent | 990d6c2d7aee921e3bce22b2d6a750fd552262be (diff) | |
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>
| -rw-r--r-- | fs/compat.c | 13 | ||||
| -rw-r--r-- | fs/exportfs/expfs.c | 2 | ||||
| -rw-r--r-- | fs/fhandle.c | 158 | ||||
| -rw-r--r-- | fs/internal.h | 3 | ||||
| -rw-r--r-- | include/linux/syscalls.h | 3 | ||||
| -rw-r--r-- | kernel/sys_ni.c | 2 |
6 files changed, 181 insertions, 0 deletions
diff --git a/fs/compat.c b/fs/compat.c index a071775f3bb3..c6d31a3bab88 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
| @@ -2284,3 +2284,16 @@ asmlinkage long compat_sys_timerfd_gettime(int ufd, | |||
| 2284 | } | 2284 | } |
| 2285 | 2285 | ||
| 2286 | #endif /* CONFIG_TIMERFD */ | 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 | ||
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index cfe55731b6dc..b05acb796135 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
| @@ -374,6 +374,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, | |||
| 374 | /* | 374 | /* |
| 375 | * Try to get any dentry for the given file handle from the filesystem. | 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 | result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); | 379 | result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); |
| 378 | if (!result) | 380 | if (!result) |
| 379 | result = ERR_PTR(-ESTALE); | 381 | result = ERR_PTR(-ESTALE); |
diff --git a/fs/fhandle.c b/fs/fhandle.c index 9f79e743a840..bf93ad2bee07 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include <linux/mount.h> | 5 | #include <linux/mount.h> |
| 6 | #include <linux/namei.h> | 6 | #include <linux/namei.h> |
| 7 | #include <linux/exportfs.h> | 7 | #include <linux/exportfs.h> |
| 8 | #include <linux/fs_struct.h> | ||
| 9 | #include <linux/fsnotify.h> | ||
| 8 | #include <asm/uaccess.h> | 10 | #include <asm/uaccess.h> |
| 9 | #include "internal.h" | 11 | #include "internal.h" |
| 10 | 12 | ||
| @@ -105,3 +107,159 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, | |||
| 105 | } | 107 | } |
| 106 | return err; | 108 | return err; |
| 107 | } | 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; | ||
| 265 | } | ||
diff --git a/fs/internal.h b/fs/internal.h index 52abc5287f50..f3d15de44b15 100644 --- a/fs/internal.h +++ b/fs/internal.h | |||
| @@ -117,6 +117,9 @@ extern struct file *do_filp_open(int dfd, const char *pathname, | |||
| 117 | extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, | 117 | extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, |
| 118 | const char *, const struct open_flags *, int lookup_flags); | 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 | * inode.c | 124 | * inode.c |
| 122 | */ | 125 | */ |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 970112613fb4..2d9b79c0f224 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
| @@ -836,4 +836,7 @@ asmlinkage long sys_old_mmap(struct mmap_arg_struct __user *arg); | |||
| 836 | asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name, | 836 | asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name, |
| 837 | struct file_handle __user *handle, | 837 | struct file_handle __user *handle, |
| 838 | int __user *mnt_id, int flag); | 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 | #endif | 842 | #endif |
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 4e013439ac28..25cc41cd8f33 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c | |||
| @@ -189,3 +189,5 @@ cond_syscall(sys_fanotify_mark); | |||
| 189 | 189 | ||
| 190 | /* open by handle */ | 190 | /* open by handle */ |
| 191 | cond_syscall(sys_name_to_handle_at); | 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); | ||
