xfs_ioctl.c 50.4 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2 3
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
L
Linus Torvalds 已提交
4
 *
5 6
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
L
Linus Torvalds 已提交
7 8
 * published by the Free Software Foundation.
 *
9 10 11 12
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
L
Linus Torvalds 已提交
13
 *
14 15 16
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
L
Linus Torvalds 已提交
17 18 19
 */
#include "xfs.h"
#include "xfs_fs.h"
20
#include "xfs_shared.h"
21 22 23
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
L
Linus Torvalds 已提交
24 25
#include "xfs_mount.h"
#include "xfs_inode.h"
26
#include "xfs_ioctl.h"
27
#include "xfs_alloc.h"
L
Linus Torvalds 已提交
28 29
#include "xfs_rtalloc.h"
#include "xfs_itable.h"
30
#include "xfs_error.h"
L
Linus Torvalds 已提交
31
#include "xfs_attr.h"
32
#include "xfs_bmap.h"
D
Dave Chinner 已提交
33
#include "xfs_bmap_util.h"
L
Linus Torvalds 已提交
34
#include "xfs_fsops.h"
C
Christoph Hellwig 已提交
35
#include "xfs_discard.h"
36
#include "xfs_quota.h"
37
#include "xfs_export.h"
C
Christoph Hellwig 已提交
38
#include "xfs_trace.h"
39
#include "xfs_icache.h"
D
Dave Chinner 已提交
40
#include "xfs_symlink.h"
41
#include "xfs_trans.h"
42
#include "xfs_acl.h"
43 44 45
#include "xfs_btree.h"
#include <linux/fsmap.h>
#include "xfs_fsmap.h"
46
#include "scrub/xfs_scrub.h"
47
#include "xfs_sb.h"
L
Linus Torvalds 已提交
48

49
#include <linux/capability.h>
50
#include <linux/cred.h>
L
Linus Torvalds 已提交
51 52 53 54
#include <linux/dcache.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
55
#include <linux/slab.h>
56
#include <linux/exportfs.h>
L
Linus Torvalds 已提交
57 58 59 60 61 62 63 64 65 66 67 68

/*
 * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
 * a file or fs handle.
 *
 * XFS_IOC_PATH_TO_FSHANDLE
 *    returns fs handle for a mount point or path within that mount point
 * XFS_IOC_FD_TO_HANDLE
 *    returns full handle for a FD opened in user space
 * XFS_IOC_PATH_TO_HANDLE
 *    returns full handle for a path
 */
69
int
L
Linus Torvalds 已提交
70 71
xfs_find_handle(
	unsigned int		cmd,
72
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
73 74 75 76
{
	int			hsize;
	xfs_handle_t		handle;
	struct inode		*inode;
77
	struct fd		f = {NULL};
C
Christoph Hellwig 已提交
78
	struct path		path;
79
	int			error;
C
Christoph Hellwig 已提交
80
	struct xfs_inode	*ip;
L
Linus Torvalds 已提交
81

C
Christoph Hellwig 已提交
82
	if (cmd == XFS_IOC_FD_TO_HANDLE) {
83 84
		f = fdget(hreq->fd);
		if (!f.file)
C
Christoph Hellwig 已提交
85
			return -EBADF;
A
Al Viro 已提交
86
		inode = file_inode(f.file);
C
Christoph Hellwig 已提交
87 88 89 90
	} else {
		error = user_lpath((const char __user *)hreq->path, &path);
		if (error)
			return error;
91
		inode = d_inode(path.dentry);
L
Linus Torvalds 已提交
92
	}
C
Christoph Hellwig 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	ip = XFS_I(inode);

	/*
	 * We can only generate handles for inodes residing on a XFS filesystem,
	 * and only for regular files, directories or symbolic links.
	 */
	error = -EINVAL;
	if (inode->i_sb->s_magic != XFS_SB_MAGIC)
		goto out_put;

	error = -EBADF;
	if (!S_ISREG(inode->i_mode) &&
	    !S_ISDIR(inode->i_mode) &&
	    !S_ISLNK(inode->i_mode))
		goto out_put;


	memcpy(&handle.ha_fsid, ip->i_mount->m_fixedfsid, sizeof(xfs_fsid_t));

	if (cmd == XFS_IOC_PATH_TO_FSHANDLE) {
		/*
		 * This handle only contains an fsid, zero the rest.
		 */
		memset(&handle.ha_fid, 0, sizeof(handle.ha_fid));
		hsize = sizeof(xfs_fsid_t);
	} else {
C
Christoph Hellwig 已提交
119 120 121
		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
					sizeof(handle.ha_fid.fid_len);
		handle.ha_fid.fid_pad = 0;
122
		handle.ha_fid.fid_gen = inode->i_generation;
C
Christoph Hellwig 已提交
123
		handle.ha_fid.fid_ino = ip->i_ino;
C
Christoph Hellwig 已提交
124
		hsize = sizeof(xfs_handle_t);
L
Linus Torvalds 已提交
125 126
	}

C
Christoph Hellwig 已提交
127
	error = -EFAULT;
128
	if (copy_to_user(hreq->ohandle, &handle, hsize) ||
C
Christoph Hellwig 已提交
129 130
	    copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
		goto out_put;
L
Linus Torvalds 已提交
131

C
Christoph Hellwig 已提交
132 133 134 135
	error = 0;

 out_put:
	if (cmd == XFS_IOC_FD_TO_HANDLE)
136
		fdput(f);
C
Christoph Hellwig 已提交
137 138 139
	else
		path_put(&path);
	return error;
L
Linus Torvalds 已提交
140 141 142
}

/*
143 144
 * No need to do permission checks on the various pathname components
 * as the handle operations are privileged.
L
Linus Torvalds 已提交
145 146
 */
STATIC int
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
xfs_handle_acceptable(
	void			*context,
	struct dentry		*dentry)
{
	return 1;
}

/*
 * Convert userspace handle data into a dentry.
 */
struct dentry *
xfs_handle_to_dentry(
	struct file		*parfilp,
	void __user		*uhandle,
	u32			hlen)
L
Linus Torvalds 已提交
162 163
{
	xfs_handle_t		handle;
164
	struct xfs_fid64	fid;
L
Linus Torvalds 已提交
165 166 167 168

	/*
	 * Only allow handle opens under a directory.
	 */
A
Al Viro 已提交
169
	if (!S_ISDIR(file_inode(parfilp)->i_mode))
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
		return ERR_PTR(-ENOTDIR);

	if (hlen != sizeof(xfs_handle_t))
		return ERR_PTR(-EINVAL);
	if (copy_from_user(&handle, uhandle, hlen))
		return ERR_PTR(-EFAULT);
	if (handle.ha_fid.fid_len !=
	    sizeof(handle.ha_fid) - sizeof(handle.ha_fid.fid_len))
		return ERR_PTR(-EINVAL);

	memset(&fid, 0, sizeof(struct fid));
	fid.ino = handle.ha_fid.fid_ino;
	fid.gen = handle.ha_fid.fid_gen;

	return exportfs_decode_fh(parfilp->f_path.mnt, (struct fid *)&fid, 3,
			FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG,
			xfs_handle_acceptable, NULL);
}
L
Linus Torvalds 已提交
188

189 190 191 192 193 194
STATIC struct dentry *
xfs_handlereq_to_dentry(
	struct file		*parfilp,
	xfs_fsop_handlereq_t	*hreq)
{
	return xfs_handle_to_dentry(parfilp, hreq->ihandle, hreq->ihandlen);
L
Linus Torvalds 已提交
195 196
}

197
int
L
Linus Torvalds 已提交
198 199
xfs_open_by_handle(
	struct file		*parfilp,
200
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
201
{
202
	const struct cred	*cred = current_cred();
L
Linus Torvalds 已提交
203
	int			error;
204
	int			fd;
L
Linus Torvalds 已提交
205 206 207 208
	int			permflag;
	struct file		*filp;
	struct inode		*inode;
	struct dentry		*dentry;
209
	fmode_t			fmode;
210
	struct path		path;
L
Linus Torvalds 已提交
211 212

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
213
		return -EPERM;
L
Linus Torvalds 已提交
214

215 216 217
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
218
	inode = d_inode(dentry);
L
Linus Torvalds 已提交
219 220 221

	/* Restrict xfs_open_by_handle to directories & regular files. */
	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
E
Eric Sandeen 已提交
222
		error = -EPERM;
223
		goto out_dput;
L
Linus Torvalds 已提交
224 225 226
	}

#if BITS_PER_LONG != 32
227
	hreq->oflags |= O_LARGEFILE;
L
Linus Torvalds 已提交
228
#endif
229

230
	permflag = hreq->oflags;
231
	fmode = OPEN_FMODE(permflag);
L
Linus Torvalds 已提交
232
	if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
233
	    (fmode & FMODE_WRITE) && IS_APPEND(inode)) {
E
Eric Sandeen 已提交
234
		error = -EPERM;
235
		goto out_dput;
L
Linus Torvalds 已提交
236 237
	}

238
	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
E
Eryu Guan 已提交
239
		error = -EPERM;
240
		goto out_dput;
L
Linus Torvalds 已提交
241 242 243
	}

	/* Can't write directories. */
244
	if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {
E
Eric Sandeen 已提交
245
		error = -EISDIR;
246
		goto out_dput;
L
Linus Torvalds 已提交
247 248
	}

249
	fd = get_unused_fd_flags(0);
250 251 252
	if (fd < 0) {
		error = fd;
		goto out_dput;
L
Linus Torvalds 已提交
253 254
	}

255 256 257 258
	path.mnt = parfilp->f_path.mnt;
	path.dentry = dentry;
	filp = dentry_open(&path, hreq->oflags, cred);
	dput(dentry);
L
Linus Torvalds 已提交
259
	if (IS_ERR(filp)) {
260 261
		put_unused_fd(fd);
		return PTR_ERR(filp);
L
Linus Torvalds 已提交
262
	}
263

A
Al Viro 已提交
264
	if (S_ISREG(inode->i_mode)) {
265
		filp->f_flags |= O_NOATIME;
266
		filp->f_mode |= FMODE_NOCMTIME;
267
	}
L
Linus Torvalds 已提交
268

269 270 271 272 273 274
	fd_install(fd, filp);
	return fd;

 out_dput:
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
275 276
}

