18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 38c2ecf20Sopenharmony_ci#include <linux/slab.h> 48c2ecf20Sopenharmony_ci#include <linux/fs.h> 58c2ecf20Sopenharmony_ci#include <linux/file.h> 68c2ecf20Sopenharmony_ci#include <linux/mount.h> 78c2ecf20Sopenharmony_ci#include <linux/namei.h> 88c2ecf20Sopenharmony_ci#include <linux/exportfs.h> 98c2ecf20Sopenharmony_ci#include <linux/fs_struct.h> 108c2ecf20Sopenharmony_ci#include <linux/fsnotify.h> 118c2ecf20Sopenharmony_ci#include <linux/personality.h> 128c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 138c2ecf20Sopenharmony_ci#include <linux/compat.h> 148c2ecf20Sopenharmony_ci#include "internal.h" 158c2ecf20Sopenharmony_ci#include "mount.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic long do_sys_name_to_handle(struct path *path, 188c2ecf20Sopenharmony_ci struct file_handle __user *ufh, 198c2ecf20Sopenharmony_ci int __user *mnt_id) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci long retval; 228c2ecf20Sopenharmony_ci struct file_handle f_handle; 238c2ecf20Sopenharmony_ci int handle_dwords, handle_bytes; 248c2ecf20Sopenharmony_ci struct file_handle *handle = NULL; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci /* 278c2ecf20Sopenharmony_ci * We need to make sure whether the file system 288c2ecf20Sopenharmony_ci * support decoding of the file handle 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci if (!path->dentry->d_sb->s_export_op || 318c2ecf20Sopenharmony_ci !path->dentry->d_sb->s_export_op->fh_to_dentry) 328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) 358c2ecf20Sopenharmony_ci return -EFAULT; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (f_handle.handle_bytes > MAX_HANDLE_SZ) 388c2ecf20Sopenharmony_ci return -EINVAL; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci handle = kzalloc(sizeof(struct file_handle) + f_handle.handle_bytes, 418c2ecf20Sopenharmony_ci GFP_KERNEL); 428c2ecf20Sopenharmony_ci if (!handle) 438c2ecf20Sopenharmony_ci return -ENOMEM; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* convert handle size to multiple of sizeof(u32) */ 468c2ecf20Sopenharmony_ci handle_dwords = f_handle.handle_bytes >> 2; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* we ask for a non connected handle */ 498c2ecf20Sopenharmony_ci retval = exportfs_encode_fh(path->dentry, 508c2ecf20Sopenharmony_ci (struct fid *)handle->f_handle, 518c2ecf20Sopenharmony_ci &handle_dwords, 0); 528c2ecf20Sopenharmony_ci handle->handle_type = retval; 538c2ecf20Sopenharmony_ci /* convert handle size to bytes */ 548c2ecf20Sopenharmony_ci handle_bytes = handle_dwords * sizeof(u32); 558c2ecf20Sopenharmony_ci handle->handle_bytes = handle_bytes; 568c2ecf20Sopenharmony_ci if ((handle->handle_bytes > f_handle.handle_bytes) || 578c2ecf20Sopenharmony_ci (retval == FILEID_INVALID) || (retval == -ENOSPC)) { 588c2ecf20Sopenharmony_ci /* As per old exportfs_encode_fh documentation 598c2ecf20Sopenharmony_ci * we could return ENOSPC to indicate overflow 608c2ecf20Sopenharmony_ci * But file system returned 255 always. So handle 618c2ecf20Sopenharmony_ci * both the values 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci /* 648c2ecf20Sopenharmony_ci * set the handle size to zero so we copy only 658c2ecf20Sopenharmony_ci * non variable part of the file_handle 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci handle_bytes = 0; 688c2ecf20Sopenharmony_ci retval = -EOVERFLOW; 698c2ecf20Sopenharmony_ci } else 708c2ecf20Sopenharmony_ci retval = 0; 718c2ecf20Sopenharmony_ci /* copy the mount id */ 728c2ecf20Sopenharmony_ci if (put_user(real_mount(path->mnt)->mnt_id, mnt_id) || 738c2ecf20Sopenharmony_ci copy_to_user(ufh, handle, 748c2ecf20Sopenharmony_ci sizeof(struct file_handle) + handle_bytes)) 758c2ecf20Sopenharmony_ci retval = -EFAULT; 768c2ecf20Sopenharmony_ci kfree(handle); 778c2ecf20Sopenharmony_ci return retval; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * sys_name_to_handle_at: convert name to handle 828c2ecf20Sopenharmony_ci * @dfd: directory relative to which name is interpreted if not absolute 838c2ecf20Sopenharmony_ci * @name: name that should be converted to handle. 848c2ecf20Sopenharmony_ci * @handle: resulting file handle 858c2ecf20Sopenharmony_ci * @mnt_id: mount id of the file system containing the file 868c2ecf20Sopenharmony_ci * @flag: flag value to indicate whether to follow symlink or not 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * @handle->handle_size indicate the space available to store the 898c2ecf20Sopenharmony_ci * variable part of the file handle in bytes. If there is not 908c2ecf20Sopenharmony_ci * enough space, the field is updated to return the minimum 918c2ecf20Sopenharmony_ci * value required. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ciSYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name, 948c2ecf20Sopenharmony_ci struct file_handle __user *, handle, int __user *, mnt_id, 958c2ecf20Sopenharmony_ci int, flag) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct path path; 988c2ecf20Sopenharmony_ci int lookup_flags; 998c2ecf20Sopenharmony_ci int err; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if ((flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0) 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0; 1058c2ecf20Sopenharmony_ci if (flag & AT_EMPTY_PATH) 1068c2ecf20Sopenharmony_ci lookup_flags |= LOOKUP_EMPTY; 1078c2ecf20Sopenharmony_ci err = user_path_at(dfd, name, lookup_flags, &path); 1088c2ecf20Sopenharmony_ci if (!err) { 1098c2ecf20Sopenharmony_ci err = do_sys_name_to_handle(&path, handle, mnt_id); 1108c2ecf20Sopenharmony_ci path_put(&path); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci return err; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct vfsmount *get_vfsmount_from_fd(int fd) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct vfsmount *mnt; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (fd == AT_FDCWD) { 1208c2ecf20Sopenharmony_ci struct fs_struct *fs = current->fs; 1218c2ecf20Sopenharmony_ci spin_lock(&fs->lock); 1228c2ecf20Sopenharmony_ci mnt = mntget(fs->pwd.mnt); 1238c2ecf20Sopenharmony_ci spin_unlock(&fs->lock); 1248c2ecf20Sopenharmony_ci } else { 1258c2ecf20Sopenharmony_ci struct fd f = fdget(fd); 1268c2ecf20Sopenharmony_ci if (!f.file) 1278c2ecf20Sopenharmony_ci return ERR_PTR(-EBADF); 1288c2ecf20Sopenharmony_ci mnt = mntget(f.file->f_path.mnt); 1298c2ecf20Sopenharmony_ci fdput(f); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci return mnt; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int vfs_dentry_acceptable(void *context, struct dentry *dentry) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return 1; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int do_handle_to_path(int mountdirfd, struct file_handle *handle, 1408c2ecf20Sopenharmony_ci struct path *path) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int retval = 0; 1438c2ecf20Sopenharmony_ci int handle_dwords; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci path->mnt = get_vfsmount_from_fd(mountdirfd); 1468c2ecf20Sopenharmony_ci if (IS_ERR(path->mnt)) { 1478c2ecf20Sopenharmony_ci retval = PTR_ERR(path->mnt); 1488c2ecf20Sopenharmony_ci goto out_err; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci /* change the handle size to multiple of sizeof(u32) */ 1518c2ecf20Sopenharmony_ci handle_dwords = handle->handle_bytes >> 2; 1528c2ecf20Sopenharmony_ci path->dentry = exportfs_decode_fh(path->mnt, 1538c2ecf20Sopenharmony_ci (struct fid *)handle->f_handle, 1548c2ecf20Sopenharmony_ci handle_dwords, handle->handle_type, 1558c2ecf20Sopenharmony_ci vfs_dentry_acceptable, NULL); 1568c2ecf20Sopenharmony_ci if (IS_ERR(path->dentry)) { 1578c2ecf20Sopenharmony_ci retval = PTR_ERR(path->dentry); 1588c2ecf20Sopenharmony_ci goto out_mnt; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ciout_mnt: 1628c2ecf20Sopenharmony_ci mntput(path->mnt); 1638c2ecf20Sopenharmony_ciout_err: 1648c2ecf20Sopenharmony_ci return retval; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int handle_to_path(int mountdirfd, struct file_handle __user *ufh, 1688c2ecf20Sopenharmony_ci struct path *path) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int retval = 0; 1718c2ecf20Sopenharmony_ci struct file_handle f_handle; 1728c2ecf20Sopenharmony_ci struct file_handle *handle = NULL; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * With handle we don't look at the execute bit on the 1768c2ecf20Sopenharmony_ci * the directory. Ideally we would like CAP_DAC_SEARCH. 1778c2ecf20Sopenharmony_ci * But we don't have that 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci if (!capable(CAP_DAC_READ_SEARCH)) { 1808c2ecf20Sopenharmony_ci retval = -EPERM; 1818c2ecf20Sopenharmony_ci goto out_err; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) { 1848c2ecf20Sopenharmony_ci retval = -EFAULT; 1858c2ecf20Sopenharmony_ci goto out_err; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci if ((f_handle.handle_bytes > MAX_HANDLE_SZ) || 1888c2ecf20Sopenharmony_ci (f_handle.handle_bytes == 0)) { 1898c2ecf20Sopenharmony_ci retval = -EINVAL; 1908c2ecf20Sopenharmony_ci goto out_err; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes, 1938c2ecf20Sopenharmony_ci GFP_KERNEL); 1948c2ecf20Sopenharmony_ci if (!handle) { 1958c2ecf20Sopenharmony_ci retval = -ENOMEM; 1968c2ecf20Sopenharmony_ci goto out_err; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci /* copy the full handle */ 1998c2ecf20Sopenharmony_ci *handle = f_handle; 2008c2ecf20Sopenharmony_ci if (copy_from_user(&handle->f_handle, 2018c2ecf20Sopenharmony_ci &ufh->f_handle, 2028c2ecf20Sopenharmony_ci f_handle.handle_bytes)) { 2038c2ecf20Sopenharmony_ci retval = -EFAULT; 2048c2ecf20Sopenharmony_ci goto out_handle; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci retval = do_handle_to_path(mountdirfd, handle, path); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciout_handle: 2108c2ecf20Sopenharmony_ci kfree(handle); 2118c2ecf20Sopenharmony_ciout_err: 2128c2ecf20Sopenharmony_ci return retval; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic long do_handle_open(int mountdirfd, struct file_handle __user *ufh, 2168c2ecf20Sopenharmony_ci int open_flag) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci long retval = 0; 2198c2ecf20Sopenharmony_ci struct path path; 2208c2ecf20Sopenharmony_ci struct file *file; 2218c2ecf20Sopenharmony_ci int fd; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci retval = handle_to_path(mountdirfd, ufh, &path); 2248c2ecf20Sopenharmony_ci if (retval) 2258c2ecf20Sopenharmony_ci return retval; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci fd = get_unused_fd_flags(open_flag); 2288c2ecf20Sopenharmony_ci if (fd < 0) { 2298c2ecf20Sopenharmony_ci path_put(&path); 2308c2ecf20Sopenharmony_ci return fd; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci file = file_open_root(&path, "", open_flag, 0); 2338c2ecf20Sopenharmony_ci if (IS_ERR(file)) { 2348c2ecf20Sopenharmony_ci put_unused_fd(fd); 2358c2ecf20Sopenharmony_ci retval = PTR_ERR(file); 2368c2ecf20Sopenharmony_ci } else { 2378c2ecf20Sopenharmony_ci retval = fd; 2388c2ecf20Sopenharmony_ci fsnotify_open(file); 2398c2ecf20Sopenharmony_ci fd_install(fd, file); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci path_put(&path); 2428c2ecf20Sopenharmony_ci return retval; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/** 2468c2ecf20Sopenharmony_ci * sys_open_by_handle_at: Open the file handle 2478c2ecf20Sopenharmony_ci * @mountdirfd: directory file descriptor 2488c2ecf20Sopenharmony_ci * @handle: file handle to be opened 2498c2ecf20Sopenharmony_ci * @flags: open flags. 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * @mountdirfd indicate the directory file descriptor 2528c2ecf20Sopenharmony_ci * of the mount point. file handle is decoded relative 2538c2ecf20Sopenharmony_ci * to the vfsmount pointed by the @mountdirfd. @flags 2548c2ecf20Sopenharmony_ci * value is same as the open(2) flags. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, 2578c2ecf20Sopenharmony_ci struct file_handle __user *, handle, 2588c2ecf20Sopenharmony_ci int, flags) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci long ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (force_o_largefile()) 2638c2ecf20Sopenharmony_ci flags |= O_LARGEFILE; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = do_handle_open(mountdirfd, handle, flags); 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * Exactly like fs/open.c:sys_open_by_handle_at(), except that it 2728c2ecf20Sopenharmony_ci * doesn't set the O_LARGEFILE flag. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd, 2758c2ecf20Sopenharmony_ci struct file_handle __user *, handle, int, flags) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci return do_handle_open(mountdirfd, handle, flags); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci#endif 280