// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* Copyright (c) 2022-2024 Oracle.
* All rights reserved.
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_shared.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_inode.h"
#include "xfs_error.h"
#include "xfs_trace.h"
#include "xfs_trans.h"
#include "xfs_da_format.h"
#include "xfs_da_btree.h"
#include "xfs_attr.h"
#include "xfs_ioctl.h"
#include "xfs_parent.h"
#include "xfs_handle.h"
#include "xfs_health.h"
#include "xfs_icache.h"
#include "xfs_export.h"
#include "xfs_xattr.h"
#include "xfs_acl.h"
#include <linux/namei.h>
static inline size_t
xfs_filehandle_fid_len(void)
{
struct xfs_handle *handle = NULL;
return sizeof(struct xfs_fid) - sizeof(handle->ha_fid.fid_len);
}
static inline size_t
xfs_filehandle_init(
struct xfs_mount *mp,
xfs_ino_t ino,
uint32_t gen,
struct xfs_handle *handle)
{
memcpy(&handle->ha_fsid, mp->m_fixedfsid, sizeof(struct xfs_fsid));
handle->ha_fid.fid_len = xfs_filehandle_fid_len();
handle->ha_fid.fid_pad = 0;
handle->ha_fid.fid_gen = gen;
handle->ha_fid.fid_ino = ino;
return sizeof(struct xfs_handle);
}
static inline size_t
xfs_fshandle_init(
struct xfs_mount *mp,
struct xfs_handle *handle)
{
memcpy(&handle->ha_fsid, mp->m_fixedfsid, sizeof(struct xfs_fsid));
memset(&handle->ha_fid, 0, sizeof(handle->ha_fid));
return sizeof(struct xfs_fsid);
}
/*
* xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
* a file or fs handle.
*
* XFS_IOC_PATH_TO_FSHANDLE
* returns fs handle for a mount point or path within that mount point
* XFS_IOC_FD_TO_HANDLE
* returns full handle for a FD opened in user space
* XFS_IOC_PATH_TO_HANDLE
* returns full handle for a path
*/
int
xfs_find_handle(
unsigned int cmd,
xfs_fsop_handlereq_t *hreq)
{
int hsize;
xfs_handle_t handle;
struct inode *inode;
struct path path;
int error;
struct xfs_inode *ip;
if (cmd == XFS_IOC_FD_TO_HANDLE) {
CLASS(fd, f)(hreq->fd);
if (fd_empty(f))
return -EBADF;
path = fd_file(f)->f_path;
path_get(&path);
} else {
error = user_path_at(AT_FDCWD, hreq->path, 0, &path);
if (error)
return error;
}
inode = d_inode(path.dentry);
ip = XFS_I(inode);
/*
* We can only generate handles for inodes residing on a XFS filesystem,
* and only for regular files, directories or symbolic links.
*/
error = -EINVAL;
if (inode->i_sb->s_magic != XFS_SB_MAGIC)
goto out_put;
error = -EBADF;
if (!S_ISREG(inode->i_mode) &&
!S_ISDIR(inode->i_mode) &&
!S_ISLNK(inode->i_mode))
goto out_put;
memcpy(&handle.ha_fsid, ip->i_mount->m_fixedfsid, sizeof(xfs_fsid_t));
if (cmd == XFS_IOC_PATH_TO_FSHANDLE)
hsize = xfs_fshandle_init(ip->i_mount, &handle);
else
hsize = xfs_filehandle_init(ip->i_mount, ip->i_ino,
inode->i_generation, &handle);
error = -EFAULT;
if (copy_to_user(hreq->ohandle, &handle, hsize) ||
copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
goto out_put;
error = 0;
out_put:
path_put(&path);
return error;
}
/*
* No need to do permission checks on the various pathname components
* as the handle operations are privileged.
*/
STATIC int
xfs_handle_acceptable(
void *context,
struct dentry *dentry)
{
return 1;
}
/* Convert handle already copied to kernel space into a dentry. */
static struct dentry *
xfs_khandle_to_dentry(
struct file *file,
struct xfs_handle *handle)
{
struct xfs_fid64 fid = {
.ino = handle->ha_fid.fid_ino,
.gen = handle->ha_fid.fid_gen,
};
/*
* Only allow handle opens under a directory.
*/
if (!S_ISDIR(file_inode(file)->i_mode))
return ERR_PTR(-ENOTDIR);
if (handle->ha_fid.fid_len != xfs_filehandle_fid_len())
return ERR_PTR(-EINVAL);
return exportfs_decode_fh(file->f_path.mnt, (struct fid *)&fid, 3,
FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG,
xfs_handle_acceptable, NULL);
}
/* Convert handle already copied to kernel space into an xfs_inode. */
static struct xfs_inode *
xfs_khandle_to_inode