xfs_ioctl.c 48.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"
44 45 46
#include "xfs_btree.h"
#include <linux/fsmap.h>
#include "xfs_fsmap.h"
47
#include "scrub/xfs_scrub.h"
48
#include "xfs_sb.h"
L
Linus Torvalds 已提交
49

50
#include <linux/capability.h>
51
#include <linux/cred.h>
L
Linus Torvalds 已提交
52 53 54 55
#include <linux/dcache.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
56
#include <linux/slab.h>
57
#include <linux/exportfs.h>
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65 66 67 68 69

/*
 * 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
 */
70
int
L
Linus Torvalds 已提交
71 72
xfs_find_handle(
	unsigned int		cmd,
73
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
74 75 76 77
{
	int			hsize;
	xfs_handle_t		handle;
	struct inode		*inode;
78
	struct fd		f = {NULL};
C
Christoph Hellwig 已提交
79
	struct path		path;
80
	int			error;
C
Christoph Hellwig 已提交
81
	struct xfs_inode	*ip;
L
Linus Torvalds 已提交
82

C
Christoph Hellwig 已提交
83
	if (cmd == XFS_IOC_FD_TO_HANDLE) {
84 85
		f = fdget(hreq->fd);
		if (!f.file)
C
Christoph Hellwig 已提交
86
			return -EBADF;
A
Al Viro 已提交
87
		inode = file_inode(f.file);
C
Christoph Hellwig 已提交
88 89 90 91
	} else {
		error = user_lpath((const char __user *)hreq->path, &path);
		if (error)
			return error;
92
		inode = d_inode(path.dentry);
L
Linus Torvalds 已提交
93
	}
C
Christoph Hellwig 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
	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 已提交
120 121 122
		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
					sizeof(handle.ha_fid.fid_len);
		handle.ha_fid.fid_pad = 0;
123
		handle.ha_fid.fid_gen = inode->i_generation;
C
Christoph Hellwig 已提交
124
		handle.ha_fid.fid_ino = ip->i_ino;
C
Christoph Hellwig 已提交
125
		hsize = sizeof(xfs_handle_t);
L
Linus Torvalds 已提交
126 127
	}

C
Christoph Hellwig 已提交
128
	error = -EFAULT;
129
	if (copy_to_user(hreq->ohandle, &handle, hsize) ||
C
Christoph Hellwig 已提交
130 131
	    copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
		goto out_put;
L
Linus Torvalds 已提交
132

C
Christoph Hellwig 已提交
133 134 135 136
	error = 0;

 out_put:
	if (cmd == XFS_IOC_FD_TO_HANDLE)
137
		fdput(f);
C
Christoph Hellwig 已提交
138 139 140
	else
		path_put(&path);
	return error;
L
Linus Torvalds 已提交
141 142 143
}

/*
144 145
 * No need to do permission checks on the various pathname components
 * as the handle operations are privileged.
L
Linus Torvalds 已提交
146 147
 */
STATIC int
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
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 已提交
163 164
{
	xfs_handle_t		handle;
165
	struct xfs_fid64	fid;
L
Linus Torvalds 已提交
166 167 168 169

	/*
	 * Only allow handle opens under a directory.
	 */
A
Al Viro 已提交
170
	if (!S_ISDIR(file_inode(parfilp)->i_mode))
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
		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 已提交
189

190 191 192 193 194 195
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 已提交
196 197
}

198
int
L
Linus Torvalds 已提交
199 200
xfs_open_by_handle(
	struct file		*parfilp,
201
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
202
{
203
	const struct cred	*cred = current_cred();
L
Linus Torvalds 已提交
204
	int			error;
205
	int			fd;
L
Linus Torvalds 已提交
206 207 208 209
	int			permflag;
	struct file		*filp;
	struct inode		*inode;
	struct dentry		*dentry;
210
	fmode_t			fmode;
211
	struct path		path;
L
Linus Torvalds 已提交
212 213

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

216 217 218
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
219
	inode = d_inode(dentry);
L
Linus Torvalds 已提交
220 221 222

	/* Restrict xfs_open_by_handle to directories & regular files. */
	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
E
Eric Sandeen 已提交
223
		error = -EPERM;
224
		goto out_dput;
L
Linus Torvalds 已提交
225 226 227
	}

#if BITS_PER_LONG != 32
228
	hreq->oflags |= O_LARGEFILE;
L
Linus Torvalds 已提交
229
#endif
230

231
	permflag = hreq->oflags;
232
	fmode = OPEN_FMODE(permflag);
L
Linus Torvalds 已提交
233
	if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
234
	    (fmode & FMODE_WRITE) && IS_APPEND(inode)) {
E
Eric Sandeen 已提交
235
		error = -EPERM;
236
		goto out_dput;
L
Linus Torvalds 已提交
237 238
	}

239
	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
E
Eryu Guan 已提交
240
		error = -EPERM;
241
		goto out_dput;
L
Linus Torvalds 已提交
242 243 244
	}

	/* Can't write directories. */
245
	if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {
E
Eric Sandeen 已提交
246
		error = -EISDIR;
247
		goto out_dput;
L
Linus Torvalds 已提交
248 249
	}

250
	fd = get_unused_fd_flags(0);
251 252 253
	if (fd < 0) {
		error = fd;
		goto out_dput;
L
Linus Torvalds 已提交
254 255
	}

256 257 258 259
	path.mnt = parfilp->f_path.mnt;
	path.dentry = dentry;
	filp = dentry_open(&path, hreq->oflags, cred);
	dput(dentry);
L
Linus Torvalds 已提交
260
	if (IS_ERR(filp)) {
261 262
		put_unused_fd(fd);
		return PTR_ERR(filp);
L
Linus Torvalds 已提交
263
	}
264

A
Al Viro 已提交
265
	if (S_ISREG(inode->i_mode)) {
266
		filp->f_flags |= O_NOATIME;
267
		filp->f_mode |= FMODE_NOCMTIME;
268
	}
L
Linus Torvalds 已提交
269

270 271 272 273 274 275
	fd_install(fd, filp);
	return fd;

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

