xfs_ioctl.c 42.6 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_pnfs.h"
43
#include "xfs_acl.h"
L
Linus Torvalds 已提交
44

45
#include <linux/capability.h>
L
Linus Torvalds 已提交
46 47 48 49
#include <linux/dcache.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
50
#include <linux/slab.h>
51
#include <linux/exportfs.h>
L
Linus Torvalds 已提交
52 53 54 55 56 57 58 59 60 61 62 63

/*
 * 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
 */
64
int
L
Linus Torvalds 已提交
65 66
xfs_find_handle(
	unsigned int		cmd,
67
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
68 69 70 71
{
	int			hsize;
	xfs_handle_t		handle;
	struct inode		*inode;
72
	struct fd		f = {NULL};
C
Christoph Hellwig 已提交
73
	struct path		path;
74
	int			error;
C
Christoph Hellwig 已提交
75
	struct xfs_inode	*ip;
L
Linus Torvalds 已提交
76

C
Christoph Hellwig 已提交
77
	if (cmd == XFS_IOC_FD_TO_HANDLE) {
78 79
		f = fdget(hreq->fd);
		if (!f.file)
C
Christoph Hellwig 已提交
80
			return -EBADF;
A
Al Viro 已提交
81
		inode = file_inode(f.file);
C
Christoph Hellwig 已提交
82 83 84 85
	} else {
		error = user_lpath((const char __user *)hreq->path, &path);
		if (error)
			return error;
86
		inode = d_inode(path.dentry);
L
Linus Torvalds 已提交
87
	}
C
Christoph Hellwig 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	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 已提交
114 115 116
		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
					sizeof(handle.ha_fid.fid_len);
		handle.ha_fid.fid_pad = 0;
117
		handle.ha_fid.fid_gen = inode->i_generation;
C
Christoph Hellwig 已提交
118
		handle.ha_fid.fid_ino = ip->i_ino;
L
Linus Torvalds 已提交
119 120 121 122

		hsize = XFS_HSIZE(handle);
	}

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

C
Christoph Hellwig 已提交
128 129 130 131
	error = 0;

 out_put:
	if (cmd == XFS_IOC_FD_TO_HANDLE)
132
		fdput(f);
C
Christoph Hellwig 已提交
133 134 135
	else
		path_put(&path);
	return error;
L
Linus Torvalds 已提交
136 137 138
}

/*
139 140
 * No need to do permission checks on the various pathname components
 * as the handle operations are privileged.
L
Linus Torvalds 已提交
141 142
 */
STATIC int
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
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 已提交
158 159
{
	xfs_handle_t		handle;
160
	struct xfs_fid64	fid;
L
Linus Torvalds 已提交
161 162 163 164

	/*
	 * Only allow handle opens under a directory.
	 */
A
Al Viro 已提交
165
	if (!S_ISDIR(file_inode(parfilp)->i_mode))
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
		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 已提交
184

185 186 187 188 189 190
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 已提交
191 192
}

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

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

211 212 213
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
214
	inode = d_inode(dentry);
L
Linus Torvalds 已提交
215 216 217

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

#if BITS_PER_LONG != 32
223
	hreq->oflags |= O_LARGEFILE;
L
Linus Torvalds 已提交
224
#endif
225

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

234
	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
E
Eric Sandeen 已提交
235
		error = -EACCES;
236
		goto out_dput;
L
Linus Torvalds 已提交
237 238 239
	}

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

245
	fd = get_unused_fd_flags(0);
246 247 248
	if (fd < 0) {
		error = fd;
		goto out_dput;
L
Linus Torvalds 已提交
249 250
	}

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

A
Al Viro 已提交
260
	if (S_ISREG(inode->i_mode)) {
261
		filp->f_flags |= O_NOATIME;
262
		filp->f_mode |= FMODE_NOCMTIME;
263
	}
L
Linus Torvalds 已提交
264

265 266 267 268 269 270
	fd_install(fd, filp);
	return fd;

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

273
int
L
Linus Torvalds 已提交
274
xfs_readlink_by_handle(
275 276
	struct file		*parfilp,
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
277
{
278
	struct dentry		*dentry;
L
Linus Torvalds 已提交
279
	__u32			olen;
280
	int			error;
L
Linus Torvalds 已提交
281 282

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

285 286 287
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
288 289

	/* Restrict this handle operation to symlinks only. */
290
	if (!d_inode(dentry)->i_op->readlink) {
E
Eric Sandeen 已提交
291
		error = -EINVAL;
292
		goto out_dput;
L
Linus Torvalds 已提交
293 294
	}

295
	if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
E
Eric Sandeen 已提交
296
		error = -EFAULT;
297
		goto out_dput;
L
Linus Torvalds 已提交
298 299
	}

300
	error = d_inode(dentry)->i_op->readlink(dentry, hreq->ohandle, olen);
301

302 303
 out_dput:
	dput(dentry);
304
	return error;
L
Linus Torvalds 已提交
305 306
}

D
Dave Chinner 已提交
307 308 309 310 311 312 313 314 315 316 317
int
xfs_set_dmattrs(
	xfs_inode_t     *ip,
	u_int		evmask,
	u_int16_t	state)
{
	xfs_mount_t	*mp = ip->i_mount;
	xfs_trans_t	*tp;
	int		error;

	if (!capable(CAP_SYS_ADMIN))
D
Dave Chinner 已提交
318
		return -EPERM;
D
Dave Chinner 已提交
319 320

	if (XFS_FORCED_SHUTDOWN(mp))
D
Dave Chinner 已提交
321
		return -EIO;
D
Dave Chinner 已提交
322

323 324
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
	if (error)
D
Dave Chinner 已提交
325
		return error;
326

D
Dave Chinner 已提交
327 328 329 330 331 332 333
	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);
