xfs_ioctl.c 42.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
Eryu Guan 已提交
235
		error = -EPERM;
236
		goto out_dput;
L
Linus Torvalds 已提交
237 238 239
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

D
Dave Chinner 已提交
327 328 329 330 331 332 333
	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);

	ip->i_d.di_dmevmask = evmask;
	ip->i_d.di_dmstate  = state;

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
334
	error = xfs_trans_commit(tp);
D
Dave Chinner 已提交
335 336 337 338

	return error;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

423 424 425 426 427
	if (copy_to_user(&p->pos, cursor, sizeof(attrlist_cursor_kern_t))) {
		error = -EFAULT;
		goto out_kfree;
	}

L
Linus Torvalds 已提交
428 429 430
	if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
		error = -EFAULT;

431 432 433
out_kfree:
	kmem_free(kbuf);
out_dput:
434 435
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
436 437
}

438
int
L
Linus Torvalds 已提交
439
xfs_attrmulti_attr_get(
440
	struct inode		*inode,
441 442
	unsigned char		*name,
	unsigned char		__user *ubuf,
L
Linus Torvalds 已提交
443 444 445
	__uint32_t		*len,
	__uint32_t		flags)
{
446
	unsigned char		*kbuf;
D
Dave Chinner 已提交
447
	int			error = -EFAULT;
448

449
	if (*len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
450
		return -EINVAL;
451 452
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
D
Dave Chinner 已提交
453
		return -ENOMEM;
L
Linus Torvalds 已提交
454

455
	error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
L
Linus Torvalds 已提交
456 457 458 459
	if (error)
		goto out_kfree;

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

462 463
out_kfree:
	kmem_free(kbuf);
L
Linus Torvalds 已提交
464 465 466
	return error;
}

467
int
L
Linus Torvalds 已提交
468
xfs_attrmulti_attr_set(
469
	struct inode		*inode,
470 471
	unsigned char		*name,
	const unsigned char	__user *ubuf,
L
Linus Torvalds 已提交
472 473 474
	__uint32_t		len,
	__uint32_t		flags)
{
475
	unsigned char		*kbuf;
476
	int			error;
L
Linus Torvalds 已提交
477

478
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
479
		return -EPERM;
480
	if (len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
481
		return -EINVAL;
L
Linus Torvalds 已提交
482

L
Li Zefan 已提交
483 484 485
	kbuf = memdup_user(ubuf, len);
	if (IS_ERR(kbuf))
		return PTR_ERR(kbuf);
486

487
	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
488 489
	if (!error)
		xfs_forget_acl(inode, name, flags);
490 491
	kfree(kbuf);
	return error;
L
Linus Torvalds 已提交
492 493
}

494
int
L
Linus Torvalds 已提交
495
xfs_attrmulti_attr_remove(
496
	struct inode		*inode,
497
	unsigned char		*name,
L
Linus Torvalds 已提交
498 499
	__uint32_t		flags)
{
500 501
	int			error;

502
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
503
		return -EPERM;
504 505 506 507
	error = xfs_attr_remove(XFS_I(inode), name, flags);
	if (!error)
		xfs_forget_acl(inode, name, flags);
	return error;
L
Linus Torvalds 已提交
508 509 510 511
}

STATIC int
xfs_attrmulti_by_handle(
512
	struct file		*parfilp,
513
	void			__user *arg)
L
Linus Torvalds 已提交
514 515 516 517
{
	int			error;
	xfs_attr_multiop_t	*ops;
	xfs_fsop_attrmulti_handlereq_t am_hreq;
518
	struct dentry		*dentry;
L
Linus Torvalds 已提交
519
	unsigned int		i, size;
520
	unsigned char		*attr_name;
L
Linus Torvalds 已提交
521 522

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
523
		return -EPERM;
L
Linus Torvalds 已提交
524
	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
E
Eric Sandeen 已提交
525
		return -EFAULT;
L
Linus Torvalds 已提交
526

527 528 529 530
	/* overflow check */
	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
		return -E2BIG;

531 532 533
	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
534

D
Dave Chinner 已提交
535
	error = -E2BIG;
C
Christoph Hellwig 已提交
536
	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
L
Linus Torvalds 已提交
537
	if (!size || size > 16 * PAGE_SIZE)
538
		goto out_dput;
L
Linus Torvalds 已提交
539

L
Li Zefan 已提交
540 541
	ops = memdup_user(am_hreq.ops, size);
	if (IS_ERR(ops)) {
D
Dave Chinner 已提交
542
		error = PTR_ERR(ops);
543
		goto out_dput;
L
Li Zefan 已提交
544
	}
L
Linus Torvalds 已提交
545

D
Dave Chinner 已提交
546
	error = -ENOMEM;
L
Linus Torvalds 已提交
547 548 549 550 551 552
	attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
	if (!attr_name)
		goto out_kfree_ops;

	error = 0;
	for (i = 0; i < am_hreq.opcount; i++) {
553
		ops[i].am_error = strncpy_from_user((char *)attr_name,
L
Linus Torvalds 已提交
554 555
				ops[i].am_attrname, MAXNAMELEN);
		if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
D
Dave Chinner 已提交
556
			error = -ERANGE;
L
Linus Torvalds 已提交
557 558 559 560 561
		if (ops[i].am_error < 0)
			break;

		switch (ops[i].am_opcode) {
		case ATTR_OP_GET:
562
			ops[i].am_error = xfs_attrmulti_attr_get(
563
					d_inode(dentry), attr_name,
564 565
					ops[i].am_attrvalue, &ops[i].am_length,
					ops[i].am_flags);
L
Linus Torvalds 已提交
566 567
			break;
		case ATTR_OP_SET:
568
			ops[i].am_error = mnt_want_write_file(parfilp);
569 570
			if (ops[i].am_error)
				break;
571
			ops[i].am_error = xfs_attrmulti_attr_set(
572
					d_inode(dentry), attr_name,
573 574
					ops[i].am_attrvalue, ops[i].am_length,
					ops[i].am_flags);
A
Al Viro 已提交
575
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
576 577
			break;
		case ATTR_OP_REMOVE:
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_remove(
582
					d_inode(dentry), attr_name,
583
					ops[i].am_flags);
A
Al Viro 已提交
584
			mnt_drop_write_file(parfilp);
L
Linus Torvalds 已提交
585 586
			break;
		default:
D
Dave Chinner 已提交
587
			ops[i].am_error = -EINVAL;
L
Linus Torvalds 已提交
588 589 590 591
		}
	}

	if (copy_to_user(am_hreq.ops, ops, size))
D
Dave Chinner 已提交
592
		error = -EFAULT;
L
Linus Torvalds 已提交
593 594 595 596

	kfree(attr_name);
 out_kfree_ops:
	kfree(ops);
597 598
 out_dput:
	dput(dentry);
D
Dave Chinner 已提交
599
	return error;
L
Linus Torvalds 已提交
600 601
}

602
int
L
Linus Torvalds 已提交
603 604 605
xfs_ioc_space(
	struct file		*filp,
	unsigned int		cmd,
606
	xfs_flock64_t		*bf)
L
Linus Torvalds 已提交
607
{
608 609
	struct inode		*inode = file_inode(filp);
	struct xfs_inode	*ip = XFS_I(inode);
610
	struct iattr		iattr;
611
	enum xfs_prealloc_flags	flags = 0;
612
	uint			iolock = XFS_IOLOCK_EXCL;
L
Linus Torvalds 已提交
613 614
	int			error;

615 616 617 618 619 620
	/*
	 * 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 已提交
621
		return -EPERM;
622

623
	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
E
Eric Sandeen 已提交
624
		return -EPERM;
L
Linus Torvalds 已提交
625

626
	if (!(filp->f_mode & FMODE_WRITE))
E
Eric Sandeen 已提交
627
		return -EBADF;
L
Linus Torvalds 已提交
628

629
	if (!S_ISREG(inode->i_mode))
E
Eric Sandeen 已提交
630
		return -EINVAL;
L
Linus Torvalds 已提交
631

632 633
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
634
	if (filp->f_mode & FMODE_NOCMTIME)
635 636
		flags |= XFS_PREALLOC_INVISIBLE;

J
Jan Kara 已提交
637 638 639
	error = mnt_want_write_file(filp);
	if (error)
		return error;
640

641
	xfs_ilock(ip, iolock);
642
	error = xfs_break_layouts(inode, &iolock, false);
643 644
	if (error)
		goto out_unlock;
645

646 647 648
	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
	iolock |= XFS_MMAPLOCK_EXCL;

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

	if (bf->l_start < 0 ||
686
	    bf->l_start > inode->i_sb->s_maxbytes ||
687
	    bf->l_start + bf->l_len < 0 ||
688
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
D
Dave Chinner 已提交
689
		error = -EINVAL;
690 691 692 693 694
		goto out_unlock;
	}

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

	if (error)
		goto out_unlock;

733
	error = xfs_update_prealloc_flags(ip, flags);
734 735

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

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 已提交
760
		return -EIO;
L
Linus Torvalds 已提交
761 762

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
E
Eric Sandeen 已提交
763
		return -EFAULT;
L
Linus Torvalds 已提交
764 765

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

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

771
	if (bulkreq.ubuffer == NULL)
E
Eric Sandeen 已提交
772
		return -EINVAL;
773

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

	if (error)
D
Dave Chinner 已提交
786
		return error;
L
Linus Torvalds 已提交
787 788 789 790

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

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
E
Eric Sandeen 已提交
794
			return -EFAULT;
L
Linus Torvalds 已提交
795 796 797 798 799 800 801 802 803 804
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry_v1(
	xfs_mount_t		*mp,
	void			__user *arg)
{
805
	xfs_fsop_geom_t         fsgeo;
L
Linus Torvalds 已提交
806 807
	int			error;

808
	error = xfs_fs_geometry(mp, &fsgeo, 3);
L
Linus Torvalds 已提交
809
	if (error)
D
Dave Chinner 已提交
810
		return error;
L
Linus Torvalds 已提交
811

812 813 814 815 816 817
	/*
	 * 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 已提交
818
		return -EFAULT;
L
Linus Torvalds 已提交
819 820 821 822 823 824 825 826 827 828 829 830 831
	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 已提交
832
		return error;
L
Linus Torvalds 已提交
833 834

	if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
E
Eric Sandeen 已提交
835
		return -EFAULT;
L
Linus Torvalds 已提交
836 837 838 839 840 841 842 843 844 845 846 847 848 849
	return 0;
}

/*
 * Linux extended inode flags interface.
 */

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

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

	return xflags;
}

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

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
881
		flags |= FS_IMMUTABLE_FL;
L
Linus Torvalds 已提交
882
	if (di_flags & XFS_DIFLAG_APPEND)
883
		flags |= FS_APPEND_FL;
L
Linus Torvalds 已提交
884
	if (di_flags & XFS_DIFLAG_SYNC)
885
		flags |= FS_SYNC_FL;
L
Linus Torvalds 已提交
886
	if (di_flags & XFS_DIFLAG_NOATIME)
887
		flags |= FS_NOATIME_FL;
L
Linus Torvalds 已提交
888
	if (di_flags & XFS_DIFLAG_NODUMP)
889
		flags |= FS_NODUMP_FL;
L
Linus Torvalds 已提交
890 891 892
	return flags;
}

893 894 895 896 897 898 899 900
STATIC int
xfs_ioc_fsgetxattr(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;

901 902
	memset(&fa, 0, sizeof(struct fsxattr));

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

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

931 932 933 934 935 936
STATIC void
xfs_set_diflags(
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	unsigned int		di_flags;
937
	uint64_t		di_flags2;
938 939 940

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

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

982 983
}

984 985 986 987
STATIC void
xfs_diflags_to_linux(
	struct xfs_inode	*ip)
{
988
	struct inode		*inode = VFS_I(ip);
989 990
	unsigned int		xflags = xfs_ip2xflags(ip);

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

1012
}
1013

1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
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) &&
1024
	    XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & FS_XFLAG_REALTIME))
