xfs_ioctl.c 38.7 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"
L
Linus Torvalds 已提交
42

43
#include <linux/capability.h>
L
Linus Torvalds 已提交
44 45 46 47
#include <linux/dcache.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
48
#include <linux/slab.h>
49
#include <linux/exportfs.h>
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59 60 61

/*
 * 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
 */
62
int
L
Linus Torvalds 已提交
63 64
xfs_find_handle(
	unsigned int		cmd,
65
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
66 67 68 69
{
	int			hsize;
	xfs_handle_t		handle;
	struct inode		*inode;
70
	struct fd		f = {NULL};
C
Christoph Hellwig 已提交
71
	struct path		path;
72
	int			error;
C
Christoph Hellwig 已提交
73
	struct xfs_inode	*ip;
L
Linus Torvalds 已提交
74

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

		hsize = XFS_HSIZE(handle);
	}

C
Christoph Hellwig 已提交
121
	error = -EFAULT;
122
	if (copy_to_user(hreq->ohandle, &handle, hsize) ||
C
Christoph Hellwig 已提交
123 124
	    copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
		goto out_put;
L
Linus Torvalds 已提交
125

C
Christoph Hellwig 已提交
126 127 128 129
	error = 0;

 out_put:
	if (cmd == XFS_IOC_FD_TO_HANDLE)
130
		fdput(f);
C
Christoph Hellwig 已提交
131 132 133
	else
		path_put(&path);
	return error;
L
Linus Torvalds 已提交
134 135 136
}

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

	/*
	 * Only allow handle opens under a directory.
	 */
A
Al Viro 已提交
163
	if (!S_ISDIR(file_inode(parfilp)->i_mode))
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
		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 已提交
182

183 184 185 186 187 188
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 已提交
189 190
}

191
int
L
Linus Torvalds 已提交
192 193
xfs_open_by_handle(
	struct file		*parfilp,
194
	xfs_fsop_handlereq_t	*hreq)
L
Linus Torvalds 已提交
195
{
196
	const struct cred	*cred = current_cred();
L
Linus Torvalds 已提交
197
	int			error;
198
	int			fd;
L
Linus Torvalds 已提交
199 200 201 202
	int			permflag;
	struct file		*filp;
	struct inode		*inode;
	struct dentry		*dentry;
203
	fmode_t			fmode;
204
	struct path		path;
L
Linus Torvalds 已提交
205 206

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

209 210 211 212
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
	inode = dentry->d_inode;
L
Linus Torvalds 已提交
213 214 215

	/* Restrict xfs_open_by_handle to directories & regular files. */
	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
E
Eric Sandeen 已提交
216
		error = -EPERM;
217
		goto out_dput;
L
Linus Torvalds 已提交
218 219 220
	}

#if BITS_PER_LONG != 32
221
	hreq->oflags |= O_LARGEFILE;
L
Linus Torvalds 已提交
222
#endif
223

224
	permflag = hreq->oflags;
225
	fmode = OPEN_FMODE(permflag);
L
Linus Torvalds 已提交
226
	if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
227
	    (fmode & FMODE_WRITE) && IS_APPEND(inode)) {
E
Eric Sandeen 已提交
228
		error = -EPERM;
229
		goto out_dput;
L
Linus Torvalds 已提交
230 231
	}

232
	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
E
Eric Sandeen 已提交
233
		error = -EACCES;
234
		goto out_dput;
L
Linus Torvalds 已提交
235 236 237
	}

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

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

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

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

263 264 265 266 267 268
	fd_install(fd, filp);
	return fd;

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

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

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

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

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

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

299
	link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
300
	if (!link) {
E
Eric Sandeen 已提交
301
		error = -ENOMEM;
302 303
		goto out_dput;
	}
L
Linus Torvalds 已提交
304

D
Dave Chinner 已提交
305
	error = xfs_readlink(XFS_I(dentry->d_inode), link);
306
	if (error)
307
		goto out_kfree;
A
Al Viro 已提交
308
	error = readlink_copy(hreq->ohandle, olen, link);
309 310
	if (error)
		goto out_kfree;
311

312 313
 out_kfree:
	kfree(link);
314 315
 out_dput:
	dput(dentry);
316
	return error;
L
Linus Torvalds 已提交
317 318
}

D
Dave Chinner 已提交
319 320 321 322 323 324 325 326 327 328 329
int
xfs_set_dmattrs(
	xfs_inode_t     *ip,
	u_int		evmask,
	u_int16_t	state)
{
	xfs_mount_t	*mp = ip->i_mount;
	xfs_trans_t	*tp;
	int		error;

	if (!capable(CAP_SYS_ADMIN))
D
Dave Chinner 已提交
330
		return -EPERM;
D
Dave Chinner 已提交
331 332

	if (XFS_FORCED_SHUTDOWN(mp))
D
Dave Chinner 已提交
333
		return -EIO;
D
Dave Chinner 已提交
334 335

	tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS);
336
	error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
D
Dave Chinner 已提交
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	if (error) {
		xfs_trans_cancel(tp, 0);
		return error;
	}
	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);

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

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
	error = xfs_trans_commit(tp, 0);

	return error;
}

