xfs_ioctl.c 42.4 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2 3
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
L
Linus Torvalds 已提交
4
 *
5 6
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
L
Linus Torvalds 已提交
7 8
 * published by the Free Software Foundation.
 *
9 10 11 12
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
L
Linus Torvalds 已提交
13
 *
14 15 16
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
L
Linus Torvalds 已提交
17 18 19
 */
#include "xfs.h"
#include "xfs_fs.h"
20
#include "xfs_shared.h"
21 22 23
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
L
Linus Torvalds 已提交
24 25
#include "xfs_mount.h"
#include "xfs_inode.h"
26
#include "xfs_ioctl.h"
27
#include "xfs_alloc.h"
L
Linus Torvalds 已提交
28 29
#include "xfs_rtalloc.h"
#include "xfs_itable.h"
30
#include "xfs_error.h"
L
Linus Torvalds 已提交
31
#include "xfs_attr.h"
32
#include "xfs_bmap.h"
D
Dave Chinner 已提交
33
#include "xfs_bmap_util.h"
L
Linus Torvalds 已提交
34
#include "xfs_fsops.h"
C
Christoph Hellwig 已提交
35
#include "xfs_discard.h"
36
#include "xfs_quota.h"
37
#include "xfs_export.h"
C
Christoph Hellwig 已提交
38
#include "xfs_trace.h"
39
#include "xfs_icache.h"
D
Dave Chinner 已提交
40
#include "xfs_symlink.h"
41
#include "xfs_trans.h"
42
#include "xfs_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
xfs_ioc_space(
598
	struct xfs_inode	*ip,
599
	struct inode		*inode,
L
Linus Torvalds 已提交
600 601 602
	struct file		*filp,
	int			ioflags,
	unsigned int		cmd,
603
	xfs_flock64_t		*bf)
L
Linus Torvalds 已提交
604
{
605
	struct iattr		iattr;
606
	enum xfs_prealloc_flags	flags = 0;
607
	uint			iolock = XFS_IOLOCK_EXCL;
L
Linus Torvalds 已提交
608 609
	int			error;

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

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

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

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

627 628
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
629
	if (ioflags & XFS_IO_INVIS)
630 631
		flags |= XFS_PREALLOC_INVISIBLE;

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

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

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

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

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

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

	if (error)
		goto out_unlock;

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

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

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

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

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

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

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

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

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

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

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

	return 0;
}

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

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

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

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

/*
 * Linux extended inode flags interface.
 */

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

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

	return xflags;
}

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

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

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

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

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

	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;
}

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

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

	/* 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;

977 978
}

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

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

1007
}
1008

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
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) &&
1019
	    XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & FS_XFLAG_REALTIME))
1020 1021 1022
		return -EINVAL;

	/* If realtime flag is set then must have realtime device */
1023
	if (fa->fsx_xflags & FS_XFLAG_REALTIME) {
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
		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)) ||
1034
	     (fa->fsx_xflags & (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND))) &&
1035 1036 1037 1038 1039 1040 1041
	    !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);
1042
	XFS_STATS_INC(mp, xs_ig_attrchg);
1043 1044 1045
	return 0;
}

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

1065 1066
	/*
	 * It is only valid to set the DAX flag on regular files and
1067 1068
	 * directories on filesystems where the block size is equal to the page
	 * size. On directories it serves as an inherit hint.
1069
	 */
1070 1071 1072 1073 1074 1075
	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;
	}
1076

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

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

1099 1100
}

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

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

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

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

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

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

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

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

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

1214 1215 1216
	return 0;
}

1217
static int
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
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;
1237
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1238 1239 1240 1241 1242
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1243 1244 1245 1246

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

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

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

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

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


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

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

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

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

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

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

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

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

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

C
Christoph Hellwig 已提交
1354
	return code;
1355

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1481
	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
D
Dave Chinner 已提交
1482
	if (ioflags & XFS_IO_INVIS)
1483
		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
L
Linus Torvalds 已提交
1484

1485
	error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
1486
			    (__force struct getbmap *)arg+1);
L
Linus Torvalds 已提交
1487
	if (error)
D
Dave Chinner 已提交
1488
		return error;
L
Linus Torvalds 已提交
1489

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

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

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

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

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

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

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

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

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

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

	return 0;
}
L
Lachlan McIlroy 已提交
1536

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

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

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

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1574
		error = -EINVAL;
