xfs_ioctl.c 49.7 KB
Newer Older
D
Dave Chinner 已提交
1
// SPDX-License-Identifier: GPL-2.0
L
Linus Torvalds 已提交
2
/*
3 4
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
L
Linus Torvalds 已提交
5 6 7
 */
#include "xfs.h"
#include "xfs_fs.h"
8
#include "xfs_shared.h"
9 10 11
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
L
Linus Torvalds 已提交
12 13 14 15
#include "xfs_mount.h"
#include "xfs_inode.h"
#include "xfs_rtalloc.h"
#include "xfs_itable.h"
16
#include "xfs_error.h"
L
Linus Torvalds 已提交
17
#include "xfs_attr.h"
18
#include "xfs_bmap.h"
D
Dave Chinner 已提交
19
#include "xfs_bmap_util.h"
L
Linus Torvalds 已提交
20
#include "xfs_fsops.h"
C
Christoph Hellwig 已提交
21
#include "xfs_discard.h"
22
#include "xfs_quota.h"
23
#include "xfs_export.h"
C
Christoph Hellwig 已提交
24
#include "xfs_trace.h"
25
#include "xfs_icache.h"
26
#include "xfs_trans.h"
27
#include "xfs_acl.h"
28 29 30
#include "xfs_btree.h"
#include <linux/fsmap.h>
#include "xfs_fsmap.h"
31
#include "scrub/xfs_scrub.h"
32
#include "xfs_sb.h"
33
#include "xfs_ag.h"
34
#include "xfs_health.h"
L
Linus Torvalds 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

#include <linux/mount.h>
#include <linux/namei.h>

/*
 * 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
 */
50
int
L
Linus Torvalds 已提交
51 52
xfs_find_handle(
	unsigned int		cmd,
53
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
54 55 56 57
{
	int			hsize;
	xfs_handle_t		handle;
	struct inode		*inode;
58
	struct fd		f = {NULL};
C
Christoph Hellwig 已提交
59
	struct path		path;
60
	int			error;
C
Christoph Hellwig 已提交
61
	struct xfs_inode	*ip;
L
Linus Torvalds 已提交
62

C
Christoph Hellwig 已提交
63
	if (cmd == XFS_IOC_FD_TO_HANDLE) {
64 65
		f = fdget(hreq->fd);
		if (!f.file)
C
Christoph Hellwig 已提交
66
			return -EBADF;
A
Al Viro 已提交
67
		inode = file_inode(f.file);
C
Christoph Hellwig 已提交
68 69 70 71
	} else {
		error = user_lpath((const char __user *)hreq->path, &path);
		if (error)
			return error;
72
		inode = d_inode(path.dentry);
L
Linus Torvalds 已提交
73
	}
C
Christoph Hellwig 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
	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 已提交
100 101 102
		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
					sizeof(handle.ha_fid.fid_len);
		handle.ha_fid.fid_pad = 0;
103
		handle.ha_fid.fid_gen = inode->i_generation;
C
Christoph Hellwig 已提交
104
		handle.ha_fid.fid_ino = ip->i_ino;
C
Christoph Hellwig 已提交
105
		hsize = sizeof(xfs_handle_t);
L
Linus Torvalds 已提交
106 107
	}

C
Christoph Hellwig 已提交
108
	error = -EFAULT;
109
	if (copy_to_user(hreq->ohandle, &handle, hsize) ||
C
Christoph Hellwig 已提交
110 111
	    copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
		goto out_put;
L
Linus Torvalds 已提交
112

C
Christoph Hellwig 已提交
113 114 115 116
	error = 0;

 out_put:
	if (cmd == XFS_IOC_FD_TO_HANDLE)
117
		fdput(f);
C
Christoph Hellwig 已提交
118 119 120
	else
		path_put(&path);
	return error;
L
Linus Torvalds 已提交
121 122 123
}

/*
124 125
 * No need to do permission checks on the various pathname components
 * as the handle operations are privileged.
L
Linus Torvalds 已提交
126 127
 */
STATIC int
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
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 已提交
143 144
{
	xfs_handle_t		handle;
145
	struct xfs_fid64	fid;
L
Linus Torvalds 已提交
146 147 148 149

	/*
	 * Only allow handle opens under a directory.
	 */
A
Al Viro 已提交
150
	if (!S_ISDIR(file_inode(parfilp)->i_mode))
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
		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 已提交
169

170 171 172 173 174 175
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 已提交
176 177
}

178
int
L
Linus Torvalds 已提交
179 180
xfs_open_by_handle(
	struct file		*parfilp,
181
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
182
{
183
	const struct cred	*cred = current_cred();
L
Linus Torvalds 已提交
184
	int			error;
185
	int			fd;
L
Linus Torvalds 已提交
186 187 188 189
	int			permflag;
	struct file		*filp;
	struct inode		*inode;
	struct dentry		*dentry;
190
	fmode_t			fmode;
191
	struct path		path;
L
Linus Torvalds 已提交
192 193

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

196 197 198
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
199
	inode = d_inode(dentry);
L
Linus Torvalds 已提交
200 201 202

	/* Restrict xfs_open_by_handle to directories & regular files. */
	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
E
Eric Sandeen 已提交
203
		error = -EPERM;
204
		goto out_dput;
L
Linus Torvalds 已提交
205 206 207
	}

#if BITS_PER_LONG != 32
208
	hreq->oflags |= O_LARGEFILE;
L
Linus Torvalds 已提交
209
#endif
210

211
	permflag = hreq->oflags;
212
	fmode = OPEN_FMODE(permflag);
L
Linus Torvalds 已提交
213
	if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
214
	    (fmode & FMODE_WRITE) && IS_APPEND(inode)) {
E
Eric Sandeen 已提交
215
		error = -EPERM;
216
		goto out_dput;
L
Linus Torvalds 已提交
217 218
	}

219
	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
E
Eryu Guan 已提交
220
		error = -EPERM;
221
		goto out_dput;
L
Linus Torvalds 已提交
222 223 224
	}

	/* Can't write directories. */
225
	if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {
E
Eric Sandeen 已提交
226
		error = -EISDIR;
227
		goto out_dput;
L
Linus Torvalds 已提交
228 229
	}

230
	fd = get_unused_fd_flags(0);
231 232 233
	if (fd < 0) {
		error = fd;
		goto out_dput;
L
Linus Torvalds 已提交
234 235
	}

236 237 238 239
	path.mnt = parfilp->f_path.mnt;
	path.dentry = dentry;
	filp = dentry_open(&path, hreq->oflags, cred);
	dput(dentry);