1025 1026 1027
		return -EINVAL;

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

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
/*
 * If we are changing DAX flags, we have to ensure the file is clean and any
 * cached objects in the address space are invalidated and removed. This
 * requires us to lock out other IO and page faults similar to a truncate
 * operation. The locks need to be held until the transaction has been committed
 * so that the cache invalidation is atomic with respect to the DAX flag
 * manipulation.
 */
static int
xfs_ioctl_setattr_dax_invalidate(
	struct xfs_inode	*ip,
	struct fsxattr		*fa,
	int			*join_flags)
{
	struct inode		*inode = VFS_I(ip);
	int			error;

	*join_flags = 0;

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

1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
	/* If the DAX state is not changing, we have nothing to do here. */
	if ((fa->fsx_xflags & FS_XFLAG_DAX) && IS_DAX(inode))
		return 0;
	if (!(fa->fsx_xflags & FS_XFLAG_DAX) && !IS_DAX(inode))
		return 0;

	/* lock, flush and invalidate mapping in preparation for flag change */
	xfs_ilock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL);
	error = filemap_write_and_wait(inode->i_mapping);
	if (error)
		goto out_unlock;
	error = invalidate_inode_pages2(inode->i_mapping);
	if (error)
		goto out_unlock;

	*join_flags = XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL;
