xfs_ioctl.c 42.7 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 281
	void			*link;
	int			error;
L
Linus Torvalds 已提交
282 283

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

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

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

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

301
	link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
302
	if (!link) {
E
Eric Sandeen 已提交
303
		error = -ENOMEM;
304 305
		goto out_dput;
	}
L
Linus Torvalds 已提交
306

307
	error = xfs_readlink(XFS_I(d_inode(dentry)), link);
308
	if (error)
309
		goto out_kfree;
A
Al Viro 已提交
310
	error = readlink_copy(hreq->ohandle, olen, link);
311 312
	if (error)
		goto out_kfree;
313

314 315
 out_kfree:
	kfree(link);
316 317
 out_dput:
	dput(dentry);
318
	return error;
L
Linus Torvalds 已提交
319 320
}

D
Dave Chinner 已提交
321 322 323 324 325 326 327 328 329 330 331
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 已提交
332
		return -EPERM;
D
Dave Chinner 已提交
333 334

	if (XFS_FORCED_SHUTDOWN(mp))
D
Dave Chinner 已提交
335
		return -EIO;
D
Dave Chinner 已提交
336 337

	tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS);
338
	error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
D
Dave Chinner 已提交
339
	if (error) {
340
		xfs_trans_cancel(tp);
D
Dave Chinner 已提交
341 342 343 344 345 346 347 348 349
		return error;
	}
	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);
350
	error = xfs_trans_commit(tp);
D
Dave Chinner 已提交
351 352 353 354

	return error;
}

L
Linus Torvalds 已提交
355 356
STATIC int
xfs_fssetdm_by_handle(
357 358
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
359 360 361 362
{
	int			error;
	struct fsdmidata	fsd;
	xfs_fsop_setdm_handlereq_t dmhreq;
363
	struct dentry		*dentry;
L
Linus Torvalds 已提交
364 365

	if (!capable(CAP_MKNOD))
E
Eric Sandeen 已提交
366
		return -EPERM;
L
Linus Torvalds 已提交
367
	if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
E
Eric Sandeen 已提交
368
		return -EFAULT;
L
Linus Torvalds 已提交
369

J
Jan Kara 已提交
370 371 372 373
	error = mnt_want_write_file(parfilp);
	if (error)
		return error;

374
	dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
J
Jan Kara 已提交
375 376
	if (IS_ERR(dentry)) {
		mnt_drop_write_file(parfilp);
377
		return PTR_ERR(dentry);
J
Jan Kara 已提交
378
	}
L
Linus Torvalds 已提交
379

380
	if (IS_IMMUTABLE(d_inode(dentry)) || IS_APPEND(d_inode(dentry))) {
E
Eric Sandeen 已提交
381
		error = -EPERM;
382
		goto out;
L
Linus Torvalds 已提交
383 384 385
	}

	if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
E
Eric Sandeen 已提交
386
		error = -EFAULT;
387
		goto out;
L
Linus Torvalds 已提交
388 389
	}

390
	error = xfs_set_dmattrs(XFS_I(d_inode(dentry)), fsd.fsd_dmevmask,
391
				 fsd.fsd_dmstate);
L
Linus Torvalds 已提交
392

393
 out:
J
Jan Kara 已提交
394
	mnt_drop_write_file(parfilp);
395
	dput(dentry);
396
	return error;
L
Linus Torvalds 已提交
397 398 399 400
}