277
int
L
Linus Torvalds 已提交
278
xfs_readlink_by_handle(
279 280
	struct file		*parfilp,
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
281
{
282
	struct dentry		*dentry;
L
Linus Torvalds 已提交
283
	__u32			olen;
284
	int			error;
L
Linus Torvalds 已提交
285 286

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
287
		return -EPERM;
L
Linus Torvalds 已提交
288

289 290 291
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
292 293

	/* Restrict this handle operation to symlinks only. */
294
	if (!d_is_symlink(dentry)) {
E
Eric Sandeen 已提交
295
		error = -EINVAL;
296
		goto out_dput;
L
Linus Torvalds 已提交
297 298
	}

299
	if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
E
Eric Sandeen 已提交
300
		error = -EFAULT;
301
		goto out_dput;
L
Linus Torvalds 已提交
302 303
	}

304
	error = vfs_readlink(dentry, hreq->ohandle, olen);
305

306 307
 out_dput:
	dput(dentry);
308
	return error;
L
Linus Torvalds 已提交
309 310
}

D
Dave Chinner 已提交
311 312 313
int
xfs_set_dmattrs(
	xfs_inode_t     *ip,
D
Darrick J. Wong 已提交
314 315
	uint		evmask,
	uint16_t	state)
D
Dave Chinner 已提交
316 317 318 319 320 321
{
	xfs_mount_t	*mp = ip->i_mount;
	xfs_trans_t	*tp;
	int		error;

	if (!capable(CAP_SYS_ADMIN))
D
Dave Chinner 已提交
322
		return -EPERM;
D
Dave Chinner 已提交
323 324

	if (XFS_FORCED_SHUTDOWN(mp))
D
Dave Chinner 已提交
325
		return -EIO;
D
Dave Chinner 已提交
326

327 328
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
	if (error)
D
Dave Chinner 已提交
329
		return error;
330

D
Dave Chinner 已提交
331 332 333 334 335 336 337
	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);

	ip->i_d.di_dmevmask = evmask;
	ip->i_d.di_dmstate  = state;

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
338
	error = xfs_trans_commit(tp);
D
Dave Chinner 已提交
339 340 341 342

	return error;
}

L
Linus Torvalds 已提交
343 344
STATIC int
xfs_fssetdm_by_handle(
345 346
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
347 348 349 350
{
	int			error;
	struct fsdmidata	fsd;
	xfs_fsop_setdm_handlereq_t dmhreq;
351
	struct dentry		*dentry;
L
Linus Torvalds 已提交
352 353

	if (!capable(CAP_MKNOD))
E
Eric Sandeen 已提交
354
		return -EPERM;
L
Linus Torvalds 已提交
355
	if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
E
Eric Sandeen 已提交
356
		return -EFAULT;
L
Linus Torvalds 已提交
357

J
Jan Kara 已提交
358 359 360 361
	error = mnt_want_write_file(parfilp);
	if (error)
		return error;

362
	dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
J
Jan Kara 已提交
363 364
	if (IS_ERR(dentry)) {
		mnt_drop_write_file(parfilp);
365
		return PTR_ERR(dentry);
J
Jan Kara 已提交
366
	}
L
Linus Torvalds 已提交
367

368
	if (IS_IMMUTABLE(d_inode(dentry)) || IS_APPEND(d_inode(dentry))) {
E
Eric Sandeen 已提交
369
		error = -EPERM;
370
		goto out;
L
Linus Torvalds 已提交
371 372 373
	}

	if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
E
Eric Sandeen 已提交
374
		error = -EFAULT;
375
		goto out;
L
Linus Torvalds 已提交
376 377
	}

378
	error = xfs_set_dmattrs(XFS_I(d_inode(dentry)), fsd.fsd_dmevmask,
379
				 fsd.fsd_dmstate);
L
Linus Torvalds 已提交
380

381
 out:
J
Jan Kara 已提交
382
	mnt_drop_write_file(parfilp);
383
	dput(dentry);
384
	return error;
L
Linus Torvalds 已提交
385 386 387 388
}