278
int
L
Linus Torvalds 已提交
279
xfs_readlink_by_handle(
280 281
	struct file		*parfilp,
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
282
{
283
	struct dentry		*dentry;
L
Linus Torvalds 已提交
284
	__u32			olen;
285
	int			error;
L
Linus Torvalds 已提交
286 287

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

290 291 292
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
293 294

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

300
	if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
E
Eric Sandeen 已提交
301
		error = -EFAULT;
302
		goto out_dput;
L
Linus Torvalds 已提交
303 304
	}

305
	error = vfs_readlink(dentry, hreq->ohandle, olen);
306

307 308
 out_dput:
	dput(dentry);
309
	return error;
L
Linus Torvalds 已提交
310 311
}

D
Dave Chinner 已提交
312 313 314
int
xfs_set_dmattrs(
	xfs_inode_t     *ip,
D
Darrick J. Wong 已提交
315 316
	uint		evmask,
	uint16_t	state)
D
Dave Chinner 已提交
317 318 319 320 321 322
{
	xfs_mount_t	*mp = ip->i_mount;
	xfs_trans_t	*tp;
	int		error;

	if (!capable(CAP_SYS_ADMIN))
D
Dave Chinner 已提交
323
		return -EPERM;
D
Dave Chinner 已提交
324 325

	if (XFS_FORCED_SHUTDOWN(mp))
D
Dave Chinner 已提交
326
		return -EIO;
D
Dave Chinner 已提交
327

328 329
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
	if (error)
D
Dave Chinner 已提交
330
		return error;
331

D
Dave Chinner 已提交
332 333 334 335 336 337 338
	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);
339
	error = xfs_trans_commit(tp);
D
Dave Chinner 已提交
340 341 342 343

	return error;
}

L
Linus Torvalds 已提交
344 345
STATIC int
xfs_fssetdm_by_handle(
346 347
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
348 349 350 351
{
	int			error;
	struct fsdmidata	fsd;
	xfs_fsop_setdm_handlereq_t dmhreq;
352
	struct dentry		*dentry;
L
Linus Torvalds 已提交
353 354

	if (!capable(CAP_MKNOD))
E
Eric Sandeen 已提交
355
		return -EPERM;
L
Linus Torvalds 已提交
356
	if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
E
Eric Sandeen 已提交
357
		return -EFAULT;
L
Linus Torvalds 已提交
358

J
Jan Kara 已提交
359 360 361 362
	error = mnt_want_write_file(parfilp);
	if (error)
		return error;

363
	dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
J
Jan Kara 已提交
364 365
	if (IS_ERR(dentry)) {
		mnt_drop_write_file(parfilp);
366
		return PTR_ERR(dentry);
J
Jan Kara 已提交
367
	}
L
Linus Torvalds 已提交
368

369
	if (IS_IMMUTABLE(d_inode(dentry)) || IS_APPEND(d_inode(dentry))) {
E
Eric Sandeen 已提交
370
		error = -EPERM;
371
		goto out;
L
Linus Torvalds 已提交
372 373 374
	}

	if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
E
Eric Sandeen 已提交
375
		error = -EFAULT;
376
		goto out;
L
Linus Torvalds 已提交
377 378
	}

379
	error = xfs_set_dmattrs(XFS_I(d_inode(dentry)), fsd.fsd_dmevmask,
380
				 fsd.fsd_dmstate);
L
Linus Torvalds 已提交
381

382
 out:
J
Jan Kara 已提交
383
	mnt_drop_write_file(parfilp);
384
	dput(dentry);
385
	return error;
L
Linus Torvalds 已提交
386 387 388 389
}

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

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

408 409 410 411
	/*
	 * Reject flags, only allow namespaces.
	 */
	if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
E
Eric Sandeen 已提交
412
		return -EINVAL;
413

414 415 416
	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
417

418 419 420
	kbuf = kmem_zalloc_large(al_hreq.buflen, KM_SLEEP);
	if (!kbuf)
		goto out_dput;
L
Linus Torvalds 已提交
421 422

	cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
423
	error = xfs_attr_list(XFS_I(d_inode(dentry)), kbuf, al_hreq.buflen,
424
					al_hreq.flags, cursor);
L
Linus Torvalds 已提交
425 426 427
	if (error)
		goto out_kfree;

428 429 430 431 432
	if (copy_to_user(&p->pos, cursor, sizeof(attrlist_cursor_kern_t))) {
		error = -EFAULT;
		goto out_kfree;
	}

L
Linus Torvalds 已提交
433 434 435
	if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
		error = -EFAULT;

436 437 438
out_kfree:
	kmem_free(kbuf);
out_dput:
439 440
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
441 442
}

443
int
L
Linus Torvalds 已提交
444
xfs_attrmulti_attr_get(
445
	struct inode		*inode,
446 447
	unsigned char		*name,
	unsigned char		__user *ubuf,
448 449
	uint32_t		*len,
	uint32_t		flags)
