162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2004-2005 Silicon Graphics, Inc.
462306a36Sopenharmony_ci * All Rights Reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/mount.h>
762306a36Sopenharmony_ci#include <linux/fsmap.h>
862306a36Sopenharmony_ci#include "xfs.h"
962306a36Sopenharmony_ci#include "xfs_fs.h"
1062306a36Sopenharmony_ci#include "xfs_shared.h"
1162306a36Sopenharmony_ci#include "xfs_format.h"
1262306a36Sopenharmony_ci#include "xfs_log_format.h"
1362306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1462306a36Sopenharmony_ci#include "xfs_mount.h"
1562306a36Sopenharmony_ci#include "xfs_inode.h"
1662306a36Sopenharmony_ci#include "xfs_iwalk.h"
1762306a36Sopenharmony_ci#include "xfs_itable.h"
1862306a36Sopenharmony_ci#include "xfs_fsops.h"
1962306a36Sopenharmony_ci#include "xfs_rtalloc.h"
2062306a36Sopenharmony_ci#include "xfs_da_format.h"
2162306a36Sopenharmony_ci#include "xfs_da_btree.h"
2262306a36Sopenharmony_ci#include "xfs_attr.h"
2362306a36Sopenharmony_ci#include "xfs_ioctl.h"
2462306a36Sopenharmony_ci#include "xfs_ioctl32.h"
2562306a36Sopenharmony_ci#include "xfs_trace.h"
2662306a36Sopenharmony_ci#include "xfs_sb.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define  _NATIVE_IOC(cmd, type) \
2962306a36Sopenharmony_ci	  _IOC(_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), sizeof(type))
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifdef BROKEN_X86_ALIGNMENT
3262306a36Sopenharmony_ciSTATIC int
3362306a36Sopenharmony_cixfs_compat_ioc_fsgeometry_v1(
3462306a36Sopenharmony_ci	struct xfs_mount	  *mp,
3562306a36Sopenharmony_ci	compat_xfs_fsop_geom_v1_t __user *arg32)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct xfs_fsop_geom	  fsgeo;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	xfs_fs_geometry(mp, &fsgeo, 3);
4062306a36Sopenharmony_ci	/* The 32-bit variant simply has some padding at the end */
4162306a36Sopenharmony_ci	if (copy_to_user(arg32, &fsgeo, sizeof(struct compat_xfs_fsop_geom_v1)))
4262306a36Sopenharmony_ci		return -EFAULT;
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciSTATIC int
4762306a36Sopenharmony_cixfs_compat_growfs_data_copyin(
4862306a36Sopenharmony_ci	struct xfs_growfs_data	 *in,
4962306a36Sopenharmony_ci	compat_xfs_growfs_data_t __user *arg32)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	if (get_user(in->newblocks, &arg32->newblocks) ||
5262306a36Sopenharmony_ci	    get_user(in->imaxpct,   &arg32->imaxpct))
5362306a36Sopenharmony_ci		return -EFAULT;
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ciSTATIC int
5862306a36Sopenharmony_cixfs_compat_growfs_rt_copyin(
5962306a36Sopenharmony_ci	struct xfs_growfs_rt	 *in,
6062306a36Sopenharmony_ci	compat_xfs_growfs_rt_t	__user *arg32)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	if (get_user(in->newblocks, &arg32->newblocks) ||
6362306a36Sopenharmony_ci	    get_user(in->extsize,   &arg32->extsize))
6462306a36Sopenharmony_ci		return -EFAULT;
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciSTATIC int
6962306a36Sopenharmony_cixfs_fsinumbers_fmt_compat(
7062306a36Sopenharmony_ci	struct xfs_ibulk		*breq,
7162306a36Sopenharmony_ci	const struct xfs_inumbers	*ig)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct compat_xfs_inogrp __user	*p32 = breq->ubuffer;
7462306a36Sopenharmony_ci	struct xfs_inogrp		ig1;
7562306a36Sopenharmony_ci	struct xfs_inogrp		*igrp = &ig1;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	xfs_inumbers_to_inogrp(&ig1, ig);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (put_user(igrp->xi_startino,   &p32->xi_startino) ||
8062306a36Sopenharmony_ci	    put_user(igrp->xi_alloccount, &p32->xi_alloccount) ||
8162306a36Sopenharmony_ci	    put_user(igrp->xi_allocmask,  &p32->xi_allocmask))
8262306a36Sopenharmony_ci		return -EFAULT;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_inogrp));
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#else
8862306a36Sopenharmony_ci#define xfs_fsinumbers_fmt_compat xfs_fsinumbers_fmt
8962306a36Sopenharmony_ci#endif	/* BROKEN_X86_ALIGNMENT */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciSTATIC int
9262306a36Sopenharmony_cixfs_ioctl32_bstime_copyin(
9362306a36Sopenharmony_ci	xfs_bstime_t		*bstime,
9462306a36Sopenharmony_ci	compat_xfs_bstime_t	__user *bstime32)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	old_time32_t		sec32;	/* tv_sec differs on 64 vs. 32 */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (get_user(sec32,		&bstime32->tv_sec)	||
9962306a36Sopenharmony_ci	    get_user(bstime->tv_nsec,	&bstime32->tv_nsec))
10062306a36Sopenharmony_ci		return -EFAULT;
10162306a36Sopenharmony_ci	bstime->tv_sec = sec32;
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/*
10662306a36Sopenharmony_ci * struct xfs_bstat has differing alignment on intel, & bstime_t sizes
10762306a36Sopenharmony_ci * everywhere
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_ciSTATIC int
11062306a36Sopenharmony_cixfs_ioctl32_bstat_copyin(
11162306a36Sopenharmony_ci	struct xfs_bstat		*bstat,
11262306a36Sopenharmony_ci	struct compat_xfs_bstat	__user	*bstat32)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (get_user(bstat->bs_ino,	&bstat32->bs_ino)	||
11562306a36Sopenharmony_ci	    get_user(bstat->bs_mode,	&bstat32->bs_mode)	||
11662306a36Sopenharmony_ci	    get_user(bstat->bs_nlink,	&bstat32->bs_nlink)	||
11762306a36Sopenharmony_ci	    get_user(bstat->bs_uid,	&bstat32->bs_uid)	||
11862306a36Sopenharmony_ci	    get_user(bstat->bs_gid,	&bstat32->bs_gid)	||
11962306a36Sopenharmony_ci	    get_user(bstat->bs_rdev,	&bstat32->bs_rdev)	||
12062306a36Sopenharmony_ci	    get_user(bstat->bs_blksize,	&bstat32->bs_blksize)	||
12162306a36Sopenharmony_ci	    get_user(bstat->bs_size,	&bstat32->bs_size)	||
12262306a36Sopenharmony_ci	    xfs_ioctl32_bstime_copyin(&bstat->bs_atime, &bstat32->bs_atime) ||
12362306a36Sopenharmony_ci	    xfs_ioctl32_bstime_copyin(&bstat->bs_mtime, &bstat32->bs_mtime) ||
12462306a36Sopenharmony_ci	    xfs_ioctl32_bstime_copyin(&bstat->bs_ctime, &bstat32->bs_ctime) ||
12562306a36Sopenharmony_ci	    get_user(bstat->bs_blocks,	&bstat32->bs_size)	||
12662306a36Sopenharmony_ci	    get_user(bstat->bs_xflags,	&bstat32->bs_size)	||
12762306a36Sopenharmony_ci	    get_user(bstat->bs_extsize,	&bstat32->bs_extsize)	||
12862306a36Sopenharmony_ci	    get_user(bstat->bs_extents,	&bstat32->bs_extents)	||
12962306a36Sopenharmony_ci	    get_user(bstat->bs_gen,	&bstat32->bs_gen)	||
13062306a36Sopenharmony_ci	    get_user(bstat->bs_projid_lo, &bstat32->bs_projid_lo) ||
13162306a36Sopenharmony_ci	    get_user(bstat->bs_projid_hi, &bstat32->bs_projid_hi) ||
13262306a36Sopenharmony_ci	    get_user(bstat->bs_forkoff,	&bstat32->bs_forkoff)	||
13362306a36Sopenharmony_ci	    get_user(bstat->bs_dmevmask, &bstat32->bs_dmevmask)	||
13462306a36Sopenharmony_ci	    get_user(bstat->bs_dmstate,	&bstat32->bs_dmstate)	||
13562306a36Sopenharmony_ci	    get_user(bstat->bs_aextents, &bstat32->bs_aextents))
13662306a36Sopenharmony_ci		return -EFAULT;
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/* XFS_IOC_FSBULKSTAT and friends */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciSTATIC int
14362306a36Sopenharmony_cixfs_bstime_store_compat(
14462306a36Sopenharmony_ci	compat_xfs_bstime_t	__user *p32,
14562306a36Sopenharmony_ci	const xfs_bstime_t	*p)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	__s32			sec32;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	sec32 = p->tv_sec;
15062306a36Sopenharmony_ci	if (put_user(sec32, &p32->tv_sec) ||
15162306a36Sopenharmony_ci	    put_user(p->tv_nsec, &p32->tv_nsec))
15262306a36Sopenharmony_ci		return -EFAULT;
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* Return 0 on success or positive error (to xfs_bulkstat()) */
15762306a36Sopenharmony_ciSTATIC int
15862306a36Sopenharmony_cixfs_fsbulkstat_one_fmt_compat(
15962306a36Sopenharmony_ci	struct xfs_ibulk		*breq,
16062306a36Sopenharmony_ci	const struct xfs_bulkstat	*bstat)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct compat_xfs_bstat	__user	*p32 = breq->ubuffer;
16362306a36Sopenharmony_ci	struct xfs_bstat		bs1;
16462306a36Sopenharmony_ci	struct xfs_bstat		*buffer = &bs1;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (put_user(buffer->bs_ino,	  &p32->bs_ino)		||
16962306a36Sopenharmony_ci	    put_user(buffer->bs_mode,	  &p32->bs_mode)	||
17062306a36Sopenharmony_ci	    put_user(buffer->bs_nlink,	  &p32->bs_nlink)	||
17162306a36Sopenharmony_ci	    put_user(buffer->bs_uid,	  &p32->bs_uid)		||
17262306a36Sopenharmony_ci	    put_user(buffer->bs_gid,	  &p32->bs_gid)		||
17362306a36Sopenharmony_ci	    put_user(buffer->bs_rdev,	  &p32->bs_rdev)	||
17462306a36Sopenharmony_ci	    put_user(buffer->bs_blksize,  &p32->bs_blksize)	||
17562306a36Sopenharmony_ci	    put_user(buffer->bs_size,	  &p32->bs_size)	||
17662306a36Sopenharmony_ci	    xfs_bstime_store_compat(&p32->bs_atime, &buffer->bs_atime) ||
17762306a36Sopenharmony_ci	    xfs_bstime_store_compat(&p32->bs_mtime, &buffer->bs_mtime) ||
17862306a36Sopenharmony_ci	    xfs_bstime_store_compat(&p32->bs_ctime, &buffer->bs_ctime) ||
17962306a36Sopenharmony_ci	    put_user(buffer->bs_blocks,	  &p32->bs_blocks)	||
18062306a36Sopenharmony_ci	    put_user(buffer->bs_xflags,	  &p32->bs_xflags)	||
18162306a36Sopenharmony_ci	    put_user(buffer->bs_extsize,  &p32->bs_extsize)	||
18262306a36Sopenharmony_ci	    put_user(buffer->bs_extents,  &p32->bs_extents)	||
18362306a36Sopenharmony_ci	    put_user(buffer->bs_gen,	  &p32->bs_gen)		||
18462306a36Sopenharmony_ci	    put_user(buffer->bs_projid,	  &p32->bs_projid)	||
18562306a36Sopenharmony_ci	    put_user(buffer->bs_projid_hi,	&p32->bs_projid_hi)	||
18662306a36Sopenharmony_ci	    put_user(buffer->bs_forkoff,  &p32->bs_forkoff)	||
18762306a36Sopenharmony_ci	    put_user(buffer->bs_dmevmask, &p32->bs_dmevmask)	||
18862306a36Sopenharmony_ci	    put_user(buffer->bs_dmstate,  &p32->bs_dmstate)	||
18962306a36Sopenharmony_ci	    put_user(buffer->bs_aextents, &p32->bs_aextents))
19062306a36Sopenharmony_ci		return -EFAULT;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_bstat));
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/* copied from xfs_ioctl.c */
19662306a36Sopenharmony_ciSTATIC int
19762306a36Sopenharmony_cixfs_compat_ioc_fsbulkstat(
19862306a36Sopenharmony_ci	struct file		*file,
19962306a36Sopenharmony_ci	unsigned int		  cmd,
20062306a36Sopenharmony_ci	struct compat_xfs_fsop_bulkreq __user *p32)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct xfs_mount	*mp = XFS_I(file_inode(file))->i_mount;
20362306a36Sopenharmony_ci	u32			addr;
20462306a36Sopenharmony_ci	struct xfs_fsop_bulkreq	bulkreq;
20562306a36Sopenharmony_ci	struct xfs_ibulk	breq = {
20662306a36Sopenharmony_ci		.mp		= mp,
20762306a36Sopenharmony_ci		.idmap		= file_mnt_idmap(file),
20862306a36Sopenharmony_ci		.ocount		= 0,
20962306a36Sopenharmony_ci	};
21062306a36Sopenharmony_ci	xfs_ino_t		lastino;
21162306a36Sopenharmony_ci	int			error;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/*
21462306a36Sopenharmony_ci	 * Output structure handling functions.  Depending on the command,
21562306a36Sopenharmony_ci	 * either the xfs_bstat and xfs_inogrp structures are written out
21662306a36Sopenharmony_ci	 * to userpace memory via bulkreq.ubuffer.  Normally the compat
21762306a36Sopenharmony_ci	 * functions and structure size are the correct ones to use ...
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	inumbers_fmt_pf		inumbers_func = xfs_fsinumbers_fmt_compat;
22062306a36Sopenharmony_ci	bulkstat_one_fmt_pf	bs_one_func = xfs_fsbulkstat_one_fmt_compat;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci#ifdef CONFIG_X86_X32_ABI
22362306a36Sopenharmony_ci	if (in_x32_syscall()) {
22462306a36Sopenharmony_ci		/*
22562306a36Sopenharmony_ci		 * ... but on x32 the input xfs_fsop_bulkreq has pointers
22662306a36Sopenharmony_ci		 * which must be handled in the "compat" (32-bit) way, while
22762306a36Sopenharmony_ci		 * the xfs_bstat and xfs_inogrp structures follow native 64-
22862306a36Sopenharmony_ci		 * bit layout convention.  So adjust accordingly, otherwise
22962306a36Sopenharmony_ci		 * the data written out in compat layout will not match what
23062306a36Sopenharmony_ci		 * x32 userspace expects.
23162306a36Sopenharmony_ci		 */
23262306a36Sopenharmony_ci		inumbers_func = xfs_fsinumbers_fmt;
23362306a36Sopenharmony_ci		bs_one_func = xfs_fsbulkstat_one_fmt;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci#endif
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* done = 1 if there are more stats to get and if bulkstat */
23862306a36Sopenharmony_ci	/* should be called again (unused here, but used in dmapi) */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
24162306a36Sopenharmony_ci		return -EPERM;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (xfs_is_shutdown(mp))
24462306a36Sopenharmony_ci		return -EIO;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (get_user(addr, &p32->lastip))
24762306a36Sopenharmony_ci		return -EFAULT;
24862306a36Sopenharmony_ci	bulkreq.lastip = compat_ptr(addr);
24962306a36Sopenharmony_ci	if (get_user(bulkreq.icount, &p32->icount) ||
25062306a36Sopenharmony_ci	    get_user(addr, &p32->ubuffer))
25162306a36Sopenharmony_ci		return -EFAULT;
25262306a36Sopenharmony_ci	bulkreq.ubuffer = compat_ptr(addr);
25362306a36Sopenharmony_ci	if (get_user(addr, &p32->ocount))
25462306a36Sopenharmony_ci		return -EFAULT;
25562306a36Sopenharmony_ci	bulkreq.ocount = compat_ptr(addr);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64)))
25862306a36Sopenharmony_ci		return -EFAULT;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (bulkreq.icount <= 0)
26162306a36Sopenharmony_ci		return -EINVAL;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (bulkreq.ubuffer == NULL)
26462306a36Sopenharmony_ci		return -EINVAL;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	breq.ubuffer = bulkreq.ubuffer;
26762306a36Sopenharmony_ci	breq.icount = bulkreq.icount;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/*
27062306a36Sopenharmony_ci	 * FSBULKSTAT_SINGLE expects that *lastip contains the inode number
27162306a36Sopenharmony_ci	 * that we want to stat.  However, FSINUMBERS and FSBULKSTAT expect
27262306a36Sopenharmony_ci	 * that *lastip contains either zero or the number of the last inode to
27362306a36Sopenharmony_ci	 * be examined by the previous call and return results starting with
27462306a36Sopenharmony_ci	 * the next inode after that.  The new bulk request back end functions
27562306a36Sopenharmony_ci	 * take the inode to start with, so we have to compute the startino
27662306a36Sopenharmony_ci	 * parameter from lastino to maintain correct function.  lastino == 0
27762306a36Sopenharmony_ci	 * is a special case because it has traditionally meant "first inode
27862306a36Sopenharmony_ci	 * in filesystem".
27962306a36Sopenharmony_ci	 */
28062306a36Sopenharmony_ci	if (cmd == XFS_IOC_FSINUMBERS_32) {
28162306a36Sopenharmony_ci		breq.startino = lastino ? lastino + 1 : 0;
28262306a36Sopenharmony_ci		error = xfs_inumbers(&breq, inumbers_func);
28362306a36Sopenharmony_ci		lastino = breq.startino - 1;
28462306a36Sopenharmony_ci	} else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32) {
28562306a36Sopenharmony_ci		breq.startino = lastino;
28662306a36Sopenharmony_ci		breq.icount = 1;
28762306a36Sopenharmony_ci		error = xfs_bulkstat_one(&breq, bs_one_func);
28862306a36Sopenharmony_ci		lastino = breq.startino;
28962306a36Sopenharmony_ci	} else if (cmd == XFS_IOC_FSBULKSTAT_32) {
29062306a36Sopenharmony_ci		breq.startino = lastino ? lastino + 1 : 0;
29162306a36Sopenharmony_ci		error = xfs_bulkstat(&breq, bs_one_func);
29262306a36Sopenharmony_ci		lastino = breq.startino - 1;
29362306a36Sopenharmony_ci	} else {
29462306a36Sopenharmony_ci		error = -EINVAL;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci	if (error)
29762306a36Sopenharmony_ci		return error;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (bulkreq.lastip != NULL &&
30062306a36Sopenharmony_ci	    copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t)))
30162306a36Sopenharmony_ci		return -EFAULT;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (bulkreq.ocount != NULL &&
30462306a36Sopenharmony_ci	    copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32)))
30562306a36Sopenharmony_ci		return -EFAULT;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ciSTATIC int
31162306a36Sopenharmony_cixfs_compat_handlereq_copyin(
31262306a36Sopenharmony_ci	xfs_fsop_handlereq_t		*hreq,
31362306a36Sopenharmony_ci	compat_xfs_fsop_handlereq_t	__user *arg32)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	compat_xfs_fsop_handlereq_t	hreq32;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (copy_from_user(&hreq32, arg32, sizeof(compat_xfs_fsop_handlereq_t)))
31862306a36Sopenharmony_ci		return -EFAULT;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	hreq->fd = hreq32.fd;
32162306a36Sopenharmony_ci	hreq->path = compat_ptr(hreq32.path);
32262306a36Sopenharmony_ci	hreq->oflags = hreq32.oflags;
32362306a36Sopenharmony_ci	hreq->ihandle = compat_ptr(hreq32.ihandle);
32462306a36Sopenharmony_ci	hreq->ihandlen = hreq32.ihandlen;
32562306a36Sopenharmony_ci	hreq->ohandle = compat_ptr(hreq32.ohandle);
32662306a36Sopenharmony_ci	hreq->ohandlen = compat_ptr(hreq32.ohandlen);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ciSTATIC struct dentry *
33262306a36Sopenharmony_cixfs_compat_handlereq_to_dentry(
33362306a36Sopenharmony_ci	struct file		*parfilp,
33462306a36Sopenharmony_ci	compat_xfs_fsop_handlereq_t *hreq)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	return xfs_handle_to_dentry(parfilp,
33762306a36Sopenharmony_ci			compat_ptr(hreq->ihandle), hreq->ihandlen);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ciSTATIC int
34162306a36Sopenharmony_cixfs_compat_attrlist_by_handle(
34262306a36Sopenharmony_ci	struct file		*parfilp,
34362306a36Sopenharmony_ci	compat_xfs_fsop_attrlist_handlereq_t __user *p)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	compat_xfs_fsop_attrlist_handlereq_t al_hreq;
34662306a36Sopenharmony_ci	struct dentry		*dentry;
34762306a36Sopenharmony_ci	int			error;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
35062306a36Sopenharmony_ci		return -EPERM;
35162306a36Sopenharmony_ci	if (copy_from_user(&al_hreq, p, sizeof(al_hreq)))
35262306a36Sopenharmony_ci		return -EFAULT;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	dentry = xfs_compat_handlereq_to_dentry(parfilp, &al_hreq.hreq);
35562306a36Sopenharmony_ci	if (IS_ERR(dentry))
35662306a36Sopenharmony_ci		return PTR_ERR(dentry);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)),
35962306a36Sopenharmony_ci			compat_ptr(al_hreq.buffer), al_hreq.buflen,
36062306a36Sopenharmony_ci			al_hreq.flags, &p->pos);
36162306a36Sopenharmony_ci	dput(dentry);
36262306a36Sopenharmony_ci	return error;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ciSTATIC int
36662306a36Sopenharmony_cixfs_compat_attrmulti_by_handle(
36762306a36Sopenharmony_ci	struct file				*parfilp,
36862306a36Sopenharmony_ci	void					__user *arg)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	int					error;
37162306a36Sopenharmony_ci	compat_xfs_attr_multiop_t		*ops;
37262306a36Sopenharmony_ci	compat_xfs_fsop_attrmulti_handlereq_t	am_hreq;
37362306a36Sopenharmony_ci	struct dentry				*dentry;
37462306a36Sopenharmony_ci	unsigned int				i, size;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
37762306a36Sopenharmony_ci		return -EPERM;
37862306a36Sopenharmony_ci	if (copy_from_user(&am_hreq, arg,
37962306a36Sopenharmony_ci			   sizeof(compat_xfs_fsop_attrmulti_handlereq_t)))
38062306a36Sopenharmony_ci		return -EFAULT;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* overflow check */
38362306a36Sopenharmony_ci	if (am_hreq.opcount >= INT_MAX / sizeof(compat_xfs_attr_multiop_t))
38462306a36Sopenharmony_ci		return -E2BIG;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	dentry = xfs_compat_handlereq_to_dentry(parfilp, &am_hreq.hreq);
38762306a36Sopenharmony_ci	if (IS_ERR(dentry))
38862306a36Sopenharmony_ci		return PTR_ERR(dentry);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	error = -E2BIG;
39162306a36Sopenharmony_ci	size = am_hreq.opcount * sizeof(compat_xfs_attr_multiop_t);
39262306a36Sopenharmony_ci	if (!size || size > 16 * PAGE_SIZE)
39362306a36Sopenharmony_ci		goto out_dput;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ops = memdup_user(compat_ptr(am_hreq.ops), size);
39662306a36Sopenharmony_ci	if (IS_ERR(ops)) {
39762306a36Sopenharmony_ci		error = PTR_ERR(ops);
39862306a36Sopenharmony_ci		goto out_dput;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	error = 0;
40262306a36Sopenharmony_ci	for (i = 0; i < am_hreq.opcount; i++) {
40362306a36Sopenharmony_ci		ops[i].am_error = xfs_ioc_attrmulti_one(parfilp,
40462306a36Sopenharmony_ci				d_inode(dentry), ops[i].am_opcode,
40562306a36Sopenharmony_ci				compat_ptr(ops[i].am_attrname),
40662306a36Sopenharmony_ci				compat_ptr(ops[i].am_attrvalue),
40762306a36Sopenharmony_ci				&ops[i].am_length, ops[i].am_flags);
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (copy_to_user(compat_ptr(am_hreq.ops), ops, size))
41162306a36Sopenharmony_ci		error = -EFAULT;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	kfree(ops);
41462306a36Sopenharmony_ci out_dput:
41562306a36Sopenharmony_ci	dput(dentry);
41662306a36Sopenharmony_ci	return error;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cilong
42062306a36Sopenharmony_cixfs_file_compat_ioctl(
42162306a36Sopenharmony_ci	struct file		*filp,
42262306a36Sopenharmony_ci	unsigned		cmd,
42362306a36Sopenharmony_ci	unsigned long		p)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct inode		*inode = file_inode(filp);
42662306a36Sopenharmony_ci	struct xfs_inode	*ip = XFS_I(inode);
42762306a36Sopenharmony_ci	void			__user *arg = compat_ptr(p);
42862306a36Sopenharmony_ci	int			error;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	trace_xfs_file_compat_ioctl(ip);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	switch (cmd) {
43362306a36Sopenharmony_ci#if defined(BROKEN_X86_ALIGNMENT)
43462306a36Sopenharmony_ci	case XFS_IOC_FSGEOMETRY_V1_32:
43562306a36Sopenharmony_ci		return xfs_compat_ioc_fsgeometry_v1(ip->i_mount, arg);
43662306a36Sopenharmony_ci	case XFS_IOC_FSGROWFSDATA_32: {
43762306a36Sopenharmony_ci		struct xfs_growfs_data	in;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		if (xfs_compat_growfs_data_copyin(&in, arg))
44062306a36Sopenharmony_ci			return -EFAULT;
44162306a36Sopenharmony_ci		error = mnt_want_write_file(filp);
44262306a36Sopenharmony_ci		if (error)
44362306a36Sopenharmony_ci			return error;
44462306a36Sopenharmony_ci		error = xfs_growfs_data(ip->i_mount, &in);
44562306a36Sopenharmony_ci		mnt_drop_write_file(filp);
44662306a36Sopenharmony_ci		return error;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci	case XFS_IOC_FSGROWFSRT_32: {
44962306a36Sopenharmony_ci		struct xfs_growfs_rt	in;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		if (xfs_compat_growfs_rt_copyin(&in, arg))
45262306a36Sopenharmony_ci			return -EFAULT;
45362306a36Sopenharmony_ci		error = mnt_want_write_file(filp);
45462306a36Sopenharmony_ci		if (error)
45562306a36Sopenharmony_ci			return error;
45662306a36Sopenharmony_ci		error = xfs_growfs_rt(ip->i_mount, &in);
45762306a36Sopenharmony_ci		mnt_drop_write_file(filp);
45862306a36Sopenharmony_ci		return error;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci#endif
46162306a36Sopenharmony_ci	/* long changes size, but xfs only copiese out 32 bits */
46262306a36Sopenharmony_ci	case XFS_IOC_GETVERSION_32:
46362306a36Sopenharmony_ci		cmd = _NATIVE_IOC(cmd, long);
46462306a36Sopenharmony_ci		return xfs_file_ioctl(filp, cmd, p);
46562306a36Sopenharmony_ci	case XFS_IOC_SWAPEXT_32: {
46662306a36Sopenharmony_ci		struct xfs_swapext	  sxp;
46762306a36Sopenharmony_ci		struct compat_xfs_swapext __user *sxu = arg;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		/* Bulk copy in up to the sx_stat field, then copy bstat */
47062306a36Sopenharmony_ci		if (copy_from_user(&sxp, sxu,
47162306a36Sopenharmony_ci				   offsetof(struct xfs_swapext, sx_stat)) ||
47262306a36Sopenharmony_ci		    xfs_ioctl32_bstat_copyin(&sxp.sx_stat, &sxu->sx_stat))
47362306a36Sopenharmony_ci			return -EFAULT;
47462306a36Sopenharmony_ci		error = mnt_want_write_file(filp);
47562306a36Sopenharmony_ci		if (error)
47662306a36Sopenharmony_ci			return error;
47762306a36Sopenharmony_ci		error = xfs_ioc_swapext(&sxp);
47862306a36Sopenharmony_ci		mnt_drop_write_file(filp);
47962306a36Sopenharmony_ci		return error;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	case XFS_IOC_FSBULKSTAT_32:
48262306a36Sopenharmony_ci	case XFS_IOC_FSBULKSTAT_SINGLE_32:
48362306a36Sopenharmony_ci	case XFS_IOC_FSINUMBERS_32:
48462306a36Sopenharmony_ci		return xfs_compat_ioc_fsbulkstat(filp, cmd, arg);
48562306a36Sopenharmony_ci	case XFS_IOC_FD_TO_HANDLE_32:
48662306a36Sopenharmony_ci	case XFS_IOC_PATH_TO_HANDLE_32:
48762306a36Sopenharmony_ci	case XFS_IOC_PATH_TO_FSHANDLE_32: {
48862306a36Sopenharmony_ci		struct xfs_fsop_handlereq	hreq;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		if (xfs_compat_handlereq_copyin(&hreq, arg))
49162306a36Sopenharmony_ci			return -EFAULT;
49262306a36Sopenharmony_ci		cmd = _NATIVE_IOC(cmd, struct xfs_fsop_handlereq);
49362306a36Sopenharmony_ci		return xfs_find_handle(cmd, &hreq);
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci	case XFS_IOC_OPEN_BY_HANDLE_32: {
49662306a36Sopenharmony_ci		struct xfs_fsop_handlereq	hreq;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		if (xfs_compat_handlereq_copyin(&hreq, arg))
49962306a36Sopenharmony_ci			return -EFAULT;
50062306a36Sopenharmony_ci		return xfs_open_by_handle(filp, &hreq);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	case XFS_IOC_READLINK_BY_HANDLE_32: {
50362306a36Sopenharmony_ci		struct xfs_fsop_handlereq	hreq;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		if (xfs_compat_handlereq_copyin(&hreq, arg))
50662306a36Sopenharmony_ci			return -EFAULT;
50762306a36Sopenharmony_ci		return xfs_readlink_by_handle(filp, &hreq);
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci	case XFS_IOC_ATTRLIST_BY_HANDLE_32:
51062306a36Sopenharmony_ci		return xfs_compat_attrlist_by_handle(filp, arg);
51162306a36Sopenharmony_ci	case XFS_IOC_ATTRMULTI_BY_HANDLE_32:
51262306a36Sopenharmony_ci		return xfs_compat_attrmulti_by_handle(filp, arg);
51362306a36Sopenharmony_ci	default:
51462306a36Sopenharmony_ci		/* try the native version */
51562306a36Sopenharmony_ci		return xfs_file_ioctl(filp, cmd, (unsigned long)arg);
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci}
518