STATIC int
xfs_attrlist_by_handle(
401 402
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
403
{
404
	int			error = -ENOMEM;
L
Linus Torvalds 已提交
405 406
	attrlist_cursor_kern_t	*cursor;
	xfs_fsop_attrlist_handlereq_t al_hreq;
407
	struct dentry		*dentry;
L
Linus Torvalds 已提交
408 409 410
	char			*kbuf;

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
411
		return -EPERM;
L
Linus Torvalds 已提交
412
	if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
E
Eric Sandeen 已提交
413
		return -EFAULT;
414
	if (al_hreq.buflen < sizeof(struct attrlist) ||
J
Jan Tulak 已提交
415
	    al_hreq.buflen > XFS_XATTR_LIST_MAX)
E
Eric Sandeen 已提交
416
		return -EINVAL;
L
Linus Torvalds 已提交
417

418 419 420 421
	/*
	 * Reject flags, only allow namespaces.
	 */
	if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
E
Eric Sandeen 已提交
422
		return -EINVAL;
423

424 425 426
	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
427

428 429 430
	kbuf = kmem_zalloc_large(al_hreq.buflen, KM_SLEEP);
	if (!kbuf)
		goto out_dput;
L
Linus Torvalds 已提交
431 432

	cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
433
	error = xfs_attr_list(XFS_I(d_inode(dentry)), kbuf, al_hreq.buflen,
434
					al_hreq.flags, cursor);
L
Linus Torvalds 已提交
435 436 437 438 439 440
	if (error)
		goto out_kfree;

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

441 442 443
out_kfree:
	kmem_free(kbuf);
out_dput:
444 445
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
446 447
}

448
int
L
Linus Torvalds 已提交
449
xfs_attrmulti_attr_get(
450
	struct inode		*inode,
451 452
	unsigned char		*name,
	unsigned char		__user *ubuf,
L
Linus Torvalds 已提交
453 454 455
	__uint32_t		*len,
	__uint32_t		flags)
{
456
	unsigned char		*kbuf;
D
Dave Chinner 已提交
457
	int			error = -EFAULT;
458

459
	if (*len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
460
		return -EINVAL;
461 462
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
D
Dave Chinner 已提交
463
		return -ENOMEM;
L
Linus Torvalds 已提交
464

465
	error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
L
Linus Torvalds 已提交
466 467 468 469
	if (error)
		goto out_kfree;

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

472 473
out_kfree:
	kmem_free(kbuf);
L
Linus Torvalds 已提交
474 475 476
	return error;
}

477
int
L
Linus Torvalds 已提交
478
xfs_attrmulti_attr_set(
479
	struct inode		*inode,
480 481
	unsigned char		*name,
	const unsigned char	__user *ubuf,
L
Linus Torvalds 已提交
482 483 484
	__uint32_t		len,
	__uint32_t		flags)
{
485
	unsigned char		*kbuf;
486
	int			error;
L
Linus Torvalds 已提交
487

488
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
489
		return -EPERM;
490
	if (len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
491
		return -EINVAL;
L
Linus Torvalds 已提交
492

L
Li Zefan 已提交
493 494 495
	kbuf = memdup_user(ubuf, len);
	if (IS_ERR(kbuf))
		return PTR_ERR(kbuf);
496

497
	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
498 499
	if (!error)
		xfs_forget_acl(inode, name, flags);
500 501
	kfree(kbuf);
	return error;
L
Linus Torvalds 已提交
502 503
}

504
int
L
Linus Torvalds 已提交
505
xfs_attrmulti_attr_remove(
506
	struct inode		*inode,
507
	unsigned char		*name,
L
Linus Torvalds 已提交
508 509
	__uint32_t		flags)
{
510 511
	int			error;

512
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
513
		return -EPERM;
514 515 516 517
	error = xfs_attr_remove(XFS_I(inode), name, flags);
	if (!error)
		xfs_forget_acl(inode, name, flags);
	return error;
L
Linus Torvalds 已提交
518 519 520 521
}

STATIC int
xfs_attrmulti_by_handle(
522
	struct file		*parfilp,
523
	void			__user *arg)
L
Linus Torvalds 已提交
524 525 526 527
{
	int			error;
	xfs_attr_multiop_t	*ops;
	xfs_fsop_attrmulti_handlereq_t am_hreq;
528
	struct dentry		*dentry;
L
Linus Torvalds 已提交
529
	unsigned int		i, size;
530
	unsigned char		*attr_name;
L
Linus Torvalds 已提交
531 532

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
533
		return -EPERM;
L
Linus Torvalds 已提交
534
	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
E
Eric Sandeen 已提交
535
		return -EFAULT;
L
Linus Torvalds 已提交
536

537 538 539 540
	/* overflow check */
	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
		return -E2BIG;

541 542 543
	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
544

D
Dave Chinner 已提交
545
	error = -E2BIG;
C
Christoph Hellwig 已提交
546
	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
L
Linus Torvalds 已提交
547
	if (!size || size > 16 * PAGE_SIZE)
548
		goto out_dput;
L
Linus Torvalds 已提交
549

L
Li Zefan 已提交
550 551
	ops = memdup_user(am_hreq.ops, size);
	if (IS_ERR(ops)) {
D
Dave Chinner 已提交
552
		error = PTR_ERR(ops);
553
		goto out_dput;
L
Li Zefan 已提交
554
	}
L
Linus Torvalds 已提交
555

D
Dave Chinner 已提交
556
	error = -ENOMEM;
L
Linus Torvalds 已提交
557 558 559 560 561 562
	attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
	if (!attr_name)
		goto out_kfree_ops;

	error = 0;
	for (i = 0; i < am_hreq.opcount; i++) {
563
		ops[i].am_error = strncpy_from_user((char *)attr_name,
L
Linus Torvalds 已提交
564 565
				ops[i].am_attrname, MAXNAMELEN);
		if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
D
Dave Chinner 已提交
566
			error = -ERANGE;
L
Linus Torvalds 已提交
567 568 569 570 571
		if (ops[i].am_error < 0)
			break;

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

	if (copy_to_user(am_hreq.ops, ops, size))
D
Dave Chinner 已提交
602
		error = -EFAULT;
L
Linus Torvalds 已提交
603 604 605 606

	kfree(attr_name);
 out_kfree_ops:
	kfree(ops);
607 608
 out_dput:
	dput(dentry);
D
Dave Chinner 已提交
609
	return error;
L
Linus Torvalds 已提交
610 611
}

612
int
L
Linus Torvalds 已提交
613
xfs_ioc_space(
614
	struct xfs_inode	*ip,
615
	struct inode		*inode,
L
Linus Torvalds 已提交
616 617 618
	struct file		*filp,
	int			ioflags,
	unsigned int		cmd,
619
	xfs_flock64_t		*bf)
L
Linus Torvalds 已提交
620
{
621
	struct iattr		iattr;
622
	enum xfs_prealloc_flags	flags = 0;
623
	uint			iolock = XFS_IOLOCK_EXCL;
L
Linus Torvalds 已提交
624 625
	int			error;

626 627 628 629 630 631
	/*
	 * 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 已提交
632
		return -EPERM;
633

634
	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
E
Eric Sandeen 已提交
635
		return -EPERM;
L
Linus Torvalds 已提交
636

637
	if (!(filp->f_mode & FMODE_WRITE))
E
Eric Sandeen 已提交
638
		return -EBADF;
L
Linus Torvalds 已提交
639

640
	if (!S_ISREG(inode->i_mode))
E
Eric Sandeen 已提交
641
		return -EINVAL;
L
Linus Torvalds 已提交
642

643 644
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
645
	if (ioflags & XFS_IO_INVIS)
646 647
		flags |= XFS_PREALLOC_INVISIBLE;

J
Jan Kara 已提交
648 649 650
	error = mnt_want_write_file(filp);
	if (error)
		return error;
651

652
	xfs_ilock(ip, iolock);
653
	error = xfs_break_layouts(inode, &iolock, false);
654 655
	if (error)
		goto out_unlock;
656

657 658 659
	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
	iolock |= XFS_MMAPLOCK_EXCL;

660 661 662 663 664 665 666 667 668 669
	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 已提交
670
		error = -EINVAL;
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
		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 已提交
687
			error = -EINVAL;
688 689 690 691 692 693 694 695 696
			goto out_unlock;
		}
		break;
	default:
		bf->l_len = 0;
		break;
	}

	if (bf->l_start < 0 ||
697
	    bf->l_start > inode->i_sb->s_maxbytes ||
698
	    bf->l_start + bf->l_len < 0 ||
699
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
D
Dave Chinner 已提交
700
		error = -EINVAL;
701 702 703 704 705
		goto out_unlock;
	}

	switch (cmd) {
	case XFS_IOC_ZERO_RANGE:
706
		flags |= XFS_PREALLOC_SET;
707 708 709 710
		error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
		break;
	case XFS_IOC_RESVSP:
	case XFS_IOC_RESVSP64:
711
		flags |= XFS_PREALLOC_SET;
712 713 714 715 716 717 718 719 720 721 722
		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:
723
		flags |= XFS_PREALLOC_CLEAR;
724 725 726 727 728 729 730 731 732 733 734 735 736 737
		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 已提交
738
		error = -EINVAL;
739 740 741 742 743
	}

	if (error)
		goto out_unlock;

744
	error = xfs_update_prealloc_flags(ip, flags);
745 746

out_unlock:
747
	xfs_iunlock(ip, iolock);
J
Jan Kara 已提交
748
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
749
	return error;
L
Linus Torvalds 已提交
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
}

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 已提交
771
		return -EIO;
L
Linus Torvalds 已提交
772 773

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
E
Eric Sandeen 已提交
774
		return -EFAULT;
L
Linus Torvalds 已提交
775 776

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

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

782
	if (bulkreq.ubuffer == NULL)
E
Eric Sandeen 已提交
783
		return -EINVAL;
784

L
Linus Torvalds 已提交
785 786
	if (cmd == XFS_IOC_FSINUMBERS)
		error = xfs_inumbers(mp, &inlast, &count,
787
					bulkreq.ubuffer, xfs_inumbers_fmt);
L
Linus Torvalds 已提交
788
	else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
789 790
		error = xfs_bulkstat_one(mp, inlast, bulkreq.ubuffer,
					sizeof(xfs_bstat_t), NULL, &done);
791
	else	/* XFS_IOC_FSBULKSTAT */
792 793 794
		error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one,
				     sizeof(xfs_bstat_t), bulkreq.ubuffer,
				     &done);
L
Linus Torvalds 已提交
795 796

	if (error)
D
Dave Chinner 已提交
797
		return error;
L
Linus Torvalds 已提交
798 799 800 801

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

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
E
Eric Sandeen 已提交
805
			return -EFAULT;
L
Linus Torvalds 已提交
806 807 808 809 810 811 812 813 814 815
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry_v1(
	xfs_mount_t		*mp,
	void			__user *arg)
{
816
	xfs_fsop_geom_t         fsgeo;
L
Linus Torvalds 已提交
817 818
	int			error;

819
	error = xfs_fs_geometry(mp, &fsgeo, 3);
L
Linus Torvalds 已提交
820
	if (error)
D
Dave Chinner 已提交
821
		return error;
L
Linus Torvalds 已提交
822

823 824 825 826 827 828
	/*
	 * 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 已提交
829
		return -EFAULT;
L
Linus Torvalds 已提交
830 831 832 833 834 835 836 837 838 839 840 841 842
	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 已提交
843
		return error;
L
Linus Torvalds 已提交
844 845

	if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
E
Eric Sandeen 已提交
846
		return -EFAULT;
L
Linus Torvalds 已提交
847 848 849 850 851 852 853 854 855 856 857 858 859 860
	return 0;
}

/*
 * Linux extended inode flags interface.
 */

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

861
	if (flags & FS_IMMUTABLE_FL)
862
		xflags |= FS_XFLAG_IMMUTABLE;
L
Linus Torvalds 已提交
863
	else
864
		xflags &= ~FS_XFLAG_IMMUTABLE;
865
	if (flags & FS_APPEND_FL)
866
		xflags |= FS_XFLAG_APPEND;
L
Linus Torvalds 已提交
867
	else
868
		xflags &= ~FS_XFLAG_APPEND;
869
	if (flags & FS_SYNC_FL)
870
		xflags |= FS_XFLAG_SYNC;
L
Linus Torvalds 已提交
871
	else
872
		xflags &= ~FS_XFLAG_SYNC;
873
	if (flags & FS_NOATIME_FL)
874
		xflags |= FS_XFLAG_NOATIME;
L
Linus Torvalds 已提交
875
	else
876
		xflags &= ~FS_XFLAG_NOATIME;
877
	if (flags & FS_NODUMP_FL)
878
		xflags |= FS_XFLAG_NODUMP;
L
Linus Torvalds 已提交
879
	else
880
		xflags &= ~FS_XFLAG_NODUMP;
L
Linus Torvalds 已提交
881 882 883 884 885 886 887 888 889 890 891

	return xflags;
}

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

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
892
		flags |= FS_IMMUTABLE_FL;
L
Linus Torvalds 已提交
893
	if (di_flags & XFS_DIFLAG_APPEND)
894
		flags |= FS_APPEND_FL;
L
Linus Torvalds 已提交
895
	if (di_flags & XFS_DIFLAG_SYNC)
896
		flags |= FS_SYNC_FL;
L
Linus Torvalds 已提交
897
	if (di_flags & XFS_DIFLAG_NOATIME)
898
		flags |= FS_NOATIME_FL;
L
Linus Torvalds 已提交
899
	if (di_flags & XFS_DIFLAG_NODUMP)
900
		flags |= FS_NODUMP_FL;
L
Linus Torvalds 已提交
901 902 903
	return flags;
}

904 905 906 907 908 909 910 911
STATIC int
xfs_ioc_fsgetxattr(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;

912 913
	memset(&fa, 0, sizeof(struct fsxattr));

914 915 916
	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;
917
	fa.fsx_projid = xfs_get_projid(ip);
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941

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

942 943 944 945 946 947
STATIC void
xfs_set_diflags(
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	unsigned int		di_flags;
948
	uint64_t		di_flags2;
949 950 951

	/* can't set PREALLOC this way, just preserve it */
	di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
952
	if (xflags & FS_XFLAG_IMMUTABLE)
953
		di_flags |= XFS_DIFLAG_IMMUTABLE;
954
	if (xflags & FS_XFLAG_APPEND)
955
		di_flags |= XFS_DIFLAG_APPEND;
956
	if (xflags & FS_XFLAG_SYNC)
957
		di_flags |= XFS_DIFLAG_SYNC;
958
	if (xflags & FS_XFLAG_NOATIME)
959
		di_flags |= XFS_DIFLAG_NOATIME;
960
	if (xflags & FS_XFLAG_NODUMP)
961
		di_flags |= XFS_DIFLAG_NODUMP;
962
	if (xflags & FS_XFLAG_NODEFRAG)
963
		di_flags |= XFS_DIFLAG_NODEFRAG;
964
	if (xflags & FS_XFLAG_FILESTREAM)
965
		di_flags |= XFS_DIFLAG_FILESTREAM;
D
Dave Chinner 已提交
966
	if (S_ISDIR(VFS_I(ip)->i_mode)) {
967
		if (xflags & FS_XFLAG_RTINHERIT)
968
			di_flags |= XFS_DIFLAG_RTINHERIT;
969
		if (xflags & FS_XFLAG_NOSYMLINKS)
970
			di_flags |= XFS_DIFLAG_NOSYMLINKS;
971
		if (xflags & FS_XFLAG_EXTSZINHERIT)
972
			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
973
		if (xflags & FS_XFLAG_PROJINHERIT)
974
			di_flags |= XFS_DIFLAG_PROJINHERIT;
D
Dave Chinner 已提交
975
	} else if (S_ISREG(VFS_I(ip)->i_mode)) {
976
		if (xflags & FS_XFLAG_REALTIME)
977
			di_flags |= XFS_DIFLAG_REALTIME;
978
		if (xflags & FS_XFLAG_EXTSIZE)
979 980 981
			di_flags |= XFS_DIFLAG_EXTSIZE;
	}
	ip->i_d.di_flags = di_flags;
982 983 984 985 986 987 988 989 990 991 992

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

993 994
}

995 996 997 998
STATIC void
xfs_diflags_to_linux(
	struct xfs_inode	*ip)
{
999
	struct inode		*inode = VFS_I(ip);
1000 1001
	unsigned int		xflags = xfs_ip2xflags(ip);

1002
	if (xflags & FS_XFLAG_IMMUTABLE)
1003 1004 1005
		inode->i_flags |= S_IMMUTABLE;
	else
		inode->i_flags &= ~S_IMMUTABLE;
1006
	if (xflags & FS_XFLAG_APPEND)
1007 1008 1009
		inode->i_flags |= S_APPEND;
	else
		inode->i_flags &= ~S_APPEND;
1010
	if (xflags & FS_XFLAG_SYNC)
1011 1012 1013
		inode->i_flags |= S_SYNC;
	else
		inode->i_flags &= ~S_SYNC;
1014
	if (xflags & FS_XFLAG_NOATIME)
1015 1016 1017
		inode->i_flags |= S_NOATIME;
	else
		inode->i_flags &= ~S_NOATIME;
1018 1019 1020 1021 1022
	if (xflags & FS_XFLAG_DAX)
		inode->i_flags |= S_DAX;
	else
		inode->i_flags &= ~S_DAX;

1023
}
1024

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
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) &&
1035
	    XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & FS_XFLAG_REALTIME))
1036 1037 1038
		return -EINVAL;

	/* If realtime flag is set then must have realtime device */
1039
	if (fa->fsx_xflags & FS_XFLAG_REALTIME) {
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
		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)) ||