L
Linus Torvalds 已提交
353 354
STATIC int
xfs_fssetdm_by_handle(
355 356
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
357 358 359 360
{
	int			error;
	struct fsdmidata	fsd;
	xfs_fsop_setdm_handlereq_t dmhreq;
361
	struct dentry		*dentry;
L
Linus Torvalds 已提交
362 363

	if (!capable(CAP_MKNOD))
E
Eric Sandeen 已提交
364
		return -EPERM;
L
Linus Torvalds 已提交
365
	if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
E
Eric Sandeen 已提交
366
		return -EFAULT;
L
Linus Torvalds 已提交
367

J
Jan Kara 已提交
368 369 370 371
	error = mnt_want_write_file(parfilp);
	if (error)
		return error;

372
	dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
J
Jan Kara 已提交
373 374
	if (IS_ERR(dentry)) {
		mnt_drop_write_file(parfilp);
375
		return PTR_ERR(dentry);
J
Jan Kara 已提交
376
	}
L
Linus Torvalds 已提交
377

378
	if (IS_IMMUTABLE(dentry->d_inode) || IS_APPEND(dentry->d_inode)) {
E
Eric Sandeen 已提交
379
		error = -EPERM;
380
		goto out;
L
Linus Torvalds 已提交
381 382 383
	}

	if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
E
Eric Sandeen 已提交
384
		error = -EFAULT;
385
		goto out;
L
Linus Torvalds 已提交
386 387
	}

D
Dave Chinner 已提交
388
	error = xfs_set_dmattrs(XFS_I(dentry->d_inode), fsd.fsd_dmevmask,
389
				 fsd.fsd_dmstate);
L
Linus Torvalds 已提交
390

391
 out:
J
Jan Kara 已提交
392
	mnt_drop_write_file(parfilp);
393
	dput(dentry);
394
	return error;
L
Linus Torvalds 已提交
395 396 397 398
}

STATIC int
xfs_attrlist_by_handle(
399 400
	struct file		*parfilp,
	void			__user *arg)
L
Linus Torvalds 已提交
401
{
402
	int			error = -ENOMEM;
L
Linus Torvalds 已提交
403 404
	attrlist_cursor_kern_t	*cursor;
	xfs_fsop_attrlist_handlereq_t al_hreq;
405
	struct dentry		*dentry;
L
Linus Torvalds 已提交
406 407 408
	char			*kbuf;

	if (!capable(CAP_SYS_ADMIN))
E
Eric Sandeen 已提交
409
		return -EPERM;
L
Linus Torvalds 已提交
410
	if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
E
Eric Sandeen 已提交
411
		return -EFAULT;
412 413
	if (al_hreq.buflen < sizeof(struct attrlist) ||
	    al_hreq.buflen > XATTR_LIST_MAX)
E
Eric Sandeen 已提交
414
		return -EINVAL;
L
Linus Torvalds 已提交
415

416 417 418 419
	/*
	 * Reject flags, only allow namespaces.
	 */
	if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
E
Eric Sandeen 已提交
420
		return -EINVAL;
421

422 423 424
	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
L
Linus Torvalds 已提交
425

426 427 428
	kbuf = kmem_zalloc_large(al_hreq.buflen, KM_SLEEP);
	if (!kbuf)
		goto out_dput;
L
Linus Torvalds 已提交
429 430

	cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
D
Dave Chinner 已提交
431
	error = xfs_attr_list(XFS_I(dentry->d_inode), kbuf, al_hreq.buflen,
432
					al_hreq.flags, cursor);
L
Linus Torvalds 已提交
433 434 435 436 437 438
	if (error)
		goto out_kfree;

	if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
		error = -EFAULT;

439 440 441
out_kfree:
	kmem_free(kbuf);
out_dput:
442 443
	dput(dentry);
	return error;
L
Linus Torvalds 已提交
444 445
}

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

L
Linus Torvalds 已提交
457
	if (*len > XATTR_SIZE_MAX)
D
Dave Chinner 已提交
458
		return -EINVAL;
459 460
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
D
Dave Chinner 已提交
461
		return -ENOMEM;
L
Linus Torvalds 已提交
462

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

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

470 471
out_kfree:
	kmem_free(kbuf);
L
Linus Torvalds 已提交
472 473 474
	return error;
}

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

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

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

D
Dave Chinner 已提交
494
	return xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
L
Linus Torvalds 已提交
495 496
}

497
int
L
Linus Torvalds 已提交
498
xfs_attrmulti_attr_remove(
499
	struct inode		*inode,
500
	unsigned char		*name,
L
Linus Torvalds 已提交
501 502
	__uint32_t		flags)
{
503
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
D
Dave Chinner 已提交
504
		return -EPERM;
505
	return xfs_attr_remove(XFS_I(inode), name, flags);
L
Linus Torvalds 已提交
506 507 508 509
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

630 631 632 633 634
	if (filp->f_flags & O_DSYNC)
		flags |= XFS_PREALLOC_SYNC;
	if (ioflags & XFS_IO_INVIS)	
		flags |= XFS_PREALLOC_INVISIBLE;

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

639
	xfs_ilock(ip, XFS_IOLOCK_EXCL);
640 641 642 643 644 645 646 647 648 649 650

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

	if (bf->l_start < 0 ||
678
	    bf->l_start > inode->i_sb->s_maxbytes ||
679
	    bf->l_start + bf->l_len < 0 ||
680
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
D
Dave Chinner 已提交
681
		error = -EINVAL;
682 683 684 685 686
		goto out_unlock;
	}

	switch (cmd) {
	case XFS_IOC_ZERO_RANGE:
687
		flags |= XFS_PREALLOC_SET;
688 689 690 691
		error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
		break;
	case XFS_IOC_RESVSP:
	case XFS_IOC_RESVSP64:
692
		flags |= XFS_PREALLOC_SET;
693 694 695 696 697 698 699 700 701 702 703
		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:
704
		flags |= XFS_PREALLOC_CLEAR;
705 706 707 708 709 710 711 712 713 714 715 716 717 718
		if (bf->l_start > XFS_ISIZE(ip)) {
			error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
					bf->l_start - XFS_ISIZE(ip), 0);
			if (error)
				goto out_unlock;
		}

		iattr.ia_valid = ATTR_SIZE;
		iattr.ia_size = bf->l_start;

		error = xfs_setattr_size(ip, &iattr);
		break;
	default:
		ASSERT(0);
D
Dave Chinner 已提交
719
		error = -EINVAL;
720 721 722 723 724
	}

	if (error)
		goto out_unlock;

