brintos

brintos / linux-shallow public Read only

0
0
Text · 21.4 KiB · 49e5e5f Raw
947 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2000-2005 Silicon Graphics, Inc.4 * Copyright (c) 2022-2024 Oracle.5 * All rights reserved.6 */7#include "xfs.h"8#include "xfs_fs.h"9#include "xfs_format.h"10#include "xfs_log_format.h"11#include "xfs_shared.h"12#include "xfs_trans_resv.h"13#include "xfs_mount.h"14#include "xfs_bmap_btree.h"15#include "xfs_inode.h"16#include "xfs_error.h"17#include "xfs_trace.h"18#include "xfs_trans.h"19#include "xfs_da_format.h"20#include "xfs_da_btree.h"21#include "xfs_attr.h"22#include "xfs_ioctl.h"23#include "xfs_parent.h"24#include "xfs_handle.h"25#include "xfs_health.h"26#include "xfs_icache.h"27#include "xfs_export.h"28#include "xfs_xattr.h"29#include "xfs_acl.h"30 31#include <linux/namei.h>32 33static inline size_t34xfs_filehandle_fid_len(void)35{36	struct xfs_handle	*handle = NULL;37 38	return sizeof(struct xfs_fid) - sizeof(handle->ha_fid.fid_len);39}40 41static inline size_t42xfs_filehandle_init(43	struct xfs_mount	*mp,44	xfs_ino_t		ino,45	uint32_t		gen,46	struct xfs_handle	*handle)47{48	memcpy(&handle->ha_fsid, mp->m_fixedfsid, sizeof(struct xfs_fsid));49 50	handle->ha_fid.fid_len = xfs_filehandle_fid_len();51	handle->ha_fid.fid_pad = 0;52	handle->ha_fid.fid_gen = gen;53	handle->ha_fid.fid_ino = ino;54 55	return sizeof(struct xfs_handle);56}57 58static inline size_t59xfs_fshandle_init(60	struct xfs_mount	*mp,61	struct xfs_handle	*handle)62{63	memcpy(&handle->ha_fsid, mp->m_fixedfsid, sizeof(struct xfs_fsid));64	memset(&handle->ha_fid, 0, sizeof(handle->ha_fid));65 66	return sizeof(struct xfs_fsid);67}68 69/*70 * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to71 * a file or fs handle.72 *73 * XFS_IOC_PATH_TO_FSHANDLE74 *    returns fs handle for a mount point or path within that mount point75 * XFS_IOC_FD_TO_HANDLE76 *    returns full handle for a FD opened in user space77 * XFS_IOC_PATH_TO_HANDLE78 *    returns full handle for a path79 */80int81xfs_find_handle(82	unsigned int		cmd,83	xfs_fsop_handlereq_t	*hreq)84{85	int			hsize;86	xfs_handle_t		handle;87	struct inode		*inode;88	struct fd		f = EMPTY_FD;89	struct path		path;90	int			error;91	struct xfs_inode	*ip;92 93	if (cmd == XFS_IOC_FD_TO_HANDLE) {94		f = fdget(hreq->fd);95		if (!fd_file(f))96			return -EBADF;97		inode = file_inode(fd_file(f));98	} else {99		error = user_path_at(AT_FDCWD, hreq->path, 0, &path);100		if (error)101			return error;102		inode = d_inode(path.dentry);103	}104	ip = XFS_I(inode);105 106	/*107	 * We can only generate handles for inodes residing on a XFS filesystem,108	 * and only for regular files, directories or symbolic links.109	 */110	error = -EINVAL;111	if (inode->i_sb->s_magic != XFS_SB_MAGIC)112		goto out_put;113 114	error = -EBADF;115	if (!S_ISREG(inode->i_mode) &&116	    !S_ISDIR(inode->i_mode) &&117	    !S_ISLNK(inode->i_mode))118		goto out_put;119 120 121	memcpy(&handle.ha_fsid, ip->i_mount->m_fixedfsid, sizeof(xfs_fsid_t));122 123	if (cmd == XFS_IOC_PATH_TO_FSHANDLE)124		hsize = xfs_fshandle_init(ip->i_mount, &handle);125	else126		hsize = xfs_filehandle_init(ip->i_mount, ip->i_ino,127				inode->i_generation, &handle);128 129	error = -EFAULT;130	if (copy_to_user(hreq->ohandle, &handle, hsize) ||131	    copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))132		goto out_put;133 134	error = 0;135 136 out_put:137	if (cmd == XFS_IOC_FD_TO_HANDLE)138		fdput(f);139	else140		path_put(&path);141	return error;142}143 144/*145 * No need to do permission checks on the various pathname components146 * as the handle operations are privileged.147 */148STATIC int149xfs_handle_acceptable(150	void			*context,151	struct dentry		*dentry)152{153	return 1;154}155 156/* Convert handle already copied to kernel space into a dentry. */157static struct dentry *158xfs_khandle_to_dentry(159	struct file		*file,160	struct xfs_handle	*handle)161{162	struct xfs_fid64        fid = {163		.ino		= handle->ha_fid.fid_ino,164		.gen		= handle->ha_fid.fid_gen,165	};166 167	/*168	 * Only allow handle opens under a directory.169	 */170	if (!S_ISDIR(file_inode(file)->i_mode))171		return ERR_PTR(-ENOTDIR);172 173	if (handle->ha_fid.fid_len != xfs_filehandle_fid_len())174		return ERR_PTR(-EINVAL);175 176	return exportfs_decode_fh(file->f_path.mnt, (struct fid *)&fid, 3,177			FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG,178			xfs_handle_acceptable, NULL);179}180 181/* Convert handle already copied to kernel space into an xfs_inode. */182static struct xfs_inode *183xfs_khandle_to_inode(184	struct file		*file,185	struct xfs_handle	*handle)186{187	struct xfs_inode	*ip = XFS_I(file_inode(file));188	struct xfs_mount	*mp = ip->i_mount;189	struct inode		*inode;190 191	if (!S_ISDIR(VFS_I(ip)->i_mode))192		return ERR_PTR(-ENOTDIR);193 194	if (handle->ha_fid.fid_len != xfs_filehandle_fid_len())195		return ERR_PTR(-EINVAL);196 197	inode = xfs_nfs_get_inode(mp->m_super, handle->ha_fid.fid_ino,198			handle->ha_fid.fid_gen);199	if (IS_ERR(inode))200		return ERR_CAST(inode);201 202	return XFS_I(inode);203}204 205/*206 * Convert userspace handle data into a dentry.207 */208struct dentry *209xfs_handle_to_dentry(210	struct file		*parfilp,211	void __user		*uhandle,212	u32			hlen)213{214	xfs_handle_t		handle;215 216	if (hlen != sizeof(xfs_handle_t))217		return ERR_PTR(-EINVAL);218	if (copy_from_user(&handle, uhandle, hlen))219		return ERR_PTR(-EFAULT);220 221	return xfs_khandle_to_dentry(parfilp, &handle);222}223 224STATIC struct dentry *225xfs_handlereq_to_dentry(226	struct file		*parfilp,227	xfs_fsop_handlereq_t	*hreq)228{229	return xfs_handle_to_dentry(parfilp, hreq->ihandle, hreq->ihandlen);230}231 232int233xfs_open_by_handle(234	struct file		*parfilp,235	xfs_fsop_handlereq_t	*hreq)236{237	const struct cred	*cred = current_cred();238	int			error;239	int			fd;240	int			permflag;241	struct file		*filp;242	struct inode		*inode;243	struct dentry		*dentry;244	fmode_t			fmode;245	struct path		path;246 247	if (!capable(CAP_SYS_ADMIN))248		return -EPERM;249 250	dentry = xfs_handlereq_to_dentry(parfilp, hreq);251	if (IS_ERR(dentry))252		return PTR_ERR(dentry);253	inode = d_inode(dentry);254 255	/* Restrict xfs_open_by_handle to directories & regular files. */256	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {257		error = -EPERM;258		goto out_dput;259	}260 261#if BITS_PER_LONG != 32262	hreq->oflags |= O_LARGEFILE;263#endif264 265	permflag = hreq->oflags;266	fmode = OPEN_FMODE(permflag);267	if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&268	    (fmode & FMODE_WRITE) && IS_APPEND(inode)) {269		error = -EPERM;270		goto out_dput;271	}272 273	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {274		error = -EPERM;275		goto out_dput;276	}277 278	/* Can't write directories. */279	if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {280		error = -EISDIR;281		goto out_dput;282	}283 284	fd = get_unused_fd_flags(0);285	if (fd < 0) {286		error = fd;287		goto out_dput;288	}289 290	path.mnt = parfilp->f_path.mnt;291	path.dentry = dentry;292	filp = dentry_open(&path, hreq->oflags, cred);293	dput(dentry);294	if (IS_ERR(filp)) {295		put_unused_fd(fd);296		return PTR_ERR(filp);297	}298 299	if (S_ISREG(inode->i_mode)) {300		filp->f_flags |= O_NOATIME;301		filp->f_mode |= FMODE_NOCMTIME;302	}303 304	fd_install(fd, filp);305	return fd;306 307 out_dput:308	dput(dentry);309	return error;310}311 312int313xfs_readlink_by_handle(314	struct file		*parfilp,315	xfs_fsop_handlereq_t	*hreq)316{317	struct dentry		*dentry;318	__u32			olen;319	int			error;320 321	if (!capable(CAP_SYS_ADMIN))322		return -EPERM;323 324	dentry = xfs_handlereq_to_dentry(parfilp, hreq);325	if (IS_ERR(dentry))326		return PTR_ERR(dentry);327 328	/* Restrict this handle operation to symlinks only. */329	if (!d_is_symlink(dentry)) {330		error = -EINVAL;331		goto out_dput;332	}333 334	if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {335		error = -EFAULT;336		goto out_dput;337	}338 339	error = vfs_readlink(dentry, hreq->ohandle, olen);340 341 out_dput:342	dput(dentry);343	return error;344}345 346/*347 * Format an attribute and copy it out to the user's buffer.348 * Take care to check values and protect against them changing later,349 * we may be reading them directly out of a user buffer.350 */351static void352xfs_ioc_attr_put_listent(353	struct xfs_attr_list_context *context,354	int			flags,355	unsigned char		*name,356	int			namelen,357	void			*value,358	int			valuelen)359{360	struct xfs_attrlist	*alist = context->buffer;361	struct xfs_attrlist_ent	*aep;362	int			arraytop;363 364	ASSERT(!context->seen_enough);365	ASSERT(context->count >= 0);366	ASSERT(context->count < (ATTR_MAX_VALUELEN/8));367	ASSERT(context->firstu >= sizeof(*alist));368	ASSERT(context->firstu <= context->bufsize);369 370	/*371	 * Only list entries in the right namespace.372	 */373	if (context->attr_filter != (flags & XFS_ATTR_NSP_ONDISK_MASK))374		return;375 376	arraytop = sizeof(*alist) +377			context->count * sizeof(alist->al_offset[0]);378 379	/* decrement by the actual bytes used by the attr */380	context->firstu -= round_up(offsetof(struct xfs_attrlist_ent, a_name) +381			namelen + 1, sizeof(uint32_t));382	if (context->firstu < arraytop) {383		trace_xfs_attr_list_full(context);384		alist->al_more = 1;385		context->seen_enough = 1;386		return;387	}388 389	aep = context->buffer + context->firstu;390	aep->a_valuelen = valuelen;391	memcpy(aep->a_name, name, namelen);392	aep->a_name[namelen] = 0;393	alist->al_offset[context->count++] = context->firstu;394	alist->al_count = context->count;395	trace_xfs_attr_list_add(context);396}397 398static unsigned int399xfs_attr_filter(400	u32			ioc_flags)401{402	if (ioc_flags & XFS_IOC_ATTR_ROOT)403		return XFS_ATTR_ROOT;404	if (ioc_flags & XFS_IOC_ATTR_SECURE)405		return XFS_ATTR_SECURE;406	return 0;407}408 409static inline enum xfs_attr_update410xfs_xattr_flags(411	u32			ioc_flags,412	void			*value)413{414	if (!value)415		return XFS_ATTRUPDATE_REMOVE;416	if (ioc_flags & XFS_IOC_ATTR_CREATE)417		return XFS_ATTRUPDATE_CREATE;418	if (ioc_flags & XFS_IOC_ATTR_REPLACE)419		return XFS_ATTRUPDATE_REPLACE;420	return XFS_ATTRUPDATE_UPSERT;421}422 423int424xfs_ioc_attr_list(425	struct xfs_inode		*dp,426	void __user			*ubuf,427	size_t				bufsize,428	int				flags,429	struct xfs_attrlist_cursor __user *ucursor)430{431	struct xfs_attr_list_context	context = { };432	struct xfs_attrlist		*alist;433	void				*buffer;434	int				error;435 436	if (bufsize < sizeof(struct xfs_attrlist) ||437	    bufsize > XFS_XATTR_LIST_MAX)438		return -EINVAL;439 440	/*441	 * Reject flags, only allow namespaces.442	 */443	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))444		return -EINVAL;445	if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))446		return -EINVAL;447 448	/*449	 * Validate the cursor.450	 */451	if (copy_from_user(&context.cursor, ucursor, sizeof(context.cursor)))452		return -EFAULT;453	if (context.cursor.pad1 || context.cursor.pad2)454		return -EINVAL;455	if (!context.cursor.initted &&456	    (context.cursor.hashval || context.cursor.blkno ||457	     context.cursor.offset))458		return -EINVAL;459 460	buffer = kvzalloc(bufsize, GFP_KERNEL);461	if (!buffer)462		return -ENOMEM;463 464	/*465	 * Initialize the output buffer.466	 */467	context.dp = dp;468	context.resynch = 1;469	context.attr_filter = xfs_attr_filter(flags);470	context.buffer = buffer;471	context.bufsize = round_down(bufsize, sizeof(uint32_t));472	context.firstu = context.bufsize;473	context.put_listent = xfs_ioc_attr_put_listent;474 475	alist = context.buffer;476	alist->al_count = 0;477	alist->al_more = 0;478	alist->al_offset[0] = context.bufsize;479 480	error = xfs_attr_list(&context);481	if (error)482		goto out_free;483 484	if (copy_to_user(ubuf, buffer, bufsize) ||485	    copy_to_user(ucursor, &context.cursor, sizeof(context.cursor)))486		error = -EFAULT;487out_free:488	kvfree(buffer);489	return error;490}491 492int493xfs_attrlist_by_handle(494	struct file		*parfilp,495	struct xfs_fsop_attrlist_handlereq __user *p)496{497	struct xfs_fsop_attrlist_handlereq al_hreq;498	struct dentry		*dentry;499	int			error = -ENOMEM;500 501	if (!capable(CAP_SYS_ADMIN))502		return -EPERM;503	if (copy_from_user(&al_hreq, p, sizeof(al_hreq)))504		return -EFAULT;505 506	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);507	if (IS_ERR(dentry))508		return PTR_ERR(dentry);509 510	error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)), al_hreq.buffer,511				  al_hreq.buflen, al_hreq.flags, &p->pos);512	dput(dentry);513	return error;514}515 516static int517xfs_attrmulti_attr_get(518	struct inode		*inode,519	unsigned char		*name,520	unsigned char		__user *ubuf,521	uint32_t		*len,522	uint32_t		flags)523{524	struct xfs_da_args	args = {525		.dp		= XFS_I(inode),526		.attr_filter	= xfs_attr_filter(flags),527		.name		= name,528		.namelen	= strlen(name),529		.valuelen	= *len,530	};531	int			error;532 533	if (*len > XFS_XATTR_SIZE_MAX)534		return -EINVAL;535 536	error = xfs_attr_get(&args);537	if (error)538		goto out_kfree;539 540	*len = args.valuelen;541	if (copy_to_user(ubuf, args.value, args.valuelen))542		error = -EFAULT;543 544out_kfree:545	kvfree(args.value);546	return error;547}548 549static int550xfs_attrmulti_attr_set(551	struct inode		*inode,552	unsigned char		*name,553	const unsigned char	__user *ubuf,554	uint32_t		len,555	uint32_t		flags)556{557	struct xfs_da_args	args = {558		.dp		= XFS_I(inode),559		.attr_filter	= xfs_attr_filter(flags),560		.name		= name,561		.namelen	= strlen(name),562	};563	int			error;564 565	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))566		return -EPERM;567 568	if (ubuf) {569		if (len > XFS_XATTR_SIZE_MAX)570			return -EINVAL;571		args.value = memdup_user(ubuf, len);572		if (IS_ERR(args.value))573			return PTR_ERR(args.value);574		args.valuelen = len;575	}576 577	error = xfs_attr_change(&args, xfs_xattr_flags(flags, args.value));578	if (!error && (flags & XFS_IOC_ATTR_ROOT))579		xfs_forget_acl(inode, name);580	kfree(args.value);581	return error;582}583 584int585xfs_ioc_attrmulti_one(586	struct file		*parfilp,587	struct inode		*inode,588	uint32_t		opcode,589	void __user		*uname,590	void __user		*value,591	uint32_t		*len,592	uint32_t		flags)593{594	unsigned char		*name;595	int			error;596 597	if ((flags & XFS_IOC_ATTR_ROOT) && (flags & XFS_IOC_ATTR_SECURE))598		return -EINVAL;599 600	name = strndup_user(uname, MAXNAMELEN);601	if (IS_ERR(name))602		return PTR_ERR(name);603 604	switch (opcode) {605	case ATTR_OP_GET:606		error = xfs_attrmulti_attr_get(inode, name, value, len, flags);607		break;608	case ATTR_OP_REMOVE:609		value = NULL;610		*len = 0;611		fallthrough;612	case ATTR_OP_SET:613		error = mnt_want_write_file(parfilp);614		if (error)615			break;616		error = xfs_attrmulti_attr_set(inode, name, value, *len, flags);617		mnt_drop_write_file(parfilp);618		break;619	default:620		error = -EINVAL;621		break;622	}623 624	kfree(name);625	return error;626}627 628int629xfs_attrmulti_by_handle(630	struct file		*parfilp,631	void			__user *arg)632{633	int			error;634	xfs_attr_multiop_t	*ops;635	xfs_fsop_attrmulti_handlereq_t am_hreq;636	struct dentry		*dentry;637	unsigned int		i, size;638 639	if (!capable(CAP_SYS_ADMIN))640		return -EPERM;641	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))642		return -EFAULT;643 644	/* overflow check */645	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))646		return -E2BIG;647 648	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);649	if (IS_ERR(dentry))650		return PTR_ERR(dentry);651 652	error = -E2BIG;653	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);654	if (!size || size > 16 * PAGE_SIZE)655		goto out_dput;656 657	ops = memdup_user(am_hreq.ops, size);658	if (IS_ERR(ops)) {659		error = PTR_ERR(ops);660		goto out_dput;661	}662 663	error = 0;664	for (i = 0; i < am_hreq.opcount; i++) {665		ops[i].am_error = xfs_ioc_attrmulti_one(parfilp,666				d_inode(dentry), ops[i].am_opcode,667				ops[i].am_attrname, ops[i].am_attrvalue,668				&ops[i].am_length, ops[i].am_flags);669	}670 671	if (copy_to_user(am_hreq.ops, ops, size))672		error = -EFAULT;673 674	kfree(ops);675 out_dput:676	dput(dentry);677	return error;678}679 680struct xfs_getparents_ctx {681	struct xfs_attr_list_context	context;682	struct xfs_getparents_by_handle	gph;683 684	/* File to target */685	struct xfs_inode		*ip;686 687	/* Internal buffer where we format records */688	void				*krecords;689 690	/* Last record filled out */691	struct xfs_getparents_rec	*lastrec;692 693	unsigned int			count;694};695 696static inline unsigned int697xfs_getparents_rec_sizeof(698	unsigned int		namelen)699{700	return round_up(sizeof(struct xfs_getparents_rec) + namelen + 1,701			sizeof(uint64_t));702}703 704static void705xfs_getparents_put_listent(706	struct xfs_attr_list_context	*context,707	int				flags,708	unsigned char			*name,709	int				namelen,710	void				*value,711	int				valuelen)712{713	struct xfs_getparents_ctx	*gpx =714		container_of(context, struct xfs_getparents_ctx, context);715	struct xfs_inode		*ip = context->dp;716	struct xfs_mount		*mp = ip->i_mount;717	struct xfs_getparents		*gp = &gpx->gph.gph_request;718	struct xfs_getparents_rec	*gpr = gpx->krecords + context->firstu;719	unsigned short			reclen =720		xfs_getparents_rec_sizeof(namelen);721	xfs_ino_t			ino;722	uint32_t			gen;723	int				error;724 725	if (!(flags & XFS_ATTR_PARENT))726		return;727 728	error = xfs_parent_from_attr(mp, flags, name, namelen, value, valuelen,729			&ino, &gen);730	if (error) {731		xfs_inode_mark_sick(ip, XFS_SICK_INO_PARENT);732		context->seen_enough = -EFSCORRUPTED;733		return;734	}735 736	/*737	 * We found a parent pointer, but we've filled up the buffer.  Signal738	 * to the caller that we did /not/ reach the end of the parent pointer739	 * recordset.740	 */741	if (context->firstu > context->bufsize - reclen) {742		context->seen_enough = 1;743		return;744	}745 746	/* Format the parent pointer directly into the caller buffer. */747	gpr->gpr_reclen = reclen;748	xfs_filehandle_init(mp, ino, gen, &gpr->gpr_parent);749	memcpy(gpr->gpr_name, name, namelen);750	gpr->gpr_name[namelen] = 0;751 752	trace_xfs_getparents_put_listent(ip, gp, context, gpr);753 754	context->firstu += reclen;755	gpx->count++;756	gpx->lastrec = gpr;757}758 759/* Expand the last record to fill the rest of the caller's buffer. */760static inline void761xfs_getparents_expand_lastrec(762	struct xfs_getparents_ctx	*gpx)763{764	struct xfs_getparents		*gp = &gpx->gph.gph_request;765	struct xfs_getparents_rec	*gpr = gpx->lastrec;766 767	if (!gpx->lastrec)768		gpr = gpx->krecords;769 770	gpr->gpr_reclen = gp->gp_bufsize - ((void *)gpr - gpx->krecords);771 772	trace_xfs_getparents_expand_lastrec(gpx->ip, gp, &gpx->context, gpr);773}774 775/* Retrieve the parent pointers for a given inode. */776STATIC int777xfs_getparents(778	struct xfs_getparents_ctx	*gpx)779{780	struct xfs_getparents		*gp = &gpx->gph.gph_request;781	struct xfs_inode		*ip = gpx->ip;782	struct xfs_mount		*mp = ip->i_mount;783	size_t				bufsize;784	int				error;785 786	/* Check size of buffer requested by user */787	if (gp->gp_bufsize > XFS_XATTR_LIST_MAX)788		return -ENOMEM;789	if (gp->gp_bufsize < xfs_getparents_rec_sizeof(1))790		return -EINVAL;791 792	if (gp->gp_iflags & ~XFS_GETPARENTS_IFLAGS_ALL)793		return -EINVAL;794	if (gp->gp_reserved)795		return -EINVAL;796 797	bufsize = round_down(gp->gp_bufsize, sizeof(uint64_t));798	gpx->krecords = kvzalloc(bufsize, GFP_KERNEL);799	if (!gpx->krecords) {800		bufsize = min(bufsize, PAGE_SIZE);801		gpx->krecords = kvzalloc(bufsize, GFP_KERNEL);802		if (!gpx->krecords)803			return -ENOMEM;804	}805 806	gpx->context.dp = ip;807	gpx->context.resynch = 1;808	gpx->context.put_listent = xfs_getparents_put_listent;809	gpx->context.bufsize = bufsize;810	/* firstu is used to track the bytes filled in the buffer */811	gpx->context.firstu = 0;812 813	/* Copy the cursor provided by caller */814	memcpy(&gpx->context.cursor, &gp->gp_cursor,815			sizeof(struct xfs_attrlist_cursor));816	gpx->count = 0;817	gp->gp_oflags = 0;818 819	trace_xfs_getparents_begin(ip, gp, &gpx->context.cursor);820 821	error = xfs_attr_list(&gpx->context);822	if (error)823		goto out_free_buf;824	if (gpx->context.seen_enough < 0) {825		error = gpx->context.seen_enough;826		goto out_free_buf;827	}828	xfs_getparents_expand_lastrec(gpx);829 830	/* Update the caller with the current cursor position */831	memcpy(&gp->gp_cursor, &gpx->context.cursor,832			sizeof(struct xfs_attrlist_cursor));833 834	/* Is this the root directory? */835	if (ip->i_ino == mp->m_sb.sb_rootino)836		gp->gp_oflags |= XFS_GETPARENTS_OFLAG_ROOT;837 838	if (gpx->context.seen_enough == 0) {839		/*840		 * If we did not run out of buffer space, then we reached the841		 * end of the pptr recordset, so set the DONE flag.842		 */843		gp->gp_oflags |= XFS_GETPARENTS_OFLAG_DONE;844	} else if (gpx->count == 0) {845		/*846		 * If we ran out of buffer space before copying any parent847		 * pointers at all, the caller's buffer was too short.  Tell848		 * userspace that, erm, the message is too long.849		 */850		error = -EMSGSIZE;851		goto out_free_buf;852	}853 854	trace_xfs_getparents_end(ip, gp, &gpx->context.cursor);855 856	ASSERT(gpx->context.firstu <= gpx->gph.gph_request.gp_bufsize);857 858	/* Copy the records to userspace. */859	if (copy_to_user(u64_to_user_ptr(gpx->gph.gph_request.gp_buffer),860				gpx->krecords, gpx->context.firstu))861		error = -EFAULT;862 863out_free_buf:864	kvfree(gpx->krecords);865	gpx->krecords = NULL;866	return error;867}868 869/* Retrieve the parents of this file and pass them back to userspace. */870int871xfs_ioc_getparents(872	struct file			*file,873	struct xfs_getparents __user	*ureq)874{875	struct xfs_getparents_ctx	gpx = {876		.ip			= XFS_I(file_inode(file)),877	};878	struct xfs_getparents		*kreq = &gpx.gph.gph_request;879	struct xfs_mount		*mp = gpx.ip->i_mount;880	int				error;881 882	if (!capable(CAP_SYS_ADMIN))883		return -EPERM;884	if (!xfs_has_parent(mp))885		return -EOPNOTSUPP;886	if (copy_from_user(kreq, ureq, sizeof(*kreq)))887		return -EFAULT;888 889	error = xfs_getparents(&gpx);890	if (error)891		return error;892 893	if (copy_to_user(ureq, kreq, sizeof(*kreq)))894		return -EFAULT;895 896	return 0;897}898 899/* Retrieve the parents of this file handle and pass them back to userspace. */900int901xfs_ioc_getparents_by_handle(902	struct file			*file,903	struct xfs_getparents_by_handle __user	*ureq)904{905	struct xfs_getparents_ctx	gpx = { };906	struct xfs_inode		*ip = XFS_I(file_inode(file));907	struct xfs_mount		*mp = ip->i_mount;908	struct xfs_getparents_by_handle	*kreq = &gpx.gph;909	struct xfs_handle		*handle = &kreq->gph_handle;910	int				error;911 912	if (!capable(CAP_SYS_ADMIN))913		return -EPERM;914	if (!xfs_has_parent(mp))915		return -EOPNOTSUPP;916	if (copy_from_user(kreq, ureq, sizeof(*kreq)))917		return -EFAULT;918 919	/*920	 * We don't use exportfs_decode_fh because it does too much work here.921	 * If the handle refers to a directory, the exportfs code will walk922	 * upwards through the directory tree to connect the dentries to the923	 * root directory dentry.  For GETPARENTS we don't care about that924	 * because we're not actually going to open a file descriptor; we only925	 * want to open an inode and read its parent pointers.926	 *927	 * Note that xfs_scrub uses GETPARENTS to log that it will try to fix a928	 * corrupted file's metadata.  For this usecase we would really rather929	 * userspace single-step the path reconstruction to avoid loops or930	 * other strange things if the directory tree is corrupt.931	 */932	gpx.ip = xfs_khandle_to_inode(file, handle);933	if (IS_ERR(gpx.ip))934		return PTR_ERR(gpx.ip);935 936	error = xfs_getparents(&gpx);937	if (error)938		goto out_rele;939 940	if (copy_to_user(ureq, kreq, sizeof(*kreq)))941		error = -EFAULT;942 943out_rele:944	xfs_irele(gpx.ip);945	return error;946}947