xfs_ioctl.c 40.1 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 117 118
		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
					sizeof(handle.ha_fid.fid_len);
		handle.ha_fid.fid_pad = 0;
		handle.ha_fid.fid_gen = ip->i_d.di_gen;
		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
	    !capable(CAP_LINUX_IMMUTABLE))
		return -EPERM;

1054 1055 1056 1057 1058 1059 1060 1061
	/*
	 * It is only valid to set the DAX flag on regular files and
	 * directories. On directories it serves as an inherit hint.
	 */
	if ((fa->fsx_xflags & FS_XFLAG_DAX) &&
	    !(S_ISREG(VFS_I(ip)->i_mode) || S_ISDIR(VFS_I(ip)->i_mode)))
		return -EINVAL;

1062 1063 1064 1065
	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);
1066
	XFS_STATS_INC(mp, xs_ig_attrchg);
1067 1068 1069
	return 0;
}

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 1106 1107 1108 1109 1110 1111 1112 1113
/*
 * 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:
1114
	xfs_trans_cancel(tp);
1115 1116 1117
	return ERR_PTR(error);
}

1118 1119 1120 1121
/*
 * extent size hint validation is somewhat cumbersome. Rules are:
 *
 * 1. extent size hint is only valid for directories and regular files
1122 1123
 * 2. FS_XFLAG_EXTSIZE is only valid for regular files
 * 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
1124 1125 1126 1127 1128 1129 1130 1131
 * 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.
 */
1132
static int
1133 1134 1135 1136 1137 1138
xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

1139
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
1140 1141
		return -EINVAL;

1142
	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
1143 1144 1145 1146
	    !S_ISDIR(ip->i_d.di_mode))
		return -EINVAL;

	if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents &&
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
	    ((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) ||
1159
		    (fa->fsx_xflags & FS_XFLAG_REALTIME)) {
1160 1161 1162 1163 1164 1165 1166 1167 1168
			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;
1169
	} else
1170
		fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
1171

1172 1173 1174
	return 0;
}

1175
static int
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
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;
1195
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1196 1197 1198 1199 1200
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1201 1202 1203 1204

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
1205
	struct fsxattr		*fa)
1206 1207 1208
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
C
Christoph Hellwig 已提交
1209
	struct xfs_dquot	*udqp = NULL;
1210
	struct xfs_dquot	*pdqp = NULL;
1211 1212 1213
	struct xfs_dquot	*olddquot = NULL;
	int			code;

C
Christoph Hellwig 已提交
1214
	trace_xfs_ioctl_setattr(ip);
1215

1216 1217 1218
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1219

1220 1221 1222 1223 1224 1225 1226 1227
	/*
	 * 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.
	 */
1228
	if (XFS_IS_QUOTA_ON(mp)) {
C
Christoph Hellwig 已提交
1229
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1230
					 ip->i_d.di_gid, fa->fsx_projid,
1231
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1232 1233 1234 1235
		if (code)
			return code;
	}

1236 1237 1238 1239
	tp = xfs_ioctl_setattr_get_trans(ip);
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1240 1241 1242
	}


1243 1244 1245 1246 1247
	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 */
1248
			goto error_trans_cancel;
1249 1250
	}

1251 1252 1253
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1254

1255 1256
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1257
		goto error_trans_cancel;
1258 1259

	/*
1260 1261 1262 1263 1264
	 * 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()
1265 1266
	 */

1267 1268 1269
	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);
1270

1271 1272 1273 1274 1275 1276 1277 1278
	/* 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);
1279
	}
1280

1281 1282 1283 1284 1285
	/*
	 * 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.
	 */
1286 1287 1288 1289
	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;
1290

1291
	code = xfs_trans_commit(tp);
1292 1293 1294 1295

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1296 1297
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1298
	xfs_qm_dqrele(pdqp);
1299

C
Christoph Hellwig 已提交
1300
	return code;
1301

1302
error_trans_cancel:
1303
	xfs_trans_cancel(tp);
1304
error_free_dquots:
C
Christoph Hellwig 已提交
1305
	xfs_qm_dqrele(udqp);
1306
	xfs_qm_dqrele(pdqp);
1307 1308 1309
	return code;
}

L
Linus Torvalds 已提交
1310
STATIC int
L
Lachlan McIlroy 已提交
1311
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1312 1313 1314 1315 1316
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1317
	int error;
L
Lachlan McIlroy 已提交
1318 1319 1320

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

J
Jan Kara 已提交
1322 1323 1324
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1325
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1326
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1327
	return error;
L
Lachlan McIlroy 已提交
1328
}
L
Linus Torvalds 已提交
1329

L
Lachlan McIlroy 已提交
1330 1331 1332 1333 1334 1335
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1336

