xfs_ioctl.c 48.0 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 | XFS_MMAPLOCK_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 654 655 656 657 658 659 660

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

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

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

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

	if (error)
		goto out_unlock;

735
	error = xfs_update_prealloc_flags(ip, flags);
736 737

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

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

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

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

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

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

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

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

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

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

	return 0;
}

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

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

814 815 816 817 818 819
	/*
	 * 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 已提交
820
		return -EFAULT;
L
Linus Torvalds 已提交
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;

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

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

/*
 * Linux extended inode flags interface.
 */

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

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

	return xflags;
}

STATIC unsigned int
xfs_di2lxflags(
878
	uint16_t	di_flags)
L
Linus Torvalds 已提交
879 880 881 882
{
	unsigned int	flags = 0;

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

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

903 904
	memset(&fa, 0, sizeof(struct fsxattr));

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

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

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

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

972 973 974 975 976 977 978 979 980 981
	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);
982 983 984

	if (xflags & FS_XFLAG_DAX)
		di_flags2 |= XFS_DIFLAG2_DAX;
985 986
	if (xflags & FS_XFLAG_COWEXTSIZE)
		di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
987

988
	return di_flags2;
989 990
}

991 992 993 994
STATIC void
xfs_diflags_to_linux(
	struct xfs_inode	*ip)
{
995
	struct inode		*inode = VFS_I(ip);
996 997
	unsigned int		xflags = xfs_ip2xflags(ip);

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

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

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

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

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

1047 1048 1049 1050
	/* 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;

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

1060 1061 1062 1063 1064 1065 1066 1067
	/* 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;

1068 1069 1070
	xfs_diflags_to_linux(ip);
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
1071
	XFS_STATS_INC(mp, xs_ig_attrchg);
1072 1073 1074
	return 0;
}

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

	*join_flags = 0;

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

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

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

1129 1130
}

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

	if (mp->m_flags & XFS_MOUNT_RDONLY)
1153 1154
		goto out_unlock;
	error = -EIO;
1155
	if (XFS_FORCED_SHUTDOWN(mp))
1156
		goto out_unlock;
1157

1158
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1159
	if (error)
1160
		return ERR_PTR(error);
1161 1162

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

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

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

D
Dave Chinner 已提交
1213
	if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(VFS_I(ip)->i_mode))
1214 1215
		return -EINVAL;

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

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

1246 1247 1248
	return 0;
}

1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
/*
 * 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 已提交
1261 1262
 *
 * Please keep this function in sync with xfs_scrub_inode_cowextsize.
1263 1264 1265 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
 */
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;
}

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

	return 0;
}
1327 1328 1329 1330

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

C
Christoph Hellwig 已提交
1341
	trace_xfs_ioctl_setattr(ip);
1342

1343 1344 1345
	code = xfs_ioctl_setattr_check_projid(ip, fa);
	if (code)
		return code;
1346

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

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


1381 1382 1383 1384 1385
	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 */
1386
			goto error_trans_cancel;
1387 1388
	}

1389 1390 1391
	code = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (code)
		goto error_trans_cancel;
1392

1393 1394 1395 1396
	code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
	if (code)
		goto error_trans_cancel;

1397 1398
	code = xfs_ioctl_setattr_xflags(tp, ip, fa);
	if (code)
1399
		goto error_trans_cancel;
1400 1401

	/*
1402 1403 1404 1405 1406
	 * 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()
1407 1408
	 */

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

1413 1414 1415 1416 1417 1418 1419 1420
	/* 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);
1421
	}
1422

1423 1424 1425 1426 1427
	/*
	 * 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.
	 */
1428 1429 1430 1431
	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;
1432 1433 1434 1435 1436 1437
	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;
1438

1439
	code = xfs_trans_commit(tp);
1440 1441 1442 1443

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

C
Christoph Hellwig 已提交
1448
	return code;
1449

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

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

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

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

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

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

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

L
Lachlan McIlroy 已提交
1503 1504
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1505

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

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

J
Jan Kara 已提交
1513 1514 1515
	error = mnt_want_write_file(filp);
	if (error)
		return error;
1516

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

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
1536
		xfs_trans_cancel(tp);
1537 1538 1539
		goto out_drop_write;
	}

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

1546 1547 1548 1549 1550
static bool
xfs_getbmap_format(
	struct kgetbmap		*p,
	struct getbmapx __user	*u,
	size_t			recsize)
1551
{
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
	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;
1566 1567
}

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

1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592
	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 已提交
1593
		return -EINVAL;
1594
	}
L
Linus Torvalds 已提交
1595

