xfs_ioctl.c 48.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2 3
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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.
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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"
Linus Torvalds's avatar
Linus Torvalds committed
24 25
#include "xfs_mount.h"
#include "xfs_inode.h"
26
#include "xfs_ioctl.h"
27
#include "xfs_alloc.h"
Linus Torvalds's avatar
Linus Torvalds committed
28 29
#include "xfs_rtalloc.h"
#include "xfs_itable.h"
30
#include "xfs_error.h"
Linus Torvalds's avatar
Linus Torvalds committed
31
#include "xfs_attr.h"
32
#include "xfs_bmap.h"
Dave Chinner's avatar
Dave Chinner committed
33
#include "xfs_bmap_util.h"
Linus Torvalds's avatar
Linus Torvalds committed
34
#include "xfs_fsops.h"
Christoph Hellwig's avatar
Christoph Hellwig committed
35
#include "xfs_discard.h"
36
#include "xfs_quota.h"
37
#include "xfs_export.h"
Christoph Hellwig's avatar
Christoph Hellwig committed
38
#include "xfs_trace.h"
39
#include "xfs_icache.h"
Dave Chinner's avatar
Dave Chinner committed
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"
Linus Torvalds's avatar
Linus Torvalds committed
48

49
#include <linux/capability.h>
50
#include <linux/cred.h>
Linus Torvalds's avatar
Linus Torvalds committed
51 52 53 54
#include <linux/dcache.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
55
#include <linux/slab.h>
56
#include <linux/exportfs.h>
Linus Torvalds's avatar
Linus Torvalds committed
57 58 59 60 61 62 63 64 65 66 67 68

/*
 * 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
 */
69
int
Linus Torvalds's avatar
Linus Torvalds committed
70 71
xfs_find_handle(
	unsigned int		cmd,
72
	xfs_fsop_handlereq_t	*hreq)
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75 76
{
	int			hsize;
	xfs_handle_t		handle;
	struct inode		*inode;
77
	struct fd		f = {NULL};
78
	struct path		path;
79
	int			error;
80
	struct xfs_inode	*ip;
Linus Torvalds's avatar
Linus Torvalds committed
81

82
	if (cmd == XFS_IOC_FD_TO_HANDLE) {
83 84
		f = fdget(hreq->fd);
		if (!f.file)
85
			return -EBADF;
Al Viro's avatar
Al Viro committed
86
		inode = file_inode(f.file);
87 88 89 90
	} else {
		error = user_lpath((const char __user *)hreq->path, &path);
		if (error)
			return error;
91
		inode = d_inode(path.dentry);
Linus Torvalds's avatar
Linus Torvalds committed
92
	}
93 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
	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 {
119 120 121
		handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
					sizeof(handle.ha_fid.fid_len);
		handle.ha_fid.fid_pad = 0;
122
		handle.ha_fid.fid_gen = inode->i_generation;
123
		handle.ha_fid.fid_ino = ip->i_ino;
Christoph Hellwig's avatar
Christoph Hellwig committed
124
		hsize = sizeof(xfs_handle_t);
Linus Torvalds's avatar
Linus Torvalds committed
125 126
	}

127
	error = -EFAULT;
128
	if (copy_to_user(hreq->ohandle, &handle, hsize) ||
129 130
	    copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
		goto out_put;
Linus Torvalds's avatar
Linus Torvalds committed
131

132 133 134 135
	error = 0;

 out_put:
	if (cmd == XFS_IOC_FD_TO_HANDLE)
136
		fdput(f);
137 138 139
	else
		path_put(&path);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142
}

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

	/*
	 * Only allow handle opens under a directory.
	 */
Al Viro's avatar
Al Viro committed
169
	if (!S_ISDIR(file_inode(parfilp)->i_mode))
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
		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);
}
Linus Torvalds's avatar
Linus Torvalds committed
188

189 190 191 192 193 194
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);
Linus Torvalds's avatar
Linus Torvalds committed
195 196
}

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

	if (!capable(CAP_SYS_ADMIN))
Eric Sandeen's avatar
Eric Sandeen committed
213
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
214

215 216 217
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
218
	inode = d_inode(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
219 220 221

	/* Restrict xfs_open_by_handle to directories & regular files. */
	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
Eric Sandeen's avatar
Eric Sandeen committed
222
		error = -EPERM;
223
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
224 225 226
	}