STATIC int
xfs_attrlist_by_handle(
389 390
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
391
{
392
	int			error = -ENOMEM;
L
Linus Torvalds 已提交
393
	attrlist_cursor_kern_t	*cursor;
394
	struct xfs_fsop_attrlist_handlereq __user	*p = arg;
L
Linus Torvalds 已提交
395
	xfs_fsop_attrlist_handlereq_t al_hreq;
396
	struct dentry		*dentry;
L
Linus Torvalds 已提交
397 398 399
	char			*kbuf;

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
400
		return -EPERM;
L
Linus Torvalds 已提交
401
	if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
E
Eric Sandeen 已提交
402
		return -EFAULT;
403
	if (al_hreq.buflen < sizeof(struct attrlist) ||
J
Jan Tulak 已提交
404
	    al_hreq.buflen > XFS_XATTR_LIST_MAX)
E
Eric Sandeen 已提交
405
		return -EINVAL;
L
Linus Torvalds 已提交
406

407 408 409 410
	/*
	 * Reject flags, only allow namespaces.
	 */
	if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
E
Eric Sandeen 已提交
411
		return -EINVAL;
412

413 414 415
	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
416

417 418 419
	kbuf = kmem_zalloc_large(al_hreq.buflen, KM_SLEEP);
	if (!kbuf)
		goto out_dput;
L
Linus Torvalds 已提交
420 421

	cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
422
	error = xfs_attr_list(XFS_I(d_inode(dentry)), kbuf, al_hreq.buflen,
423
					al_hreq.flags, cursor);
L
Linus Torvalds 已提交
424 425 426
	if (error)
		goto out_kfree;

427 428 429 430 431
	if (copy_to_user(&p->pos, cursor, sizeof(attrlist_cursor_kern_t))) {
		error = -EFAULT;
		goto out_kfree;
	}

L
Linus Torvalds 已提交
432 433 434
	if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
		error = -EFAULT;

435 436 437
out_kfree:
	kmem_free(kbuf);
out_dput:
438 439
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
440 441
}

442
int
L
Linus Torvalds 已提交
443
xfs_attrmulti_attr_get(
444
	struct inode		*inode,
445 446
	unsigned char		*name,
	unsigned char		__user *ubuf,
447 448
	uint32_t		*len,
	uint32_t		flags)
L
Linus Torvalds 已提交
449
{
450
	unsigned char		*kbuf;
D
Dave Chinner 已提交
451
	int			error = -EFAULT;
452

453
	if (*len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
454
		return -EINVAL;
455 456
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
D
Dave Chinner 已提交
457
		return -ENOMEM;
L
Linus Torvalds 已提交
458

459
	error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
L
Linus Torvalds 已提交
460 461 462 463
	if (error)
		goto out_kfree;

	if (copy_to_user(ubuf, kbuf, *len))
D
Dave Chinner 已提交
464
		error = -EFAULT;
L
Linus Torvalds 已提交
465

466 467
out_kfree:
	kmem_free(kbuf);
L
Linus Torvalds 已提交
468 469 470
	return error;
}

471
int
L
Linus Torvalds 已提交
472
xfs_attrmulti_attr_set(
473
	struct inode		*inode,
474 475
	unsigned char		*name,
	const unsigned char	__user *ubuf,
476 477
	uint32_t		len,
	uint32_t		flags)
L
Linus Torvalds 已提交
478
{
479
	unsigned char		*kbuf;
480
	int			error;
L
Linus Torvalds 已提交
481

482
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
483
		return -EPERM;
484
	if (len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
485
		return -EINVAL;
L
Linus Torvalds 已提交
486

L
Li Zefan 已提交
487 488 489
	kbuf = memdup_user(ubuf, len);
	if (IS_ERR(kbuf))
		return PTR_ERR(kbuf);
490

491
	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
492 493
	if (!error)
		xfs_forget_acl(inode, name, flags);
494 495
	kfree(kbuf);
	return error;
L
Linus Torvalds 已提交
496 497
}

498
int
L
Linus Torvalds 已提交
499
xfs_attrmulti_attr_remove(
500
	struct inode		*inode,
501
	unsigned char		*name,
502
	uint32_t		flags)
L
Linus Torvalds 已提交
503
{
504 505
	int			error;

506
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
507
		return -EPERM;
508 509 510 511
	error = xfs_attr_remove(XFS_I(inode), name, flags);
	if (!error)
		xfs_forget_acl(inode, name, flags);
	return error;
L
Linus Torvalds 已提交
512 513 514 515
}

STATIC int
xfs_attrmulti_by_handle(
516
	struct file		*parfilp,
517
	void			__user *arg)
L
Linus Torvalds 已提交
518 519 520 521
{
	int			error;
	xfs_attr_multiop_t	*ops;
	xfs_fsop_attrmulti_handlereq_t am_hreq;
522
	struct dentry		*dentry;
L
Linus Torvalds 已提交
523
	unsigned int		i, size;
524
	unsigned char		*attr_name;
L
Linus Torvalds 已提交
525 526

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
527
		return -EPERM;
L
Linus Torvalds 已提交
528
	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
E
Eric Sandeen 已提交
529
		return -EFAULT;
L
Linus Torvalds 已提交
530

531 532 533 534
	/* overflow check */
	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
		return -E2BIG;

535 536 537
	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
538

D
Dave Chinner 已提交
539
	error = -E2BIG;
C
Christoph Hellwig 已提交
540
	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
L
Linus Torvalds 已提交
541
	if (!size || size > 16 * PAGE_SIZE)
542
		goto out_dput;
L
Linus Torvalds 已提交
543

L
Li Zefan 已提交
544 545
	ops = memdup_user(am_hreq.ops, size);
	if (IS_ERR(ops)) {
D
Dave Chinner 已提交
546
		error = PTR_ERR(ops);
547
		goto out_dput;
L
Li Zefan 已提交
548
	}
L
Linus Torvalds 已提交
549

D
Dave Chinner 已提交
550
	error = -ENOMEM;
L
Linus Torvalds 已提交
551 552 553 554 555 556
	attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
	if (!attr_name)
		goto out_kfree_ops;

	error = 0;
	for (i = 0; i < am_hreq.opcount; i++) {
557
		ops[i].am_error = strncpy_from_user((char *)attr_name,
L
Linus Torvalds 已提交
558 559
				ops[i].am_attrname, MAXNAMELEN);
		if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
D
Dave Chinner 已提交
560
			error = -ERANGE;
L
Linus Torvalds 已提交
561 562 563 564 565
		if (ops[i].am_error < 0)
			break;

		switch (ops[i].am_opcode) {
		case ATTR_OP_GET:
566
			ops[i].am_error = xfs_attrmulti_attr_get(
567
					d_inode(dentry), attr_name,
568 569
					ops[i].am_attrvalue, &ops[i].am_length,
					ops[i].am_flags);
L
Linus Torvalds 已提交
570 571
			break;
		case ATTR_OP_SET:
572
			ops[i].am_error = mnt_want_write_file(parfilp);
573 574
			if (ops[i].am_error)
				break;
575
			ops[i].am_error = xfs_attrmulti_attr_set(
576
					d_inode(dentry), attr_name,
577 578
					ops[i].am_attrvalue, ops[i].am_length,
					ops[i].am_flags);
A
Al Viro 已提交
579
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
580 581
			break;
		case ATTR_OP_REMOVE:
582
			ops[i].am_error = mnt_want_write_file(parfilp);
583 584
			if (ops[i].am_error)
				break;
585
			ops[i].am_error = xfs_attrmulti_attr_remove(
586
					d_inode(dentry), attr_name,
587
					ops[i].am_flags);
A
Al Viro 已提交
588
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
589 590
			break;
		default:
D
Dave Chinner 已提交
591
			ops[i].am_error = -EINVAL;
L
Linus Torvalds 已提交
592 593 594 595
		}
	}

	if (copy_to_user(am_hreq.ops, ops, size))
D
Dave Chinner 已提交
596
		error = -EFAULT;
L
Linus Torvalds 已提交
597 598 599 600

	kfree(attr_name);
 out_kfree_ops:
	kfree(ops);
601 602
 out_dput:
	dput(dentry);
D
Dave Chinner 已提交
603
	return error;
L
Linus Torvalds 已提交
604 605
}

606
int
L
Linus Torvalds 已提交
607 608 609
xfs_ioc_space(
	struct file		*filp,
	unsigned int		cmd,
610
	xfs_flock64_t		*bf)
L
Linus Torvalds 已提交
611
{
612 613
	struct inode		*inode = file_inode(filp);
	struct xfs_inode	*ip = XFS_I(inode);
614
	struct iattr		iattr;
615
	enum xfs_prealloc_flags	flags = 0;
616
	uint			iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
L
Linus Torvalds 已提交
617 618
	int			error;

619 620 621 622 623 624
	/*
	 * Only allow the sys admin to reserve space unless
	 * unwritten extents are enabled.
	 */
	if (!xfs_sb_version_hasextflgbit(&ip->i_mount->m_sb) &&
	    !capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
625
		return -EPERM;
626

627
	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
E
Eric Sandeen 已提交
628
		return -EPERM;
L
Linus Torvalds 已提交
629

630
	if (!(filp->f_mode & FMODE_WRITE))
E
Eric Sandeen 已提交
631
		return -EBADF;
L
Linus Torvalds 已提交
632

633
	if (!S_ISREG(inode->i_mode))
E
Eric Sandeen 已提交
634
		return -EINVAL;
L
Linus Torvalds 已提交
635

636 637
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
638
	if (filp->f_mode & FMODE_NOCMTIME)
639 640
		flags |= XFS_PREALLOC_INVISIBLE;

J
Jan Kara 已提交
641 642 643
	error = mnt_want_write_file(filp);
	if (error)
		return error;
644

645
	xfs_ilock(ip, iolock);
646
	error = xfs_break_layouts(inode, &iolock, BREAK_UNMAP);
647 648
	if (error)
		goto out_unlock;
649 650 651 652 653 654 655 656 657 658 659

	switch (bf->l_whence) {
	case 0: /*SEEK_SET*/
		break;
	case 1: /*SEEK_CUR*/
		bf->l_start += filp->f_pos;
		break;
	case 2: /*SEEK_END*/
		bf->l_start += XFS_ISIZE(ip);
		break;
	default:
D
Dave Chinner 已提交
660
		error = -EINVAL;
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
		goto out_unlock;
	}

	/*
	 * length of <= 0 for resv/unresv/zero is invalid.  length for
	 * alloc/free is ignored completely and we have no idea what userspace
	 * might have set it to, so set it to zero to allow range
	 * checks to pass.
	 */
	switch (cmd) {
	case XFS_IOC_ZERO_RANGE:
	case XFS_IOC_RESVSP:
	case XFS_IOC_RESVSP64:
	case XFS_IOC_UNRESVSP:
	case XFS_IOC_UNRESVSP64:
		if (bf->l_len <= 0) {
D
Dave Chinner 已提交
677
			error = -EINVAL;
678 679 680 681 682 683 684 685 686
			goto out_unlock;
		}
		break;
	default:
		bf->l_len = 0;
		break;
	}

	if (bf->l_start < 0 ||
687
	    bf->l_start > inode->i_sb->s_maxbytes ||
688
	    bf->l_start + bf->l_len < 0 ||
689
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
D
Dave Chinner 已提交
690
		error = -EINVAL;
691 692 693 694 695
		goto out_unlock;
	}

	switch (cmd) {
	case XFS_IOC_ZERO_RANGE:
696
		flags |= XFS_PREALLOC_SET;
697 698 699 700
		error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
		break;
	case XFS_IOC_RESVSP:
	case XFS_IOC_RESVSP64:
701
		flags |= XFS_PREALLOC_SET;
702 703 704 705 706 707 708 709 710 711 712
		error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len,
						XFS_BMAPI_PREALLOC);
		break;
	case XFS_IOC_UNRESVSP:
	case XFS_IOC_UNRESVSP64:
		error = xfs_free_file_space(ip, bf->l_start, bf->l_len);
		break;
	case XFS_IOC_ALLOCSP:
	case XFS_IOC_ALLOCSP64:
	case XFS_IOC_FREESP:
	case XFS_IOC_FREESP64:
713
		flags |= XFS_PREALLOC_CLEAR;
714 715 716 717 718 719 720 721 722 723
		if (bf->l_start > XFS_ISIZE(ip)) {
			error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
					bf->l_start - XFS_ISIZE(ip), 0);
			if (error)
				goto out_unlock;
		}

		iattr.ia_valid = ATTR_SIZE;
		iattr.ia_size = bf->l_start;

724
		error = xfs_vn_setattr_size(file_dentry(filp), &iattr);
725 726 727
		break;
	default:
		ASSERT(0);
D
Dave Chinner 已提交
728
		error = -EINVAL;
729 730 731 732 733
	}

	if (error)
		goto out_unlock;

734
	error = xfs_update_prealloc_flags(ip, flags);
735 736

out_unlock:
737
	xfs_iunlock(ip, iolock);
J
Jan Kara 已提交
738
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
739
	return error;
L
Linus Torvalds 已提交
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
}

STATIC int
xfs_ioc_bulkstat(
	xfs_mount_t		*mp,
	unsigned int		cmd,
	void			__user *arg)
{
	xfs_fsop_bulkreq_t	bulkreq;
	int			count;	/* # of records returned */
	xfs_ino_t		inlast;	/* last inode number */
	int			done;
	int			error;

	/* done = 1 if there are more stats to get and if bulkstat */
	/* should be called again (unused here, but used in dmapi) */

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (XFS_FORCED_SHUTDOWN(mp))
E
Eric Sandeen 已提交
761
		return -EIO;
L
Linus Torvalds 已提交
762 763

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
E
Eric Sandeen 已提交
764
		return -EFAULT;
L
Linus Torvalds 已提交
765 766

	if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
E
Eric Sandeen 已提交
767
		return -EFAULT;
L
Linus Torvalds 已提交
768 769

	if ((count = bulkreq.icount) <= 0)
E
Eric Sandeen 已提交
770
		return -EINVAL;
L
Linus Torvalds 已提交
771

772
	if (bulkreq.ubuffer == NULL)
E
Eric Sandeen 已提交
773
		return -EINVAL;
774

L
Linus Torvalds 已提交
775 776
	if (cmd == XFS_IOC_FSINUMBERS)
		error = xfs_inumbers(mp, &inlast, &count,
777
					bulkreq.ubuffer, xfs_inumbers_fmt);
L
Linus Torvalds 已提交
778
	else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
779 780
		error = xfs_bulkstat_one(mp, inlast, bulkreq.ubuffer,
					sizeof(xfs_bstat_t), NULL, &done);
781
	else	/* XFS_IOC_FSBULKSTAT */
782 783 784
		error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one,
				     sizeof(xfs_bstat_t), bulkreq.ubuffer,
				     &done);
L
Linus Torvalds 已提交
785 786

	if (error)
D
Dave Chinner 已提交
787
		return error;
L
Linus Torvalds 已提交
788 789 790 791

	if (bulkreq.ocount != NULL) {
		if (copy_to_user(bulkreq.lastip, &inlast,
						sizeof(xfs_ino_t)))
E
Eric Sandeen 已提交
792
			return -EFAULT;
L
Linus Torvalds 已提交
793 794

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
E
Eric Sandeen 已提交
795
			return -EFAULT;
L
Linus Torvalds 已提交
796 797 798 799 800 801 802 803 804 805
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry_v1(
	xfs_mount_t		*mp,
	void			__user *arg)
{
806
	xfs_fsop_geom_t         fsgeo;
L
Linus Torvalds 已提交
807 808
	int			error;

809
	error = xfs_fs_geometry(&mp->m_sb, &fsgeo, 3);
L
Linus Torvalds 已提交
810
	if (error)
D
Dave Chinner 已提交
811
		return error;
L
Linus Torvalds 已提交
812

813 814 815 816 817 818
	/*
	 * Caller should have passed an argument of type
	 * xfs_fsop_geom_v1_t.  This is a proper subset of the
	 * xfs_fsop_geom_t that xfs_fs_geometry() fills in.
	 */
	if (copy_to_user(arg, &fsgeo, sizeof(xfs_fsop_geom_v1_t)))
E
Eric Sandeen 已提交
819
		return -EFAULT;
L
Linus Torvalds 已提交
820 821 822 823 824 825 826 827 828 829 830
	return 0;
}

STATIC int
xfs_ioc_fsgeometry(
	xfs_mount_t		*mp,
	void			__user *arg)
{
	xfs_fsop_geom_t		fsgeo;
	int			error;

831
	error = xfs_fs_geometry(&mp->m_sb, &fsgeo, 4);
L
Linus Torvalds 已提交
832
	if (error)
D
Dave Chinner 已提交
833
		return error;
L
Linus Torvalds 已提交
834 835

	if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
E
Eric Sandeen 已提交
836
		return -EFAULT;
L
Linus Torvalds 已提交
837 838 839 840 841 842 843 844 845 846 847 848 849 850
	return 0;
}

/*
 * Linux extended inode flags interface.
 */

STATIC unsigned int
xfs_merge_ioc_xflags(
	unsigned int	flags,
	unsigned int	start)
{
	unsigned int	xflags = start;

851
	if (flags & FS_IMMUTABLE_FL)
852
		xflags |= FS_XFLAG_IMMUTABLE;
L
Linus Torvalds 已提交
853
	else
854
		xflags &= ~FS_XFLAG_IMMUTABLE;
855
	if (flags & FS_APPEND_FL)
856
		xflags |= FS_XFLAG_APPEND;
L
Linus Torvalds 已提交
857
	else
858
		xflags &= ~FS_XFLAG_APPEND;
859
	if (flags & FS_SYNC_FL)
860
		xflags |= FS_XFLAG_SYNC;
L
Linus Torvalds 已提交
861
	else
862
		xflags &= ~FS_XFLAG_SYNC;
863
	if (flags & FS_NOATIME_FL)
864
		xflags |= FS_XFLAG_NOATIME;
L
Linus Torvalds 已提交
865
	else
866
		xflags &= ~FS_XFLAG_NOATIME;
867
	if (flags & FS_NODUMP_FL)
868
		xflags |= FS_XFLAG_NODUMP;
L
Linus Torvalds 已提交
869
	else
870
		xflags &= ~FS_XFLAG_NODUMP;
L
Linus Torvalds 已提交
871 872 873 874 875 876

	return xflags;
}

STATIC unsigned int
xfs_di2lxflags(
877
	uint16_t	di_flags)
L
Linus Torvalds 已提交
878 879 880 881
{
	unsigned int	flags = 0;

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
882
		flags |= FS_IMMUTABLE_FL;
L
Linus Torvalds 已提交
883
	if (di_flags & XFS_DIFLAG_APPEND)
884
		flags |= FS_APPEND_FL;
L
Linus Torvalds 已提交
885
	if (di_flags & XFS_DIFLAG_SYNC)
886
		flags |= FS_SYNC_FL;
L
Linus Torvalds 已提交
887
	if (di_flags & XFS_DIFLAG_NOATIME)
888
		flags |= FS_NOATIME_FL;
L
Linus Torvalds 已提交
889
	if (di_flags & XFS_DIFLAG_NODUMP)
890
		flags |= FS_NODUMP_FL;
L
Linus Torvalds 已提交
891 892 893
	return flags;
}

894 895 896 897 898 899 900 901
STATIC int
xfs_ioc_fsgetxattr(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;

902 903
	memset(&fa, 0, sizeof(struct fsxattr));

904 905 906
	xfs_ilock(ip, XFS_ILOCK_SHARED);
	fa.fsx_xflags = xfs_ip2xflags(ip);
	fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
907 908
	fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
			ip->i_mount->m_sb.sb_blocklog;
909
	fa.fsx_projid = xfs_get_projid(ip);
910 911 912 913

	if (attr) {
		if (ip->i_afp) {
			if (ip->i_afp->if_flags & XFS_IFEXTENTS)
914
				fa.fsx_nextents = xfs_iext_count(ip->i_afp);
915 916 917 918 919 920
			else
				fa.fsx_nextents = ip->i_d.di_anextents;
		} else
			fa.fsx_nextents = 0;
	} else {
		if (ip->i_df.if_flags & XFS_IFEXTENTS)
921
			fa.fsx_nextents = xfs_iext_count(&ip->i_df);
922 923 924 925 926 927 928 929 930 931
		else
			fa.fsx_nextents = ip->i_d.di_nextents;
	}
	xfs_iunlock(ip, XFS_ILOCK_SHARED);

	if (copy_to_user(arg, &fa, sizeof(fa)))
		return -EFAULT;
	return 0;
}

932 933
STATIC uint16_t
xfs_flags2diflags(
934 935 936 937
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	/* can't set PREALLOC this way, just preserve it */
938 939 940
	uint16_t		di_flags =
		(ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);

941
	if (xflags & FS_XFLAG_IMMUTABLE)
942
		di_flags |= XFS_DIFLAG_IMMUTABLE;
943
	if (xflags & FS_XFLAG_APPEND)
944
		di_flags |= XFS_DIFLAG_APPEND;
945
	if (xflags & FS_XFLAG_SYNC)
946
		di_flags |= XFS_DIFLAG_SYNC;
947
	if (xflags & FS_XFLAG_NOATIME)
948
		di_flags |= XFS_DIFLAG_NOATIME;
949
	if (xflags & FS_XFLAG_NODUMP)
950
		di_flags |= XFS_DIFLAG_NODUMP;
951
	if (xflags & FS_XFLAG_NODEFRAG)
952
		di_flags |= XFS_DIFLAG_NODEFRAG;
953
	if (xflags & FS_XFLAG_FILESTREAM)
954
		di_flags |= XFS_DIFLAG_FILESTREAM;
D
Dave Chinner 已提交
955
	if (S_ISDIR(VFS_I(ip)->i_mode)) {
956
		if (xflags & FS_XFLAG_RTINHERIT)
957
			di_flags |= XFS_DIFLAG_RTINHERIT;
958
		if (xflags & FS_XFLAG_NOSYMLINKS)
959
			di_flags |= XFS_DIFLAG_NOSYMLINKS;
960
		if (xflags & FS_XFLAG_EXTSZINHERIT)
961
			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
962
		if (xflags & FS_XFLAG_PROJINHERIT)
963
			di_flags |= XFS_DIFLAG_PROJINHERIT;
D
Dave Chinner 已提交
964
	} else if (S_ISREG(VFS_I(ip)->i_mode)) {
965
		if (xflags & FS_XFLAG_REALTIME)
966
			di_flags |= XFS_DIFLAG_REALTIME;
967
		if (xflags & FS_XFLAG_EXTSIZE)
968 969
			di_flags |= XFS_DIFLAG_EXTSIZE;
	}
970

971 972 973 974 975 976 977 978 979 980
	return di_flags;
}

STATIC uint64_t
xfs_flags2diflags2(
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	uint64_t		di_flags2 =
		(ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
981 982 983

	if (xflags & FS_XFLAG_DAX)
		di_flags2 |= XFS_DIFLAG2_DAX;
984 985
	if (xflags & FS_XFLAG_COWEXTSIZE)
		di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
986

987
	return di_flags2;
988 989
}

990 991 992 993
STATIC void
xfs_diflags_to_linux(
	struct xfs_inode	*ip)
{
994
	struct inode		*inode = VFS_I(ip);
995 996
	unsigned int		xflags = xfs_ip2xflags(ip);

997
	if (xflags & FS_XFLAG_IMMUTABLE)
998 999 1000
		inode->i_flags |= S_IMMUTABLE;
	else
		inode->i_flags &= ~S_IMMUTABLE;
1001
	if (xflags & FS_XFLAG_APPEND)
1002 1003 1004
		inode->i_flags |= S_APPEND;
	else
		inode->i_flags &= ~S_APPEND;
1005
	if (xflags & FS_XFLAG_SYNC)
1006 1007 1008
		inode->i_flags |= S_SYNC;
	else
		inode->i_flags &= ~S_SYNC;
1009
	if (xflags & FS_XFLAG_NOATIME)
1010 1011 1012
		inode->i_flags |= S_NOATIME;
	else
		inode->i_flags &= ~S_NOATIME;
1013
#if 0	/* disabled until the flag switching races are sorted out */
1014 1015 1016 1017
	if (xflags & FS_XFLAG_DAX)
		inode->i_flags |= S_DAX;
	else
		inode->i_flags &= ~S_DAX;
1018
#endif
1019
}
1020

1021 1022 1023 1024 1025 1026 1027
static int
xfs_ioctl_setattr_xflags(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
1028
	uint64_t		di_flags2;
1029 1030 1031

	/* Can't change realtime flag if any extents are allocated. */
	if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
1032
	    XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & FS_XFLAG_REALTIME))
1033 1034 1035
		return -EINVAL;

	/* If realtime flag is set then must have realtime device */
1036
	if (fa->fsx_xflags & FS_XFLAG_REALTIME) {
1037 1038 1039 1040 1041
		if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 ||
		    (ip->i_d.di_extsize % mp->m_sb.sb_rextsize))
			return -EINVAL;
	}

1042
	/* Clear reflink if we are actually able to set the rt flag. */
1043
	if ((fa->fsx_xflags & FS_XFLAG_REALTIME) && xfs_is_reflink_inode(ip))
1044
		ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
1045

1046 1047 1048 1049
	/* Don't allow us to set DAX mode for a reflinked file for now. */
	if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip))
		return -EINVAL;