L
Linus Torvalds 已提交
450
{
451
	unsigned char		*kbuf;
D
Dave Chinner 已提交
452
	int			error = -EFAULT;
453

454
	if (*len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
455
		return -EINVAL;
456 457
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
D
Dave Chinner 已提交
458
		return -ENOMEM;
L
Linus Torvalds 已提交
459

460
	error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
L
Linus Torvalds 已提交
461 462 463 464
	if (error)
		goto out_kfree;

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

467 468
out_kfree:
	kmem_free(kbuf);
L
Linus Torvalds 已提交
469 470 471
	return error;
}

472
int
L
Linus Torvalds 已提交
473
xfs_attrmulti_attr_set(
474
	struct inode		*inode,
475 476
	unsigned char		*name,
	const unsigned char	__user *ubuf,
477 478
	uint32_t		len,
	uint32_t		flags)
L
Linus Torvalds 已提交
479
{
480
	unsigned char		*kbuf;
481
	int			error;
L
Linus Torvalds 已提交
482

483
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
484
		return -EPERM;
485
	if (len > XFS_XATTR_SIZE_MAX)
D
Dave Chinner 已提交
486
		return -EINVAL;
L
Linus Torvalds 已提交
487

L
Li Zefan 已提交
488 489 490
	kbuf = memdup_user(ubuf, len);
	if (IS_ERR(kbuf))
		return PTR_ERR(kbuf);
491

492
	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
493 494
	if (!error)
		xfs_forget_acl(inode, name, flags);
495 496
	kfree(kbuf);
	return error;
L
Linus Torvalds 已提交
497 498
}

499
int
L
Linus Torvalds 已提交
500
xfs_attrmulti_attr_remove(
501
	struct inode		*inode,
502
	unsigned char		*name,
503
	uint32_t		flags)
L
Linus Torvalds 已提交
504
{
505 506
	int			error;

507
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
508
		return -EPERM;
509 510 511 512
	error = xfs_attr_remove(XFS_I(inode), name, flags);
	if (!error)
		xfs_forget_acl(inode, name, flags);
	return error;
L
Linus Torvalds 已提交
513 514 515 516
}

STATIC int
xfs_attrmulti_by_handle(
517
	struct file		*parfilp,
518
	void			__user *arg)
L
Linus Torvalds 已提交
519 520 521 522
{
	int			error;
	xfs_attr_multiop_t	*ops;
	xfs_fsop_attrmulti_handlereq_t am_hreq;
523
	struct dentry		*dentry;
L
Linus Torvalds 已提交
524
	unsigned int		i, size;
525
	unsigned char		*attr_name;
L
Linus Torvalds 已提交
526 527

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
528
		return -EPERM;
L
Linus Torvalds 已提交
529
	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
E
Eric Sandeen 已提交
530
		return -EFAULT;
L
Linus Torvalds 已提交
531

532 533 534 535
	/* overflow check */
	if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
		return -E2BIG;

536 537 538
	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
539

D
Dave Chinner 已提交
540
	error = -E2BIG;
C
Christoph Hellwig 已提交
541
	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
L
Linus Torvalds 已提交
542
	if (!size || size > 16 * PAGE_SIZE)
543
		goto out_dput;
L
Linus Torvalds 已提交
544

L
Li Zefan 已提交
545 546
	ops = memdup_user(am_hreq.ops, size);
	if (IS_ERR(ops)) {
D
Dave Chinner 已提交
547
		error = PTR_ERR(ops);
548
		goto out_dput;
L
Li Zefan 已提交
549
	}
L
Linus Torvalds 已提交
550

D
Dave Chinner 已提交
551
	error = -ENOMEM;
L
Linus Torvalds 已提交
552 553 554 555 556 557
	attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
	if (!attr_name)
		goto out_kfree_ops;

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

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

	if (copy_to_user(am_hreq.ops, ops, size))
D
Dave Chinner 已提交
597
		error = -EFAULT;
L
Linus Torvalds 已提交
598 599 600 601

	kfree(attr_name);
 out_kfree_ops:
	kfree(ops);
602 603
 out_dput:
	dput(dentry);
D
Dave Chinner 已提交
604
	return error;
L
Linus Torvalds 已提交
605 606
}

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

620 621 622 623 624 625
	/*
	 * 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 已提交
626
		return -EPERM;
627

628
	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
E
Eric Sandeen 已提交
629
		return -EPERM;
L
Linus Torvalds 已提交
630

631
	if (!(filp->f_mode & FMODE_WRITE))
E
Eric Sandeen 已提交
632
		return -EBADF;
L
Linus Torvalds 已提交
633

634
	if (!S_ISREG(inode->i_mode))
E
Eric Sandeen 已提交
635
		return -EINVAL;
L
Linus Torvalds 已提交
636

637 638
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
639
	if (filp->f_mode & FMODE_NOCMTIME)
640 641
		flags |= XFS_PREALLOC_INVISIBLE;

J
Jan Kara 已提交
642 643 644
	error = mnt_want_write_file(filp);
	if (error)
		return error;
645

646
	xfs_ilock(ip, iolock);
647
	error = xfs_break_layouts(inode, &iolock);
648 649
	if (error)
		goto out_unlock;
650

651 652 653
	xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
	iolock |= XFS_MMAPLOCK_EXCL;

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

	if (bf->l_start < 0 ||
691
	    bf->l_start > inode->i_sb->s_maxbytes ||
692
	    bf->l_start + bf->l_len < 0 ||
693
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
D
Dave Chinner 已提交
694
		error = -EINVAL;
695 696 697 698 699
		goto out_unlock;
	}

	switch (cmd) {
	case XFS_IOC_ZERO_RANGE:
700
		flags |= XFS_PREALLOC_SET;
701 702 703 704
		error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
		break;
	case XFS_IOC_RESVSP:
	case XFS_IOC_RESVSP64:
705
		flags |= XFS_PREALLOC_SET;
706 707 708 709 710 711 712 713 714 715 716
		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:
717
		flags |= XFS_PREALLOC_CLEAR;
718 719 720 721 722 723 724 725 726 727
		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;

728
		error = xfs_vn_setattr_size(file_dentry(filp), &iattr);
729 730 731
		break;
	default:
		ASSERT(0);
D
Dave Chinner 已提交
732
		error = -EINVAL;
733 734 735 736 737
	}

	if (error)
		goto out_unlock;

738
	error = xfs_update_prealloc_flags(ip, flags);
739 740

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

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 已提交
765
		return -EIO;
L
Linus Torvalds 已提交
766 767

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
E
Eric Sandeen 已提交
768
		return -EFAULT;
L
Linus Torvalds 已提交
769 770

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

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

776
	if (bulkreq.ubuffer == NULL)
E
Eric Sandeen 已提交
777
		return -EINVAL;
778

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

	if (error)
D
Dave Chinner 已提交
791
		return error;
L
Linus Torvalds 已提交
792 793 794 795

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

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
E
Eric Sandeen 已提交
799
			return -EFAULT;
L
Linus Torvalds 已提交
800 801 802 803 804 805 806 807 808 809
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry_v1(
	xfs_mount_t		*mp,
	void			__user *arg)
{
810
	xfs_fsop_geom_t         fsgeo;
L
Linus Torvalds 已提交
811 812
	int			error;

813
	error = xfs_fs_geometry(&mp->m_sb, &fsgeo, 3);
L
Linus Torvalds 已提交
814
	if (error)
D
Dave Chinner 已提交
815
		return error;
L
Linus Torvalds 已提交
816

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

STATIC int
xfs_ioc_fsgeometry(
	xfs_mount_t		*mp,
	void			__user *arg)
{
	xfs_fsop_geom_t		fsgeo;
	int			error;

835
	error = xfs_fs_geometry(&mp->m_sb, &fsgeo, 4);
L
Linus Torvalds 已提交
836
	if (error)
D
Dave Chinner 已提交
837
		return error;
L
Linus Torvalds 已提交
838 839

	if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
E
Eric Sandeen 已提交
840
		return -EFAULT;
L
Linus Torvalds 已提交
841 842 843 844 845 846 847 848 849 850 851 852 853 854
	return 0;
}

/*
 * Linux extended inode flags interface.
 */

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

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

	return xflags;
}