1050
	     (fa->fsx_xflags & (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND))) &&
1051 1052 1053 1054 1055 1056 1057
	    !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);
1058
	XFS_STATS_INC(mp, xs_ig_attrchg);
1059 1060 1061
	return 0;
}

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
/*
 * 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;

1081 1082
	/*
	 * It is only valid to set the DAX flag on regular files and
1083 1084
	 * directories on filesystems where the block size is equal to the page
	 * size. On directories it serves as an inherit hint.
1085
	 */
1086 1087 1088 1089 1090 1091
	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;
	}
1092

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

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

1115 1116
}

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

	if (mp->m_flags & XFS_MOUNT_RDONLY)
1139 1140
		goto out_unlock;
	error = -EIO;
1141
	if (XFS_FORCED_SHUTDOWN(mp))
1142
		goto out_unlock;
1143 1144 1145 1146 1147 1148 1149

	tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
	error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
	if (error)
		goto out_cancel;

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

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

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

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

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

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

1231 1232 1233
	return 0;
}

1234
static int
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
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;
1254
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1255 1256 1257 1258 1259
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1260 1261 1262 1263

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
1264
	struct fsxattr		*fa)
1265 1266 1267
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
C
Christoph Hellwig 已提交
1268
	struct xfs_dquot	*udqp = NULL;