L
Linus Torvalds 已提交
240
	if (IS_ERR(filp)) {
241 242
		put_unused_fd(fd);
		return PTR_ERR(filp);
L
Linus Torvalds 已提交
243
	}
244

A
Al Viro 已提交
245
	if (S_ISREG(inode->i_mode)) {
246
		filp->f_flags |= O_NOATIME;
247
		filp->f_mode |= FMODE_NOCMTIME;
248
	}
L
Linus Torvalds 已提交
249

250 251 252 253 254 255
	fd_install(fd, filp);
	return fd;

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

258
int
L
Linus Torvalds 已提交
259
xfs_readlink_by_handle(
260 261
	struct file		*parfilp,
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
262
{
263
	struct dentry		*dentry;
L
Linus Torvalds 已提交
264
	__u32			olen;
265
	int			error;
L
Linus Torvalds 已提交
266 267

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

270 271 272
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
273 274

	/* Restrict this handle operation to symlinks only. */
275
	if (!d_is_symlink(dentry)) {
E
Eric Sandeen 已提交
276
		error = -EINVAL;
277
		goto out_dput;
L
Linus Torvalds 已提交
278 279
	}

280
	if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
E
Eric Sandeen 已提交
281
		error = -EFAULT;
282
		goto out_dput;
L
Linus Torvalds 已提交
283 284
	}

285
	error = vfs_readlink(dentry, hreq->ohandle, olen);
286

287 288
 out_dput:
	dput(dentry);
289
	return error;
L
Linus Torvalds 已提交
290 291
}

D
Dave Chinner 已提交
292 293 294
int
xfs_set_dmattrs(
	xfs_inode_t     *ip,
D
Darrick J. Wong 已提交
295 296
	uint		evmask,
	uint16_t	state)
D
Dave Chinner 已提交
297 298 299 300 301 302
{
	xfs_mount_t	*mp = ip->i_mount;
	xfs_trans_t	*tp;
	int		error;

	if (!capable(CAP_SYS_ADMIN))
D
Dave Chinner 已提交
303
		return -EPERM;
D
Dave Chinner 已提交
304 305

	if (XFS_FORCED_SHUTDOWN(mp))
D
Dave Chinner 已提交
306
		return -EIO;
D
Dave Chinner 已提交
307

308 309
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
	if (error)
D
Dave Chinner 已提交
310
		return error;
311

D
Dave Chinner 已提交
312 313 314 315 316 317 318
	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);
319
	error = xfs_trans_commit(tp);
D
Dave Chinner 已提交
320 321 322 323

	return error;
}

L
Linus Torvalds 已提交
324 325
STATIC int
xfs_fssetdm_by_handle(
326 327
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
328 329 330 331
{
	int			error;
	struct fsdmidata	fsd;
	xfs_fsop_setdm_handlereq_t dmhreq;
332
	struct dentry		*dentry;
L
Linus Torvalds 已提交
333 334

	if (!capable(CAP_MKNOD))
E
Eric Sandeen 已提交
335
		return -EPERM;
L
Linus Torvalds 已提交
336
	if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
E
Eric Sandeen 已提交
337
		return -EFAULT;
L
Linus Torvalds 已提交
338

J
Jan Kara 已提交
339 340 341 342
	error = mnt_want_write_file(parfilp);
	if (error)
		return error;

343
	dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
J
Jan Kara 已提交
344 345
	if (IS_ERR(dentry)) {
		mnt_drop_write_file(parfilp);
346
		return PTR_ERR(dentry);
J
Jan Kara 已提交
347
	}
L
Linus Torvalds 已提交
348

349
	if (IS_IMMUTABLE(d_inode(dentry)) || IS_APPEND(d_inode(dentry))) {
E
Eric Sandeen 已提交
350
		error = -EPERM;
351
		goto out;
L
Linus Torvalds 已提交
352 353 354
	}

	if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
E
Eric Sandeen 已提交
355
		error = -EFAULT;
356
		goto out;
L
Linus Torvalds 已提交
357 358
	}

359
	error = xfs_set_dmattrs(XFS_I(d_inode(dentry)), fsd.fsd_dmevmask,
360
				 fsd.fsd_dmstate);
L
Linus Torvalds 已提交
361

362
 out:
J
Jan Kara 已提交
363
	mnt_drop_write_file(parfilp);
364
	dput(dentry);
365
	return error;
L
Linus Torvalds 已提交
366 367 368 369
}

