|
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 |
|
@@ -105,3 +107,159 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
105 | 107 | }
|
106 | 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 | +} |
0 commit comments