#if BITS_PER_LONG != 32
227
	hreq->oflags |= O_LARGEFILE;
Linus Torvalds's avatar
Linus Torvalds committed
228
#endif
229

230
	permflag = hreq->oflags;
231
	fmode = OPEN_FMODE(permflag);
Linus Torvalds's avatar
Linus Torvalds committed
232
	if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
233
	    (fmode & FMODE_WRITE) && IS_APPEND(inode)) {
Eric Sandeen's avatar
Eric Sandeen committed
234
		error = -EPERM;
235
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
236 237
	}

238
	if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
239
		error = -EPERM;
240
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243
	}

	/* Can't write directories. */
244
	if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {
Eric Sandeen's avatar
Eric Sandeen committed
245
		error = -EISDIR;
246
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
247 248
	}

249
	fd = get_unused_fd_flags(0);
250 251 252
	if (fd < 0) {
		error = fd;
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
253 254
	}

255 256 257 258
	path.mnt = parfilp->f_path.mnt;
	path.dentry = dentry;
	filp = dentry_open(&path, hreq->oflags, cred);
	dput(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
259
	if (IS_ERR(filp)) {
260 261
		put_unused_fd(fd);
		return PTR_ERR(filp);
Linus Torvalds's avatar
Linus Torvalds committed
262
	}
263

Al Viro's avatar
Al Viro committed
264
	if (S_ISREG(inode->i_mode)) {
265
		filp->f_flags |= O_NOATIME;
266
		filp->f_mode |= FMODE_NOCMTIME;
267
	}
Linus Torvalds's avatar
Linus Torvalds committed
268

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

 out_dput:
	dput(dentry);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
275 276
}

277
int
Linus Torvalds's avatar
Linus Torvalds committed
278
xfs_readlink_by_handle(
279 280
	struct file		*parfilp,
	xfs_fsop_handlereq_t	*hreq)
Linus Torvalds's avatar
Linus Torvalds committed
281
{
282
	struct dentry		*dentry;
Linus Torvalds's avatar
Linus Torvalds committed
283
	__u32			olen;
284
	int			error;
Linus Torvalds's avatar
Linus Torvalds committed
285 286

	if (!capable(CAP_SYS_ADMIN))
Eric Sandeen's avatar
Eric Sandeen committed
287
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
288

289 290 291
	dentry = xfs_handlereq_to_dentry(parfilp, hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
292 293

	/* Restrict this handle operation to symlinks only. */
294
	if (!d_is_symlink(dentry)) {
Eric Sandeen's avatar
Eric Sandeen committed
295
		error = -EINVAL;
296
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
297 298
	}

299
	if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
Eric Sandeen's avatar
Eric Sandeen committed
300
		error = -EFAULT;
301
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
302 303
	}

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

306 307
 out_dput:
	dput(dentry);
308
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
309 310
}

Dave Chinner's avatar
Dave Chinner committed
311 312 313
int
xfs_set_dmattrs(
	xfs_inode_t     *ip,
314 315
	uint		evmask,
	uint16_t	state)
Dave Chinner's avatar
Dave Chinner committed
316 317 318 319 320 321
{
	xfs_mount_t	*mp = ip->i_mount;
	xfs_trans_t	*tp;
	int		error;

	if (!capable(CAP_SYS_ADMIN))
322
		return -EPERM;
Dave Chinner's avatar
Dave Chinner committed
323 324

	if (XFS_FORCED_SHUTDOWN(mp))
325
		return -EIO;
Dave Chinner's avatar
Dave Chinner committed
326

327 328
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
	if (error)
Dave Chinner's avatar
Dave Chinner committed
329
		return error;
330

Dave Chinner's avatar
Dave Chinner committed
331 332 333 334 335 336 337
	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);
338
	error = xfs_trans_commit(tp);
Dave Chinner's avatar
Dave Chinner committed
339 340 341 342

	return error;
}

Linus Torvalds's avatar
Linus Torvalds committed
343 344
STATIC int
xfs_fssetdm_by_handle(
345 346
	struct file		*parfilp,
	void			__user *arg)