L
Lachlan McIlroy 已提交
1337 1338 1339 1340 1341
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1342

L
Lachlan McIlroy 已提交
1343 1344
STATIC int
xfs_ioc_setxflags(
1345
	struct xfs_inode	*ip,
L
Lachlan McIlroy 已提交
1346 1347 1348
	struct file		*filp,
	void			__user *arg)
{
1349
	struct xfs_trans	*tp;
1350
	struct fsxattr		fa;
L
Lachlan McIlroy 已提交
1351
	unsigned int		flags;
1352
	int			error;
L
Linus Torvalds 已提交
1353

L
Lachlan McIlroy 已提交
1354 1355
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1356

L
Lachlan McIlroy 已提交
1357 1358 1359 1360
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1361

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

J
Jan Kara 已提交
1364 1365 1366
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1367 1368 1369 1370 1371 1372 1373 1374 1375

	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) {
1376
		xfs_trans_cancel(tp);
1377 1378 1379
		goto out_drop_write;
	}

1380
	error = xfs_trans_commit(tp);
1381
out_drop_write:
J
Jan Kara 已提交
1382
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1383
	return error;
L
Linus Torvalds 已提交
1384 1385
}

1386 1387 1388
STATIC int
xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
{
1389
	struct getbmap __user	*base = (struct getbmap __user *)*ap;
1390 1391 1392

	/* copy only getbmap portion (not getbmapx) */
	if (copy_to_user(base, bmv, sizeof(struct getbmap)))
D
Dave Chinner 已提交
1393
		return -EFAULT;
1394 1395 1396 1397 1398

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

L
Linus Torvalds 已提交
1399 1400
STATIC int
xfs_ioc_getbmap(
1401
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1402 1403 1404 1405
	int			ioflags,
	unsigned int		cmd,
	void			__user *arg)
{
1406
	struct getbmapx		bmx;
L
Linus Torvalds 已提交
1407 1408
	int			error;

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

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

1415
	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
D
Dave Chinner 已提交
1416
	if (ioflags & XFS_IO_INVIS)
1417
		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
L
Linus Torvalds 已提交
1418

1419
	error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
1420
			    (__force struct getbmap *)arg+1);
L
Linus Torvalds 已提交
1421
	if (error)
D
Dave Chinner 已提交
1422
		return error;
L
Linus Torvalds 已提交
1423

1424 1425
	/* copy back header - only size of getbmap */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
E
Eric Sandeen 已提交
1426
		return -EFAULT;
L
Linus Torvalds 已提交
1427 1428 1429
	return 0;
}

1430 1431 1432
STATIC int
xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
{
1433
	struct getbmapx __user	*base = (struct getbmapx __user *)*ap;
1434 1435

	if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
D
Dave Chinner 已提交
1436
		return -EFAULT;
1437 1438 1439 1440 1441

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

L
Linus Torvalds 已提交
1442 1443
STATIC int
xfs_ioc_getbmapx(
1444
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1445 1446 1447 1448 1449 1450
	void			__user *arg)
{
	struct getbmapx		bmx;
	int			error;

	if (copy_from_user(&bmx, arg, sizeof(bmx)))
E
Eric Sandeen 已提交
1451
		return -EFAULT;
L
Linus Torvalds 已提交
1452 1453

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

1456
	if (bmx.bmv_iflags & (~BMV_IF_VALID))
E
Eric Sandeen 已提交
1457
		return -EINVAL;
L
Linus Torvalds 已提交
1458

1459
	error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
1460
			    (__force struct getbmapx *)arg+1);
L
Linus Torvalds 已提交
1461
	if (error)
D
Dave Chinner 已提交
1462
		return error;
L
Linus Torvalds 已提交
1463

1464 1465
	/* copy back header */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
E
Eric Sandeen 已提交
1466
		return -EFAULT;
L
Linus Torvalds 已提交
1467 1468 1469

	return 0;
}
L
Lachlan McIlroy 已提交
1470

D
Dave Chinner 已提交
1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
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 已提交
1482
		error = -EINVAL;
D
Dave Chinner 已提交
1483 1484 1485 1486 1487 1488
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1489
		error = -EBADF;
D
Dave Chinner 已提交
1490 1491 1492 1493 1494
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1495
		error = -EINVAL;
D
Dave Chinner 已提交
1496 1497 1498 1499 1500 1501
		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 已提交
1502
		error = -EBADF;
D
Dave Chinner 已提交
1503 1504 1505 1506 1507
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1508
		error = -EINVAL;
D
Dave Chinner 已提交
1509 1510 1511 1512 1513 1514 1515
		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 已提交
1516
		error = -EINVAL;
D
Dave Chinner 已提交
1517 1518 1519 1520
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1521
		error = -EINVAL;