725
	error = xfs_update_prealloc_flags(ip, flags);
726 727

out_unlock:
728
	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
J
Jan Kara 已提交
729
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
730
	return error;
L
Linus Torvalds 已提交
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
}

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 已提交
752
		return -EIO;
L
Linus Torvalds 已提交
753 754

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
E
Eric Sandeen 已提交
755
		return -EFAULT;
L
Linus Torvalds 已提交
756 757

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

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

763
	if (bulkreq.ubuffer == NULL)
E
Eric Sandeen 已提交
764
		return -EINVAL;
765

L
Linus Torvalds 已提交
766 767
	if (cmd == XFS_IOC_FSINUMBERS)
		error = xfs_inumbers(mp, &inlast, &count,
768
					bulkreq.ubuffer, xfs_inumbers_fmt);
L
Linus Torvalds 已提交
769
	else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
770 771
		error = xfs_bulkstat_one(mp, inlast, bulkreq.ubuffer,
					sizeof(xfs_bstat_t), NULL, &done);
772
	else	/* XFS_IOC_FSBULKSTAT */
773 774 775
		error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one,
				     sizeof(xfs_bstat_t), bulkreq.ubuffer,
				     &done);
L
Linus Torvalds 已提交
776 777

	if (error)
D
Dave Chinner 已提交
778
		return error;
L
Linus Torvalds 已提交
779 780 781 782

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

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
E
Eric Sandeen 已提交
786
			return -EFAULT;
L
Linus Torvalds 已提交
787 788 789 790 791 792 793 794 795 796
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry_v1(
	xfs_mount_t		*mp,
	void			__user *arg)
{
797
	xfs_fsop_geom_t         fsgeo;
L
Linus Torvalds 已提交
798 799
	int			error;

800
	error = xfs_fs_geometry(mp, &fsgeo, 3);
L
Linus Torvalds 已提交
801
	if (error)
D
Dave Chinner 已提交
802
		return error;
L
Linus Torvalds 已提交
803

804 805 806 807 808 809
	/*
	 * 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 已提交
810
		return -EFAULT;
L
Linus Torvalds 已提交
811 812 813 814 815 816 817 818 819 820 821 822 823
	return 0;
}

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

	error = xfs_fs_geometry(mp, &fsgeo, 4);
	if (error)
D
Dave Chinner 已提交
824
		return error;
L
Linus Torvalds 已提交
825 826

	if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
E
Eric Sandeen 已提交
827
		return -EFAULT;
L
Linus Torvalds 已提交
828 829 830 831 832 833 834 835 836 837 838 839 840 841
	return 0;
}

/*
 * Linux extended inode flags interface.
 */

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

842
	if (flags & FS_IMMUTABLE_FL)
L
Linus Torvalds 已提交
843 844 845
		xflags |= XFS_XFLAG_IMMUTABLE;
	else
		xflags &= ~XFS_XFLAG_IMMUTABLE;
846
	if (flags & FS_APPEND_FL)
L
Linus Torvalds 已提交
847 848 849
		xflags |= XFS_XFLAG_APPEND;
	else
		xflags &= ~XFS_XFLAG_APPEND;
850
	if (flags & FS_SYNC_FL)
L
Linus Torvalds 已提交
851 852 853
		xflags |= XFS_XFLAG_SYNC;
	else
		xflags &= ~XFS_XFLAG_SYNC;
854
	if (flags & FS_NOATIME_FL)
L
Linus Torvalds 已提交
855 856 857
		xflags |= XFS_XFLAG_NOATIME;
	else
		xflags &= ~XFS_XFLAG_NOATIME;
858
	if (flags & FS_NODUMP_FL)
L
Linus Torvalds 已提交
859 860 861 862 863 864 865 866 867 868 869 870 871 872
		xflags |= XFS_XFLAG_NODUMP;
	else
		xflags &= ~XFS_XFLAG_NODUMP;

	return xflags;
}

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

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
873
		flags |= FS_IMMUTABLE_FL;
L
Linus Torvalds 已提交
874
	if (di_flags & XFS_DIFLAG_APPEND)
875
		flags |= FS_APPEND_FL;
L
Linus Torvalds 已提交
876
	if (di_flags & XFS_DIFLAG_SYNC)
877
		flags |= FS_SYNC_FL;
L
Linus Torvalds 已提交
878
	if (di_flags & XFS_DIFLAG_NOATIME)
879
		flags |= FS_NOATIME_FL;
L
Linus Torvalds 已提交
880
	if (di_flags & XFS_DIFLAG_NODUMP)
881
		flags |= FS_NODUMP_FL;
L
Linus Torvalds 已提交
882 883 884
	return flags;
}

885 886 887 888 889 890 891 892
STATIC int
xfs_ioc_fsgetxattr(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;

893 894
	memset(&fa, 0, sizeof(struct fsxattr));

895 896 897
	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;
898
	fa.fsx_projid = xfs_get_projid(ip);
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922

	if (attr) {
		if (ip->i_afp) {
			if (ip->i_afp->if_flags & XFS_IFEXTENTS)
				fa.fsx_nextents = ip->i_afp->if_bytes /
							sizeof(xfs_bmbt_rec_t);
			else
				fa.fsx_nextents = ip->i_d.di_anextents;
		} else
			fa.fsx_nextents = 0;
	} else {
		if (ip->i_df.if_flags & XFS_IFEXTENTS)
			fa.fsx_nextents = ip->i_df.if_bytes /
						sizeof(xfs_bmbt_rec_t);
		else
			fa.fsx_nextents = ip->i_d.di_nextents;
	}
	xfs_iunlock(ip, XFS_ILOCK_SHARED);

	if (copy_to_user(arg, &fa, sizeof(fa)))
		return -EFAULT;
	return 0;
}