STATIC int
xfs_attrlist_by_handle(
370 371
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
372
{
373
	int			error = -ENOMEM;
L
Linus Torvalds 已提交
374
	attrlist_cursor_kern_t	*cursor;
375
	struct xfs_fsop_attrlist_handlereq __user	*p = arg;
L
Linus Torvalds 已提交
376
	xfs_fsop_attrlist_handlereq_t al_hreq;
377
	struct dentry		*dentry;
L
Linus Torvalds 已提交
378 379 380
	char			*kbuf;

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
381
		return -EPERM;
L
Linus Torvalds 已提交
382
	if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
E
Eric Sandeen 已提交
383
		return -EFAULT;
384
	if (al_hreq.buflen < sizeof(struct attrlist) ||
J
Jan Tulak 已提交
385
	    al_hreq.buflen > XFS_XATTR_LIST_MAX)
E
Eric Sandeen 已提交
386
		return -EINVAL;
L
Linus Torvalds 已提交
387

388 389 390 391
	/*
	 * Reject flags, only allow namespaces.
	 */
	if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
E
Eric Sandeen 已提交
392
		return -EINVAL;
393

394 395 396
	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
397

398 399 400
	kbuf = kmem_zalloc_large(al_hreq.buflen, KM_SLEEP);
	if (!kbuf)
		goto out_dput;
L
Linus Torvalds 已提交
401 402

	cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
403
	error = xfs_attr_list(XFS_I(d_inode(dentry)), kbuf, al_hreq.buflen,
404
					al_hreq.flags, cursor);
L
Linus Torvalds 已提交
405 406 407
	if (error)
		goto out_kfree;

408 409 410 411 412
	if (copy_to_user(&p->pos, cursor, sizeof(attrlist_cursor_kern_t))) {
		error = -EFAULT;
		goto out_kfree;
	}

L
Linus Torvalds 已提交
413 414 415
	if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
		error = -EFAULT;

416 417 418
out_kfree:
	kmem_free(kbuf);
out_dput:
419 420
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
421 422
}

423
int
L
Linus Torvalds 已提交
424
xfs_attrmulti_attr_get(
425
	struct inode		*inode,
426 427
	unsigned char		*name,
	unsigned char		__user *ubuf,
428 429
	uint32_t		*len,
	uint32_t		flags)
L
Linus Torvalds 已提交
430
{
431
	unsigned char		*kbuf;
D
Dave Chinner 已提交
432
	int			error = -EFAULT;
433

434
	if (*len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
435
		return -EINVAL;
436 437
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
D
Dave Chinner 已提交
438
		return -ENOMEM;
L
Linus Torvalds 已提交
439

440
	error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
L
Linus Torvalds 已提交
441 442 443 444
	if (error)
		goto out_kfree;

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

447 448
out_kfree:
	kmem_free(kbuf);
L
Linus Torvalds 已提交
449 450 451
	return error;
}

452
int
L
Linus Torvalds 已提交
453
xfs_attrmulti_attr_set(
454
	struct inode		*inode,
455 456
	unsigned char		*name,
	const unsigned char	__user *ubuf,
457 458
	uint32_t		len,
	uint32_t		flags)
L
Linus Torvalds 已提交
459
{
460
	unsigned char		*kbuf;
461
	int			error;
L
Linus Torvalds 已提交
462

463
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
464
		return -EPERM;
465
	if (len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
466
		return -EINVAL;
L
Linus Torvalds 已提交
467

L
Li Zefan 已提交
468 469 470
	kbuf = memdup_user(ubuf, len);
	if (IS_ERR(kbuf))
		return PTR_ERR(kbuf);
471

472
	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
473 474
	if (!error)
		xfs_forget_acl(inode, name, flags);
475 476
	kfree(kbuf);
	return error;
L
Linus Torvalds 已提交
477 478
}

479
int
L
Linus Torvalds 已提交
480
xfs_attrmulti_attr_remove(
481
	struct inode		*inode,
482
	unsigned char		*name,
483
	uint32_t		flags)
L
Linus Torvalds 已提交
484
{
485 486
	int			error;

487
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
488
		return -EPERM;
489 490 491 492
	error = xfs_attr_remove(XFS_I(inode), name, flags);
	if (!error)
		xfs_forget_acl(inode, name, flags);
	return error;
L
Linus Torvalds 已提交
493 494 495 496
}

STATIC int
xfs_attrmulti_by_handle(
497
	struct file		*parfilp,
498
	void			__user *arg)
L
Linus Torvalds 已提交
499 500 501 502
{
	int			error;
	xfs_attr_multiop_t	*ops;
	xfs_fsop_attrmulti_handlereq_t am_hreq;
503
	struct dentry		*dentry;
L
Linus Torvalds 已提交
504
	unsigned int		i, size;
505
	unsigned char		*attr_name;
L
Linus Torvalds 已提交
506 507

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
508
		return -EPERM;
L
Linus Torvalds 已提交
509
	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
E
Eric Sandeen 已提交
510
		return -EFAULT;
L
Linus Torvalds 已提交
511

512 513 514 515
	/* overflow check */
	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
		return -E2BIG;

516 517 518
	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
519

D
Dave Chinner 已提交
520
	error = -E2BIG;
C
Christoph Hellwig 已提交
521
	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
L
Linus Torvalds 已提交
522
	if (!size || size > 16 * PAGE_SIZE)
523
		goto out_dput;
L
Linus Torvalds 已提交
524

L
Li Zefan 已提交
525 526
	ops = memdup_user(am_hreq.ops, size);
	if (IS_ERR(ops)) {
D
Dave Chinner 已提交
527
		error = PTR_ERR(ops);
528
		goto out_dput;
L
Li Zefan 已提交
529
	}
L
Linus Torvalds 已提交
530

D
Dave Chinner 已提交
531
	error = -ENOMEM;
L
Linus Torvalds 已提交
532 533 534 535 536 537
	attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
	if (!attr_name)
		goto out_kfree_ops;

	error = 0;
	for (i = 0; i < am_hreq.opcount; i++) {
538
		ops[i].am_error = strncpy_from_user((char *)attr_name,
L
Linus Torvalds 已提交
539 540
				ops[i].am_attrname, MAXNAMELEN);
		if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
D
Dave Chinner 已提交
541
			error = -ERANGE;
L
Linus Torvalds 已提交
542 543 544 545 546
		if (ops[i].am_error < 0)
			break;

		switch (ops[i].am_opcode) {
		case ATTR_OP_GET:
547
			ops[i].am_error = xfs_attrmulti_attr_get(
548
					d_inode(dentry), attr_name,
549 550
					ops[i].am_attrvalue, &ops[i].am_length,
					ops[i].am_flags);
L
Linus Torvalds 已提交
551 552
			break;
		case ATTR_OP_SET:
553
			ops[i].am_error = mnt_want_write_file(parfilp);
554 555
			if (ops[i].am_error)
				break;
556
			ops[i].am_error = xfs_attrmulti_attr_set(
557
					d_inode(dentry), attr_name,
558 559
					ops[i].am_attrvalue, ops[i].am_length,
					ops[i].am_flags);
A
Al Viro 已提交
560
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
561 562
			break;
		case ATTR_OP_REMOVE:
563
			ops[i].am_error = mnt_want_write_file(parfilp);
564 565
			if (ops[i].am_error)
				break;
566
			ops[i].am_error = xfs_attrmulti_attr_remove(
567
					d_inode(dentry), attr_name,
568
					ops[i].am_flags);
A
Al Viro 已提交
569
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
570 571
			break;
		default:
D
Dave Chinner 已提交
572
			ops[i].am_error = -EINVAL;
L
Linus Torvalds 已提交
573 574 575 576
		}
	}

	if (copy_to_user(am_hreq.ops, ops, size))
D
Dave Chinner 已提交
577
		error = -EFAULT;
L
Linus Torvalds 已提交
578 579 580 581

	kfree(attr_name);
 out_kfree_ops:
	kfree(ops);
582 583
 out_dput:
	dput(dentry);
D
Dave Chinner 已提交
584
	return error;
L
Linus Torvalds 已提交
585 586
}

587
int
L
Linus Torvalds 已提交
588 589 590
xfs_ioc_space(
	struct file		*filp,
	unsigned int		cmd,
591
	xfs_flock64_t		*bf)
L
Linus Torvalds 已提交
592
{
593 594
	struct inode		*inode = file_inode(filp);
	struct xfs_inode	*ip = XFS_I(inode);
595
	struct iattr		iattr;
596
	enum xfs_prealloc_flags	flags = 0;
597
	uint			iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
L
Linus Torvalds 已提交
598 599
	int			error;

600
	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
E
Eric Sandeen 已提交
601
		return -EPERM;
L
Linus Torvalds 已提交
602

603
	if (!(filp->f_mode & FMODE_WRITE))
E
Eric Sandeen 已提交
604
		return -EBADF;
L
Linus Torvalds 已提交
605

606
	if (!S_ISREG(inode->i_mode))
E
Eric Sandeen 已提交
607
		return -EINVAL;
L
Linus Torvalds 已提交
608

609 610
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
611
	if (filp->f_mode & FMODE_NOCMTIME)
612 613
		flags |= XFS_PREALLOC_INVISIBLE;

J
Jan Kara 已提交
614 615 616
	error = mnt_want_write_file(filp);
	if (error)
		return error;
617

618
	xfs_ilock(ip, iolock);
619
	error = xfs_break_layouts(inode, &iolock, BREAK_UNMAP);
620 621
	if (error)
		goto out_unlock;
622 623 624 625 626 627 628 629 630 631 632

	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 已提交
633
		error = -EINVAL;
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
		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 已提交
650
			error = -EINVAL;
651 652 653 654 655 656 657 658 659
			goto out_unlock;
		}
		break;
	default:
		bf->l_len = 0;
		break;
	}

	if (bf->l_start < 0 ||
660
	    bf->l_start > inode->i_sb->s_maxbytes ||
661
	    bf->l_start + bf->l_len < 0 ||
662
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
D
Dave Chinner 已提交
663
		error = -EINVAL;
664 665 666 667 668
		goto out_unlock;
	}

	switch (cmd) {
	case XFS_IOC_ZERO_RANGE:
669
		flags |= XFS_PREALLOC_SET;
670 671 672 673
		error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
		break;
	case XFS_IOC_RESVSP:
	case XFS_IOC_RESVSP64:
674
		flags |= XFS_PREALLOC_SET;
675 676 677 678 679 680 681 682 683 684 685
		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:
686
		flags |= XFS_PREALLOC_CLEAR;
687 688 689 690 691 692 693 694 695 696
		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;

697
		error = xfs_vn_setattr_size(file_dentry(filp), &iattr);
698 699 700
		break;
	default:
		ASSERT(0);
D
Dave Chinner 已提交
701
		error = -EINVAL;
702 703 704 705 706
	}

	if (error)
		goto out_unlock;

707
	error = xfs_update_prealloc_flags(ip, flags);
708 709

out_unlock:
710
	xfs_iunlock(ip, iolock);
J
Jan Kara 已提交
711
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
712
	return error;
L
Linus Torvalds 已提交
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
}

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 已提交
734
		return -EIO;
L
Linus Torvalds 已提交
735 736

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
E
Eric Sandeen 已提交
737
		return -EFAULT;
L
Linus Torvalds 已提交
738 739

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

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

745
	if (bulkreq.ubuffer == NULL)
E
Eric Sandeen 已提交
746
		return -EINVAL;
747

L
Linus Torvalds 已提交
748 749
	if (cmd == XFS_IOC_FSINUMBERS)
		error = xfs_inumbers(mp, &inlast, &count,
750
					bulkreq.ubuffer, xfs_inumbers_fmt);
L
Linus Torvalds 已提交
751
	else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
752 753
		error = xfs_bulkstat_one(mp, inlast, bulkreq.ubuffer,
					sizeof(xfs_bstat_t), NULL, &done);
754
	else	/* XFS_IOC_FSBULKSTAT */
755 756 757
		error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one,
				     sizeof(xfs_bstat_t), bulkreq.ubuffer,
				     &done);