D
Dave Chinner 已提交
1522 1523 1524 1525
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1526
		error = -EIO;
D
Dave Chinner 已提交
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
		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;
}

1540 1541 1542 1543 1544 1545 1546 1547
/*
 * 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 已提交
1548 1549
	struct file		*filp,
	unsigned int		cmd,
1550
	unsigned long		p)
L
Lachlan McIlroy 已提交
1551
{
A
Al Viro 已提交
1552
	struct inode		*inode = file_inode(filp);
1553 1554 1555 1556
	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 已提交
1557 1558
	int			error;

1559
	if (filp->f_mode & FMODE_NOCMTIME)
D
Dave Chinner 已提交
1560
		ioflags |= XFS_IO_INVIS;
L
Lachlan McIlroy 已提交
1561

C
Christoph Hellwig 已提交
1562
	trace_xfs_file_ioctl(ip);
1563 1564

	switch (cmd) {
C
Christoph Hellwig 已提交
1565 1566
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
L
Lachlan McIlroy 已提交
1567 1568 1569 1570 1571 1572 1573
	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 已提交
1574 1575
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1576
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1577

1578
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1579
			return -EFAULT;
1580 1581
		return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf);
	}
L
Lachlan McIlroy 已提交
1582 1583 1584 1585 1586 1587
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1588
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1589 1590 1591
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

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

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1627 1628 1629 1630
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1631 1632
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1633
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1634
		return error;
L
Lachlan McIlroy 已提交
1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
	}

	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:
1646 1647
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1648

1649
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1650
			return -EFAULT;
1651 1652 1653 1654
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1655

1656
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1657
			return -EFAULT;
1658
		return xfs_open_by_handle(filp, &hreq);
1659
	}
L
Lachlan McIlroy 已提交
1660
	case XFS_IOC_FSSETDM_BY_HANDLE:
1661
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1662

1663 1664
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1665

1666
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1667
			return -EFAULT;
1668
		return xfs_readlink_by_handle(filp, &hreq);
1669
	}
L
Lachlan McIlroy 已提交
1670
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1671
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1672 1673

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1674
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1675 1676

	case XFS_IOC_SWAPEXT: {
1677 1678 1679
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
1680
			return -EFAULT;
J
Jan Kara 已提交
1681 1682 1683
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
1684
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
1685
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1686
		return error;
L
Lachlan McIlroy 已提交
1687 1688 1689 1690 1691 1692 1693
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1694
			return error;
L
Lachlan McIlroy 已提交
1695 1696

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1697
			return -EFAULT;
L
Lachlan McIlroy 已提交
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707
		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 已提交
1708
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1709
			return -EROFS;
E
Eric Sandeen 已提交
1710

L
Lachlan McIlroy 已提交
1711
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1712
			return -EFAULT;
L
Lachlan McIlroy 已提交
1713

J
Jan Kara 已提交
1714 1715 1716 1717
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1718 1719 1720
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
1721
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
1722
		if (error)
D
Dave Chinner 已提交
1723
			return error;
L
Lachlan McIlroy 已提交
1724 1725

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
1726
			return -EFAULT;
L
Lachlan McIlroy 已提交
1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737
		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 已提交
1738
			return error;
L
Lachlan McIlroy 已提交
1739 1740

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1741
			return -EFAULT;
L
Lachlan McIlroy 已提交
1742 1743 1744 1745 1746 1747 1748 1749

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

J
Jan Kara 已提交
1752 1753 1754
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1755
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
1756
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1757
		return error;
L
Lachlan McIlroy 已提交
1758 1759 1760 1761 1762 1763
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

J
Jan Kara 已提交
1766 1767 1768
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1769
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
1770
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1771
		return error;
L
Lachlan McIlroy 已提交
1772 1773 1774 1775 1776 1777
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
1780 1781 1782
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1783
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
1784
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1785
		return error;
L
Lachlan McIlroy 已提交
1786 1787 1788 1789 1790 1791 1792 1793 1794
	}

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

D
Dave Chinner 已提交
1797
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
1798 1799 1800 1801 1802 1803 1804 1805 1806
	}

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

D
Dave Chinner 已提交
1809
		return xfs_errortag_add(in.errtag, mp);
L
Lachlan McIlroy 已提交
1810 1811 1812 1813 1814 1815
	}

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

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

1818
	case XFS_IOC_FREE_EOFBLOCKS: {
1819 1820
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
1821

1822 1823 1824 1825
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

1828
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
1829
			return -EFAULT;
1830

1831 1832
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
1833
			return error;
1834

D
Dave Chinner 已提交
1835
		return xfs_icache_free_eofblocks(mp, &keofb);
1836 1837
	}

L
Lachlan McIlroy 已提交
1838 1839 1840 1841
	default:
		return -ENOTTY;
	}
}