blob: 30bb994d289955c546ae23be243a130fcfb2ba49 [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_dir2.h"
27#include "xfs_alloc.h"
28#include "xfs_dmapi.h"
29#include "xfs_mount.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include "xfs_bmap_btree.h"
Nathan Scotta844f452005-11-02 14:38:42 +110031#include "xfs_alloc_btree.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include "xfs_ialloc_btree.h"
Nathan Scotta844f452005-11-02 14:38:42 +110033#include "xfs_attr_sf.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include "xfs_dir2_sf.h"
35#include "xfs_dinode.h"
36#include "xfs_inode.h"
Nathan Scotta844f452005-11-02 14:38:42 +110037#include "xfs_btree.h"
38#include "xfs_ialloc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "xfs_rtalloc.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include "xfs_itable.h"
Nathan Scotta844f452005-11-02 14:38:42 +110041#include "xfs_error.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include "xfs_rw.h"
43#include "xfs_acl.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include "xfs_attr.h"
Nathan Scotta844f452005-11-02 14:38:42 +110045#include "xfs_bmap.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include "xfs_buf_item.h"
47#include "xfs_utils.h"
48#include "xfs_dfrag.h"
49#include "xfs_fsops.h"
Christoph Hellwig993386c2007-08-28 16:12:30 +100050#include "xfs_vnodeops.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Randy Dunlap16f7e0f2006-01-11 12:17:46 -080052#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include <linux/dcache.h>
54#include <linux/mount.h>
55#include <linux/namei.h>
56#include <linux/pagemap.h>
57
58/*
59 * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
60 * a file or fs handle.
61 *
62 * XFS_IOC_PATH_TO_FSHANDLE
63 * returns fs handle for a mount point or path within that mount point
64 * XFS_IOC_FD_TO_HANDLE
65 * returns full handle for a FD opened in user space
66 * XFS_IOC_PATH_TO_HANDLE
67 * returns full handle for a path
68 */
69STATIC int
70xfs_find_handle(
71 unsigned int cmd,
72 void __user *arg)
73{
74 int hsize;
75 xfs_handle_t handle;
76 xfs_fsop_handlereq_t hreq;
77 struct inode *inode;
Nathan Scott67fcaa72006-06-09 17:00:52 +100078 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
80 if (copy_from_user(&hreq, arg, sizeof(hreq)))
81 return -XFS_ERROR(EFAULT);
82
83 memset((char *)&handle, 0, sizeof(handle));
84
85 switch (cmd) {
86 case XFS_IOC_PATH_TO_FSHANDLE:
87 case XFS_IOC_PATH_TO_HANDLE: {
88 struct nameidata nd;
89 int error;
90
91 error = user_path_walk_link((const char __user *)hreq.path, &nd);
92 if (error)
93 return error;
94
95 ASSERT(nd.dentry);
96 ASSERT(nd.dentry->d_inode);
97 inode = igrab(nd.dentry->d_inode);
98 path_release(&nd);
99 break;
100 }
101
102 case XFS_IOC_FD_TO_HANDLE: {
103 struct file *file;
104
105 file = fget(hreq.fd);
106 if (!file)
107 return -EBADF;
108
Josef "Jeff" Sipeke678fb02006-12-08 02:36:49 -0800109 ASSERT(file->f_path.dentry);
110 ASSERT(file->f_path.dentry->d_inode);
111 inode = igrab(file->f_path.dentry->d_inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 fput(file);
113 break;
114 }
115
116 default:
117 ASSERT(0);
118 return -XFS_ERROR(EINVAL);
119 }
120
121 if (inode->i_sb->s_magic != XFS_SB_MAGIC) {
122 /* we're not in XFS anymore, Toto */
123 iput(inode);
124 return -XFS_ERROR(EINVAL);
125 }
126
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000127 switch (inode->i_mode & S_IFMT) {
128 case S_IFREG:
129 case S_IFDIR:
130 case S_IFLNK:
131 break;
132 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 iput(inode);
134 return -XFS_ERROR(EBADF);
135 }
136
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000137 /* we need the vnode */
Nathan Scottec86dc02006-03-17 17:25:36 +1100138 vp = vn_from_inode(inode);
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 /* now we can grab the fsid */
Christoph Hellwig0ce4cfd2007-08-30 17:20:53 +1000141 memcpy(&handle.ha_fsid, XFS_I(inode)->i_mount->m_fixedfsid,
Christoph Hellwig2f6f7b32007-08-29 11:44:18 +1000142 sizeof(xfs_fsid_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 hsize = sizeof(xfs_fsid_t);
144
145 if (cmd != XFS_IOC_PATH_TO_FSHANDLE) {
146 xfs_inode_t *ip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 int lock_mode;
148
149 /* need to get access to the xfs_inode to read the generation */
Christoph Hellwig75e17b32006-01-11 20:58:44 +1100150 ip = xfs_vtoi(vp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 ASSERT(ip);
152 lock_mode = xfs_ilock_map_shared(ip);
153
154 /* fill in fid section of handle from inode */
Christoph Hellwigc6143912007-09-14 15:22:37 +1000155 handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
156 sizeof(handle.ha_fid.fid_len);
157 handle.ha_fid.fid_pad = 0;
158 handle.ha_fid.fid_gen = ip->i_d.di_gen;
159 handle.ha_fid.fid_ino = ip->i_ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
161 xfs_iunlock_map_shared(ip, lock_mode);
162
163 hsize = XFS_HSIZE(handle);
164 }
165
166 /* now copy our handle into the user buffer & write out the size */
167 if (copy_to_user(hreq.ohandle, &handle, hsize) ||
168 copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) {
169 iput(inode);
170 return -XFS_ERROR(EFAULT);
171 }
172
173 iput(inode);
174 return 0;
175}
176
177
178/*
179 * Convert userspace handle data into vnode (and inode).
180 * We [ab]use the fact that all the fsop_handlereq ioctl calls
181 * have a data structure argument whose first component is always
182 * a xfs_fsop_handlereq_t, so we can cast to and from this type.
183 * This allows us to optimise the copy_from_user calls and gives
184 * a handy, shared routine.
185 *
186 * If no error, caller must always VN_RELE the returned vp.
187 */
188STATIC int
189xfs_vget_fsop_handlereq(
190 xfs_mount_t *mp,
191 struct inode *parinode, /* parent inode pointer */
192 xfs_fsop_handlereq_t *hreq,
Nathan Scott67fcaa72006-06-09 17:00:52 +1000193 bhv_vnode_t **vp,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 struct inode **inode)
195{
196 void __user *hanp;
197 size_t hlen;
198 xfs_fid_t *xfid;
199 xfs_handle_t *handlep;
200 xfs_handle_t handle;
201 xfs_inode_t *ip;
202 struct inode *inodep;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000203 bhv_vnode_t *vpp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 xfs_ino_t ino;
205 __u32 igen;
206 int error;
207
208 /*
209 * Only allow handle opens under a directory.
210 */
211 if (!S_ISDIR(parinode->i_mode))
212 return XFS_ERROR(ENOTDIR);
213
214 hanp = hreq->ihandle;
215 hlen = hreq->ihandlen;
216 handlep = &handle;
217
218 if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
219 return XFS_ERROR(EINVAL);
220 if (copy_from_user(handlep, hanp, hlen))
221 return XFS_ERROR(EFAULT);
222 if (hlen < sizeof(*handlep))
223 memset(((char *)handlep) + hlen, 0, sizeof(*handlep) - hlen);
224 if (hlen > sizeof(handlep->ha_fsid)) {
Christoph Hellwigc6143912007-09-14 15:22:37 +1000225 if (handlep->ha_fid.fid_len !=
226 (hlen - sizeof(handlep->ha_fsid) -
227 sizeof(handlep->ha_fid.fid_len)) ||
228 handlep->ha_fid.fid_pad)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 return XFS_ERROR(EINVAL);
230 }
231
232 /*
233 * Crack the handle, obtain the inode # & generation #
234 */
235 xfid = (struct xfs_fid *)&handlep->ha_fid;
Christoph Hellwigc6143912007-09-14 15:22:37 +1000236 if (xfid->fid_len == sizeof(*xfid) - sizeof(xfid->fid_len)) {
237 ino = xfid->fid_ino;
238 igen = xfid->fid_gen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 } else {
240 return XFS_ERROR(EINVAL);
241 }
242
243 /*
244 * Get the XFS inode, building a vnode to go with it.
245 */
246 error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0);
247 if (error)
248 return error;
249 if (ip == NULL)
250 return XFS_ERROR(EIO);
251 if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
252 xfs_iput_new(ip, XFS_ILOCK_SHARED);
253 return XFS_ERROR(ENOENT);
254 }
255
256 vpp = XFS_ITOV(ip);
Nathan Scottec86dc02006-03-17 17:25:36 +1100257 inodep = vn_to_inode(vpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 xfs_iunlock(ip, XFS_ILOCK_SHARED);
259
260 *vp = vpp;
261 *inode = inodep;
262 return 0;
263}
264
265STATIC int
266xfs_open_by_handle(
267 xfs_mount_t *mp,
268 void __user *arg,
269 struct file *parfilp,
270 struct inode *parinode)
271{
272 int error;
273 int new_fd;
274 int permflag;
275 struct file *filp;
276 struct inode *inode;
277 struct dentry *dentry;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000278 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 xfs_fsop_handlereq_t hreq;
280
281 if (!capable(CAP_SYS_ADMIN))
282 return -XFS_ERROR(EPERM);
283 if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
284 return -XFS_ERROR(EFAULT);
285
286 error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &vp, &inode);
287 if (error)
288 return -error;
289
290 /* Restrict xfs_open_by_handle to directories & regular files. */
291 if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
292 iput(inode);
293 return -XFS_ERROR(EINVAL);
294 }
295
296#if BITS_PER_LONG != 32
297 hreq.oflags |= O_LARGEFILE;
298#endif
299 /* Put open permission in namei format. */
300 permflag = hreq.oflags;
301 if ((permflag+1) & O_ACCMODE)
302 permflag++;
303 if (permflag & O_TRUNC)
304 permflag |= 2;
305
306 if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
307 (permflag & FMODE_WRITE) && IS_APPEND(inode)) {
308 iput(inode);
309 return -XFS_ERROR(EPERM);
310 }
311
312 if ((permflag & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
313 iput(inode);
314 return -XFS_ERROR(EACCES);
315 }
316
317 /* Can't write directories. */
318 if ( S_ISDIR(inode->i_mode) && (permflag & FMODE_WRITE)) {
319 iput(inode);
320 return -XFS_ERROR(EISDIR);
321 }
322
323 if ((new_fd = get_unused_fd()) < 0) {
324 iput(inode);
325 return new_fd;
326 }
327
328 dentry = d_alloc_anon(inode);
329 if (dentry == NULL) {
330 iput(inode);
331 put_unused_fd(new_fd);
332 return -XFS_ERROR(ENOMEM);
333 }
334
335 /* Ensure umount returns EBUSY on umounts while this file is open. */
Josef "Jeff" Sipeke678fb02006-12-08 02:36:49 -0800336 mntget(parfilp->f_path.mnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338 /* Create file pointer. */
Josef "Jeff" Sipeke678fb02006-12-08 02:36:49 -0800339 filp = dentry_open(dentry, parfilp->f_path.mnt, hreq.oflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 if (IS_ERR(filp)) {
341 put_unused_fd(new_fd);
342 return -XFS_ERROR(-PTR_ERR(filp));
343 }
Vlad Apostolov2e2e7bb2006-11-11 18:04:47 +1100344 if (inode->i_mode & S_IFREG) {
345 /* invisible operation should not change atime */
346 filp->f_flags |= O_NOATIME;
Nathan Scott3562fd42006-03-14 14:00:35 +1100347 filp->f_op = &xfs_invis_file_operations;
Vlad Apostolov2e2e7bb2006-11-11 18:04:47 +1100348 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
350 fd_install(new_fd, filp);
351 return new_fd;
352}
353
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000354/*
355 * This is a copy from fs/namei.c:vfs_readlink(), except for removing it's
356 * unused first argument.
357 */
358STATIC int
359do_readlink(
360 char __user *buffer,
361 int buflen,
362 const char *link)
363{
364 int len;
365
366 len = PTR_ERR(link);
367 if (IS_ERR(link))
368 goto out;
369
370 len = strlen(link);
371 if (len > (unsigned) buflen)
372 len = buflen;
373 if (copy_to_user(buffer, link, len))
374 len = -EFAULT;
375 out:
376 return len;
377}
378
379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380STATIC int
381xfs_readlink_by_handle(
382 xfs_mount_t *mp,
383 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 struct inode *parinode)
385{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 struct inode *inode;
387 xfs_fsop_handlereq_t hreq;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000388 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 __u32 olen;
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000390 void *link;
391 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
393 if (!capable(CAP_SYS_ADMIN))
394 return -XFS_ERROR(EPERM);
395 if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
396 return -XFS_ERROR(EFAULT);
397
398 error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &vp, &inode);
399 if (error)
400 return -error;
401
402 /* Restrict this handle operation to symlinks only. */
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000403 if (!S_ISLNK(inode->i_mode)) {
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000404 error = -XFS_ERROR(EINVAL);
405 goto out_iput;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 }
407
408 if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) {
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000409 error = -XFS_ERROR(EFAULT);
410 goto out_iput;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000413 link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
414 if (!link)
415 goto out_iput;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000417 error = -xfs_readlink(XFS_I(inode), link);
Nathan Scott67fcaa72006-06-09 17:00:52 +1000418 if (error)
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000419 goto out_kfree;
420 error = do_readlink(hreq.ohandle, olen, link);
421 if (error)
422 goto out_kfree;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000423
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000424 out_kfree:
425 kfree(link);
426 out_iput:
427 iput(inode);
428 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429}
430
431STATIC int
432xfs_fssetdm_by_handle(
433 xfs_mount_t *mp,
434 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 struct inode *parinode)
436{
437 int error;
438 struct fsdmidata fsd;
439 xfs_fsop_setdm_handlereq_t dmhreq;
440 struct inode *inode;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000441 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
443 if (!capable(CAP_MKNOD))
444 return -XFS_ERROR(EPERM);
445 if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
446 return -XFS_ERROR(EFAULT);
447
448 error = xfs_vget_fsop_handlereq(mp, parinode, &dmhreq.hreq, &vp, &inode);
449 if (error)
450 return -error;
451
452 if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
453 VN_RELE(vp);
454 return -XFS_ERROR(EPERM);
455 }
456
457 if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
458 VN_RELE(vp);
459 return -XFS_ERROR(EFAULT);
460 }
461
Christoph Hellwig993386c2007-08-28 16:12:30 +1000462 error = xfs_set_dmattrs(xfs_vtoi(vp),
463 fsd.fsd_dmevmask, fsd.fsd_dmstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464
465 VN_RELE(vp);
466 if (error)
467 return -error;
468 return 0;
469}
470
471STATIC int
472xfs_attrlist_by_handle(
473 xfs_mount_t *mp,
474 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 struct inode *parinode)
476{
477 int error;
478 attrlist_cursor_kern_t *cursor;
479 xfs_fsop_attrlist_handlereq_t al_hreq;
480 struct inode *inode;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000481 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 char *kbuf;
483
484 if (!capable(CAP_SYS_ADMIN))
485 return -XFS_ERROR(EPERM);
486 if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
487 return -XFS_ERROR(EFAULT);
488 if (al_hreq.buflen > XATTR_LIST_MAX)
489 return -XFS_ERROR(EINVAL);
490
491 error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq,
492 &vp, &inode);
493 if (error)
494 goto out;
495
496 kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL);
497 if (!kbuf)
498 goto out_vn_rele;
499
500 cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000501 error = xfs_attr_list(XFS_I(inode), kbuf, al_hreq.buflen,
502 al_hreq.flags, cursor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 if (error)
504 goto out_kfree;
505
506 if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
507 error = -EFAULT;
508
509 out_kfree:
510 kfree(kbuf);
511 out_vn_rele:
512 VN_RELE(vp);
513 out:
514 return -error;
515}
516
517STATIC int
518xfs_attrmulti_attr_get(
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000519 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 char *name,
521 char __user *ubuf,
522 __uint32_t *len,
523 __uint32_t flags)
524{
525 char *kbuf;
526 int error = EFAULT;
527
528 if (*len > XATTR_SIZE_MAX)
529 return EINVAL;
530 kbuf = kmalloc(*len, GFP_KERNEL);
531 if (!kbuf)
532 return ENOMEM;
533
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000534 error = xfs_attr_get(XFS_I(inode), name, kbuf, len, flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 if (error)
536 goto out_kfree;
537
538 if (copy_to_user(ubuf, kbuf, *len))
539 error = EFAULT;
540
541 out_kfree:
542 kfree(kbuf);
543 return error;
544}
545
546STATIC int
547xfs_attrmulti_attr_set(
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000548 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 char *name,
550 const char __user *ubuf,
551 __uint32_t len,
552 __uint32_t flags)
553{
554 char *kbuf;
555 int error = EFAULT;
556
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000557 if (IS_RDONLY(inode))
Christoph Hellwig3542c6e2006-01-09 20:52:00 -0800558 return -EROFS;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000559 if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 return EPERM;
561 if (len > XATTR_SIZE_MAX)
562 return EINVAL;
563
564 kbuf = kmalloc(len, GFP_KERNEL);
565 if (!kbuf)
566 return ENOMEM;
567
568 if (copy_from_user(kbuf, ubuf, len))
569 goto out_kfree;
570
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000571 error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
573 out_kfree:
574 kfree(kbuf);
575 return error;
576}
577
578STATIC int
579xfs_attrmulti_attr_remove(
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000580 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 char *name,
582 __uint32_t flags)
583{
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000584 if (IS_RDONLY(inode))
Christoph Hellwig3542c6e2006-01-09 20:52:00 -0800585 return -EROFS;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000586 if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 return EPERM;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000588 return xfs_attr_remove(XFS_I(inode), name, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590
591STATIC int
592xfs_attrmulti_by_handle(
593 xfs_mount_t *mp,
594 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 struct inode *parinode)
596{
597 int error;
598 xfs_attr_multiop_t *ops;
599 xfs_fsop_attrmulti_handlereq_t am_hreq;
600 struct inode *inode;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000601 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 unsigned int i, size;
603 char *attr_name;
604
605 if (!capable(CAP_SYS_ADMIN))
606 return -XFS_ERROR(EPERM);
607 if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
608 return -XFS_ERROR(EFAULT);
609
610 error = xfs_vget_fsop_handlereq(mp, parinode, &am_hreq.hreq, &vp, &inode);
611 if (error)
612 goto out;
613
614 error = E2BIG;
615 size = am_hreq.opcount * sizeof(attr_multiop_t);
616 if (!size || size > 16 * PAGE_SIZE)
617 goto out_vn_rele;
618
619 error = ENOMEM;
620 ops = kmalloc(size, GFP_KERNEL);
621 if (!ops)
622 goto out_vn_rele;
623
624 error = EFAULT;
625 if (copy_from_user(ops, am_hreq.ops, size))
626 goto out_kfree_ops;
627
628 attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
629 if (!attr_name)
630 goto out_kfree_ops;
631
632
633 error = 0;
634 for (i = 0; i < am_hreq.opcount; i++) {
635 ops[i].am_error = strncpy_from_user(attr_name,
636 ops[i].am_attrname, MAXNAMELEN);
637 if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
638 error = -ERANGE;
639 if (ops[i].am_error < 0)
640 break;
641
642 switch (ops[i].am_opcode) {
643 case ATTR_OP_GET:
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000644 ops[i].am_error = xfs_attrmulti_attr_get(inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 attr_name, ops[i].am_attrvalue,
646 &ops[i].am_length, ops[i].am_flags);
647 break;
648 case ATTR_OP_SET:
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000649 ops[i].am_error = xfs_attrmulti_attr_set(inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 attr_name, ops[i].am_attrvalue,
651 ops[i].am_length, ops[i].am_flags);
652 break;
653 case ATTR_OP_REMOVE:
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000654 ops[i].am_error = xfs_attrmulti_attr_remove(inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 attr_name, ops[i].am_flags);
656 break;
657 default:
658 ops[i].am_error = EINVAL;
659 }
660 }
661
662 if (copy_to_user(am_hreq.ops, ops, size))
663 error = XFS_ERROR(EFAULT);
664
665 kfree(attr_name);
666 out_kfree_ops:
667 kfree(ops);
668 out_vn_rele:
669 VN_RELE(vp);
670 out:
671 return -error;
672}
673
674/* prototypes for a few of the stack-hungry cases that have
675 * their own functions. Functions are defined after their use
676 * so gcc doesn't get fancy and inline them with -03 */
677
678STATIC int
679xfs_ioc_space(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000680 struct xfs_inode *ip,
Alexey Dobriyanf37ea142006-09-28 10:52:04 +1000681 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 struct file *filp,
683 int flags,
684 unsigned int cmd,
685 void __user *arg);
686
687STATIC int
688xfs_ioc_bulkstat(
689 xfs_mount_t *mp,
690 unsigned int cmd,
691 void __user *arg);
692
693STATIC int
694xfs_ioc_fsgeometry_v1(
695 xfs_mount_t *mp,
696 void __user *arg);
697
698STATIC int
699xfs_ioc_fsgeometry(
700 xfs_mount_t *mp,
701 void __user *arg);
702
703STATIC int
704xfs_ioc_xattr(
Nathan Scott67fcaa72006-06-09 17:00:52 +1000705 bhv_vnode_t *vp,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 xfs_inode_t *ip,
707 struct file *filp,
708 unsigned int cmd,
709 void __user *arg);
710
711STATIC int
Christoph Hellwigc83bfab2007-10-11 17:47:00 +1000712xfs_ioc_fsgetxattr(
713 xfs_inode_t *ip,
714 int attr,
715 void __user *arg);
716
717STATIC int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718xfs_ioc_getbmap(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000719 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 int flags,
721 unsigned int cmd,
722 void __user *arg);
723
724STATIC int
725xfs_ioc_getbmapx(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000726 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 void __user *arg);
728
729int
730xfs_ioctl(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000731 xfs_inode_t *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 struct file *filp,
733 int ioflags,
734 unsigned int cmd,
735 void __user *arg)
736{
Christoph Hellwig993386c2007-08-28 16:12:30 +1000737 struct inode *inode = filp->f_path.dentry->d_inode;
738 bhv_vnode_t *vp = vn_from_inode(inode);
739 xfs_mount_t *mp = ip->i_mount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Lachlan McIlroycf441ee2008-02-07 16:42:19 +1100742 xfs_itrace_entry(XFS_I(inode));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 switch (cmd) {
744
745 case XFS_IOC_ALLOCSP:
746 case XFS_IOC_FREESP:
747 case XFS_IOC_RESVSP:
748 case XFS_IOC_UNRESVSP:
749 case XFS_IOC_ALLOCSP64:
750 case XFS_IOC_FREESP64:
751 case XFS_IOC_RESVSP64:
752 case XFS_IOC_UNRESVSP64:
753 /*
754 * Only allow the sys admin to reserve space unless
755 * unwritten extents are enabled.
756 */
757 if (!XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) &&
758 !capable(CAP_SYS_ADMIN))
759 return -EPERM;
760
Christoph Hellwig993386c2007-08-28 16:12:30 +1000761 return xfs_ioc_space(ip, inode, filp, ioflags, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
763 case XFS_IOC_DIOINFO: {
764 struct dioattr da;
765 xfs_buftarg_t *target =
766 (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) ?
767 mp->m_rtdev_targp : mp->m_ddev_targp;
768
Nathan Scottce8e9222006-01-11 15:39:08 +1100769 da.d_mem = da.d_miniosz = 1 << target->bt_sshift;
Nathan Scott0d148242006-01-11 15:33:51 +1100770 da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
772 if (copy_to_user(arg, &da, sizeof(da)))
773 return -XFS_ERROR(EFAULT);
774 return 0;
775 }
776
777 case XFS_IOC_FSBULKSTAT_SINGLE:
778 case XFS_IOC_FSBULKSTAT:
779 case XFS_IOC_FSINUMBERS:
780 return xfs_ioc_bulkstat(mp, cmd, arg);
781
782 case XFS_IOC_FSGEOMETRY_V1:
783 return xfs_ioc_fsgeometry_v1(mp, arg);
784
785 case XFS_IOC_FSGEOMETRY:
786 return xfs_ioc_fsgeometry(mp, arg);
787
788 case XFS_IOC_GETVERSION:
Alexey Dobriyan87395de2006-09-28 10:56:01 +1000789 return put_user(inode->i_generation, (int __user *)arg);
790
Christoph Hellwigc83bfab2007-10-11 17:47:00 +1000791 case XFS_IOC_FSGETXATTR:
792 return xfs_ioc_fsgetxattr(ip, 0, arg);
793 case XFS_IOC_FSGETXATTRA:
794 return xfs_ioc_fsgetxattr(ip, 1, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 case XFS_IOC_GETXFLAGS:
796 case XFS_IOC_SETXFLAGS:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 case XFS_IOC_FSSETXATTR:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 return xfs_ioc_xattr(vp, ip, filp, cmd, arg);
799
800 case XFS_IOC_FSSETDM: {
801 struct fsdmidata dmi;
802
803 if (copy_from_user(&dmi, arg, sizeof(dmi)))
804 return -XFS_ERROR(EFAULT);
805
Christoph Hellwig993386c2007-08-28 16:12:30 +1000806 error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
807 dmi.fsd_dmstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 return -error;
809 }
810
811 case XFS_IOC_GETBMAP:
812 case XFS_IOC_GETBMAPA:
Christoph Hellwig993386c2007-08-28 16:12:30 +1000813 return xfs_ioc_getbmap(ip, ioflags, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 case XFS_IOC_GETBMAPX:
Christoph Hellwig993386c2007-08-28 16:12:30 +1000816 return xfs_ioc_getbmapx(ip, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 case XFS_IOC_FD_TO_HANDLE:
819 case XFS_IOC_PATH_TO_HANDLE:
820 case XFS_IOC_PATH_TO_FSHANDLE:
821 return xfs_find_handle(cmd, arg);
822
823 case XFS_IOC_OPEN_BY_HANDLE:
824 return xfs_open_by_handle(mp, arg, filp, inode);
825
826 case XFS_IOC_FSSETDM_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100827 return xfs_fssetdm_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
829 case XFS_IOC_READLINK_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100830 return xfs_readlink_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
832 case XFS_IOC_ATTRLIST_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100833 return xfs_attrlist_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
835 case XFS_IOC_ATTRMULTI_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100836 return xfs_attrmulti_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
838 case XFS_IOC_SWAPEXT: {
839 error = xfs_swapext((struct xfs_swapext __user *)arg);
840 return -error;
841 }
842
843 case XFS_IOC_FSCOUNTS: {
844 xfs_fsop_counts_t out;
845
846 error = xfs_fs_counts(mp, &out);
847 if (error)
848 return -error;
849
850 if (copy_to_user(arg, &out, sizeof(out)))
851 return -XFS_ERROR(EFAULT);
852 return 0;
853 }
854
855 case XFS_IOC_SET_RESBLKS: {
856 xfs_fsop_resblks_t inout;
857 __uint64_t in;
858
859 if (!capable(CAP_SYS_ADMIN))
860 return -EPERM;
861
862 if (copy_from_user(&inout, arg, sizeof(inout)))
863 return -XFS_ERROR(EFAULT);
864
865 /* input parameter is passed in resblks field of structure */
866 in = inout.resblks;
867 error = xfs_reserve_blocks(mp, &in, &inout);
868 if (error)
869 return -error;
870
871 if (copy_to_user(arg, &inout, sizeof(inout)))
872 return -XFS_ERROR(EFAULT);
873 return 0;
874 }
875
876 case XFS_IOC_GET_RESBLKS: {
877 xfs_fsop_resblks_t out;
878
879 if (!capable(CAP_SYS_ADMIN))
880 return -EPERM;
881
882 error = xfs_reserve_blocks(mp, NULL, &out);
883 if (error)
884 return -error;
885
886 if (copy_to_user(arg, &out, sizeof(out)))
887 return -XFS_ERROR(EFAULT);
888
889 return 0;
890 }
891
892 case XFS_IOC_FSGROWFSDATA: {
893 xfs_growfs_data_t in;
894
895 if (!capable(CAP_SYS_ADMIN))
896 return -EPERM;
897
898 if (copy_from_user(&in, arg, sizeof(in)))
899 return -XFS_ERROR(EFAULT);
900
901 error = xfs_growfs_data(mp, &in);
902 return -error;
903 }
904
905 case XFS_IOC_FSGROWFSLOG: {
906 xfs_growfs_log_t in;
907
908 if (!capable(CAP_SYS_ADMIN))
909 return -EPERM;
910
911 if (copy_from_user(&in, arg, sizeof(in)))
912 return -XFS_ERROR(EFAULT);
913
914 error = xfs_growfs_log(mp, &in);
915 return -error;
916 }
917
918 case XFS_IOC_FSGROWFSRT: {
919 xfs_growfs_rt_t in;
920
921 if (!capable(CAP_SYS_ADMIN))
922 return -EPERM;
923
924 if (copy_from_user(&in, arg, sizeof(in)))
925 return -XFS_ERROR(EFAULT);
926
927 error = xfs_growfs_rt(mp, &in);
928 return -error;
929 }
930
931 case XFS_IOC_FREEZE:
932 if (!capable(CAP_SYS_ADMIN))
933 return -EPERM;
934
935 if (inode->i_sb->s_frozen == SB_UNFROZEN)
936 freeze_bdev(inode->i_sb->s_bdev);
937 return 0;
938
939 case XFS_IOC_THAW:
940 if (!capable(CAP_SYS_ADMIN))
941 return -EPERM;
942 if (inode->i_sb->s_frozen != SB_UNFROZEN)
943 thaw_bdev(inode->i_sb->s_bdev, inode->i_sb);
944 return 0;
945
946 case XFS_IOC_GOINGDOWN: {
947 __uint32_t in;
948
949 if (!capable(CAP_SYS_ADMIN))
950 return -EPERM;
951
952 if (get_user(in, (__uint32_t __user *)arg))
953 return -XFS_ERROR(EFAULT);
954
955 error = xfs_fs_goingdown(mp, in);
956 return -error;
957 }
958
959 case XFS_IOC_ERROR_INJECTION: {
960 xfs_error_injection_t in;
961
962 if (!capable(CAP_SYS_ADMIN))
963 return -EPERM;
964
965 if (copy_from_user(&in, arg, sizeof(in)))
966 return -XFS_ERROR(EFAULT);
967
968 error = xfs_errortag_add(in.errtag, mp);
969 return -error;
970 }
971
972 case XFS_IOC_ERROR_CLEARALL:
973 if (!capable(CAP_SYS_ADMIN))
974 return -EPERM;
975
Christoph Hellwig0ce4cfd2007-08-30 17:20:53 +1000976 error = xfs_errortag_clearall(mp, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 return -error;
978
979 default:
980 return -ENOTTY;
981 }
982}
983
984STATIC int
985xfs_ioc_space(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000986 struct xfs_inode *ip,
Alexey Dobriyanf37ea142006-09-28 10:52:04 +1000987 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 struct file *filp,
989 int ioflags,
990 unsigned int cmd,
991 void __user *arg)
992{
993 xfs_flock64_t bf;
994 int attr_flags = 0;
995 int error;
996
Alexey Dobriyanf37ea142006-09-28 10:52:04 +1000997 if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 return -XFS_ERROR(EPERM);
999
Eric Sandeenad4a8ac2005-09-02 16:41:16 +10001000 if (!(filp->f_mode & FMODE_WRITE))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 return -XFS_ERROR(EBADF);
1002
Alexey Dobriyanf37ea142006-09-28 10:52:04 +10001003 if (!S_ISREG(inode->i_mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 return -XFS_ERROR(EINVAL);
1005
1006 if (copy_from_user(&bf, arg, sizeof(bf)))
1007 return -XFS_ERROR(EFAULT);
1008
1009 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1010 attr_flags |= ATTR_NONBLOCK;
1011 if (ioflags & IO_INVIS)
1012 attr_flags |= ATTR_DMI;
1013
Christoph Hellwig993386c2007-08-28 16:12:30 +10001014 error = xfs_change_file_space(ip, cmd, &bf, filp->f_pos,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 NULL, attr_flags);
1016 return -error;
1017}
1018
1019STATIC int
1020xfs_ioc_bulkstat(
1021 xfs_mount_t *mp,
1022 unsigned int cmd,
1023 void __user *arg)
1024{
1025 xfs_fsop_bulkreq_t bulkreq;
1026 int count; /* # of records returned */
1027 xfs_ino_t inlast; /* last inode number */
1028 int done;
1029 int error;
1030
1031 /* done = 1 if there are more stats to get and if bulkstat */
1032 /* should be called again (unused here, but used in dmapi) */
1033
1034 if (!capable(CAP_SYS_ADMIN))
1035 return -EPERM;
1036
1037 if (XFS_FORCED_SHUTDOWN(mp))
1038 return -XFS_ERROR(EIO);
1039
1040 if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
1041 return -XFS_ERROR(EFAULT);
1042
1043 if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
1044 return -XFS_ERROR(EFAULT);
1045
1046 if ((count = bulkreq.icount) <= 0)
1047 return -XFS_ERROR(EINVAL);
1048
Lachlan McIlroycd57e592007-11-23 16:30:32 +11001049 if (bulkreq.ubuffer == NULL)
1050 return -XFS_ERROR(EINVAL);
1051
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 if (cmd == XFS_IOC_FSINUMBERS)
1053 error = xfs_inumbers(mp, &inlast, &count,
Michal Marekfaa63e92007-07-11 11:10:19 +10001054 bulkreq.ubuffer, xfs_inumbers_fmt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
1056 error = xfs_bulkstat_single(mp, &inlast,
1057 bulkreq.ubuffer, &done);
Lachlan McIlroycd57e592007-11-23 16:30:32 +11001058 else /* XFS_IOC_FSBULKSTAT */
1059 error = xfs_bulkstat(mp, &inlast, &count,
1060 (bulkstat_one_pf)xfs_bulkstat_one, NULL,
1061 sizeof(xfs_bstat_t), bulkreq.ubuffer,
1062 BULKSTAT_FG_QUICK, &done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
1064 if (error)
1065 return -error;
1066
1067 if (bulkreq.ocount != NULL) {
1068 if (copy_to_user(bulkreq.lastip, &inlast,
1069 sizeof(xfs_ino_t)))
1070 return -XFS_ERROR(EFAULT);
1071
1072 if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
1073 return -XFS_ERROR(EFAULT);
1074 }
1075
1076 return 0;
1077}
1078
1079STATIC int
1080xfs_ioc_fsgeometry_v1(
1081 xfs_mount_t *mp,
1082 void __user *arg)
1083{
1084 xfs_fsop_geom_v1_t fsgeo;
1085 int error;
1086
1087 error = xfs_fs_geometry(mp, (xfs_fsop_geom_t *)&fsgeo, 3);
1088 if (error)
1089 return -error;
1090
1091 if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
1092 return -XFS_ERROR(EFAULT);
1093 return 0;
1094}
1095
1096STATIC int
1097xfs_ioc_fsgeometry(
1098 xfs_mount_t *mp,
1099 void __user *arg)
1100{
1101 xfs_fsop_geom_t fsgeo;
1102 int error;
1103
1104 error = xfs_fs_geometry(mp, &fsgeo, 4);
1105 if (error)
1106 return -error;
1107
1108 if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
1109 return -XFS_ERROR(EFAULT);
1110 return 0;
1111}
1112
1113/*
1114 * Linux extended inode flags interface.
1115 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
1117STATIC unsigned int
1118xfs_merge_ioc_xflags(
1119 unsigned int flags,
1120 unsigned int start)
1121{
1122 unsigned int xflags = start;
1123
Eric Sandeen39058a02007-02-10 18:37:10 +11001124 if (flags & FS_IMMUTABLE_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 xflags |= XFS_XFLAG_IMMUTABLE;
1126 else
1127 xflags &= ~XFS_XFLAG_IMMUTABLE;
Eric Sandeen39058a02007-02-10 18:37:10 +11001128 if (flags & FS_APPEND_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 xflags |= XFS_XFLAG_APPEND;
1130 else
1131 xflags &= ~XFS_XFLAG_APPEND;
Eric Sandeen39058a02007-02-10 18:37:10 +11001132 if (flags & FS_SYNC_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 xflags |= XFS_XFLAG_SYNC;
1134 else
1135 xflags &= ~XFS_XFLAG_SYNC;
Eric Sandeen39058a02007-02-10 18:37:10 +11001136 if (flags & FS_NOATIME_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 xflags |= XFS_XFLAG_NOATIME;
1138 else
1139 xflags &= ~XFS_XFLAG_NOATIME;
Eric Sandeen39058a02007-02-10 18:37:10 +11001140 if (flags & FS_NODUMP_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 xflags |= XFS_XFLAG_NODUMP;
1142 else
1143 xflags &= ~XFS_XFLAG_NODUMP;
1144
1145 return xflags;
1146}
1147
1148STATIC unsigned int
1149xfs_di2lxflags(
1150 __uint16_t di_flags)
1151{
1152 unsigned int flags = 0;
1153
1154 if (di_flags & XFS_DIFLAG_IMMUTABLE)
Eric Sandeen39058a02007-02-10 18:37:10 +11001155 flags |= FS_IMMUTABLE_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 if (di_flags & XFS_DIFLAG_APPEND)
Eric Sandeen39058a02007-02-10 18:37:10 +11001157 flags |= FS_APPEND_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 if (di_flags & XFS_DIFLAG_SYNC)
Eric Sandeen39058a02007-02-10 18:37:10 +11001159 flags |= FS_SYNC_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 if (di_flags & XFS_DIFLAG_NOATIME)
Eric Sandeen39058a02007-02-10 18:37:10 +11001161 flags |= FS_NOATIME_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (di_flags & XFS_DIFLAG_NODUMP)
Eric Sandeen39058a02007-02-10 18:37:10 +11001163 flags |= FS_NODUMP_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 return flags;
1165}
1166
1167STATIC int
Christoph Hellwigc83bfab2007-10-11 17:47:00 +10001168xfs_ioc_fsgetxattr(
1169 xfs_inode_t *ip,
1170 int attr,
1171 void __user *arg)
1172{
1173 struct fsxattr fa;
1174
1175 xfs_ilock(ip, XFS_ILOCK_SHARED);
1176 fa.fsx_xflags = xfs_ip2xflags(ip);
1177 fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
1178 fa.fsx_projid = ip->i_d.di_projid;
1179
1180 if (attr) {
1181 if (ip->i_afp) {
1182 if (ip->i_afp->if_flags & XFS_IFEXTENTS)
1183 fa.fsx_nextents = ip->i_afp->if_bytes /
1184 sizeof(xfs_bmbt_rec_t);
1185 else
1186 fa.fsx_nextents = ip->i_d.di_anextents;
1187 } else
1188 fa.fsx_nextents = 0;
1189 } else {
1190 if (ip->i_df.if_flags & XFS_IFEXTENTS)
1191 fa.fsx_nextents = ip->i_df.if_bytes /
1192 sizeof(xfs_bmbt_rec_t);
1193 else
1194 fa.fsx_nextents = ip->i_d.di_nextents;
1195 }
1196 xfs_iunlock(ip, XFS_ILOCK_SHARED);
1197
1198 if (copy_to_user(arg, &fa, sizeof(fa)))
1199 return -EFAULT;
1200 return 0;
1201}
1202
1203STATIC int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204xfs_ioc_xattr(
Nathan Scott67fcaa72006-06-09 17:00:52 +10001205 bhv_vnode_t *vp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 xfs_inode_t *ip,
1207 struct file *filp,
1208 unsigned int cmd,
1209 void __user *arg)
1210{
1211 struct fsxattr fa;
Nathan Scott8285fb52006-06-09 17:07:12 +10001212 struct bhv_vattr *vattr;
Nathan Scott220b5282006-03-14 13:33:36 +11001213 int error = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 int attr_flags;
1215 unsigned int flags;
1216
Nathan Scott220b5282006-03-14 13:33:36 +11001217 vattr = kmalloc(sizeof(*vattr), GFP_KERNEL);
1218 if (unlikely(!vattr))
1219 return -ENOMEM;
1220
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 case XFS_IOC_FSSETXATTR: {
Nathan Scott220b5282006-03-14 13:33:36 +11001223 if (copy_from_user(&fa, arg, sizeof(fa))) {
1224 error = -EFAULT;
1225 break;
1226 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
1228 attr_flags = 0;
1229 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1230 attr_flags |= ATTR_NONBLOCK;
1231
Nathan Scott220b5282006-03-14 13:33:36 +11001232 vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | XFS_AT_PROJID;
1233 vattr->va_xflags = fa.fsx_xflags;
1234 vattr->va_extsize = fa.fsx_extsize;
1235 vattr->va_projid = fa.fsx_projid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
Christoph Hellwig739bfb22007-08-29 10:58:01 +10001237 error = xfs_setattr(ip, vattr, attr_flags, NULL);
Nathan Scott220b5282006-03-14 13:33:36 +11001238 if (likely(!error))
1239 __vn_revalidate(vp, vattr); /* update flags */
1240 error = -error;
1241 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 }
1243
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 case XFS_IOC_GETXFLAGS: {
1245 flags = xfs_di2lxflags(ip->i_d.di_flags);
1246 if (copy_to_user(arg, &flags, sizeof(flags)))
Nathan Scott220b5282006-03-14 13:33:36 +11001247 error = -EFAULT;
1248 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 }
1250
1251 case XFS_IOC_SETXFLAGS: {
Nathan Scott220b5282006-03-14 13:33:36 +11001252 if (copy_from_user(&flags, arg, sizeof(flags))) {
1253 error = -EFAULT;
1254 break;
1255 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
Eric Sandeen39058a02007-02-10 18:37:10 +11001257 if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
1258 FS_NOATIME_FL | FS_NODUMP_FL | \
1259 FS_SYNC_FL)) {
Nathan Scott220b5282006-03-14 13:33:36 +11001260 error = -EOPNOTSUPP;
1261 break;
1262 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 attr_flags = 0;
1265 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1266 attr_flags |= ATTR_NONBLOCK;
1267
Nathan Scott220b5282006-03-14 13:33:36 +11001268 vattr->va_mask = XFS_AT_XFLAGS;
1269 vattr->va_xflags = xfs_merge_ioc_xflags(flags,
1270 xfs_ip2xflags(ip));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
Christoph Hellwig739bfb22007-08-29 10:58:01 +10001272 error = xfs_setattr(ip, vattr, attr_flags, NULL);
Nathan Scott220b5282006-03-14 13:33:36 +11001273 if (likely(!error))
1274 __vn_revalidate(vp, vattr); /* update flags */
1275 error = -error;
1276 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 }
1278
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 default:
Nathan Scott220b5282006-03-14 13:33:36 +11001280 error = -ENOTTY;
1281 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 }
Nathan Scott220b5282006-03-14 13:33:36 +11001283
1284 kfree(vattr);
1285 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286}
1287
1288STATIC int
1289xfs_ioc_getbmap(
Christoph Hellwig993386c2007-08-28 16:12:30 +10001290 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 int ioflags,
1292 unsigned int cmd,
1293 void __user *arg)
1294{
1295 struct getbmap bm;
1296 int iflags;
1297 int error;
1298
1299 if (copy_from_user(&bm, arg, sizeof(bm)))
1300 return -XFS_ERROR(EFAULT);
1301
1302 if (bm.bmv_count < 2)
1303 return -XFS_ERROR(EINVAL);
1304
1305 iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
1306 if (ioflags & IO_INVIS)
1307 iflags |= BMV_IF_NO_DMAPI_READ;
1308
Christoph Hellwig993386c2007-08-28 16:12:30 +10001309 error = xfs_getbmap(ip, &bm, (struct getbmap __user *)arg+1, iflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 if (error)
1311 return -error;
1312
1313 if (copy_to_user(arg, &bm, sizeof(bm)))
1314 return -XFS_ERROR(EFAULT);
1315 return 0;
1316}
1317
1318STATIC int
1319xfs_ioc_getbmapx(
Christoph Hellwig993386c2007-08-28 16:12:30 +10001320 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 void __user *arg)
1322{
1323 struct getbmapx bmx;
1324 struct getbmap bm;
1325 int iflags;
1326 int error;
1327
1328 if (copy_from_user(&bmx, arg, sizeof(bmx)))
1329 return -XFS_ERROR(EFAULT);
1330
1331 if (bmx.bmv_count < 2)
1332 return -XFS_ERROR(EINVAL);
1333
1334 /*
1335 * Map input getbmapx structure to a getbmap
1336 * structure for xfs_getbmap.
1337 */
1338 GETBMAP_CONVERT(bmx, bm);
1339
1340 iflags = bmx.bmv_iflags;
1341
1342 if (iflags & (~BMV_IF_VALID))
1343 return -XFS_ERROR(EINVAL);
1344
1345 iflags |= BMV_IF_EXTENDED;
1346
Christoph Hellwig993386c2007-08-28 16:12:30 +10001347 error = xfs_getbmap(ip, &bm, (struct getbmapx __user *)arg+1, iflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 if (error)
1349 return -error;
1350
1351 GETBMAP_CONVERT(bm, bmx);
1352
1353 if (copy_to_user(arg, &bmx, sizeof(bmx)))
1354 return -XFS_ERROR(EFAULT);
1355
1356 return 0;
1357}