1098
	return 0;
1099 1100 1101 1102 1103

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

1104 1105
}

1106 1107 1108 1109 1110
/*
 * 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.
1111 1112 1113 1114 1115 1116
 *
 * The inode might already be IO locked on call. If this is the case, it is
 * indicated in @join_flags and we take full responsibility for ensuring they
 * are unlocked from now on. Hence if we have an error here, we still have to
 * unlock them. Otherwise, once they are joined to the transaction, they will
 * be unlocked on commit/cancel.
1117 1118 1119
 */
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
1120 1121
	struct xfs_inode	*ip,
	int			join_flags)
1122 1123 1124
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
1125
	int			error = -EROFS;
1126 1127

	if (mp->m_flags & XFS_MOUNT_RDONLY)
1128 1129
		goto out_unlock;
	error = -EIO;
1130
	if (XFS_FORCED_SHUTDOWN(mp))
1131
		goto out_unlock;
1132

1133
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1134
	if (error)
1135
		return ERR_PTR(error);
1136 1137

	xfs_ilock(ip, XFS_ILOCK_EXCL);
1138 1139
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags);
	join_flags = 0;
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157

	/*
	 * 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:
1158
	xfs_trans_cancel(tp);
1159 1160 1161
out_unlock:
	if (join_flags)
		xfs_iunlock(ip, join_flags);
1162 1163 1164
	return ERR_PTR(error);
}

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

D
Dave Chinner 已提交
1186
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode))
1187 1188
		return -EINVAL;

1189
	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
D
Dave Chinner 已提交
1190
	    !S_ISDIR(VFS_I(ip)->i_mode))
1191 1192
		return -EINVAL;

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

1219 1220 1221
	return 0;
}

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

	return 0;
}
1248 1249 1250 1251

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

C
Christoph Hellwig 已提交
1262
	trace_xfs_ioctl_setattr(ip);
1263

1264 1265 1266
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1267

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

1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
	/*
	 * Changing DAX config may require inode locking for mapping
	 * invalidation. These need to be held all the way to transaction commit
	 * or cancel time, so need to be passed through to
	 * xfs_ioctl_setattr_get_trans() so it can apply them to the join call
	 * appropriately.
	 */
	code = xfs_ioctl_setattr_dax_invalidate(ip, fa, &join_flags);
	if (code)
		goto error_free_dquots;

	tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