334
	error = xfs_trans_commit(tp);
D
Dave Chinner 已提交
335 336 337 338

	return error;
}

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

	if (!capable(CAP_MKNOD))
E
Eric Sandeen 已提交
350
		return -EPERM;
L
Linus Torvalds 已提交
351
	if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
E
Eric Sandeen 已提交
352
		return -EFAULT;
L
Linus Torvalds 已提交
353

J
Jan Kara 已提交
354 355 356 357
	error = mnt_want_write_file(parfilp);
	if (error)
		return error;

358
	dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
J
Jan Kara 已提交
359 360
	if (IS_ERR(dentry)) {
		mnt_drop_write_file(parfilp);
361
		return PTR_ERR(dentry);
J
Jan Kara 已提交
362
	}
L
Linus Torvalds 已提交
363

364
	if (IS_IMMUTABLE(d_inode(dentry)) || IS_APPEND(d_inode(dentry))) {
E
Eric Sandeen 已提交
365
		error = -EPERM;
366
		goto out;
L
Linus Torvalds 已提交
367 368 369
	}

	if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
E
Eric Sandeen 已提交
370
		error = -EFAULT;
371
		goto out;
L
Linus Torvalds 已提交
372 373
	}

374
	error = xfs_set_dmattrs(XFS_I(d_inode(dentry)), fsd.fsd_dmevmask,
375
				 fsd.fsd_dmstate);
L
Linus Torvalds 已提交
376

377
 out:
J
Jan Kara 已提交
378
	mnt_drop_write_file(parfilp);
379
	dput(dentry);
380
	return error;
L
Linus Torvalds 已提交
381 382 383 384
}