L
Linus Torvalds 已提交
758 759

	if (error)
D
Dave Chinner 已提交
760
		return error;
L
Linus Torvalds 已提交
761 762 763 764

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

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
E
Eric Sandeen 已提交
768
			return -EFAULT;
L
Linus Torvalds 已提交
769 770 771 772 773 774 775
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry(
776 777 778
	struct xfs_mount	*mp,
	void			__user *arg,
	int			struct_version)
L
Linus Torvalds 已提交
779
{
780 781
	struct xfs_fsop_geom	fsgeo;
	size_t			len;
L
Linus Torvalds 已提交
782

783
	xfs_fs_geometry(&mp->m_sb, &fsgeo, struct_version);
L
Linus Torvalds 已提交
784

785 786 787 788
	if (struct_version <= 3)
		len = sizeof(struct xfs_fsop_geom_v1);
	else if (struct_version == 4)
		len = sizeof(struct xfs_fsop_geom_v4);
789 790
	else {
		xfs_fsop_geom_health(mp, &fsgeo);
791
		len = sizeof(fsgeo);
792
	}
793 794

	if (copy_to_user(arg, &fsgeo, len))
E
Eric Sandeen 已提交
795
		return -EFAULT;
L
Linus Torvalds 已提交
796 797 798
	return 0;
}

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
STATIC int
xfs_ioc_ag_geometry(
	struct xfs_mount	*mp,
	void			__user *arg)
{
	struct xfs_ag_geometry	ageo;
	int			error;

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

	error = xfs_ag_get_geometry(mp, ageo.ag_number, &ageo);
	if (error)
		return error;

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

L
Linus Torvalds 已提交
819 820 821 822 823 824 825 826 827 828 829
/*
 * Linux extended inode flags interface.
 */

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

830
	if (flags & FS_IMMUTABLE_FL)
831
		xflags |= FS_XFLAG_IMMUTABLE;
L
Linus Torvalds 已提交
832
	else
833
		xflags &= ~FS_XFLAG_IMMUTABLE;
834
	if (flags & FS_APPEND_FL)
835
		xflags |= FS_XFLAG_APPEND;
L
Linus Torvalds 已提交
836
	else
837
		xflags &= ~FS_XFLAG_APPEND;
838
	if (flags & FS_SYNC_FL)
839
		xflags |= FS_XFLAG_SYNC;
L
Linus Torvalds 已提交
840
	else
841
		xflags &= ~FS_XFLAG_SYNC;
842
	if (flags & FS_NOATIME_FL)
843
		xflags |= FS_XFLAG_NOATIME;
L
Linus Torvalds 已提交
844
	else
845
		xflags &= ~FS_XFLAG_NOATIME;
846
	if (flags & FS_NODUMP_FL)
847
		xflags |= FS_XFLAG_NODUMP;
L
Linus Torvalds 已提交
848
	else
849
		xflags &= ~FS_XFLAG_NODUMP;
L
Linus Torvalds 已提交
850 851 852 853 854 855

	return xflags;
}

STATIC unsigned int
xfs_di2lxflags(
856
	uint16_t	di_flags)