1050 1051 1052 1053 1054
	/*
	 * Can't modify an immutable/append-only file unless
	 * we have appropriate permission.
	 */
	if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) ||
1055
	     (fa->fsx_xflags & (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND))) &&
1056 1057 1058
	    !capable(CAP_LINUX_IMMUTABLE))
		return -EPERM;

1059 1060 1061 1062 1063 1064 1065 1066
	/* diflags2 only valid for v3 inodes. */
	di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
	if (di_flags2 && ip->i_d.di_version < 3)
		return -EINVAL;

	ip->i_d.di_flags = xfs_flags2diflags(ip, fa->fsx_xflags);
	ip->i_d.di_flags2 = di_flags2;

1067 1068 1069
	xfs_diflags_to_linux(ip);
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1070
	XFS_STATS_INC(mp, xs_ig_attrchg);
1071 1072 1073
	return 0;
}

1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
/*
 * If we are changing DAX flags, we have to ensure the file is clean and any
 * cached objects in the address space are invalidated and removed. This
 * requires us to lock out other IO and page faults similar to a truncate
 * operation. The locks need to be held until the transaction has been committed
 * so that the cache invalidation is atomic with respect to the DAX flag
 * manipulation.
 */
static int
xfs_ioctl_setattr_dax_invalidate(
	struct xfs_inode	*ip,
	struct fsxattr		*fa,
	int			*join_flags)
{
	struct inode		*inode = VFS_I(ip);
1089
	struct super_block	*sb = inode->i_sb;
1090 1091 1092 1093
	int			error;

	*join_flags = 0;

1094 1095
	/*
	 * It is only valid to set the DAX flag on regular files and
1096 1097
	 * directories on filesystems where the block size is equal to the page
	 * size. On directories it serves as an inherit hint.
1098
	 */
1099 1100 1101
	if (fa->fsx_xflags & FS_XFLAG_DAX) {
		if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
			return -EINVAL;
1102 1103
		if (!bdev_dax_supported(xfs_find_bdev_for_inode(VFS_I(ip)),
				sb->s_blocksize))
1104 1105
			return -EINVAL;
	}
1106

1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
	/* If the DAX state is not changing, we have nothing to do here. */
	if ((fa->fsx_xflags & FS_XFLAG_DAX) && IS_DAX(inode))
		return 0;
	if (!(fa->fsx_xflags & FS_XFLAG_DAX) && !IS_DAX(inode))
		return 0;

	/* lock, flush and invalidate mapping in preparation for flag change */
	xfs_ilock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL);
	error = filemap_write_and_wait(inode->i_mapping);
	if (error)
		goto out_unlock;
	error = invalidate_inode_pages2(inode->i_mapping);
	if (error)
		goto out_unlock;

	*join_flags = XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL;