STATIC int
xfs_attrlist_by_handle(
385 386
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
387
{
388
	int			error = -ENOMEM;
L
Linus Torvalds 已提交
389 390
	attrlist_cursor_kern_t	*cursor;
	xfs_fsop_attrlist_handlereq_t al_hreq;
391
	struct dentry		*dentry;
L
Linus Torvalds 已提交
392 393 394
	char			*kbuf;

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

402 403 404 405
	/*
	 * Reject flags, only allow namespaces.
	 */
	if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
E
Eric Sandeen 已提交
406
		return -EINVAL;
407

408 409 410
	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
411

412 413 414
	kbuf = kmem_zalloc_large(al_hreq.buflen, KM_SLEEP);
	if (!kbuf)
		goto out_dput;
L
Linus Torvalds 已提交
415 416

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

	if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
		error = -EFAULT;

425 426 427
out_kfree:
	kmem_free(kbuf);
out_dput:
428 429
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
430 431
}

432
int
L
Linus Torvalds 已提交
433
xfs_attrmulti_attr_get(
434
	struct inode		*inode,
435 436
	unsigned char		*name,
	unsigned char		__user *ubuf,
L
Linus Torvalds 已提交
437 438 439
	__uint32_t		*len,
	__uint32_t		flags)
{
440
	unsigned char		*kbuf;
D
Dave Chinner 已提交
441
	int			error = -EFAULT;
442

443
	if (*len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
444
		return -EINVAL;
445 446
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
D
Dave Chinner 已提交
447
		return -ENOMEM;
L
Linus Torvalds 已提交
448

449
	error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
L
Linus Torvalds 已提交
450 451 452 453
	if (error)
		goto out_kfree;

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

456 457
out_kfree:
	kmem_free(kbuf);
L
Linus Torvalds 已提交
458 459 460
	return error;
}

461
int
L
Linus Torvalds 已提交
462
xfs_attrmulti_attr_set(
463
	struct inode		*inode,
464 465
	unsigned char		*name,
	const unsigned char	__user *ubuf,
L
Linus Torvalds 已提交
466 467 468
	__uint32_t		len,
	__uint32_t		flags)
{
469
	unsigned char		*kbuf;
470
	int			error;
L
Linus Torvalds 已提交
471

472
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
473
		return -EPERM;
474
	if (len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
475
		return -EINVAL;
L
Linus Torvalds 已提交
476

L
Li Zefan 已提交
477 478 479
	kbuf = memdup_user(ubuf, len);
	if (IS_ERR(kbuf))
		return PTR_ERR(kbuf);
480

481
	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
482 483
	if (!error)
		xfs_forget_acl(inode, name, flags);
484 485
	kfree(kbuf);
	return error;
L
Linus Torvalds 已提交
486 487
}

488
int
L
Linus Torvalds 已提交
489
xfs_attrmulti_attr_remove(
490
	struct inode		*inode,
491
	unsigned char		*name,
L
Linus Torvalds 已提交
492 493
	__uint32_t		flags)
{
494 495
	int			error;

496
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
497
		return -EPERM;
498 499 500 501
	error = xfs_attr_remove(XFS_I(inode), name, flags);
	if (!error)
		xfs_forget_acl(inode, name, flags);
	return error;
L
Linus Torvalds 已提交
502 503 504 505
}

STATIC int
xfs_attrmulti_by_handle(
506
	struct file		*parfilp,
507
	void			__user *arg)
L
Linus Torvalds 已提交
508 509 510 511
{
	int			error;
	xfs_attr_multiop_t	*ops;
	xfs_fsop_attrmulti_handlereq_t am_hreq;
512
	struct dentry		*dentry;
L
Linus Torvalds 已提交
513
	unsigned int		i, size;
514
	unsigned char		*attr_name;
L
Linus Torvalds 已提交
515 516

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
517
		return -EPERM;
L
Linus Torvalds 已提交
518
	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
E
Eric Sandeen 已提交
519
		return -EFAULT;
L
Linus Torvalds 已提交
520

521 522 523 524
	/* overflow check */
	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
		return -E2BIG;

525 526 527
	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
528

D
Dave Chinner 已提交
529
	error = -E2BIG;
C
Christoph Hellwig 已提交
530
	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
L
Linus Torvalds 已提交
531
	if (!size || size > 16 * PAGE_SIZE)
532
		goto out_dput;
L
Linus Torvalds 已提交
533

L
Li Zefan 已提交
534 535
	ops = memdup_user(am_hreq.ops, size);
	if (IS_ERR(ops)) {
D
Dave Chinner 已提交
536
		error = PTR_ERR(ops);
537
		goto out_dput;
L
Li Zefan 已提交
538
	}
L
Linus Torvalds 已提交
539

D
Dave Chinner 已提交
540
	error = -ENOMEM;
L
Linus Torvalds 已提交
541 542 543 544 545 546
	attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
	if (!attr_name)
		goto out_kfree_ops;

	error = 0;
	for (i = 0; i < am_hreq.opcount; i++) {
547
		ops[i].am_error = strncpy_from_user((char *)attr_name,
L
Linus Torvalds 已提交
548 549
				ops[i].am_attrname, MAXNAMELEN);
		if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
D
Dave Chinner 已提交
550
			error = -ERANGE;
L
Linus Torvalds 已提交
551 552 553 554 555
		if (ops[i].am_error < 0)
			break;

		switch (ops[i].am_opcode) {
		case ATTR_OP_GET:
556
			ops[i].am_error = xfs_attrmulti_attr_get(
557
					d_inode(dentry), attr_name,
558 559
					ops[i].am_attrvalue, &ops[i].am_length,
					ops[i].am_flags);
L
Linus Torvalds 已提交
560 561
			break;
		case ATTR_OP_SET:
562
			ops[i].am_error = mnt_want_write_file(parfilp);
563 564
			if (ops[i].am_error)
				break;
565
			ops[i].am_error = xfs_attrmulti_attr_set(
566
					d_inode(dentry), attr_name,
567 568
					ops[i].am_attrvalue, ops[i].am_length,
					ops[i].am_flags);
A
Al Viro 已提交
569
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
570 571
			break;
		case ATTR_OP_REMOVE:
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_remove(
576
					d_inode(dentry), attr_name,
577
					ops[i].am_flags);
A
Al Viro 已提交
578
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
579 580
			break;
		default:
D
Dave Chinner 已提交
581
			ops[i].am_error = -EINVAL;
L
Linus Torvalds 已提交
582 583 584 585
		}
	}

	if (copy_to_user(am_hreq.ops, ops, size))
D
Dave Chinner 已提交
586
		error = -EFAULT;
L
Linus Torvalds 已提交
587 588 589 590

	kfree(attr_name);
 out_kfree_ops:
	kfree(ops);
591 592
 out_dput:
	dput(dentry);
D
Dave Chinner 已提交
593
	return error;
L
Linus Torvalds 已提交
594 595
}

596
int
L
Linus Torvalds 已提交
597 598 599
xfs_ioc_space(
	struct file		*filp,
	unsigned int		cmd,
600
	xfs_flock64_t		*bf)
L
Linus Torvalds 已提交
601
{
602 603
	struct inode		*inode = file_inode(filp);
	struct xfs_inode	*ip = XFS_I(inode);
604
	struct iattr		iattr;
605
	enum xfs_prealloc_flags	flags = 0;
606
	uint			iolock = XFS_IOLOCK_EXCL;
L
Linus Torvalds 已提交
607 608
	int			error;

609 610 611 612 613 614
	/*
	 * 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 已提交
615
		return -EPERM;
616

617
	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
E
Eric Sandeen 已提交
618
		return -EPERM;
L
Linus Torvalds 已提交
619

620
	if (!(filp->f_mode & FMODE_WRITE))
E
Eric Sandeen 已提交
621
		return -EBADF;
L
Linus Torvalds 已提交
622

623
	if (!S_ISREG(inode->i_mode))
E
Eric Sandeen 已提交
624
		return -EINVAL;
L
Linus Torvalds 已提交
625

626 627
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
628
	if (filp->f_mode & FMODE_NOCMTIME)
629 630
		flags |= XFS_PREALLOC_INVISIBLE;

J
Jan Kara 已提交
631 632 633
	error = mnt_want_write_file(filp);
	if (error)
		return error;
634

635
	xfs_ilock(ip, iolock);
636
	error = xfs_break_layouts(inode, &iolock, false);
637 638
	if (error)
		goto out_unlock;
639

640 641 642
	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
	iolock |= XFS_MMAPLOCK_EXCL;

643 644 645 646 647 648 649 650 651 652
	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 已提交
653
		error = -EINVAL;
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
		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 已提交
670
			error = -EINVAL;
671 672 673 674 675 676 677 678 679
			goto out_unlock;
		}
		break;
	default:
		bf->l_len = 0;
		break;
	}

	if (bf->l_start < 0 ||
680
	    bf->l_start > inode->i_sb->s_maxbytes ||
681
	    bf->l_start + bf->l_len < 0 ||
682
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
D
Dave Chinner 已提交
683
		error = -EINVAL;
684 685 686 687 688
		goto out_unlock;
	}

	switch (cmd) {
	case XFS_IOC_ZERO_RANGE:
689
		flags |= XFS_PREALLOC_SET;
690 691 692 693
		error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
		break;
	case XFS_IOC_RESVSP:
	case XFS_IOC_RESVSP64:
694
		flags |= XFS_PREALLOC_SET;
695 696 697 698 699 700 701 702 703 704 705
		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:
706
		flags |= XFS_PREALLOC_CLEAR;
707 708 709 710 711 712 713 714 715 716 717 718 719 720
		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;

		error = xfs_setattr_size(ip, &iattr);
		break;
	default:
		ASSERT(0);
D
Dave Chinner 已提交
721
		error = -EINVAL;
722 723 724 725 726
	}

	if (error)
		goto out_unlock;

727
	error = xfs_update_prealloc_flags(ip, flags);
728 729

out_unlock:
730
	xfs_iunlock(ip, iolock);
J
Jan Kara 已提交
731
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
732
	return error;
L
Linus Torvalds 已提交
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
}

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 已提交
754
		return -EIO;
L
Linus Torvalds 已提交
755 756

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
E
Eric Sandeen 已提交
757
		return -EFAULT;
L
Linus Torvalds 已提交
758 759

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

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

765
	if (bulkreq.ubuffer == NULL)
E
Eric Sandeen 已提交
766
		return -EINVAL;
767

L
Linus Torvalds 已提交
768 769
	if (cmd == XFS_IOC_FSINUMBERS)
		error = xfs_inumbers(mp, &inlast, &count,
770
					bulkreq.ubuffer, xfs_inumbers_fmt);
L
Linus Torvalds 已提交
771
	else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
772 773
		error = xfs_bulkstat_one(mp, inlast, bulkreq.ubuffer,
					sizeof(xfs_bstat_t), NULL, &done);
774
	else	/* XFS_IOC_FSBULKSTAT */
775 776 777
		error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one,
				     sizeof(xfs_bstat_t), bulkreq.ubuffer,
				     &done);
L
Linus Torvalds 已提交
778 779

	if (error)
D
Dave Chinner 已提交
780
		return error;
L
Linus Torvalds 已提交
781 782 783 784

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

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
E
Eric Sandeen 已提交
788
			return -EFAULT;
L
Linus Torvalds 已提交
789 790 791 792 793 794 795 796 797 798
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry_v1(
	xfs_mount_t		*mp,
	void			__user *arg)
{
799
	xfs_fsop_geom_t         fsgeo;
L
Linus Torvalds 已提交
800 801
	int			error;

802
	error = xfs_fs_geometry(mp, &fsgeo, 3);
L
Linus Torvalds 已提交
803
	if (error)
D
Dave Chinner 已提交
804
		return error;
L
Linus Torvalds 已提交
805

806 807 808 809 810 811
	/*
	 * 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 已提交
812
		return -EFAULT;
L
Linus Torvalds 已提交
813 814 815 816 817 818 819 820 821 822 823 824 825
	return 0;
}

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

	error = xfs_fs_geometry(mp, &fsgeo, 4);
	if (error)
D
Dave Chinner 已提交
826
		return error;
L
Linus Torvalds 已提交
827 828

	if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
E
Eric Sandeen 已提交
829
		return -EFAULT;
L
Linus Torvalds 已提交
830 831 832 833 834 835 836 837 838 839 840 841 842 843
	return 0;
}

/*
 * Linux extended inode flags interface.
 */

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

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

	return xflags;
}

STATIC unsigned int
xfs_di2lxflags(
	__uint16_t	di_flags)
{
	unsigned int	flags = 0;

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
875
		flags |= FS_IMMUTABLE_FL;
L
Linus Torvalds 已提交
876
	if (di_flags & XFS_DIFLAG_APPEND)
877
		flags |= FS_APPEND_FL;
L
Linus Torvalds 已提交
878
	if (di_flags & XFS_DIFLAG_SYNC)
879
		flags |= FS_SYNC_FL;
L
Linus Torvalds 已提交
880
	if (di_flags & XFS_DIFLAG_NOATIME)
881
		flags |= FS_NOATIME_FL;
L
Linus Torvalds 已提交
882
	if (di_flags & XFS_DIFLAG_NODUMP)
883
		flags |= FS_NODUMP_FL;
L
Linus Torvalds 已提交
884 885 886
	return flags;
}

887 888 889 890 891 892 893 894
STATIC int
xfs_ioc_fsgetxattr(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;

895 896
	memset(&fa, 0, sizeof(struct fsxattr));

897 898 899
	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;
900
	fa.fsx_projid = xfs_get_projid(ip);
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924

	if (attr) {
		if (ip->i_afp) {
			if (ip->i_afp->if_flags & XFS_IFEXTENTS)
				fa.fsx_nextents = ip->i_afp->if_bytes /
							sizeof(xfs_bmbt_rec_t);
			else
				fa.fsx_nextents = ip->i_d.di_anextents;
		} else
			fa.fsx_nextents = 0;
	} else {
		if (ip->i_df.if_flags & XFS_IFEXTENTS)
			fa.fsx_nextents = ip->i_df.if_bytes /
						sizeof(xfs_bmbt_rec_t);
		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;
}

925 926 927 928 929 930
STATIC void
xfs_set_diflags(
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	unsigned int		di_flags;
931
	uint64_t		di_flags2;
932 933 934

	/* can't set PREALLOC this way, just preserve it */
	di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
935
	if (xflags & FS_XFLAG_IMMUTABLE)
936
		di_flags |= XFS_DIFLAG_IMMUTABLE;
937
	if (xflags & FS_XFLAG_APPEND)
938
		di_flags |= XFS_DIFLAG_APPEND;
939
	if (xflags & FS_XFLAG_SYNC)
940
		di_flags |= XFS_DIFLAG_SYNC;
941
	if (xflags & FS_XFLAG_NOATIME)
942
		di_flags |= XFS_DIFLAG_NOATIME;
943
	if (xflags & FS_XFLAG_NODUMP)
944
		di_flags |= XFS_DIFLAG_NODUMP;
945
	if (xflags & FS_XFLAG_NODEFRAG)
946
		di_flags |= XFS_DIFLAG_NODEFRAG;
947
	if (xflags & FS_XFLAG_FILESTREAM)
948
		di_flags |= XFS_DIFLAG_FILESTREAM;
D
Dave Chinner 已提交
949
	if (S_ISDIR(VFS_I(ip)->i_mode)) {
950
		if (xflags & FS_XFLAG_RTINHERIT)
951
			di_flags |= XFS_DIFLAG_RTINHERIT;
952
		if (xflags & FS_XFLAG_NOSYMLINKS)
953
			di_flags |= XFS_DIFLAG_NOSYMLINKS;
954
		if (xflags & FS_XFLAG_EXTSZINHERIT)
955
			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
956
		if (xflags & FS_XFLAG_PROJINHERIT)
957
			di_flags |= XFS_DIFLAG_PROJINHERIT;
D
Dave Chinner 已提交
958
	} else if (S_ISREG(VFS_I(ip)->i_mode)) {
959
		if (xflags & FS_XFLAG_REALTIME)
960
			di_flags |= XFS_DIFLAG_REALTIME;
961
		if (xflags & FS_XFLAG_EXTSIZE)
962 963 964
			di_flags |= XFS_DIFLAG_EXTSIZE;
	}
	ip->i_d.di_flags = di_flags;
965 966 967 968 969 970 971 972 973 974 975

	/* diflags2 only valid for v3 inodes. */
	if (ip->i_d.di_version < 3)
		return;

	di_flags2 = 0;
	if (xflags & FS_XFLAG_DAX)
		di_flags2 |= XFS_DIFLAG2_DAX;

	ip->i_d.di_flags2 = di_flags2;

976 977
}

978 979 980 981
STATIC void
xfs_diflags_to_linux(
	struct xfs_inode	*ip)
{
982
	struct inode		*inode = VFS_I(ip);
983 984
	unsigned int		xflags = xfs_ip2xflags(ip);

985
	if (xflags & FS_XFLAG_IMMUTABLE)
986 987 988
		inode->i_flags |= S_IMMUTABLE;
	else
		inode->i_flags &= ~S_IMMUTABLE;
989
	if (xflags & FS_XFLAG_APPEND)
990 991 992
		inode->i_flags |= S_APPEND;
	else
		inode->i_flags &= ~S_APPEND;
993
	if (xflags & FS_XFLAG_SYNC)
994 995 996
		inode->i_flags |= S_SYNC;
	else
		inode->i_flags &= ~S_SYNC;
997
	if (xflags & FS_XFLAG_NOATIME)
998 999 1000
		inode->i_flags |= S_NOATIME;
	else
		inode->i_flags &= ~S_NOATIME;
1001 1002 1003 1004 1005
	if (xflags & FS_XFLAG_DAX)
		inode->i_flags |= S_DAX;
	else
		inode->i_flags &= ~S_DAX;

1006
}
1007

1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
static int
xfs_ioctl_setattr_xflags(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

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

	/* If realtime flag is set then must have realtime device */
1022
	if (fa->fsx_xflags & FS_XFLAG_REALTIME) {
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
		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;
	}

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

	xfs_set_diflags(ip, fa->fsx_xflags);
	xfs_diflags_to_linux(ip);
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1041
	XFS_STATS_INC(mp, xs_ig_attrchg);
1042 1043 1044
	return 0;
}

1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
/*
 * 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);
	int			error;

	*join_flags = 0;

1064 1065
	/*
	 * It is only valid to set the DAX flag on regular files and
1066 1067
	 * directories on filesystems where the block size is equal to the page
	 * size. On directories it serves as an inherit hint.
1068
	 */
1069 1070 1071 1072 1073 1074
	if (fa->fsx_xflags & FS_XFLAG_DAX) {
		if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
			return -EINVAL;
		if (ip->i_mount->m_sb.sb_blocksize != PAGE_SIZE)
			return -EINVAL;
	}
1075

1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
	/* 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;
1092
	return 0;
1093 1094 1095 1096 1097

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

1098 1099
}

1100 1101 1102 1103 1104
/*
 * 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.
1105 1106 1107 1108 1109 1110
 *
 * 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.
1111 1112 1113
 */
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
1114 1115
	struct xfs_inode	*ip,
	int			join_flags)
1116 1117 1118
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
1119
	int			error = -EROFS;
1120 1121

	if (mp->m_flags & XFS_MOUNT_RDONLY)
1122 1123
		goto out_unlock;
	error = -EIO;
1124
	if (XFS_FORCED_SHUTDOWN(mp))
1125
		goto out_unlock;
1126

1127
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1128
	if (error)
1129
		return ERR_PTR(error);
1130 1131

	xfs_ilock(ip, XFS_ILOCK_EXCL);
1132 1133
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags);
	join_flags = 0;
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151

	/*
	 * 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:
1152
	xfs_trans_cancel(tp);
1153 1154 1155
out_unlock:
	if (join_flags)
		xfs_iunlock(ip, join_flags);
1156 1157 1158
	return ERR_PTR(error);
}

1159 1160 1161 1162
/*
 * extent size hint validation is somewhat cumbersome. Rules are:
 *
 * 1. extent size hint is only valid for directories and regular files
1163 1164
 * 2. FS_XFLAG_EXTSIZE is only valid for regular files
 * 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
1165 1166 1167 1168 1169 1170 1171 1172
 * 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.
 */
1173
static int
1174 1175 1176 1177 1178 1179
xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

D
Dave Chinner 已提交
1180
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode))
1181 1182
		return -EINVAL;

