brintos

brintos / linux-shallow public Read only

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