STATIC unsigned int
xfs_di2lxflags(
881
	uint16_t	di_flags)
L
Linus Torvalds 已提交
882 883 884 885
{
	unsigned int	flags = 0;

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
886
		flags |= FS_IMMUTABLE_FL;
L
Linus Torvalds 已提交
887
	if (di_flags & XFS_DIFLAG_APPEND)
888
		flags |= FS_APPEND_FL;
L
Linus Torvalds 已提交
889
	if (di_flags & XFS_DIFLAG_SYNC)
890
		flags |= FS_SYNC_FL;
L
Linus Torvalds 已提交
891
	if (di_flags & XFS_DIFLAG_NOATIME)
892
		flags |= FS_NOATIME_FL;
L
Linus Torvalds 已提交
893
	if (di_flags & XFS_DIFLAG_NODUMP)
894
		flags |= FS_NODUMP_FL;
L
Linus Torvalds 已提交
895 896 897
	return flags;
}

898 899 900 901 902 903 904 905
STATIC int
xfs_ioc_fsgetxattr(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;

906 907
	memset(&fa, 0, sizeof(struct fsxattr));

908 909 910
	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;
911 912
	fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
			ip->i_mount->m_sb.sb_blocklog;
913
	fa.fsx_projid = xfs_get_projid(ip);
914 915 916 917

	if (attr) {
		if (ip->i_afp) {
			if (ip->i_afp->if_flags & XFS_IFEXTENTS)
918
				fa.fsx_nextents = xfs_iext_count(ip->i_afp);
919 920 921 922 923 924
			else
				fa.fsx_nextents = ip->i_d.di_anextents;
		} else
			fa.fsx_nextents = 0;
	} else {
		if (ip->i_df.if_flags & XFS_IFEXTENTS)
925
			fa.fsx_nextents = xfs_iext_count(&ip->i_df);
926 927 928 929 930 931 932 933 934 935
		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;
}

936 937
STATIC uint16_t
xfs_flags2diflags(
938 939 940 941
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	/* can't set PREALLOC this way, just preserve it */
942 943 944
	uint16_t		di_flags =
		(ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);

945
	if (xflags & FS_XFLAG_IMMUTABLE)
946
		di_flags |= XFS_DIFLAG_IMMUTABLE;
947
	if (xflags & FS_XFLAG_APPEND)
948
		di_flags |= XFS_DIFLAG_APPEND;
949
	if (xflags & FS_XFLAG_SYNC)
950
		di_flags |= XFS_DIFLAG_SYNC;
951
	if (xflags & FS_XFLAG_NOATIME)
952
		di_flags |= XFS_DIFLAG_NOATIME;
953
	if (xflags & FS_XFLAG_NODUMP)
954
		di_flags |= XFS_DIFLAG_NODUMP;
955
	if (xflags & FS_XFLAG_NODEFRAG)
956
		di_flags |= XFS_DIFLAG_NODEFRAG;
957
	if (xflags & FS_XFLAG_FILESTREAM)
958
		di_flags |= XFS_DIFLAG_FILESTREAM;
D
Dave Chinner 已提交
959
	if (S_ISDIR(VFS_I(ip)->i_mode)) {
960
		if (xflags & FS_XFLAG_RTINHERIT)
961
			di_flags |= XFS_DIFLAG_RTINHERIT;
962
		if (xflags & FS_XFLAG_NOSYMLINKS)
963
			di_flags |= XFS_DIFLAG_NOSYMLINKS;
964
		if (xflags & FS_XFLAG_EXTSZINHERIT)
965
			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
966
		if (xflags & FS_XFLAG_PROJINHERIT)
967
			di_flags |= XFS_DIFLAG_PROJINHERIT;
D
Dave Chinner 已提交
968
	} else if (S_ISREG(VFS_I(ip)->i_mode)) {
969
		if (xflags & FS_XFLAG_REALTIME)
970
			di_flags |= XFS_DIFLAG_REALTIME;
971
		if (xflags & FS_XFLAG_EXTSIZE)
972 973
			di_flags |= XFS_DIFLAG_EXTSIZE;
	}
974

975 976 977 978 979 980 981 982 983 984
	return di_flags;
}

STATIC uint64_t
xfs_flags2diflags2(
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	uint64_t		di_flags2 =
		(ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
985 986 987

	if (xflags & FS_XFLAG_DAX)
		di_flags2 |= XFS_DIFLAG2_DAX;
988 989
	if (xflags & FS_XFLAG_COWEXTSIZE)
		di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
990

991
	return di_flags2;
992 993
}

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

1001
	if (xflags & FS_XFLAG_IMMUTABLE)
1002 1003 1004
		inode->i_flags |= S_IMMUTABLE;
	else
		inode->i_flags &= ~S_IMMUTABLE;
1005
	if (xflags & FS_XFLAG_APPEND)
1006 1007 1008
		inode->i_flags |= S_APPEND;
	else
		inode->i_flags &= ~S_APPEND;
1009
	if (xflags & FS_XFLAG_SYNC)
1010 1011 1012
		inode->i_flags |= S_SYNC;
	else
		inode->i_flags &= ~S_SYNC;
1013
	if (xflags & FS_XFLAG_NOATIME)
1014 1015 1016
		inode->i_flags |= S_NOATIME;
	else
		inode->i_flags &= ~S_NOATIME;
1017
#if 0	/* disabled until the flag switching races are sorted out */
1018 1019 1020 1021
	if (xflags & FS_XFLAG_DAX)
		inode->i_flags |= S_DAX;
	else
		inode->i_flags &= ~S_DAX;
1022
#endif
1023
}
1024

1025 1026 1027 1028 1029 1030 1031
static int
xfs_ioctl_setattr_xflags(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
1032
	uint64_t		di_flags2;
1033 1034 1035

	/* Can't change realtime flag if any extents are allocated. */
	if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
1036
	    XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & FS_XFLAG_REALTIME))
1037 1038 1039
		return -EINVAL;

	/* If realtime flag is set then must have realtime device */
1040
	if (fa->fsx_xflags & FS_XFLAG_REALTIME) {
1041 1042 1043 1044 1045
		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;
	}