923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
STATIC void
xfs_set_diflags(
	struct xfs_inode	*ip,
	unsigned int		xflags)
{
	unsigned int		di_flags;

	/* can't set PREALLOC this way, just preserve it */
	di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
	if (xflags & XFS_XFLAG_IMMUTABLE)
		di_flags |= XFS_DIFLAG_IMMUTABLE;
	if (xflags & XFS_XFLAG_APPEND)
		di_flags |= XFS_DIFLAG_APPEND;
	if (xflags & XFS_XFLAG_SYNC)
		di_flags |= XFS_DIFLAG_SYNC;
	if (xflags & XFS_XFLAG_NOATIME)
		di_flags |= XFS_DIFLAG_NOATIME;
	if (xflags & XFS_XFLAG_NODUMP)
		di_flags |= XFS_DIFLAG_NODUMP;
	if (xflags & XFS_XFLAG_NODEFRAG)
		di_flags |= XFS_DIFLAG_NODEFRAG;
	if (xflags & XFS_XFLAG_FILESTREAM)
		di_flags |= XFS_DIFLAG_FILESTREAM;
946
	if (S_ISDIR(ip->i_d.di_mode)) {
947 948 949 950 951 952
		if (xflags & XFS_XFLAG_RTINHERIT)
			di_flags |= XFS_DIFLAG_RTINHERIT;
		if (xflags & XFS_XFLAG_NOSYMLINKS)
			di_flags |= XFS_DIFLAG_NOSYMLINKS;
		if (xflags & XFS_XFLAG_EXTSZINHERIT)
			di_flags |= XFS_DIFLAG_EXTSZINHERIT;
953 954
		if (xflags & XFS_XFLAG_PROJINHERIT)
			di_flags |= XFS_DIFLAG_PROJINHERIT;
955
	} else if (S_ISREG(ip->i_d.di_mode)) {
956 957 958 959 960 961 962 963 964
		if (xflags & XFS_XFLAG_REALTIME)
			di_flags |= XFS_DIFLAG_REALTIME;
		if (xflags & XFS_XFLAG_EXTSIZE)
			di_flags |= XFS_DIFLAG_EXTSIZE;
	}

	ip->i_d.di_flags = di_flags;
}

965 966 967 968
STATIC void
xfs_diflags_to_linux(
	struct xfs_inode	*ip)
{
969
	struct inode		*inode = VFS_I(ip);
970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
	unsigned int		xflags = xfs_ip2xflags(ip);

	if (xflags & XFS_XFLAG_IMMUTABLE)
		inode->i_flags |= S_IMMUTABLE;
	else
		inode->i_flags &= ~S_IMMUTABLE;
	if (xflags & XFS_XFLAG_APPEND)
		inode->i_flags |= S_APPEND;
	else
		inode->i_flags &= ~S_APPEND;
	if (xflags & XFS_XFLAG_SYNC)
		inode->i_flags |= S_SYNC;
	else
		inode->i_flags &= ~S_SYNC;
	if (xflags & XFS_XFLAG_NOATIME)
		inode->i_flags |= S_NOATIME;
	else
		inode->i_flags &= ~S_NOATIME;
}
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003

#define FSX_PROJID	1
#define FSX_EXTSIZE	2
#define FSX_XFLAGS	4
#define FSX_NONBLOCK	8

STATIC int
xfs_ioctl_setattr(
	xfs_inode_t		*ip,
	struct fsxattr		*fa,
	int			mask)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
	unsigned int		lock_flags = 0;
C
Christoph Hellwig 已提交
1004
	struct xfs_dquot	*udqp = NULL;
1005
	struct xfs_dquot	*pdqp = NULL;
1006 1007 1008
	struct xfs_dquot	*olddquot = NULL;
	int			code;

C
Christoph Hellwig 已提交
1009
	trace_xfs_ioctl_setattr(ip);
1010 1011

	if (mp->m_flags & XFS_MOUNT_RDONLY)
D
Dave Chinner 已提交
1012
		return -EROFS;
1013
	if (XFS_FORCED_SHUTDOWN(mp))
D
Dave Chinner 已提交
1014
		return -EIO;
1015

1016
	/*
1017
	 * Disallow 32bit project ids when projid32bit feature is not enabled.
1018
	 */
1019 1020
	if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1) &&
			!xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
D
Dave Chinner 已提交
1021
		return -EINVAL;
1022

1023 1024 1025 1026 1027 1028 1029 1030 1031
	/*
	 * 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.
	 */
	if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
C
Christoph Hellwig 已提交
1032
		code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
1033
					 ip->i_d.di_gid, fa->fsx_projid,
1034
					 XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
1035 1036 1037 1038 1039 1040 1041 1042 1043
		if (code)
			return code;
	}

	/*
	 * For the other attributes, we acquire the inode lock and
	 * first do an error checking pass.
	 */
	tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
1044
	code = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
	if (code)
		goto error_return;

	lock_flags = XFS_ILOCK_EXCL;
	xfs_ilock(ip, lock_flags);

	/*
	 * 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.
	 */