1269
	struct xfs_dquot	*pdqp = NULL;
1270 1271
	struct xfs_dquot	*olddquot = NULL;
	int			code;
1272
	int			join_flags = 0;
1273

C
Christoph Hellwig 已提交
1274
	trace_xfs_ioctl_setattr(ip);
1275

1276 1277 1278
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1279

1280 1281 1282 1283 1284 1285 1286 1287
	/*
	 * 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.
	 */
1288
	if (XFS_IS_QUOTA_ON(mp)) {
C
Christoph Hellwig 已提交
1289
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1290
					 ip->i_d.di_gid, fa->fsx_projid,
1291
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1292 1293 1294 1295
		if (code)
			return code;
	}

1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
	/*
	 * 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);
1308 1309 1310
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1311 1312 1313
	}


1314 1315 1316 1317 1318
	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 */
1319
			goto error_trans_cancel;
1320 1321
	}

1322 1323 1324
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1325

1326 1327
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1328
		goto error_trans_cancel;
1329 1330

	/*
1331 1332 1333 1334 1335
	 * 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()
1336 1337
	 */

D
Dave Chinner 已提交
1338
	if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
1339
	    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
D
Dave Chinner 已提交
1340
		VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
1341

1342 1343 1344 1345 1346 1347 1348 1349
	/* 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);
1350
	}
1351

1352 1353 1354 1355 1356
	/*
	 * 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.
	 */
