xfs_ioctl.c 39.8 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;
966
	if (S_ISDIR(ip->i_d.di_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;
975
	} else if (S_ISREG(ip->i_d.di_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 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 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.
 */
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
	struct xfs_inode	*ip)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
	int			error;

	if (mp->m_flags & XFS_MOUNT_RDONLY)
		return ERR_PTR(-EROFS);
	if (XFS_FORCED_SHUTDOWN(mp))
		return ERR_PTR(-EIO);

	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);
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);

	/*
	 * 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:
1106
	xfs_trans_cancel(tp);
1107 1108 1109
	return ERR_PTR(error);
}

1110 1111 1112 1113
/*
 * extent size hint validation is somewhat cumbersome. Rules are:
 *
 * 1. extent size hint is only valid for directories and regular files
1114 1115
 * 2. FS_XFLAG_EXTSIZE is only valid for regular files
 * 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
1116 1117 1118 1119 1120 1121 1122 1123
 * 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.
 */
1124
static int
1125 1126 1127 1128 1129 1130
xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

1131
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
1132 1133
		return -EINVAL;

1134
	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
1135 1136 1137 1138
	    !S_ISDIR(ip->i_d.di_mode))
		return -EINVAL;

	if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents &&
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
	    ((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) ||
1151
		    (fa->fsx_xflags & FS_XFLAG_REALTIME)) {
1152 1153 1154 1155 1156 1157 1158 1159 1160
			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;
1161
	} else
1162
		fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
1163

1164 1165 1166
	return 0;
}

1167
static int
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
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;
1187
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1188 1189 1190 1191 1192
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1193 1194 1195 1196

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
1197
	struct fsxattr		*fa)
1198 1199 1200
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
C
Christoph Hellwig 已提交
1201
	struct xfs_dquot	*udqp = NULL;
1202
	struct xfs_dquot	*pdqp = NULL;
1203 1204 1205
	struct xfs_dquot	*olddquot = NULL;
	int			code;

C
Christoph Hellwig 已提交
1206
	trace_xfs_ioctl_setattr(ip);
1207

1208 1209 1210
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1211

1212 1213 1214 1215 1216 1217 1218 1219
	/*
	 * 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.
	 */
1220
	if (XFS_IS_QUOTA_ON(mp)) {
C
Christoph Hellwig 已提交
1221
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1222
					 ip->i_d.di_gid, fa->fsx_projid,
1223
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1224 1225 1226 1227
		if (code)
			return code;
	}

1228 1229 1230 1231
	tp = xfs_ioctl_setattr_get_trans(ip);
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1232 1233 1234
	}


1235 1236 1237 1238 1239
	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 */
1240
			goto error_trans_cancel;
1241 1242
	}

1243 1244 1245
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1246

1247 1248
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1249
		goto error_trans_cancel;
1250 1251

	/*
1252 1253 1254 1255 1256
	 * 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()
1257 1258
	 */

1259 1260 1261
	if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
	    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
		ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
1262

1263 1264 1265 1266 1267 1268 1269 1270
	/* 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);
1271
	}
1272

1273 1274 1275 1276 1277
	/*
	 * 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.
	 */
1278 1279 1280 1281
	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;
1282

1283
	code = xfs_trans_commit(tp);
1284 1285 1286 1287

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1288 1289
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1290
	xfs_qm_dqrele(pdqp);
1291

C
Christoph Hellwig 已提交
1292
	return code;
1293

1294
error_trans_cancel:
1295
	xfs_trans_cancel(tp);
1296
error_free_dquots:
C
Christoph Hellwig 已提交
1297
	xfs_qm_dqrele(udqp);
1298
	xfs_qm_dqrele(pdqp);
1299 1300 1301
	return code;
}

L
Linus Torvalds 已提交
1302
STATIC int
L
Lachlan McIlroy 已提交
1303
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1304 1305 1306 1307 1308
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1309
	int error;
L
Lachlan McIlroy 已提交
1310 1311 1312

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

J
Jan Kara 已提交
1314 1315 1316
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1317
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1318
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1319
	return error;
L
Lachlan McIlroy 已提交
1320
}
L
Linus Torvalds 已提交
1321

L
Lachlan McIlroy 已提交
1322 1323 1324 1325 1326 1327
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1328

L
Lachlan McIlroy 已提交
1329 1330 1331 1332 1333
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1334

L
Lachlan McIlroy 已提交
1335 1336
STATIC int
xfs_ioc_setxflags(
1337
	struct xfs_inode	*ip,
L
Lachlan McIlroy 已提交
1338 1339 1340
	struct file		*filp,
	void			__user *arg)
{
1341
	struct xfs_trans	*tp;
1342
	struct fsxattr		fa;
L
Lachlan McIlroy 已提交
1343
	unsigned int		flags;
1344
	int			error;
L
Linus Torvalds 已提交
1345

L
Lachlan McIlroy 已提交
1346 1347
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1348

L
Lachlan McIlroy 已提交
1349 1350 1351 1352
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1353

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

J
Jan Kara 已提交
1356 1357 1358
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1359 1360 1361 1362 1363 1364 1365 1366 1367

	tp = xfs_ioctl_setattr_get_trans(ip);
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1368
		xfs_trans_cancel(tp);
1369 1370 1371
		goto out_drop_write;
	}