1058
	if (!inode_owner_or_capable(VFS_I(ip))) {
D
Dave Chinner 已提交
1059
		code = -EPERM;
1060 1061 1062 1063 1064
		goto error_return;
	}

	/*
	 * Do a quota reservation only if projid is actually going to change.
1065 1066
	 * Only allow changing of projid from init_user_ns since it is a
	 * non user namespace aware identifier.
1067 1068
	 */
	if (mask & FSX_PROJID) {
1069
		if (current_user_ns() != &init_user_ns) {
D
Dave Chinner 已提交
1070
			code = -EINVAL;
1071 1072 1073
			goto error_return;
		}

C
Christoph Hellwig 已提交
1074 1075
		if (XFS_IS_QUOTA_RUNNING(mp) &&
		    XFS_IS_PQUOTA_ON(mp) &&
1076
		    xfs_get_projid(ip) != fa->fsx_projid) {
1077
			ASSERT(tp);
1078 1079
			code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL,
						pdqp, capable(CAP_FOWNER) ?
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
						XFS_QMOPT_FORCE_RES : 0);
			if (code)	/* out of quota */
				goto error_return;
		}
	}

	if (mask & FSX_EXTSIZE) {
		/*
		 * Can't change extent size if any extents are allocated.
		 */
		if (ip->i_d.di_nextents &&
		    ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
		     fa->fsx_extsize)) {
D
Dave Chinner 已提交
1093
			code = -EINVAL;	/* EFBIG? */
1094 1095 1096 1097 1098
			goto error_return;
		}

		/*
		 * Extent size must be a multiple of the appropriate block
1099 1100 1101 1102 1103 1104
		 * size, if set at all. It must also be smaller than the
		 * maximum extent size supported by the filesystem.
		 *
		 * Also, for non-realtime files, limit the extent size hint to
		 * half the size of the AGs in the filesystem so alignment
		 * doesn't result in extents larger than an AG.
1105 1106
		 */
		if (fa->fsx_extsize != 0) {
1107 1108 1109 1110 1111
			xfs_extlen_t    size;
			xfs_fsblock_t   extsize_fsb;

			extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
			if (extsize_fsb > MAXEXTLEN) {
D
Dave Chinner 已提交
1112
				code = -EINVAL;
1113 1114
				goto error_return;
			}
1115 1116 1117 1118 1119 1120 1121 1122

			if (XFS_IS_REALTIME_INODE(ip) ||
			    ((mask & FSX_XFLAGS) &&
			    (fa->fsx_xflags & XFS_XFLAG_REALTIME))) {
				size = mp->m_sb.sb_rextsize <<
				       mp->m_sb.sb_blocklog;
			} else {
				size = mp->m_sb.sb_blocksize;
1123
				if (extsize_fsb > mp->m_sb.sb_agblocks / 2) {
D
Dave Chinner 已提交
1124
					code = -EINVAL;
1125 1126
					goto error_return;
				}
1127 1128 1129
			}

			if (fa->fsx_extsize % size) {
D
Dave Chinner 已提交
1130
				code = -EINVAL;
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
				goto error_return;
			}
		}
	}


	if (mask & FSX_XFLAGS) {
		/*
		 * Can't change realtime flag if any extents are allocated.
		 */
		if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
		    (XFS_IS_REALTIME_INODE(ip)) !=
		    (fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
D
Dave Chinner 已提交
1144
			code = -EINVAL;	/* EFBIG? */
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
			goto error_return;
		}

		/*
		 * If realtime flag is set then must have realtime data.
		 */
		if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
			if ((mp->m_sb.sb_rblocks == 0) ||
			    (mp->m_sb.sb_rextsize == 0) ||
			    (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
D
Dave Chinner 已提交
1155
				code = -EINVAL;
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168
				goto error_return;
			}
		}

		/*
		 * 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) ||
		     (fa->fsx_xflags &
				(XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
		    !capable(CAP_LINUX_IMMUTABLE)) {
D
Dave Chinner 已提交
1169
			code = -EPERM;
1170 1171 1172 1173
			goto error_return;
		}
	}

1174
	xfs_trans_ijoin(tp, ip, 0);
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186

	/*
	 * Change file ownership.  Must be the owner or privileged.
	 */
	if (mask & FSX_PROJID) {
		/*
		 * 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()
		 */
		if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
1187
		    !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
1188 1189 1190 1191 1192 1193
			ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);

		/*
		 * Change the ownerships and register quota modifications
		 * in the transaction.
		 */
1194
		if (xfs_get_projid(ip) != fa->fsx_projid) {
C
Christoph Hellwig 已提交
1195 1196
			if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
				olddquot = xfs_qm_vop_chown(tp, ip,
1197
							&ip->i_pdquot, pdqp);
1198
			}
1199
			ASSERT(ip->i_d.di_version > 1);
1200
			xfs_set_projid(ip, fa->fsx_projid);
1201 1202 1203 1204
		}

	}

1205
	if (mask & FSX_XFLAGS) {
1206
		xfs_set_diflags(ip, fa->fsx_xflags);
1207 1208
		xfs_diflags_to_linux(ip);
	}
1209

1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
	/*
	 * 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.
	 */
	if (mask & FSX_EXTSIZE) {
		int	extsize = 0;

		if (ip->i_d.di_flags &
				(XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
			extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
		ip->i_d.di_extsize = extsize;
	}

1224
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);

	XFS_STATS_INC(xs_ig_attrchg);

	/*
	 * If this is a synchronous mount, make sure that the
	 * transaction goes to disk before returning to the user.
	 * This is slightly sub-optimal in that truncates require
	 * two sync transactions instead of one for wsync filesystems.
	 * One for the truncate and one for the timestamps since we
	 * don't want to change the timestamps unless we're sure the
	 * truncate worked.  Truncates are less than 1% of the laddis
	 * mix so this probably isn't worth the trouble to optimize.
	 */
	if (mp->m_flags & XFS_MOUNT_WSYNC)
		xfs_trans_set_sync(tp);
	code = xfs_trans_commit(tp, 0);
	xfs_iunlock(ip, lock_flags);

	/*
	 * Release any dquot(s) the inode had kept before chown.
	 */
C
Christoph Hellwig 已提交
1247 1248
	xfs_qm_dqrele(olddquot);
	xfs_qm_dqrele(udqp);
1249
	xfs_qm_dqrele(pdqp);
1250

C
Christoph Hellwig 已提交
1251
	return code;
1252 1253

 error_return:
C
Christoph Hellwig 已提交
1254
	xfs_qm_dqrele(udqp);
1255
	xfs_qm_dqrele(pdqp);
1256 1257 1258 1259 1260 1261
	xfs_trans_cancel(tp, 0);
	if (lock_flags)
		xfs_iunlock(ip, lock_flags);
	return code;
}