1046
	/* Clear reflink if we are actually able to set the rt flag. */
1047
	if ((fa->fsx_xflags & FS_XFLAG_REALTIME) && xfs_is_reflink_inode(ip))
1048
		ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
1049

1050 1051 1052 1053
	/* Don't allow us to set DAX mode for a reflinked file for now. */
	if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip))
		return -EINVAL;

1054 1055 1056 1057 1058
	/*
	 * 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)) ||
1059
	     (fa->fsx_xflags & (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND))) &&
1060 1061 1062
	    !capable(CAP_LINUX_IMMUTABLE))
		return -EPERM;

1063 1064 1065 1066 1067 1068 1069 1070
	/* diflags2 only valid for v3 inodes. */
	di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
	if (di_flags2 && ip->i_d.di_version < 3)
		return -EINVAL;

	ip->i_d.di_flags = xfs_flags2diflags(ip, fa->fsx_xflags);
	ip->i_d.di_flags2 = di_flags2;

1071 1072 1073
	xfs_diflags_to_linux(ip);
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1074
	XFS_STATS_INC(mp, xs_ig_attrchg);
1075 1076 1077
	return 0;
}

1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
/*
 * 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);
1093
	struct super_block	*sb = inode->i_sb;
1094 1095 1096 1097
	int			error;

	*join_flags = 0;

1098 1099
	/*
	 * It is only valid to set the DAX flag on regular files and
1100 1101
	 * directories on filesystems where the block size is equal to the page
	 * size. On directories it serves as an inherit hint.
1102
	 */
1103 1104 1105
	if (fa->fsx_xflags & FS_XFLAG_DAX) {
		if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
			return -EINVAL;
1106
		if (bdev_dax_supported(sb, sb->s_blocksize) < 0)
1107 1108
			return -EINVAL;
	}
1109

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
	/* 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;
1126
	return 0;
1127 1128 1129 1130 1131

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

1132 1133
}

1134 1135 1136 1137 1138
/*
 * 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.
1139 1140 1141 1142 1143 1144
 *
 * 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.
1145 1146 1147
 */
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
1148 1149
	struct xfs_inode	*ip,
	int			join_flags)
1150 1151 1152
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
1153
	int			error = -EROFS;
1154 1155

	if (mp->m_flags & XFS_MOUNT_RDONLY)
1156 1157
		goto out_unlock;
	error = -EIO;
1158
	if (XFS_FORCED_SHUTDOWN(mp))
1159
		goto out_unlock;
1160

1161
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1162
	if (error)
1163
		return ERR_PTR(error);
1164 1165

	xfs_ilock(ip, XFS_ILOCK_EXCL);
1166 1167
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags);
	join_flags = 0;
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185

	/*
	 * 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:
1186
	xfs_trans_cancel(tp);
1187 1188 1189
out_unlock:
	if (join_flags)
		xfs_iunlock(ip, join_flags);
1190 1191 1192
	return ERR_PTR(error);
}

1193 1194 1195 1196
/*
 * extent size hint validation is somewhat cumbersome. Rules are:
 *
 * 1. extent size hint is only valid for directories and regular files
1197 1198
 * 2. FS_XFLAG_EXTSIZE is only valid for regular files
 * 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
1199 1200 1201 1202 1203 1204 1205
 * 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.
D
Darrick J. Wong 已提交
1206 1207
 *
 * Please keep this function in sync with xfs_scrub_inode_extsize.
1208
 */
1209
static int
1210 1211 1212 1213 1214 1215
xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

D
Dave Chinner 已提交
1216
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode))
1217 1218
		return -EINVAL;

1219
	if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) &&
D
Dave Chinner 已提交
1220
	    !S_ISDIR(VFS_I(ip)->i_mode))
1221 1222
		return -EINVAL;

D
Dave Chinner 已提交
1223
	if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents &&
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
	    ((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) ||
1236
		    (fa->fsx_xflags & FS_XFLAG_REALTIME)) {
1237 1238 1239 1240 1241 1242 1243 1244 1245
			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;
1246
	} else
1247
		fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT);
1248

1249 1250 1251
	return 0;
}

1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
/*
 * CoW extent size hint validation rules are:
 *
 * 1. CoW extent size hint can only be set if reflink is enabled on the fs.
 *    The inode does not have to have any shared blocks, but it must be a v3.
 * 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
 *    for a directory, the hint is propagated to new files.
 * 3. Can be changed on files & directories at any time.
 * 4. CoW extsize hint of 0 turns off hints, clears inode flags.
 * 5. Extent size must be a multiple of the appropriate block size.
 * 6. The extent size hint must be limited to half the AG size to avoid
 *    alignment extending the extent beyond the limits of the AG.
D
Darrick J. Wong 已提交
1264 1265
 *
 * Please keep this function in sync with xfs_scrub_inode_cowextsize.
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
 */
static int
xfs_ioctl_setattr_check_cowextsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;

	if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
		return 0;

	if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb) ||
	    ip->i_d.di_version != 3)
		return -EINVAL;

	if (!S_ISREG(VFS_I(ip)->i_mode) && !S_ISDIR(VFS_I(ip)->i_mode))
		return -EINVAL;

	if (fa->fsx_cowextsize != 0) {
		xfs_extlen_t    size;
		xfs_fsblock_t   cowextsize_fsb;

		cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
		if (cowextsize_fsb > MAXEXTLEN)
			return -EINVAL;

		size = mp->m_sb.sb_blocksize;
		if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
			return -EINVAL;

		if (fa->fsx_cowextsize % size)
			return -EINVAL;
	} else
		fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;

	return 0;
}

