blob: 21667ba6dcd590c6a3a726f27184f948578c4dc0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Nathan Scott7b718762005-11-02 14:58:39 +11002 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Nathan Scott7b718762005-11-02 14:58:39 +11005 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * published by the Free Software Foundation.
8 *
Nathan Scott7b718762005-11-02 14:58:39 +11009 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 *
Nathan Scott7b718762005-11-02 14:58:39 +110014 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include "xfs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include "xfs_fs.h"
Nathan Scotta844f452005-11-02 14:38:42 +110020#include "xfs_bit.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include "xfs_log.h"
Nathan Scotta844f452005-11-02 14:38:42 +110022#include "xfs_inum.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include "xfs_trans.h"
24#include "xfs_sb.h"
Nathan Scotta844f452005-11-02 14:38:42 +110025#include "xfs_ag.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include "xfs_dir.h"
27#include "xfs_dir2.h"
28#include "xfs_alloc.h"
29#include "xfs_dmapi.h"
30#include "xfs_mount.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include "xfs_bmap_btree.h"
Nathan Scotta844f452005-11-02 14:38:42 +110032#include "xfs_alloc_btree.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include "xfs_ialloc_btree.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include "xfs_dir_sf.h"
Nathan Scotta844f452005-11-02 14:38:42 +110035#include "xfs_attr_sf.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include "xfs_dir2_sf.h"
37#include "xfs_dinode.h"
38#include "xfs_inode.h"
Nathan Scotta844f452005-11-02 14:38:42 +110039#include "xfs_btree.h"
40#include "xfs_ialloc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include "xfs_rtalloc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include "xfs_itable.h"
Nathan Scotta844f452005-11-02 14:38:42 +110043#include "xfs_error.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include "xfs_rw.h"
45#include "xfs_acl.h"
46#include "xfs_cap.h"
47#include "xfs_mac.h"
48#include "xfs_attr.h"
Nathan Scotta844f452005-11-02 14:38:42 +110049#include "xfs_bmap.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#include "xfs_buf_item.h"
51#include "xfs_utils.h"
52#include "xfs_dfrag.h"
53#include "xfs_fsops.h"
54
Randy Dunlap16f7e0f2006-01-11 12:17:46 -080055#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#include <linux/dcache.h>
57#include <linux/mount.h>
58#include <linux/namei.h>
59#include <linux/pagemap.h>
60
61/*
62 * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
63 * a file or fs handle.
64 *
65 * XFS_IOC_PATH_TO_FSHANDLE
66 * returns fs handle for a mount point or path within that mount point
67 * XFS_IOC_FD_TO_HANDLE
68 * returns full handle for a FD opened in user space
69 * XFS_IOC_PATH_TO_HANDLE
70 * returns full handle for a path
71 */
72STATIC int
73xfs_find_handle(
74 unsigned int cmd,
75 void __user *arg)
76{
77 int hsize;
78 xfs_handle_t handle;
79 xfs_fsop_handlereq_t hreq;
80 struct inode *inode;
81 struct vnode *vp;
82
83 if (copy_from_user(&hreq, arg, sizeof(hreq)))
84 return -XFS_ERROR(EFAULT);
85
86 memset((char *)&handle, 0, sizeof(handle));
87
88 switch (cmd) {
89 case XFS_IOC_PATH_TO_FSHANDLE:
90 case XFS_IOC_PATH_TO_HANDLE: {
91 struct nameidata nd;
92 int error;
93
94 error = user_path_walk_link((const char __user *)hreq.path, &nd);
95 if (error)
96 return error;
97
98 ASSERT(nd.dentry);
99 ASSERT(nd.dentry->d_inode);
100 inode = igrab(nd.dentry->d_inode);
101 path_release(&nd);
102 break;
103 }
104
105 case XFS_IOC_FD_TO_HANDLE: {
106 struct file *file;
107
108 file = fget(hreq.fd);
109 if (!file)
110 return -EBADF;
111
112 ASSERT(file->f_dentry);
113 ASSERT(file->f_dentry->d_inode);
114 inode = igrab(file->f_dentry->d_inode);
115 fput(file);
116 break;
117 }
118
119 default:
120 ASSERT(0);
121 return -XFS_ERROR(EINVAL);
122 }
123
124 if (inode->i_sb->s_magic != XFS_SB_MAGIC) {
125 /* we're not in XFS anymore, Toto */
126 iput(inode);
127 return -XFS_ERROR(EINVAL);
128 }
129
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000130 switch (inode->i_mode & S_IFMT) {
131 case S_IFREG:
132 case S_IFDIR:
133 case S_IFLNK:
134 break;
135 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 iput(inode);
137 return -XFS_ERROR(EBADF);
138 }
139
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000140 /* we need the vnode */
141 vp = LINVFS_GET_VP(inode);
142
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 /* now we can grab the fsid */
144 memcpy(&handle.ha_fsid, vp->v_vfsp->vfs_altfsid, sizeof(xfs_fsid_t));
145 hsize = sizeof(xfs_fsid_t);
146
147 if (cmd != XFS_IOC_PATH_TO_FSHANDLE) {
148 xfs_inode_t *ip;
149 bhv_desc_t *bhv;
150 int lock_mode;
151
152 /* need to get access to the xfs_inode to read the generation */
153 bhv = vn_bhv_lookup_unlocked(VN_BHV_HEAD(vp), &xfs_vnodeops);
154 ASSERT(bhv);
155 ip = XFS_BHVTOI(bhv);
156 ASSERT(ip);
157 lock_mode = xfs_ilock_map_shared(ip);
158
159 /* fill in fid section of handle from inode */
160 handle.ha_fid.xfs_fid_len = sizeof(xfs_fid_t) -
161 sizeof(handle.ha_fid.xfs_fid_len);
162 handle.ha_fid.xfs_fid_pad = 0;
163 handle.ha_fid.xfs_fid_gen = ip->i_d.di_gen;
164 handle.ha_fid.xfs_fid_ino = ip->i_ino;
165
166 xfs_iunlock_map_shared(ip, lock_mode);
167
168 hsize = XFS_HSIZE(handle);
169 }
170
171 /* now copy our handle into the user buffer & write out the size */
172 if (copy_to_user(hreq.ohandle, &handle, hsize) ||
173 copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) {
174 iput(inode);
175 return -XFS_ERROR(EFAULT);
176 }
177
178 iput(inode);
179 return 0;
180}
181
182
183/*
184 * Convert userspace handle data into vnode (and inode).
185 * We [ab]use the fact that all the fsop_handlereq ioctl calls
186 * have a data structure argument whose first component is always
187 * a xfs_fsop_handlereq_t, so we can cast to and from this type.
188 * This allows us to optimise the copy_from_user calls and gives
189 * a handy, shared routine.
190 *
191 * If no error, caller must always VN_RELE the returned vp.
192 */
193STATIC int
194xfs_vget_fsop_handlereq(
195 xfs_mount_t *mp,
196 struct inode *parinode, /* parent inode pointer */
197 xfs_fsop_handlereq_t *hreq,
198 vnode_t **vp,
199 struct inode **inode)
200{
201 void __user *hanp;
202 size_t hlen;
203 xfs_fid_t *xfid;
204 xfs_handle_t *handlep;
205 xfs_handle_t handle;
206 xfs_inode_t *ip;
207 struct inode *inodep;
208 vnode_t *vpp;
209 xfs_ino_t ino;
210 __u32 igen;
211 int error;
212
213 /*
214 * Only allow handle opens under a directory.
215 */
216 if (!S_ISDIR(parinode->i_mode))
217 return XFS_ERROR(ENOTDIR);
218
219 hanp = hreq->ihandle;
220 hlen = hreq->ihandlen;
221 handlep = &handle;
222
223 if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
224 return XFS_ERROR(EINVAL);
225 if (copy_from_user(handlep, hanp, hlen))
226 return XFS_ERROR(EFAULT);
227 if (hlen < sizeof(*handlep))
228 memset(((char *)handlep) + hlen, 0, sizeof(*handlep) - hlen);
229 if (hlen > sizeof(handlep->ha_fsid)) {
230 if (handlep->ha_fid.xfs_fid_len !=
231 (hlen - sizeof(handlep->ha_fsid)
232 - sizeof(handlep->ha_fid.xfs_fid_len))
233 || handlep->ha_fid.xfs_fid_pad)
234 return XFS_ERROR(EINVAL);
235 }
236
237 /*
238 * Crack the handle, obtain the inode # & generation #
239 */
240 xfid = (struct xfs_fid *)&handlep->ha_fid;
241 if (xfid->xfs_fid_len == sizeof(*xfid) - sizeof(xfid->xfs_fid_len)) {
242 ino = xfid->xfs_fid_ino;
243 igen = xfid->xfs_fid_gen;
244 } else {
245 return XFS_ERROR(EINVAL);
246 }
247
248 /*
249 * Get the XFS inode, building a vnode to go with it.
250 */
251 error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0);
252 if (error)
253 return error;
254 if (ip == NULL)
255 return XFS_ERROR(EIO);
256 if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
257 xfs_iput_new(ip, XFS_ILOCK_SHARED);
258 return XFS_ERROR(ENOENT);
259 }
260
261 vpp = XFS_ITOV(ip);
262 inodep = LINVFS_GET_IP(vpp);
263 xfs_iunlock(ip, XFS_ILOCK_SHARED);
264
265 *vp = vpp;
266 *inode = inodep;
267 return 0;
268}
269
270STATIC int
271xfs_open_by_handle(
272 xfs_mount_t *mp,
273 void __user *arg,
274 struct file *parfilp,
275 struct inode *parinode)
276{
277 int error;
278 int new_fd;
279 int permflag;
280 struct file *filp;
281 struct inode *inode;
282 struct dentry *dentry;
283 vnode_t *vp;
284 xfs_fsop_handlereq_t hreq;
285
286 if (!capable(CAP_SYS_ADMIN))
287 return -XFS_ERROR(EPERM);
288 if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
289 return -XFS_ERROR(EFAULT);
290
291 error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &vp, &inode);
292 if (error)
293 return -error;
294
295 /* Restrict xfs_open_by_handle to directories & regular files. */
296 if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
297 iput(inode);
298 return -XFS_ERROR(EINVAL);
299 }
300
301#if BITS_PER_LONG != 32
302 hreq.oflags |= O_LARGEFILE;
303#endif
304 /* Put open permission in namei format. */
305 permflag = hreq.oflags;
306 if ((permflag+1) & O_ACCMODE)
307 permflag++;
308 if (permflag & O_TRUNC)
309 permflag |= 2;
310
311 if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
312 (permflag & FMODE_WRITE) && IS_APPEND(inode)) {
313 iput(inode);
314 return -XFS_ERROR(EPERM);
315 }
316
317 if ((permflag & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
318 iput(inode);
319 return -XFS_ERROR(EACCES);
320 }
321
322 /* Can't write directories. */
323 if ( S_ISDIR(inode->i_mode) && (permflag & FMODE_WRITE)) {
324 iput(inode);
325 return -XFS_ERROR(EISDIR);
326 }
327
328 if ((new_fd = get_unused_fd()) < 0) {
329 iput(inode);
330 return new_fd;
331 }
332
333 dentry = d_alloc_anon(inode);
334 if (dentry == NULL) {
335 iput(inode);
336 put_unused_fd(new_fd);
337 return -XFS_ERROR(ENOMEM);
338 }
339
340 /* Ensure umount returns EBUSY on umounts while this file is open. */
341 mntget(parfilp->f_vfsmnt);
342
343 /* Create file pointer. */
344 filp = dentry_open(dentry, parfilp->f_vfsmnt, hreq.oflags);
345 if (IS_ERR(filp)) {
346 put_unused_fd(new_fd);
347 return -XFS_ERROR(-PTR_ERR(filp));
348 }
349 if (inode->i_mode & S_IFREG)
350 filp->f_op = &linvfs_invis_file_operations;
351
352 fd_install(new_fd, filp);
353 return new_fd;
354}
355
356STATIC int
357xfs_readlink_by_handle(
358 xfs_mount_t *mp,
359 void __user *arg,
360 struct file *parfilp,
361 struct inode *parinode)
362{
363 int error;
364 struct iovec aiov;
365 struct uio auio;
366 struct inode *inode;
367 xfs_fsop_handlereq_t hreq;
368 vnode_t *vp;
369 __u32 olen;
370
371 if (!capable(CAP_SYS_ADMIN))
372 return -XFS_ERROR(EPERM);
373 if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
374 return -XFS_ERROR(EFAULT);
375
376 error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &vp, &inode);
377 if (error)
378 return -error;
379
380 /* Restrict this handle operation to symlinks only. */
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000381 if (!S_ISLNK(inode->i_mode)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 VN_RELE(vp);
383 return -XFS_ERROR(EINVAL);
384 }
385
386 if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) {
387 VN_RELE(vp);
388 return -XFS_ERROR(EFAULT);
389 }
390 aiov.iov_len = olen;
391 aiov.iov_base = hreq.ohandle;
392
393 auio.uio_iov = &aiov;
394 auio.uio_iovcnt = 1;
395 auio.uio_offset = 0;
396 auio.uio_segflg = UIO_USERSPACE;
397 auio.uio_resid = olen;
398
399 VOP_READLINK(vp, &auio, IO_INVIS, NULL, error);
400
401 VN_RELE(vp);
402 return (olen - auio.uio_resid);
403}
404
405STATIC int
406xfs_fssetdm_by_handle(
407 xfs_mount_t *mp,
408 void __user *arg,
409 struct file *parfilp,
410 struct inode *parinode)
411{
412 int error;
413 struct fsdmidata fsd;
414 xfs_fsop_setdm_handlereq_t dmhreq;
415 struct inode *inode;
416 bhv_desc_t *bdp;
417 vnode_t *vp;
418
419 if (!capable(CAP_MKNOD))
420 return -XFS_ERROR(EPERM);
421 if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
422 return -XFS_ERROR(EFAULT);
423
424 error = xfs_vget_fsop_handlereq(mp, parinode, &dmhreq.hreq, &vp, &inode);
425 if (error)
426 return -error;
427
428 if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
429 VN_RELE(vp);
430 return -XFS_ERROR(EPERM);
431 }
432
433 if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
434 VN_RELE(vp);
435 return -XFS_ERROR(EFAULT);
436 }
437
438 bdp = bhv_base_unlocked(VN_BHV_HEAD(vp));
439 error = xfs_set_dmattrs(bdp, fsd.fsd_dmevmask, fsd.fsd_dmstate, NULL);
440
441 VN_RELE(vp);
442 if (error)
443 return -error;
444 return 0;
445}
446
447STATIC int
448xfs_attrlist_by_handle(
449 xfs_mount_t *mp,
450 void __user *arg,
451 struct file *parfilp,
452 struct inode *parinode)
453{
454 int error;
455 attrlist_cursor_kern_t *cursor;
456 xfs_fsop_attrlist_handlereq_t al_hreq;
457 struct inode *inode;
458 vnode_t *vp;
459 char *kbuf;
460
461 if (!capable(CAP_SYS_ADMIN))
462 return -XFS_ERROR(EPERM);
463 if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
464 return -XFS_ERROR(EFAULT);
465 if (al_hreq.buflen > XATTR_LIST_MAX)
466 return -XFS_ERROR(EINVAL);
467
468 error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq,
469 &vp, &inode);
470 if (error)
471 goto out;
472
473 kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL);
474 if (!kbuf)
475 goto out_vn_rele;
476
477 cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
478 VOP_ATTR_LIST(vp, kbuf, al_hreq.buflen, al_hreq.flags,
479 cursor, NULL, error);
480 if (error)
481 goto out_kfree;
482
483 if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
484 error = -EFAULT;
485
486 out_kfree:
487 kfree(kbuf);
488 out_vn_rele:
489 VN_RELE(vp);
490 out:
491 return -error;
492}
493
494STATIC int
495xfs_attrmulti_attr_get(
496 struct vnode *vp,
497 char *name,
498 char __user *ubuf,
499 __uint32_t *len,
500 __uint32_t flags)
501{
502 char *kbuf;
503 int error = EFAULT;
504
505 if (*len > XATTR_SIZE_MAX)
506 return EINVAL;
507 kbuf = kmalloc(*len, GFP_KERNEL);
508 if (!kbuf)
509 return ENOMEM;
510
511 VOP_ATTR_GET(vp, name, kbuf, len, flags, NULL, error);
512 if (error)
513 goto out_kfree;
514
515 if (copy_to_user(ubuf, kbuf, *len))
516 error = EFAULT;
517
518 out_kfree:
519 kfree(kbuf);
520 return error;
521}
522
523STATIC int
524xfs_attrmulti_attr_set(
525 struct vnode *vp,
526 char *name,
527 const char __user *ubuf,
528 __uint32_t len,
529 __uint32_t flags)
530{
531 char *kbuf;
532 int error = EFAULT;
533
Christoph Hellwig3542c6e2006-01-09 20:52:00 -0800534 if (IS_RDONLY(&vp->v_inode))
535 return -EROFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if (IS_IMMUTABLE(&vp->v_inode) || IS_APPEND(&vp->v_inode))
537 return EPERM;
538 if (len > XATTR_SIZE_MAX)
539 return EINVAL;
540
541 kbuf = kmalloc(len, GFP_KERNEL);
542 if (!kbuf)
543 return ENOMEM;
544
545 if (copy_from_user(kbuf, ubuf, len))
546 goto out_kfree;
547
548 VOP_ATTR_SET(vp, name, kbuf, len, flags, NULL, error);
549
550 out_kfree:
551 kfree(kbuf);
552 return error;
553}
554
555STATIC int
556xfs_attrmulti_attr_remove(
557 struct vnode *vp,
558 char *name,
559 __uint32_t flags)
560{
561 int error;
562
Christoph Hellwig3542c6e2006-01-09 20:52:00 -0800563
564 if (IS_RDONLY(&vp->v_inode))
565 return -EROFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 if (IS_IMMUTABLE(&vp->v_inode) || IS_APPEND(&vp->v_inode))
567 return EPERM;
568
569 VOP_ATTR_REMOVE(vp, name, flags, NULL, error);
570 return error;
571}
572
573STATIC int
574xfs_attrmulti_by_handle(
575 xfs_mount_t *mp,
576 void __user *arg,
577 struct file *parfilp,
578 struct inode *parinode)
579{
580 int error;
581 xfs_attr_multiop_t *ops;
582 xfs_fsop_attrmulti_handlereq_t am_hreq;
583 struct inode *inode;
584 vnode_t *vp;
585 unsigned int i, size;
586 char *attr_name;
587
588 if (!capable(CAP_SYS_ADMIN))
589 return -XFS_ERROR(EPERM);
590 if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
591 return -XFS_ERROR(EFAULT);
592
593 error = xfs_vget_fsop_handlereq(mp, parinode, &am_hreq.hreq, &vp, &inode);
594 if (error)
595 goto out;
596
597 error = E2BIG;
598 size = am_hreq.opcount * sizeof(attr_multiop_t);
599 if (!size || size > 16 * PAGE_SIZE)
600 goto out_vn_rele;
601
602 error = ENOMEM;
603 ops = kmalloc(size, GFP_KERNEL);
604 if (!ops)
605 goto out_vn_rele;
606
607 error = EFAULT;
608 if (copy_from_user(ops, am_hreq.ops, size))
609 goto out_kfree_ops;
610
611 attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
612 if (!attr_name)
613 goto out_kfree_ops;
614
615
616 error = 0;
617 for (i = 0; i < am_hreq.opcount; i++) {
618 ops[i].am_error = strncpy_from_user(attr_name,
619 ops[i].am_attrname, MAXNAMELEN);
620 if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
621 error = -ERANGE;
622 if (ops[i].am_error < 0)
623 break;
624
625 switch (ops[i].am_opcode) {
626 case ATTR_OP_GET:
627 ops[i].am_error = xfs_attrmulti_attr_get(vp,
628 attr_name, ops[i].am_attrvalue,
629 &ops[i].am_length, ops[i].am_flags);
630 break;
631 case ATTR_OP_SET:
632 ops[i].am_error = xfs_attrmulti_attr_set(vp,
633 attr_name, ops[i].am_attrvalue,
634 ops[i].am_length, ops[i].am_flags);
635 break;
636 case ATTR_OP_REMOVE:
637 ops[i].am_error = xfs_attrmulti_attr_remove(vp,
638 attr_name, ops[i].am_flags);
639 break;
640 default:
641 ops[i].am_error = EINVAL;
642 }
643 }
644
645 if (copy_to_user(am_hreq.ops, ops, size))
646 error = XFS_ERROR(EFAULT);
647
648 kfree(attr_name);
649 out_kfree_ops:
650 kfree(ops);
651 out_vn_rele:
652 VN_RELE(vp);
653 out:
654 return -error;
655}
656
657/* prototypes for a few of the stack-hungry cases that have
658 * their own functions. Functions are defined after their use
659 * so gcc doesn't get fancy and inline them with -03 */
660
661STATIC int
662xfs_ioc_space(
663 bhv_desc_t *bdp,
664 vnode_t *vp,
665 struct file *filp,
666 int flags,
667 unsigned int cmd,
668 void __user *arg);
669
670STATIC int
671xfs_ioc_bulkstat(
672 xfs_mount_t *mp,
673 unsigned int cmd,
674 void __user *arg);
675
676STATIC int
677xfs_ioc_fsgeometry_v1(
678 xfs_mount_t *mp,
679 void __user *arg);
680
681STATIC int
682xfs_ioc_fsgeometry(
683 xfs_mount_t *mp,
684 void __user *arg);
685
686STATIC int
687xfs_ioc_xattr(
688 vnode_t *vp,
689 xfs_inode_t *ip,
690 struct file *filp,
691 unsigned int cmd,
692 void __user *arg);
693
694STATIC int
695xfs_ioc_getbmap(
696 bhv_desc_t *bdp,
697 struct file *filp,
698 int flags,
699 unsigned int cmd,
700 void __user *arg);
701
702STATIC int
703xfs_ioc_getbmapx(
704 bhv_desc_t *bdp,
705 void __user *arg);
706
707int
708xfs_ioctl(
709 bhv_desc_t *bdp,
710 struct inode *inode,
711 struct file *filp,
712 int ioflags,
713 unsigned int cmd,
714 void __user *arg)
715{
716 int error;
717 vnode_t *vp;
718 xfs_inode_t *ip;
719 xfs_mount_t *mp;
720
721 vp = LINVFS_GET_VP(inode);
722
723 vn_trace_entry(vp, "xfs_ioctl", (inst_t *)__return_address);
724
725 ip = XFS_BHVTOI(bdp);
726 mp = ip->i_mount;
727
728 switch (cmd) {
729
730 case XFS_IOC_ALLOCSP:
731 case XFS_IOC_FREESP:
732 case XFS_IOC_RESVSP:
733 case XFS_IOC_UNRESVSP:
734 case XFS_IOC_ALLOCSP64:
735 case XFS_IOC_FREESP64:
736 case XFS_IOC_RESVSP64:
737 case XFS_IOC_UNRESVSP64:
738 /*
739 * Only allow the sys admin to reserve space unless
740 * unwritten extents are enabled.
741 */
742 if (!XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) &&
743 !capable(CAP_SYS_ADMIN))
744 return -EPERM;
745
746 return xfs_ioc_space(bdp, vp, filp, ioflags, cmd, arg);
747
748 case XFS_IOC_DIOINFO: {
749 struct dioattr da;
750 xfs_buftarg_t *target =
751 (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) ?
752 mp->m_rtdev_targp : mp->m_ddev_targp;
753
754 da.d_mem = da.d_miniosz = 1 << target->pbr_sshift;
755 /* The size dio will do in one go */
756 da.d_maxiosz = 64 * PAGE_CACHE_SIZE;
757
758 if (copy_to_user(arg, &da, sizeof(da)))
759 return -XFS_ERROR(EFAULT);
760 return 0;
761 }
762
763 case XFS_IOC_FSBULKSTAT_SINGLE:
764 case XFS_IOC_FSBULKSTAT:
765 case XFS_IOC_FSINUMBERS:
766 return xfs_ioc_bulkstat(mp, cmd, arg);
767
768 case XFS_IOC_FSGEOMETRY_V1:
769 return xfs_ioc_fsgeometry_v1(mp, arg);
770
771 case XFS_IOC_FSGEOMETRY:
772 return xfs_ioc_fsgeometry(mp, arg);
773
774 case XFS_IOC_GETVERSION:
775 case XFS_IOC_GETXFLAGS:
776 case XFS_IOC_SETXFLAGS:
777 case XFS_IOC_FSGETXATTR:
778 case XFS_IOC_FSSETXATTR:
779 case XFS_IOC_FSGETXATTRA:
780 return xfs_ioc_xattr(vp, ip, filp, cmd, arg);
781
782 case XFS_IOC_FSSETDM: {
783 struct fsdmidata dmi;
784
785 if (copy_from_user(&dmi, arg, sizeof(dmi)))
786 return -XFS_ERROR(EFAULT);
787
788 error = xfs_set_dmattrs(bdp, dmi.fsd_dmevmask, dmi.fsd_dmstate,
789 NULL);
790 return -error;
791 }
792
793 case XFS_IOC_GETBMAP:
794 case XFS_IOC_GETBMAPA:
795 return xfs_ioc_getbmap(bdp, filp, ioflags, cmd, arg);
796
797 case XFS_IOC_GETBMAPX:
798 return xfs_ioc_getbmapx(bdp, arg);
799
800 case XFS_IOC_FD_TO_HANDLE:
801 case XFS_IOC_PATH_TO_HANDLE:
802 case XFS_IOC_PATH_TO_FSHANDLE:
803 return xfs_find_handle(cmd, arg);
804
805 case XFS_IOC_OPEN_BY_HANDLE:
806 return xfs_open_by_handle(mp, arg, filp, inode);
807
808 case XFS_IOC_FSSETDM_BY_HANDLE:
809 return xfs_fssetdm_by_handle(mp, arg, filp, inode);
810
811 case XFS_IOC_READLINK_BY_HANDLE:
812 return xfs_readlink_by_handle(mp, arg, filp, inode);
813
814 case XFS_IOC_ATTRLIST_BY_HANDLE:
815 return xfs_attrlist_by_handle(mp, arg, filp, inode);
816
817 case XFS_IOC_ATTRMULTI_BY_HANDLE:
818 return xfs_attrmulti_by_handle(mp, arg, filp, inode);
819
820 case XFS_IOC_SWAPEXT: {
821 error = xfs_swapext((struct xfs_swapext __user *)arg);
822 return -error;
823 }
824
825 case XFS_IOC_FSCOUNTS: {
826 xfs_fsop_counts_t out;
827
828 error = xfs_fs_counts(mp, &out);
829 if (error)
830 return -error;
831
832 if (copy_to_user(arg, &out, sizeof(out)))
833 return -XFS_ERROR(EFAULT);
834 return 0;
835 }
836
837 case XFS_IOC_SET_RESBLKS: {
838 xfs_fsop_resblks_t inout;
839 __uint64_t in;
840
841 if (!capable(CAP_SYS_ADMIN))
842 return -EPERM;
843
844 if (copy_from_user(&inout, arg, sizeof(inout)))
845 return -XFS_ERROR(EFAULT);
846
847 /* input parameter is passed in resblks field of structure */
848 in = inout.resblks;
849 error = xfs_reserve_blocks(mp, &in, &inout);
850 if (error)
851 return -error;
852
853 if (copy_to_user(arg, &inout, sizeof(inout)))
854 return -XFS_ERROR(EFAULT);
855 return 0;
856 }
857
858 case XFS_IOC_GET_RESBLKS: {
859 xfs_fsop_resblks_t out;
860
861 if (!capable(CAP_SYS_ADMIN))
862 return -EPERM;
863
864 error = xfs_reserve_blocks(mp, NULL, &out);
865 if (error)
866 return -error;
867
868 if (copy_to_user(arg, &out, sizeof(out)))
869 return -XFS_ERROR(EFAULT);
870
871 return 0;
872 }
873
874 case XFS_IOC_FSGROWFSDATA: {
875 xfs_growfs_data_t in;
876
877 if (!capable(CAP_SYS_ADMIN))
878 return -EPERM;
879
880 if (copy_from_user(&in, arg, sizeof(in)))
881 return -XFS_ERROR(EFAULT);
882
883 error = xfs_growfs_data(mp, &in);
884 return -error;
885 }
886
887 case XFS_IOC_FSGROWFSLOG: {
888 xfs_growfs_log_t in;
889
890 if (!capable(CAP_SYS_ADMIN))
891 return -EPERM;
892
893 if (copy_from_user(&in, arg, sizeof(in)))
894 return -XFS_ERROR(EFAULT);
895
896 error = xfs_growfs_log(mp, &in);
897 return -error;
898 }
899
900 case XFS_IOC_FSGROWFSRT: {
901 xfs_growfs_rt_t in;
902
903 if (!capable(CAP_SYS_ADMIN))
904 return -EPERM;
905
906 if (copy_from_user(&in, arg, sizeof(in)))
907 return -XFS_ERROR(EFAULT);
908
909 error = xfs_growfs_rt(mp, &in);
910 return -error;
911 }
912
913 case XFS_IOC_FREEZE:
914 if (!capable(CAP_SYS_ADMIN))
915 return -EPERM;
916
917 if (inode->i_sb->s_frozen == SB_UNFROZEN)
918 freeze_bdev(inode->i_sb->s_bdev);
919 return 0;
920
921 case XFS_IOC_THAW:
922 if (!capable(CAP_SYS_ADMIN))
923 return -EPERM;
924 if (inode->i_sb->s_frozen != SB_UNFROZEN)
925 thaw_bdev(inode->i_sb->s_bdev, inode->i_sb);
926 return 0;
927
928 case XFS_IOC_GOINGDOWN: {
929 __uint32_t in;
930
931 if (!capable(CAP_SYS_ADMIN))
932 return -EPERM;
933
934 if (get_user(in, (__uint32_t __user *)arg))
935 return -XFS_ERROR(EFAULT);
936
937 error = xfs_fs_goingdown(mp, in);
938 return -error;
939 }
940
941 case XFS_IOC_ERROR_INJECTION: {
942 xfs_error_injection_t in;
943
944 if (!capable(CAP_SYS_ADMIN))
945 return -EPERM;
946
947 if (copy_from_user(&in, arg, sizeof(in)))
948 return -XFS_ERROR(EFAULT);
949
950 error = xfs_errortag_add(in.errtag, mp);
951 return -error;
952 }
953
954 case XFS_IOC_ERROR_CLEARALL:
955 if (!capable(CAP_SYS_ADMIN))
956 return -EPERM;
957
958 error = xfs_errortag_clearall(mp);
959 return -error;
960
961 default:
962 return -ENOTTY;
963 }
964}
965
966STATIC int
967xfs_ioc_space(
968 bhv_desc_t *bdp,
969 vnode_t *vp,
970 struct file *filp,
971 int ioflags,
972 unsigned int cmd,
973 void __user *arg)
974{
975 xfs_flock64_t bf;
976 int attr_flags = 0;
977 int error;
978
979 if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND))
980 return -XFS_ERROR(EPERM);
981
Eric Sandeenad4a8ac2005-09-02 16:41:16 +1000982 if (!(filp->f_mode & FMODE_WRITE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 return -XFS_ERROR(EBADF);
984
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000985 if (!VN_ISREG(vp))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 return -XFS_ERROR(EINVAL);
987
988 if (copy_from_user(&bf, arg, sizeof(bf)))
989 return -XFS_ERROR(EFAULT);
990
991 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
992 attr_flags |= ATTR_NONBLOCK;
993 if (ioflags & IO_INVIS)
994 attr_flags |= ATTR_DMI;
995
996 error = xfs_change_file_space(bdp, cmd, &bf, filp->f_pos,
997 NULL, attr_flags);
998 return -error;
999}
1000
1001STATIC int
1002xfs_ioc_bulkstat(
1003 xfs_mount_t *mp,
1004 unsigned int cmd,
1005 void __user *arg)
1006{
1007 xfs_fsop_bulkreq_t bulkreq;
1008 int count; /* # of records returned */
1009 xfs_ino_t inlast; /* last inode number */
1010 int done;
1011 int error;
1012
1013 /* done = 1 if there are more stats to get and if bulkstat */
1014 /* should be called again (unused here, but used in dmapi) */
1015
1016 if (!capable(CAP_SYS_ADMIN))
1017 return -EPERM;
1018
1019 if (XFS_FORCED_SHUTDOWN(mp))
1020 return -XFS_ERROR(EIO);
1021
1022 if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
1023 return -XFS_ERROR(EFAULT);
1024
1025 if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
1026 return -XFS_ERROR(EFAULT);
1027
1028 if ((count = bulkreq.icount) <= 0)
1029 return -XFS_ERROR(EINVAL);
1030
1031 if (cmd == XFS_IOC_FSINUMBERS)
1032 error = xfs_inumbers(mp, &inlast, &count,
1033 bulkreq.ubuffer);
1034 else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
1035 error = xfs_bulkstat_single(mp, &inlast,
1036 bulkreq.ubuffer, &done);
1037 else { /* XFS_IOC_FSBULKSTAT */
1038 if (count == 1 && inlast != 0) {
1039 inlast++;
1040 error = xfs_bulkstat_single(mp, &inlast,
1041 bulkreq.ubuffer, &done);
1042 } else {
1043 error = xfs_bulkstat(mp, &inlast, &count,
1044 (bulkstat_one_pf)xfs_bulkstat_one, NULL,
1045 sizeof(xfs_bstat_t), bulkreq.ubuffer,
1046 BULKSTAT_FG_QUICK, &done);
1047 }
1048 }
1049
1050 if (error)
1051 return -error;
1052
1053 if (bulkreq.ocount != NULL) {
1054 if (copy_to_user(bulkreq.lastip, &inlast,
1055 sizeof(xfs_ino_t)))
1056 return -XFS_ERROR(EFAULT);
1057
1058 if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
1059 return -XFS_ERROR(EFAULT);
1060 }
1061
1062 return 0;
1063}
1064
1065STATIC int
1066xfs_ioc_fsgeometry_v1(
1067 xfs_mount_t *mp,
1068 void __user *arg)
1069{
1070 xfs_fsop_geom_v1_t fsgeo;
1071 int error;
1072
1073 error = xfs_fs_geometry(mp, (xfs_fsop_geom_t *)&fsgeo, 3);
1074 if (error)
1075 return -error;
1076
1077 if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
1078 return -XFS_ERROR(EFAULT);
1079 return 0;
1080}
1081
1082STATIC int
1083xfs_ioc_fsgeometry(
1084 xfs_mount_t *mp,
1085 void __user *arg)
1086{
1087 xfs_fsop_geom_t fsgeo;
1088 int error;
1089
1090 error = xfs_fs_geometry(mp, &fsgeo, 4);
1091 if (error)
1092 return -error;
1093
1094 if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
1095 return -XFS_ERROR(EFAULT);
1096 return 0;
1097}
1098
1099/*
1100 * Linux extended inode flags interface.
1101 */
1102#define LINUX_XFLAG_SYNC 0x00000008 /* Synchronous updates */
1103#define LINUX_XFLAG_IMMUTABLE 0x00000010 /* Immutable file */
1104#define LINUX_XFLAG_APPEND 0x00000020 /* writes to file may only append */
1105#define LINUX_XFLAG_NODUMP 0x00000040 /* do not dump file */
1106#define LINUX_XFLAG_NOATIME 0x00000080 /* do not update atime */
1107
1108STATIC unsigned int
1109xfs_merge_ioc_xflags(
1110 unsigned int flags,
1111 unsigned int start)
1112{
1113 unsigned int xflags = start;
1114
1115 if (flags & LINUX_XFLAG_IMMUTABLE)
1116 xflags |= XFS_XFLAG_IMMUTABLE;
1117 else
1118 xflags &= ~XFS_XFLAG_IMMUTABLE;
1119 if (flags & LINUX_XFLAG_APPEND)
1120 xflags |= XFS_XFLAG_APPEND;
1121 else
1122 xflags &= ~XFS_XFLAG_APPEND;
1123 if (flags & LINUX_XFLAG_SYNC)
1124 xflags |= XFS_XFLAG_SYNC;
1125 else
1126 xflags &= ~XFS_XFLAG_SYNC;
1127 if (flags & LINUX_XFLAG_NOATIME)
1128 xflags |= XFS_XFLAG_NOATIME;
1129 else
1130 xflags &= ~XFS_XFLAG_NOATIME;
1131 if (flags & LINUX_XFLAG_NODUMP)
1132 xflags |= XFS_XFLAG_NODUMP;
1133 else
1134 xflags &= ~XFS_XFLAG_NODUMP;
1135
1136 return xflags;
1137}
1138
1139STATIC unsigned int
1140xfs_di2lxflags(
1141 __uint16_t di_flags)
1142{
1143 unsigned int flags = 0;
1144
1145 if (di_flags & XFS_DIFLAG_IMMUTABLE)
1146 flags |= LINUX_XFLAG_IMMUTABLE;
1147 if (di_flags & XFS_DIFLAG_APPEND)
1148 flags |= LINUX_XFLAG_APPEND;
1149 if (di_flags & XFS_DIFLAG_SYNC)
1150 flags |= LINUX_XFLAG_SYNC;
1151 if (di_flags & XFS_DIFLAG_NOATIME)
1152 flags |= LINUX_XFLAG_NOATIME;
1153 if (di_flags & XFS_DIFLAG_NODUMP)
1154 flags |= LINUX_XFLAG_NODUMP;
1155 return flags;
1156}
1157
1158STATIC int
1159xfs_ioc_xattr(
1160 vnode_t *vp,
1161 xfs_inode_t *ip,
1162 struct file *filp,
1163 unsigned int cmd,
1164 void __user *arg)
1165{
1166 struct fsxattr fa;
1167 vattr_t va;
1168 int error;
1169 int attr_flags;
1170 unsigned int flags;
1171
1172 switch (cmd) {
1173 case XFS_IOC_FSGETXATTR: {
Nathan Scott77bc5be2005-06-21 15:48:04 +10001174 va.va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \
1175 XFS_AT_NEXTENTS | XFS_AT_PROJID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 VOP_GETATTR(vp, &va, 0, NULL, error);
1177 if (error)
1178 return -error;
1179
1180 fa.fsx_xflags = va.va_xflags;
1181 fa.fsx_extsize = va.va_extsize;
1182 fa.fsx_nextents = va.va_nextents;
Nathan Scott77bc5be2005-06-21 15:48:04 +10001183 fa.fsx_projid = va.va_projid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184
1185 if (copy_to_user(arg, &fa, sizeof(fa)))
1186 return -XFS_ERROR(EFAULT);
1187 return 0;
1188 }
1189
1190 case XFS_IOC_FSSETXATTR: {
1191 if (copy_from_user(&fa, arg, sizeof(fa)))
1192 return -XFS_ERROR(EFAULT);
1193
1194 attr_flags = 0;
1195 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1196 attr_flags |= ATTR_NONBLOCK;
1197
Nathan Scott77bc5be2005-06-21 15:48:04 +10001198 va.va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | XFS_AT_PROJID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 va.va_xflags = fa.fsx_xflags;
1200 va.va_extsize = fa.fsx_extsize;
Nathan Scott77bc5be2005-06-21 15:48:04 +10001201 va.va_projid = fa.fsx_projid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
1203 VOP_SETATTR(vp, &va, attr_flags, NULL, error);
1204 if (!error)
1205 vn_revalidate(vp); /* update Linux inode flags */
1206 return -error;
1207 }
1208
1209 case XFS_IOC_FSGETXATTRA: {
Nathan Scott77bc5be2005-06-21 15:48:04 +10001210 va.va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \
1211 XFS_AT_ANEXTENTS | XFS_AT_PROJID;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 VOP_GETATTR(vp, &va, 0, NULL, error);
1213 if (error)
1214 return -error;
1215
1216 fa.fsx_xflags = va.va_xflags;
1217 fa.fsx_extsize = va.va_extsize;
1218 fa.fsx_nextents = va.va_anextents;
Nathan Scott77bc5be2005-06-21 15:48:04 +10001219 fa.fsx_projid = va.va_projid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220
1221 if (copy_to_user(arg, &fa, sizeof(fa)))
1222 return -XFS_ERROR(EFAULT);
1223 return 0;
1224 }
1225
1226 case XFS_IOC_GETXFLAGS: {
1227 flags = xfs_di2lxflags(ip->i_d.di_flags);
1228 if (copy_to_user(arg, &flags, sizeof(flags)))
1229 return -XFS_ERROR(EFAULT);
1230 return 0;
1231 }
1232
1233 case XFS_IOC_SETXFLAGS: {
1234 if (copy_from_user(&flags, arg, sizeof(flags)))
1235 return -XFS_ERROR(EFAULT);
1236
1237 if (flags & ~(LINUX_XFLAG_IMMUTABLE | LINUX_XFLAG_APPEND | \
1238 LINUX_XFLAG_NOATIME | LINUX_XFLAG_NODUMP | \
1239 LINUX_XFLAG_SYNC))
1240 return -XFS_ERROR(EOPNOTSUPP);
1241
1242 attr_flags = 0;
1243 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1244 attr_flags |= ATTR_NONBLOCK;
1245
1246 va.va_mask = XFS_AT_XFLAGS;
1247 va.va_xflags = xfs_merge_ioc_xflags(flags,
1248 xfs_ip2xflags(ip));
1249
1250 VOP_SETATTR(vp, &va, attr_flags, NULL, error);
1251 if (!error)
1252 vn_revalidate(vp); /* update Linux inode flags */
1253 return -error;
1254 }
1255
1256 case XFS_IOC_GETVERSION: {
1257 flags = LINVFS_GET_IP(vp)->i_generation;
1258 if (copy_to_user(arg, &flags, sizeof(flags)))
1259 return -XFS_ERROR(EFAULT);
1260 return 0;
1261 }
1262
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 default:
1264 return -ENOTTY;
1265 }
1266}
1267
1268STATIC int
1269xfs_ioc_getbmap(
1270 bhv_desc_t *bdp,
1271 struct file *filp,
1272 int ioflags,
1273 unsigned int cmd,
1274 void __user *arg)
1275{
1276 struct getbmap bm;
1277 int iflags;
1278 int error;
1279
1280 if (copy_from_user(&bm, arg, sizeof(bm)))
1281 return -XFS_ERROR(EFAULT);
1282
1283 if (bm.bmv_count < 2)
1284 return -XFS_ERROR(EINVAL);
1285
1286 iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
1287 if (ioflags & IO_INVIS)
1288 iflags |= BMV_IF_NO_DMAPI_READ;
1289
1290 error = xfs_getbmap(bdp, &bm, (struct getbmap __user *)arg+1, iflags);
1291 if (error)
1292 return -error;
1293
1294 if (copy_to_user(arg, &bm, sizeof(bm)))
1295 return -XFS_ERROR(EFAULT);
1296 return 0;
1297}
1298
1299STATIC int
1300xfs_ioc_getbmapx(
1301 bhv_desc_t *bdp,
1302 void __user *arg)
1303{
1304 struct getbmapx bmx;
1305 struct getbmap bm;
1306 int iflags;
1307 int error;
1308
1309 if (copy_from_user(&bmx, arg, sizeof(bmx)))
1310 return -XFS_ERROR(EFAULT);
1311
1312 if (bmx.bmv_count < 2)
1313 return -XFS_ERROR(EINVAL);
1314
1315 /*
1316 * Map input getbmapx structure to a getbmap
1317 * structure for xfs_getbmap.
1318 */
1319 GETBMAP_CONVERT(bmx, bm);
1320
1321 iflags = bmx.bmv_iflags;
1322
1323 if (iflags & (~BMV_IF_VALID))
1324 return -XFS_ERROR(EINVAL);
1325
1326 iflags |= BMV_IF_EXTENDED;
1327
1328 error = xfs_getbmap(bdp, &bm, (struct getbmapx __user *)arg+1, iflags);
1329 if (error)
1330 return -error;
1331
1332 GETBMAP_CONVERT(bm, bmx);
1333
1334 if (copy_to_user(arg, &bmx, sizeof(bmx)))
1335 return -XFS_ERROR(EFAULT);
1336
1337 return 0;
1338}