Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : #include <linux/syscalls.h> 3 : : #include <linux/slab.h> 4 : : #include <linux/fs.h> 5 : : #include <linux/file.h> 6 : : #include <linux/mount.h> 7 : : #include <linux/namei.h> 8 : : #include <linux/exportfs.h> 9 : : #include <linux/fs_struct.h> 10 : : #include <linux/fsnotify.h> 11 : : #include <linux/personality.h> 12 : : #include <linux/uaccess.h> 13 : : #include <linux/compat.h> 14 : : #include "internal.h" 15 : : #include "mount.h" 16 : : 17 : 3 : static long do_sys_name_to_handle(struct path *path, 18 : : struct file_handle __user *ufh, 19 : : int __user *mnt_id) 20 : : { 21 : : long retval; 22 : : struct file_handle f_handle; 23 : : int handle_dwords, handle_bytes; 24 : : struct file_handle *handle = NULL; 25 : : 26 : : /* 27 : : * We need to make sure whether the file system 28 : : * support decoding of the file handle 29 : : */ 30 : 3 : if (!path->dentry->d_sb->s_export_op || 31 : 3 : !path->dentry->d_sb->s_export_op->fh_to_dentry) 32 : : return -EOPNOTSUPP; 33 : : 34 : 3 : if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) 35 : : return -EFAULT; 36 : : 37 : 3 : if (f_handle.handle_bytes > MAX_HANDLE_SZ) 38 : : return -EINVAL; 39 : : 40 : 3 : handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes, 41 : : GFP_KERNEL); 42 : 3 : if (!handle) 43 : : return -ENOMEM; 44 : : 45 : : /* convert handle size to multiple of sizeof(u32) */ 46 : 3 : handle_dwords = f_handle.handle_bytes >> 2; 47 : : 48 : : /* we ask for a non connected handle */ 49 : 3 : retval = exportfs_encode_fh(path->dentry, 50 : 3 : (struct fid *)handle->f_handle, 51 : : &handle_dwords, 0); 52 : 3 : handle->handle_type = retval; 53 : : /* convert handle size to bytes */ 54 : 3 : handle_bytes = handle_dwords * sizeof(u32); 55 : 3 : handle->handle_bytes = handle_bytes; 56 : 3 : if ((handle->handle_bytes > f_handle.handle_bytes) || 57 : 3 : (retval == FILEID_INVALID) || (retval == -ENOSPC)) { 58 : : /* As per old exportfs_encode_fh documentation 59 : : * we could return ENOSPC to indicate overflow 60 : : * But file system returned 255 always. So handle 61 : : * both the values 62 : : */ 63 : : /* 64 : : * set the handle size to zero so we copy only 65 : : * non variable part of the file_handle 66 : : */ 67 : : handle_bytes = 0; 68 : : retval = -EOVERFLOW; 69 : : } else 70 : : retval = 0; 71 : : /* copy the mount id */ 72 : 3 : if (put_user(real_mount(path->mnt)->mnt_id, mnt_id) || 73 : 3 : copy_to_user(ufh, handle, 74 : 3 : sizeof(struct file_handle) + handle_bytes)) 75 : : retval = -EFAULT; 76 : 3 : kfree(handle); 77 : 3 : return retval; 78 : : } 79 : : 80 : : /** 81 : : * sys_name_to_handle_at: convert name to handle 82 : : * @dfd: directory relative to which name is interpreted if not absolute 83 : : * @name: name that should be converted to handle. 84 : : * @handle: resulting file handle 85 : : * @mnt_id: mount id of the file system containing the file 86 : : * @flag: flag value to indicate whether to follow symlink or not 87 : : * 88 : : * @handle->handle_size indicate the space available to store the 89 : : * variable part of the file handle in bytes. If there is not 90 : : * enough space, the field is updated to return the minimum 91 : : * value required. 92 : : */ 93 : 3 : SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, 94 : : struct file_handle __user *, handle, int __user *, mnt_id, 95 : : int, flag) 96 : : { 97 : : struct path path; 98 : : int lookup_flags; 99 : : int err; 100 : : 101 : 3 : if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) 102 : : return -EINVAL; 103 : : 104 : 3 : lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; 105 : 3 : if (flag & AT_EMPTY_PATH) 106 : 3 : lookup_flags |= LOOKUP_EMPTY; 107 : 3 : err = user_path_at(dfd, name, lookup_flags, &path); 108 : 3 : if (!err) { 109 : 3 : err = do_sys_name_to_handle(&path, handle, mnt_id); 110 : 3 : path_put(&path); 111 : : } 112 : 3 : return err; 113 : : } 114 : : 115 : 0 : static struct vfsmount *get_vfsmount_from_fd(int fd) 116 : : { 117 : : struct vfsmount *mnt; 118 : : 119 : 0 : if (fd == AT_FDCWD) { 120 : 0 : struct fs_struct *fs = current->fs; 121 : : spin_lock(&fs->lock); 122 : 0 : mnt = mntget(fs->pwd.mnt); 123 : : spin_unlock(&fs->lock); 124 : : } else { 125 : 0 : struct fd f = fdget(fd); 126 : 0 : if (!f.file) 127 : : return ERR_PTR(-EBADF); 128 : 0 : mnt = mntget(f.file->f_path.mnt); 129 : : fdput(f); 130 : : } 131 : 0 : return mnt; 132 : : } 133 : : 134 : 0 : static int vfs_dentry_acceptable(void *context, struct dentry *dentry) 135 : : { 136 : 0 : return 1; 137 : : } 138 : : 139 : 0 : static int do_handle_to_path(int mountdirfd, struct file_handle *handle, 140 : : struct path *path) 141 : : { 142 : : int retval = 0; 143 : : int handle_dwords; 144 : : 145 : 0 : path->mnt = get_vfsmount_from_fd(mountdirfd); 146 : 0 : if (IS_ERR(path->mnt)) { 147 : : retval = PTR_ERR(path->mnt); 148 : 0 : goto out_err; 149 : : } 150 : : /* change the handle size to multiple of sizeof(u32) */ 151 : 0 : handle_dwords = handle->handle_bytes >> 2; 152 : 0 : path->dentry = exportfs_decode_fh(path->mnt, 153 : 0 : (struct fid *)handle->f_handle, 154 : : handle_dwords, handle->handle_type, 155 : : vfs_dentry_acceptable, NULL); 156 : 0 : if (IS_ERR(path->dentry)) { 157 : : retval = PTR_ERR(path->dentry); 158 : : goto out_mnt; 159 : : } 160 : : return 0; 161 : : out_mnt: 162 : 0 : mntput(path->mnt); 163 : : out_err: 164 : 0 : return retval; 165 : : } 166 : : 167 : 0 : static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, 168 : : struct path *path) 169 : : { 170 : : int retval = 0; 171 : : struct file_handle f_handle; 172 : : struct file_handle *handle = NULL; 173 : : 174 : : /* 175 : : * With handle we don't look at the execute bit on the 176 : : * the directory. Ideally we would like CAP_DAC_SEARCH. 177 : : * But we don't have that 178 : : */ 179 : 0 : if (!capable(CAP_DAC_READ_SEARCH)) { 180 : : retval = -EPERM; 181 : : goto out_err; 182 : : } 183 : 0 : if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) { 184 : : retval = -EFAULT; 185 : : goto out_err; 186 : : } 187 : 0 : if ((f_handle.handle_bytes > MAX_HANDLE_SZ) || 188 : : (f_handle.handle_bytes == 0)) { 189 : : retval = -EINVAL; 190 : : goto out_err; 191 : : } 192 : 0 : handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes, 193 : : GFP_KERNEL); 194 : 0 : if (!handle) { 195 : : retval = -ENOMEM; 196 : : goto out_err; 197 : : } 198 : : /* copy the full handle */ 199 : 0 : *handle = f_handle; 200 : 0 : if (copy_from_user(&handle->f_handle, 201 : 0 : &ufh->f_handle, 202 : 0 : f_handle.handle_bytes)) { 203 : : retval = -EFAULT; 204 : : goto out_handle; 205 : : } 206 : : 207 : 0 : retval = do_handle_to_path(mountdirfd, handle, path); 208 : : 209 : : out_handle: 210 : 0 : kfree(handle); 211 : : out_err: 212 : 0 : return retval; 213 : : } 214 : : 215 : 0 : static long do_handle_open(int mountdirfd, struct file_handle __user *ufh, 216 : : int open_flag) 217 : : { 218 : : long retval = 0; 219 : : struct path path; 220 : : struct file *file; 221 : : int fd; 222 : : 223 : 0 : retval = handle_to_path(mountdirfd, ufh, &path); 224 : 0 : if (retval) 225 : : return retval; 226 : : 227 : 0 : fd = get_unused_fd_flags(open_flag); 228 : 0 : if (fd < 0) { 229 : 0 : path_put(&path); 230 : 0 : return fd; 231 : : } 232 : 0 : file = file_open_root(path.dentry, path.mnt, "", open_flag, 0); 233 : 0 : if (IS_ERR(file)) { 234 : 0 : put_unused_fd(fd); 235 : : retval = PTR_ERR(file); 236 : : } else { 237 : : retval = fd; 238 : 0 : fsnotify_open(file); 239 : 0 : fd_install(fd, file); 240 : : } 241 : 0 : path_put(&path); 242 : 0 : return retval; 243 : : } 244 : : 245 : : /** 246 : : * sys_open_by_handle_at: Open the file handle 247 : : * @mountdirfd: directory file descriptor 248 : : * @handle: file handle to be opened 249 : : * @flags: open flags. 250 : : * 251 : : * @mountdirfd indicate the directory file descriptor 252 : : * of the mount point. file handle is decoded relative 253 : : * to the vfsmount pointed by the @mountdirfd. @flags 254 : : * value is same as the open(2) flags. 255 : : */ 256 : 0 : SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, 257 : : struct file_handle __user *, handle, 258 : : int, flags) 259 : : { 260 : : long ret; 261 : : 262 : : if (force_o_largefile()) 263 : : flags |= O_LARGEFILE; 264 : : 265 : 0 : ret = do_handle_open(mountdirfd, handle, flags); 266 : : return ret; 267 : : } 268 : : 269 : : #ifdef CONFIG_COMPAT 270 : : /* 271 : : * Exactly like fs/open.c:sys_open_by_handle_at(), except that it 272 : : * doesn't set the O_LARGEFILE flag. 273 : : */ 274 : : COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, 275 : : struct file_handle __user *, handle, int, flags) 276 : : { 277 : : return do_handle_open(mountdirfd, handle, flags); 278 : : } 279 : : #endif