L
Linus Torvalds 已提交
1262
STATIC int
L
Lachlan McIlroy 已提交
1263
xfs_ioc_fssetxattr(
L
Linus Torvalds 已提交
1264 1265 1266 1267 1268
	xfs_inode_t		*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
1269
	unsigned int		mask;
J
Jan Kara 已提交
1270
	int error;
L
Lachlan McIlroy 已提交
1271 1272 1273

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

1275
	mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID;
L
Lachlan McIlroy 已提交
1276
	if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1277
		mask |= FSX_NONBLOCK;
L
Linus Torvalds 已提交
1278

J
Jan Kara 已提交
1279 1280 1281 1282 1283
	error = mnt_want_write_file(filp);
	if (error)
		return error;
	error = xfs_ioctl_setattr(ip, &fa, mask);
	mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1284
	return error;
L
Lachlan McIlroy 已提交
1285
}
L
Linus Torvalds 已提交
1286

L
Lachlan McIlroy 已提交
1287 1288 1289 1290 1291 1292
STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;
L
Linus Torvalds 已提交
1293

L
Lachlan McIlroy 已提交
1294 1295 1296 1297 1298
	flags = xfs_di2lxflags(ip->i_d.di_flags);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}
L
Linus Torvalds 已提交
1299

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

L
Lachlan McIlroy 已提交
1311 1312
	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;
L
Linus Torvalds 已提交
1313

L
Lachlan McIlroy 已提交
1314 1315 1316 1317
	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL))
		return -EOPNOTSUPP;
L
Linus Torvalds 已提交
1318

1319
	mask = FSX_XFLAGS;
L
Lachlan McIlroy 已提交
1320
	if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1321 1322
		mask |= FSX_NONBLOCK;
	fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
L
Linus Torvalds 已提交
1323

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

1332 1333 1334
STATIC int
xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
{
1335
	struct getbmap __user	*base = (struct getbmap __user *)*ap;
1336 1337 1338

	/* copy only getbmap portion (not getbmapx) */
	if (copy_to_user(base, bmv, sizeof(struct getbmap)))
D
Dave Chinner 已提交
1339
		return -EFAULT;
1340 1341 1342 1343 1344

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

L
Linus Torvalds 已提交
1345 1346
STATIC int
xfs_ioc_getbmap(
1347
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1348 1349 1350 1351
	int			ioflags,
	unsigned int		cmd,
	void			__user *arg)
{
1352
	struct getbmapx		bmx;
L
Linus Torvalds 已提交
1353 1354
	int			error;

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

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

1361
	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
D
Dave Chinner 已提交
1362
	if (ioflags & XFS_IO_INVIS)
1363
		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
L
Linus Torvalds 已提交
1364

1365
	error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
1366
			    (__force struct getbmap *)arg+1);
L
Linus Torvalds 已提交
1367
	if (error)
D
Dave Chinner 已提交
1368
		return error;
L
Linus Torvalds 已提交
1369

1370 1371
	/* copy back header - only size of getbmap */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
E
Eric Sandeen 已提交
1372
		return -EFAULT;
L
Linus Torvalds 已提交
1373 1374 1375
	return 0;
}

1376 1377 1378
STATIC int
xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
{
1379
	struct getbmapx __user	*base = (struct getbmapx __user *)*ap;
1380 1381

	if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
D
Dave Chinner 已提交
1382
		return -EFAULT;
1383 1384 1385 1386 1387

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

L
Linus Torvalds 已提交
1388 1389
STATIC int
xfs_ioc_getbmapx(
1390
	struct xfs_inode	*ip,
L
Linus Torvalds 已提交
1391 1392 1393 1394 1395 1396
	void			__user *arg)
{
	struct getbmapx		bmx;
	int			error;

	if (copy_from_user(&bmx, arg, sizeof(bmx)))
E
Eric Sandeen 已提交
1397
		return -EFAULT;
L
Linus Torvalds 已提交
1398 1399

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

1402
	if (bmx.bmv_iflags & (~BMV_IF_VALID))
E
Eric Sandeen 已提交
1403
		return -EINVAL;
L
Linus Torvalds 已提交
1404

1405
	error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
1406
			    (__force struct getbmapx *)arg+1);
L
Linus Torvalds 已提交
1407
	if (error)
D
Dave Chinner 已提交
1408
		return error;
L
Linus Torvalds 已提交
1409

1410 1411
	/* copy back header */
	if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
E
Eric Sandeen 已提交
1412
		return -EFAULT;
L
Linus Torvalds 已提交
1413 1414 1415

	return 0;
}
L
Lachlan McIlroy 已提交
1416

D
Dave Chinner 已提交
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427
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 已提交
1428
		error = -EINVAL;