1304
static int
1305 1306 1307 1308 1309
xfs_ioctl_setattr_check_projid(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
{
	/* Disallow 32bit project ids if projid32bit feature is not enabled. */
1310
	if (fa->fsx_projid > (uint16_t)-1 &&
1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
	    !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;
1324
	if ((fa->fsx_xflags & FS_XFLAG_PROJINHERIT) !=
1325 1326 1327 1328 1329
	    (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
		return -EINVAL;

	return 0;
}
1330 1331 1332 1333

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
1334
	struct fsxattr		*fa)
1335 1336 1337
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
C
Christoph Hellwig 已提交
1338
	struct xfs_dquot	*udqp = NULL;
1339
	struct xfs_dquot	*pdqp = NULL;
1340 1341
	struct xfs_dquot	*olddquot = NULL;
	int			code;
1342
	int			join_flags = 0;
1343

C
Christoph Hellwig 已提交
1344
	trace_xfs_ioctl_setattr(ip);
1345

1346 1347 1348
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1349

1350 1351 1352 1353 1354 1355 1356 1357
	/*
	 * 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.
	 */
1358
	if (XFS_IS_QUOTA_ON(mp)) {
C
Christoph Hellwig 已提交
1359
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1360
					 ip->i_d.di_gid, fa->fsx_projid,
1361
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1362 1363 1364 1365
		if (code)
			return code;
	}

1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
	/*
	 * 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);
1378 1379 1380
	if (IS_ERR(tp)) {
		code = PTR_ERR(tp);
		goto error_free_dquots;
1381 1382 1383
	}


1384 1385 1386 1387 1388
	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 */
1389
			goto error_trans_cancel;
1390 1391
	}

1392 1393 1394
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1395

1396 1397 1398 1399
	code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
	if (code)
		goto error_trans_cancel;

1400 1401
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1402
		goto error_trans_cancel;
1403 1404

	/*
1405 1406 1407 1408 1409
	 * 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()
1410 1411
	 */

D
Dave Chinner 已提交
1412
	if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
1413
	    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
D
Dave Chinner 已提交
1414
		VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
1415

1416 1417 1418 1419 1420 1421 1422 1423
	/* 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);
1424
	}
1425

1426 1427 1428 1429 1430
	/*
	 * 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.
	 */
1431 1432 1433 1434
	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;
1435 1436 1437 1438 1439 1440
	if (ip->i_d.di_version == 3 &&
	    (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
		ip->i_d.di_cowextsize = fa->fsx_cowextsize >>
				mp->m_sb.sb_blocklog;
	else
		ip->i_d.di_cowextsize = 0;
1441

1442
	code = xfs_trans_commit(tp);
1443 1444 1445 1446

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1447 1448
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1449
	xfs_qm_dqrele(pdqp);
1450

C
Christoph Hellwig 已提交
1451
	return code;
1452

1453
error_trans_cancel:
1454
	xfs_trans_cancel(tp);
1455
error_free_dquots:
C
Christoph Hellwig 已提交
1456
	xfs_qm_dqrele(udqp);
1457
	xfs_qm_dqrele(pdqp);
1458 1459 1460
	return code;
}

L
Linus Torvalds 已提交
1461
STATIC int
L
Lachlan McIlroy 已提交
1462
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1463 1464 1465 1466 1467
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
J
Jan Kara 已提交
1468
	int error;
L
Lachlan McIlroy 已提交
1469 1470 1471

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

J
Jan Kara 已提交
1473 1474 1475
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1476
	error = xfs_ioctl_setattr(ip, &fa);
J
Jan Kara 已提交
1477
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1478
	return error;
L
Lachlan McIlroy 已提交
1479
}
L
Linus Torvalds 已提交
1480

L
Lachlan McIlroy 已提交
1481 1482 1483 1484 1485 1486
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1487

L
Lachlan McIlroy 已提交
1488 1489 1490 1491 1492
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1493

L
Lachlan McIlroy 已提交
1494 1495
STATIC int
xfs_ioc_setxflags(
1496
	struct xfs_inode	*ip,
L
Lachlan McIlroy 已提交
1497 1498 1499
	struct file		*filp,
	void			__user *arg)
{
1500
	struct xfs_trans	*tp;
1501
	struct fsxattr		fa;
L
Lachlan McIlroy 已提交
1502
	unsigned int		flags;
1503
	int			join_flags = 0;
1504
	int			error;
L
Linus Torvalds 已提交
1505

L
Lachlan McIlroy 已提交
1506 1507
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1508

L
Lachlan McIlroy 已提交
1509 1510 1511 1512
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1513

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

J
Jan Kara 已提交
1516 1517 1518
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1519

1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531
	/*
	 * 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);
1532 1533 1534 1535 1536 1537 1538
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1539
		xfs_trans_cancel(tp);
1540 1541 1542
		goto out_drop_write;
	}

1543
	error = xfs_trans_commit(tp);
1544
out_drop_write:
J
Jan Kara 已提交
1545
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1546
	return error;
L
Linus Torvalds 已提交
1547 1548
}

1549 1550 1551 1552 1553
static bool
xfs_getbmap_format(
	struct kgetbmap		*p,
	struct getbmapx __user	*u,
	size_t			recsize)
1554
{
1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
	if (put_user(p->bmv_offset, &u->bmv_offset) ||
	    put_user(p->bmv_block, &u->bmv_block) ||
	    put_user(p->bmv_length, &u->bmv_length) ||
	    put_user(0, &u->bmv_count) ||
	    put_user(0, &u->bmv_entries))
		return false;
	if (recsize < sizeof(struct getbmapx))
		return true;
	if (put_user(0, &u->bmv_iflags) ||
	    put_user(p->bmv_oflags, &u->bmv_oflags) ||
	    put_user(0, &u->bmv_unused1) ||
	    put_user(0, &u->bmv_unused2))
		return false;
	return true;
1569 1570
}

L
Linus Torvalds 已提交
1571 1572
STATIC int
xfs_ioc_getbmap(
1573
	struct file		*file,
L
Linus Torvalds 已提交
1574 1575 1576
	unsigned int		cmd,
	void			__user *arg)
{
1577
	struct getbmapx		bmx = { 0 };
1578 1579 1580
	struct kgetbmap		*buf;
	size_t			recsize;
	int			error, i;
L
Linus Torvalds 已提交
1581

1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595
	switch (cmd) {
	case XFS_IOC_GETBMAPA:
		bmx.bmv_iflags = BMV_IF_ATTRFORK;
		/*FALLTHRU*/
	case XFS_IOC_GETBMAP:
		if (file->f_mode & FMODE_NOCMTIME)
			bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
		/* struct getbmap is a strict subset of struct getbmapx. */
		recsize = sizeof(struct getbmap);
		break;
	case XFS_IOC_GETBMAPX:
		recsize = sizeof(struct getbmapx);
		break;
	default:
E
Eric Sandeen 已提交
1596
		return -EINVAL;
1597
	}
L
Linus Torvalds 已提交
1598

1599
	if (copy_from_user(&bmx, arg, recsize))
E
Eric Sandeen 已提交
1600
		return -EFAULT;
L
Linus Torvalds 已提交
1601 1602

	if (bmx.bmv_count < 2)
E
Eric Sandeen 已提交
1603
		return -EINVAL;
1604 1605
	if (bmx.bmv_count > ULONG_MAX / recsize)
		return -ENOMEM;
L
Linus Torvalds 已提交
1606

1607 1608 1609
	buf = kmem_zalloc_large(bmx.bmv_count * sizeof(*buf), 0);
	if (!buf)
		return -ENOMEM;
L
Linus Torvalds 已提交
1610

1611
	error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf);
L
Linus Torvalds 已提交
1612
	if (error)
1613
		goto out_free_buf;
L
Linus Torvalds 已提交
1614

1615 1616 1617 1618 1619 1620 1621 1622 1623 1624
	error = -EFAULT;
	if (copy_to_user(arg, &bmx, recsize))
		goto out_free_buf;
	arg += recsize;

	for (i = 0; i < bmx.bmv_entries; i++) {
		if (!xfs_getbmap_format(buf + i, arg, recsize))
			goto out_free_buf;
		arg += recsize;
	}
L
Linus Torvalds 已提交
1625

1626 1627 1628
	error = 0;
out_free_buf:
	kmem_free(buf);
L
Linus Torvalds 已提交
1629 1630
	return 0;
}
L
Lachlan McIlroy 已提交
1631