L
Linus Torvalds 已提交
857 858 859 860
{
	unsigned int	flags = 0;

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
861
		flags |= FS_IMMUTABLE_FL;
L
Linus Torvalds 已提交
862
	if (di_flags & XFS_DIFLAG_APPEND)
863
		flags |= FS_APPEND_FL;
L
Linus Torvalds 已提交
864
	if (di_flags & XFS_DIFLAG_SYNC)
865
		flags |= FS_SYNC_FL;
L
Linus Torvalds 已提交
866
	if (di_flags & XFS_DIFLAG_NOATIME)
867
		flags |= FS_NOATIME_FL;
L
Linus Torvalds 已提交
868
	if (di_flags & XFS_DIFLAG_NODUMP)
869
		flags |= FS_NODUMP_FL;
L
Linus Torvalds 已提交
870 871 872
	return flags;
}

873 874 875 876 877 878 879 880
STATIC int
xfs_ioc_fsgetxattr(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;

881 882
	memset(&fa, 0, sizeof(struct fsxattr));

883 884 885
	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;
886 887
	fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
			ip->i_mount->m_sb.sb_blocklog;
888
	fa.fsx_projid = xfs_get_projid(ip);
889 890 891 892

	if (attr) {
		if (ip->i_afp) {
			if (ip->i_afp->if_flags & XFS_IFEXTENTS)
893
				fa.fsx_nextents = xfs_iext_count(ip->i_afp);
894 895 896 897 898 899
			else
				fa.fsx_nextents = ip->i_d.di_anextents;
		} else
			fa.fsx_nextents = 0;
	} else {
		if (ip->i_df.if_flags & XFS_IFEXTENTS)
900
			fa.fsx_nextents = xfs_iext_count(&ip->i_df);
901 902 903 904 905 906 907 908 909 910
		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;
}

911 912
STATIC uint16_t
xfs_flags2diflags(
913 914 915 916
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	/* can't set PREALLOC this way, just preserve it */
917 918 919
	uint16_t		di_flags =
		(ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);

920
	if (xflags & FS_XFLAG_IMMUTABLE)
921
		di_flags |= XFS_DIFLAG_IMMUTABLE;
922
	if (xflags & FS_XFLAG_APPEND)
923
		di_flags |= XFS_DIFLAG_APPEND;
924
	if (xflags & FS_XFLAG_SYNC)
925
		di_flags |= XFS_DIFLAG_SYNC;
926
	if (xflags & FS_XFLAG_NOATIME)
927
		di_flags |= XFS_DIFLAG_NOATIME;
928
	if (xflags & FS_XFLAG_NODUMP)
929
		di_flags |= XFS_DIFLAG_NODUMP;
930
	if (xflags & FS_XFLAG_NODEFRAG)
931
		di_flags |= XFS_DIFLAG_NODEFRAG;
932
	if (xflags & FS_XFLAG_FILESTREAM)
933
		di_flags |= XFS_DIFLAG_FILESTREAM;
D
Dave Chinner 已提交
934
	if (S_ISDIR(VFS_I(ip)->i_mode)) {
935
		if (xflags & FS_XFLAG_RTINHERIT)
936
			di_flags |= XFS_DIFLAG_RTINHERIT;
937
		if (xflags & FS_XFLAG_NOSYMLINKS)
938
			di_flags |= XFS_DIFLAG_NOSYMLINKS;
939
		if (xflags & FS_XFLAG_EXTSZINHERIT)
940
			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
941
		if (xflags & FS_XFLAG_PROJINHERIT)
942
			di_flags |= XFS_DIFLAG_PROJINHERIT;
D
Dave Chinner 已提交
943
	} else if (S_ISREG(VFS_I(ip)->i_mode)) {
944
		if (xflags & FS_XFLAG_REALTIME)
945
			di_flags |= XFS_DIFLAG_REALTIME;
946
		if (xflags & FS_XFLAG_EXTSIZE)
947 948
			di_flags |= XFS_DIFLAG_EXTSIZE;
	}
949

950 951 952 953 954 955 956 957 958 959
	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);
960 961 962

	if (xflags & FS_XFLAG_DAX)
		di_flags2 |= XFS_DIFLAG2_DAX;
963 964
	if (xflags & FS_XFLAG_COWEXTSIZE)
		di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
965

966
	return di_flags2;
967 968
}

969 970 971 972
STATIC void
xfs_diflags_to_linux(
	struct xfs_inode	*ip)
{
973
	struct inode		*inode = VFS_I(ip);
974 975
	unsigned int		xflags = xfs_ip2xflags(ip);

976
	if (xflags & FS_XFLAG_IMMUTABLE)
977 978 979
		inode->i_flags |= S_IMMUTABLE;
	else
		inode->i_flags &= ~S_IMMUTABLE;
980
	if (xflags & FS_XFLAG_APPEND)
981 982 983
		inode->i_flags |= S_APPEND;
	else
		inode->i_flags &= ~S_APPEND;
984
	if (xflags & FS_XFLAG_SYNC)
985 986 987
		inode->i_flags |= S_SYNC;
	else
		inode->i_flags &= ~S_SYNC;
988
	if (xflags & FS_XFLAG_NOATIME)
989 990 991
		inode->i_flags |= S_NOATIME;
	else
		inode->i_flags &= ~S_NOATIME;
992
#if 0	/* disabled until the flag switching races are sorted out */
993 994 995 996
	if (xflags & FS_XFLAG_DAX)
		inode->i_flags |= S_DAX;
	else
		inode->i_flags &= ~S_DAX;
997
#endif
998
}
999