D
Dave Chinner 已提交
1429 1430 1431 1432 1433 1434
		goto out;
	}

	if (!(f.file->f_mode & FMODE_WRITE) ||
	    !(f.file->f_mode & FMODE_READ) ||
	    (f.file->f_flags & O_APPEND)) {
D
Dave Chinner 已提交
1435
		error = -EBADF;
D
Dave Chinner 已提交
1436 1437 1438 1439 1440
		goto out_put_file;
	}

	tmp = fdget((int)sxp->sx_fdtmp);
	if (!tmp.file) {
D
Dave Chinner 已提交
1441
		error = -EINVAL;
D
Dave Chinner 已提交
1442 1443 1444 1445 1446 1447
		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 已提交
1448
		error = -EBADF;
D
Dave Chinner 已提交
1449 1450 1451 1452 1453
		goto out_put_tmp_file;
	}

	if (IS_SWAPFILE(file_inode(f.file)) ||
	    IS_SWAPFILE(file_inode(tmp.file))) {
D
Dave Chinner 已提交
1454
		error = -EINVAL;
D
Dave Chinner 已提交
1455 1456 1457 1458 1459 1460 1461
		goto out_put_tmp_file;
	}

	ip = XFS_I(file_inode(f.file));
	tip = XFS_I(file_inode(tmp.file));

	if (ip->i_mount != tip->i_mount) {
D
Dave Chinner 已提交
1462
		error = -EINVAL;
D
Dave Chinner 已提交
1463 1464 1465 1466
		goto out_put_tmp_file;
	}

	if (ip->i_ino == tip->i_ino) {
D
Dave Chinner 已提交
1467
		error = -EINVAL;
D
Dave Chinner 已提交
1468 1469 1470 1471
		goto out_put_tmp_file;
	}

	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
D
Dave Chinner 已提交
1472
		error = -EIO;
D
Dave Chinner 已提交
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
		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;
}

1486 1487 1488 1489 1490 1491 1492 1493
/*
 * 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 已提交
1494 1495
	struct file		*filp,
	unsigned int		cmd,
1496
	unsigned long		p)
L
Lachlan McIlroy 已提交
1497
{
A
Al Viro 已提交
1498
	struct inode		*inode = file_inode(filp);
1499 1500 1501 1502
	struct xfs_inode	*ip = XFS_I(inode);
	struct xfs_mount	*mp = ip->i_mount;
	void			__user *arg = (void __user *)p;
	int			ioflags = 0;
L
Lachlan McIlroy 已提交
1503 1504
	int			error;

1505
	if (filp->f_mode & FMODE_NOCMTIME)
D
Dave Chinner 已提交
1506
		ioflags |= XFS_IO_INVIS;
L
Lachlan McIlroy 已提交
1507

C
Christoph Hellwig 已提交
1508
	trace_xfs_file_ioctl(ip);
1509 1510

	switch (cmd) {
C
Christoph Hellwig 已提交
1511 1512
	case FITRIM:
		return xfs_ioc_trim(mp, arg);
L
Lachlan McIlroy 已提交
1513 1514 1515 1516 1517 1518 1519
	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 已提交
1520 1521
	case XFS_IOC_UNRESVSP64:
	case XFS_IOC_ZERO_RANGE: {
1522
		xfs_flock64_t		bf;
L
Lachlan McIlroy 已提交
1523

1524
		if (copy_from_user(&bf, arg, sizeof(bf)))
E
Eric Sandeen 已提交
1525
			return -EFAULT;
1526 1527
		return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf);
	}
L
Lachlan McIlroy 已提交
1528 1529 1530 1531 1532 1533
	case XFS_IOC_DIOINFO: {
		struct dioattr	da;
		xfs_buftarg_t	*target =
			XFS_IS_REALTIME_INODE(ip) ?
			mp->m_rtdev_targp : mp->m_ddev_targp;

1534
		da.d_mem =  da.d_miniosz = target->bt_logical_sectorsize;
L
Lachlan McIlroy 已提交
1535 1536 1537
		da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);

		if (copy_to_user(arg, &da, sizeof(da)))
E
Eric Sandeen 已提交
1538
			return -EFAULT;
L
Lachlan McIlroy 已提交
1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
		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 已提交
1560 1561
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(ip, filp, arg);
L
Lachlan McIlroy 已提交
1562
	case XFS_IOC_GETXFLAGS:
L
Lachlan McIlroy 已提交
1563
		return xfs_ioc_getxflags(ip, arg);
L
Lachlan McIlroy 已提交
1564
	case XFS_IOC_SETXFLAGS:
L
Lachlan McIlroy 已提交
1565
		return xfs_ioc_setxflags(ip, filp, arg);
L
Lachlan McIlroy 已提交
1566 1567 1568 1569 1570

	case XFS_IOC_FSSETDM: {
		struct fsdmidata	dmi;

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

J
Jan Kara 已提交
1573 1574 1575 1576
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1577 1578
		error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
				dmi.fsd_dmstate);
J
Jan Kara 已提交
1579
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1580
		return error;
L
Lachlan McIlroy 已提交
1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
	}

	case XFS_IOC_GETBMAP:
	case XFS_IOC_GETBMAPA:
		return xfs_ioc_getbmap(ip, ioflags, cmd, arg);

	case XFS_IOC_GETBMAPX:
		return xfs_ioc_getbmapx(ip, arg);

	case XFS_IOC_FD_TO_HANDLE:
	case XFS_IOC_PATH_TO_HANDLE:
1592 1593
	case XFS_IOC_PATH_TO_FSHANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1594

1595
		if (copy_from_user(&hreq, arg, sizeof(hreq)))
E
Eric Sandeen 已提交
1596
			return -EFAULT;
1597 1598 1599 1600
		return xfs_find_handle(cmd, &hreq);
	}
	case XFS_IOC_OPEN_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1601

1602
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1603
			return -EFAULT;
1604
		return xfs_open_by_handle(filp, &hreq);
1605
	}
L
Lachlan McIlroy 已提交
1606
	case XFS_IOC_FSSETDM_BY_HANDLE:
1607
		return xfs_fssetdm_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1608

1609 1610
	case XFS_IOC_READLINK_BY_HANDLE: {
		xfs_fsop_handlereq_t	hreq;
L
Lachlan McIlroy 已提交
1611

1612
		if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
E
Eric Sandeen 已提交
1613
			return -EFAULT;
1614
		return xfs_readlink_by_handle(filp, &hreq);
1615
	}
L
Lachlan McIlroy 已提交
1616
	case XFS_IOC_ATTRLIST_BY_HANDLE:
1617
		return xfs_attrlist_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1618 1619

	case XFS_IOC_ATTRMULTI_BY_HANDLE:
1620
		return xfs_attrmulti_by_handle(filp, arg);
L
Lachlan McIlroy 已提交
1621 1622

	case XFS_IOC_SWAPEXT: {
1623 1624 1625
		struct xfs_swapext	sxp;

		if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
E
Eric Sandeen 已提交
1626
			return -EFAULT;
J
Jan Kara 已提交
1627 1628 1629
		error = mnt_want_write_file(filp);
		if (error)
			return error;
D
Dave Chinner 已提交
1630
		error = xfs_ioc_swapext(&sxp);
J
Jan Kara 已提交
1631
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1632
		return error;
L
Lachlan McIlroy 已提交
1633 1634 1635 1636 1637 1638 1639
	}

	case XFS_IOC_FSCOUNTS: {
		xfs_fsop_counts_t out;

		error = xfs_fs_counts(mp, &out);
		if (error)
D
Dave Chinner 已提交
1640
			return error;
L
Lachlan McIlroy 已提交
1641 1642

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1643
			return -EFAULT;
L
Lachlan McIlroy 已提交
1644 1645 1646 1647 1648 1649 1650 1651 1652 1653
		return 0;
	}

	case XFS_IOC_SET_RESBLKS: {
		xfs_fsop_resblks_t inout;
		__uint64_t	   in;

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

E
Eric Sandeen 已提交
1654
		if (mp->m_flags & XFS_MOUNT_RDONLY)
E
Eric Sandeen 已提交
1655
			return -EROFS;
E
Eric Sandeen 已提交
1656

L
Lachlan McIlroy 已提交
1657
		if (copy_from_user(&inout, arg, sizeof(inout)))
E
Eric Sandeen 已提交
1658
			return -EFAULT;
L
Lachlan McIlroy 已提交
1659

J
Jan Kara 已提交
1660 1661 1662 1663
		error = mnt_want_write_file(filp);
		if (error)
			return error;

L
Lachlan McIlroy 已提交
1664 1665 1666
		/* input parameter is passed in resblks field of structure */
		in = inout.resblks;
		error = xfs_reserve_blocks(mp, &in, &inout);