1357 1358 1359 1360
	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;
1361

1362
	code = xfs_trans_commit(tp);
1363 1364 1365 1366

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1367 1368
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1369
	xfs_qm_dqrele(pdqp);
1370

C
Christoph Hellwig 已提交
1371
	return code;
1372

1373
error_trans_cancel:
1374
	xfs_trans_cancel(tp);
1375
error_free_dquots:
C
Christoph Hellwig 已提交
1376
	xfs_qm_dqrele(udqp);
1377
	xfs_qm_dqrele(pdqp);
1378 1379 1380
	return code;
}

L
Linus Torvalds 已提交
1381
STATIC int
L
Lachlan McIlroy 已提交
1382
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1383 1384 1385 1386 1387
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1388
	int error;
L
Lachlan McIlroy 已提交
1389 1390 1391

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

J
Jan Kara 已提交
1393 1394 1395
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1396
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1397
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1398
	return error;
L
Lachlan McIlroy 已提交
1399
}
L
Linus Torvalds 已提交
1400

L
Lachlan McIlroy 已提交
1401 1402 1403 1404 1405 1406
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1407

L
Lachlan McIlroy 已提交
1408 1409 1410 1411 1412
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1413

L
Lachlan McIlroy 已提交
1414 1415
STATIC int
xfs_ioc_setxflags(
1416
	struct xfs_inode	*ip,
L
Lachlan McIlroy 已提交
1417 1418 1419
	struct file		*filp,
	void			__user *arg)
{
1420
	struct xfs_trans	*tp;
1421
	struct fsxattr		fa;
L
Lachlan McIlroy 已提交
1422
	unsigned int		flags;
1423
	int			join_flags = 0;
1424
	int			error;
L
Linus Torvalds 已提交
1425

L
Lachlan McIlroy 已提交
1426 1427
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1428

L
Lachlan McIlroy 已提交
1429 1430 1431 1432
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1433

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

J
Jan Kara 已提交
1436 1437 1438
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1439

1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451
	/*
	 * 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);
1452 1453 1454 1455 1456 1457 1458
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1459
		xfs_trans_cancel(tp);
1460 1461 1462
		goto out_drop_write;
	}

1463
	error = xfs_trans_commit(tp);
1464
out_drop_write:
J
Jan Kara 已提交
1465
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1466
	return error;
L
Linus Torvalds 已提交
1467 1468
}

1469 1470 1471
STATIC int
xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
{
1472
	struct getbmap __user	*base = (struct getbmap __user *)*ap;
1473 1474 1475

	/* copy only getbmap portion (not getbmapx) */
	if (copy_to_user(base, bmv, sizeof(struct getbmap)))
D
Dave Chinner 已提交
1476
		return -EFAULT;
1477 1478 1479 1480 1481

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

L
Linus Torvalds 已提交
1482 1483
STATIC int
xfs_ioc_getbmap(
1484
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1485 1486 1487 1488
	int			ioflags,
	unsigned int		cmd,
	void			__user *arg)
{
1489
	struct getbmapx		bmx;
L
Linus Torvalds 已提交
1490 1491
	int			error;

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

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

1498
	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
D
Dave Chinner 已提交
1499
	if (ioflags & XFS_IO_INVIS)
1500
		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
L
Linus Torvalds 已提交
1501

1502
	error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
1503
			    (__force struct getbmap *)arg+1);
L
Linus Torvalds 已提交
1504
	if (error)
D
Dave Chinner 已提交
1505
		return error;
L
Linus Torvalds 已提交
1506

1507 1508
	/* copy back header - only size of getbmap */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
E
Eric Sandeen 已提交
1509
		return -EFAULT;
L
Linus Torvalds 已提交
1510 1511 1512
	return 0;
}

