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