1183
	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
D
Dave Chinner 已提交
1184
	    !S_ISDIR(VFS_I(ip)->i_mode))
1185 1186
		return -EINVAL;

D
Dave Chinner 已提交
1187
	if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents &&
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
	    ((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) ||
1200
		    (fa->fsx_xflags & FS_XFLAG_REALTIME)) {
1201 1202 1203 1204 1205 1206 1207 1208 1209
			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;
1210
	} else
1211
		fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
1212

1213 1214 1215
	return 0;
}

1216
static int
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
xfs_ioctl_setattr_check_projid(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	/* Disallow 32bit project ids if projid32bit feature is not enabled. */
	if (fa->fsx_projid > (__uint16_t)-1 &&
	    !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;
1236
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1237 1238 1239 1240 1241
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1242 1243 1244 1245

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
1246
	struct fsxattr		*fa)
1247 1248 1249
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
C
Christoph Hellwig 已提交
1250
	struct xfs_dquot	*udqp = NULL;
1251
	struct xfs_dquot	*pdqp = NULL;
1252 1253
	struct xfs_dquot	*olddquot = NULL;
	int			code;
1254
	int			join_flags = 0;
1255

C
Christoph Hellwig 已提交
1256
	trace_xfs_ioctl_setattr(ip);
1257

1258 1259 1260
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1261

1262 1263 1264 1265 1266 1267 1268 1269
	/*
	 * 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.
	 */
1270
	if (XFS_IS_QUOTA_ON(mp)) {
C
Christoph Hellwig 已提交
1271
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1272
					 ip->i_d.di_gid, fa->fsx_projid,
1273
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1274 1275 1276 1277
		if (code)
			return code;
	}

1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
	/*
	 * 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);
1290 1291 1292
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1293 1294 1295
	}


1296 1297 1298 1299 1300
	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 */
1301
			goto error_trans_cancel;
1302 1303
	}