D
Dave Chinner 已提交
1575 1576 1577 1578 1579 1580 1581
		goto out_put_tmp_file;
	}

	ip = XFS_I(file_inode(f.file));
	tip = XFS_I(file_inode(tmp.file));

	if (ip->i_mount != tip->i_mount) {
D
Dave Chinner 已提交
1582
		error = -EINVAL;
D
Dave Chinner 已提交
1583 1584 1585 1586
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1587
		error = -EINVAL;
D
Dave Chinner 已提交
1588 1589 1590 1591
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1592
		error = -EIO;
D
Dave Chinner 已提交
1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
		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;
}

1606 1607 1608 1609 1610 1611 1612 1613
/*
 * 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 已提交
1614 1615
	struct file		*filp,
	unsigned int		cmd,
1616
	unsigned long		p)
L
Lachlan McIlroy 已提交
1617
{
A
Al Viro 已提交
1618
	struct inode		*inode = file_inode(filp);
1619 1620 1621 1622
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	void			__user *arg = (void __user *)p;
	int			ioflags = 0;
L
Lachlan McIlroy 已提交
1623 1624
	int			error;

1625
	if (filp->f_mode & FMODE_NOCMTIME)
D
Dave Chinner 已提交
1626
		ioflags |= XFS_IO_INVIS;
L
Lachlan McIlroy 已提交
1627

C
Christoph Hellwig 已提交
1628
	trace_xfs_file_ioctl(ip);
1629 1630

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

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

1654
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1655 1656 1657
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

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

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1693 1694 1695 1696
		error = mnt_want_write_file(filp);
		if (error)
			return error;

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

	case XFS_IOC_GETBMAP:
	case XFS_IOC_GETBMAPA:
		return xfs_ioc_getbmap(ip, ioflags, cmd, arg);

	case XFS_IOC_GETBMAPX:
		return xfs_ioc_getbmapx(ip, arg);

	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
1712 1713
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1714

1715
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1716
			return -EFAULT;
1717 1718 1719 1720
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1721

1722
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1723
			return -EFAULT;
1724
		return xfs_open_by_handle(filp, &hreq);
1725
	}
L
Lachlan McIlroy 已提交
1726
	case XFS_IOC_FSSETDM_BY_HANDLE:
1727
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1728

1729 1730
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1731

1732
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1733
			return -EFAULT;
1734
		return xfs_readlink_by_handle(filp, &hreq);
1735
	}
L
Lachlan McIlroy 已提交
1736
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1737
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1738 1739

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1740
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1741 1742

	case XFS_IOC_SWAPEXT: {
1743 1744 1745
		struct xfs_swapext	sxp;

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

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1760
			return error;
L
Lachlan McIlroy 已提交
1761 1762

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1763
			return -EFAULT;
L
Lachlan McIlroy 已提交
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773
		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 已提交
1774
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1775
			return -EROFS;
E
Eric Sandeen 已提交
1776

L
Lachlan McIlroy 已提交
1777
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1778
			return -EFAULT;
L
Lachlan McIlroy 已提交
1779

J
Jan Kara 已提交
1780 1781 1782 1783
		error = mnt_want_write_file(filp);
		if (error)
			return error;

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

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

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1807
			return -EFAULT;
L
Lachlan McIlroy 已提交
1808 1809 1810 1811 1812 1813 1814 1815

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

J
Jan Kara 已提交
1818 1819 1820
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1821
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
1822
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1823
		return error;
L
Lachlan McIlroy 已提交
1824 1825 1826 1827 1828 1829
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

J
Jan Kara 已提交
1832 1833 1834
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1835
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
1836
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1837
		return error;
L
Lachlan McIlroy 已提交
1838 1839 1840 1841 1842 1843
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
1846 1847 1848
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1849
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
1850
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1851
		return error;
L
Lachlan McIlroy 已提交
1852 1853 1854 1855 1856 1857 1858 1859 1860
	}

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

D
Dave Chinner 已提交
1863
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
1864 1865 1866 1867 1868 1869 1870 1871 1872
	}

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

D
Dave Chinner 已提交
1875
		return xfs_errortag_add(in.errtag, mp);
L
Lachlan McIlroy 已提交
1876 1877 1878 1879 1880 1881
	}

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

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

1884
	case XFS_IOC_FREE_EOFBLOCKS: {
1885 1886
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
1887

1888 1889 1890 1891
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

1894
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
1895
			return -EFAULT;
1896

1897 1898
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
1899
			return error;
1900

D
Dave Chinner 已提交
1901
		return xfs_icache_free_eofblocks(mp, &keofb);
1902 1903
	}

L
Lachlan McIlroy 已提交
1904 1905 1906 1907
	default:
		return -ENOTTY;
	}
}