Linus Torvalds's avatar
Linus Torvalds committed
347 348 349 350
{
	int			error;
	struct fsdmidata	fsd;
	xfs_fsop_setdm_handlereq_t dmhreq;
351
	struct dentry		*dentry;
Linus Torvalds's avatar
Linus Torvalds committed
352 353

	if (!capable(CAP_MKNOD))
Eric Sandeen's avatar
Eric Sandeen committed
354
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
355
	if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
Eric Sandeen's avatar
Eric Sandeen committed
356
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
357

Jan Kara's avatar
Jan Kara committed
358 359 360 361
	error = mnt_want_write_file(parfilp);
	if (error)
		return error;

362
	dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
Jan Kara's avatar
Jan Kara committed
363 364
	if (IS_ERR(dentry)) {
		mnt_drop_write_file(parfilp);
365
		return PTR_ERR(dentry);
Jan Kara's avatar
Jan Kara committed
366
	}
Linus Torvalds's avatar
Linus Torvalds committed
367

368
	if (IS_IMMUTABLE(d_inode(dentry)) || IS_APPEND(d_inode(dentry))) {
Eric Sandeen's avatar
Eric Sandeen committed
369
		error = -EPERM;
370
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
371 372 373
	}

	if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
Eric Sandeen's avatar
Eric Sandeen committed
374
		error = -EFAULT;
375
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
376 377
	}

378
	error = xfs_set_dmattrs(XFS_I(d_inode(dentry)), fsd.fsd_dmevmask,
379
				 fsd.fsd_dmstate);
Linus Torvalds's avatar
Linus Torvalds committed
380

381
 out:
Jan Kara's avatar
Jan Kara committed
382
	mnt_drop_write_file(parfilp);
383
	dput(dentry);
384
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387 388
}

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

	if (!capable(CAP_SYS_ADMIN))
Eric Sandeen's avatar
Eric Sandeen committed
400
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
401
	if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
Eric Sandeen's avatar
Eric Sandeen committed
402
		return -EFAULT;
403
	if (al_hreq.buflen < sizeof(struct attrlist) ||
404
	    al_hreq.buflen > XFS_XATTR_LIST_MAX)
Eric Sandeen's avatar
Eric Sandeen committed
405
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
406

407 408 409 410
	/*
	 * Reject flags, only allow namespaces.
	 */
	if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
Eric Sandeen's avatar
Eric Sandeen committed
411
		return -EINVAL;
412

413 414 415
	dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
416

417 418 419
	kbuf = kmem_zalloc_large(al_hreq.buflen, KM_SLEEP);
	if (!kbuf)
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
420 421

	cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
422
	error = xfs_attr_list(XFS_I(d_inode(dentry)), kbuf, al_hreq.buflen,
423
					al_hreq.flags, cursor);
Linus Torvalds's avatar
Linus Torvalds committed
424 425 426
	if (error)
		goto out_kfree;

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

Linus Torvalds's avatar
Linus Torvalds committed
432 433 434
	if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
		error = -EFAULT;

435 436 437
out_kfree:
	kmem_free(kbuf);
out_dput:
438 439
	dput(dentry);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
440 441
}

442
int
Linus Torvalds's avatar
Linus Torvalds committed
443
xfs_attrmulti_attr_get(
444
	struct inode		*inode,
445 446
	unsigned char		*name,
	unsigned char		__user *ubuf,
447 448
	uint32_t		*len,
	uint32_t		flags)
Linus Torvalds's avatar
Linus Torvalds committed
449
{
450
	unsigned char		*kbuf;
451
	int			error = -EFAULT;
452

453
	if (*len > XFS_XATTR_SIZE_MAX)
454
		return -EINVAL;
455 456
	kbuf = kmem_zalloc_large(*len, KM_SLEEP);
	if (!kbuf)
457
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
458

459
	error = xfs_attr_get(XFS_I(inode), name, kbuf, (int *)len, flags);
Linus Torvalds's avatar
Linus Torvalds committed
460 461 462 463
	if (error)
		goto out_kfree;

	if (copy_to_user(ubuf, kbuf, *len))
464
		error = -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
465

466 467
out_kfree:
	kmem_free(kbuf);
Linus Torvalds's avatar
Linus Torvalds committed
468 469 470
	return error;
}