1296 1297 1298
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1299 1300 1301
	}


1302 1303 1304 1305 1306
	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 */
1307
			goto error_trans_cancel;
1308 1309
	}

1310 1311 1312
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1313

1314 1315
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1316
		goto error_trans_cancel;
1317 1318

	/*
1319 1320 1321 1322 1323
	 * 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()
1324 1325
	 */

D
Dave Chinner 已提交
1326
	if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
1327
	    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
D
Dave Chinner 已提交
1328
		VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
1329

1330 1331 1332 1333 1334 1335 1336 1337
	/* 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);
1338
	}
1339

1340 1341 1342 1343 1344
	/*
	 * 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.
	 */
1345 1346 1347 1348
	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;
1349

1350
	code = xfs_trans_commit(tp);
1351 1352 1353 1354

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1355 1356
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1357
	xfs_qm_dqrele(pdqp);
1358

C
Christoph Hellwig 已提交
1359
	return code;
1360

1361
error_trans_cancel:
1362
	xfs_trans_cancel(tp);
1363
error_free_dquots:
C
Christoph Hellwig 已提交
1364
	xfs_qm_dqrele(udqp);
1365
	xfs_qm_dqrele(pdqp);
1366 1367 1368
	return code;
}

L
Linus Torvalds 已提交
1369
STATIC int
L
Lachlan McIlroy 已提交
1370
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1371 1372 1373 1374 1375
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1376
	int error;
