xref: /openbmc/linux/fs/xfs/xfs_ioctl.c (revision d37cf9b63113f13d742713881ce691fc615d8b3b)
10b61f8a4SDave Chinner // SPDX-License-Identifier: GPL-2.0
2c59d87c4SChristoph Hellwig /*
3c59d87c4SChristoph Hellwig  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
4c59d87c4SChristoph Hellwig  * All Rights Reserved.
5c59d87c4SChristoph Hellwig  */
6c59d87c4SChristoph Hellwig #include "xfs.h"
7c59d87c4SChristoph Hellwig #include "xfs_fs.h"
870a9883cSDave Chinner #include "xfs_shared.h"
9239880efSDave Chinner #include "xfs_format.h"
10239880efSDave Chinner #include "xfs_log_format.h"
11239880efSDave Chinner #include "xfs_trans_resv.h"
12c59d87c4SChristoph Hellwig #include "xfs_mount.h"
13c59d87c4SChristoph Hellwig #include "xfs_inode.h"
14c59d87c4SChristoph Hellwig #include "xfs_rtalloc.h"
152810bd68SDarrick J. Wong #include "xfs_iwalk.h"
16c59d87c4SChristoph Hellwig #include "xfs_itable.h"
17c59d87c4SChristoph Hellwig #include "xfs_error.h"
18e0c41089SDave Chinner #include "xfs_da_format.h"
19e0c41089SDave Chinner #include "xfs_da_btree.h"
20c59d87c4SChristoph Hellwig #include "xfs_attr.h"
21c59d87c4SChristoph Hellwig #include "xfs_bmap.h"
2268988114SDave Chinner #include "xfs_bmap_util.h"
23c59d87c4SChristoph Hellwig #include "xfs_fsops.h"
24c59d87c4SChristoph Hellwig #include "xfs_discard.h"
25c59d87c4SChristoph Hellwig #include "xfs_quota.h"
26c59d87c4SChristoph Hellwig #include "xfs_export.h"
27c59d87c4SChristoph Hellwig #include "xfs_trace.h"
288ca149deSBrian Foster #include "xfs_icache.h"
29a4fbe6abSDave Chinner #include "xfs_trans.h"
3047e1bf64SAndreas Gruenbacher #include "xfs_acl.h"
31e89c0413SDarrick J. Wong #include "xfs_btree.h"
32e89c0413SDarrick J. Wong #include <linux/fsmap.h>
33e89c0413SDarrick J. Wong #include "xfs_fsmap.h"
3436fd6e86SDarrick J. Wong #include "scrub/xfs_scrub.h"
35c368ebcdSDarrick J. Wong #include "xfs_sb.h"
367cd5006bSDarrick J. Wong #include "xfs_ag.h"
37c23232d4SDarrick J. Wong #include "xfs_health.h"
387a42c70eSChristoph Hellwig #include "xfs_reflink.h"
395f213ddbSDarrick J. Wong #include "xfs_ioctl.h"
40efc2efebSDarrick J. Wong #include "xfs_xattr.h"
417531c9abSDarrick J. Wong #include "xfs_file.h"
42c59d87c4SChristoph Hellwig 
43c59d87c4SChristoph Hellwig #include <linux/mount.h>
44c59d87c4SChristoph Hellwig #include <linux/namei.h>
459fefd5dbSMiklos Szeredi #include <linux/fileattr.h>
46c59d87c4SChristoph Hellwig 
47c59d87c4SChristoph Hellwig /*
48c59d87c4SChristoph Hellwig  * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
49c59d87c4SChristoph Hellwig  * a file or fs handle.
50c59d87c4SChristoph Hellwig  *
51c59d87c4SChristoph Hellwig  * XFS_IOC_PATH_TO_FSHANDLE
52c59d87c4SChristoph Hellwig  *    returns fs handle for a mount point or path within that mount point
53c59d87c4SChristoph Hellwig  * XFS_IOC_FD_TO_HANDLE
54c59d87c4SChristoph Hellwig  *    returns full handle for a FD opened in user space
55c59d87c4SChristoph Hellwig  * XFS_IOC_PATH_TO_HANDLE
56c59d87c4SChristoph Hellwig  *    returns full handle for a path
57c59d87c4SChristoph Hellwig  */
58c59d87c4SChristoph Hellwig int
xfs_find_handle(unsigned int cmd,xfs_fsop_handlereq_t * hreq)59c59d87c4SChristoph Hellwig xfs_find_handle(
60c59d87c4SChristoph Hellwig 	unsigned int		cmd,
61c59d87c4SChristoph Hellwig 	xfs_fsop_handlereq_t	*hreq)
62c59d87c4SChristoph Hellwig {
63c59d87c4SChristoph Hellwig 	int			hsize;
64c59d87c4SChristoph Hellwig 	xfs_handle_t		handle;
65c59d87c4SChristoph Hellwig 	struct inode		*inode;
66a30b0367SDave Chinner 	struct fd		f = {NULL};
67c59d87c4SChristoph Hellwig 	struct path		path;
682903ff01SAl Viro 	int			error;
69c59d87c4SChristoph Hellwig 	struct xfs_inode	*ip;
70c59d87c4SChristoph Hellwig 
71c59d87c4SChristoph Hellwig 	if (cmd == XFS_IOC_FD_TO_HANDLE) {
722903ff01SAl Viro 		f = fdget(hreq->fd);
732903ff01SAl Viro 		if (!f.file)
74c59d87c4SChristoph Hellwig 			return -EBADF;
75496ad9aaSAl Viro 		inode = file_inode(f.file);
76c59d87c4SChristoph Hellwig 	} else {
77ce6595a2SAl Viro 		error = user_path_at(AT_FDCWD, hreq->path, 0, &path);
78c59d87c4SChristoph Hellwig 		if (error)
79c59d87c4SChristoph Hellwig 			return error;
802b0143b5SDavid Howells 		inode = d_inode(path.dentry);
81c59d87c4SChristoph Hellwig 	}
82c59d87c4SChristoph Hellwig 	ip = XFS_I(inode);
83c59d87c4SChristoph Hellwig 
84c59d87c4SChristoph Hellwig 	/*
85c59d87c4SChristoph Hellwig 	 * We can only generate handles for inodes residing on a XFS filesystem,
86c59d87c4SChristoph Hellwig 	 * and only for regular files, directories or symbolic links.
87c59d87c4SChristoph Hellwig 	 */
88c59d87c4SChristoph Hellwig 	error = -EINVAL;
89c59d87c4SChristoph Hellwig 	if (inode->i_sb->s_magic != XFS_SB_MAGIC)
90c59d87c4SChristoph Hellwig 		goto out_put;
91c59d87c4SChristoph Hellwig 
92c59d87c4SChristoph Hellwig 	error = -EBADF;
93c59d87c4SChristoph Hellwig 	if (!S_ISREG(inode->i_mode) &&
94c59d87c4SChristoph Hellwig 	    !S_ISDIR(inode->i_mode) &&
95c59d87c4SChristoph Hellwig 	    !S_ISLNK(inode->i_mode))
96c59d87c4SChristoph Hellwig 		goto out_put;
97c59d87c4SChristoph Hellwig 
98c59d87c4SChristoph Hellwig 
99c59d87c4SChristoph Hellwig 	memcpy(&handle.ha_fsid, ip->i_mount->m_fixedfsid, sizeof(xfs_fsid_t));
100c59d87c4SChristoph Hellwig 
101c59d87c4SChristoph Hellwig 	if (cmd == XFS_IOC_PATH_TO_FSHANDLE) {
102c59d87c4SChristoph Hellwig 		/*
103c59d87c4SChristoph Hellwig 		 * This handle only contains an fsid, zero the rest.
104c59d87c4SChristoph Hellwig 		 */
105c59d87c4SChristoph Hellwig 		memset(&handle.ha_fid, 0, sizeof(handle.ha_fid));
106c59d87c4SChristoph Hellwig 		hsize = sizeof(xfs_fsid_t);
107c59d87c4SChristoph Hellwig 	} else {
108c59d87c4SChristoph Hellwig 		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
109c59d87c4SChristoph Hellwig 					sizeof(handle.ha_fid.fid_len);
110c59d87c4SChristoph Hellwig 		handle.ha_fid.fid_pad = 0;
1119e9a2674SDave Chinner 		handle.ha_fid.fid_gen = inode->i_generation;
112c59d87c4SChristoph Hellwig 		handle.ha_fid.fid_ino = ip->i_ino;
1133398a400SChristoph Hellwig 		hsize = sizeof(xfs_handle_t);
114c59d87c4SChristoph Hellwig 	}
115c59d87c4SChristoph Hellwig 
116c59d87c4SChristoph Hellwig 	error = -EFAULT;
117c59d87c4SChristoph Hellwig 	if (copy_to_user(hreq->ohandle, &handle, hsize) ||
118c59d87c4SChristoph Hellwig 	    copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
119c59d87c4SChristoph Hellwig 		goto out_put;
120c59d87c4SChristoph Hellwig 
121c59d87c4SChristoph Hellwig 	error = 0;
122c59d87c4SChristoph Hellwig 
123c59d87c4SChristoph Hellwig  out_put:
124c59d87c4SChristoph Hellwig 	if (cmd == XFS_IOC_FD_TO_HANDLE)
1252903ff01SAl Viro 		fdput(f);
126c59d87c4SChristoph Hellwig 	else
127c59d87c4SChristoph Hellwig 		path_put(&path);
128c59d87c4SChristoph Hellwig 	return error;
129c59d87c4SChristoph Hellwig }
130c59d87c4SChristoph Hellwig 
131c59d87c4SChristoph Hellwig /*
132c59d87c4SChristoph Hellwig  * No need to do permission checks on the various pathname components
133c59d87c4SChristoph Hellwig  * as the handle operations are privileged.
134c59d87c4SChristoph Hellwig  */
135c59d87c4SChristoph Hellwig STATIC int
xfs_handle_acceptable(void * context,struct dentry * dentry)136c59d87c4SChristoph Hellwig xfs_handle_acceptable(
137c59d87c4SChristoph Hellwig 	void			*context,
138c59d87c4SChristoph Hellwig 	struct dentry		*dentry)
139c59d87c4SChristoph Hellwig {
140c59d87c4SChristoph Hellwig 	return 1;
141c59d87c4SChristoph Hellwig }
142c59d87c4SChristoph Hellwig 
143c59d87c4SChristoph Hellwig /*
144c59d87c4SChristoph Hellwig  * Convert userspace handle data into a dentry.
145c59d87c4SChristoph Hellwig  */
146c59d87c4SChristoph Hellwig struct dentry *
xfs_handle_to_dentry(struct file * parfilp,void __user * uhandle,u32 hlen)147c59d87c4SChristoph Hellwig xfs_handle_to_dentry(
148c59d87c4SChristoph Hellwig 	struct file		*parfilp,
149c59d87c4SChristoph Hellwig 	void __user		*uhandle,
150c59d87c4SChristoph Hellwig 	u32			hlen)
151c59d87c4SChristoph Hellwig {
152c59d87c4SChristoph Hellwig 	xfs_handle_t		handle;
153c59d87c4SChristoph Hellwig 	struct xfs_fid64	fid;
154c59d87c4SChristoph Hellwig 
155c59d87c4SChristoph Hellwig 	/*
156c59d87c4SChristoph Hellwig 	 * Only allow handle opens under a directory.
157c59d87c4SChristoph Hellwig 	 */
158496ad9aaSAl Viro 	if (!S_ISDIR(file_inode(parfilp)->i_mode))
159c59d87c4SChristoph Hellwig 		return ERR_PTR(-ENOTDIR);
160c59d87c4SChristoph Hellwig 
161c59d87c4SChristoph Hellwig 	if (hlen != sizeof(xfs_handle_t))
162c59d87c4SChristoph Hellwig 		return ERR_PTR(-EINVAL);
163c59d87c4SChristoph Hellwig 	if (copy_from_user(&handle, uhandle, hlen))
164c59d87c4SChristoph Hellwig 		return ERR_PTR(-EFAULT);
165c59d87c4SChristoph Hellwig 	if (handle.ha_fid.fid_len !=
166c59d87c4SChristoph Hellwig 	    sizeof(handle.ha_fid) - sizeof(handle.ha_fid.fid_len))
167c59d87c4SChristoph Hellwig 		return ERR_PTR(-EINVAL);
168c59d87c4SChristoph Hellwig 
169c59d87c4SChristoph Hellwig 	memset(&fid, 0, sizeof(struct fid));
170c59d87c4SChristoph Hellwig 	fid.ino = handle.ha_fid.fid_ino;
171c59d87c4SChristoph Hellwig 	fid.gen = handle.ha_fid.fid_gen;
172c59d87c4SChristoph Hellwig 
173c59d87c4SChristoph Hellwig 	return exportfs_decode_fh(parfilp->f_path.mnt, (struct fid *)&fid, 3,
174c59d87c4SChristoph Hellwig 			FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG,
175c59d87c4SChristoph Hellwig 			xfs_handle_acceptable, NULL);
176c59d87c4SChristoph Hellwig }
177c59d87c4SChristoph Hellwig 
178c59d87c4SChristoph Hellwig STATIC struct dentry *
xfs_handlereq_to_dentry(struct file * parfilp,xfs_fsop_handlereq_t * hreq)179c59d87c4SChristoph Hellwig xfs_handlereq_to_dentry(
180c59d87c4SChristoph Hellwig 	struct file		*parfilp,
181c59d87c4SChristoph Hellwig 	xfs_fsop_handlereq_t	*hreq)
182c59d87c4SChristoph Hellwig {
183c59d87c4SChristoph Hellwig 	return xfs_handle_to_dentry(parfilp, hreq->ihandle, hreq->ihandlen);
184c59d87c4SChristoph Hellwig }
185c59d87c4SChristoph Hellwig 
186c59d87c4SChristoph Hellwig int
xfs_open_by_handle(struct file * parfilp,xfs_fsop_handlereq_t * hreq)187c59d87c4SChristoph Hellwig xfs_open_by_handle(
188c59d87c4SChristoph Hellwig 	struct file		*parfilp,
189c59d87c4SChristoph Hellwig 	xfs_fsop_handlereq_t	*hreq)
190c59d87c4SChristoph Hellwig {
191c59d87c4SChristoph Hellwig 	const struct cred	*cred = current_cred();
192c59d87c4SChristoph Hellwig 	int			error;
193c59d87c4SChristoph Hellwig 	int			fd;
194c59d87c4SChristoph Hellwig 	int			permflag;
195c59d87c4SChristoph Hellwig 	struct file		*filp;
196c59d87c4SChristoph Hellwig 	struct inode		*inode;
197c59d87c4SChristoph Hellwig 	struct dentry		*dentry;
1981a1d7724SDave Chinner 	fmode_t			fmode;
199765927b2SAl Viro 	struct path		path;
200c59d87c4SChristoph Hellwig 
201c59d87c4SChristoph Hellwig 	if (!capable(CAP_SYS_ADMIN))
202b474c7aeSEric Sandeen 		return -EPERM;
203c59d87c4SChristoph Hellwig 
204c59d87c4SChristoph Hellwig 	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
205c59d87c4SChristoph Hellwig 	if (IS_ERR(dentry))
206c59d87c4SChristoph Hellwig 		return PTR_ERR(dentry);
2072b0143b5SDavid Howells 	inode = d_inode(dentry);
208c59d87c4SChristoph Hellwig 
209c59d87c4SChristoph Hellwig 	/* Restrict xfs_open_by_handle to directories & regular files. */
210c59d87c4SChristoph Hellwig 	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
211b474c7aeSEric Sandeen 		error = -EPERM;
212c59d87c4SChristoph Hellwig 		goto out_dput;
213c59d87c4SChristoph Hellwig 	}
214c59d87c4SChristoph Hellwig 
215c59d87c4SChristoph Hellwig #if BITS_PER_LONG != 32
216c59d87c4SChristoph Hellwig 	hreq->oflags |= O_LARGEFILE;
217c59d87c4SChristoph Hellwig #endif
218c59d87c4SChristoph Hellwig 
219c59d87c4SChristoph Hellwig 	permflag = hreq->oflags;
2201a1d7724SDave Chinner 	fmode = OPEN_FMODE(permflag);
221c59d87c4SChristoph Hellwig 	if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
2221a1d7724SDave Chinner 	    (fmode & FMODE_WRITE) && IS_APPEND(inode)) {
223b474c7aeSEric Sandeen 		error = -EPERM;
224c59d87c4SChristoph Hellwig 		goto out_dput;
225c59d87c4SChristoph Hellwig 	}
226c59d87c4SChristoph Hellwig 
2271a1d7724SDave Chinner 	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
228337684a1SEryu Guan 		error = -EPERM;
229c59d87c4SChristoph Hellwig 		goto out_dput;
230c59d87c4SChristoph Hellwig 	}
231c59d87c4SChristoph Hellwig 
232c59d87c4SChristoph Hellwig 	/* Can't write directories. */
2331a1d7724SDave Chinner 	if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {
234b474c7aeSEric Sandeen 		error = -EISDIR;
235c59d87c4SChristoph Hellwig 		goto out_dput;
236c59d87c4SChristoph Hellwig 	}
237c59d87c4SChristoph Hellwig 
238862a6293SYann Droneaud 	fd = get_unused_fd_flags(0);
239c59d87c4SChristoph Hellwig 	if (fd < 0) {
240c59d87c4SChristoph Hellwig 		error = fd;
241c59d87c4SChristoph Hellwig 		goto out_dput;
242c59d87c4SChristoph Hellwig 	}
243c59d87c4SChristoph Hellwig 
244765927b2SAl Viro 	path.mnt = parfilp->f_path.mnt;
245765927b2SAl Viro 	path.dentry = dentry;
246765927b2SAl Viro 	filp = dentry_open(&path, hreq->oflags, cred);
247765927b2SAl Viro 	dput(dentry);
248c59d87c4SChristoph Hellwig 	if (IS_ERR(filp)) {
249c59d87c4SChristoph Hellwig 		put_unused_fd(fd);
250c59d87c4SChristoph Hellwig 		return PTR_ERR(filp);
251c59d87c4SChristoph Hellwig 	}
252c59d87c4SChristoph Hellwig 
253c59d87c4SChristoph Hellwig 	if (S_ISREG(inode->i_mode)) {
254c59d87c4SChristoph Hellwig 		filp->f_flags |= O_NOATIME;
255c59d87c4SChristoph Hellwig 		filp->f_mode |= FMODE_NOCMTIME;
256c59d87c4SChristoph Hellwig 	}
257c59d87c4SChristoph Hellwig 
258c59d87c4SChristoph Hellwig 	fd_install(fd, filp);
259c59d87c4SChristoph Hellwig 	return fd;
260c59d87c4SChristoph Hellwig 
261c59d87c4SChristoph Hellwig  out_dput:
262c59d87c4SChristoph Hellwig 	dput(dentry);
263c59d87c4SChristoph Hellwig 	return error;
264c59d87c4SChristoph Hellwig }
265c59d87c4SChristoph Hellwig 
266c59d87c4SChristoph Hellwig int
xfs_readlink_by_handle(struct file * parfilp,xfs_fsop_handlereq_t * hreq)267c59d87c4SChristoph Hellwig xfs_readlink_by_handle(
268c59d87c4SChristoph Hellwig 	struct file		*parfilp,
269c59d87c4SChristoph Hellwig 	xfs_fsop_handlereq_t	*hreq)
270c59d87c4SChristoph Hellwig {
271c59d87c4SChristoph Hellwig 	struct dentry		*dentry;
272c59d87c4SChristoph Hellwig 	__u32			olen;
273c59d87c4SChristoph Hellwig 	int			error;
274c59d87c4SChristoph Hellwig 
275c59d87c4SChristoph Hellwig 	if (!capable(CAP_SYS_ADMIN))
276b474c7aeSEric Sandeen 		return -EPERM;
277c59d87c4SChristoph Hellwig 
278c59d87c4SChristoph Hellwig 	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
279c59d87c4SChristoph Hellwig 	if (IS_ERR(dentry))
280c59d87c4SChristoph Hellwig 		return PTR_ERR(dentry);
281c59d87c4SChristoph Hellwig 
282c59d87c4SChristoph Hellwig 	/* Restrict this handle operation to symlinks only. */
283fd4a0edfSMiklos Szeredi 	if (!d_is_symlink(dentry)) {
284b474c7aeSEric Sandeen 		error = -EINVAL;
285c59d87c4SChristoph Hellwig 		goto out_dput;
286c59d87c4SChristoph Hellwig 	}
287c59d87c4SChristoph Hellwig 
288c59d87c4SChristoph Hellwig 	if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
289b474c7aeSEric Sandeen 		error = -EFAULT;
290c59d87c4SChristoph Hellwig 		goto out_dput;
291c59d87c4SChristoph Hellwig 	}
292c59d87c4SChristoph Hellwig 
293fd4a0edfSMiklos Szeredi 	error = vfs_readlink(dentry, hreq->ohandle, olen);
294c59d87c4SChristoph Hellwig 
295c59d87c4SChristoph Hellwig  out_dput:
296c59d87c4SChristoph Hellwig 	dput(dentry);
297c59d87c4SChristoph Hellwig 	return error;
298c59d87c4SChristoph Hellwig }
299c59d87c4SChristoph Hellwig 
3003e7a7799SChristoph Hellwig /*
3013e7a7799SChristoph Hellwig  * Format an attribute and copy it out to the user's buffer.
3023e7a7799SChristoph Hellwig  * Take care to check values and protect against them changing later,
3033e7a7799SChristoph Hellwig  * we may be reading them directly out of a user buffer.
3043e7a7799SChristoph Hellwig  */
3053e7a7799SChristoph Hellwig static void
xfs_ioc_attr_put_listent(struct xfs_attr_list_context * context,int flags,unsigned char * name,int namelen,int valuelen)3063e7a7799SChristoph Hellwig xfs_ioc_attr_put_listent(
3073e7a7799SChristoph Hellwig 	struct xfs_attr_list_context *context,
3083e7a7799SChristoph Hellwig 	int			flags,
3093e7a7799SChristoph Hellwig 	unsigned char		*name,
3103e7a7799SChristoph Hellwig 	int			namelen,
3113e7a7799SChristoph Hellwig 	int			valuelen)
3123e7a7799SChristoph Hellwig {
3133e7a7799SChristoph Hellwig 	struct xfs_attrlist	*alist = context->buffer;
3143e7a7799SChristoph Hellwig 	struct xfs_attrlist_ent	*aep;
3153e7a7799SChristoph Hellwig 	int			arraytop;
3163e7a7799SChristoph Hellwig 
3173e7a7799SChristoph Hellwig 	ASSERT(!context->seen_enough);
3183e7a7799SChristoph Hellwig 	ASSERT(context->count >= 0);
3193e7a7799SChristoph Hellwig 	ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
3203e7a7799SChristoph Hellwig 	ASSERT(context->firstu >= sizeof(*alist));
3213e7a7799SChristoph Hellwig 	ASSERT(context->firstu <= context->bufsize);
3223e7a7799SChristoph Hellwig 
3233e7a7799SChristoph Hellwig 	/*
3243e7a7799SChristoph Hellwig 	 * Only list entries in the right namespace.
3253e7a7799SChristoph Hellwig 	 */
326d5f0f49aSChristoph Hellwig 	if (context->attr_filter != (flags & XFS_ATTR_NSP_ONDISK_MASK))
3273e7a7799SChristoph Hellwig 		return;
3283e7a7799SChristoph Hellwig 
3293e7a7799SChristoph Hellwig 	arraytop = sizeof(*alist) +
3303e7a7799SChristoph Hellwig 			context->count * sizeof(alist->al_offset[0]);
3313e7a7799SChristoph Hellwig 
3323e7a7799SChristoph Hellwig 	/* decrement by the actual bytes used by the attr */
3333e7a7799SChristoph Hellwig 	context->firstu -= round_up(offsetof(struct xfs_attrlist_ent, a_name) +
3343e7a7799SChristoph Hellwig 			namelen + 1, sizeof(uint32_t));
3353e7a7799SChristoph Hellwig 	if (context->firstu < arraytop) {
3363e7a7799SChristoph Hellwig 		trace_xfs_attr_list_full(context);
3373e7a7799SChristoph Hellwig 		alist->al_more = 1;
3383e7a7799SChristoph Hellwig 		context->seen_enough = 1;
3393e7a7799SChristoph Hellwig 		return;
3403e7a7799SChristoph Hellwig 	}
3413e7a7799SChristoph Hellwig 
3423e7a7799SChristoph Hellwig 	aep = context->buffer + context->firstu;
3433e7a7799SChristoph Hellwig 	aep->a_valuelen = valuelen;
3443e7a7799SChristoph Hellwig 	memcpy(aep->a_name, name, namelen);
3453e7a7799SChristoph Hellwig 	aep->a_name[namelen] = 0;
3463e7a7799SChristoph Hellwig 	alist->al_offset[context->count++] = context->firstu;
3473e7a7799SChristoph Hellwig 	alist->al_count = context->count;
3483e7a7799SChristoph Hellwig 	trace_xfs_attr_list_add(context);
3493e7a7799SChristoph Hellwig }
3503e7a7799SChristoph Hellwig 
351d5f0f49aSChristoph Hellwig static unsigned int
xfs_attr_filter(u32 ioc_flags)352d5f0f49aSChristoph Hellwig xfs_attr_filter(
353d5f0f49aSChristoph Hellwig 	u32			ioc_flags)
354d5f0f49aSChristoph Hellwig {
355d5f0f49aSChristoph Hellwig 	if (ioc_flags & XFS_IOC_ATTR_ROOT)
356d5f0f49aSChristoph Hellwig 		return XFS_ATTR_ROOT;
357d5f0f49aSChristoph Hellwig 	if (ioc_flags & XFS_IOC_ATTR_SECURE)
358d5f0f49aSChristoph Hellwig 		return XFS_ATTR_SECURE;
359d5f0f49aSChristoph Hellwig 	return 0;
360d5f0f49aSChristoph Hellwig }
361d5f0f49aSChristoph Hellwig 
362d5f0f49aSChristoph Hellwig static unsigned int
xfs_attr_flags(u32 ioc_flags)363d5f0f49aSChristoph Hellwig xfs_attr_flags(
364d5f0f49aSChristoph Hellwig 	u32			ioc_flags)
365d5f0f49aSChristoph Hellwig {
366d5f0f49aSChristoph Hellwig 	if (ioc_flags & XFS_IOC_ATTR_CREATE)
367d5f0f49aSChristoph Hellwig 		return XATTR_CREATE;
368d5f0f49aSChristoph Hellwig 	if (ioc_flags & XFS_IOC_ATTR_REPLACE)
369d5f0f49aSChristoph Hellwig 		return XATTR_REPLACE;
370d5f0f49aSChristoph Hellwig 	return 0;
371d5f0f49aSChristoph Hellwig }
372d5f0f49aSChristoph Hellwig 
3733e7a7799SChristoph Hellwig int
xfs_ioc_attr_list(struct xfs_inode * dp,void __user * ubuf,size_t bufsize,int flags,struct xfs_attrlist_cursor __user * ucursor)3743e7a7799SChristoph Hellwig xfs_ioc_attr_list(
3753e7a7799SChristoph Hellwig 	struct xfs_inode		*dp,
376eb241c74SChristoph Hellwig 	void __user			*ubuf,
3776ed6356bSDan Carpenter 	size_t				bufsize,
3783e7a7799SChristoph Hellwig 	int				flags,
37953ac39fdSChristoph Hellwig 	struct xfs_attrlist_cursor __user *ucursor)
3803e7a7799SChristoph Hellwig {
381e3a19cdeSChristoph Hellwig 	struct xfs_attr_list_context	context = { };
3823e7a7799SChristoph Hellwig 	struct xfs_attrlist		*alist;
383eb241c74SChristoph Hellwig 	void				*buffer;
3843e7a7799SChristoph Hellwig 	int				error;
3853e7a7799SChristoph Hellwig 
386f6046319SChristoph Hellwig 	if (bufsize < sizeof(struct xfs_attrlist) ||
387f6046319SChristoph Hellwig 	    bufsize > XFS_XATTR_LIST_MAX)
388f6046319SChristoph Hellwig 		return -EINVAL;
389f6046319SChristoph Hellwig 
390f6046319SChristoph Hellwig 	/*
391f6046319SChristoph Hellwig 	 * Reject flags, only allow namespaces.
392f6046319SChristoph Hellwig 	 */
393d5f0f49aSChristoph Hellwig 	if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
394f6046319SChristoph Hellwig 		return -EINVAL;
395d5f0f49aSChristoph Hellwig 	if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
396f6046319SChristoph Hellwig 		return -EINVAL;
397f6046319SChristoph Hellwig 
3983e7a7799SChristoph Hellwig 	/*
3993e7a7799SChristoph Hellwig 	 * Validate the cursor.
4003e7a7799SChristoph Hellwig 	 */
401e3a19cdeSChristoph Hellwig 	if (copy_from_user(&context.cursor, ucursor, sizeof(context.cursor)))
40253ac39fdSChristoph Hellwig 		return -EFAULT;
403e3a19cdeSChristoph Hellwig 	if (context.cursor.pad1 || context.cursor.pad2)
4043e7a7799SChristoph Hellwig 		return -EINVAL;
405e3a19cdeSChristoph Hellwig 	if (!context.cursor.initted &&
406e3a19cdeSChristoph Hellwig 	    (context.cursor.hashval || context.cursor.blkno ||
407e3a19cdeSChristoph Hellwig 	     context.cursor.offset))
4083e7a7799SChristoph Hellwig 		return -EINVAL;
4093e7a7799SChristoph Hellwig 
4108ca79df8SCarlos Maiolino 	buffer = kvzalloc(bufsize, GFP_KERNEL);
411eb241c74SChristoph Hellwig 	if (!buffer)
412eb241c74SChristoph Hellwig 		return -ENOMEM;
4133e7a7799SChristoph Hellwig 
4143e7a7799SChristoph Hellwig 	/*
4153e7a7799SChristoph Hellwig 	 * Initialize the output buffer.
4163e7a7799SChristoph Hellwig 	 */
4173e7a7799SChristoph Hellwig 	context.dp = dp;
4183e7a7799SChristoph Hellwig 	context.resynch = 1;
419d5f0f49aSChristoph Hellwig 	context.attr_filter = xfs_attr_filter(flags);
4203e7a7799SChristoph Hellwig 	context.buffer = buffer;
421f311d771SChristoph Hellwig 	context.bufsize = round_down(bufsize, sizeof(uint32_t));
4223e7a7799SChristoph Hellwig 	context.firstu = context.bufsize;
4233e7a7799SChristoph Hellwig 	context.put_listent = xfs_ioc_attr_put_listent;
4243e7a7799SChristoph Hellwig 
4253e7a7799SChristoph Hellwig 	alist = context.buffer;
4263e7a7799SChristoph Hellwig 	alist->al_count = 0;
4273e7a7799SChristoph Hellwig 	alist->al_more = 0;
4283e7a7799SChristoph Hellwig 	alist->al_offset[0] = context.bufsize;
4293e7a7799SChristoph Hellwig 
43017e1dd83SChristoph Hellwig 	error = xfs_attr_list(&context);
431eb241c74SChristoph Hellwig 	if (error)
432eb241c74SChristoph Hellwig 		goto out_free;
433eb241c74SChristoph Hellwig 
43453ac39fdSChristoph Hellwig 	if (copy_to_user(ubuf, buffer, bufsize) ||
435e3a19cdeSChristoph Hellwig 	    copy_to_user(ucursor, &context.cursor, sizeof(context.cursor)))
436eb241c74SChristoph Hellwig 		error = -EFAULT;
437eb241c74SChristoph Hellwig out_free:
438eb241c74SChristoph Hellwig 	kmem_free(buffer);
4393e7a7799SChristoph Hellwig 	return error;
4403e7a7799SChristoph Hellwig }
4413e7a7799SChristoph Hellwig 
442c59d87c4SChristoph Hellwig STATIC int
xfs_attrlist_by_handle(struct file * parfilp,struct xfs_fsop_attrlist_handlereq __user * p)443c59d87c4SChristoph Hellwig xfs_attrlist_by_handle(
444c59d87c4SChristoph Hellwig 	struct file		*parfilp,
44553ac39fdSChristoph Hellwig 	struct xfs_fsop_attrlist_handlereq __user *p)
446c59d87c4SChristoph Hellwig {
44753ac39fdSChristoph Hellwig 	struct xfs_fsop_attrlist_handlereq al_hreq;
448c59d87c4SChristoph Hellwig 	struct dentry		*dentry;
44953ac39fdSChristoph Hellwig 	int			error = -ENOMEM;
450c59d87c4SChristoph Hellwig 
451c59d87c4SChristoph Hellwig 	if (!capable(CAP_SYS_ADMIN))
452b474c7aeSEric Sandeen 		return -EPERM;
45353ac39fdSChristoph Hellwig 	if (copy_from_user(&al_hreq, p, sizeof(al_hreq)))
454b474c7aeSEric Sandeen 		return -EFAULT;
455c59d87c4SChristoph Hellwig 
456c59d87c4SChristoph Hellwig 	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
457c59d87c4SChristoph Hellwig 	if (IS_ERR(dentry))
458c59d87c4SChristoph Hellwig 		return PTR_ERR(dentry);
459c59d87c4SChristoph Hellwig 
460eb241c74SChristoph Hellwig 	error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)), al_hreq.buffer,
46153ac39fdSChristoph Hellwig 				  al_hreq.buflen, al_hreq.flags, &p->pos);
462c59d87c4SChristoph Hellwig 	dput(dentry);
463c59d87c4SChristoph Hellwig 	return error;
464c59d87c4SChristoph Hellwig }
465c59d87c4SChristoph Hellwig 
466d0ce6439SChristoph Hellwig static int
xfs_attrmulti_attr_get(struct inode * inode,unsigned char * name,unsigned char __user * ubuf,uint32_t * len,uint32_t flags)467c59d87c4SChristoph Hellwig xfs_attrmulti_attr_get(
468c59d87c4SChristoph Hellwig 	struct inode		*inode,
469c59d87c4SChristoph Hellwig 	unsigned char		*name,
470c59d87c4SChristoph Hellwig 	unsigned char		__user *ubuf,
471c8ce540dSDarrick J. Wong 	uint32_t		*len,
472c8ce540dSDarrick J. Wong 	uint32_t		flags)
473c59d87c4SChristoph Hellwig {
474e5171d7eSChristoph Hellwig 	struct xfs_da_args	args = {
475e5171d7eSChristoph Hellwig 		.dp		= XFS_I(inode),
476d5f0f49aSChristoph Hellwig 		.attr_filter	= xfs_attr_filter(flags),
477d5f0f49aSChristoph Hellwig 		.attr_flags	= xfs_attr_flags(flags),
478e5171d7eSChristoph Hellwig 		.name		= name,
479e5171d7eSChristoph Hellwig 		.namelen	= strlen(name),
480e5171d7eSChristoph Hellwig 		.valuelen	= *len,
481e5171d7eSChristoph Hellwig 	};
482e5171d7eSChristoph Hellwig 	int			error;
483c59d87c4SChristoph Hellwig 
48451fcbfe7SJan Tulak 	if (*len > XFS_XATTR_SIZE_MAX)
4852451337dSDave Chinner 		return -EINVAL;
486e5171d7eSChristoph Hellwig 
487e5171d7eSChristoph Hellwig 	error = xfs_attr_get(&args);
488c59d87c4SChristoph Hellwig 	if (error)
489c59d87c4SChristoph Hellwig 		goto out_kfree;
490c59d87c4SChristoph Hellwig 
491e5171d7eSChristoph Hellwig 	*len = args.valuelen;
492e5171d7eSChristoph Hellwig 	if (copy_to_user(ubuf, args.value, args.valuelen))
4932451337dSDave Chinner 		error = -EFAULT;
494c59d87c4SChristoph Hellwig 
495c59d87c4SChristoph Hellwig out_kfree:
496e5171d7eSChristoph Hellwig 	kmem_free(args.value);
497c59d87c4SChristoph Hellwig 	return error;
498c59d87c4SChristoph Hellwig }
499c59d87c4SChristoph Hellwig 
500d0ce6439SChristoph Hellwig static int
xfs_attrmulti_attr_set(struct inode * inode,unsigned char * name,const unsigned char __user * ubuf,uint32_t len,uint32_t flags)501c59d87c4SChristoph Hellwig xfs_attrmulti_attr_set(
502c59d87c4SChristoph Hellwig 	struct inode		*inode,
503c59d87c4SChristoph Hellwig 	unsigned char		*name,
504c59d87c4SChristoph Hellwig 	const unsigned char	__user *ubuf,
505c8ce540dSDarrick J. Wong 	uint32_t		len,
506c8ce540dSDarrick J. Wong 	uint32_t		flags)
507c59d87c4SChristoph Hellwig {
508a2544622SChristoph Hellwig 	struct xfs_da_args	args = {
509a2544622SChristoph Hellwig 		.dp		= XFS_I(inode),
510d5f0f49aSChristoph Hellwig 		.attr_filter	= xfs_attr_filter(flags),
511d5f0f49aSChristoph Hellwig 		.attr_flags	= xfs_attr_flags(flags),
512a2544622SChristoph Hellwig 		.name		= name,
513a2544622SChristoph Hellwig 		.namelen	= strlen(name),
514a2544622SChristoph Hellwig 	};
51509cb22d2SAndreas Gruenbacher 	int			error;
516c59d87c4SChristoph Hellwig 
517c59d87c4SChristoph Hellwig 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
5182451337dSDave Chinner 		return -EPERM;
5196cc4f4ffSChristoph Hellwig 
5206cc4f4ffSChristoph Hellwig 	if (ubuf) {
52151fcbfe7SJan Tulak 		if (len > XFS_XATTR_SIZE_MAX)
5222451337dSDave Chinner 			return -EINVAL;
523a2544622SChristoph Hellwig 		args.value = memdup_user(ubuf, len);
524a2544622SChristoph Hellwig 		if (IS_ERR(args.value))
525a2544622SChristoph Hellwig 			return PTR_ERR(args.value);
526a2544622SChristoph Hellwig 		args.valuelen = len;
5276cc4f4ffSChristoph Hellwig 	}
528c59d87c4SChristoph Hellwig 
529efc2efebSDarrick J. Wong 	error = xfs_attr_change(&args);
530d5f0f49aSChristoph Hellwig 	if (!error && (flags & XFS_IOC_ATTR_ROOT))
5315a3930e2SChristoph Hellwig 		xfs_forget_acl(inode, name);
532a2544622SChristoph Hellwig 	kfree(args.value);
53309cb22d2SAndreas Gruenbacher 	return error;
534c59d87c4SChristoph Hellwig }
535c59d87c4SChristoph Hellwig 
536d0ce6439SChristoph Hellwig int
xfs_ioc_attrmulti_one(struct file * parfilp,struct inode * inode,uint32_t opcode,void __user * uname,void __user * value,uint32_t * len,uint32_t flags)537d0ce6439SChristoph Hellwig xfs_ioc_attrmulti_one(
538d0ce6439SChristoph Hellwig 	struct file		*parfilp,
539d0ce6439SChristoph Hellwig 	struct inode		*inode,
540d0ce6439SChristoph Hellwig 	uint32_t		opcode,
541d0ce6439SChristoph Hellwig 	void __user		*uname,
542d0ce6439SChristoph Hellwig 	void __user		*value,
543d0ce6439SChristoph Hellwig 	uint32_t		*len,
544d0ce6439SChristoph Hellwig 	uint32_t		flags)
545d0ce6439SChristoph Hellwig {
546d0ce6439SChristoph Hellwig 	unsigned char		*name;
547d0ce6439SChristoph Hellwig 	int			error;
548d0ce6439SChristoph Hellwig 
549d5f0f49aSChristoph Hellwig 	if ((flags & XFS_IOC_ATTR_ROOT) && (flags & XFS_IOC_ATTR_SECURE))
550d0ce6439SChristoph Hellwig 		return -EINVAL;
551d0ce6439SChristoph Hellwig 
552d0ce6439SChristoph Hellwig 	name = strndup_user(uname, MAXNAMELEN);
553d0ce6439SChristoph Hellwig 	if (IS_ERR(name))
554d0ce6439SChristoph Hellwig 		return PTR_ERR(name);
555d0ce6439SChristoph Hellwig 
556d0ce6439SChristoph Hellwig 	switch (opcode) {
557d0ce6439SChristoph Hellwig 	case ATTR_OP_GET:
558d0ce6439SChristoph Hellwig 		error = xfs_attrmulti_attr_get(inode, name, value, len, flags);
559d0ce6439SChristoph Hellwig 		break;
560d0ce6439SChristoph Hellwig 	case ATTR_OP_REMOVE:
561d0ce6439SChristoph Hellwig 		value = NULL;
562d0ce6439SChristoph Hellwig 		*len = 0;
56353004ee7SGustavo A. R. Silva 		fallthrough;
564d0ce6439SChristoph Hellwig 	case ATTR_OP_SET:
565d0ce6439SChristoph Hellwig 		error = mnt_want_write_file(parfilp);
566d0ce6439SChristoph Hellwig 		if (error)
567d0ce6439SChristoph Hellwig 			break;
568d0ce6439SChristoph Hellwig 		error = xfs_attrmulti_attr_set(inode, name, value, *len, flags);
569d0ce6439SChristoph Hellwig 		mnt_drop_write_file(parfilp);
570d0ce6439SChristoph Hellwig 		break;
571d0ce6439SChristoph Hellwig 	default:
572d0ce6439SChristoph Hellwig 		error = -EINVAL;
573d0ce6439SChristoph Hellwig 		break;
574d0ce6439SChristoph Hellwig 	}
575d0ce6439SChristoph Hellwig 
576d0ce6439SChristoph Hellwig 	kfree(name);
577d0ce6439SChristoph Hellwig 	return error;
578d0ce6439SChristoph Hellwig }
579d0ce6439SChristoph Hellwig 
580c59d87c4SChristoph Hellwig STATIC int
xfs_attrmulti_by_handle(struct file * parfilp,void __user * arg)581c59d87c4SChristoph Hellwig xfs_attrmulti_by_handle(
582c59d87c4SChristoph Hellwig 	struct file		*parfilp,
583c59d87c4SChristoph Hellwig 	void			__user *arg)
584c59d87c4SChristoph Hellwig {
585c59d87c4SChristoph Hellwig 	int			error;
586c59d87c4SChristoph Hellwig 	xfs_attr_multiop_t	*ops;
587c59d87c4SChristoph Hellwig 	xfs_fsop_attrmulti_handlereq_t am_hreq;
588c59d87c4SChristoph Hellwig 	struct dentry		*dentry;
589c59d87c4SChristoph Hellwig 	unsigned int		i, size;
590c59d87c4SChristoph Hellwig 
591c59d87c4SChristoph Hellwig 	if (!capable(CAP_SYS_ADMIN))
592b474c7aeSEric Sandeen 		return -EPERM;
593c59d87c4SChristoph Hellwig 	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
594b474c7aeSEric Sandeen 		return -EFAULT;
595c59d87c4SChristoph Hellwig 
596c59d87c4SChristoph Hellwig 	/* overflow check */
597c59d87c4SChristoph Hellwig 	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
598c59d87c4SChristoph Hellwig 		return -E2BIG;
599c59d87c4SChristoph Hellwig 
600c59d87c4SChristoph Hellwig 	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
601c59d87c4SChristoph Hellwig 	if (IS_ERR(dentry))
602c59d87c4SChristoph Hellwig 		return PTR_ERR(dentry);
603c59d87c4SChristoph Hellwig 
6042451337dSDave Chinner 	error = -E2BIG;
605c59d87c4SChristoph Hellwig 	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
606c59d87c4SChristoph Hellwig 	if (!size || size > 16 * PAGE_SIZE)
607c59d87c4SChristoph Hellwig 		goto out_dput;
608c59d87c4SChristoph Hellwig 
609c59d87c4SChristoph Hellwig 	ops = memdup_user(am_hreq.ops, size);
610c59d87c4SChristoph Hellwig 	if (IS_ERR(ops)) {
6112451337dSDave Chinner 		error = PTR_ERR(ops);
612c59d87c4SChristoph Hellwig 		goto out_dput;
613c59d87c4SChristoph Hellwig 	}
614c59d87c4SChristoph Hellwig 
615c59d87c4SChristoph Hellwig 	error = 0;
616c59d87c4SChristoph Hellwig 	for (i = 0; i < am_hreq.opcount; i++) {
617d0ce6439SChristoph Hellwig 		ops[i].am_error = xfs_ioc_attrmulti_one(parfilp,
618d0ce6439SChristoph Hellwig 				d_inode(dentry), ops[i].am_opcode,
619d0ce6439SChristoph Hellwig 				ops[i].am_attrname, ops[i].am_attrvalue,
620d0ce6439SChristoph Hellwig 				&ops[i].am_length, ops[i].am_flags);
621c59d87c4SChristoph Hellwig 	}
622c59d87c4SChristoph Hellwig 
623c59d87c4SChristoph Hellwig 	if (copy_to_user(am_hreq.ops, ops, size))
6242451337dSDave Chinner 		error = -EFAULT;
625c59d87c4SChristoph Hellwig 
626c59d87c4SChristoph Hellwig 	kfree(ops);
627c59d87c4SChristoph Hellwig  out_dput:
628c59d87c4SChristoph Hellwig 	dput(dentry);
6292451337dSDave Chinner 	return error;
630c59d87c4SChristoph Hellwig }
631c59d87c4SChristoph Hellwig 
6322810bd68SDarrick J. Wong /* Return 0 on success or positive error */
6332810bd68SDarrick J. Wong int
xfs_fsbulkstat_one_fmt(struct xfs_ibulk * breq,const struct xfs_bulkstat * bstat)6348bfe9d18SDarrick J. Wong xfs_fsbulkstat_one_fmt(
6352810bd68SDarrick J. Wong 	struct xfs_ibulk		*breq,
6367035f972SDarrick J. Wong 	const struct xfs_bulkstat	*bstat)
6372810bd68SDarrick J. Wong {
6387035f972SDarrick J. Wong 	struct xfs_bstat		bs1;
6397035f972SDarrick J. Wong 
6407035f972SDarrick J. Wong 	xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);
6417035f972SDarrick J. Wong 	if (copy_to_user(breq->ubuffer, &bs1, sizeof(bs1)))
6422810bd68SDarrick J. Wong 		return -EFAULT;
6432810bd68SDarrick J. Wong 	return xfs_ibulk_advance(breq, sizeof(struct xfs_bstat));
6442810bd68SDarrick J. Wong }
6452810bd68SDarrick J. Wong 
646677717fbSDarrick J. Wong int
xfs_fsinumbers_fmt(struct xfs_ibulk * breq,const struct xfs_inumbers * igrp)6478bfe9d18SDarrick J. Wong xfs_fsinumbers_fmt(
648677717fbSDarrick J. Wong 	struct xfs_ibulk		*breq,
6495f19c7fcSDarrick J. Wong 	const struct xfs_inumbers	*igrp)
650677717fbSDarrick J. Wong {
6515f19c7fcSDarrick J. Wong 	struct xfs_inogrp		ig1;
6525f19c7fcSDarrick J. Wong 
6535f19c7fcSDarrick J. Wong 	xfs_inumbers_to_inogrp(&ig1, igrp);
6545f19c7fcSDarrick J. Wong 	if (copy_to_user(breq->ubuffer, &ig1, sizeof(struct xfs_inogrp)))
655677717fbSDarrick J. Wong 		return -EFAULT;
656677717fbSDarrick J. Wong 	return xfs_ibulk_advance(breq, sizeof(struct xfs_inogrp));
657677717fbSDarrick J. Wong }
658677717fbSDarrick J. Wong 
659c59d87c4SChristoph Hellwig STATIC int
xfs_ioc_fsbulkstat(struct file * file,unsigned int cmd,void __user * arg)6608bfe9d18SDarrick J. Wong xfs_ioc_fsbulkstat(
661f736d93dSChristoph Hellwig 	struct file		*file,
662c59d87c4SChristoph Hellwig 	unsigned int		cmd,
663c59d87c4SChristoph Hellwig 	void			__user *arg)
664c59d87c4SChristoph Hellwig {
665f736d93dSChristoph Hellwig 	struct xfs_mount	*mp = XFS_I(file_inode(file))->i_mount;
6662810bd68SDarrick J. Wong 	struct xfs_fsop_bulkreq	bulkreq;
6672810bd68SDarrick J. Wong 	struct xfs_ibulk	breq = {
6682810bd68SDarrick J. Wong 		.mp		= mp,
669e67fe633SChristian Brauner 		.idmap		= file_mnt_idmap(file),
6702810bd68SDarrick J. Wong 		.ocount		= 0,
6712810bd68SDarrick J. Wong 	};
6722810bd68SDarrick J. Wong 	xfs_ino_t		lastino;
673c59d87c4SChristoph Hellwig 	int			error;
674c59d87c4SChristoph Hellwig 
675c59d87c4SChristoph Hellwig 	/* done = 1 if there are more stats to get and if bulkstat */
676c59d87c4SChristoph Hellwig 	/* should be called again (unused here, but used in dmapi) */
677c59d87c4SChristoph Hellwig 
678c59d87c4SChristoph Hellwig 	if (!capable(CAP_SYS_ADMIN))
679c59d87c4SChristoph Hellwig 		return -EPERM;
680c59d87c4SChristoph Hellwig 
68175c8c50fSDave Chinner 	if (xfs_is_shutdown(mp))
682b474c7aeSEric Sandeen 		return -EIO;
683c59d87c4SChristoph Hellwig 
6846f71fb68SDarrick J. Wong 	if (copy_from_user(&bulkreq, arg, sizeof(struct xfs_fsop_bulkreq)))
685b474c7aeSEric Sandeen 		return -EFAULT;
686c59d87c4SChristoph Hellwig 
6872810bd68SDarrick J. Wong 	if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64)))
688b474c7aeSEric Sandeen 		return -EFAULT;
689c59d87c4SChristoph Hellwig 
6902810bd68SDarrick J. Wong 	if (bulkreq.icount <= 0)
691b474c7aeSEric Sandeen 		return -EINVAL;
692c59d87c4SChristoph Hellwig 
693c59d87c4SChristoph Hellwig 	if (bulkreq.ubuffer == NULL)
694b474c7aeSEric Sandeen 		return -EINVAL;
695c59d87c4SChristoph Hellwig 
6962810bd68SDarrick J. Wong 	breq.ubuffer = bulkreq.ubuffer;
6972810bd68SDarrick J. Wong 	breq.icount = bulkreq.icount;
6982810bd68SDarrick J. Wong 
6992810bd68SDarrick J. Wong 	/*
7002810bd68SDarrick J. Wong 	 * FSBULKSTAT_SINGLE expects that *lastip contains the inode number
7012810bd68SDarrick J. Wong 	 * that we want to stat.  However, FSINUMBERS and FSBULKSTAT expect
7022810bd68SDarrick J. Wong 	 * that *lastip contains either zero or the number of the last inode to
7032810bd68SDarrick J. Wong 	 * be examined by the previous call and return results starting with
7042810bd68SDarrick J. Wong 	 * the next inode after that.  The new bulk request back end functions
7052810bd68SDarrick J. Wong 	 * take the inode to start with, so we have to compute the startino
7062810bd68SDarrick J. Wong 	 * parameter from lastino to maintain correct function.  lastino == 0
7072810bd68SDarrick J. Wong 	 * is a special case because it has traditionally meant "first inode
7082810bd68SDarrick J. Wong 	 * in filesystem".
7092810bd68SDarrick J. Wong 	 */
7102810bd68SDarrick J. Wong 	if (cmd == XFS_IOC_FSINUMBERS) {
711677717fbSDarrick J. Wong 		breq.startino = lastino ? lastino + 1 : 0;
7128bfe9d18SDarrick J. Wong 		error = xfs_inumbers(&breq, xfs_fsinumbers_fmt);
713677717fbSDarrick J. Wong 		lastino = breq.startino - 1;
7142810bd68SDarrick J. Wong 	} else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) {
7152810bd68SDarrick J. Wong 		breq.startino = lastino;
7162810bd68SDarrick J. Wong 		breq.icount = 1;
7178bfe9d18SDarrick J. Wong 		error = xfs_bulkstat_one(&breq, xfs_fsbulkstat_one_fmt);
7182810bd68SDarrick J. Wong 	} else {	/* XFS_IOC_FSBULKSTAT */
7192810bd68SDarrick J. Wong 		breq.startino = lastino ? lastino + 1 : 0;
7208bfe9d18SDarrick J. Wong 		error = xfs_bulkstat(&breq, xfs_fsbulkstat_one_fmt);
7212810bd68SDarrick J. Wong 		lastino = breq.startino - 1;
7222810bd68SDarrick J. Wong 	}
723c59d87c4SChristoph Hellwig 
724c59d87c4SChristoph Hellwig 	if (error)
7252451337dSDave Chinner 		return error;
726c59d87c4SChristoph Hellwig 
727f16fe3ecSDarrick J. Wong 	if (bulkreq.lastip != NULL &&
7282810bd68SDarrick J. Wong 	    copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t)))
729b474c7aeSEric Sandeen 		return -EFAULT;
730c59d87c4SChristoph Hellwig 
731f16fe3ecSDarrick J. Wong 	if (bulkreq.ocount != NULL &&
7322810bd68SDarrick J. Wong 	    copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32)))
733b474c7aeSEric Sandeen 		return -EFAULT;
734c59d87c4SChristoph Hellwig 
735c59d87c4SChristoph Hellwig 	return 0;
736c59d87c4SChristoph Hellwig }
737c59d87c4SChristoph Hellwig 
7380448b6f4SDarrick J. Wong /* Return 0 on success or positive error */
7390448b6f4SDarrick J. Wong static int
xfs_bulkstat_fmt(struct xfs_ibulk * breq,const struct xfs_bulkstat * bstat)7400448b6f4SDarrick J. Wong xfs_bulkstat_fmt(
7410448b6f4SDarrick J. Wong 	struct xfs_ibulk		*breq,
7420448b6f4SDarrick J. Wong 	const struct xfs_bulkstat	*bstat)
7430448b6f4SDarrick J. Wong {
7440448b6f4SDarrick J. Wong 	if (copy_to_user(breq->ubuffer, bstat, sizeof(struct xfs_bulkstat)))
7450448b6f4SDarrick J. Wong 		return -EFAULT;
7460448b6f4SDarrick J. Wong 	return xfs_ibulk_advance(breq, sizeof(struct xfs_bulkstat));
7470448b6f4SDarrick J. Wong }
7480448b6f4SDarrick J. Wong 
7490448b6f4SDarrick J. Wong /*
7500448b6f4SDarrick J. Wong  * Check the incoming bulk request @hdr from userspace and initialize the
7510448b6f4SDarrick J. Wong  * internal @breq bulk request appropriately.  Returns 0 if the bulk request
752e7ee96dfSDarrick J. Wong  * should proceed; -ECANCELED if there's nothing to do; or the usual
7530448b6f4SDarrick J. Wong  * negative error code.
7540448b6f4SDarrick J. Wong  */
7550448b6f4SDarrick J. Wong static int
xfs_bulk_ireq_setup(struct xfs_mount * mp,const struct xfs_bulk_ireq * hdr,struct xfs_ibulk * breq,void __user * ubuffer)7560448b6f4SDarrick J. Wong xfs_bulk_ireq_setup(
7570448b6f4SDarrick J. Wong 	struct xfs_mount	*mp,
758817644faSHironori Shiina 	const struct xfs_bulk_ireq *hdr,
7590448b6f4SDarrick J. Wong 	struct xfs_ibulk	*breq,
7600448b6f4SDarrick J. Wong 	void __user		*ubuffer)
7610448b6f4SDarrick J. Wong {
7620448b6f4SDarrick J. Wong 	if (hdr->icount == 0 ||
7630448b6f4SDarrick J. Wong 	    (hdr->flags & ~XFS_BULK_IREQ_FLAGS_ALL) ||
7640448b6f4SDarrick J. Wong 	    memchr_inv(hdr->reserved, 0, sizeof(hdr->reserved)))
7650448b6f4SDarrick J. Wong 		return -EINVAL;
7660448b6f4SDarrick J. Wong 
7670448b6f4SDarrick J. Wong 	breq->startino = hdr->ino;
7680448b6f4SDarrick J. Wong 	breq->ubuffer = ubuffer;
7690448b6f4SDarrick J. Wong 	breq->icount = hdr->icount;
7700448b6f4SDarrick J. Wong 	breq->ocount = 0;
77113d59a2aSDarrick J. Wong 	breq->flags = 0;
77213d59a2aSDarrick J. Wong 
77313d59a2aSDarrick J. Wong 	/*
774bf3cb394SDarrick J. Wong 	 * The @ino parameter is a special value, so we must look it up here.
775bf3cb394SDarrick J. Wong 	 * We're not allowed to have IREQ_AGNO, and we only return one inode
776bf3cb394SDarrick J. Wong 	 * worth of data.
777bf3cb394SDarrick J. Wong 	 */
778bf3cb394SDarrick J. Wong 	if (hdr->flags & XFS_BULK_IREQ_SPECIAL) {
779bf3cb394SDarrick J. Wong 		if (hdr->flags & XFS_BULK_IREQ_AGNO)
780bf3cb394SDarrick J. Wong 			return -EINVAL;
781bf3cb394SDarrick J. Wong 
782bf3cb394SDarrick J. Wong 		switch (hdr->ino) {
783bf3cb394SDarrick J. Wong 		case XFS_BULK_IREQ_SPECIAL_ROOT:
784817644faSHironori Shiina 			breq->startino = mp->m_sb.sb_rootino;
785bf3cb394SDarrick J. Wong 			break;
786bf3cb394SDarrick J. Wong 		default:
787bf3cb394SDarrick J. Wong 			return -EINVAL;
788bf3cb394SDarrick J. Wong 		}
789bf3cb394SDarrick J. Wong 		breq->icount = 1;
790bf3cb394SDarrick J. Wong 	}
791bf3cb394SDarrick J. Wong 
792bf3cb394SDarrick J. Wong 	/*
79313d59a2aSDarrick J. Wong 	 * The IREQ_AGNO flag means that we only want results from a given AG.
79413d59a2aSDarrick J. Wong 	 * If @hdr->ino is zero, we start iterating in that AG.  If @hdr->ino is
79513d59a2aSDarrick J. Wong 	 * beyond the specified AG then we return no results.
79613d59a2aSDarrick J. Wong 	 */
79713d59a2aSDarrick J. Wong 	if (hdr->flags & XFS_BULK_IREQ_AGNO) {
79813d59a2aSDarrick J. Wong 		if (hdr->agno >= mp->m_sb.sb_agcount)
79913d59a2aSDarrick J. Wong 			return -EINVAL;
80013d59a2aSDarrick J. Wong 
80113d59a2aSDarrick J. Wong 		if (breq->startino == 0)
80213d59a2aSDarrick J. Wong 			breq->startino = XFS_AGINO_TO_INO(mp, hdr->agno, 0);
80313d59a2aSDarrick J. Wong 		else if (XFS_INO_TO_AGNO(mp, breq->startino) < hdr->agno)
80413d59a2aSDarrick J. Wong 			return -EINVAL;
80513d59a2aSDarrick J. Wong 
80613d59a2aSDarrick J. Wong 		breq->flags |= XFS_IBULK_SAME_AG;
80713d59a2aSDarrick J. Wong 
80813d59a2aSDarrick J. Wong 		/* Asking for an inode past the end of the AG?  We're done! */
80913d59a2aSDarrick J. Wong 		if (XFS_INO_TO_AGNO(mp, breq->startino) > hdr->agno)
810e7ee96dfSDarrick J. Wong 			return -ECANCELED;
81113d59a2aSDarrick J. Wong 	} else if (hdr->agno)
81213d59a2aSDarrick J. Wong 		return -EINVAL;
8130448b6f4SDarrick J. Wong 
8140448b6f4SDarrick J. Wong 	/* Asking for an inode past the end of the FS?  We're done! */
8150448b6f4SDarrick J. Wong 	if (XFS_INO_TO_AGNO(mp, breq->startino) >= mp->m_sb.sb_agcount)
816e7ee96dfSDarrick J. Wong 		return -ECANCELED;
8170448b6f4SDarrick J. Wong 
818c3c4ecb5SChandan Babu R 	if (hdr->flags & XFS_BULK_IREQ_NREXT64)
819c3c4ecb5SChandan Babu R 		breq->flags |= XFS_IBULK_NREXT64;
820c3c4ecb5SChandan Babu R 
8210448b6f4SDarrick J. Wong 	return 0;
8220448b6f4SDarrick J. Wong }
8230448b6f4SDarrick J. Wong 
8240448b6f4SDarrick J. Wong /*
8250448b6f4SDarrick J. Wong  * Update the userspace bulk request @hdr to reflect the end state of the
8260448b6f4SDarrick J. Wong  * internal bulk request @breq.
8270448b6f4SDarrick J. Wong  */
8280448b6f4SDarrick J. Wong static void
xfs_bulk_ireq_teardown(struct xfs_bulk_ireq * hdr,struct xfs_ibulk * breq)8290448b6f4SDarrick J. Wong xfs_bulk_ireq_teardown(
8300448b6f4SDarrick J. Wong 	struct xfs_bulk_ireq	*hdr,
8310448b6f4SDarrick J. Wong 	struct xfs_ibulk	*breq)
8320448b6f4SDarrick J. Wong {
8330448b6f4SDarrick J. Wong 	hdr->ino = breq->startino;
8340448b6f4SDarrick J. Wong 	hdr->ocount = breq->ocount;
8350448b6f4SDarrick J. Wong }
8360448b6f4SDarrick J. Wong 
8370448b6f4SDarrick J. Wong /* Handle the v5 bulkstat ioctl. */
8380448b6f4SDarrick J. Wong STATIC int
xfs_ioc_bulkstat(struct file * file,unsigned int cmd,struct xfs_bulkstat_req __user * arg)8390448b6f4SDarrick J. Wong xfs_ioc_bulkstat(
840f736d93dSChristoph Hellwig 	struct file			*file,
8410448b6f4SDarrick J. Wong 	unsigned int			cmd,
8420448b6f4SDarrick J. Wong 	struct xfs_bulkstat_req __user	*arg)
8430448b6f4SDarrick J. Wong {
844f736d93dSChristoph Hellwig 	struct xfs_mount		*mp = XFS_I(file_inode(file))->i_mount;
8450448b6f4SDarrick J. Wong 	struct xfs_bulk_ireq		hdr;
8460448b6f4SDarrick J. Wong 	struct xfs_ibulk		breq = {
8470448b6f4SDarrick J. Wong 		.mp			= mp,
848e67fe633SChristian Brauner 		.idmap			= file_mnt_idmap(file),
8490448b6f4SDarrick J. Wong 	};
8500448b6f4SDarrick J. Wong 	int				error;
8510448b6f4SDarrick J. Wong 
8520448b6f4SDarrick J. Wong 	if (!capable(CAP_SYS_ADMIN))
8530448b6f4SDarrick J. Wong 		return -EPERM;
8540448b6f4SDarrick J. Wong 
85575c8c50fSDave Chinner 	if (xfs_is_shutdown(mp))
8560448b6f4SDarrick J. Wong 		return -EIO;
8570448b6f4SDarrick J. Wong 
8580448b6f4SDarrick J. Wong 	if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr)))
8590448b6f4SDarrick J. Wong 		return -EFAULT;
8600448b6f4SDarrick J. Wong 
8610448b6f4SDarrick J. Wong 	error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->bulkstat);
862e7ee96dfSDarrick J. Wong 	if (error == -ECANCELED)
8630448b6f4SDarrick J. Wong 		goto out_teardown;
8640448b6f4SDarrick J. Wong 	if (error < 0)
8650448b6f4SDarrick J. Wong 		return error;
8660448b6f4SDarrick J. Wong 
8670448b6f4SDarrick J. Wong 	error = xfs_bulkstat(&breq, xfs_bulkstat_fmt);
8680448b6f4SDarrick J. Wong 	if (error)
8690448b6f4SDarrick J. Wong 		return error;
8700448b6f4SDarrick J. Wong 
8710448b6f4SDarrick J. Wong out_teardown:
8720448b6f4SDarrick J. Wong 	xfs_bulk_ireq_teardown(&hdr, &breq);
8730448b6f4SDarrick J. Wong 	if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr)))
8740448b6f4SDarrick J. Wong 		return -EFAULT;
8750448b6f4SDarrick J. Wong 
8760448b6f4SDarrick J. Wong 	return 0;
8770448b6f4SDarrick J. Wong }
8780448b6f4SDarrick J. Wong 
879c59d87c4SChristoph Hellwig STATIC int
xfs_inumbers_fmt(struct xfs_ibulk * breq,const struct xfs_inumbers * igrp)880fba9760aSDarrick J. Wong xfs_inumbers_fmt(
881fba9760aSDarrick J. Wong 	struct xfs_ibulk		*breq,
882fba9760aSDarrick J. Wong 	const struct xfs_inumbers	*igrp)
883fba9760aSDarrick J. Wong {
884fba9760aSDarrick J. Wong 	if (copy_to_user(breq->ubuffer, igrp, sizeof(struct xfs_inumbers)))
885fba9760aSDarrick J. Wong 		return -EFAULT;
886fba9760aSDarrick J. Wong 	return xfs_ibulk_advance(breq, sizeof(struct xfs_inumbers));
887fba9760aSDarrick J. Wong }
888fba9760aSDarrick J. Wong 
889fba9760aSDarrick J. Wong /* Handle the v5 inumbers ioctl. */
890fba9760aSDarrick J. Wong STATIC int
xfs_ioc_inumbers(struct xfs_mount * mp,unsigned int cmd,struct xfs_inumbers_req __user * arg)891fba9760aSDarrick J. Wong xfs_ioc_inumbers(
892fba9760aSDarrick J. Wong 	struct xfs_mount		*mp,
893fba9760aSDarrick J. Wong 	unsigned int			cmd,
894fba9760aSDarrick J. Wong 	struct xfs_inumbers_req __user	*arg)
895fba9760aSDarrick J. Wong {
896fba9760aSDarrick J. Wong 	struct xfs_bulk_ireq		hdr;
897fba9760aSDarrick J. Wong 	struct xfs_ibulk		breq = {
898fba9760aSDarrick J. Wong 		.mp			= mp,
899fba9760aSDarrick J. Wong 	};
900fba9760aSDarrick J. Wong 	int				error;
901fba9760aSDarrick J. Wong 
902fba9760aSDarrick J. Wong 	if (!capable(CAP_SYS_ADMIN))
903fba9760aSDarrick J. Wong 		return -EPERM;
904fba9760aSDarrick J. Wong 
90575c8c50fSDave Chinner 	if (xfs_is_shutdown(mp))
906fba9760aSDarrick J. Wong 		return -EIO;
907fba9760aSDarrick J. Wong 
908fba9760aSDarrick J. Wong 	if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr)))
909fba9760aSDarrick J. Wong 		return -EFAULT;
910fba9760aSDarrick J. Wong 
911fba9760aSDarrick J. Wong 	error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->inumbers);
912e7ee96dfSDarrick J. Wong 	if (error == -ECANCELED)
913fba9760aSDarrick J. Wong 		goto out_teardown;
914fba9760aSDarrick J. Wong 	if (error < 0)
915fba9760aSDarrick J. Wong 		return error;
916fba9760aSDarrick J. Wong 
917fba9760aSDarrick J. Wong 	error = xfs_inumbers(&breq, xfs_inumbers_fmt);
918fba9760aSDarrick J. Wong 	if (error)
919fba9760aSDarrick J. Wong 		return error;
920fba9760aSDarrick J. Wong 
921fba9760aSDarrick J. Wong out_teardown:
922fba9760aSDarrick J. Wong 	xfs_bulk_ireq_teardown(&hdr, &breq);
923fba9760aSDarrick J. Wong 	if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr)))
924fba9760aSDarrick J. Wong 		return -EFAULT;
925fba9760aSDarrick J. Wong 
926c59d87c4SChristoph Hellwig 	return 0;
927c59d87c4SChristoph Hellwig }
928c59d87c4SChristoph Hellwig 
929c59d87c4SChristoph Hellwig STATIC int
xfs_ioc_fsgeometry(struct xfs_mount * mp,void __user * arg,int struct_version)930c59d87c4SChristoph Hellwig xfs_ioc_fsgeometry(
9311b6d968dSDave Chinner 	struct xfs_mount	*mp,
9321b6d968dSDave Chinner 	void			__user *arg,
9331b6d968dSDave Chinner 	int			struct_version)
934c59d87c4SChristoph Hellwig {
9351b6d968dSDave Chinner 	struct xfs_fsop_geom	fsgeo;
9361b6d968dSDave Chinner 	size_t			len;
937c59d87c4SChristoph Hellwig 
93803288b19SDave Chinner 	xfs_fs_geometry(mp, &fsgeo, struct_version);
939c59d87c4SChristoph Hellwig 
9401b6d968dSDave Chinner 	if (struct_version <= 3)
9411b6d968dSDave Chinner 		len = sizeof(struct xfs_fsop_geom_v1);
9421b6d968dSDave Chinner 	else if (struct_version == 4)
9431b6d968dSDave Chinner 		len = sizeof(struct xfs_fsop_geom_v4);
944c23232d4SDarrick J. Wong 	else {
945c23232d4SDarrick J. Wong 		xfs_fsop_geom_health(mp, &fsgeo);
9461b6d968dSDave Chinner 		len = sizeof(fsgeo);
947c23232d4SDarrick J. Wong 	}
9481b6d968dSDave Chinner 
9491b6d968dSDave Chinner 	if (copy_to_user(arg, &fsgeo, len))
950b474c7aeSEric Sandeen 		return -EFAULT;
951c59d87c4SChristoph Hellwig 	return 0;
952c59d87c4SChristoph Hellwig }
953c59d87c4SChristoph Hellwig 
9547cd5006bSDarrick J. Wong STATIC int
xfs_ioc_ag_geometry(struct xfs_mount * mp,void __user * arg)9557cd5006bSDarrick J. Wong xfs_ioc_ag_geometry(
9567cd5006bSDarrick J. Wong 	struct xfs_mount	*mp,
9577cd5006bSDarrick J. Wong 	void			__user *arg)
9587cd5006bSDarrick J. Wong {
959c6aee248SDave Chinner 	struct xfs_perag	*pag;
9607cd5006bSDarrick J. Wong 	struct xfs_ag_geometry	ageo;
9617cd5006bSDarrick J. Wong 	int			error;
9627cd5006bSDarrick J. Wong 
9637cd5006bSDarrick J. Wong 	if (copy_from_user(&ageo, arg, sizeof(ageo)))
9647cd5006bSDarrick J. Wong 		return -EFAULT;
96576f17933SDarrick J. Wong 	if (ageo.ag_flags)
96676f17933SDarrick J. Wong 		return -EINVAL;
96776f17933SDarrick J. Wong 	if (memchr_inv(&ageo.ag_reserved, 0, sizeof(ageo.ag_reserved)))
96876f17933SDarrick J. Wong 		return -EINVAL;
9697cd5006bSDarrick J. Wong 
970c6aee248SDave Chinner 	pag = xfs_perag_get(mp, ageo.ag_number);
971c6aee248SDave Chinner 	if (!pag)
972c6aee248SDave Chinner 		return -EINVAL;
973c6aee248SDave Chinner 
974c6aee248SDave Chinner 	error = xfs_ag_get_geometry(pag, &ageo);
975c6aee248SDave Chinner 	xfs_perag_put(pag);
9767cd5006bSDarrick J. Wong 	if (error)
9777cd5006bSDarrick J. Wong 		return error;
9787cd5006bSDarrick J. Wong 
9797cd5006bSDarrick J. Wong 	if (copy_to_user(arg, &ageo, sizeof(ageo)))
9807cd5006bSDarrick J. Wong 		return -EFAULT;
9817cd5006bSDarrick J. Wong 	return 0;
9827cd5006bSDarrick J. Wong }
9837cd5006bSDarrick J. Wong 
984c59d87c4SChristoph Hellwig /*
985c59d87c4SChristoph Hellwig  * Linux extended inode flags interface.
986c59d87c4SChristoph Hellwig  */
987c59d87c4SChristoph Hellwig 
9887b0e492eSDarrick J. Wong static void
xfs_fill_fsxattr(struct xfs_inode * ip,int whichfork,struct fileattr * fa)9897b0e492eSDarrick J. Wong xfs_fill_fsxattr(
9907b0e492eSDarrick J. Wong 	struct xfs_inode	*ip,
9919fefd5dbSMiklos Szeredi 	int			whichfork,
9929fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
9937b0e492eSDarrick J. Wong {
9944800887bSChristoph Hellwig 	struct xfs_mount	*mp = ip->i_mount;
995732436efSDarrick J. Wong 	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
996daf83964SChristoph Hellwig 
9979fefd5dbSMiklos Szeredi 	fileattr_fill_xflags(fa, xfs_ip2xflags(ip));
9984800887bSChristoph Hellwig 
9995aa5b278SDarrick J. Wong 	if (ip->i_diflags & XFS_DIFLAG_EXTSIZE) {
10004800887bSChristoph Hellwig 		fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
10015aa5b278SDarrick J. Wong 	} else if (ip->i_diflags & XFS_DIFLAG_EXTSZINHERIT) {
10025aa5b278SDarrick J. Wong 		/*
10035aa5b278SDarrick J. Wong 		 * Don't let a misaligned extent size hint on a directory
10045aa5b278SDarrick J. Wong 		 * escape to userspace if it won't pass the setattr checks
10055aa5b278SDarrick J. Wong 		 * later.
10065aa5b278SDarrick J. Wong 		 */
10075aa5b278SDarrick J. Wong 		if ((ip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
10085aa5b278SDarrick J. Wong 		    ip->i_extsize % mp->m_sb.sb_rextsize > 0) {
10095aa5b278SDarrick J. Wong 			fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE |
10105aa5b278SDarrick J. Wong 					    FS_XFLAG_EXTSZINHERIT);
10115aa5b278SDarrick J. Wong 			fa->fsx_extsize = 0;
10125aa5b278SDarrick J. Wong 		} else {
10135aa5b278SDarrick J. Wong 			fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
10145aa5b278SDarrick J. Wong 		}
10155aa5b278SDarrick J. Wong 	}
10165aa5b278SDarrick J. Wong 
10173e09ab8fSChristoph Hellwig 	if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
10184800887bSChristoph Hellwig 		fa->fsx_cowextsize = XFS_FSB_TO_B(mp, ip->i_cowextsize);
1019ceaf603cSChristoph Hellwig 	fa->fsx_projid = ip->i_projid;
1020b2197a36SChristoph Hellwig 	if (ifp && !xfs_need_iread_extents(ifp))
1021daf83964SChristoph Hellwig 		fa->fsx_nextents = xfs_iext_count(ifp);
10227b0e492eSDarrick J. Wong 	else
1023daf83964SChristoph Hellwig 		fa->fsx_nextents = xfs_ifork_nextents(ifp);
10247b0e492eSDarrick J. Wong }
10257b0e492eSDarrick J. Wong 
1026c59d87c4SChristoph Hellwig STATIC int
xfs_ioc_fsgetxattra(xfs_inode_t * ip,void __user * arg)10279fefd5dbSMiklos Szeredi xfs_ioc_fsgetxattra(
1028c59d87c4SChristoph Hellwig 	xfs_inode_t		*ip,
1029c59d87c4SChristoph Hellwig 	void			__user *arg)
1030c59d87c4SChristoph Hellwig {
10319fefd5dbSMiklos Szeredi 	struct fileattr		fa;
1032c59d87c4SChristoph Hellwig 
1033c59d87c4SChristoph Hellwig 	xfs_ilock(ip, XFS_ILOCK_SHARED);
10349fefd5dbSMiklos Szeredi 	xfs_fill_fsxattr(ip, XFS_ATTR_FORK, &fa);
1035c59d87c4SChristoph Hellwig 	xfs_iunlock(ip, XFS_ILOCK_SHARED);
1036c59d87c4SChristoph Hellwig 
10379fefd5dbSMiklos Szeredi 	return copy_fsxattr_to_user(&fa, arg);
10389fefd5dbSMiklos Szeredi }
10399fefd5dbSMiklos Szeredi 
10409fefd5dbSMiklos Szeredi int
xfs_fileattr_get(struct dentry * dentry,struct fileattr * fa)10419fefd5dbSMiklos Szeredi xfs_fileattr_get(
10429fefd5dbSMiklos Szeredi 	struct dentry		*dentry,
10439fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
10449fefd5dbSMiklos Szeredi {
10459fefd5dbSMiklos Szeredi 	struct xfs_inode	*ip = XFS_I(d_inode(dentry));
10469fefd5dbSMiklos Szeredi 
10479fefd5dbSMiklos Szeredi 	if (d_is_special(dentry))
10489fefd5dbSMiklos Szeredi 		return -ENOTTY;
10499fefd5dbSMiklos Szeredi 
10509fefd5dbSMiklos Szeredi 	xfs_ilock(ip, XFS_ILOCK_SHARED);
10519fefd5dbSMiklos Szeredi 	xfs_fill_fsxattr(ip, XFS_DATA_FORK, fa);
10529fefd5dbSMiklos Szeredi 	xfs_iunlock(ip, XFS_ILOCK_SHARED);
10539fefd5dbSMiklos Szeredi 
1054c59d87c4SChristoph Hellwig 	return 0;
1055c59d87c4SChristoph Hellwig }
1056c59d87c4SChristoph Hellwig 
1057dd60687eSChristoph Hellwig STATIC uint16_t
xfs_flags2diflags(struct xfs_inode * ip,unsigned int xflags)1058dd60687eSChristoph Hellwig xfs_flags2diflags(
1059c59d87c4SChristoph Hellwig 	struct xfs_inode	*ip,
1060c59d87c4SChristoph Hellwig 	unsigned int		xflags)
1061c59d87c4SChristoph Hellwig {
1062c59d87c4SChristoph Hellwig 	/* can't set PREALLOC this way, just preserve it */
1063dd60687eSChristoph Hellwig 	uint16_t		di_flags =
1064db07349dSChristoph Hellwig 		(ip->i_diflags & XFS_DIFLAG_PREALLOC);
1065dd60687eSChristoph Hellwig 
1066e7b89481SDave Chinner 	if (xflags & FS_XFLAG_IMMUTABLE)
1067c59d87c4SChristoph Hellwig 		di_flags |= XFS_DIFLAG_IMMUTABLE;
1068e7b89481SDave Chinner 	if (xflags & FS_XFLAG_APPEND)
1069c59d87c4SChristoph Hellwig 		di_flags |= XFS_DIFLAG_APPEND;
1070e7b89481SDave Chinner 	if (xflags & FS_XFLAG_SYNC)
1071c59d87c4SChristoph Hellwig 		di_flags |= XFS_DIFLAG_SYNC;
1072e7b89481SDave Chinner 	if (xflags & FS_XFLAG_NOATIME)
1073c59d87c4SChristoph Hellwig 		di_flags |= XFS_DIFLAG_NOATIME;
1074e7b89481SDave Chinner 	if (xflags & FS_XFLAG_NODUMP)
1075c59d87c4SChristoph Hellwig 		di_flags |= XFS_DIFLAG_NODUMP;
1076e7b89481SDave Chinner 	if (xflags & FS_XFLAG_NODEFRAG)
1077c59d87c4SChristoph Hellwig 		di_flags |= XFS_DIFLAG_NODEFRAG;
1078e7b89481SDave Chinner 	if (xflags & FS_XFLAG_FILESTREAM)
1079c59d87c4SChristoph Hellwig 		di_flags |= XFS_DIFLAG_FILESTREAM;
1080c19b3b05SDave Chinner 	if (S_ISDIR(VFS_I(ip)->i_mode)) {
1081e7b89481SDave Chinner 		if (xflags & FS_XFLAG_RTINHERIT)
1082c59d87c4SChristoph Hellwig 			di_flags |= XFS_DIFLAG_RTINHERIT;
1083e7b89481SDave Chinner 		if (xflags & FS_XFLAG_NOSYMLINKS)
1084c59d87c4SChristoph Hellwig 			di_flags |= XFS_DIFLAG_NOSYMLINKS;
1085e7b89481SDave Chinner 		if (xflags & FS_XFLAG_EXTSZINHERIT)
1086c59d87c4SChristoph Hellwig 			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
1087e7b89481SDave Chinner 		if (xflags & FS_XFLAG_PROJINHERIT)
10889336e3a7SDave Chinner 			di_flags |= XFS_DIFLAG_PROJINHERIT;
1089c19b3b05SDave Chinner 	} else if (S_ISREG(VFS_I(ip)->i_mode)) {
1090e7b89481SDave Chinner 		if (xflags & FS_XFLAG_REALTIME)
1091c59d87c4SChristoph Hellwig 			di_flags |= XFS_DIFLAG_REALTIME;
1092e7b89481SDave Chinner 		if (xflags & FS_XFLAG_EXTSIZE)
1093c59d87c4SChristoph Hellwig 			di_flags |= XFS_DIFLAG_EXTSIZE;
1094c59d87c4SChristoph Hellwig 	}
109558f88ca2SDave Chinner 
1096dd60687eSChristoph Hellwig 	return di_flags;
1097dd60687eSChristoph Hellwig }
109858f88ca2SDave Chinner 
1099dd60687eSChristoph Hellwig STATIC uint64_t
xfs_flags2diflags2(struct xfs_inode * ip,unsigned int xflags)1100dd60687eSChristoph Hellwig xfs_flags2diflags2(
1101dd60687eSChristoph Hellwig 	struct xfs_inode	*ip,
1102dd60687eSChristoph Hellwig 	unsigned int		xflags)
1103dd60687eSChristoph Hellwig {
1104dd60687eSChristoph Hellwig 	uint64_t		di_flags2 =
11053e09ab8fSChristoph Hellwig 		(ip->i_diflags2 & (XFS_DIFLAG2_REFLINK |
1106e89ab76dSDarrick J. Wong 				   XFS_DIFLAG2_BIGTIME |
1107e89ab76dSDarrick J. Wong 				   XFS_DIFLAG2_NREXT64));
1108dd60687eSChristoph Hellwig 
110958f88ca2SDave Chinner 	if (xflags & FS_XFLAG_DAX)
111058f88ca2SDave Chinner 		di_flags2 |= XFS_DIFLAG2_DAX;
1111f7ca3522SDarrick J. Wong 	if (xflags & FS_XFLAG_COWEXTSIZE)
1112f7ca3522SDarrick J. Wong 		di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
111358f88ca2SDave Chinner 
1114dd60687eSChristoph Hellwig 	return di_flags2;
1115c59d87c4SChristoph Hellwig }
1116c59d87c4SChristoph Hellwig 
111729a17c00SDave Chinner static int
xfs_ioctl_setattr_xflags(struct xfs_trans * tp,struct xfs_inode * ip,struct fileattr * fa)111829a17c00SDave Chinner xfs_ioctl_setattr_xflags(
111929a17c00SDave Chinner 	struct xfs_trans	*tp,
112029a17c00SDave Chinner 	struct xfs_inode	*ip,
11219fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
112229a17c00SDave Chinner {
112329a17c00SDave Chinner 	struct xfs_mount	*mp = ip->i_mount;
1124d7d5ed65SChristoph Hellwig 	bool			rtflag = (fa->fsx_xflags & FS_XFLAG_REALTIME);
11253e09ab8fSChristoph Hellwig 	uint64_t		i_flags2;
112629a17c00SDave Chinner 
1127d7d5ed65SChristoph Hellwig 	if (rtflag != XFS_IS_REALTIME_INODE(ip)) {
112829a17c00SDave Chinner 		/* Can't change realtime flag if any extents are allocated. */
1129*b887d2feSOjaswin Mujoo 		if (xfs_inode_has_filedata(ip))
113029a17c00SDave Chinner 			return -EINVAL;
11314a82db7aSDarrick J. Wong 
11324a82db7aSDarrick J. Wong 		/*
11334a82db7aSDarrick J. Wong 		 * If S_DAX is enabled on this file, we can only switch the
11344a82db7aSDarrick J. Wong 		 * device if both support fsdax.  We can't update S_DAX because
11354a82db7aSDarrick J. Wong 		 * there might be other threads walking down the access paths.
11364a82db7aSDarrick J. Wong 		 */
11374a82db7aSDarrick J. Wong 		if (IS_DAX(VFS_I(ip)) &&
11384a82db7aSDarrick J. Wong 		    (mp->m_ddev_targp->bt_daxdev == NULL ||
11394a82db7aSDarrick J. Wong 		     (mp->m_rtdev_targp &&
11404a82db7aSDarrick J. Wong 		      mp->m_rtdev_targp->bt_daxdev == NULL)))
11414a82db7aSDarrick J. Wong 			return -EINVAL;
114229a17c00SDave Chinner 	}
114329a17c00SDave Chinner 
1144d7d5ed65SChristoph Hellwig 	if (rtflag) {
1145d7d5ed65SChristoph Hellwig 		/* If realtime flag is set then must have realtime device */
1146d7d5ed65SChristoph Hellwig 		if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 ||
1147d7d5ed65SChristoph Hellwig 		    (ip->i_extsize % mp->m_sb.sb_rextsize))
1148d7d5ed65SChristoph Hellwig 			return -EINVAL;
1149d7d5ed65SChristoph Hellwig 
11501987fd74SDarrick J. Wong 		/* Clear reflink if we are actually able to set the rt flag. */
1151d7d5ed65SChristoph Hellwig 		if (xfs_is_reflink_inode(ip))
11523e09ab8fSChristoph Hellwig 			ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;
1153d7d5ed65SChristoph Hellwig 	}
1154c8e156acSDarrick J. Wong 
1155dd60687eSChristoph Hellwig 	/* diflags2 only valid for v3 inodes. */
11563e09ab8fSChristoph Hellwig 	i_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
115738c26bfdSDave Chinner 	if (i_flags2 && !xfs_has_v3inodes(mp))
1158dd60687eSChristoph Hellwig 		return -EINVAL;
1159dd60687eSChristoph Hellwig 
1160db07349dSChristoph Hellwig 	ip->i_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
11613e09ab8fSChristoph Hellwig 	ip->i_diflags2 = i_flags2;
1162dd60687eSChristoph Hellwig 
1163840d493dSIra Weiny 	xfs_diflags_to_iflags(ip, false);
116405955a70SChristoph Hellwig 
116505955a70SChristoph Hellwig 	/*
116605955a70SChristoph Hellwig 	 * Make the stable writes flag match that of the device the inode
116705955a70SChristoph Hellwig 	 * resides on when flipping the RT flag.
116805955a70SChristoph Hellwig 	 */
116905955a70SChristoph Hellwig 	if (rtflag != XFS_IS_REALTIME_INODE(ip) && S_ISREG(VFS_I(ip)->i_mode))
117005955a70SChristoph Hellwig 		xfs_update_stable_writes(ip);
117105955a70SChristoph Hellwig 
117229a17c00SDave Chinner 	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
117329a17c00SDave Chinner 	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1174ff6d6af2SBill O'Donnell 	XFS_STATS_INC(mp, xs_ig_attrchg);
117529a17c00SDave Chinner 	return 0;
117629a17c00SDave Chinner }
117729a17c00SDave Chinner 
1178e4f9ba20SIra Weiny static void
xfs_ioctl_setattr_prepare_dax(struct xfs_inode * ip,struct fileattr * fa)1179e4f9ba20SIra Weiny xfs_ioctl_setattr_prepare_dax(
11803a6a854aSDave Chinner 	struct xfs_inode	*ip,
11819fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
11823a6a854aSDave Chinner {
1183e4f9ba20SIra Weiny 	struct xfs_mount	*mp = ip->i_mount;
11843a6a854aSDave Chinner 	struct inode            *inode = VFS_I(ip);
11853a6a854aSDave Chinner 
1186aaacdd25SDarrick J. Wong 	if (S_ISDIR(inode->i_mode))
1187e4f9ba20SIra Weiny 		return;
1188aaacdd25SDarrick J. Wong 
11890560f31aSDave Chinner 	if (xfs_has_dax_always(mp) || xfs_has_dax_never(mp))
1190e4f9ba20SIra Weiny 		return;
11913a6a854aSDave Chinner 
1192e4f9ba20SIra Weiny 	if (((fa->fsx_xflags & FS_XFLAG_DAX) &&
11933e09ab8fSChristoph Hellwig 	    !(ip->i_diflags2 & XFS_DIFLAG2_DAX)) ||
1194e4f9ba20SIra Weiny 	    (!(fa->fsx_xflags & FS_XFLAG_DAX) &&
11953e09ab8fSChristoph Hellwig 	     (ip->i_diflags2 & XFS_DIFLAG2_DAX)))
1196e4f9ba20SIra Weiny 		d_mark_dontcache(inode);
11973a6a854aSDave Chinner }
11983a6a854aSDave Chinner 
11993a6a854aSDave Chinner /*
12008f3d17abSDave Chinner  * Set up the transaction structure for the setattr operation, checking that we
12018f3d17abSDave Chinner  * have permission to do so. On success, return a clean transaction and the
12028f3d17abSDave Chinner  * inode locked exclusively ready for further operation specific checks. On
12038f3d17abSDave Chinner  * failure, return an error without modifying or locking the inode.
12048f3d17abSDave Chinner  */
12058f3d17abSDave Chinner static struct xfs_trans *
xfs_ioctl_setattr_get_trans(struct xfs_inode * ip,struct xfs_dquot * pdqp)12068f3d17abSDave Chinner xfs_ioctl_setattr_get_trans(
12079fefd5dbSMiklos Szeredi 	struct xfs_inode	*ip,
12087317a03dSDarrick J. Wong 	struct xfs_dquot	*pdqp)
12098f3d17abSDave Chinner {
12108f3d17abSDave Chinner 	struct xfs_mount	*mp = ip->i_mount;
12118f3d17abSDave Chinner 	struct xfs_trans	*tp;
12123a6a854aSDave Chinner 	int			error = -EROFS;
12138f3d17abSDave Chinner 
12142e973b2cSDave Chinner 	if (xfs_is_readonly(mp))
12157317a03dSDarrick J. Wong 		goto out_error;
12163a6a854aSDave Chinner 	error = -EIO;
121775c8c50fSDave Chinner 	if (xfs_is_shutdown(mp))
12187317a03dSDarrick J. Wong 		goto out_error;
12198f3d17abSDave Chinner 
12207317a03dSDarrick J. Wong 	error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
1221eba0549bSDarrick J. Wong 			has_capability_noaudit(current, CAP_FOWNER), &tp);
12228f3d17abSDave Chinner 	if (error)
12237317a03dSDarrick J. Wong 		goto out_error;
12248f3d17abSDave Chinner 
12250560f31aSDave Chinner 	if (xfs_has_wsync(mp))
12268f3d17abSDave Chinner 		xfs_trans_set_sync(tp);
12278f3d17abSDave Chinner 
12288f3d17abSDave Chinner 	return tp;
12298f3d17abSDave Chinner 
12307317a03dSDarrick J. Wong out_error:
12318f3d17abSDave Chinner 	return ERR_PTR(error);
12328f3d17abSDave Chinner }
12338f3d17abSDave Chinner 
12349b94fcc3SIustin Pop /*
12356b69e485SDarrick J. Wong  * Validate a proposed extent size hint.  For regular files, the hint can only
12366b69e485SDarrick J. Wong  * be changed if no extents are allocated.
12379b94fcc3SIustin Pop  */
1238f92090e9Skbuild test robot static int
xfs_ioctl_setattr_check_extsize(struct xfs_inode * ip,struct fileattr * fa)1239d4388d3cSDave Chinner xfs_ioctl_setattr_check_extsize(
1240d4388d3cSDave Chinner 	struct xfs_inode	*ip,
12419fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
1242d4388d3cSDave Chinner {
1243d4388d3cSDave Chinner 	struct xfs_mount	*mp = ip->i_mount;
12446b69e485SDarrick J. Wong 	xfs_failaddr_t		failaddr;
12456b69e485SDarrick J. Wong 	uint16_t		new_diflags;
12469b94fcc3SIustin Pop 
12479fefd5dbSMiklos Szeredi 	if (!fa->fsx_valid)
12489fefd5dbSMiklos Szeredi 		return 0;
12499fefd5dbSMiklos Szeredi 
1250*b887d2feSOjaswin Mujoo 	if (S_ISREG(VFS_I(ip)->i_mode) && xfs_inode_has_filedata(ip) &&
12516b69e485SDarrick J. Wong 	    XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize)
1252d4388d3cSDave Chinner 		return -EINVAL;
1253d4388d3cSDave Chinner 
12546b69e485SDarrick J. Wong 	if (fa->fsx_extsize & mp->m_blockmask)
1255d4388d3cSDave Chinner 		return -EINVAL;
1256d4388d3cSDave Chinner 
12576b69e485SDarrick J. Wong 	new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
12586b69e485SDarrick J. Wong 
1259603f000bSDarrick J. Wong 	/*
126083193e5eSDarrick J. Wong 	 * Inode verifiers do not check that the extent size hint is an integer
126183193e5eSDarrick J. Wong 	 * multiple of the rt extent size on a directory with both rtinherit
126283193e5eSDarrick J. Wong 	 * and extszinherit flags set.  Don't let sysadmins misconfigure
126383193e5eSDarrick J. Wong 	 * directories.
1264603f000bSDarrick J. Wong 	 */
1265603f000bSDarrick J. Wong 	if ((new_diflags & XFS_DIFLAG_RTINHERIT) &&
1266603f000bSDarrick J. Wong 	    (new_diflags & XFS_DIFLAG_EXTSZINHERIT)) {
1267603f000bSDarrick J. Wong 		unsigned int	rtextsize_bytes;
1268603f000bSDarrick J. Wong 
1269603f000bSDarrick J. Wong 		rtextsize_bytes = XFS_FSB_TO_B(mp, mp->m_sb.sb_rextsize);
1270603f000bSDarrick J. Wong 		if (fa->fsx_extsize % rtextsize_bytes)
1271603f000bSDarrick J. Wong 			return -EINVAL;
1272603f000bSDarrick J. Wong 	}
1273603f000bSDarrick J. Wong 
12746b69e485SDarrick J. Wong 	failaddr = xfs_inode_validate_extsize(ip->i_mount,
12756b69e485SDarrick J. Wong 			XFS_B_TO_FSB(mp, fa->fsx_extsize),
12766b69e485SDarrick J. Wong 			VFS_I(ip)->i_mode, new_diflags);
12776b69e485SDarrick J. Wong 	return failaddr != NULL ? -EINVAL : 0;
1278d4388d3cSDave Chinner }
1279d4388d3cSDave Chinner 
1280f7ca3522SDarrick J. Wong static int
xfs_ioctl_setattr_check_cowextsize(struct xfs_inode * ip,struct fileattr * fa)1281f7ca3522SDarrick J. Wong xfs_ioctl_setattr_check_cowextsize(
1282f7ca3522SDarrick J. Wong 	struct xfs_inode	*ip,
12839fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
1284f7ca3522SDarrick J. Wong {
1285f7ca3522SDarrick J. Wong 	struct xfs_mount	*mp = ip->i_mount;
12866b69e485SDarrick J. Wong 	xfs_failaddr_t		failaddr;
12876b69e485SDarrick J. Wong 	uint64_t		new_diflags2;
12886b69e485SDarrick J. Wong 	uint16_t		new_diflags;
1289f7ca3522SDarrick J. Wong 
12909fefd5dbSMiklos Szeredi 	if (!fa->fsx_valid)
12919fefd5dbSMiklos Szeredi 		return 0;
12929fefd5dbSMiklos Szeredi 
12936b69e485SDarrick J. Wong 	if (fa->fsx_cowextsize & mp->m_blockmask)
1294f7ca3522SDarrick J. Wong 		return -EINVAL;
1295f7ca3522SDarrick J. Wong 
12966b69e485SDarrick J. Wong 	new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
12976b69e485SDarrick J. Wong 	new_diflags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
1298f7ca3522SDarrick J. Wong 
12996b69e485SDarrick J. Wong 	failaddr = xfs_inode_validate_cowextsize(ip->i_mount,
13006b69e485SDarrick J. Wong 			XFS_B_TO_FSB(mp, fa->fsx_cowextsize),
13016b69e485SDarrick J. Wong 			VFS_I(ip)->i_mode, new_diflags, new_diflags2);
13026b69e485SDarrick J. Wong 	return failaddr != NULL ? -EINVAL : 0;
1303f7ca3522SDarrick J. Wong }
1304f7ca3522SDarrick J. Wong 
1305f92090e9Skbuild test robot static int
xfs_ioctl_setattr_check_projid(struct xfs_inode * ip,struct fileattr * fa)130623bd0735SDave Chinner xfs_ioctl_setattr_check_projid(
130723bd0735SDave Chinner 	struct xfs_inode	*ip,
13089fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
130923bd0735SDave Chinner {
13109fefd5dbSMiklos Szeredi 	if (!fa->fsx_valid)
13119fefd5dbSMiklos Szeredi 		return 0;
13129fefd5dbSMiklos Szeredi 
131338c26bfdSDave Chinner 	/* Disallow 32bit project ids if 32bit IDs are not enabled. */
1314c8ce540dSDarrick J. Wong 	if (fa->fsx_projid > (uint16_t)-1 &&
131538c26bfdSDave Chinner 	    !xfs_has_projid32(ip->i_mount))
131623bd0735SDave Chinner 		return -EINVAL;
131723bd0735SDave Chinner 	return 0;
131823bd0735SDave Chinner }
1319c59d87c4SChristoph Hellwig 
13209fefd5dbSMiklos Szeredi int
xfs_fileattr_set(struct mnt_idmap * idmap,struct dentry * dentry,struct fileattr * fa)13219fefd5dbSMiklos Szeredi xfs_fileattr_set(
13228782a9aeSChristian Brauner 	struct mnt_idmap	*idmap,
13239fefd5dbSMiklos Szeredi 	struct dentry		*dentry,
13249fefd5dbSMiklos Szeredi 	struct fileattr		*fa)
1325c59d87c4SChristoph Hellwig {
13269fefd5dbSMiklos Szeredi 	struct xfs_inode	*ip = XFS_I(d_inode(dentry));
1327c59d87c4SChristoph Hellwig 	struct xfs_mount	*mp = ip->i_mount;
1328c59d87c4SChristoph Hellwig 	struct xfs_trans	*tp;
132992f8ff73SChandra Seetharaman 	struct xfs_dquot	*pdqp = NULL;
1330c59d87c4SChristoph Hellwig 	struct xfs_dquot	*olddquot = NULL;
1331fea7aae6SDarrick J. Wong 	int			error;
1332c59d87c4SChristoph Hellwig 
1333c59d87c4SChristoph Hellwig 	trace_xfs_ioctl_setattr(ip);
1334c59d87c4SChristoph Hellwig 
13359fefd5dbSMiklos Szeredi 	if (d_is_special(dentry))
13369fefd5dbSMiklos Szeredi 		return -ENOTTY;
13379fefd5dbSMiklos Szeredi 
13389fefd5dbSMiklos Szeredi 	if (!fa->fsx_valid) {
13399fefd5dbSMiklos Szeredi 		if (fa->flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL |
13409fefd5dbSMiklos Szeredi 				  FS_NOATIME_FL | FS_NODUMP_FL |
13419fefd5dbSMiklos Szeredi 				  FS_SYNC_FL | FS_DAX_FL | FS_PROJINHERIT_FL))
13429fefd5dbSMiklos Szeredi 			return -EOPNOTSUPP;
13439fefd5dbSMiklos Szeredi 	}
13449fefd5dbSMiklos Szeredi 
1345fea7aae6SDarrick J. Wong 	error = xfs_ioctl_setattr_check_projid(ip, fa);
1346fea7aae6SDarrick J. Wong 	if (error)
1347fea7aae6SDarrick J. Wong 		return error;
1348c59d87c4SChristoph Hellwig 
1349c59d87c4SChristoph Hellwig 	/*
1350c59d87c4SChristoph Hellwig 	 * If disk quotas is on, we make sure that the dquots do exist on disk,
1351c59d87c4SChristoph Hellwig 	 * before we start any other transactions. Trying to do this later
1352c59d87c4SChristoph Hellwig 	 * is messy. We don't care to take a readlock to look at the ids
1353c59d87c4SChristoph Hellwig 	 * in inode here, because we can't hold it across the trans_reserve.
1354c59d87c4SChristoph Hellwig 	 * If the IDs do change before we take the ilock, we're covered
1355c59d87c4SChristoph Hellwig 	 * because the i_*dquot fields will get updated anyway.
1356c59d87c4SChristoph Hellwig 	 */
13579fefd5dbSMiklos Szeredi 	if (fa->fsx_valid && XFS_IS_QUOTA_ON(mp)) {
1358fea7aae6SDarrick J. Wong 		error = xfs_qm_vop_dqalloc(ip, VFS_I(ip)->i_uid,
135954295159SChristoph Hellwig 				VFS_I(ip)->i_gid, fa->fsx_projid,
1360ea1c9040SKaixu Xia 				XFS_QMOPT_PQUOTA, NULL, NULL, &pdqp);
1361fea7aae6SDarrick J. Wong 		if (error)
1362fea7aae6SDarrick J. Wong 			return error;
1363c59d87c4SChristoph Hellwig 	}
1364c59d87c4SChristoph Hellwig 
1365e4f9ba20SIra Weiny 	xfs_ioctl_setattr_prepare_dax(ip, fa);
13663a6a854aSDave Chinner 
13679fefd5dbSMiklos Szeredi 	tp = xfs_ioctl_setattr_get_trans(ip, pdqp);
13688f3d17abSDave Chinner 	if (IS_ERR(tp)) {
1369fea7aae6SDarrick J. Wong 		error = PTR_ERR(tp);
13708f3d17abSDave Chinner 		goto error_free_dquots;
1371c59d87c4SChristoph Hellwig 	}
1372c59d87c4SChristoph Hellwig 
1373fea7aae6SDarrick J. Wong 	error = xfs_ioctl_setattr_check_extsize(ip, fa);
1374fea7aae6SDarrick J. Wong 	if (error)
1375d4388d3cSDave Chinner 		goto error_trans_cancel;
1376c59d87c4SChristoph Hellwig 
1377fea7aae6SDarrick J. Wong 	error = xfs_ioctl_setattr_check_cowextsize(ip, fa);
1378fea7aae6SDarrick J. Wong 	if (error)
1379f7ca3522SDarrick J. Wong 		goto error_trans_cancel;
1380f7ca3522SDarrick J. Wong 
1381fea7aae6SDarrick J. Wong 	error = xfs_ioctl_setattr_xflags(tp, ip, fa);
1382fea7aae6SDarrick J. Wong 	if (error)
1383d4388d3cSDave Chinner 		goto error_trans_cancel;
1384c59d87c4SChristoph Hellwig 
13859fefd5dbSMiklos Szeredi 	if (!fa->fsx_valid)
13869fefd5dbSMiklos Szeredi 		goto skip_xattr;
1387c59d87c4SChristoph Hellwig 	/*
1388fd179b9cSDave Chinner 	 * Change file ownership.  Must be the owner or privileged.  CAP_FSETID
1389fd179b9cSDave Chinner 	 * overrides the following restrictions:
1390c59d87c4SChristoph Hellwig 	 *
1391fd179b9cSDave Chinner 	 * The set-user-ID and set-group-ID bits of a file will be cleared upon
1392fd179b9cSDave Chinner 	 * successful return from chown()
1393c59d87c4SChristoph Hellwig 	 */
1394c59d87c4SChristoph Hellwig 
1395c19b3b05SDave Chinner 	if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
13969452e93eSChristian Brauner 	    !capable_wrt_inode_uidgid(idmap, VFS_I(ip), CAP_FSETID))
1397c19b3b05SDave Chinner 		VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
1398c59d87c4SChristoph Hellwig 
1399fd179b9cSDave Chinner 	/* Change the ownerships and register project quota modifications */
1400ceaf603cSChristoph Hellwig 	if (ip->i_projid != fa->fsx_projid) {
1401149e53afSChristoph Hellwig 		if (XFS_IS_PQUOTA_ON(mp)) {
1402c59d87c4SChristoph Hellwig 			olddquot = xfs_qm_vop_chown(tp, ip,
140392f8ff73SChandra Seetharaman 						&ip->i_pdquot, pdqp);
1404c59d87c4SChristoph Hellwig 		}
1405ceaf603cSChristoph Hellwig 		ip->i_projid = fa->fsx_projid;
1406c59d87c4SChristoph Hellwig 	}
1407c59d87c4SChristoph Hellwig 
1408a872703fSDave Chinner 	/*
1409a872703fSDave Chinner 	 * Only set the extent size hint if we've already determined that the
1410a872703fSDave Chinner 	 * extent size hint should be set on the inode. If no extent size flags
1411a872703fSDave Chinner 	 * are set on the inode then unconditionally clear the extent size hint.
1412a872703fSDave Chinner 	 */
1413db07349dSChristoph Hellwig 	if (ip->i_diflags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
1414b231b122SChristoph Hellwig 		ip->i_extsize = XFS_B_TO_FSB(mp, fa->fsx_extsize);
1415fd179b9cSDave Chinner 	else
1416031474c2SChristoph Hellwig 		ip->i_extsize = 0;
1417ee7b83fdSChristoph Hellwig 
141838c26bfdSDave Chinner 	if (xfs_has_v3inodes(mp)) {
14193e09ab8fSChristoph Hellwig 		if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
1420b231b122SChristoph Hellwig 			ip->i_cowextsize = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
1421f7ca3522SDarrick J. Wong 		else
1422b33ce57dSChristoph Hellwig 			ip->i_cowextsize = 0;
1423ee7b83fdSChristoph Hellwig 	}
1424a872703fSDave Chinner 
14259fefd5dbSMiklos Szeredi skip_xattr:
1426fea7aae6SDarrick J. Wong 	error = xfs_trans_commit(tp);
1427c59d87c4SChristoph Hellwig 
1428c59d87c4SChristoph Hellwig 	/*
1429c59d87c4SChristoph Hellwig 	 * Release any dquot(s) the inode had kept before chown.
1430c59d87c4SChristoph Hellwig 	 */
1431c59d87c4SChristoph Hellwig 	xfs_qm_dqrele(olddquot);
143292f8ff73SChandra Seetharaman 	xfs_qm_dqrele(pdqp);
1433c59d87c4SChristoph Hellwig 
1434fea7aae6SDarrick J. Wong 	return error;
1435c59d87c4SChristoph Hellwig 
1436d4388d3cSDave Chinner error_trans_cancel:
14374906e215SChristoph Hellwig 	xfs_trans_cancel(tp);
14388f3d17abSDave Chinner error_free_dquots:
143992f8ff73SChandra Seetharaman 	xfs_qm_dqrele(pdqp);
1440fea7aae6SDarrick J. Wong 	return error;
1441c59d87c4SChristoph Hellwig }
1442c59d87c4SChristoph Hellwig 
1443232b5194SChristoph Hellwig static bool
xfs_getbmap_format(struct kgetbmap * p,struct getbmapx __user * u,size_t recsize)1444232b5194SChristoph Hellwig xfs_getbmap_format(
1445232b5194SChristoph Hellwig 	struct kgetbmap		*p,
1446232b5194SChristoph Hellwig 	struct getbmapx __user	*u,
1447232b5194SChristoph Hellwig 	size_t			recsize)
1448c59d87c4SChristoph Hellwig {
1449232b5194SChristoph Hellwig 	if (put_user(p->bmv_offset, &u->bmv_offset) ||
1450232b5194SChristoph Hellwig 	    put_user(p->bmv_block, &u->bmv_block) ||
1451232b5194SChristoph Hellwig 	    put_user(p->bmv_length, &u->bmv_length) ||
1452232b5194SChristoph Hellwig 	    put_user(0, &u->bmv_count) ||
1453232b5194SChristoph Hellwig 	    put_user(0, &u->bmv_entries))
1454232b5194SChristoph Hellwig 		return false;
1455232b5194SChristoph Hellwig 	if (recsize < sizeof(struct getbmapx))
1456232b5194SChristoph Hellwig 		return true;
1457232b5194SChristoph Hellwig 	if (put_user(0, &u->bmv_iflags) ||
1458232b5194SChristoph Hellwig 	    put_user(p->bmv_oflags, &u->bmv_oflags) ||
1459232b5194SChristoph Hellwig 	    put_user(0, &u->bmv_unused1) ||
1460232b5194SChristoph Hellwig 	    put_user(0, &u->bmv_unused2))
1461232b5194SChristoph Hellwig 		return false;
1462232b5194SChristoph Hellwig 	return true;
1463c59d87c4SChristoph Hellwig }
1464c59d87c4SChristoph Hellwig 
1465c59d87c4SChristoph Hellwig STATIC int
xfs_ioc_getbmap(struct file * file,unsigned int cmd,void __user * arg)1466c59d87c4SChristoph Hellwig xfs_ioc_getbmap(
14678f3e2058SChristoph Hellwig 	struct file		*file,
1468c59d87c4SChristoph Hellwig 	unsigned int		cmd,
1469c59d87c4SChristoph Hellwig 	void			__user *arg)
1470c59d87c4SChristoph Hellwig {
1471be6324c0SDarrick J. Wong 	struct getbmapx		bmx = { 0 };
1472232b5194SChristoph Hellwig 	struct kgetbmap		*buf;
1473232b5194SChristoph Hellwig 	size_t			recsize;
1474232b5194SChristoph Hellwig 	int			error, i;
1475c59d87c4SChristoph Hellwig 
1476232b5194SChristoph Hellwig 	switch (cmd) {
1477232b5194SChristoph Hellwig 	case XFS_IOC_GETBMAPA:
1478232b5194SChristoph Hellwig 		bmx.bmv_iflags = BMV_IF_ATTRFORK;
147953004ee7SGustavo A. R. Silva 		fallthrough;
1480232b5194SChristoph Hellwig 	case XFS_IOC_GETBMAP:
1481232b5194SChristoph Hellwig 		/* struct getbmap is a strict subset of struct getbmapx. */
1482232b5194SChristoph Hellwig 		recsize = sizeof(struct getbmap);
1483232b5194SChristoph Hellwig 		break;
1484232b5194SChristoph Hellwig 	case XFS_IOC_GETBMAPX:
1485232b5194SChristoph Hellwig 		recsize = sizeof(struct getbmapx);
1486232b5194SChristoph Hellwig 		break;
1487232b5194SChristoph Hellwig 	default:
1488232b5194SChristoph Hellwig 		return -EINVAL;
1489c59d87c4SChristoph Hellwig 	}
1490c59d87c4SChristoph Hellwig 
1491232b5194SChristoph Hellwig 	if (copy_from_user(&bmx, arg, recsize))
1492b474c7aeSEric Sandeen 		return -EFAULT;
1493c59d87c4SChristoph Hellwig 
1494c59d87c4SChristoph Hellwig 	if (bmx.bmv_count < 2)
1495b474c7aeSEric Sandeen 		return -EINVAL;
149629d650f7SDarrick J. Wong 	if (bmx.bmv_count >= INT_MAX / recsize)
1497232b5194SChristoph Hellwig 		return -ENOMEM;
1498c59d87c4SChristoph Hellwig 
1499c2e4e3b7SGustavo A. R. Silva 	buf = kvcalloc(bmx.bmv_count, sizeof(*buf), GFP_KERNEL);
1500232b5194SChristoph Hellwig 	if (!buf)
1501232b5194SChristoph Hellwig 		return -ENOMEM;
1502c59d87c4SChristoph Hellwig 
1503232b5194SChristoph Hellwig 	error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf);
1504c59d87c4SChristoph Hellwig 	if (error)
1505232b5194SChristoph Hellwig 		goto out_free_buf;
1506c59d87c4SChristoph Hellwig 
1507232b5194SChristoph Hellwig 	error = -EFAULT;
1508232b5194SChristoph Hellwig 	if (copy_to_user(arg, &bmx, recsize))
1509232b5194SChristoph Hellwig 		goto out_free_buf;
1510232b5194SChristoph Hellwig 	arg += recsize;
1511c59d87c4SChristoph Hellwig 
1512232b5194SChristoph Hellwig 	for (i = 0; i < bmx.bmv_entries; i++) {
1513232b5194SChristoph Hellwig 		if (!xfs_getbmap_format(buf + i, arg, recsize))
1514232b5194SChristoph Hellwig 			goto out_free_buf;
1515232b5194SChristoph Hellwig 		arg += recsize;
1516232b5194SChristoph Hellwig 	}
1517232b5194SChristoph Hellwig 
1518232b5194SChristoph Hellwig 	error = 0;
1519232b5194SChristoph Hellwig out_free_buf:
1520232b5194SChristoph Hellwig 	kmem_free(buf);
1521132bf672SChristophe JAILLET 	return error;
1522c59d87c4SChristoph Hellwig }
1523c59d87c4SChristoph Hellwig 
1524e89c0413SDarrick J. Wong STATIC int
xfs_ioc_getfsmap(struct xfs_inode * ip,struct fsmap_head __user * arg)1525e89c0413SDarrick J. Wong xfs_ioc_getfsmap(
1526e89c0413SDarrick J. Wong 	struct xfs_inode	*ip,
15279d17e14cSChristoph Hellwig 	struct fsmap_head	__user *arg)
1528e89c0413SDarrick J. Wong {
1529e89c0413SDarrick J. Wong 	struct xfs_fsmap_head	xhead = {0};
1530e89c0413SDarrick J. Wong 	struct fsmap_head	head;
15318ffa90e1SDarrick J. Wong 	struct fsmap		*recs;
15328ffa90e1SDarrick J. Wong 	unsigned int		count;
15338ffa90e1SDarrick J. Wong 	__u32			last_flags = 0;
15348ffa90e1SDarrick J. Wong 	bool			done = false;
1535e89c0413SDarrick J. Wong 	int			error;
1536e89c0413SDarrick J. Wong 
1537e89c0413SDarrick J. Wong 	if (copy_from_user(&head, arg, sizeof(struct fsmap_head)))
1538e89c0413SDarrick J. Wong 		return -EFAULT;
1539e89c0413SDarrick J. Wong 	if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) ||
1540e89c0413SDarrick J. Wong 	    memchr_inv(head.fmh_keys[0].fmr_reserved, 0,
1541e89c0413SDarrick J. Wong 		       sizeof(head.fmh_keys[0].fmr_reserved)) ||
1542e89c0413SDarrick J. Wong 	    memchr_inv(head.fmh_keys[1].fmr_reserved, 0,
1543e89c0413SDarrick J. Wong 		       sizeof(head.fmh_keys[1].fmr_reserved)))
1544e89c0413SDarrick J. Wong 		return -EINVAL;
1545e89c0413SDarrick J. Wong 
15468ffa90e1SDarrick J. Wong 	/*
15478ffa90e1SDarrick J. Wong 	 * Use an internal memory buffer so that we don't have to copy fsmap
15488ffa90e1SDarrick J. Wong 	 * data to userspace while holding locks.  Start by trying to allocate
15498ffa90e1SDarrick J. Wong 	 * up to 128k for the buffer, but fall back to a single page if needed.
15508ffa90e1SDarrick J. Wong 	 */
15518ffa90e1SDarrick J. Wong 	count = min_t(unsigned int, head.fmh_count,
15528ffa90e1SDarrick J. Wong 			131072 / sizeof(struct fsmap));
1553c2e4e3b7SGustavo A. R. Silva 	recs = kvcalloc(count, sizeof(struct fsmap), GFP_KERNEL);
15548ffa90e1SDarrick J. Wong 	if (!recs) {
15558ffa90e1SDarrick J. Wong 		count = min_t(unsigned int, head.fmh_count,
15568ffa90e1SDarrick J. Wong 				PAGE_SIZE / sizeof(struct fsmap));
1557c2e4e3b7SGustavo A. R. Silva 		recs = kvcalloc(count, sizeof(struct fsmap), GFP_KERNEL);
15588ffa90e1SDarrick J. Wong 		if (!recs)
15598ffa90e1SDarrick J. Wong 			return -ENOMEM;
15608ffa90e1SDarrick J. Wong 	}
15618ffa90e1SDarrick J. Wong 
1562e89c0413SDarrick J. Wong 	xhead.fmh_iflags = head.fmh_iflags;
1563e89c0413SDarrick J. Wong 	xfs_fsmap_to_internal(&xhead.fmh_keys[0], &head.fmh_keys[0]);
1564e89c0413SDarrick J. Wong 	xfs_fsmap_to_internal(&xhead.fmh_keys[1], &head.fmh_keys[1]);
1565e89c0413SDarrick J. Wong 
1566e89c0413SDarrick J. Wong 	trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]);
1567e89c0413SDarrick J. Wong 	trace_xfs_getfsmap_high_key(ip->i_mount, &xhead.fmh_keys[1]);
1568e89c0413SDarrick J. Wong 
15698ffa90e1SDarrick J. Wong 	head.fmh_entries = 0;
15708ffa90e1SDarrick J. Wong 	do {
15718ffa90e1SDarrick J. Wong 		struct fsmap __user	*user_recs;
15728ffa90e1SDarrick J. Wong 		struct fsmap		*last_rec;
1573e89c0413SDarrick J. Wong 
15748ffa90e1SDarrick J. Wong 		user_recs = &arg->fmh_recs[head.fmh_entries];
15758ffa90e1SDarrick J. Wong 		xhead.fmh_entries = 0;
15768ffa90e1SDarrick J. Wong 		xhead.fmh_count = min_t(unsigned int, count,
15778ffa90e1SDarrick J. Wong 					head.fmh_count - head.fmh_entries);
15788ffa90e1SDarrick J. Wong 
15798ffa90e1SDarrick J. Wong 		/* Run query, record how many entries we got. */
15808ffa90e1SDarrick J. Wong 		error = xfs_getfsmap(ip->i_mount, &xhead, recs);
15818ffa90e1SDarrick J. Wong 		switch (error) {
15828ffa90e1SDarrick J. Wong 		case 0:
15838ffa90e1SDarrick J. Wong 			/*
15848ffa90e1SDarrick J. Wong 			 * There are no more records in the result set.  Copy
15858ffa90e1SDarrick J. Wong 			 * whatever we got to userspace and break out.
15868ffa90e1SDarrick J. Wong 			 */
15878ffa90e1SDarrick J. Wong 			done = true;
15888ffa90e1SDarrick J. Wong 			break;
15898ffa90e1SDarrick J. Wong 		case -ECANCELED:
15908ffa90e1SDarrick J. Wong 			/*
15918ffa90e1SDarrick J. Wong 			 * The internal memory buffer is full.  Copy whatever
15928ffa90e1SDarrick J. Wong 			 * records we got to userspace and go again if we have
15938ffa90e1SDarrick J. Wong 			 * not yet filled the userspace buffer.
15948ffa90e1SDarrick J. Wong 			 */
15958ffa90e1SDarrick J. Wong 			error = 0;
15968ffa90e1SDarrick J. Wong 			break;
15978ffa90e1SDarrick J. Wong 		default:
15988ffa90e1SDarrick J. Wong 			goto out_free;
15998ffa90e1SDarrick J. Wong 		}
16008ffa90e1SDarrick J. Wong 		head.fmh_entries += xhead.fmh_entries;
16018ffa90e1SDarrick J. Wong 		head.fmh_oflags = xhead.fmh_oflags;
16028ffa90e1SDarrick J. Wong 
16038ffa90e1SDarrick J. Wong 		/*
16048ffa90e1SDarrick J. Wong 		 * If the caller wanted a record count or there aren't any
16058ffa90e1SDarrick J. Wong 		 * new records to return, we're done.
16068ffa90e1SDarrick J. Wong 		 */
16078ffa90e1SDarrick J. Wong 		if (head.fmh_count == 0 || xhead.fmh_entries == 0)
16088ffa90e1SDarrick J. Wong 			break;
16098ffa90e1SDarrick J. Wong 
16108ffa90e1SDarrick J. Wong 		/* Copy all the records we got out to userspace. */
16118ffa90e1SDarrick J. Wong 		if (copy_to_user(user_recs, recs,
16128ffa90e1SDarrick J. Wong 				 xhead.fmh_entries * sizeof(struct fsmap))) {
16138ffa90e1SDarrick J. Wong 			error = -EFAULT;
16148ffa90e1SDarrick J. Wong 			goto out_free;
16158ffa90e1SDarrick J. Wong 		}
16168ffa90e1SDarrick J. Wong 
16178ffa90e1SDarrick J. Wong 		/* Remember the last record flags we copied to userspace. */
16188ffa90e1SDarrick J. Wong 		last_rec = &recs[xhead.fmh_entries - 1];
16198ffa90e1SDarrick J. Wong 		last_flags = last_rec->fmr_flags;
16208ffa90e1SDarrick J. Wong 
16218ffa90e1SDarrick J. Wong 		/* Set up the low key for the next iteration. */
16228ffa90e1SDarrick J. Wong 		xfs_fsmap_to_internal(&xhead.fmh_keys[0], last_rec);
16238ffa90e1SDarrick J. Wong 		trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]);
16248ffa90e1SDarrick J. Wong 	} while (!done && head.fmh_entries < head.fmh_count);
16258ffa90e1SDarrick J. Wong 
16268ffa90e1SDarrick J. Wong 	/*
16278ffa90e1SDarrick J. Wong 	 * If there are no more records in the query result set and we're not
16288ffa90e1SDarrick J. Wong 	 * in counting mode, mark the last record returned with the LAST flag.
16298ffa90e1SDarrick J. Wong 	 */
16308ffa90e1SDarrick J. Wong 	if (done && head.fmh_count > 0 && head.fmh_entries > 0) {
16318ffa90e1SDarrick J. Wong 		struct fsmap __user	*user_rec;
16328ffa90e1SDarrick J. Wong 
16338ffa90e1SDarrick J. Wong 		last_flags |= FMR_OF_LAST;
16348ffa90e1SDarrick J. Wong 		user_rec = &arg->fmh_recs[head.fmh_entries - 1];
16358ffa90e1SDarrick J. Wong 
16368ffa90e1SDarrick J. Wong 		if (copy_to_user(&user_rec->fmr_flags, &last_flags,
16378ffa90e1SDarrick J. Wong 					sizeof(last_flags))) {
16388ffa90e1SDarrick J. Wong 			error = -EFAULT;
16398ffa90e1SDarrick J. Wong 			goto out_free;
16408ffa90e1SDarrick J. Wong 		}
1641e89c0413SDarrick J. Wong 	}
1642e89c0413SDarrick J. Wong 
1643e89c0413SDarrick J. Wong 	/* copy back header */
16448ffa90e1SDarrick J. Wong 	if (copy_to_user(arg, &head, sizeof(struct fsmap_head))) {
16458ffa90e1SDarrick J. Wong 		error = -EFAULT;
16468ffa90e1SDarrick J. Wong 		goto out_free;
16478ffa90e1SDarrick J. Wong 	}
1648e89c0413SDarrick J. Wong 
16498ffa90e1SDarrick J. Wong out_free:
16508ffa90e1SDarrick J. Wong 	kmem_free(recs);
16518ffa90e1SDarrick J. Wong 	return error;
1652e89c0413SDarrick J. Wong }
1653e89c0413SDarrick J. Wong 
165436fd6e86SDarrick J. Wong STATIC int
xfs_ioc_scrub_metadata(struct file * file,void __user * arg)165536fd6e86SDarrick J. Wong xfs_ioc_scrub_metadata(
165671bddbccSDarrick J. Wong 	struct file			*file,
165736fd6e86SDarrick J. Wong 	void				__user *arg)
165836fd6e86SDarrick J. Wong {
165936fd6e86SDarrick J. Wong 	struct xfs_scrub_metadata	scrub;
166036fd6e86SDarrick J. Wong 	int				error;
166136fd6e86SDarrick J. Wong 
166236fd6e86SDarrick J. Wong 	if (!capable(CAP_SYS_ADMIN))
166336fd6e86SDarrick J. Wong 		return -EPERM;
166436fd6e86SDarrick J. Wong 
166536fd6e86SDarrick J. Wong 	if (copy_from_user(&scrub, arg, sizeof(scrub)))
166636fd6e86SDarrick J. Wong 		return -EFAULT;
166736fd6e86SDarrick J. Wong 
166871bddbccSDarrick J. Wong 	error = xfs_scrub_metadata(file, &scrub);
166936fd6e86SDarrick J. Wong 	if (error)
167036fd6e86SDarrick J. Wong 		return error;
167136fd6e86SDarrick J. Wong 
167236fd6e86SDarrick J. Wong 	if (copy_to_user(arg, &scrub, sizeof(scrub)))
167336fd6e86SDarrick J. Wong 		return -EFAULT;
167436fd6e86SDarrick J. Wong 
167536fd6e86SDarrick J. Wong 	return 0;
167636fd6e86SDarrick J. Wong }
167736fd6e86SDarrick J. Wong 
1678a133d952SDave Chinner int
xfs_ioc_swapext(xfs_swapext_t * sxp)1679a133d952SDave Chinner xfs_ioc_swapext(
1680a133d952SDave Chinner 	xfs_swapext_t	*sxp)
1681a133d952SDave Chinner {
1682a133d952SDave Chinner 	xfs_inode_t     *ip, *tip;
1683a133d952SDave Chinner 	struct fd	f, tmp;
1684a133d952SDave Chinner 	int		error = 0;
1685a133d952SDave Chinner 
1686a133d952SDave Chinner 	/* Pull information for the target fd */
1687a133d952SDave Chinner 	f = fdget((int)sxp->sx_fdtarget);
1688a133d952SDave Chinner 	if (!f.file) {
16892451337dSDave Chinner 		error = -EINVAL;
1690a133d952SDave Chinner 		goto out;
1691a133d952SDave Chinner 	}
1692a133d952SDave Chinner 
1693a133d952SDave Chinner 	if (!(f.file->f_mode & FMODE_WRITE) ||
1694a133d952SDave Chinner 	    !(f.file->f_mode & FMODE_READ) ||
1695a133d952SDave Chinner 	    (f.file->f_flags & O_APPEND)) {
16962451337dSDave Chinner 		error = -EBADF;
1697a133d952SDave Chinner 		goto out_put_file;
1698a133d952SDave Chinner 	}
1699a133d952SDave Chinner 
1700a133d952SDave Chinner 	tmp = fdget((int)sxp->sx_fdtmp);
1701a133d952SDave Chinner 	if (!tmp.file) {
17022451337dSDave Chinner 		error = -EINVAL;
1703a133d952SDave Chinner 		goto out_put_file;
1704a133d952SDave Chinner 	}
1705a133d952SDave Chinner 
1706a133d952SDave Chinner 	if (!(tmp.file->f_mode & FMODE_WRITE) ||
1707a133d952SDave Chinner 	    !(tmp.file->f_mode & FMODE_READ) ||
1708a133d952SDave Chinner 	    (tmp.file->f_flags & O_APPEND)) {
17092451337dSDave Chinner 		error = -EBADF;
1710a133d952SDave Chinner 		goto out_put_tmp_file;
1711a133d952SDave Chinner 	}
1712a133d952SDave Chinner 
1713a133d952SDave Chinner 	if (IS_SWAPFILE(file_inode(f.file)) ||
1714a133d952SDave Chinner 	    IS_SWAPFILE(file_inode(tmp.file))) {
17152451337dSDave Chinner 		error = -EINVAL;
1716a133d952SDave Chinner 		goto out_put_tmp_file;
1717a133d952SDave Chinner 	}
1718a133d952SDave Chinner 
17197f1b6245SJann Horn 	/*
17207f1b6245SJann Horn 	 * We need to ensure that the fds passed in point to XFS inodes
17217f1b6245SJann Horn 	 * before we cast and access them as XFS structures as we have no
17227f1b6245SJann Horn 	 * control over what the user passes us here.
17237f1b6245SJann Horn 	 */
17247f1b6245SJann Horn 	if (f.file->f_op != &xfs_file_operations ||
17257f1b6245SJann Horn 	    tmp.file->f_op != &xfs_file_operations) {
17267f1b6245SJann Horn 		error = -EINVAL;
17277f1b6245SJann Horn 		goto out_put_tmp_file;
17287f1b6245SJann Horn 	}
17297f1b6245SJann Horn 
1730a133d952SDave Chinner 	ip = XFS_I(file_inode(f.file));
1731a133d952SDave Chinner 	tip = XFS_I(file_inode(tmp.file));
1732a133d952SDave Chinner 
1733a133d952SDave Chinner 	if (ip->i_mount != tip->i_mount) {
17342451337dSDave Chinner 		error = -EINVAL;
1735a133d952SDave Chinner 		goto out_put_tmp_file;
1736a133d952SDave Chinner 	}
1737a133d952SDave Chinner 
1738a133d952SDave Chinner 	if (ip->i_ino == tip->i_ino) {
17392451337dSDave Chinner 		error = -EINVAL;
1740a133d952SDave Chinner 		goto out_put_tmp_file;
1741a133d952SDave Chinner 	}
1742a133d952SDave Chinner 
174375c8c50fSDave Chinner 	if (xfs_is_shutdown(ip->i_mount)) {
17442451337dSDave Chinner 		error = -EIO;
1745a133d952SDave Chinner 		goto out_put_tmp_file;
1746a133d952SDave Chinner 	}
1747a133d952SDave Chinner 
1748a133d952SDave Chinner 	error = xfs_swap_extents(ip, tip, sxp);
1749a133d952SDave Chinner 
1750a133d952SDave Chinner  out_put_tmp_file:
1751a133d952SDave Chinner 	fdput(tmp);
1752a133d952SDave Chinner  out_put_file:
1753a133d952SDave Chinner 	fdput(f);
1754a133d952SDave Chinner  out:
1755a133d952SDave Chinner 	return error;
1756a133d952SDave Chinner }
1757a133d952SDave Chinner 
1758f7664b31SEric Sandeen static int
xfs_ioc_getlabel(struct xfs_mount * mp,char __user * user_label)1759f7664b31SEric Sandeen xfs_ioc_getlabel(
1760f7664b31SEric Sandeen 	struct xfs_mount	*mp,
1761f7664b31SEric Sandeen 	char			__user *user_label)
1762f7664b31SEric Sandeen {
1763f7664b31SEric Sandeen 	struct xfs_sb		*sbp = &mp->m_sb;
1764f7664b31SEric Sandeen 	char			label[XFSLABEL_MAX + 1];
1765f7664b31SEric Sandeen 
1766f7664b31SEric Sandeen 	/* Paranoia */
1767f7664b31SEric Sandeen 	BUILD_BUG_ON(sizeof(sbp->sb_fname) > FSLABEL_MAX);
1768f7664b31SEric Sandeen 
17694bb8b65aSArnd Bergmann 	/* 1 larger than sb_fname, so this ensures a trailing NUL char */
17704bb8b65aSArnd Bergmann 	memset(label, 0, sizeof(label));
1771f7664b31SEric Sandeen 	spin_lock(&mp->m_sb_lock);
17724bb8b65aSArnd Bergmann 	strncpy(label, sbp->sb_fname, XFSLABEL_MAX);
1773f7664b31SEric Sandeen 	spin_unlock(&mp->m_sb_lock);
1774f7664b31SEric Sandeen 
17754bb8b65aSArnd Bergmann 	if (copy_to_user(user_label, label, sizeof(label)))
1776f7664b31SEric Sandeen 		return -EFAULT;
1777f7664b31SEric Sandeen 	return 0;
1778f7664b31SEric Sandeen }
1779f7664b31SEric Sandeen 
1780f7664b31SEric Sandeen static int
xfs_ioc_setlabel(struct file * filp,struct xfs_mount * mp,char __user * newlabel)1781f7664b31SEric Sandeen xfs_ioc_setlabel(
1782f7664b31SEric Sandeen 	struct file		*filp,
1783f7664b31SEric Sandeen 	struct xfs_mount	*mp,
1784f7664b31SEric Sandeen 	char			__user *newlabel)
1785f7664b31SEric Sandeen {
1786f7664b31SEric Sandeen 	struct xfs_sb		*sbp = &mp->m_sb;
1787f7664b31SEric Sandeen 	char			label[XFSLABEL_MAX + 1];
1788f7664b31SEric Sandeen 	size_t			len;
1789f7664b31SEric Sandeen 	int			error;
1790f7664b31SEric Sandeen 
1791f7664b31SEric Sandeen 	if (!capable(CAP_SYS_ADMIN))
1792f7664b31SEric Sandeen 		return -EPERM;
1793f7664b31SEric Sandeen 	/*
1794f7664b31SEric Sandeen 	 * The generic ioctl allows up to FSLABEL_MAX chars, but XFS is much
1795f7664b31SEric Sandeen 	 * smaller, at 12 bytes.  We copy one more to be sure we find the
1796f7664b31SEric Sandeen 	 * (required) NULL character to test the incoming label length.
1797f7664b31SEric Sandeen 	 * NB: The on disk label doesn't need to be null terminated.
1798f7664b31SEric Sandeen 	 */
1799f7664b31SEric Sandeen 	if (copy_from_user(label, newlabel, XFSLABEL_MAX + 1))
1800f7664b31SEric Sandeen 		return -EFAULT;
1801f7664b31SEric Sandeen 	len = strnlen(label, XFSLABEL_MAX + 1);
1802f7664b31SEric Sandeen 	if (len > sizeof(sbp->sb_fname))
1803f7664b31SEric Sandeen 		return -EINVAL;
1804f7664b31SEric Sandeen 
1805f7664b31SEric Sandeen 	error = mnt_want_write_file(filp);
1806f7664b31SEric Sandeen 	if (error)
1807f7664b31SEric Sandeen 		return error;
1808f7664b31SEric Sandeen 
1809f7664b31SEric Sandeen 	spin_lock(&mp->m_sb_lock);
1810f7664b31SEric Sandeen 	memset(sbp->sb_fname, 0, sizeof(sbp->sb_fname));
18114bb8b65aSArnd Bergmann 	memcpy(sbp->sb_fname, label, len);
1812f7664b31SEric Sandeen 	spin_unlock(&mp->m_sb_lock);
1813f7664b31SEric Sandeen 
1814f7664b31SEric Sandeen 	/*
1815f7664b31SEric Sandeen 	 * Now we do several things to satisfy userspace.
1816f7664b31SEric Sandeen 	 * In addition to normal logging of the primary superblock, we also
1817f7664b31SEric Sandeen 	 * immediately write these changes to sector zero for the primary, then
1818f7664b31SEric Sandeen 	 * update all backup supers (as xfs_db does for a label change), then
1819f7664b31SEric Sandeen 	 * invalidate the block device page cache.  This is so that any prior
1820f7664b31SEric Sandeen 	 * buffered reads from userspace (i.e. from blkid) are invalidated,
1821f7664b31SEric Sandeen 	 * and userspace will see the newly-written label.
1822f7664b31SEric Sandeen 	 */
1823f7664b31SEric Sandeen 	error = xfs_sync_sb_buf(mp);
1824f7664b31SEric Sandeen 	if (error)
1825f7664b31SEric Sandeen 		goto out;
1826f7664b31SEric Sandeen 	/*
1827f7664b31SEric Sandeen 	 * growfs also updates backup supers so lock against that.
1828f7664b31SEric Sandeen 	 */
1829f7664b31SEric Sandeen 	mutex_lock(&mp->m_growlock);
1830f7664b31SEric Sandeen 	error = xfs_update_secondary_sbs(mp);
1831f7664b31SEric Sandeen 	mutex_unlock(&mp->m_growlock);
1832f7664b31SEric Sandeen 
1833f7664b31SEric Sandeen 	invalidate_bdev(mp->m_ddev_targp->bt_bdev);
1834f7664b31SEric Sandeen 
1835f7664b31SEric Sandeen out:
1836f7664b31SEric Sandeen 	mnt_drop_write_file(filp);
1837f7664b31SEric Sandeen 	return error;
1838f7664b31SEric Sandeen }
1839f7664b31SEric Sandeen 
18403737bb2cSDarrick J. Wong static inline int
xfs_fs_eofblocks_from_user(struct xfs_fs_eofblocks * src,struct xfs_icwalk * dst)18413737bb2cSDarrick J. Wong xfs_fs_eofblocks_from_user(
18423737bb2cSDarrick J. Wong 	struct xfs_fs_eofblocks		*src,
1843b26b2bf1SDarrick J. Wong 	struct xfs_icwalk		*dst)
18443737bb2cSDarrick J. Wong {
18453737bb2cSDarrick J. Wong 	if (src->eof_version != XFS_EOFBLOCKS_VERSION)
18463737bb2cSDarrick J. Wong 		return -EINVAL;
18473737bb2cSDarrick J. Wong 
18483737bb2cSDarrick J. Wong 	if (src->eof_flags & ~XFS_EOF_FLAGS_VALID)
18493737bb2cSDarrick J. Wong 		return -EINVAL;
18503737bb2cSDarrick J. Wong 
18513737bb2cSDarrick J. Wong 	if (memchr_inv(&src->pad32, 0, sizeof(src->pad32)) ||
18523737bb2cSDarrick J. Wong 	    memchr_inv(src->pad64, 0, sizeof(src->pad64)))
18533737bb2cSDarrick J. Wong 		return -EINVAL;
18543737bb2cSDarrick J. Wong 
1855b26b2bf1SDarrick J. Wong 	dst->icw_flags = 0;
18562d53f66bSDarrick J. Wong 	if (src->eof_flags & XFS_EOF_FLAGS_SYNC)
1857b26b2bf1SDarrick J. Wong 		dst->icw_flags |= XFS_ICWALK_FLAG_SYNC;
18582d53f66bSDarrick J. Wong 	if (src->eof_flags & XFS_EOF_FLAGS_UID)
1859b26b2bf1SDarrick J. Wong 		dst->icw_flags |= XFS_ICWALK_FLAG_UID;
18602d53f66bSDarrick J. Wong 	if (src->eof_flags & XFS_EOF_FLAGS_GID)
1861b26b2bf1SDarrick J. Wong 		dst->icw_flags |= XFS_ICWALK_FLAG_GID;
18622d53f66bSDarrick J. Wong 	if (src->eof_flags & XFS_EOF_FLAGS_PRID)
1863b26b2bf1SDarrick J. Wong 		dst->icw_flags |= XFS_ICWALK_FLAG_PRID;
18642d53f66bSDarrick J. Wong 	if (src->eof_flags & XFS_EOF_FLAGS_MINFILESIZE)
1865b26b2bf1SDarrick J. Wong 		dst->icw_flags |= XFS_ICWALK_FLAG_MINFILESIZE;
18663737bb2cSDarrick J. Wong 
1867b26b2bf1SDarrick J. Wong 	dst->icw_prid = src->eof_prid;
1868b26b2bf1SDarrick J. Wong 	dst->icw_min_file_size = src->eof_min_file_size;
18693737bb2cSDarrick J. Wong 
1870b26b2bf1SDarrick J. Wong 	dst->icw_uid = INVALID_UID;
18713737bb2cSDarrick J. Wong 	if (src->eof_flags & XFS_EOF_FLAGS_UID) {
1872b26b2bf1SDarrick J. Wong 		dst->icw_uid = make_kuid(current_user_ns(), src->eof_uid);
1873b26b2bf1SDarrick J. Wong 		if (!uid_valid(dst->icw_uid))
18743737bb2cSDarrick J. Wong 			return -EINVAL;
18753737bb2cSDarrick J. Wong 	}
18763737bb2cSDarrick J. Wong 
1877b26b2bf1SDarrick J. Wong 	dst->icw_gid = INVALID_GID;
18783737bb2cSDarrick J. Wong 	if (src->eof_flags & XFS_EOF_FLAGS_GID) {
1879b26b2bf1SDarrick J. Wong 		dst->icw_gid = make_kgid(current_user_ns(), src->eof_gid);
1880b26b2bf1SDarrick J. Wong 		if (!gid_valid(dst->icw_gid))
18813737bb2cSDarrick J. Wong 			return -EINVAL;
18823737bb2cSDarrick J. Wong 	}
18833737bb2cSDarrick J. Wong 	return 0;
18843737bb2cSDarrick J. Wong }
18853737bb2cSDarrick J. Wong 
1886c59d87c4SChristoph Hellwig /*
1887b3bb9413SDarrick J. Wong  * These long-unused ioctls were removed from the official ioctl API in 5.17,
1888b3bb9413SDarrick J. Wong  * but retain these definitions so that we can log warnings about them.
1889b3bb9413SDarrick J. Wong  */
1890b3bb9413SDarrick J. Wong #define XFS_IOC_ALLOCSP		_IOW ('X', 10, struct xfs_flock64)
1891b3bb9413SDarrick J. Wong #define XFS_IOC_FREESP		_IOW ('X', 11, struct xfs_flock64)
1892b3bb9413SDarrick J. Wong #define XFS_IOC_ALLOCSP64	_IOW ('X', 36, struct xfs_flock64)
1893b3bb9413SDarrick J. Wong #define XFS_IOC_FREESP64	_IOW ('X', 37, struct xfs_flock64)
1894b3bb9413SDarrick J. Wong 
1895b3bb9413SDarrick J. Wong /*
1896c59d87c4SChristoph Hellwig  * Note: some of the ioctl's return positive numbers as a
1897c59d87c4SChristoph Hellwig  * byte count indicating success, such as readlink_by_handle.
1898c59d87c4SChristoph Hellwig  * So we don't "sign flip" like most other routines.  This means
1899c59d87c4SChristoph Hellwig  * true errors need to be returned as a negative value.
1900c59d87c4SChristoph Hellwig  */
1901c59d87c4SChristoph Hellwig long
xfs_file_ioctl(struct file * filp,unsigned int cmd,unsigned long p)1902c59d87c4SChristoph Hellwig xfs_file_ioctl(
1903c59d87c4SChristoph Hellwig 	struct file		*filp,
1904c59d87c4SChristoph Hellwig 	unsigned int		cmd,
1905c59d87c4SChristoph Hellwig 	unsigned long		p)
1906c59d87c4SChristoph Hellwig {
1907496ad9aaSAl Viro 	struct inode		*inode = file_inode(filp);
1908c59d87c4SChristoph Hellwig 	struct xfs_inode	*ip = XFS_I(inode);
1909c59d87c4SChristoph Hellwig 	struct xfs_mount	*mp = ip->i_mount;
1910c59d87c4SChristoph Hellwig 	void			__user *arg = (void __user *)p;
1911c59d87c4SChristoph Hellwig 	int			error;
1912c59d87c4SChristoph Hellwig 
1913c59d87c4SChristoph Hellwig 	trace_xfs_file_ioctl(ip);
1914c59d87c4SChristoph Hellwig 
1915c59d87c4SChristoph Hellwig 	switch (cmd) {
1916c59d87c4SChristoph Hellwig 	case FITRIM:
1917c59d87c4SChristoph Hellwig 		return xfs_ioc_trim(mp, arg);
1918f7664b31SEric Sandeen 	case FS_IOC_GETFSLABEL:
1919f7664b31SEric Sandeen 		return xfs_ioc_getlabel(mp, arg);
1920f7664b31SEric Sandeen 	case FS_IOC_SETFSLABEL:
1921f7664b31SEric Sandeen 		return xfs_ioc_setlabel(filp, mp, arg);
1922c59d87c4SChristoph Hellwig 	case XFS_IOC_ALLOCSP:
1923c59d87c4SChristoph Hellwig 	case XFS_IOC_FREESP:
1924c59d87c4SChristoph Hellwig 	case XFS_IOC_ALLOCSP64:
19254d1b97f9SDarrick J. Wong 	case XFS_IOC_FREESP64:
19264d1b97f9SDarrick J. Wong 		xfs_warn_once(mp,
19274d1b97f9SDarrick J. Wong 	"%s should use fallocate; XFS_IOC_{ALLOC,FREE}SP ioctl unsupported",
19284d1b97f9SDarrick J. Wong 				current->comm);
19294d1b97f9SDarrick J. Wong 		return -ENOTTY;
1930c59d87c4SChristoph Hellwig 	case XFS_IOC_DIOINFO: {
1931c7d68318SChristoph Hellwig 		struct xfs_buftarg	*target = xfs_inode_buftarg(ip);
1932c59d87c4SChristoph Hellwig 		struct dioattr		da;
1933c59d87c4SChristoph Hellwig 
19347c71ee78SEric Sandeen 		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
1935c59d87c4SChristoph Hellwig 		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);
1936c59d87c4SChristoph Hellwig 
1937c59d87c4SChristoph Hellwig 		if (copy_to_user(arg, &da, sizeof(da)))
1938b474c7aeSEric Sandeen 			return -EFAULT;
1939c59d87c4SChristoph Hellwig 		return 0;
1940c59d87c4SChristoph Hellwig 	}
1941c59d87c4SChristoph Hellwig 
1942c59d87c4SChristoph Hellwig 	case XFS_IOC_FSBULKSTAT_SINGLE:
1943c59d87c4SChristoph Hellwig 	case XFS_IOC_FSBULKSTAT:
1944c59d87c4SChristoph Hellwig 	case XFS_IOC_FSINUMBERS:
1945f736d93dSChristoph Hellwig 		return xfs_ioc_fsbulkstat(filp, cmd, arg);
1946c59d87c4SChristoph Hellwig 
19470448b6f4SDarrick J. Wong 	case XFS_IOC_BULKSTAT:
1948f736d93dSChristoph Hellwig 		return xfs_ioc_bulkstat(filp, cmd, arg);
1949fba9760aSDarrick J. Wong 	case XFS_IOC_INUMBERS:
1950fba9760aSDarrick J. Wong 		return xfs_ioc_inumbers(mp, cmd, arg);
1951c59d87c4SChristoph Hellwig 
1952c59d87c4SChristoph Hellwig 	case XFS_IOC_FSGEOMETRY_V1:
19531b6d968dSDave Chinner 		return xfs_ioc_fsgeometry(mp, arg, 3);
19541b6d968dSDave Chinner 	case XFS_IOC_FSGEOMETRY_V4:
19551b6d968dSDave Chinner 		return xfs_ioc_fsgeometry(mp, arg, 4);
1956c59d87c4SChristoph Hellwig 	case XFS_IOC_FSGEOMETRY:
19571b6d968dSDave Chinner 		return xfs_ioc_fsgeometry(mp, arg, 5);
1958c59d87c4SChristoph Hellwig 
19597cd5006bSDarrick J. Wong 	case XFS_IOC_AG_GEOMETRY:
19607cd5006bSDarrick J. Wong 		return xfs_ioc_ag_geometry(mp, arg);
19617cd5006bSDarrick J. Wong 
1962c59d87c4SChristoph Hellwig 	case XFS_IOC_GETVERSION:
1963c59d87c4SChristoph Hellwig 		return put_user(inode->i_generation, (int __user *)arg);
1964c59d87c4SChristoph Hellwig 
1965c59d87c4SChristoph Hellwig 	case XFS_IOC_FSGETXATTRA:
19669fefd5dbSMiklos Szeredi 		return xfs_ioc_fsgetxattra(ip, arg);
1967c59d87c4SChristoph Hellwig 
1968c59d87c4SChristoph Hellwig 	case XFS_IOC_GETBMAP:
1969c59d87c4SChristoph Hellwig 	case XFS_IOC_GETBMAPA:
1970c59d87c4SChristoph Hellwig 	case XFS_IOC_GETBMAPX:
1971232b5194SChristoph Hellwig 		return xfs_ioc_getbmap(filp, cmd, arg);
1972c59d87c4SChristoph Hellwig 
1973e89c0413SDarrick J. Wong 	case FS_IOC_GETFSMAP:
1974e89c0413SDarrick J. Wong 		return xfs_ioc_getfsmap(ip, arg);
1975e89c0413SDarrick J. Wong 
197636fd6e86SDarrick J. Wong 	case XFS_IOC_SCRUB_METADATA:
197771bddbccSDarrick J. Wong 		return xfs_ioc_scrub_metadata(filp, arg);
197836fd6e86SDarrick J. Wong 
1979c59d87c4SChristoph Hellwig 	case XFS_IOC_FD_TO_HANDLE:
1980c59d87c4SChristoph Hellwig 	case XFS_IOC_PATH_TO_HANDLE:
1981c59d87c4SChristoph Hellwig 	case XFS_IOC_PATH_TO_FSHANDLE: {
1982c59d87c4SChristoph Hellwig 		xfs_fsop_handlereq_t	hreq;
1983c59d87c4SChristoph Hellwig 
1984c59d87c4SChristoph Hellwig 		if (copy_from_user(&hreq, arg, sizeof(hreq)))
1985b474c7aeSEric Sandeen 			return -EFAULT;
1986c59d87c4SChristoph Hellwig 		return xfs_find_handle(cmd, &hreq);
1987c59d87c4SChristoph Hellwig 	}
1988c59d87c4SChristoph Hellwig 	case XFS_IOC_OPEN_BY_HANDLE: {
1989c59d87c4SChristoph Hellwig 		xfs_fsop_handlereq_t	hreq;
1990c59d87c4SChristoph Hellwig 
1991c59d87c4SChristoph Hellwig 		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
1992b474c7aeSEric Sandeen 			return -EFAULT;
1993c59d87c4SChristoph Hellwig 		return xfs_open_by_handle(filp, &hreq);
1994c59d87c4SChristoph Hellwig 	}
1995c59d87c4SChristoph Hellwig 
1996c59d87c4SChristoph Hellwig 	case XFS_IOC_READLINK_BY_HANDLE: {
1997c59d87c4SChristoph Hellwig 		xfs_fsop_handlereq_t	hreq;
1998c59d87c4SChristoph Hellwig 
1999c59d87c4SChristoph Hellwig 		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
2000b474c7aeSEric Sandeen 			return -EFAULT;
2001c59d87c4SChristoph Hellwig 		return xfs_readlink_by_handle(filp, &hreq);
2002c59d87c4SChristoph Hellwig 	}
2003c59d87c4SChristoph Hellwig 	case XFS_IOC_ATTRLIST_BY_HANDLE:
2004c59d87c4SChristoph Hellwig 		return xfs_attrlist_by_handle(filp, arg);
2005c59d87c4SChristoph Hellwig 
2006c59d87c4SChristoph Hellwig 	case XFS_IOC_ATTRMULTI_BY_HANDLE:
2007c59d87c4SChristoph Hellwig 		return xfs_attrmulti_by_handle(filp, arg);
2008c59d87c4SChristoph Hellwig 
2009c59d87c4SChristoph Hellwig 	case XFS_IOC_SWAPEXT: {
2010c59d87c4SChristoph Hellwig 		struct xfs_swapext	sxp;
2011c59d87c4SChristoph Hellwig 
2012c59d87c4SChristoph Hellwig 		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
2013b474c7aeSEric Sandeen 			return -EFAULT;
2014d9457dc0SJan Kara 		error = mnt_want_write_file(filp);
2015d9457dc0SJan Kara 		if (error)
2016d9457dc0SJan Kara 			return error;
2017a133d952SDave Chinner 		error = xfs_ioc_swapext(&sxp);
2018d9457dc0SJan Kara 		mnt_drop_write_file(filp);
20192451337dSDave Chinner 		return error;
2020c59d87c4SChristoph Hellwig 	}
2021c59d87c4SChristoph Hellwig 
2022c59d87c4SChristoph Hellwig 	case XFS_IOC_FSCOUNTS: {
2023c59d87c4SChristoph Hellwig 		xfs_fsop_counts_t out;
2024c59d87c4SChristoph Hellwig 
202591083269SEric Sandeen 		xfs_fs_counts(mp, &out);
2026c59d87c4SChristoph Hellwig 
2027c59d87c4SChristoph Hellwig 		if (copy_to_user(arg, &out, sizeof(out)))
2028b474c7aeSEric Sandeen 			return -EFAULT;
2029c59d87c4SChristoph Hellwig 		return 0;
2030c59d87c4SChristoph Hellwig 	}
2031c59d87c4SChristoph Hellwig 
2032c59d87c4SChristoph Hellwig 	case XFS_IOC_SET_RESBLKS: {
2033c59d87c4SChristoph Hellwig 		xfs_fsop_resblks_t inout;
2034c8ce540dSDarrick J. Wong 		uint64_t	   in;
2035c59d87c4SChristoph Hellwig 
2036c59d87c4SChristoph Hellwig 		if (!capable(CAP_SYS_ADMIN))
2037c59d87c4SChristoph Hellwig 			return -EPERM;
2038c59d87c4SChristoph Hellwig 
20392e973b2cSDave Chinner 		if (xfs_is_readonly(mp))
2040b474c7aeSEric Sandeen 			return -EROFS;
2041c59d87c4SChristoph Hellwig 
2042c59d87c4SChristoph Hellwig 		if (copy_from_user(&inout, arg, sizeof(inout)))
2043b474c7aeSEric Sandeen 			return -EFAULT;
2044c59d87c4SChristoph Hellwig 
2045d9457dc0SJan Kara 		error = mnt_want_write_file(filp);
2046d9457dc0SJan Kara 		if (error)
2047d9457dc0SJan Kara 			return error;
2048d9457dc0SJan Kara 
2049c59d87c4SChristoph Hellwig 		/* input parameter is passed in resblks field of structure */
2050c59d87c4SChristoph Hellwig 		in = inout.resblks;
2051c59d87c4SChristoph Hellwig 		error = xfs_reserve_blocks(mp, &in, &inout);
2052d9457dc0SJan Kara 		mnt_drop_write_file(filp);
2053c59d87c4SChristoph Hellwig 		if (error)
20542451337dSDave Chinner 			return error;
2055c59d87c4SChristoph Hellwig 
2056c59d87c4SChristoph Hellwig 		if (copy_to_user(arg, &inout, sizeof(inout)))
2057b474c7aeSEric Sandeen 			return -EFAULT;
2058c59d87c4SChristoph Hellwig 		return 0;
2059c59d87c4SChristoph Hellwig 	}
2060c59d87c4SChristoph Hellwig 
2061c59d87c4SChristoph Hellwig 	case XFS_IOC_GET_RESBLKS: {
2062c59d87c4SChristoph Hellwig 		xfs_fsop_resblks_t out;
2063c59d87c4SChristoph Hellwig 
2064c59d87c4SChristoph Hellwig 		if (!capable(CAP_SYS_ADMIN))
2065c59d87c4SChristoph Hellwig 			return -EPERM;
2066c59d87c4SChristoph Hellwig 
2067c59d87c4SChristoph Hellwig 		error = xfs_reserve_blocks(mp, NULL, &out);
2068c59d87c4SChristoph Hellwig 		if (error)
20692451337dSDave Chinner 			return error;
2070c59d87c4SChristoph Hellwig 
2071c59d87c4SChristoph Hellwig 		if (copy_to_user(arg, &out, sizeof(out)))
2072b474c7aeSEric Sandeen 			return -EFAULT;
2073c59d87c4SChristoph Hellwig 
2074c59d87c4SChristoph Hellwig 		return 0;
2075c59d87c4SChristoph Hellwig 	}
2076c59d87c4SChristoph Hellwig 
2077c59d87c4SChristoph Hellwig 	case XFS_IOC_FSGROWFSDATA: {
207807aabd9cSGao Xiang 		struct xfs_growfs_data in;
2079c59d87c4SChristoph Hellwig 
2080c59d87c4SChristoph Hellwig 		if (copy_from_user(&in, arg, sizeof(in)))
2081b474c7aeSEric Sandeen 			return -EFAULT;
2082c59d87c4SChristoph Hellwig 
2083d9457dc0SJan Kara 		error = mnt_want_write_file(filp);
2084d9457dc0SJan Kara 		if (error)
2085d9457dc0SJan Kara 			return error;
2086c59d87c4SChristoph Hellwig 		error = xfs_growfs_data(mp, &in);
2087d9457dc0SJan Kara 		mnt_drop_write_file(filp);
20882451337dSDave Chinner 		return error;
2089c59d87c4SChristoph Hellwig 	}
2090c59d87c4SChristoph Hellwig 
2091c59d87c4SChristoph Hellwig 	case XFS_IOC_FSGROWFSLOG: {
209207aabd9cSGao Xiang 		struct xfs_growfs_log in;
2093c59d87c4SChristoph Hellwig 
2094c59d87c4SChristoph Hellwig 		if (copy_from_user(&in, arg, sizeof(in)))
2095b474c7aeSEric Sandeen 			return -EFAULT;
2096c59d87c4SChristoph Hellwig 
2097d9457dc0SJan Kara 		error = mnt_want_write_file(filp);
2098d9457dc0SJan Kara 		if (error)
2099d9457dc0SJan Kara 			return error;
2100c59d87c4SChristoph Hellwig 		error = xfs_growfs_log(mp, &in);
2101d9457dc0SJan Kara 		mnt_drop_write_file(filp);
21022451337dSDave Chinner 		return error;
2103c59d87c4SChristoph Hellwig 	}
2104c59d87c4SChristoph Hellwig 
2105c59d87c4SChristoph Hellwig 	case XFS_IOC_FSGROWFSRT: {
2106c59d87c4SChristoph Hellwig 		xfs_growfs_rt_t in;
2107c59d87c4SChristoph Hellwig 
2108c59d87c4SChristoph Hellwig 		if (copy_from_user(&in, arg, sizeof(in)))
2109b474c7aeSEric Sandeen 			return -EFAULT;
2110c59d87c4SChristoph Hellwig 
2111d9457dc0SJan Kara 		error = mnt_want_write_file(filp);
2112d9457dc0SJan Kara 		if (error)
2113d9457dc0SJan Kara 			return error;
2114c59d87c4SChristoph Hellwig 		error = xfs_growfs_rt(mp, &in);
2115d9457dc0SJan Kara 		mnt_drop_write_file(filp);
21162451337dSDave Chinner 		return error;
2117c59d87c4SChristoph Hellwig 	}
2118c59d87c4SChristoph Hellwig 
2119c59d87c4SChristoph Hellwig 	case XFS_IOC_GOINGDOWN: {
2120c8ce540dSDarrick J. Wong 		uint32_t in;
2121c59d87c4SChristoph Hellwig 
2122c59d87c4SChristoph Hellwig 		if (!capable(CAP_SYS_ADMIN))
2123c59d87c4SChristoph Hellwig 			return -EPERM;
2124c59d87c4SChristoph Hellwig 
2125c8ce540dSDarrick J. Wong 		if (get_user(in, (uint32_t __user *)arg))
2126b474c7aeSEric Sandeen 			return -EFAULT;
2127c59d87c4SChristoph Hellwig 
21282451337dSDave Chinner 		return xfs_fs_goingdown(mp, in);
2129c59d87c4SChristoph Hellwig 	}
2130c59d87c4SChristoph Hellwig 
2131c59d87c4SChristoph Hellwig 	case XFS_IOC_ERROR_INJECTION: {
2132c59d87c4SChristoph Hellwig 		xfs_error_injection_t in;
2133c59d87c4SChristoph Hellwig 
2134c59d87c4SChristoph Hellwig 		if (!capable(CAP_SYS_ADMIN))
2135c59d87c4SChristoph Hellwig 			return -EPERM;
2136c59d87c4SChristoph Hellwig 
2137c59d87c4SChristoph Hellwig 		if (copy_from_user(&in, arg, sizeof(in)))
2138b474c7aeSEric Sandeen 			return -EFAULT;
2139c59d87c4SChristoph Hellwig 
214031965ef3SDarrick J. Wong 		return xfs_errortag_add(mp, in.errtag);
2141c59d87c4SChristoph Hellwig 	}
2142c59d87c4SChristoph Hellwig 
2143c59d87c4SChristoph Hellwig 	case XFS_IOC_ERROR_CLEARALL:
2144c59d87c4SChristoph Hellwig 		if (!capable(CAP_SYS_ADMIN))
2145c59d87c4SChristoph Hellwig 			return -EPERM;
2146c59d87c4SChristoph Hellwig 
214731965ef3SDarrick J. Wong 		return xfs_errortag_clearall(mp);
2148c59d87c4SChristoph Hellwig 
21498ca149deSBrian Foster 	case XFS_IOC_FREE_EOFBLOCKS: {
2150b9fe5052SDwight Engen 		struct xfs_fs_eofblocks	eofb;
2151b26b2bf1SDarrick J. Wong 		struct xfs_icwalk	icw;
21528ca149deSBrian Foster 
21538c567a7fSDwight Engen 		if (!capable(CAP_SYS_ADMIN))
21548c567a7fSDwight Engen 			return -EPERM;
21558c567a7fSDwight Engen 
21562e973b2cSDave Chinner 		if (xfs_is_readonly(mp))
2157b474c7aeSEric Sandeen 			return -EROFS;
21588c567a7fSDwight Engen 
21598ca149deSBrian Foster 		if (copy_from_user(&eofb, arg, sizeof(eofb)))
2160b474c7aeSEric Sandeen 			return -EFAULT;
21618ca149deSBrian Foster 
2162b26b2bf1SDarrick J. Wong 		error = xfs_fs_eofblocks_from_user(&eofb, &icw);
2163b9fe5052SDwight Engen 		if (error)
21642451337dSDave Chinner 			return error;
2165b9fe5052SDwight Engen 
2166b26b2bf1SDarrick J. Wong 		trace_xfs_ioc_free_eofblocks(mp, &icw, _RET_IP_);
216738899f80SDarrick J. Wong 
21684b674b9aSBrian Foster 		sb_start_write(mp->m_super);
2169b26b2bf1SDarrick J. Wong 		error = xfs_blockgc_free_space(mp, &icw);
21704b674b9aSBrian Foster 		sb_end_write(mp->m_super);
21714b674b9aSBrian Foster 		return error;
21728ca149deSBrian Foster 	}
21738ca149deSBrian Foster 
2174c59d87c4SChristoph Hellwig 	default:
2175c59d87c4SChristoph Hellwig 		return -ENOTTY;
2176c59d87c4SChristoph Hellwig 	}
2177c59d87c4SChristoph Hellwig }
2178