1596
	if (copy_from_user(&bmx, arg, recsize))
E
Eric Sandeen 已提交
1597
		return -EFAULT;
L
Linus Torvalds 已提交
1598 1599

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

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

1608
	error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf);
L
Linus Torvalds 已提交
1609
	if (error)
1610
		goto out_free_buf;
L
Linus Torvalds 已提交
1611

1612 1613 1614 1615 1616 1617 1618 1619 1620 1621
	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 已提交
1622

1623 1624 1625
	error = 0;
out_free_buf:
	kmem_free(buf);
L
Linus Torvalds 已提交
1626 1627
	return 0;
}
L
Lachlan McIlroy 已提交
1628

1629 1630
struct getfsmap_info {
	struct xfs_mount	*mp;
1631 1632
	struct fsmap_head __user *data;
	unsigned int		idx;
1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
	__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);
1646 1647
	if (copy_to_user(&info->data->fmh_recs[info->idx++], &fm,
			sizeof(struct fsmap)))
1648 1649 1650 1651 1652 1653 1654 1655
		return -EFAULT;

	return 0;
}

STATIC int
xfs_ioc_getfsmap(
	struct xfs_inode	*ip,
1656
	struct fsmap_head	__user *arg)
1657
{
1658
	struct getfsmap_info	info = { NULL };
1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681
	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;
1682
	info.data = arg;
1683 1684 1685 1686 1687 1688 1689 1690
	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 */
1691
	if (!aborted && info.idx) {
1692
		info.last_flags |= FMR_OF_LAST;
1693 1694
		if (copy_to_user(&info.data->fmh_recs[info.idx - 1].fmr_flags,
				&info.last_flags, sizeof(info.last_flags)))
1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706
			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;
}

1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730
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 已提交
1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741
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 已提交
1742
		error = -EINVAL;
D
Dave Chinner 已提交
1743 1744 1745 1746 1747 1748
		goto out;
	}

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

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

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

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

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

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

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

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

C
Christoph Hellwig 已提交
1829
	trace_xfs_file_ioctl(ip);
1830 1831

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

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

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

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

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1894 1895 1896 1897
		error = mnt_want_write_file(filp);
		if (error)
			return error;

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

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

1909 1910 1911
	case FS_IOC_GETFSMAP:
		return xfs_ioc_getfsmap(ip, arg);

1912 1913 1914
	case XFS_IOC_SCRUB_METADATA:
		return xfs_ioc_scrub_metadata(ip, arg);

L
Lachlan McIlroy 已提交
1915 1916
	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
1917 1918
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1919

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

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

1934 1935
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1936

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

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1945
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1946 1947

	case XFS_IOC_SWAPEXT: {
1948 1949 1950
		struct xfs_swapext	sxp;

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

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1965
			return error;
L
Lachlan McIlroy 已提交
1966 1967

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

	case XFS_IOC_SET_RESBLKS: {
		xfs_fsop_resblks_t inout;
1974
		uint64_t	   in;
L
Lachlan McIlroy 已提交
1975 1976 1977 1978

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

E
Eric Sandeen 已提交
1979
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1980
			return -EROFS;
E
Eric Sandeen 已提交
1981

L
Lachlan McIlroy 已提交
1982
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1983
			return -EFAULT;
L
Lachlan McIlroy 已提交
1984

J
Jan Kara 已提交
1985 1986 1987 1988
		error = mnt_want_write_file(filp);
		if (error)
			return error;

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

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

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

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

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

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

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

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

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

	case XFS_IOC_GOINGDOWN: {
2060
		uint32_t in;
L
Lachlan McIlroy 已提交
2061 2062 2063 2064

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

2065
		if (get_user(in, (uint32_t __user *)arg))
E
Eric Sandeen 已提交
2066
			return -EFAULT;
L
Lachlan McIlroy 已提交
2067

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

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

2080
		return xfs_errortag_add(mp, in.errtag);
L
Lachlan McIlroy 已提交
2081 2082 2083 2084 2085 2086
	}

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

2087
		return xfs_errortag_clearall(mp);
L
Lachlan McIlroy 已提交
2088

2089
	case XFS_IOC_FREE_EOFBLOCKS: {
2090 2091
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
2092

2093 2094 2095 2096
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

2099
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
2100
			return -EFAULT;
2101

2102 2103
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
2104
			return error;
2105

D
Dave Chinner 已提交
2106
		return xfs_icache_free_eofblocks(mp, &keofb);
2107 2108
	}

L
Lachlan McIlroy 已提交
2109 2110 2111 2112
	default:
		return -ENOTTY;
	}
}