L
Lachlan McIlroy 已提交
1377 1378 1379

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

J
Jan Kara 已提交
1381 1382 1383
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1384
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1385
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1386
	return error;
L
Lachlan McIlroy 已提交
1387
}
L
Linus Torvalds 已提交
1388

L
Lachlan McIlroy 已提交
1389 1390 1391 1392 1393 1394
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1395

L
Lachlan McIlroy 已提交
1396 1397 1398 1399 1400
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1401

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

L
Lachlan McIlroy 已提交
1414 1415
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1416

L
Lachlan McIlroy 已提交
1417 1418 1419 1420
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1421

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

J
Jan Kara 已提交
1424 1425 1426
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1427

1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439
	/*
	 * Changing DAX config may require inode locking for mapping
	 * invalidation. These need to be held all the way to transaction commit
	 * or cancel time, so need to be passed through to
	 * xfs_ioctl_setattr_get_trans() so it can apply them to the join call
	 * appropriately.
	 */
	error = xfs_ioctl_setattr_dax_invalidate(ip, &fa, &join_flags);
	if (error)
		goto out_drop_write;

	tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
1440 1441 1442 1443 1444 1445 1446
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1447
		xfs_trans_cancel(tp);
1448 1449 1450
		goto out_drop_write;
	}

1451
	error = xfs_trans_commit(tp);
1452
out_drop_write:
J
Jan Kara 已提交
1453
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1454
	return error;
L
Linus Torvalds 已提交
1455 1456
}

1457 1458 1459
STATIC int
xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
{
1460
	struct getbmap __user	*base = (struct getbmap __user *)*ap;
1461 1462 1463

	/* copy only getbmap portion (not getbmapx) */
	if (copy_to_user(base, bmv, sizeof(struct getbmap)))
D
Dave Chinner 已提交
1464
		return -EFAULT;
1465 1466 1467 1468 1469

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

L
Linus Torvalds 已提交
1470 1471
STATIC int
xfs_ioc_getbmap(
1472
	struct file		*file,
L
Linus Torvalds 已提交
1473 1474 1475
	unsigned int		cmd,
	void			__user *arg)
{
1476
	struct getbmapx		bmx;
L
Linus Torvalds 已提交
1477 1478
	int			error;

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

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

1485
	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
1486
	if (file->f_mode & FMODE_NOCMTIME)
1487
		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
L
Linus Torvalds 已提交
1488

1489
	error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, xfs_getbmap_format,
1490
			    (__force struct getbmap *)arg+1);
L
Linus Torvalds 已提交
1491
	if (error)
D
Dave Chinner 已提交
1492
		return error;
L
Linus Torvalds 已提交
1493

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

1500 1501 1502
STATIC int
xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
{
1503
	struct getbmapx __user	*base = (struct getbmapx __user *)*ap;
1504 1505

	if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
D
Dave Chinner 已提交
1506
		return -EFAULT;
1507 1508 1509 1510 1511

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

L
Linus Torvalds 已提交
1512 1513
STATIC int
xfs_ioc_getbmapx(
1514
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1515 1516 1517 1518 1519 1520
	void			__user *arg)
{
	struct getbmapx		bmx;
	int			error;

	if (copy_from_user(&bmx, arg, sizeof(bmx)))
E
Eric Sandeen 已提交
1521
		return -EFAULT;
L
Linus Torvalds 已提交
1522 1523

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

1526
	if (bmx.bmv_iflags & (~BMV_IF_VALID))
E
Eric Sandeen 已提交
1527
		return -EINVAL;
L
Linus Torvalds 已提交
1528

1529
	error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
1530
			    (__force struct getbmapx *)arg+1);
L
Linus Torvalds 已提交
1531
	if (error)
D
Dave Chinner 已提交
1532
		return error;
L
Linus Torvalds 已提交
1533

1534 1535
	/* copy back header */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
E
Eric Sandeen 已提交
1536
		return -EFAULT;
L
Linus Torvalds 已提交
1537 1538 1539

	return 0;
}
L
Lachlan McIlroy 已提交
1540

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

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

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

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

1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592
	/*
	 * We need to ensure that the fds passed in point to XFS inodes
	 * before we cast and access them as XFS structures as we have no
	 * control over what the user passes us here.
	 */
	if (f.file->f_op != &xfs_file_operations ||
	    tmp.file->f_op != &xfs_file_operations) {
		error = -EINVAL;
		goto out_put_tmp_file;
	}

D
Dave Chinner 已提交
1593 1594 1595 1596
	ip = XFS_I(file_inode(f.file));
	tip = XFS_I(file_inode(tmp.file));

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

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

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

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

C
Christoph Hellwig 已提交
1639
	trace_xfs_file_ioctl(ip);
1640 1641

	switch (cmd) {
C
Christoph Hellwig 已提交
1642 1643
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
L
Lachlan McIlroy 已提交
1644 1645 1646 1647 1648 1649 1650
	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 已提交
1651 1652
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1653
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1654

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

1665
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1666 1667 1668
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

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

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

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

L
Lachlan McIlroy 已提交
1708 1709
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1710
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1711
		return error;
L
Lachlan McIlroy 已提交
1712 1713 1714 1715
	}

	case XFS_IOC_GETBMAP:
	case XFS_IOC_GETBMAPA:
1716
		return xfs_ioc_getbmap(filp, cmd, arg);
L
Lachlan McIlroy 已提交
1717 1718 1719 1720 1721 1722

	case XFS_IOC_GETBMAPX:
		return xfs_ioc_getbmapx(ip, arg);

	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
1723 1724
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1725

1726
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1727
			return -EFAULT;
1728 1729 1730 1731
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1732

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

1740 1741
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1742

1743
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1744
			return -EFAULT;
1745
		return xfs_readlink_by_handle(filp, &hreq);
1746
	}