1513 1514 1515
STATIC int
xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
{
1516
	struct getbmapx __user	*base = (struct getbmapx __user *)*ap;
1517 1518

	if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
D
Dave Chinner 已提交
1519
		return -EFAULT;
1520 1521 1522 1523 1524

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

L
Linus Torvalds 已提交
1525 1526
STATIC int
xfs_ioc_getbmapx(
1527
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1528 1529 1530 1531 1532 1533
	void			__user *arg)
{
	struct getbmapx		bmx;
	int			error;

	if (copy_from_user(&bmx, arg, sizeof(bmx)))
E
Eric Sandeen 已提交
1534
		return -EFAULT;
L
Linus Torvalds 已提交
1535 1536

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

1539
	if (bmx.bmv_iflags & (~BMV_IF_VALID))
E
Eric Sandeen 已提交
1540
		return -EINVAL;
L
Linus Torvalds 已提交
1541

1542
	error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
1543
			    (__force struct getbmapx *)arg+1);
L
Linus Torvalds 已提交
1544
	if (error)
D
Dave Chinner 已提交
1545
		return error;
L
Linus Torvalds 已提交
1546

1547 1548
	/* copy back header */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
E
Eric Sandeen 已提交
1549
		return -EFAULT;
L
Linus Torvalds 已提交
1550 1551 1552

	return 0;
}
L
Lachlan McIlroy 已提交
1553