1000 1001 1002 1003 1004 1005 1006
static int
xfs_ioctl_setattr_xflags(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
1007
	uint64_t		di_flags2;
1008 1009 1010

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

	/* If realtime flag is set then must have realtime device */
1015
	if (fa->fsx_xflags & FS_XFLAG_REALTIME) {
1016 1017 1018 1019 1020
		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;
	}

1021
	/* Clear reflink if we are actually able to set the rt flag. */
1022
	if ((fa->fsx_xflags & FS_XFLAG_REALTIME) && xfs_is_reflink_inode(ip))
1023
		ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
1024

1025 1026 1027 1028
	/* 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;

1029 1030 1031 1032 1033
	/*
	 * 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)) ||
1034
	     (fa->fsx_xflags & (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND))) &&
1035 1036 1037
	    !capable(CAP_LINUX_IMMUTABLE))
		return -EPERM;

1038 1039 1040 1041 1042 1043 1044 1045
	/* 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;

1046 1047 1048
	xfs_diflags_to_linux(ip);
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1049
	XFS_STATS_INC(mp, xs_ig_attrchg);
1050 1051 1052
	return 0;
}

1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
/*
 * 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);
1068
	struct super_block	*sb = inode->i_sb;
1069 1070 1071 1072
	int			error;

	*join_flags = 0;

1073 1074
	/*
	 * It is only valid to set the DAX flag on regular files and
1075
	 * directories on filesystems where the block size is equal to the page
1076 1077
	 * size. On directories it serves as an inherited hint so we don't
	 * have to check the device for dax support or flush pagecache.
1078
	 */
1079 1080 1081
	if (fa->fsx_xflags & FS_XFLAG_DAX) {
		if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
			return -EINVAL;
1082 1083
		if (S_ISREG(inode->i_mode) &&
		    !bdev_dax_supported(xfs_find_bdev_for_inode(VFS_I(ip)),
1084
				sb->s_blocksize))
1085 1086
			return -EINVAL;
	}
1087

1088 1089 1090 1091 1092 1093
	/* 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;

1094 1095 1096
	if (S_ISDIR(inode->i_mode))
		return 0;

1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
	/* 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;
1107
	return 0;
1108 1109 1110 1111 1112

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

1113 1114
}

1115 1116 1117 1118 1119
/*
 * 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.
1120 1121 1122 1123 1124 1125
 *
 * 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.
1126 1127 1128
 */
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
1129 1130
	struct xfs_inode	*ip,
	int			join_flags)
1131 1132 1133
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
1134
	int			error = -EROFS;
1135 1136

	if (mp->m_flags & XFS_MOUNT_RDONLY)
1137 1138
		goto out_unlock;
	error = -EIO;
1139
	if (XFS_FORCED_SHUTDOWN(mp))
1140
		goto out_unlock;
1141

1142
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1143
	if (error)
1144
		goto out_unlock;
1145 1146

	xfs_ilock(ip, XFS_ILOCK_EXCL);
1147 1148
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags);
	join_flags = 0;
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166

	/*
	 * 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:
1167
	xfs_trans_cancel(tp);
1168 1169 1170
out_unlock:
	if (join_flags)
		xfs_iunlock(ip, join_flags);
1171 1172 1173
	return ERR_PTR(error);
}

1174 1175 1176 1177
/*
 * extent size hint validation is somewhat cumbersome. Rules are:
 *
 * 1. extent size hint is only valid for directories and regular files
1178 1179
 * 2. FS_XFLAG_EXTSIZE is only valid for regular files
 * 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
1180 1181 1182 1183 1184 1185 1186
 * 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 已提交
1187 1188
 *
 * Please keep this function in sync with xfs_scrub_inode_extsize.
1189
 */
1190
static int
1191 1192 1193 1194 1195 1196
xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

D
Dave Chinner 已提交
1197
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode))
1198 1199
		return -EINVAL;

1200
	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
D
Dave Chinner 已提交
1201
	    !S_ISDIR(VFS_I(ip)->i_mode))
1202 1203
		return -EINVAL;

D
Dave Chinner 已提交
1204
	if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents &&
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
	    ((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) ||
1217
		    (fa->fsx_xflags & FS_XFLAG_REALTIME)) {
1218 1219 1220 1221 1222 1223 1224 1225 1226
			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;
1227
	} else
1228
		fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
1229

1230 1231 1232
	return 0;
}

1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
/*
 * 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 已提交
1245 1246
 *
 * Please keep this function in sync with xfs_scrub_inode_cowextsize.
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
 */
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;
}

1285
static int
1286 1287 1288 1289 1290
xfs_ioctl_setattr_check_projid(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	/* Disallow 32bit project ids if projid32bit feature is not enabled. */
1291
	if (fa->fsx_projid > (uint16_t)-1 &&
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
	    !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;
1305
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1306 1307 1308 1309 1310
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1311 1312 1313 1314

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
1315
	struct fsxattr		*fa)
1316 1317 1318
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
C
Christoph Hellwig 已提交
1319
	struct xfs_dquot	*udqp = NULL;
1320
	struct xfs_dquot	*pdqp = NULL;
1321 1322
	struct xfs_dquot	*olddquot = NULL;
	int			code;
1323
	int			join_flags = 0;
1324

C
Christoph Hellwig 已提交
1325
	trace_xfs_ioctl_setattr(ip);
1326

1327 1328 1329
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1330

1331 1332 1333 1334 1335 1336 1337 1338
	/*
	 * 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.
	 */
1339
	if (XFS_IS_QUOTA_ON(mp)) {
C
Christoph Hellwig 已提交
1340
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1341
					 ip->i_d.di_gid, fa->fsx_projid,
1342
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1343 1344 1345 1346
		if (code)
			return code;
	}

1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358
	/*
	 * 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);
1359 1360 1361
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1362 1363 1364
	}


1365 1366 1367 1368 1369
	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 */
1370
			goto error_trans_cancel;
1371 1372
	}

1373 1374 1375
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1376

1377 1378 1379 1380
	code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
	if (code)
		goto error_trans_cancel;

1381 1382
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1383
		goto error_trans_cancel;
1384 1385

	/*
1386 1387 1388 1389 1390
	 * 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()
1391 1392
	 */

D
Dave Chinner 已提交
1393
	if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
1394
	    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
D
Dave Chinner 已提交
1395
		VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
1396

1397 1398 1399 1400 1401 1402 1403 1404
	/* 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);
1405
	}
1406

1407 1408 1409 1410 1411
	/*
	 * 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.
	 */
1412 1413 1414 1415
	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;
1416 1417 1418 1419 1420 1421
	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;
1422

1423
	code = xfs_trans_commit(tp);
1424 1425 1426 1427

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1428 1429
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1430
	xfs_qm_dqrele(pdqp);
1431

C
Christoph Hellwig 已提交
1432
	return code;
1433

1434
error_trans_cancel:
1435
	xfs_trans_cancel(tp);
1436
error_free_dquots:
C
Christoph Hellwig 已提交
1437
	xfs_qm_dqrele(udqp);
1438
	xfs_qm_dqrele(pdqp);
1439 1440 1441
	return code;
}

L
Linus Torvalds 已提交
1442
STATIC int
L
Lachlan McIlroy 已提交
1443
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1444 1445 1446 1447 1448
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1449
	int error;
L
Lachlan McIlroy 已提交
1450 1451 1452

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

J
Jan Kara 已提交
1454 1455 1456
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1457
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1458
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1459
	return error;
L
Lachlan McIlroy 已提交
1460
}
L
Linus Torvalds 已提交
1461