1304 1305 1306
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1307

1308 1309
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1310
		goto error_trans_cancel;
1311 1312

	/*
1313 1314 1315 1316 1317
	 * 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()
1318 1319
	 */

D
Dave Chinner 已提交
1320
	if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
1321
	    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
D
Dave Chinner 已提交
1322
		VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
1323

1324 1325 1326 1327 1328 1329 1330 1331
	/* 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);
1332
	}
1333

1334 1335 1336 1337 1338
	/*
	 * 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.
	 */
1339 1340 1341 1342
	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;
1343

1344
	code = xfs_trans_commit(tp);
1345 1346 1347 1348

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1349 1350
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1351
	xfs_qm_dqrele(pdqp);
1352

C
Christoph Hellwig 已提交
1353
	return code;
1354

1355
error_trans_cancel:
1356
	xfs_trans_cancel(tp);
1357
error_free_dquots:
C
Christoph Hellwig 已提交
1358
	xfs_qm_dqrele(udqp);
1359
	xfs_qm_dqrele(pdqp);
1360 1361 1362
	return code;
}

L
Linus Torvalds 已提交
1363
STATIC int
L
Lachlan McIlroy 已提交
1364
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1365 1366 1367 1368 1369
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1370
	int error;
L
Lachlan McIlroy 已提交
1371 1372 1373

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