471
int
Linus Torvalds's avatar
Linus Torvalds committed
472
xfs_attrmulti_attr_set(
473
	struct inode		*inode,
474 475
	unsigned char		*name,
	const unsigned char	__user *ubuf,
476 477
	uint32_t		len,
	uint32_t		flags)
Linus Torvalds's avatar
Linus Torvalds committed
478
{
479
	unsigned char		*kbuf;
480
	int			error;
Linus Torvalds's avatar
Linus Torvalds committed
481

482
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
483
		return -EPERM;
484
	if (len > XFS_XATTR_SIZE_MAX)
485
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
486

Li Zefan's avatar
Li Zefan committed
487 488 489
	kbuf = memdup_user(ubuf, len);
	if (IS_ERR(kbuf))
		return PTR_ERR(kbuf);
490

491
	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
492 493
	if (!error)
		xfs_forget_acl(inode, name, flags);
494 495
	kfree(kbuf);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
496 497
}

498
int
Linus Torvalds's avatar
Linus Torvalds committed
499
xfs_attrmulti_attr_remove(
500
	struct inode		*inode,
501
	unsigned char		*name,
502
	uint32_t		flags)
Linus Torvalds's avatar
Linus Torvalds committed
503
{
504 505
	int			error;

506
	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
507
		return -EPERM;
508 509 510 511
	error = xfs_attr_remove(XFS_I(inode), name, flags);
	if (!error)
		xfs_forget_acl(inode, name, flags);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
512 513 514 515
}

STATIC int
xfs_attrmulti_by_handle(
516
	struct file		*parfilp,
517
	void			__user *arg)