D
Dave Chinner 已提交
1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564
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 已提交
1565
		error = -EINVAL;
D
Dave Chinner 已提交
1566 1567 1568 1569 1570 1571
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1572
		error = -EBADF;
D
Dave Chinner 已提交
1573 1574 1575 1576 1577
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1578
		error = -EINVAL;
D
Dave Chinner 已提交
1579 1580 1581 1582 1583 1584
		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 已提交
1585
		error = -EBADF;
D
Dave Chinner 已提交
1586 1587 1588 1589 1590
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1591
		error = -EINVAL;
D
Dave Chinner 已提交
1592 1593 1594 1595 1596 1597 1598
		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 已提交
1599
		error = -EINVAL;
D
Dave Chinner 已提交
1600 1601 1602 1603
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1604
		error = -EINVAL;
D
Dave Chinner 已提交
1605 1606 1607 1608
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1609
		error = -EIO;
D
Dave Chinner 已提交
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
		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;
}

1623 1624 1625 1626 1627 1628 1629 1630
/*
 * 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 已提交
1631 1632
	struct file		*filp,
	unsigned int		cmd,
1633
	unsigned long		p)
L
Lachlan McIlroy 已提交
1634
{
A
Al Viro 已提交
1635
	struct inode		*inode = file_inode(filp);
1636 1637 1638 1639
	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 已提交
1640 1641
	int			error;

1642
	if (filp->f_mode & FMODE_NOCMTIME)
D
Dave Chinner 已提交
1643
		ioflags |= XFS_IO_INVIS;
L
Lachlan McIlroy 已提交
1644

C
Christoph Hellwig 已提交
1645
	trace_xfs_file_ioctl(ip);
1646 1647

	switch (cmd) {
C
Christoph Hellwig 已提交
1648 1649
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
L
Lachlan McIlroy 已提交
1650 1651 1652 1653 1654 1655 1656
	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 已提交
1657 1658
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1659
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1660

1661
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1662
			return -EFAULT;
1663 1664
		return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf);
	}
L
Lachlan McIlroy 已提交
1665 1666 1667 1668 1669 1670
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1671
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1672 1673 1674
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

		if (copy_to_user(arg, &da, sizeof(da)))
E
Eric Sandeen 已提交
1675
			return -EFAULT;
L
Lachlan McIlroy 已提交
1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696
		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 已提交
1697 1698
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(ip, filp, arg);
L
Lachlan McIlroy 已提交
1699
	case XFS_IOC_GETXFLAGS:
L
Lachlan McIlroy 已提交
1700
		return xfs_ioc_getxflags(ip, arg);
L
Lachlan McIlroy 已提交
1701
	case XFS_IOC_SETXFLAGS:
L
Lachlan McIlroy 已提交
1702
		return xfs_ioc_setxflags(ip, filp, arg);
L
Lachlan McIlroy 已提交
1703 1704 1705 1706 1707

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1710 1711 1712 1713
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1714 1715
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1716
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1717
		return error;
L
Lachlan McIlroy 已提交
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728
	}

	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:
1729 1730
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1731

1732
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1733
			return -EFAULT;
1734 1735 1736 1737
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1738

1739
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1740
			return -EFAULT;
1741
		return xfs_open_by_handle(filp, &hreq);
1742
	}
L
Lachlan McIlroy 已提交
1743
	case XFS_IOC_FSSETDM_BY_HANDLE:
1744
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1745

1746 1747
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1748

1749
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1750
			return -EFAULT;
1751
		return xfs_readlink_by_handle(filp, &hreq);
1752
	}
L
Lachlan McIlroy 已提交
1753
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1754
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1755 1756

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1757
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1758 1759

	case XFS_IOC_SWAPEXT: {
1760 1761 1762
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
1763
			return -EFAULT;
J
Jan Kara 已提交
1764 1765 1766
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
1767
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
1768
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1769
		return error;
L
Lachlan McIlroy 已提交
1770 1771 1772 1773 1774 1775 1776
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1777
			return error;
L
Lachlan McIlroy 已提交
1778 1779

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1780
			return -EFAULT;
L
Lachlan McIlroy 已提交
1781 1782 1783 1784 1785 1786 1787 1788 1789 1790
		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 已提交
1791
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1792
			return -EROFS;
E
Eric Sandeen 已提交
1793

L
Lachlan McIlroy 已提交
1794
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1795
			return -EFAULT;
L
Lachlan McIlroy 已提交
1796

J
Jan Kara 已提交
1797 1798 1799 1800
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1801 1802 1803
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
1804
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
1805
		if (error)
D
Dave Chinner 已提交
1806
			return error;
L
Lachlan McIlroy 已提交
1807 1808

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
1809
			return -EFAULT;
L
Lachlan McIlroy 已提交
1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820
		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 已提交
1821
			return error;
L
Lachlan McIlroy 已提交
1822 1823

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1824
			return -EFAULT;
L
Lachlan McIlroy 已提交
1825 1826 1827 1828 1829 1830 1831 1832

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

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

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

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

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
1863 1864 1865
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1866
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
1867
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1868
		return error;
L
Lachlan McIlroy 已提交
1869 1870 1871 1872 1873 1874 1875 1876 1877
	}

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

D
Dave Chinner 已提交
1880
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
1881 1882 1883 1884 1885 1886 1887 1888 1889
	}

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

D
Dave Chinner 已提交
1892
		return xfs_errortag_add(in.errtag, mp);
L
Lachlan McIlroy 已提交
1893 1894 1895 1896 1897 1898
	}

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

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

1901
	case XFS_IOC_FREE_EOFBLOCKS: {
1902 1903
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
1904

1905 1906 1907 1908
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

1911
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
1912
			return -EFAULT;
1913

1914 1915
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
1916
			return error;
1917

D
Dave Chinner 已提交
1918
		return xfs_icache_free_eofblocks(mp, &keofb);
1919 1920
	}

L
Lachlan McIlroy 已提交
1921 1922 1923 1924
	default:
		return -ENOTTY;
	}
}