J
Jan Kara 已提交
1375 1376 1377
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1378
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1379
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1380
	return error;
L
Lachlan McIlroy 已提交
1381
}
L
Linus Torvalds 已提交
1382

L
Lachlan McIlroy 已提交
1383 1384 1385 1386 1387 1388
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1389

L
Lachlan McIlroy 已提交
1390 1391 1392 1393 1394
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1395

L
Lachlan McIlroy 已提交
1396 1397
STATIC int
xfs_ioc_setxflags(
1398
	struct xfs_inode	*ip,
L
Lachlan McIlroy 已提交
1399 1400 1401
	struct file		*filp,
	void			__user *arg)
{
1402
	struct xfs_trans	*tp;
1403
	struct fsxattr		fa;
L
Lachlan McIlroy 已提交
1404
	unsigned int		flags;
1405
	int			join_flags = 0;
1406
	int			error;
L
Linus Torvalds 已提交
1407

L
Lachlan McIlroy 已提交
1408 1409
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1410

L
Lachlan McIlroy 已提交
1411 1412 1413 1414
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1415

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

J
Jan Kara 已提交
1418 1419 1420
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1421

1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433
	/*
	 * 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);
1434 1435 1436 1437 1438 1439 1440
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1441
		xfs_trans_cancel(tp);
1442 1443 1444
		goto out_drop_write;
	}

1445
	error = xfs_trans_commit(tp);
1446
out_drop_write:
J
Jan Kara 已提交
1447
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1448
	return error;
L
Linus Torvalds 已提交
1449 1450
}

1451 1452 1453
STATIC int
xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
{
1454
	struct getbmap __user	*base = (struct getbmap __user *)*ap;
1455 1456 1457

	/* copy only getbmap portion (not getbmapx) */
	if (copy_to_user(base, bmv, sizeof(struct getbmap)))
D
Dave Chinner 已提交
1458
		return -EFAULT;
1459 1460 1461 1462 1463

	*ap += sizeof(struct getbmap);
	return 0;
}