J
Jan Kara 已提交
1667
		mnt_drop_write_file(filp);
L
Lachlan McIlroy 已提交
1668
		if (error)
D
Dave Chinner 已提交
1669
			return error;
L
Lachlan McIlroy 已提交
1670 1671

		if (copy_to_user(arg, &inout, sizeof(inout)))
E
Eric Sandeen 已提交
1672
			return -EFAULT;
L
Lachlan McIlroy 已提交
1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683
		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 已提交
1684
			return error;
L
Lachlan McIlroy 已提交
1685 1686

		if (copy_to_user(arg, &out, sizeof(out)))
E
Eric Sandeen 已提交
1687
			return -EFAULT;
L
Lachlan McIlroy 已提交
1688 1689 1690 1691 1692 1693 1694 1695

		return 0;
	}

	case XFS_IOC_FSGROWFSDATA: {
		xfs_growfs_data_t in;

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

J
Jan Kara 已提交
1698 1699 1700
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1701
		error = xfs_growfs_data(mp, &in);
J
Jan Kara 已提交
1702
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1703
		return error;
L
Lachlan McIlroy 已提交
1704 1705 1706 1707 1708 1709
	}

	case XFS_IOC_FSGROWFSLOG: {
		xfs_growfs_log_t in;

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

J
Jan Kara 已提交
1712 1713 1714
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1715
		error = xfs_growfs_log(mp, &in);
J
Jan Kara 已提交
1716
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1717
		return error;
L
Lachlan McIlroy 已提交
1718 1719 1720 1721 1722 1723
	}

	case XFS_IOC_FSGROWFSRT: {
		xfs_growfs_rt_t in;

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

J
Jan Kara 已提交
1726 1727 1728
		error = mnt_want_write_file(filp);
		if (error)
			return error;
L
Lachlan McIlroy 已提交
1729
		error = xfs_growfs_rt(mp, &in);
J
Jan Kara 已提交
1730
		mnt_drop_write_file(filp);
D
Dave Chinner 已提交
1731
		return error;
L
Lachlan McIlroy 已提交
1732 1733 1734 1735 1736 1737 1738 1739 1740
	}

	case XFS_IOC_GOINGDOWN: {
		__uint32_t in;

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

		if (get_user(in, (__uint32_t __user *)arg))
E
Eric Sandeen 已提交
1741
			return -EFAULT;
L
Lachlan McIlroy 已提交
1742

D
Dave Chinner 已提交
1743
		return xfs_fs_goingdown(mp, in);
L
Lachlan McIlroy 已提交
1744 1745 1746 1747 1748 1749 1750 1751 1752
	}

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

D
Dave Chinner 已提交
1755
		return xfs_errortag_add(in.errtag, mp);
L
Lachlan McIlroy 已提交
1756 1757 1758 1759 1760 1761
	}

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

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

1764
	case XFS_IOC_FREE_EOFBLOCKS: {
1765 1766
		struct xfs_fs_eofblocks eofb;
		struct xfs_eofblocks keofb;
1767

1768 1769 1770 1771
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

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

1774
		if (copy_from_user(&eofb, arg, sizeof(eofb)))
E
Eric Sandeen 已提交
1775
			return -EFAULT;
1776

1777 1778
		error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
		if (error)
D
Dave Chinner 已提交
1779
			return error;
1780

D
Dave Chinner 已提交
1781
		return xfs_icache_free_eofblocks(mp, &keofb);
1782 1783
	}

L
Lachlan McIlroy 已提交
1784 1785 1786 1787
	default:
		return -ENOTTY;
	}
}