1123
	return 0;
1124 1125 1126 1127 1128

out_unlock:
	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL);
	return error;

1129 1130
}

1131 1132 1133 1134 1135
/*
 * Set up the transaction structure for the setattr operation, checking that we
 * have permission to do so. On success, return a clean transaction and the
 * inode locked exclusively ready for further operation specific checks. On
 * failure, return an error without modifying or locking the inode.
1136 1137 1138 1139 1140 1141
 *
 * The inode might already be IO locked on call. If this is the case, it is
 * indicated in @join_flags and we take full responsibility for ensuring they
 * are unlocked from now on. Hence if we have an error here, we still have to
 * unlock them. Otherwise, once they are joined to the transaction, they will
 * be unlocked on commit/cancel.
1142 1143 1144
 */
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
1145 1146
	struct xfs_inode	*ip,
	int			join_flags)
1147 1148 1149
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
1150
	int			error = -EROFS;
1151 1152

	if (mp->m_flags & XFS_MOUNT_RDONLY)
1153 1154
		goto out_unlock;
	error = -EIO;
1155
	if (XFS_FORCED_SHUTDOWN(mp))
1156
		goto out_unlock;
1157

1158
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1159
	if (error)
1160
		return ERR_PTR(error);
1161 1162

	xfs_ilock(ip, XFS_ILOCK_EXCL);
1163 1164
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags);
	join_flags = 0;
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182

	/*
	 * CAP_FOWNER overrides the following restrictions:
	 *
	 * The user ID of the calling process must be equal to the file owner
	 * ID, except in cases where the CAP_FSETID capability is applicable.
	 */
	if (!inode_owner_or_capable(VFS_I(ip))) {
		error = -EPERM;
		goto out_cancel;
	}

	if (mp->m_flags & XFS_MOUNT_WSYNC)
		xfs_trans_set_sync(tp);

	return tp;

out_cancel:
1183
	xfs_trans_cancel(tp);
1184 1185 1186
out_unlock:
	if (join_flags)
		xfs_iunlock(ip, join_flags);
1187 1188 1189
	return ERR_PTR(error);
}

1190 1191 1192 1193
/*
 * extent size hint validation is somewhat cumbersome. Rules are:
 *
 * 1. extent size hint is only valid for directories and regular files
1194 1195
 * 2. FS_XFLAG_EXTSIZE is only valid for regular files
 * 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
1196 1197 1198 1199 1200 1201 1202
 * 4. can only be changed on regular files if no extents are allocated
 * 5. can be changed on directories at any time
 * 6. extsize hint of 0 turns off hints, clears inode flags.
 * 7. Extent size must be a multiple of the appropriate block size.
 * 8. for non-realtime files, the extent size hint must be limited
 *    to half the AG size to avoid alignment extending the extent beyond the
 *    limits of the AG.
D
Darrick J. Wong 已提交
1203 1204
 *
 * Please keep this function in sync with xfs_scrub_inode_extsize.
1205
 */
1206
static int
1207 1208 1209 1210 1211 1212
xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

D
Dave Chinner 已提交
1213
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode))
1214 1215
		return -EINVAL;

1216
	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
D
Dave Chinner 已提交
1217
	    !S_ISDIR(VFS_I(ip)->i_mode))
1218 1219
		return -EINVAL;

D
Dave Chinner 已提交
1220
	if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents &&
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
	    ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
		return -EINVAL;

	if (fa->fsx_extsize != 0) {
		xfs_extlen_t    size;
		xfs_fsblock_t   extsize_fsb;

		extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
		if (extsize_fsb > MAXEXTLEN)
			return -EINVAL;

		if (XFS_IS_REALTIME_INODE(ip) ||
1233
		    (fa->fsx_xflags & FS_XFLAG_REALTIME)) {
1234 1235 1236 1237 1238 1239 1240 1241 1242
			size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
		} else {
			size = mp->m_sb.sb_blocksize;
			if (extsize_fsb > mp->m_sb.sb_agblocks / 2)
				return -EINVAL;
		}

		if (fa->fsx_extsize % size)
			return -EINVAL;
1243
	} else
1244
		fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
1245

1246 1247 1248
	return 0;
}

1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
/*
 * CoW extent size hint validation rules are:
 *
 * 1. CoW extent size hint can only be set if reflink is enabled on the fs.
 *    The inode does not have to have any shared blocks, but it must be a v3.
 * 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
 *    for a directory, the hint is propagated to new files.
 * 3. Can be changed on files & directories at any time.
 * 4. CoW extsize hint of 0 turns off hints, clears inode flags.
 * 5. Extent size must be a multiple of the appropriate block size.
 * 6. The extent size hint must be limited to half the AG size to avoid
 *    alignment extending the extent beyond the limits of the AG.
D
Darrick J. Wong 已提交
1261 1262
 *
 * Please keep this function in sync with xfs_scrub_inode_cowextsize.
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
 */
static int
xfs_ioctl_setattr_check_cowextsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

	if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
		return 0;

	if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb) ||
	    ip->i_d.di_version != 3)
		return -EINVAL;

	if (!S_ISREG(VFS_I(ip)->i_mode) && !S_ISDIR(VFS_I(ip)->i_mode))
		return -EINVAL;

	if (fa->fsx_cowextsize != 0) {
		xfs_extlen_t    size;
		xfs_fsblock_t   cowextsize_fsb;

		cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
		if (cowextsize_fsb > MAXEXTLEN)
			return -EINVAL;

		size = mp->m_sb.sb_blocksize;
		if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
			return -EINVAL;

		if (fa->fsx_cowextsize % size)
			return -EINVAL;
	} else
		fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;

	return 0;
}