L
Linus Torvalds 已提交
1464 1465
STATIC int
xfs_ioc_getbmap(
1466
	struct file		*file,
L
Linus Torvalds 已提交
1467 1468 1469
	unsigned int		cmd,
	void			__user *arg)
{
1470
	struct getbmapx		bmx;
L
Linus Torvalds 已提交
1471 1472
	int			error;

1473
	if (copy_from_user(&bmx, arg, sizeof(struct getbmapx)))
E
Eric Sandeen 已提交
1474
		return -EFAULT;
L
Linus Torvalds 已提交
1475

1476
	if (bmx.bmv_count < 2)
E
Eric Sandeen 已提交
1477
		return -EINVAL;
L
Linus Torvalds 已提交
1478

1479
	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
1480
	if (file->f_mode & FMODE_NOCMTIME)
1481
		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
L
Linus Torvalds 已提交
1482

1483
	error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, xfs_getbmap_format,
1484
			    (__force struct getbmap *)arg+1);
L
Linus Torvalds 已提交
1485
	if (error)
D
Dave Chinner 已提交
1486
		return error;
L
Linus Torvalds 已提交
1487

1488 1489
	/* copy back header - only size of getbmap */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
E
Eric Sandeen 已提交
1490
		return -EFAULT;
L
Linus Torvalds 已提交
1491 1492 1493
	return 0;
}

1494 1495 1496
STATIC int
xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
{
1497
	struct getbmapx __user	*base = (struct getbmapx __user *)*ap;
1498 1499

	if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
D
Dave Chinner 已提交
1500
		return -EFAULT;
1501 1502 1503 1504 1505

	*ap += sizeof(struct getbmapx);
	return 0;
}

L
Linus Torvalds 已提交
1506 1507
STATIC int
xfs_ioc_getbmapx(
1508
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1509 1510 1511 1512 1513 1514
	void			__user *arg)
{
	struct getbmapx		bmx;
	int			error;

	if (copy_from_user(&bmx, arg, sizeof(bmx)))
E
Eric Sandeen 已提交
1515
		return -EFAULT;
L
Linus Torvalds 已提交
1516 1517

	if (bmx.bmv_count < 2)
E
Eric Sandeen 已提交
1518
		return -EINVAL;
L
Linus Torvalds 已提交
1519

1520
	if (bmx.bmv_iflags & (~BMV_IF_VALID))
E
Eric Sandeen 已提交
1521
		return -EINVAL;
L
Linus Torvalds 已提交
1522

1523
	error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
1524
			    (__force struct getbmapx *)arg+1);
L
Linus Torvalds 已提交
1525
	if (error)
D
Dave Chinner 已提交
1526
		return error;
L
Linus Torvalds 已提交
1527

1528 1529
	/* copy back header */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
E
Eric Sandeen 已提交
1530
		return -EFAULT;
L
Linus Torvalds 已提交
1531 1532 1533

	return 0;
}
L
Lachlan McIlroy 已提交
1534

D
Dave Chinner 已提交
1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545
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 已提交
1546
		error = -EINVAL;
D
Dave Chinner 已提交
1547 1548 1549 1550 1551 1552
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1553
		error = -EBADF;
D
Dave Chinner 已提交
1554 1555 1556 1557 1558
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1559
		error = -EINVAL;
D
Dave Chinner 已提交
1560 1561 1562 1563 1564 1565
		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 已提交
1566
		error = -EBADF;
D
Dave Chinner 已提交
1567 1568 1569 1570 1571
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1572
		error = -EINVAL;
D
Dave Chinner 已提交
1573 1574 1575
		goto out_put_tmp_file;
	}