L
Lachlan McIlroy 已提交
1462 1463 1464 1465 1466 1467
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1468

L
Lachlan McIlroy 已提交
1469 1470 1471 1472 1473
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1474

L
Lachlan McIlroy 已提交
1475 1476
STATIC int
xfs_ioc_setxflags(
1477
	struct xfs_inode	*ip,
L
Lachlan McIlroy 已提交
1478 1479 1480
	struct file		*filp,
	void			__user *arg)
{
1481
	struct xfs_trans	*tp;
1482
	struct fsxattr		fa;
L
Lachlan McIlroy 已提交
1483
	unsigned int		flags;
1484
	int			join_flags = 0;
1485
	int			error;
L
Linus Torvalds 已提交
1486

L
Lachlan McIlroy 已提交
1487 1488
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1489

L
Lachlan McIlroy 已提交
1490 1491 1492 1493
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1494

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

J
Jan Kara 已提交
1497 1498 1499
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1500

1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
	/*
	 * 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);
1513 1514 1515 1516 1517 1518 1519
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1520
		xfs_trans_cancel(tp);
1521 1522 1523
		goto out_drop_write;
	}

1524
	error = xfs_trans_commit(tp);
1525
out_drop_write:
J
Jan Kara 已提交
1526
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1527
	return error;
L
Linus Torvalds 已提交
1528 1529
}

1530 1531 1532 1533 1534
static bool
xfs_getbmap_format(
	struct kgetbmap		*p,
	struct getbmapx __user	*u,
	size_t			recsize)
1535
{
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549
	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;
1550 1551
}

L
Linus Torvalds 已提交
1552 1553
STATIC int
xfs_ioc_getbmap(
1554
	struct file		*file,
L
Linus Torvalds 已提交
1555 1556 1557
	unsigned int		cmd,
	void			__user *arg)
{
1558
	struct getbmapx		bmx = { 0 };
1559 1560 1561
	struct kgetbmap		*buf;
	size_t			recsize;
	int			error, i;
L
Linus Torvalds 已提交
1562

1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
	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 已提交
1577
		return -EINVAL;
1578
	}
L
Linus Torvalds 已提交
1579

1580
	if (copy_from_user(&bmx, arg, recsize))
E
Eric Sandeen 已提交
1581
		return -EFAULT;
L
Linus Torvalds 已提交
1582 1583

	if (bmx.bmv_count < 2)
E
Eric Sandeen 已提交
1584
		return -EINVAL;
1585 1586
	if (bmx.bmv_count > ULONG_MAX / recsize)
		return -ENOMEM;
L
Linus Torvalds 已提交
1587

1588 1589 1590
	buf = kmem_zalloc_large(bmx.bmv_count * sizeof(*buf), 0);
	if (!buf)
		return -ENOMEM;
L
Linus Torvalds 已提交
1591

1592
	error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf);
L
Linus Torvalds 已提交
1593
	if (error)
1594
		goto out_free_buf;
L
Linus Torvalds 已提交
1595

1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
	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 已提交
1606

1607 1608 1609
	error = 0;
out_free_buf:
	kmem_free(buf);
1610
	return error;
L
Linus Torvalds 已提交
1611
}
L
Lachlan McIlroy 已提交
1612

1613 1614
struct getfsmap_info {
	struct xfs_mount	*mp;
1615 1616
	struct fsmap_head __user *data;
	unsigned int		idx;
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629
	__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);
1630 1631
	if (copy_to_user(&info->data->fmh_recs[info->idx++], &fm,
			sizeof(struct fsmap)))
1632 1633 1634 1635 1636 1637 1638 1639
		return -EFAULT;

	return 0;
}

STATIC int
xfs_ioc_getfsmap(
	struct xfs_inode	*ip,
1640
	struct fsmap_head	__user *arg)
1641
{
1642
	struct getfsmap_info	info = { NULL };
1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665
	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;
1666
	info.data = arg;
1667 1668 1669 1670 1671 1672 1673 1674
	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 */
1675
	if (!aborted && info.idx) {
1676
		info.last_flags |= FMR_OF_LAST;
1677 1678
		if (copy_to_user(&info.data->fmh_recs[info.idx - 1].fmr_flags,
				&info.last_flags, sizeof(info.last_flags)))
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690
			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;
}

1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714
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 已提交
1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725
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 已提交
1726
		error = -EINVAL;
D
Dave Chinner 已提交
1727 1728 1729 1730 1731 1732
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1733
		error = -EBADF;
D
Dave Chinner 已提交
1734 1735 1736 1737 1738
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1739
		error = -EINVAL;
D
Dave Chinner 已提交
1740 1741 1742 1743 1744 1745
		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 已提交
1746
		error = -EBADF;
D
Dave Chinner 已提交
1747 1748 1749 1750 1751
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1752
		error = -EINVAL;
D
Dave Chinner 已提交
1753 1754 1755
		goto out_put_tmp_file;
	}

1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766
	/*
	 * 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 已提交
1767 1768 1769 1770
	ip = XFS_I(file_inode(f.file));
	tip = XFS_I(file_inode(tmp.file));

	if (ip->i_mount != tip->i_mount) {
D
Dave Chinner 已提交
1771
		error = -EINVAL;
D
Dave Chinner 已提交
1772 1773 1774 1775
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1776
		error = -EINVAL;
D
Dave Chinner 已提交
1777 1778 1779 1780
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1781
		error = -EIO;
D
Dave Chinner 已提交
1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794
		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;
}

1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805
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);

1806 1807
	/* 1 larger than sb_fname, so this ensures a trailing NUL char */
	memset(label, 0, sizeof(label));
1808
	spin_lock(&mp->m_sb_lock);
1809
	strncpy(label, sbp->sb_fname, XFSLABEL_MAX);
1810 1811
	spin_unlock(&mp->m_sb_lock);

1812
	if (copy_to_user(user_label, label, sizeof(label)))
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
		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));
1848
	memcpy(sbp->sb_fname, label, len);
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
	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;
}