Linus Torvalds's avatar
Linus Torvalds committed
518 519 520 521
{
	int			error;
	xfs_attr_multiop_t	*ops;
	xfs_fsop_attrmulti_handlereq_t am_hreq;
522
	struct dentry		*dentry;
Linus Torvalds's avatar
Linus Torvalds committed
523
	unsigned int		i, size;
524
	unsigned char		*attr_name;
Linus Torvalds's avatar
Linus Torvalds committed
525 526

	if (!capable(CAP_SYS_ADMIN))
Eric Sandeen's avatar
Eric Sandeen committed
527
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
528
	if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
Eric Sandeen's avatar
Eric Sandeen committed
529
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
530

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

535 536 537
	dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
538

539
	error = -E2BIG;
Christoph Hellwig's avatar
Christoph Hellwig committed
540
	size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
Linus Torvalds's avatar
Linus Torvalds committed
541
	if (!size || size > 16 * PAGE_SIZE)
542
		goto out_dput;
Linus Torvalds's avatar
Linus Torvalds committed
543

Li Zefan's avatar
Li Zefan committed
544 545
	ops = memdup_user(am_hreq.ops, size);
	if (IS_ERR(ops)) {
546
		error = PTR_ERR(ops);
547
		goto out_dput;
Li Zefan's avatar
Li Zefan committed
548
	}
Linus Torvalds's avatar
Linus Torvalds committed
549

550
	error = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
551 552 553 554 555 556
	attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
	if (!attr_name)
		goto out_kfree_ops;

	error = 0;
	for (i = 0; i < am_hreq.opcount; i++) {
557
		ops[i].am_error = strncpy_from_user((char *)attr_name,
Linus Torvalds's avatar
Linus Torvalds committed
558 559
				ops[i].am_attrname, MAXNAMELEN);
		if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
560
			error = -ERANGE;
Linus Torvalds's avatar
Linus Torvalds committed
561 562 563 564 565
		if (ops[i].am_error < 0)
			break;

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

	if (copy_to_user(am_hreq.ops, ops, size))
596
		error = -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
597 598 599 600

	kfree(attr_name);
 out_kfree_ops:
	kfree(ops);
601 602
 out_dput:
	dput(dentry);
603
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
604 605
}

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

619 620 621 622 623 624
	/*
	 * 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))
Eric Sandeen's avatar
Eric Sandeen committed
625
		return -EPERM;
626

627
	if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
Eric Sandeen's avatar
Eric Sandeen committed
628
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
629

630
	if (!(filp->f_mode & FMODE_WRITE))
Eric Sandeen's avatar
Eric Sandeen committed
631
		return -EBADF;
Linus Torvalds's avatar
Linus Torvalds committed
632

633
	if (!S_ISREG(inode->i_mode))
Eric Sandeen's avatar
Eric Sandeen committed
634
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
635

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

Jan Kara's avatar
Jan Kara committed
641 642 643
	error = mnt_want_write_file(filp);
	if (error)
		return error;
644

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

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

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

	if (bf->l_start < 0 ||
690
	    bf->l_start > inode->i_sb->s_maxbytes ||
691
	    bf->l_start + bf->l_len < 0 ||
692
	    bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
693
		error = -EINVAL;
694 695 696 697 698
		goto out_unlock;
	}

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

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

727
		error = xfs_vn_setattr_size(file_dentry(filp), &iattr);
728 729 730
		break;
	default:
		ASSERT(0);
731
		error = -EINVAL;
732 733 734 735 736
	}

	if (error)
		goto out_unlock;

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

out_unlock:
740
	xfs_iunlock(ip, iolock);
Jan Kara's avatar
Jan Kara committed
741
	mnt_drop_write_file(filp);
742
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
}

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))
Eric Sandeen's avatar
Eric Sandeen committed
764
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
765 766

	if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
Eric Sandeen's avatar
Eric Sandeen committed
767
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
768 769

	if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
Eric Sandeen's avatar
Eric Sandeen committed
770
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
771 772

	if ((count = bulkreq.icount) <= 0)
Eric Sandeen's avatar
Eric Sandeen committed
773
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
774

775
	if (bulkreq.ubuffer == NULL)
Eric Sandeen's avatar
Eric Sandeen committed
776
		return -EINVAL;
777

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

	if (error)
790
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
791 792 793 794

	if (bulkreq.ocount != NULL) {
		if (copy_to_user(bulkreq.lastip, &inlast,
						sizeof(xfs_ino_t)))
Eric Sandeen's avatar
Eric Sandeen committed
795
			return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
796 797

		if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
Eric Sandeen's avatar
Eric Sandeen committed
798
			return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
799 800 801 802 803 804 805 806 807 808
	}

	return 0;
}

STATIC int
xfs_ioc_fsgeometry_v1(
	xfs_mount_t		*mp,
	void			__user *arg)
{
809
	xfs_fsop_geom_t         fsgeo;
Linus Torvalds's avatar
Linus Torvalds committed
810 811
	int			error;

812
	error = xfs_fs_geometry(mp, &fsgeo, 3);
Linus Torvalds's avatar
Linus Torvalds committed
813
	if (error)
814
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
815

816 817 818 819 820 821
	/*
	 * 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)))
Eric Sandeen's avatar
Eric Sandeen committed
822
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
823 824 825 826 827 828 829 830 831 832 833 834 835
	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)
836
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
837 838

	if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
Eric Sandeen's avatar
Eric Sandeen committed
839
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
840 841 842 843 844 845 846 847 848 849 850 851 852 853
	return 0;
}

/*
 * Linux extended inode flags interface.
 */

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

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

	return xflags;
}

STATIC unsigned int
xfs_di2lxflags(
880
	uint16_t	di_flags)
Linus Torvalds's avatar
Linus Torvalds committed
881 882 883 884
{
	unsigned int	flags = 0;

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
885
		flags |= FS_IMMUTABLE_FL;
Linus Torvalds's avatar
Linus Torvalds committed
886
	if (di_flags & XFS_DIFLAG_APPEND)
887
		flags |= FS_APPEND_FL;
Linus Torvalds's avatar
Linus Torvalds committed
888
	if (di_flags & XFS_DIFLAG_SYNC)
889
		flags |= FS_SYNC_FL;
Linus Torvalds's avatar
Linus Torvalds committed
890
	if (di_flags & XFS_DIFLAG_NOATIME)
891
		flags |= FS_NOATIME_FL;
Linus Torvalds's avatar
Linus Torvalds committed
892
	if (di_flags & XFS_DIFLAG_NODUMP)
893
		flags |= FS_NODUMP_FL;
Linus Torvalds's avatar
Linus Torvalds committed
894 895 896
	return flags;
}

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

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

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

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

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

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

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

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

990
	return di_flags2;
991 992
}

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

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

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

	/* Can't change realtime flag if any extents are allocated. */
	if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&