cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

xfs_ioctl32.c (14654B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2004-2005 Silicon Graphics, Inc.
      4 * All Rights Reserved.
      5 */
      6#include <linux/mount.h>
      7#include <linux/fsmap.h>
      8#include "xfs.h"
      9#include "xfs_fs.h"
     10#include "xfs_shared.h"
     11#include "xfs_format.h"
     12#include "xfs_log_format.h"
     13#include "xfs_trans_resv.h"
     14#include "xfs_mount.h"
     15#include "xfs_inode.h"
     16#include "xfs_iwalk.h"
     17#include "xfs_itable.h"
     18#include "xfs_fsops.h"
     19#include "xfs_rtalloc.h"
     20#include "xfs_da_format.h"
     21#include "xfs_da_btree.h"
     22#include "xfs_attr.h"
     23#include "xfs_ioctl.h"
     24#include "xfs_ioctl32.h"
     25#include "xfs_trace.h"
     26#include "xfs_sb.h"
     27
     28#define  _NATIVE_IOC(cmd, type) \
     29	  _IOC(_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), sizeof(type))
     30
     31#ifdef BROKEN_X86_ALIGNMENT
     32STATIC int
     33xfs_compat_ioc_fsgeometry_v1(
     34	struct xfs_mount	  *mp,
     35	compat_xfs_fsop_geom_v1_t __user *arg32)
     36{
     37	struct xfs_fsop_geom	  fsgeo;
     38
     39	xfs_fs_geometry(mp, &fsgeo, 3);
     40	/* The 32-bit variant simply has some padding at the end */
     41	if (copy_to_user(arg32, &fsgeo, sizeof(struct compat_xfs_fsop_geom_v1)))
     42		return -EFAULT;
     43	return 0;
     44}
     45
     46STATIC int
     47xfs_compat_growfs_data_copyin(
     48	struct xfs_growfs_data	 *in,
     49	compat_xfs_growfs_data_t __user *arg32)
     50{
     51	if (get_user(in->newblocks, &arg32->newblocks) ||
     52	    get_user(in->imaxpct,   &arg32->imaxpct))
     53		return -EFAULT;
     54	return 0;
     55}
     56
     57STATIC int
     58xfs_compat_growfs_rt_copyin(
     59	struct xfs_growfs_rt	 *in,
     60	compat_xfs_growfs_rt_t	__user *arg32)
     61{
     62	if (get_user(in->newblocks, &arg32->newblocks) ||
     63	    get_user(in->extsize,   &arg32->extsize))
     64		return -EFAULT;
     65	return 0;
     66}
     67
     68STATIC int
     69xfs_fsinumbers_fmt_compat(
     70	struct xfs_ibulk		*breq,
     71	const struct xfs_inumbers	*ig)
     72{
     73	struct compat_xfs_inogrp __user	*p32 = breq->ubuffer;
     74	struct xfs_inogrp		ig1;
     75	struct xfs_inogrp		*igrp = &ig1;
     76
     77	xfs_inumbers_to_inogrp(&ig1, ig);
     78
     79	if (put_user(igrp->xi_startino,   &p32->xi_startino) ||
     80	    put_user(igrp->xi_alloccount, &p32->xi_alloccount) ||
     81	    put_user(igrp->xi_allocmask,  &p32->xi_allocmask))
     82		return -EFAULT;
     83
     84	return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_inogrp));
     85}
     86
     87#else
     88#define xfs_fsinumbers_fmt_compat xfs_fsinumbers_fmt
     89#endif	/* BROKEN_X86_ALIGNMENT */
     90
     91STATIC int
     92xfs_ioctl32_bstime_copyin(
     93	xfs_bstime_t		*bstime,
     94	compat_xfs_bstime_t	__user *bstime32)
     95{
     96	old_time32_t		sec32;	/* tv_sec differs on 64 vs. 32 */
     97
     98	if (get_user(sec32,		&bstime32->tv_sec)	||
     99	    get_user(bstime->tv_nsec,	&bstime32->tv_nsec))
    100		return -EFAULT;
    101	bstime->tv_sec = sec32;
    102	return 0;
    103}
    104
    105/*
    106 * struct xfs_bstat has differing alignment on intel, & bstime_t sizes
    107 * everywhere
    108 */
    109STATIC int
    110xfs_ioctl32_bstat_copyin(
    111	struct xfs_bstat		*bstat,
    112	struct compat_xfs_bstat	__user	*bstat32)
    113{
    114	if (get_user(bstat->bs_ino,	&bstat32->bs_ino)	||
    115	    get_user(bstat->bs_mode,	&bstat32->bs_mode)	||
    116	    get_user(bstat->bs_nlink,	&bstat32->bs_nlink)	||
    117	    get_user(bstat->bs_uid,	&bstat32->bs_uid)	||
    118	    get_user(bstat->bs_gid,	&bstat32->bs_gid)	||
    119	    get_user(bstat->bs_rdev,	&bstat32->bs_rdev)	||
    120	    get_user(bstat->bs_blksize,	&bstat32->bs_blksize)	||
    121	    get_user(bstat->bs_size,	&bstat32->bs_size)	||
    122	    xfs_ioctl32_bstime_copyin(&bstat->bs_atime, &bstat32->bs_atime) ||
    123	    xfs_ioctl32_bstime_copyin(&bstat->bs_mtime, &bstat32->bs_mtime) ||
    124	    xfs_ioctl32_bstime_copyin(&bstat->bs_ctime, &bstat32->bs_ctime) ||
    125	    get_user(bstat->bs_blocks,	&bstat32->bs_size)	||
    126	    get_user(bstat->bs_xflags,	&bstat32->bs_size)	||
    127	    get_user(bstat->bs_extsize,	&bstat32->bs_extsize)	||
    128	    get_user(bstat->bs_extents,	&bstat32->bs_extents)	||
    129	    get_user(bstat->bs_gen,	&bstat32->bs_gen)	||
    130	    get_user(bstat->bs_projid_lo, &bstat32->bs_projid_lo) ||
    131	    get_user(bstat->bs_projid_hi, &bstat32->bs_projid_hi) ||
    132	    get_user(bstat->bs_forkoff,	&bstat32->bs_forkoff)	||
    133	    get_user(bstat->bs_dmevmask, &bstat32->bs_dmevmask)	||
    134	    get_user(bstat->bs_dmstate,	&bstat32->bs_dmstate)	||
    135	    get_user(bstat->bs_aextents, &bstat32->bs_aextents))
    136		return -EFAULT;
    137	return 0;
    138}
    139
    140/* XFS_IOC_FSBULKSTAT and friends */
    141
    142STATIC int
    143xfs_bstime_store_compat(
    144	compat_xfs_bstime_t	__user *p32,
    145	const xfs_bstime_t	*p)
    146{
    147	__s32			sec32;
    148
    149	sec32 = p->tv_sec;
    150	if (put_user(sec32, &p32->tv_sec) ||
    151	    put_user(p->tv_nsec, &p32->tv_nsec))
    152		return -EFAULT;
    153	return 0;
    154}
    155
    156/* Return 0 on success or positive error (to xfs_bulkstat()) */
    157STATIC int
    158xfs_fsbulkstat_one_fmt_compat(
    159	struct xfs_ibulk		*breq,
    160	const struct xfs_bulkstat	*bstat)
    161{
    162	struct compat_xfs_bstat	__user	*p32 = breq->ubuffer;
    163	struct xfs_bstat		bs1;
    164	struct xfs_bstat		*buffer = &bs1;
    165
    166	xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);
    167
    168	if (put_user(buffer->bs_ino,	  &p32->bs_ino)		||
    169	    put_user(buffer->bs_mode,	  &p32->bs_mode)	||
    170	    put_user(buffer->bs_nlink,	  &p32->bs_nlink)	||
    171	    put_user(buffer->bs_uid,	  &p32->bs_uid)		||
    172	    put_user(buffer->bs_gid,	  &p32->bs_gid)		||
    173	    put_user(buffer->bs_rdev,	  &p32->bs_rdev)	||
    174	    put_user(buffer->bs_blksize,  &p32->bs_blksize)	||
    175	    put_user(buffer->bs_size,	  &p32->bs_size)	||
    176	    xfs_bstime_store_compat(&p32->bs_atime, &buffer->bs_atime) ||
    177	    xfs_bstime_store_compat(&p32->bs_mtime, &buffer->bs_mtime) ||
    178	    xfs_bstime_store_compat(&p32->bs_ctime, &buffer->bs_ctime) ||
    179	    put_user(buffer->bs_blocks,	  &p32->bs_blocks)	||
    180	    put_user(buffer->bs_xflags,	  &p32->bs_xflags)	||
    181	    put_user(buffer->bs_extsize,  &p32->bs_extsize)	||
    182	    put_user(buffer->bs_extents,  &p32->bs_extents)	||
    183	    put_user(buffer->bs_gen,	  &p32->bs_gen)		||
    184	    put_user(buffer->bs_projid,	  &p32->bs_projid)	||
    185	    put_user(buffer->bs_projid_hi,	&p32->bs_projid_hi)	||
    186	    put_user(buffer->bs_forkoff,  &p32->bs_forkoff)	||
    187	    put_user(buffer->bs_dmevmask, &p32->bs_dmevmask)	||
    188	    put_user(buffer->bs_dmstate,  &p32->bs_dmstate)	||
    189	    put_user(buffer->bs_aextents, &p32->bs_aextents))
    190		return -EFAULT;
    191
    192	return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_bstat));
    193}
    194
    195/* copied from xfs_ioctl.c */
    196STATIC int
    197xfs_compat_ioc_fsbulkstat(
    198	struct file		*file,
    199	unsigned int		  cmd,
    200	struct compat_xfs_fsop_bulkreq __user *p32)
    201{
    202	struct xfs_mount	*mp = XFS_I(file_inode(file))->i_mount;
    203	u32			addr;
    204	struct xfs_fsop_bulkreq	bulkreq;
    205	struct xfs_ibulk	breq = {
    206		.mp		= mp,
    207		.mnt_userns	= file_mnt_user_ns(file),
    208		.ocount		= 0,
    209	};
    210	xfs_ino_t		lastino;
    211	int			error;
    212
    213	/*
    214	 * Output structure handling functions.  Depending on the command,
    215	 * either the xfs_bstat and xfs_inogrp structures are written out
    216	 * to userpace memory via bulkreq.ubuffer.  Normally the compat
    217	 * functions and structure size are the correct ones to use ...
    218	 */
    219	inumbers_fmt_pf		inumbers_func = xfs_fsinumbers_fmt_compat;
    220	bulkstat_one_fmt_pf	bs_one_func = xfs_fsbulkstat_one_fmt_compat;
    221
    222#ifdef CONFIG_X86_X32_ABI
    223	if (in_x32_syscall()) {
    224		/*
    225		 * ... but on x32 the input xfs_fsop_bulkreq has pointers
    226		 * which must be handled in the "compat" (32-bit) way, while
    227		 * the xfs_bstat and xfs_inogrp structures follow native 64-
    228		 * bit layout convention.  So adjust accordingly, otherwise
    229		 * the data written out in compat layout will not match what
    230		 * x32 userspace expects.
    231		 */
    232		inumbers_func = xfs_fsinumbers_fmt;
    233		bs_one_func = xfs_fsbulkstat_one_fmt;
    234	}
    235#endif
    236
    237	/* done = 1 if there are more stats to get and if bulkstat */
    238	/* should be called again (unused here, but used in dmapi) */
    239
    240	if (!capable(CAP_SYS_ADMIN))
    241		return -EPERM;
    242
    243	if (xfs_is_shutdown(mp))
    244		return -EIO;
    245
    246	if (get_user(addr, &p32->lastip))
    247		return -EFAULT;
    248	bulkreq.lastip = compat_ptr(addr);
    249	if (get_user(bulkreq.icount, &p32->icount) ||
    250	    get_user(addr, &p32->ubuffer))
    251		return -EFAULT;
    252	bulkreq.ubuffer = compat_ptr(addr);
    253	if (get_user(addr, &p32->ocount))
    254		return -EFAULT;
    255	bulkreq.ocount = compat_ptr(addr);
    256
    257	if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64)))
    258		return -EFAULT;
    259
    260	if (bulkreq.icount <= 0)
    261		return -EINVAL;
    262
    263	if (bulkreq.ubuffer == NULL)
    264		return -EINVAL;
    265
    266	breq.ubuffer = bulkreq.ubuffer;
    267	breq.icount = bulkreq.icount;
    268
    269	/*
    270	 * FSBULKSTAT_SINGLE expects that *lastip contains the inode number
    271	 * that we want to stat.  However, FSINUMBERS and FSBULKSTAT expect
    272	 * that *lastip contains either zero or the number of the last inode to
    273	 * be examined by the previous call and return results starting with
    274	 * the next inode after that.  The new bulk request back end functions
    275	 * take the inode to start with, so we have to compute the startino
    276	 * parameter from lastino to maintain correct function.  lastino == 0
    277	 * is a special case because it has traditionally meant "first inode
    278	 * in filesystem".
    279	 */
    280	if (cmd == XFS_IOC_FSINUMBERS_32) {
    281		breq.startino = lastino ? lastino + 1 : 0;
    282		error = xfs_inumbers(&breq, inumbers_func);
    283		lastino = breq.startino - 1;
    284	} else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32) {
    285		breq.startino = lastino;
    286		breq.icount = 1;
    287		error = xfs_bulkstat_one(&breq, bs_one_func);
    288		lastino = breq.startino;
    289	} else if (cmd == XFS_IOC_FSBULKSTAT_32) {
    290		breq.startino = lastino ? lastino + 1 : 0;
    291		error = xfs_bulkstat(&breq, bs_one_func);
    292		lastino = breq.startino - 1;
    293	} else {
    294		error = -EINVAL;
    295	}
    296	if (error)
    297		return error;
    298
    299	if (bulkreq.lastip != NULL &&
    300	    copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t)))
    301		return -EFAULT;
    302
    303	if (bulkreq.ocount != NULL &&
    304	    copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32)))
    305		return -EFAULT;
    306
    307	return 0;
    308}
    309
    310STATIC int
    311xfs_compat_handlereq_copyin(
    312	xfs_fsop_handlereq_t		*hreq,
    313	compat_xfs_fsop_handlereq_t	__user *arg32)
    314{
    315	compat_xfs_fsop_handlereq_t	hreq32;
    316
    317	if (copy_from_user(&hreq32, arg32, sizeof(compat_xfs_fsop_handlereq_t)))
    318		return -EFAULT;
    319
    320	hreq->fd = hreq32.fd;
    321	hreq->path = compat_ptr(hreq32.path);
    322	hreq->oflags = hreq32.oflags;
    323	hreq->ihandle = compat_ptr(hreq32.ihandle);
    324	hreq->ihandlen = hreq32.ihandlen;
    325	hreq->ohandle = compat_ptr(hreq32.ohandle);
    326	hreq->ohandlen = compat_ptr(hreq32.ohandlen);
    327
    328	return 0;
    329}
    330
    331STATIC struct dentry *
    332xfs_compat_handlereq_to_dentry(
    333	struct file		*parfilp,
    334	compat_xfs_fsop_handlereq_t *hreq)
    335{
    336	return xfs_handle_to_dentry(parfilp,
    337			compat_ptr(hreq->ihandle), hreq->ihandlen);
    338}
    339
    340STATIC int
    341xfs_compat_attrlist_by_handle(
    342	struct file		*parfilp,
    343	compat_xfs_fsop_attrlist_handlereq_t __user *p)
    344{
    345	compat_xfs_fsop_attrlist_handlereq_t al_hreq;
    346	struct dentry		*dentry;
    347	int			error;
    348
    349	if (!capable(CAP_SYS_ADMIN))
    350		return -EPERM;
    351	if (copy_from_user(&al_hreq, p, sizeof(al_hreq)))
    352		return -EFAULT;
    353
    354	dentry = xfs_compat_handlereq_to_dentry(parfilp, &al_hreq.hreq);
    355	if (IS_ERR(dentry))
    356		return PTR_ERR(dentry);
    357
    358	error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)),
    359			compat_ptr(al_hreq.buffer), al_hreq.buflen,
    360			al_hreq.flags, &p->pos);
    361	dput(dentry);
    362	return error;
    363}
    364
    365STATIC int
    366xfs_compat_attrmulti_by_handle(
    367	struct file				*parfilp,
    368	void					__user *arg)
    369{
    370	int					error;
    371	compat_xfs_attr_multiop_t		*ops;
    372	compat_xfs_fsop_attrmulti_handlereq_t	am_hreq;
    373	struct dentry				*dentry;
    374	unsigned int				i, size;
    375
    376	if (!capable(CAP_SYS_ADMIN))
    377		return -EPERM;
    378	if (copy_from_user(&am_hreq, arg,
    379			   sizeof(compat_xfs_fsop_attrmulti_handlereq_t)))
    380		return -EFAULT;
    381
    382	/* overflow check */
    383	if (am_hreq.opcount >= INT_MAX / sizeof(compat_xfs_attr_multiop_t))
    384		return -E2BIG;
    385
    386	dentry = xfs_compat_handlereq_to_dentry(parfilp, &am_hreq.hreq);
    387	if (IS_ERR(dentry))
    388		return PTR_ERR(dentry);
    389
    390	error = -E2BIG;
    391	size = am_hreq.opcount * sizeof(compat_xfs_attr_multiop_t);
    392	if (!size || size > 16 * PAGE_SIZE)
    393		goto out_dput;
    394
    395	ops = memdup_user(compat_ptr(am_hreq.ops), size);
    396	if (IS_ERR(ops)) {
    397		error = PTR_ERR(ops);
    398		goto out_dput;
    399	}
    400
    401	error = 0;
    402	for (i = 0; i < am_hreq.opcount; i++) {
    403		ops[i].am_error = xfs_ioc_attrmulti_one(parfilp,
    404				d_inode(dentry), ops[i].am_opcode,
    405				compat_ptr(ops[i].am_attrname),
    406				compat_ptr(ops[i].am_attrvalue),
    407				&ops[i].am_length, ops[i].am_flags);
    408	}
    409
    410	if (copy_to_user(compat_ptr(am_hreq.ops), ops, size))
    411		error = -EFAULT;
    412
    413	kfree(ops);
    414 out_dput:
    415	dput(dentry);
    416	return error;
    417}
    418
    419long
    420xfs_file_compat_ioctl(
    421	struct file		*filp,
    422	unsigned		cmd,
    423	unsigned long		p)
    424{
    425	struct inode		*inode = file_inode(filp);
    426	struct xfs_inode	*ip = XFS_I(inode);
    427	void			__user *arg = compat_ptr(p);
    428	int			error;
    429
    430	trace_xfs_file_compat_ioctl(ip);
    431
    432	switch (cmd) {
    433#if defined(BROKEN_X86_ALIGNMENT)
    434	case XFS_IOC_FSGEOMETRY_V1_32:
    435		return xfs_compat_ioc_fsgeometry_v1(ip->i_mount, arg);
    436	case XFS_IOC_FSGROWFSDATA_32: {
    437		struct xfs_growfs_data	in;
    438
    439		if (xfs_compat_growfs_data_copyin(&in, arg))
    440			return -EFAULT;
    441		error = mnt_want_write_file(filp);
    442		if (error)
    443			return error;
    444		error = xfs_growfs_data(ip->i_mount, &in);
    445		mnt_drop_write_file(filp);
    446		return error;
    447	}
    448	case XFS_IOC_FSGROWFSRT_32: {
    449		struct xfs_growfs_rt	in;
    450
    451		if (xfs_compat_growfs_rt_copyin(&in, arg))
    452			return -EFAULT;
    453		error = mnt_want_write_file(filp);
    454		if (error)
    455			return error;
    456		error = xfs_growfs_rt(ip->i_mount, &in);
    457		mnt_drop_write_file(filp);
    458		return error;
    459	}
    460#endif
    461	/* long changes size, but xfs only copiese out 32 bits */
    462	case XFS_IOC_GETVERSION_32:
    463		cmd = _NATIVE_IOC(cmd, long);
    464		return xfs_file_ioctl(filp, cmd, p);
    465	case XFS_IOC_SWAPEXT_32: {
    466		struct xfs_swapext	  sxp;
    467		struct compat_xfs_swapext __user *sxu = arg;
    468
    469		/* Bulk copy in up to the sx_stat field, then copy bstat */
    470		if (copy_from_user(&sxp, sxu,
    471				   offsetof(struct xfs_swapext, sx_stat)) ||
    472		    xfs_ioctl32_bstat_copyin(&sxp.sx_stat, &sxu->sx_stat))
    473			return -EFAULT;
    474		error = mnt_want_write_file(filp);
    475		if (error)
    476			return error;
    477		error = xfs_ioc_swapext(&sxp);
    478		mnt_drop_write_file(filp);
    479		return error;
    480	}
    481	case XFS_IOC_FSBULKSTAT_32:
    482	case XFS_IOC_FSBULKSTAT_SINGLE_32:
    483	case XFS_IOC_FSINUMBERS_32:
    484		return xfs_compat_ioc_fsbulkstat(filp, cmd, arg);
    485	case XFS_IOC_FD_TO_HANDLE_32:
    486	case XFS_IOC_PATH_TO_HANDLE_32:
    487	case XFS_IOC_PATH_TO_FSHANDLE_32: {
    488		struct xfs_fsop_handlereq	hreq;
    489
    490		if (xfs_compat_handlereq_copyin(&hreq, arg))
    491			return -EFAULT;
    492		cmd = _NATIVE_IOC(cmd, struct xfs_fsop_handlereq);
    493		return xfs_find_handle(cmd, &hreq);
    494	}
    495	case XFS_IOC_OPEN_BY_HANDLE_32: {
    496		struct xfs_fsop_handlereq	hreq;
    497
    498		if (xfs_compat_handlereq_copyin(&hreq, arg))
    499			return -EFAULT;
    500		return xfs_open_by_handle(filp, &hreq);
    501	}
    502	case XFS_IOC_READLINK_BY_HANDLE_32: {
    503		struct xfs_fsop_handlereq	hreq;
    504
    505		if (xfs_compat_handlereq_copyin(&hreq, arg))
    506			return -EFAULT;
    507		return xfs_readlink_by_handle(filp, &hreq);
    508	}
    509	case XFS_IOC_ATTRLIST_BY_HANDLE_32:
    510		return xfs_compat_attrlist_by_handle(filp, arg);
    511	case XFS_IOC_ATTRMULTI_BY_HANDLE_32:
    512		return xfs_compat_attrmulti_by_handle(filp, arg);
    513	default:
    514		/* try the native version */
    515		return xfs_file_ioctl(filp, cmd, (unsigned long)arg);
    516	}
    517}