1301
static int
1302 1303 1304 1305 1306
xfs_ioctl_setattr_check_projid(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	/* Disallow 32bit project ids if projid32bit feature is not enabled. */
1307
	if (fa->fsx_projid > (uint16_t)-1 &&
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320
	    !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
		return -EINVAL;

	/*
	 * Project Quota ID state is only allowed to change from within the init
	 * namespace. Enforce that restriction only if we are trying to change
	 * the quota ID state. Everything else is allowed in user namespaces.
	 */
	if (current_user_ns() == &init_user_ns)
		return 0;

	if (xfs_get_projid(ip) != fa->fsx_projid)
		return -EINVAL;
1321
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1322 1323 1324 1325 1326
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1327 1328 1329 1330

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
1331
	struct fsxattr		*fa)
1332 1333 1334
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
C
Christoph Hellwig 已提交
1335
	struct xfs_dquot	*udqp = NULL;
1336
	struct xfs_dquot	*pdqp = NULL;
1337 1338
	struct xfs_dquot	*olddquot = NULL;
	int			code;
1339
	int			join_flags = 0;
1340

C
Christoph Hellwig 已提交
1341
	trace_xfs_ioctl_setattr(ip);
1342

1343 1344 1345
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1346

1347 1348 1349 1350 1351 1352 1353 1354
	/*
	 * If disk quotas is on, we make sure that the dquots do exist on disk,
	 * before we start any other transactions. Trying to do this later
	 * is messy. We don't care to take a readlock to look at the ids
	 * in inode here, because we can't hold it across the trans_reserve.
	 * If the IDs do change before we take the ilock, we're covered
	 * because the i_*dquot fields will get updated anyway.
	 */
1355
	if (XFS_IS_QUOTA_ON(mp)) {
C
Christoph Hellwig 已提交
1356
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1357
					 ip->i_d.di_gid, fa->fsx_projid,
1358
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1359 1360 1361 1362
		if (code)
			return code;
	}

1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
	/*
	 * Changing DAX config may require inode locking for mapping
	 * invalidation. These need to be held all the way to transaction commit
	 * or cancel time, so need to be passed through to
	 * xfs_ioctl_setattr_get_trans() so it can apply them to the join call
	 * appropriately.
	 */
	code = xfs_ioctl_setattr_dax_invalidate(ip, fa, &join_flags);
	if (code)
		goto error_free_dquots;

	tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
1375 1376 1377
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1378 1379 1380
	}


1381 1382 1383 1384 1385
	if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
	    xfs_get_projid(ip) != fa->fsx_projid) {
		code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp,
				capable(CAP_FOWNER) ?  XFS_QMOPT_FORCE_RES : 0);
		if (code)	/* out of quota */
1386
			goto error_trans_cancel;
1387 1388
	}

1389 1390 1391
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1392

1393 1394 1395 1396
	code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
	if (code)
		goto error_trans_cancel;

1397 1398
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1399
		goto error_trans_cancel;
1400 1401

	/*
1402 1403 1404 1405 1406
	 * Change file ownership.  Must be the owner or privileged.  CAP_FSETID
	 * overrides the following restrictions:
	 *
	 * The set-user-ID and set-group-ID bits of a file will be cleared upon
	 * successful return from chown()
1407 1408
	 */

D
Dave Chinner 已提交
1409
	if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
1410
	    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
D
Dave Chinner 已提交
1411
		VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
1412

1413 1414 1415 1416 1417 1418 1419 1420
	/* Change the ownerships and register project quota modifications */
	if (xfs_get_projid(ip) != fa->fsx_projid) {
		if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
			olddquot = xfs_qm_vop_chown(tp, ip,
						&ip->i_pdquot, pdqp);
		}
		ASSERT(ip->i_d.di_version > 1);
		xfs_set_projid(ip, fa->fsx_projid);
1421
	}
1422

1423 1424 1425 1426 1427
	/*
	 * Only set the extent size hint if we've already determined that the
	 * extent size hint should be set on the inode. If no extent size flags
	 * are set on the inode then unconditionally clear the extent size hint.
	 */
1428 1429 1430 1431
	if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
		ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
	else
		ip->i_d.di_extsize = 0;
1432 1433 1434 1435 1436 1437
	if (ip->i_d.di_version == 3 &&
	    (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
		ip->i_d.di_cowextsize = fa->fsx_cowextsize >>
				mp->m_sb.sb_blocklog;
	else
		ip->i_d.di_cowextsize = 0;
1438

1439
	code = xfs_trans_commit(tp);
1440 1441 1442 1443

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1444 1445
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1446
	xfs_qm_dqrele(pdqp);
1447

C
Christoph Hellwig 已提交
1448
	return code;
1449

1450
error_trans_cancel:
1451
	xfs_trans_cancel(tp);
1452
error_free_dquots:
C
Christoph Hellwig 已提交
1453
	xfs_qm_dqrele(udqp);
1454
	xfs_qm_dqrele(pdqp);
1455 1456 1457
	return code;
}

L
Linus Torvalds 已提交
1458
STATIC int
L
Lachlan McIlroy 已提交
1459
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1460 1461 1462 1463 1464
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1465
	int error;
L
Lachlan McIlroy 已提交
1466 1467 1468

	if (copy_from_user(&fa, arg, sizeof(fa)))
		return -EFAULT;
L
Linus Torvalds 已提交
1469

J
Jan Kara 已提交
1470 1471 1472
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1473
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1474
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1475
	return error;
L
Lachlan McIlroy 已提交
1476
}
L
Linus Torvalds 已提交
1477

L
Lachlan McIlroy 已提交
1478 1479 1480 1481 1482 1483
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1484

L
Lachlan McIlroy 已提交
1485 1486 1487 1488 1489
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1490

L
Lachlan McIlroy 已提交
1491 1492
STATIC int
xfs_ioc_setxflags(
1493
	struct xfs_inode	*ip,
L
Lachlan McIlroy 已提交
1494 1495 1496
	struct file		*filp,
	void			__user *arg)
{
1497
	struct xfs_trans	*tp;
1498
	struct fsxattr		fa;
L
Lachlan McIlroy 已提交
1499
	unsigned int		flags;
1500
	int			join_flags = 0;
1501
	int			error;
L
Linus Torvalds 已提交
1502

L
Lachlan McIlroy 已提交
1503 1504
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1505

L
Lachlan McIlroy 已提交
1506 1507 1508 1509
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1510

1511
	fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
L
Linus Torvalds 已提交
1512

J
Jan Kara 已提交
1513 1514 1515
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1516

1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528
	/*
	 * Changing DAX config may require inode locking for mapping
	 * invalidation. These need to be held all the way to transaction commit
	 * or cancel time, so need to be passed through to
	 * xfs_ioctl_setattr_get_trans() so it can apply them to the join call
	 * appropriately.
	 */
	error = xfs_ioctl_setattr_dax_invalidate(ip, &fa, &join_flags);
	if (error)
		goto out_drop_write;

	tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
1529 1530 1531 1532 1533 1534 1535
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1536
		xfs_trans_cancel(tp);
1537 1538 1539
		goto out_drop_write;
	}

1540
	error = xfs_trans_commit(tp);
1541
out_drop_write:
J
Jan Kara 已提交
1542
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1543
	return error;
L
Linus Torvalds 已提交
1544 1545
}

1546 1547 1548 1549 1550
static bool
xfs_getbmap_format(
	struct kgetbmap		*p,
	struct getbmapx __user	*u,
	size_t			recsize)
1551
{
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
	if (put_user(p->bmv_offset, &u->bmv_offset) ||
	    put_user(p->bmv_block, &u->bmv_block) ||
	    put_user(p->bmv_length, &u->bmv_length) ||
	    put_user(0, &u->bmv_count) ||
	    put_user(0, &u->bmv_entries))
		return false;
	if (recsize < sizeof(struct getbmapx))
		return true;
	if (put_user(0, &u->bmv_iflags) ||
	    put_user(p->bmv_oflags, &u->bmv_oflags) ||
	    put_user(0, &u->bmv_unused1) ||
	    put_user(0, &u->bmv_unused2))
		return false;
	return true;
1566 1567
}

L
Linus Torvalds 已提交
1568 1569
STATIC int
xfs_ioc_getbmap(
1570
	struct file		*file,
L
Linus Torvalds 已提交
1571 1572 1573
	unsigned int		cmd,
	void			__user *arg)
{
1574
	struct getbmapx		bmx = { 0 };
1575 1576 1577
	struct kgetbmap		*buf;
	size_t			recsize;
	int			error, i;
L
Linus Torvalds 已提交
1578

1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592
	switch (cmd) {
	case XFS_IOC_GETBMAPA:
		bmx.bmv_iflags = BMV_IF_ATTRFORK;
		/*FALLTHRU*/
	case XFS_IOC_GETBMAP:
		if (file->f_mode & FMODE_NOCMTIME)
			bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
		/* struct getbmap is a strict subset of struct getbmapx. */
		recsize = sizeof(struct getbmap);
		break;
	case XFS_IOC_GETBMAPX:
		recsize = sizeof(struct getbmapx);
		break;
	default:
E
Eric Sandeen 已提交
1593
		return -EINVAL;
1594
	}
L
Linus Torvalds 已提交
1595

1596
	if (copy_from_user(&bmx, arg, recsize))
E
Eric Sandeen 已提交
1597
		return -EFAULT;
L
Linus Torvalds 已提交
1598 1599

	if (bmx.bmv_count < 2)
E
Eric Sandeen 已提交
1600
		return -EINVAL;
1601 1602
	if (bmx.bmv_count > ULONG_MAX / recsize)
		return -ENOMEM;
L
Linus Torvalds 已提交
1603

1604 1605 1606
	buf = kmem_zalloc_large(bmx.bmv_count * sizeof(*buf), 0);
	if (!buf)
		return -ENOMEM;
L
Linus Torvalds 已提交
1607

1608
	error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf);