1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
	/*
	 * 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 已提交
1587 1588 1589 1590
	ip = XFS_I(file_inode(f.file));
	tip = XFS_I(file_inode(tmp.file));

	if (ip->i_mount != tip->i_mount) {
D
Dave Chinner 已提交
1591
		error = -EINVAL;
D
Dave Chinner 已提交
1592 1593 1594 1595
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1596
		error = -EINVAL;
D
Dave Chinner 已提交
1597 1598 1599 1600
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1601
		error = -EIO;
D
Dave Chinner 已提交
1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614
		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;
}

1615 1616 1617 1618 1619 1620 1621 1622
/*
 * 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 已提交
1623 1624
	struct file		*filp,
	unsigned int		cmd,
1625
	unsigned long		p)
L
Lachlan McIlroy 已提交
1626
{
A
Al Viro 已提交
1627
	struct inode		*inode = file_inode(filp);
1628 1629 1630
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	void			__user *arg = (void __user *)p;
L
Lachlan McIlroy 已提交
1631 1632
	int			error;

C
Christoph Hellwig 已提交
1633
	trace_xfs_file_ioctl(ip);
1634 1635

	switch (cmd) {
C
Christoph Hellwig 已提交
1636 1637
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
L
Lachlan McIlroy 已提交
1638 1639 1640 1641 1642 1643 1644
	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 已提交
1645 1646
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1647
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1648

1649
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1650
			return -EFAULT;
1651
		return xfs_ioc_space(filp, cmd, &bf);
1652
	}
L
Lachlan McIlroy 已提交
1653 1654 1655 1656 1657 1658
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1659
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1660 1661 1662
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

		if (copy_to_user(arg, &da, sizeof(da)))
E
Eric Sandeen 已提交
1663
			return -EFAULT;
L
Lachlan McIlroy 已提交
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
		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 已提交
1685 1686
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(ip, filp, arg);
L
Lachlan McIlroy 已提交
1687
	case XFS_IOC_GETXFLAGS:
L
Lachlan McIlroy 已提交
1688
		return xfs_ioc_getxflags(ip, arg);
L
Lachlan McIlroy 已提交
1689
	case XFS_IOC_SETXFLAGS:
L
Lachlan McIlroy 已提交
1690
		return xfs_ioc_setxflags(ip, filp, arg);
L
Lachlan McIlroy 已提交
1691 1692 1693 1694 1695

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1698 1699 1700 1701
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1702 1703
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1704
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1705
		return error;
L
Lachlan McIlroy 已提交
1706 1707 1708 1709
	}

	case XFS_IOC_GETBMAP:
	case XFS_IOC_GETBMAPA:
1710
		return xfs_ioc_getbmap(filp, cmd, arg);
L
Lachlan McIlroy 已提交
1711 1712 1713 1714 1715 1716

	case XFS_IOC_GETBMAPX:
		return xfs_ioc_getbmapx(ip, arg);

	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
1717 1718
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1719

1720
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1721
			return -EFAULT;
1722 1723 1724 1725
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1726

1727
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1728
			return -EFAULT;
1729
		return xfs_open_by_handle(filp, &hreq);
1730
	}
L
Lachlan McIlroy 已提交
1731
	case XFS_IOC_FSSETDM_BY_HANDLE:
1732
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1733

1734 1735
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1736

1737
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1738
			return -EFAULT;
1739
		return xfs_readlink_by_handle(filp, &hreq);
1740
	}
L
Lachlan McIlroy 已提交
1741
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1742
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1743 1744

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1745
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1746 1747

	case XFS_IOC_SWAPEXT: {
1748 1749 1750
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
1751
			return -EFAULT;
J
Jan Kara 已提交
1752 1753 1754
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
1755
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
1756
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1757
		return error;
L
Lachlan McIlroy 已提交
1758 1759 1760 1761 1762 1763 1764
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1765
			return error;
L
Lachlan McIlroy 已提交
1766 1767

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1768
			return -EFAULT;
L
Lachlan McIlroy 已提交
1769 1770 1771 1772 1773 1774 1775 1776 1777 1778
		return 0;
	}

	case XFS_IOC_SET_RESBLKS: {
		xfs_fsop_resblks_t inout;
		__uint64_t	   in;

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

E
Eric Sandeen 已提交
1779
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1780
			return -EROFS;
E
Eric Sandeen 已提交
1781

L
Lachlan McIlroy 已提交
1782
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1783
			return -EFAULT;
L
Lachlan McIlroy 已提交
1784

J
Jan Kara 已提交
1785 1786 1787 1788
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1789 1790 1791
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
1792
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
1793
		if (error)
D
Dave Chinner 已提交
1794
			return error;
L
Lachlan McIlroy 已提交
1795 1796

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
1797
			return -EFAULT;
L
Lachlan McIlroy 已提交
1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808
		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 已提交
1809
			return error;
L
Lachlan McIlroy 已提交
1810 1811

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1812
			return -EFAULT;
L
Lachlan McIlroy 已提交
1813 1814 1815 1816 1817 1818 1819 1820

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

J
Jan Kara 已提交
1823 1824 1825
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1826
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
1827
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1828
		return error;
L
Lachlan McIlroy 已提交
1829 1830 1831 1832 1833 1834
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

J
Jan Kara 已提交
1837 1838 1839
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1840
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
1841
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1842
		return error;
L
Lachlan McIlroy 已提交
1843 1844 1845 1846 1847 1848
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
1851 1852 1853
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1854
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
1855
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1856
		return error;
L
Lachlan McIlroy 已提交
1857 1858 1859 1860 1861 1862 1863 1864 1865
	}

	case XFS_IOC_GOINGDOWN: {
		__uint32_t in;

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

		if (get_user(in, (__uint32_t __user *)arg))
E
Eric Sandeen 已提交
1866
			return -EFAULT;
L
Lachlan McIlroy 已提交
1867

D
Dave Chinner 已提交
1868
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
1869 1870 1871 1872 1873 1874 1875 1876 1877
	}

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

D
Dave Chinner 已提交
1880
		return xfs_errortag_add(in.errtag, mp);
L
Lachlan McIlroy 已提交
1881 1882 1883 1884 1885 1886
	}

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

D
Dave Chinner 已提交
1887
		return xfs_errortag_clearall(mp, 1);
L
Lachlan McIlroy 已提交
1888

1889
	case XFS_IOC_FREE_EOFBLOCKS: {
1890 1891
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
1892

1893 1894 1895 1896
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

1899
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
1900
			return -EFAULT;
1901

1902 1903
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
1904
			return error;
1905

D
Dave Chinner 已提交
1906
		return xfs_icache_free_eofblocks(mp, &keofb);
1907 1908
	}

L
Lachlan McIlroy 已提交
1909 1910 1911 1912
	default:
		return -ENOTTY;
	}
}