1632 1633
struct getfsmap_info {
	struct xfs_mount	*mp;
1634 1635
	struct fsmap_head __user *data;
	unsigned int		idx;
1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648
	__u32			last_flags;
};

STATIC int
xfs_getfsmap_format(struct xfs_fsmap *xfm, void *priv)
{
	struct getfsmap_info	*info = priv;
	struct fsmap		fm;

	trace_xfs_getfsmap_mapping(info->mp, xfm);

	info->last_flags = xfm->fmr_flags;
	xfs_fsmap_from_internal(&fm, xfm);
1649 1650
	if (copy_to_user(&info->data->fmh_recs[info->idx++], &fm,
			sizeof(struct fsmap)))
1651 1652 1653 1654 1655 1656 1657 1658
		return -EFAULT;

	return 0;
}

STATIC int
xfs_ioc_getfsmap(
	struct xfs_inode	*ip,
1659
	struct fsmap_head	__user *arg)
1660
{
1661
	struct getfsmap_info	info = { NULL };
1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684
	struct xfs_fsmap_head	xhead = {0};
	struct fsmap_head	head;
	bool			aborted = false;
	int			error;

	if (copy_from_user(&head, arg, sizeof(struct fsmap_head)))
		return -EFAULT;
	if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) ||
	    memchr_inv(head.fmh_keys[0].fmr_reserved, 0,
		       sizeof(head.fmh_keys[0].fmr_reserved)) ||
	    memchr_inv(head.fmh_keys[1].fmr_reserved, 0,
		       sizeof(head.fmh_keys[1].fmr_reserved)))
		return -EINVAL;

	xhead.fmh_iflags = head.fmh_iflags;
	xhead.fmh_count = head.fmh_count;
	xfs_fsmap_to_internal(&xhead.fmh_keys[0], &head.fmh_keys[0]);
	xfs_fsmap_to_internal(&xhead.fmh_keys[1], &head.fmh_keys[1]);

	trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]);
	trace_xfs_getfsmap_high_key(ip->i_mount, &xhead.fmh_keys[1]);

	info.mp = ip->i_mount;
1685
	info.data = arg;
1686 1687 1688 1689 1690 1691 1692 1693
	error = xfs_getfsmap(ip->i_mount, &xhead, xfs_getfsmap_format, &info);
	if (error == XFS_BTREE_QUERY_RANGE_ABORT) {
		error = 0;
		aborted = true;
	} else if (error)
		return error;

	/* If we didn't abort, set the "last" flag in the last fmx */
1694
	if (!aborted && info.idx) {
1695
		info.last_flags |= FMR_OF_LAST;
1696 1697
		if (copy_to_user(&info.data->fmh_recs[info.idx - 1].fmr_flags,
				&info.last_flags, sizeof(info.last_flags)))
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
			return -EFAULT;
	}

	/* copy back header */
	head.fmh_entries = xhead.fmh_entries;
	head.fmh_oflags = xhead.fmh_oflags;
	if (copy_to_user(arg, &head, sizeof(struct fsmap_head)))
		return -EFAULT;

	return 0;
}

1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733
STATIC int
xfs_ioc_scrub_metadata(
	struct xfs_inode		*ip,
	void				__user *arg)
{
	struct xfs_scrub_metadata	scrub;
	int				error;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (copy_from_user(&scrub, arg, sizeof(scrub)))
		return -EFAULT;

	error = xfs_scrub_metadata(ip, &scrub);
	if (error)
		return error;

	if (copy_to_user(arg, &scrub, sizeof(scrub)))
		return -EFAULT;

	return 0;
}

D
Dave Chinner 已提交
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
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 已提交
1745
		error = -EINVAL;
D
Dave Chinner 已提交
1746 1747 1748 1749 1750 1751
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1752
		error = -EBADF;
D
Dave Chinner 已提交
1753 1754 1755 1756 1757
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1758
		error = -EINVAL;
D
Dave Chinner 已提交
1759 1760 1761 1762 1763 1764
		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 已提交
1765
		error = -EBADF;
D
Dave Chinner 已提交
1766 1767 1768 1769 1770
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1771
		error = -EINVAL;
D
Dave Chinner 已提交
1772 1773 1774
		goto out_put_tmp_file;
	}