L
Linus Torvalds 已提交
1609
	if (error)
1610
		goto out_free_buf;
L
Linus Torvalds 已提交
1611

1612 1613 1614 1615 1616 1617 1618 1619 1620 1621
	error = -EFAULT;
	if (copy_to_user(arg, &bmx, recsize))
		goto out_free_buf;
	arg += recsize;

	for (i = 0; i < bmx.bmv_entries; i++) {
		if (!xfs_getbmap_format(buf + i, arg, recsize))
			goto out_free_buf;
		arg += recsize;
	}
L
Linus Torvalds 已提交
1622

1623 1624 1625
	error = 0;
out_free_buf:
	kmem_free(buf);
L
Linus Torvalds 已提交
1626 1627
	return 0;
}
L
Lachlan McIlroy 已提交
1628

1629 1630
struct getfsmap_info {
	struct xfs_mount	*mp;
1631 1632
	struct fsmap_head __user *data;
	unsigned int		idx;
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
	__u32			last_flags;
};

STATIC int
xfs_getfsmap_format(struct xfs_fsmap *xfm, void *priv)
{
	struct getfsmap_info	*info = priv;
	struct fsmap		fm;

	trace_xfs_getfsmap_mapping(info->mp, xfm);

	info->last_flags = xfm->fmr_flags;
	xfs_fsmap_from_internal(&fm, xfm);
1646 1647
	if (copy_to_user(&info->data->fmh_recs[info->idx++], &fm,
			sizeof(struct fsmap)))
1648 1649 1650 1651 1652 1653 1654 1655
		return -EFAULT;

	return 0;
}

STATIC int
xfs_ioc_getfsmap(
	struct xfs_inode	*ip,
1656
	struct fsmap_head	__user *arg)
1657
{
1658
	struct getfsmap_info	info = { NULL };
1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681
	struct xfs_fsmap_head	xhead = {0};
	struct fsmap_head	head;
	bool			aborted = false;
	int			error;

	if (copy_from_user(&head, arg, sizeof(struct fsmap_head)))
		return -EFAULT;
	if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) ||
	    memchr_inv(head.fmh_keys[0].fmr_reserved, 0,
		       sizeof(head.fmh_keys[0].fmr_reserved)) ||
	    memchr_inv(head.fmh_keys[1].fmr_reserved, 0,
		       sizeof(head.fmh_keys[1].fmr_reserved)))
		return -EINVAL;

	xhead.fmh_iflags = head.fmh_iflags;
	xhead.fmh_count = head.fmh_count;
	xfs_fsmap_to_internal(&xhead.fmh_keys[0], &head.fmh_keys[0]);
	xfs_fsmap_to_internal(&xhead.fmh_keys[1], &head.fmh_keys[1]);

	trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]);
	trace_xfs_getfsmap_high_key(ip->i_mount, &xhead.fmh_keys[1]);

	info.mp = ip->i_mount;
1682
	info.data = arg;
1683 1684 1685 1686 1687 1688 1689 1690
	error = xfs_getfsmap(ip->i_mount, &xhead, xfs_getfsmap_format, &info);
	if (error == XFS_BTREE_QUERY_RANGE_ABORT) {
		error = 0;
		aborted = true;
	} else if (error)
		return error;

	/* If we didn't abort, set the "last" flag in the last fmx */
1691
	if (!aborted && info.idx) {
1692
		info.last_flags |= FMR_OF_LAST;
1693 1694
		if (copy_to_user(&info.data->fmh_recs[info.idx - 1].fmr_flags,
				&info.last_flags, sizeof(info.last_flags)))
1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706
			return -EFAULT;
	}

	/* copy back header */
	head.fmh_entries = xhead.fmh_entries;
	head.fmh_oflags = xhead.fmh_oflags;
	if (copy_to_user(arg, &head, sizeof(struct fsmap_head)))
		return -EFAULT;

	return 0;
}

1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730
STATIC int
xfs_ioc_scrub_metadata(
	struct xfs_inode		*ip,
	void				__user *arg)
{
	struct xfs_scrub_metadata	scrub;
	int				error;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (copy_from_user(&scrub, arg, sizeof(scrub)))
		return -EFAULT;

	error = xfs_scrub_metadata(ip, &scrub);
	if (error)
		return error;

	if (copy_to_user(arg, &scrub, sizeof(scrub)))
		return -EFAULT;

	return 0;
}

D
Dave Chinner 已提交
1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741
int
xfs_ioc_swapext(
	xfs_swapext_t	*sxp)
{
	xfs_inode_t     *ip, *tip;
	struct fd	f, tmp;
	int		error = 0;

	/* Pull information for the target fd */
	f = fdget((int)sxp->sx_fdtarget);
	if (!f.file) {
D
Dave Chinner 已提交
1742
		error = -EINVAL;
D
Dave Chinner 已提交
1743 1744 1745 1746 1747 1748
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1749
		error = -EBADF;
D
Dave Chinner 已提交
1750 1751 1752 1753 1754
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1755
		error = -EINVAL;
D
Dave Chinner 已提交
1756 1757 1758 1759 1760 1761
		goto out_put_file;
	}

	if (!(tmp.file->f_mode & FMODE_WRITE) ||
	    !(tmp.file->f_mode & FMODE_READ) ||
	    (tmp.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1762
		error = -EBADF;
D
Dave Chinner 已提交
1763 1764 1765 1766 1767
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1768
		error = -EINVAL;
D
Dave Chinner 已提交
1769 1770 1771
		goto out_put_tmp_file;
	}

1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782
	/*
	 * We need to ensure that the fds passed in point to XFS inodes
	 * before we cast and access them as XFS structures as we have no
	 * control over what the user passes us here.
	 */
	if (f.file->f_op != &xfs_file_operations ||
	    tmp.file->f_op != &xfs_file_operations) {
		error = -EINVAL;
		goto out_put_tmp_file;
	}

D
Dave Chinner 已提交
1783 1784 1785 1786
	ip = XFS_I(file_inode(f.file));
	tip = XFS_I(file_inode(tmp.file));

	if (ip->i_mount != tip->i_mount) {
D
Dave Chinner 已提交
1787
		error = -EINVAL;
D
Dave Chinner 已提交
1788 1789 1790 1791
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1792
		error = -EINVAL;
D
Dave Chinner 已提交
1793 1794 1795 1796
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1797
		error = -EIO;
D
Dave Chinner 已提交
1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
		goto out_put_tmp_file;
	}

	error = xfs_swap_extents(ip, tip, sxp);

 out_put_tmp_file:
	fdput(tmp);
 out_put_file:
	fdput(f);
 out:
	return error;
}

1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892
static int
xfs_ioc_getlabel(
	struct xfs_mount	*mp,
	char			__user *user_label)
{
	struct xfs_sb		*sbp = &mp->m_sb;
	char			label[XFSLABEL_MAX + 1];

	/* Paranoia */
	BUILD_BUG_ON(sizeof(sbp->sb_fname) > FSLABEL_MAX);

	spin_lock(&mp->m_sb_lock);
	strncpy(label, sbp->sb_fname, sizeof(sbp->sb_fname));
	spin_unlock(&mp->m_sb_lock);

	/* xfs on-disk label is 12 chars, be sure we send a null to user */
	label[XFSLABEL_MAX] = '\0';
	if (copy_to_user(user_label, label, sizeof(sbp->sb_fname)))
		return -EFAULT;
	return 0;
}

static int
xfs_ioc_setlabel(
	struct file		*filp,
	struct xfs_mount	*mp,
	char			__user *newlabel)
{
	struct xfs_sb		*sbp = &mp->m_sb;
	char			label[XFSLABEL_MAX + 1];
	size_t			len;
	int			error;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	/*
	 * The generic ioctl allows up to FSLABEL_MAX chars, but XFS is much
	 * smaller, at 12 bytes.  We copy one more to be sure we find the
	 * (required) NULL character to test the incoming label length.
	 * NB: The on disk label doesn't need to be null terminated.
	 */
	if (copy_from_user(label, newlabel, XFSLABEL_MAX + 1))
		return -EFAULT;
	len = strnlen(label, XFSLABEL_MAX + 1);
	if (len > sizeof(sbp->sb_fname))
		return -EINVAL;

	error = mnt_want_write_file(filp);
	if (error)
		return error;

	spin_lock(&mp->m_sb_lock);
	memset(sbp->sb_fname, 0, sizeof(sbp->sb_fname));
	strncpy(sbp->sb_fname, label, sizeof(sbp->sb_fname));
	spin_unlock(&mp->m_sb_lock);

	/*
	 * Now we do several things to satisfy userspace.
	 * In addition to normal logging of the primary superblock, we also
	 * immediately write these changes to sector zero for the primary, then
	 * update all backup supers (as xfs_db does for a label change), then
	 * invalidate the block device page cache.  This is so that any prior
	 * buffered reads from userspace (i.e. from blkid) are invalidated,
	 * and userspace will see the newly-written label.
	 */
	error = xfs_sync_sb_buf(mp);
	if (error)
		goto out;
	/*
	 * growfs also updates backup supers so lock against that.
	 */
	mutex_lock(&mp->m_growlock);
	error = xfs_update_secondary_sbs(mp);
	mutex_unlock(&mp->m_growlock);

	invalidate_bdev(mp->m_ddev_targp->bt_bdev);

out:
	mnt_drop_write_file(filp);
	return error;
}

1893 1894 1895 1896 1897 1898 1899 1900
/*
 * Note: some of the ioctl's return positive numbers as a
 * byte count indicating success, such as readlink_by_handle.
 * So we don't "sign flip" like most other routines.  This means
 * true errors need to be returned as a negative value.
 */
long
xfs_file_ioctl(
L
Lachlan McIlroy 已提交
1901 1902
	struct file		*filp,
	unsigned int		cmd,
1903
	unsigned long		p)
L
Lachlan McIlroy 已提交
1904
{
A
Al Viro 已提交
1905
	struct inode		*inode = file_inode(filp);
1906 1907 1908
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	void			__user *arg = (void __user *)p;
L
Lachlan McIlroy 已提交
1909 1910
	int			error;

C
Christoph Hellwig 已提交
1911
	trace_xfs_file_ioctl(ip);
1912 1913

	switch (cmd) {
C
Christoph Hellwig 已提交
1914 1915
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
1916 1917 1918 1919
	case FS_IOC_GETFSLABEL:
		return xfs_ioc_getlabel(mp, arg);
	case FS_IOC_SETFSLABEL:
		return xfs_ioc_setlabel(filp, mp, arg);
L
Lachlan McIlroy 已提交
1920 1921 1922 1923 1924 1925 1926
	case XFS_IOC_ALLOCSP:
	case XFS_IOC_FREESP:
	case XFS_IOC_RESVSP:
	case XFS_IOC_UNRESVSP:
	case XFS_IOC_ALLOCSP64:
	case XFS_IOC_FREESP64:
	case XFS_IOC_RESVSP64:
D
Dave Chinner 已提交
1927 1928
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1929
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1930

1931
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1932
			return -EFAULT;
1933
		return xfs_ioc_space(filp, cmd, &bf);
1934
	}
L
Lachlan McIlroy 已提交
1935 1936 1937 1938 1939 1940
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1941
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1942 1943 1944
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

		if (copy_to_user(arg, &da, sizeof(da)))
E
Eric Sandeen 已提交
1945
			return -EFAULT;
L
Lachlan McIlroy 已提交
1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966
		return 0;
	}

	case XFS_IOC_FSBULKSTAT_SINGLE:
	case XFS_IOC_FSBULKSTAT:
	case XFS_IOC_FSINUMBERS:
		return xfs_ioc_bulkstat(mp, cmd, arg);

	case XFS_IOC_FSGEOMETRY_V1:
		return xfs_ioc_fsgeometry_v1(mp, arg);

	case XFS_IOC_FSGEOMETRY:
		return xfs_ioc_fsgeometry(mp, arg);

	case XFS_IOC_GETVERSION:
		return put_user(inode->i_generation, (int __user *)arg);

	case XFS_IOC_FSGETXATTR:
		return xfs_ioc_fsgetxattr(ip, 0, arg);
	case XFS_IOC_FSGETXATTRA:
		return xfs_ioc_fsgetxattr(ip, 1, arg);
L
Lachlan McIlroy 已提交
1967 1968
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(ip, filp, arg);
L
Lachlan McIlroy 已提交
1969
	case XFS_IOC_GETXFLAGS:
L
Lachlan McIlroy 已提交
1970
		return xfs_ioc_getxflags(ip, arg);
L
Lachlan McIlroy 已提交
1971
	case XFS_IOC_SETXFLAGS:
L
Lachlan McIlroy 已提交
1972
		return xfs_ioc_setxflags(ip, filp, arg);
L
Lachlan McIlroy 已提交
1973 1974 1975 1976 1977

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

		if (copy_from_user(&dmi, arg, sizeof(dmi)))
E
Eric Sandeen 已提交
1978
			return -EFAULT;
L
Lachlan McIlroy 已提交
1979

J
Jan Kara 已提交
1980 1981 1982 1983
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1984 1985
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1986
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1987
		return error;
L
Lachlan McIlroy 已提交
1988 1989 1990 1991 1992
	}

	case XFS_IOC_GETBMAP:
	case XFS_IOC_GETBMAPA:
	case XFS_IOC_GETBMAPX:
1993
		return xfs_ioc_getbmap(filp, cmd, arg);
L
Lachlan McIlroy 已提交
1994

1995 1996 1997
	case FS_IOC_GETFSMAP:
		return xfs_ioc_getfsmap(ip, arg);

1998 1999 2000
	case XFS_IOC_SCRUB_METADATA:
		return xfs_ioc_scrub_metadata(ip, arg);

L
Lachlan McIlroy 已提交
2001 2002
	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
2003 2004
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
2005

2006
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
2007
			return -EFAULT;
2008 2009 2010 2011
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
2012

2013
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
2014
			return -EFAULT;
2015
		return xfs_open_by_handle(filp, &hreq);
2016
	}
L
Lachlan McIlroy 已提交
2017
	case XFS_IOC_FSSETDM_BY_HANDLE:
2018
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
2019

2020 2021
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
2022

2023
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
2024
			return -EFAULT;
2025
		return xfs_readlink_by_handle(filp, &hreq);
2026
	}
L
Lachlan McIlroy 已提交
2027
	case XFS_IOC_ATTRLIST_BY_HANDLE:
2028
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
2029 2030

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
2031
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
2032 2033

	case XFS_IOC_SWAPEXT: {
2034 2035 2036
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
2037
			return -EFAULT;
J
Jan Kara 已提交
2038 2039 2040
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
2041
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
2042
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2043
		return error;
L
Lachlan McIlroy 已提交
2044 2045 2046 2047 2048 2049 2050
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
2051
			return error;
L
Lachlan McIlroy 已提交
2052 2053

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
2054
			return -EFAULT;
L
Lachlan McIlroy 已提交
2055 2056 2057 2058 2059
		return 0;
	}

	case XFS_IOC_SET_RESBLKS: {
		xfs_fsop_resblks_t inout;
2060
		uint64_t	   in;
L
Lachlan McIlroy 已提交
2061 2062 2063 2064

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

E
Eric Sandeen 已提交
2065
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
2066
			return -EROFS;
E
Eric Sandeen 已提交
2067

L
Lachlan McIlroy 已提交
2068
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
2069
			return -EFAULT;
L
Lachlan McIlroy 已提交
2070

J
Jan Kara 已提交
2071 2072 2073 2074
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
2075 2076 2077
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
2078
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
2079
		if (error)
D
Dave Chinner 已提交
2080
			return error;
L
Lachlan McIlroy 已提交
2081 2082

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
2083
			return -EFAULT;
L
Lachlan McIlroy 已提交
2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094
		return 0;
	}

	case XFS_IOC_GET_RESBLKS: {
		xfs_fsop_resblks_t out;

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

		error = xfs_reserve_blocks(mp, NULL, &out);
		if (error)
D
Dave Chinner 已提交
2095
			return error;
L
Lachlan McIlroy 已提交
2096 2097

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
2098
			return -EFAULT;
L
Lachlan McIlroy 已提交
2099 2100 2101 2102 2103 2104 2105 2106

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

		if (copy_from_user(&in, arg, sizeof(in)))
E
Eric Sandeen 已提交
2107
			return -EFAULT;
L
Lachlan McIlroy 已提交
2108

J
Jan Kara 已提交
2109 2110 2111
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
2112
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
2113
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2114
		return error;
L
Lachlan McIlroy 已提交
2115 2116 2117 2118 2119 2120
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

		if (copy_from_user(&in, arg, sizeof(in)))
E
Eric Sandeen 已提交
2121
			return -EFAULT;
L
Lachlan McIlroy 已提交
2122

J
Jan Kara 已提交
2123 2124 2125
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
2126
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
2127
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2128
		return error;
L
Lachlan McIlroy 已提交
2129 2130 2131 2132 2133 2134
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

		if (copy_from_user(&in, arg, sizeof(in)))
E
Eric Sandeen 已提交
2135
			return -EFAULT;
L
Lachlan McIlroy 已提交
2136

J
Jan Kara 已提交
2137 2138 2139
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
2140
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
2141
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2142
		return error;
L
Lachlan McIlroy 已提交
2143 2144 2145
	}

	case XFS_IOC_GOINGDOWN: {
2146
		uint32_t in;
L
Lachlan McIlroy 已提交
2147 2148 2149 2150

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

2151
		if (get_user(in, (uint32_t __user *)arg))
E
Eric Sandeen 已提交
2152
			return -EFAULT;
L
Lachlan McIlroy 已提交
2153

D
Dave Chinner 已提交
2154
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
2155 2156 2157 2158 2159 2160 2161 2162 2163
	}

	case XFS_IOC_ERROR_INJECTION: {
		xfs_error_injection_t in;

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

		if (copy_from_user(&in, arg, sizeof(in)))
E
Eric Sandeen 已提交
2164
			return -EFAULT;
L
Lachlan McIlroy 已提交
2165

2166
		return xfs_errortag_add(mp, in.errtag);
L
Lachlan McIlroy 已提交
2167 2168 2169 2170 2171 2172
	}

	case XFS_IOC_ERROR_CLEARALL:
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

2173
		return xfs_errortag_clearall(mp);
L
Lachlan McIlroy 已提交
2174

2175
	case XFS_IOC_FREE_EOFBLOCKS: {
2176 2177
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
2178

2179 2180 2181 2182
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
2183
			return -EROFS;
2184

2185
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
2186
			return -EFAULT;
2187

2188 2189
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
2190
			return error;
2191

D
Dave Chinner 已提交
2192
		return xfs_icache_free_eofblocks(mp, &keofb);
2193 2194
	}

L
Lachlan McIlroy 已提交
2195 2196 2197 2198
	default:
		return -ENOTTY;
	}
}