L
Lachlan McIlroy 已提交
1747
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1748
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1749 1750

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1751
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1752 1753

	case XFS_IOC_SWAPEXT: {
1754 1755 1756
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
1757
			return -EFAULT;
J
Jan Kara 已提交
1758 1759 1760
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
1761
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
1762
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1763
		return error;
L
Lachlan McIlroy 已提交
1764 1765 1766 1767 1768 1769 1770
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1771
			return error;
L
Lachlan McIlroy 已提交
1772 1773

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1774
			return -EFAULT;
L
Lachlan McIlroy 已提交
1775 1776 1777 1778 1779 1780 1781 1782 1783 1784
		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 已提交
1785
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1786
			return -EROFS;
E
Eric Sandeen 已提交
1787

L
Lachlan McIlroy 已提交
1788
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1789
			return -EFAULT;
L
Lachlan McIlroy 已提交
1790

J
Jan Kara 已提交
1791 1792 1793 1794
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1795 1796 1797
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
1798
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
1799
		if (error)
D
Dave Chinner 已提交
1800
			return error;
L
Lachlan McIlroy 已提交
1801 1802

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
1803
			return -EFAULT;
L
Lachlan McIlroy 已提交
1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814
		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 已提交
1815
			return error;
L
Lachlan McIlroy 已提交
1816 1817

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1818
			return -EFAULT;
L
Lachlan McIlroy 已提交
1819 1820 1821 1822 1823 1824 1825 1826

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

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

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

J
Jan Kara 已提交
1843 1844 1845
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1846
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
1847
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1848
		return error;
L
Lachlan McIlroy 已提交
1849 1850 1851 1852 1853 1854
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
1857 1858 1859
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1860
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
1861
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1862
		return error;
L
Lachlan McIlroy 已提交
1863 1864 1865 1866 1867 1868 1869 1870 1871
	}

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

D
Dave Chinner 已提交
1874
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
1875 1876 1877 1878 1879 1880 1881 1882 1883
	}

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

D
Dave Chinner 已提交
1886
		return xfs_errortag_add(in.errtag, mp);
L
Lachlan McIlroy 已提交
1887 1888 1889 1890 1891 1892
	}

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

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

1895
	case XFS_IOC_FREE_EOFBLOCKS: {
1896 1897
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
1898

1899 1900 1901 1902
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

1905
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
1906
			return -EFAULT;
1907

1908 1909
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
1910
			return error;
1911

D
Dave Chinner 已提交
1912
		return xfs_icache_free_eofblocks(mp, &keofb);
1913 1914
	}

L
Lachlan McIlroy 已提交
1915 1916 1917 1918
	default:
		return -ENOTTY;
	}
}