1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
	/*
	 * 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 已提交
1786 1787 1788 1789
	ip = XFS_I(file_inode(f.file));
	tip = XFS_I(file_inode(tmp.file));

	if (ip->i_mount != tip->i_mount) {
D
Dave Chinner 已提交
1790
		error = -EINVAL;
D
Dave Chinner 已提交
1791 1792 1793 1794
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1795
		error = -EINVAL;
D
Dave Chinner 已提交
1796 1797 1798 1799
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1800
		error = -EIO;
D
Dave Chinner 已提交
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813
		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;
}

1814 1815 1816 1817 1818 1819 1820 1821
/*
 * 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 已提交
1822 1823
	struct file		*filp,
	unsigned int		cmd,
1824
	unsigned long		p)
L
Lachlan McIlroy 已提交
1825
{
A
Al Viro 已提交
1826
	struct inode		*inode = file_inode(filp);
1827 1828 1829
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	void			__user *arg = (void __user *)p;
L
Lachlan McIlroy 已提交
1830 1831
	int			error;

C
Christoph Hellwig 已提交
1832
	trace_xfs_file_ioctl(ip);
1833 1834

	switch (cmd) {
C
Christoph Hellwig 已提交
1835 1836
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
L
Lachlan McIlroy 已提交
1837 1838 1839 1840 1841 1842 1843
	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 已提交
1844 1845
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1846
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1847

1848
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1849
			return -EFAULT;
1850
		return xfs_ioc_space(filp, cmd, &bf);
1851
	}
L
Lachlan McIlroy 已提交
1852 1853 1854 1855 1856 1857
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1858
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1859 1860 1861
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

		if (copy_to_user(arg, &da, sizeof(da)))
E
Eric Sandeen 已提交
1862
			return -EFAULT;
L
Lachlan McIlroy 已提交
1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883
		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 已提交
1884 1885
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(ip, filp, arg);
L
Lachlan McIlroy 已提交
1886
	case XFS_IOC_GETXFLAGS:
L
Lachlan McIlroy 已提交
1887
		return xfs_ioc_getxflags(ip, arg);
L
Lachlan McIlroy 已提交
1888
	case XFS_IOC_SETXFLAGS:
L
Lachlan McIlroy 已提交
1889
		return xfs_ioc_setxflags(ip, filp, arg);
L
Lachlan McIlroy 已提交
1890 1891 1892 1893 1894

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1897 1898 1899 1900
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1901 1902
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1903
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1904
		return error;
L
Lachlan McIlroy 已提交
1905 1906 1907 1908 1909
	}

	case XFS_IOC_GETBMAP:
	case XFS_IOC_GETBMAPA:
	case XFS_IOC_GETBMAPX:
1910
		return xfs_ioc_getbmap(filp, cmd, arg);
L
Lachlan McIlroy 已提交
1911

1912 1913 1914
	case FS_IOC_GETFSMAP:
		return xfs_ioc_getfsmap(ip, arg);

1915 1916 1917
	case XFS_IOC_SCRUB_METADATA:
		return xfs_ioc_scrub_metadata(ip, arg);

L
Lachlan McIlroy 已提交
1918 1919
	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
1920 1921
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1922

1923
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1924
			return -EFAULT;
1925 1926 1927 1928
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1929

1930
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1931
			return -EFAULT;
1932
		return xfs_open_by_handle(filp, &hreq);
1933
	}
L
Lachlan McIlroy 已提交
1934
	case XFS_IOC_FSSETDM_BY_HANDLE:
1935
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1936

1937 1938
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1939

1940
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1941
			return -EFAULT;
1942
		return xfs_readlink_by_handle(filp, &hreq);
1943
	}
L
Lachlan McIlroy 已提交
1944
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1945
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1946 1947

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1948
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1949 1950

	case XFS_IOC_SWAPEXT: {
1951 1952 1953
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
1954
			return -EFAULT;
J
Jan Kara 已提交
1955 1956 1957
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
1958
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
1959
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1960
		return error;
L
Lachlan McIlroy 已提交
1961 1962 1963 1964 1965 1966 1967
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1968
			return error;
L
Lachlan McIlroy 已提交
1969 1970

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1971
			return -EFAULT;
L
Lachlan McIlroy 已提交
1972 1973 1974 1975 1976
		return 0;
	}

	case XFS_IOC_SET_RESBLKS: {
		xfs_fsop_resblks_t inout;
1977
		uint64_t	   in;
L
Lachlan McIlroy 已提交
1978 1979 1980 1981

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

E
Eric Sandeen 已提交
1982
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1983
			return -EROFS;
E
Eric Sandeen 已提交
1984

L
Lachlan McIlroy 已提交
1985
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1986
			return -EFAULT;
L
Lachlan McIlroy 已提交
1987

J
Jan Kara 已提交
1988 1989 1990 1991
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1992 1993 1994
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
1995
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
1996
		if (error)
D
Dave Chinner 已提交
1997
			return error;
L
Lachlan McIlroy 已提交
1998 1999

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
2000
			return -EFAULT;
L
Lachlan McIlroy 已提交
2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
		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 已提交
2012
			return error;
L
Lachlan McIlroy 已提交
2013 2014

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
2015
			return -EFAULT;
L
Lachlan McIlroy 已提交
2016 2017 2018 2019 2020 2021 2022 2023

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

J
Jan Kara 已提交
2026 2027 2028
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
2029
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
2030
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2031
		return error;
L
Lachlan McIlroy 已提交
2032 2033 2034 2035 2036 2037
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

J
Jan Kara 已提交
2040 2041 2042
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
2043
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
2044
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2045
		return error;
L
Lachlan McIlroy 已提交
2046 2047 2048 2049 2050 2051
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
2054 2055 2056
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
2057
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
2058
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
2059
		return error;
L
Lachlan McIlroy 已提交
2060 2061 2062
	}

	case XFS_IOC_GOINGDOWN: {
2063
		uint32_t in;
L
Lachlan McIlroy 已提交
2064 2065 2066 2067

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

2068
		if (get_user(in, (uint32_t __user *)arg))
E
Eric Sandeen 已提交
2069
			return -EFAULT;
L
Lachlan McIlroy 已提交
2070

D
Dave Chinner 已提交
2071
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
2072 2073 2074 2075 2076 2077 2078 2079 2080
	}

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

2083
		return xfs_errortag_add(mp, in.errtag);
L
Lachlan McIlroy 已提交
2084 2085 2086 2087 2088 2089
	}

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

2090
		return xfs_errortag_clearall(mp);
L
Lachlan McIlroy 已提交
2091

2092
	case XFS_IOC_FREE_EOFBLOCKS: {
2093 2094
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
2095

2096 2097 2098 2099
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

2102
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
2103
			return -EFAULT;
2104

2105 2106
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
2107
			return error;
2108

D
Dave Chinner 已提交
2109
		return xfs_icache_free_eofblocks(mp, &keofb);
2110 2111
	}

L
Lachlan McIlroy 已提交
2112 2113 2114 2115
	default:
		return -ENOTTY;
	}
}