1372
	error = xfs_trans_commit(tp);
1373
out_drop_write:
J
Jan Kara 已提交
1374
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1375
	return error;
L
Linus Torvalds 已提交
1376 1377
}

1378 1379 1380
STATIC int
xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
{
1381
	struct getbmap __user	*base = (struct getbmap __user *)*ap;
1382 1383 1384

	/* copy only getbmap portion (not getbmapx) */
	if (copy_to_user(base, bmv, sizeof(struct getbmap)))
D
Dave Chinner 已提交
1385
		return -EFAULT;
1386 1387 1388 1389 1390

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

L
Linus Torvalds 已提交
1391 1392
STATIC int
xfs_ioc_getbmap(
1393
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1394 1395 1396 1397
	int			ioflags,
	unsigned int		cmd,
	void			__user *arg)
{
1398
	struct getbmapx		bmx;
L
Linus Torvalds 已提交
1399 1400
	int			error;

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

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

1407
	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
D
Dave Chinner 已提交
1408
	if (ioflags & XFS_IO_INVIS)
1409
		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
L
Linus Torvalds 已提交
1410

1411
	error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
1412
			    (__force struct getbmap *)arg+1);
L
Linus Torvalds 已提交
1413
	if (error)
D
Dave Chinner 已提交
1414
		return error;
L
Linus Torvalds 已提交
1415

1416 1417
	/* copy back header - only size of getbmap */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
E
Eric Sandeen 已提交
1418
		return -EFAULT;
L
Linus Torvalds 已提交
1419 1420 1421
	return 0;
}

1422 1423 1424
STATIC int
xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
{
1425
	struct getbmapx __user	*base = (struct getbmapx __user *)*ap;
1426 1427

	if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
D
Dave Chinner 已提交
1428
		return -EFAULT;
1429 1430 1431 1432 1433

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

L
Linus Torvalds 已提交
1434 1435
STATIC int
xfs_ioc_getbmapx(
1436
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1437 1438 1439 1440 1441 1442
	void			__user *arg)
{
	struct getbmapx		bmx;
	int			error;

	if (copy_from_user(&bmx, arg, sizeof(bmx)))
E
Eric Sandeen 已提交
1443
		return -EFAULT;
L
Linus Torvalds 已提交
1444 1445

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

1448
	if (bmx.bmv_iflags & (~BMV_IF_VALID))
E
Eric Sandeen 已提交
1449
		return -EINVAL;
L
Linus Torvalds 已提交
1450

1451
	error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
1452
			    (__force struct getbmapx *)arg+1);
L
Linus Torvalds 已提交
1453
	if (error)
D
Dave Chinner 已提交
1454
		return error;
L
Linus Torvalds 已提交
1455

1456 1457
	/* copy back header */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
E
Eric Sandeen 已提交
1458
		return -EFAULT;
L
Linus Torvalds 已提交
1459 1460 1461

	return 0;
}
L
Lachlan McIlroy 已提交
1462

D
Dave Chinner 已提交
1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
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 已提交
1474
		error = -EINVAL;
D
Dave Chinner 已提交
1475 1476 1477 1478 1479 1480
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1481
		error = -EBADF;
D
Dave Chinner 已提交
1482 1483 1484 1485 1486
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1487
		error = -EINVAL;
D
Dave Chinner 已提交
1488 1489 1490 1491 1492 1493
		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 已提交
1494
		error = -EBADF;
D
Dave Chinner 已提交
1495 1496 1497 1498 1499
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1500
		error = -EINVAL;
D
Dave Chinner 已提交
1501 1502 1503 1504 1505 1506 1507
		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 已提交
1508
		error = -EINVAL;
D
Dave Chinner 已提交
1509 1510 1511 1512
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1513
		error = -EINVAL;
D
Dave Chinner 已提交
1514 1515 1516 1517
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1518
		error = -EIO;
D
Dave Chinner 已提交
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531
		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;
}