1877 1878 1879 1880 1881 1882 1883 1884
/*
 * 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 已提交
1885 1886
	struct file		*filp,
	unsigned int		cmd,
1887
	unsigned long		p)
L
Lachlan McIlroy 已提交
1888
{
A
Al Viro 已提交
1889
	struct inode		*inode = file_inode(filp);
1890 1891 1892
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	void			__user *arg = (void __user *)p;
L
Lachlan McIlroy 已提交
1893 1894
	int			error;

C
Christoph Hellwig 已提交
1895
	trace_xfs_file_ioctl(ip);
1896 1897

	switch (cmd) {
C
Christoph Hellwig 已提交
1898 1899
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
1900 1901 1902 1903
	case FS_IOC_GETFSLABEL:
		return xfs_ioc_getlabel(mp, arg);
	case FS_IOC_SETFSLABEL:
		return xfs_ioc_setlabel(filp, mp, arg);
L
Lachlan McIlroy 已提交
1904 1905 1906 1907 1908 1909 1910
	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 已提交
1911 1912
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1913
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1914

1915
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1916
			return -EFAULT;
1917
		return xfs_ioc_space(filp, cmd, &bf);
1918
	}
L
Lachlan McIlroy 已提交
1919 1920 1921 1922 1923 1924
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1925
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1926 1927 1928
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

		if (copy_to_user(arg, &da, sizeof(da)))
E
Eric Sandeen 已提交
1929
			return -EFAULT;
L
Lachlan McIlroy 已提交
1930 1931 1932 1933 1934 1935 1936 1937 1938
		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:
1939 1940 1941
		return xfs_ioc_fsgeometry(mp, arg, 3);
	case XFS_IOC_FSGEOMETRY_V4:
		return xfs_ioc_fsgeometry(mp, arg, 4);
L
Lachlan McIlroy 已提交
1942
	case XFS_IOC_FSGEOMETRY:
1943
		return xfs_ioc_fsgeometry(mp, arg, 5);
L
Lachlan McIlroy 已提交
1944

1945 1946 1947
	case XFS_IOC_AG_GEOMETRY:
		return xfs_ioc_ag_geometry(mp, arg);

L
Lachlan McIlroy 已提交
1948 1949 1950 1951 1952 1953 1954
	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 已提交
1955 1956
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(ip, filp, arg);
L
Lachlan McIlroy 已提交
1957
	case XFS_IOC_GETXFLAGS:
L
Lachlan McIlroy 已提交
1958
		return xfs_ioc_getxflags(ip, arg);
L
Lachlan McIlroy 已提交
1959
	case XFS_IOC_SETXFLAGS:
L
Lachlan McIlroy 已提交
1960
		return xfs_ioc_setxflags(ip, filp, arg);
L
Lachlan McIlroy 已提交
1961 1962 1963 1964 1965

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1968 1969 1970 1971
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1972 1973
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1974
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1975
		return error;
L
Lachlan McIlroy 已提交
1976 1977 1978 1979 1980
	}

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

1983 1984 1985
	case FS_IOC_GETFSMAP:
		return xfs_ioc_getfsmap(ip, arg);

1986 1987 1988
	case XFS_IOC_SCRUB_METADATA:
		return xfs_ioc_scrub_metadata(ip, arg);

L
Lachlan McIlroy 已提交
1989 1990
	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
1991 1992
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1993

1994
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1995
			return -EFAULT;
1996 1997 1998 1999
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
2000

2001
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
2002
			return -EFAULT;
2003
		return xfs_open_by_handle(filp, &hreq);
2004
	}
L
Lachlan McIlroy 已提交
2005
	case XFS_IOC_FSSETDM_BY_HANDLE:
2006
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
2007

2008 2009
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
2010

2011
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
2012
			return -EFAULT;
2013
		return xfs_readlink_by_handle(filp, &hreq);
2014
	}
L
Lachlan McIlroy 已提交
2015
	case XFS_IOC_ATTRLIST_BY_HANDLE:
2016
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
2017 2018

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
2019
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
2020 2021

	case XFS_IOC_SWAPEXT: {
2022 2023 2024
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
2025
			return -EFAULT;
J
Jan Kara 已提交
2026 2027 2028
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
2029
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
2030
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2031
		return error;
L
Lachlan McIlroy 已提交
2032 2033 2034 2035 2036
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

2037
		xfs_fs_counts(mp, &out);
L
Lachlan McIlroy 已提交
2038 2039

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
2040
			return -EFAULT;
L
Lachlan McIlroy 已提交
2041 2042 2043 2044 2045
		return 0;
	}

	case XFS_IOC_SET_RESBLKS: {
		xfs_fsop_resblks_t inout;
2046
		uint64_t	   in;
L
Lachlan McIlroy 已提交
2047 2048 2049 2050

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

E
Eric Sandeen 已提交
2051
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
2052
			return -EROFS;
E
Eric Sandeen 已提交
2053

L
Lachlan McIlroy 已提交
2054
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
2055
			return -EFAULT;
L
Lachlan McIlroy 已提交
2056

J
Jan Kara 已提交
2057 2058 2059 2060
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
2061 2062 2063
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
2064
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
2065
		if (error)
D
Dave Chinner 已提交
2066
			return error;
L
Lachlan McIlroy 已提交
2067 2068

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
2069
			return -EFAULT;
L
Lachlan McIlroy 已提交
2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080
		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 已提交
2081
			return error;
L
Lachlan McIlroy 已提交
2082 2083

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

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

J
Jan Kara 已提交
2095 2096 2097
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
2098
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
2099
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2100
		return error;
L
Lachlan McIlroy 已提交
2101 2102 2103 2104 2105 2106
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_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_log(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_FSGROWFSRT: {
		xfs_growfs_rt_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_rt(mp, &in);
J
Jan Kara 已提交
2127
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2128
		return error;
L
Lachlan McIlroy 已提交
2129 2130 2131
	}

	case XFS_IOC_GOINGDOWN: {
2132
		uint32_t in;
L
Lachlan McIlroy 已提交
2133 2134 2135 2136

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

2137
		if (get_user(in, (uint32_t __user *)arg))
E
Eric Sandeen 已提交
2138
			return -EFAULT;
L
Lachlan McIlroy 已提交
2139

D
Dave Chinner 已提交
2140
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
2141 2142 2143 2144 2145 2146 2147 2148 2149
	}

	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 已提交
2150
			return -EFAULT;
L
Lachlan McIlroy 已提交
2151

2152
		return xfs_errortag_add(mp, in.errtag);
L
Lachlan McIlroy 已提交
2153 2154 2155 2156 2157 2158
	}

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

2159
		return xfs_errortag_clearall(mp);
L
Lachlan McIlroy 已提交
2160

2161
	case XFS_IOC_FREE_EOFBLOCKS: {
2162 2163
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
2164

2165 2166 2167 2168
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

2171
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
2172
			return -EFAULT;
2173

2174 2175
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
2176
			return error;
2177

D
Dave Chinner 已提交
2178
		return xfs_icache_free_eofblocks(mp, &keofb);
2179 2180
	}

L
Lachlan McIlroy 已提交
2181 2182 2183 2184
	default:
		return -ENOTTY;
	}
}