1532 1533 1534 1535 1536 1537 1538 1539
/*
 * 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 已提交
1540 1541
	struct file		*filp,
	unsigned int		cmd,
1542
	unsigned long		p)
L
Lachlan McIlroy 已提交
1543
{
A
Al Viro 已提交
1544
	struct inode		*inode = file_inode(filp);
1545 1546 1547 1548
	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 已提交
1549 1550
	int			error;

1551
	if (filp->f_mode & FMODE_NOCMTIME)
D
Dave Chinner 已提交
1552
		ioflags |= XFS_IO_INVIS;
L
Lachlan McIlroy 已提交
1553

C
Christoph Hellwig 已提交
1554
	trace_xfs_file_ioctl(ip);
1555 1556

	switch (cmd) {
C
Christoph Hellwig 已提交
1557 1558
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
L
Lachlan McIlroy 已提交
1559 1560 1561 1562 1563 1564 1565
	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 已提交
1566 1567
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1568
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1569

1570
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1571
			return -EFAULT;
1572 1573
		return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf);
	}
L
Lachlan McIlroy 已提交
1574 1575 1576 1577 1578 1579
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1580
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1581 1582 1583
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

		if (copy_to_user(arg, &da, sizeof(da)))
E
Eric Sandeen 已提交
1584
			return -EFAULT;
L
Lachlan McIlroy 已提交
1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
		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 已提交
1606 1607
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(ip, filp, arg);
L
Lachlan McIlroy 已提交
1608
	case XFS_IOC_GETXFLAGS:
L
Lachlan McIlroy 已提交
1609
		return xfs_ioc_getxflags(ip, arg);
L
Lachlan McIlroy 已提交
1610
	case XFS_IOC_SETXFLAGS:
L
Lachlan McIlroy 已提交
1611
		return xfs_ioc_setxflags(ip, filp, arg);
L
Lachlan McIlroy 已提交
1612 1613 1614 1615 1616

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1619 1620 1621 1622
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1623 1624
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1625
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1626
		return error;
L
Lachlan McIlroy 已提交
1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637
	}

	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:
1638 1639
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1640

1641
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1642
			return -EFAULT;
1643 1644 1645 1646
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1647

1648
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1649
			return -EFAULT;
1650
		return xfs_open_by_handle(filp, &hreq);
1651
	}
L
Lachlan McIlroy 已提交
1652
	case XFS_IOC_FSSETDM_BY_HANDLE:
1653
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1654

1655 1656
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1657

1658
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1659
			return -EFAULT;
1660
		return xfs_readlink_by_handle(filp, &hreq);
1661
	}
L
Lachlan McIlroy 已提交
1662
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1663
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1664 1665

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1666
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1667 1668

	case XFS_IOC_SWAPEXT: {
1669 1670 1671
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
1672
			return -EFAULT;
J
Jan Kara 已提交
1673 1674 1675
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
1676
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
1677
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1678
		return error;
L
Lachlan McIlroy 已提交
1679 1680 1681 1682 1683 1684 1685
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1686
			return error;
L
Lachlan McIlroy 已提交
1687 1688

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1689
			return -EFAULT;
L
Lachlan McIlroy 已提交
1690 1691 1692 1693 1694 1695 1696 1697 1698 1699
		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 已提交
1700
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1701
			return -EROFS;
E
Eric Sandeen 已提交
1702

L
Lachlan McIlroy 已提交
1703
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1704
			return -EFAULT;
L
Lachlan McIlroy 已提交
1705

J
Jan Kara 已提交
1706 1707 1708 1709
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1710 1711 1712
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
1713
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
1714
		if (error)
D
Dave Chinner 已提交
1715
			return error;
L
Lachlan McIlroy 已提交
1716 1717

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
1718
			return -EFAULT;
L
Lachlan McIlroy 已提交
1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729
		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 已提交
1730
			return error;
L
Lachlan McIlroy 已提交
1731 1732

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1733
			return -EFAULT;
L
Lachlan McIlroy 已提交
1734 1735 1736 1737 1738 1739 1740 1741

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

J
Jan Kara 已提交
1744 1745 1746
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1747
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
1748
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1749
		return error;
L
Lachlan McIlroy 已提交
1750 1751 1752 1753 1754 1755
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

J
Jan Kara 已提交
1758 1759 1760
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1761
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
1762
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1763
		return error;
L
Lachlan McIlroy 已提交
1764 1765 1766 1767 1768 1769
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
1772 1773 1774
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1775
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
1776
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1777
		return error;
L
Lachlan McIlroy 已提交
1778 1779 1780 1781 1782 1783 1784 1785 1786
	}

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

D
Dave Chinner 已提交
1789
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
1790 1791 1792 1793 1794 1795 1796 1797 1798
	}

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

D
Dave Chinner 已提交
1801
		return xfs_errortag_add(in.errtag, mp);
L
Lachlan McIlroy 已提交
1802 1803 1804 1805 1806 1807
	}

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

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

1810
	case XFS_IOC_FREE_EOFBLOCKS: {
1811 1812
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
1813

1814 1815 1816 1817
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

1820
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
1821
			return -EFAULT;
1822

1823 1824
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
1825
			return error;
1826

D
Dave Chinner 已提交
1827
		return xfs_icache_free_eofblocks(mp, &keofb);
1828 1829
	}

L
Lachlan McIlroy 已提交
1830 1831 1832 1833
	default:
		return -ENOTTY;
	}
}