blob: 9bba9f8be1eb886e4d9f335a4927e8118724cf4b [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 */
141 memcpy(&handle.ha_fsid, vp->v_vfsp->vfs_altfsid, sizeof(xfs_fsid_t));
142 hsize = sizeof(xfs_fsid_t);
143
144 if (cmd != XFS_IOC_PATH_TO_FSHANDLE) {
145 xfs_inode_t *ip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 int lock_mode;
147
148 /* need to get access to the xfs_inode to read the generation */
Christoph Hellwig75e17b32006-01-11 20:58:44 +1100149 ip = xfs_vtoi(vp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 ASSERT(ip);
151 lock_mode = xfs_ilock_map_shared(ip);
152
153 /* fill in fid section of handle from inode */
154 handle.ha_fid.xfs_fid_len = sizeof(xfs_fid_t) -
155 sizeof(handle.ha_fid.xfs_fid_len);
156 handle.ha_fid.xfs_fid_pad = 0;
157 handle.ha_fid.xfs_fid_gen = ip->i_d.di_gen;
158 handle.ha_fid.xfs_fid_ino = ip->i_ino;
159
160 xfs_iunlock_map_shared(ip, lock_mode);
161
162 hsize = XFS_HSIZE(handle);
163 }
164
165 /* now copy our handle into the user buffer & write out the size */
166 if (copy_to_user(hreq.ohandle, &handle, hsize) ||
167 copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) {
168 iput(inode);
169 return -XFS_ERROR(EFAULT);
170 }
171
172 iput(inode);
173 return 0;
174}
175
176
177/*
178 * Convert userspace handle data into vnode (and inode).
179 * We [ab]use the fact that all the fsop_handlereq ioctl calls
180 * have a data structure argument whose first component is always
181 * a xfs_fsop_handlereq_t, so we can cast to and from this type.
182 * This allows us to optimise the copy_from_user calls and gives
183 * a handy, shared routine.
184 *
185 * If no error, caller must always VN_RELE the returned vp.
186 */
187STATIC int
188xfs_vget_fsop_handlereq(
189 xfs_mount_t *mp,
190 struct inode *parinode, /* parent inode pointer */
191 xfs_fsop_handlereq_t *hreq,
Nathan Scott67fcaa72006-06-09 17:00:52 +1000192 bhv_vnode_t **vp,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 struct inode **inode)
194{
195 void __user *hanp;
196 size_t hlen;
197 xfs_fid_t *xfid;
198 xfs_handle_t *handlep;
199 xfs_handle_t handle;
200 xfs_inode_t *ip;
201 struct inode *inodep;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000202 bhv_vnode_t *vpp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 xfs_ino_t ino;
204 __u32 igen;
205 int error;
206
207 /*
208 * Only allow handle opens under a directory.
209 */
210 if (!S_ISDIR(parinode->i_mode))
211 return XFS_ERROR(ENOTDIR);
212
213 hanp = hreq->ihandle;
214 hlen = hreq->ihandlen;
215 handlep = &handle;
216
217 if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
218 return XFS_ERROR(EINVAL);
219 if (copy_from_user(handlep, hanp, hlen))
220 return XFS_ERROR(EFAULT);
221 if (hlen < sizeof(*handlep))
222 memset(((char *)handlep) + hlen, 0, sizeof(*handlep) - hlen);
223 if (hlen > sizeof(handlep->ha_fsid)) {
224 if (handlep->ha_fid.xfs_fid_len !=
225 (hlen - sizeof(handlep->ha_fsid)
226 - sizeof(handlep->ha_fid.xfs_fid_len))
227 || handlep->ha_fid.xfs_fid_pad)
228 return XFS_ERROR(EINVAL);
229 }
230
231 /*
232 * Crack the handle, obtain the inode # & generation #
233 */
234 xfid = (struct xfs_fid *)&handlep->ha_fid;
235 if (xfid->xfs_fid_len == sizeof(*xfid) - sizeof(xfid->xfs_fid_len)) {
236 ino = xfid->xfs_fid_ino;
237 igen = xfid->xfs_fid_gen;
238 } else {
239 return XFS_ERROR(EINVAL);
240 }
241
242 /*
243 * Get the XFS inode, building a vnode to go with it.
244 */
245 error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0);
246 if (error)
247 return error;
248 if (ip == NULL)
249 return XFS_ERROR(EIO);
250 if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
251 xfs_iput_new(ip, XFS_ILOCK_SHARED);
252 return XFS_ERROR(ENOENT);
253 }
254
255 vpp = XFS_ITOV(ip);
Nathan Scottec86dc02006-03-17 17:25:36 +1100256 inodep = vn_to_inode(vpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 xfs_iunlock(ip, XFS_ILOCK_SHARED);
258
259 *vp = vpp;
260 *inode = inodep;
261 return 0;
262}
263
264STATIC int
265xfs_open_by_handle(
266 xfs_mount_t *mp,
267 void __user *arg,
268 struct file *parfilp,
269 struct inode *parinode)
270{
271 int error;
272 int new_fd;
273 int permflag;
274 struct file *filp;
275 struct inode *inode;
276 struct dentry *dentry;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000277 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 xfs_fsop_handlereq_t hreq;
279
280 if (!capable(CAP_SYS_ADMIN))
281 return -XFS_ERROR(EPERM);
282 if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
283 return -XFS_ERROR(EFAULT);
284
285 error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &vp, &inode);
286 if (error)
287 return -error;
288
289 /* Restrict xfs_open_by_handle to directories & regular files. */
290 if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
291 iput(inode);
292 return -XFS_ERROR(EINVAL);
293 }
294
295#if BITS_PER_LONG != 32
296 hreq.oflags |= O_LARGEFILE;
297#endif
298 /* Put open permission in namei format. */
299 permflag = hreq.oflags;
300 if ((permflag+1) & O_ACCMODE)
301 permflag++;
302 if (permflag & O_TRUNC)
303 permflag |= 2;
304
305 if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
306 (permflag & FMODE_WRITE) && IS_APPEND(inode)) {
307 iput(inode);
308 return -XFS_ERROR(EPERM);
309 }
310
311 if ((permflag & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
312 iput(inode);
313 return -XFS_ERROR(EACCES);
314 }
315
316 /* Can't write directories. */
317 if ( S_ISDIR(inode->i_mode) && (permflag & FMODE_WRITE)) {
318 iput(inode);
319 return -XFS_ERROR(EISDIR);
320 }
321
322 if ((new_fd = get_unused_fd()) < 0) {
323 iput(inode);
324 return new_fd;
325 }
326
327 dentry = d_alloc_anon(inode);
328 if (dentry == NULL) {
329 iput(inode);
330 put_unused_fd(new_fd);
331 return -XFS_ERROR(ENOMEM);
332 }
333
334 /* Ensure umount returns EBUSY on umounts while this file is open. */
Josef "Jeff" Sipeke678fb02006-12-08 02:36:49 -0800335 mntget(parfilp->f_path.mnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337 /* Create file pointer. */
Josef "Jeff" Sipeke678fb02006-12-08 02:36:49 -0800338 filp = dentry_open(dentry, parfilp->f_path.mnt, hreq.oflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 if (IS_ERR(filp)) {
340 put_unused_fd(new_fd);
341 return -XFS_ERROR(-PTR_ERR(filp));
342 }
Vlad Apostolov2e2e7bb2006-11-11 18:04:47 +1100343 if (inode->i_mode & S_IFREG) {
344 /* invisible operation should not change atime */
345 filp->f_flags |= O_NOATIME;
Nathan Scott3562fd42006-03-14 14:00:35 +1100346 filp->f_op = &xfs_invis_file_operations;
Vlad Apostolov2e2e7bb2006-11-11 18:04:47 +1100347 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349 fd_install(new_fd, filp);
350 return new_fd;
351}
352
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000353/*
354 * This is a copy from fs/namei.c:vfs_readlink(), except for removing it's
355 * unused first argument.
356 */
357STATIC int
358do_readlink(
359 char __user *buffer,
360 int buflen,
361 const char *link)
362{
363 int len;
364
365 len = PTR_ERR(link);
366 if (IS_ERR(link))
367 goto out;
368
369 len = strlen(link);
370 if (len > (unsigned) buflen)
371 len = buflen;
372 if (copy_to_user(buffer, link, len))
373 len = -EFAULT;
374 out:
375 return len;
376}
377
378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379STATIC int
380xfs_readlink_by_handle(
381 xfs_mount_t *mp,
382 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 struct inode *parinode)
384{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 struct inode *inode;
386 xfs_fsop_handlereq_t hreq;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000387 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 __u32 olen;
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000389 void *link;
390 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392 if (!capable(CAP_SYS_ADMIN))
393 return -XFS_ERROR(EPERM);
394 if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
395 return -XFS_ERROR(EFAULT);
396
397 error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &vp, &inode);
398 if (error)
399 return -error;
400
401 /* Restrict this handle operation to symlinks only. */
Christoph Hellwig0432dab2005-09-02 16:46:51 +1000402 if (!S_ISLNK(inode->i_mode)) {
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000403 error = -XFS_ERROR(EINVAL);
404 goto out_iput;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 }
406
407 if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) {
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000408 error = -XFS_ERROR(EFAULT);
409 goto out_iput;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000412 link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
413 if (!link)
414 goto out_iput;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000416 error = -xfs_readlink(XFS_I(inode), link);
Nathan Scott67fcaa72006-06-09 17:00:52 +1000417 if (error)
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000418 goto out_kfree;
419 error = do_readlink(hreq.ohandle, olen, link);
420 if (error)
421 goto out_kfree;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000422
Christoph Hellwig804c83c2007-08-28 13:59:03 +1000423 out_kfree:
424 kfree(link);
425 out_iput:
426 iput(inode);
427 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428}
429
430STATIC int
431xfs_fssetdm_by_handle(
432 xfs_mount_t *mp,
433 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 struct inode *parinode)
435{
436 int error;
437 struct fsdmidata fsd;
438 xfs_fsop_setdm_handlereq_t dmhreq;
439 struct inode *inode;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000440 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 if (!capable(CAP_MKNOD))
443 return -XFS_ERROR(EPERM);
444 if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
445 return -XFS_ERROR(EFAULT);
446
447 error = xfs_vget_fsop_handlereq(mp, parinode, &dmhreq.hreq, &vp, &inode);
448 if (error)
449 return -error;
450
451 if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
452 VN_RELE(vp);
453 return -XFS_ERROR(EPERM);
454 }
455
456 if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
457 VN_RELE(vp);
458 return -XFS_ERROR(EFAULT);
459 }
460
Christoph Hellwig993386c2007-08-28 16:12:30 +1000461 error = xfs_set_dmattrs(xfs_vtoi(vp),
462 fsd.fsd_dmevmask, fsd.fsd_dmstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
464 VN_RELE(vp);
465 if (error)
466 return -error;
467 return 0;
468}
469
470STATIC int
471xfs_attrlist_by_handle(
472 xfs_mount_t *mp,
473 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 struct inode *parinode)
475{
476 int error;
477 attrlist_cursor_kern_t *cursor;
478 xfs_fsop_attrlist_handlereq_t al_hreq;
479 struct inode *inode;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000480 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 char *kbuf;
482
483 if (!capable(CAP_SYS_ADMIN))
484 return -XFS_ERROR(EPERM);
485 if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
486 return -XFS_ERROR(EFAULT);
487 if (al_hreq.buflen > XATTR_LIST_MAX)
488 return -XFS_ERROR(EINVAL);
489
490 error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq,
491 &vp, &inode);
492 if (error)
493 goto out;
494
495 kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL);
496 if (!kbuf)
497 goto out_vn_rele;
498
499 cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000500 error = xfs_attr_list(XFS_I(inode), kbuf, al_hreq.buflen,
501 al_hreq.flags, cursor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 if (error)
503 goto out_kfree;
504
505 if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
506 error = -EFAULT;
507
508 out_kfree:
509 kfree(kbuf);
510 out_vn_rele:
511 VN_RELE(vp);
512 out:
513 return -error;
514}
515
516STATIC int
517xfs_attrmulti_attr_get(
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000518 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 char *name,
520 char __user *ubuf,
521 __uint32_t *len,
522 __uint32_t flags)
523{
524 char *kbuf;
525 int error = EFAULT;
526
527 if (*len > XATTR_SIZE_MAX)
528 return EINVAL;
529 kbuf = kmalloc(*len, GFP_KERNEL);
530 if (!kbuf)
531 return ENOMEM;
532
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000533 error = xfs_attr_get(XFS_I(inode), name, kbuf, len, flags, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 if (error)
535 goto out_kfree;
536
537 if (copy_to_user(ubuf, kbuf, *len))
538 error = EFAULT;
539
540 out_kfree:
541 kfree(kbuf);
542 return error;
543}
544
545STATIC int
546xfs_attrmulti_attr_set(
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000547 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 char *name,
549 const char __user *ubuf,
550 __uint32_t len,
551 __uint32_t flags)
552{
553 char *kbuf;
554 int error = EFAULT;
555
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000556 if (IS_RDONLY(inode))
Christoph Hellwig3542c6e2006-01-09 20:52:00 -0800557 return -EROFS;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000558 if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 return EPERM;
560 if (len > XATTR_SIZE_MAX)
561 return EINVAL;
562
563 kbuf = kmalloc(len, GFP_KERNEL);
564 if (!kbuf)
565 return ENOMEM;
566
567 if (copy_from_user(kbuf, ubuf, len))
568 goto out_kfree;
569
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000570 error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
572 out_kfree:
573 kfree(kbuf);
574 return error;
575}
576
577STATIC int
578xfs_attrmulti_attr_remove(
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000579 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 char *name,
581 __uint32_t flags)
582{
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000583 if (IS_RDONLY(inode))
Christoph Hellwig3542c6e2006-01-09 20:52:00 -0800584 return -EROFS;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000585 if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 return EPERM;
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000587 return xfs_attr_remove(XFS_I(inode), name, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588}
589
590STATIC int
591xfs_attrmulti_by_handle(
592 xfs_mount_t *mp,
593 void __user *arg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 struct inode *parinode)
595{
596 int error;
597 xfs_attr_multiop_t *ops;
598 xfs_fsop_attrmulti_handlereq_t am_hreq;
599 struct inode *inode;
Nathan Scott67fcaa72006-06-09 17:00:52 +1000600 bhv_vnode_t *vp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 unsigned int i, size;
602 char *attr_name;
603
604 if (!capable(CAP_SYS_ADMIN))
605 return -XFS_ERROR(EPERM);
606 if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
607 return -XFS_ERROR(EFAULT);
608
609 error = xfs_vget_fsop_handlereq(mp, parinode, &am_hreq.hreq, &vp, &inode);
610 if (error)
611 goto out;
612
613 error = E2BIG;
614 size = am_hreq.opcount * sizeof(attr_multiop_t);
615 if (!size || size > 16 * PAGE_SIZE)
616 goto out_vn_rele;
617
618 error = ENOMEM;
619 ops = kmalloc(size, GFP_KERNEL);
620 if (!ops)
621 goto out_vn_rele;
622
623 error = EFAULT;
624 if (copy_from_user(ops, am_hreq.ops, size))
625 goto out_kfree_ops;
626
627 attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
628 if (!attr_name)
629 goto out_kfree_ops;
630
631
632 error = 0;
633 for (i = 0; i < am_hreq.opcount; i++) {
634 ops[i].am_error = strncpy_from_user(attr_name,
635 ops[i].am_attrname, MAXNAMELEN);
636 if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
637 error = -ERANGE;
638 if (ops[i].am_error < 0)
639 break;
640
641 switch (ops[i].am_opcode) {
642 case ATTR_OP_GET:
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000643 ops[i].am_error = xfs_attrmulti_attr_get(inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 attr_name, ops[i].am_attrvalue,
645 &ops[i].am_length, ops[i].am_flags);
646 break;
647 case ATTR_OP_SET:
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000648 ops[i].am_error = xfs_attrmulti_attr_set(inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 attr_name, ops[i].am_attrvalue,
650 ops[i].am_length, ops[i].am_flags);
651 break;
652 case ATTR_OP_REMOVE:
Christoph Hellwig739bfb22007-08-29 10:58:01 +1000653 ops[i].am_error = xfs_attrmulti_attr_remove(inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 attr_name, ops[i].am_flags);
655 break;
656 default:
657 ops[i].am_error = EINVAL;
658 }
659 }
660
661 if (copy_to_user(am_hreq.ops, ops, size))
662 error = XFS_ERROR(EFAULT);
663
664 kfree(attr_name);
665 out_kfree_ops:
666 kfree(ops);
667 out_vn_rele:
668 VN_RELE(vp);
669 out:
670 return -error;
671}
672
673/* prototypes for a few of the stack-hungry cases that have
674 * their own functions. Functions are defined after their use
675 * so gcc doesn't get fancy and inline them with -03 */
676
677STATIC int
678xfs_ioc_space(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000679 struct xfs_inode *ip,
Alexey Dobriyanf37ea142006-09-28 10:52:04 +1000680 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 struct file *filp,
682 int flags,
683 unsigned int cmd,
684 void __user *arg);
685
686STATIC int
687xfs_ioc_bulkstat(
688 xfs_mount_t *mp,
689 unsigned int cmd,
690 void __user *arg);
691
692STATIC int
693xfs_ioc_fsgeometry_v1(
694 xfs_mount_t *mp,
695 void __user *arg);
696
697STATIC int
698xfs_ioc_fsgeometry(
699 xfs_mount_t *mp,
700 void __user *arg);
701
702STATIC int
703xfs_ioc_xattr(
Nathan Scott67fcaa72006-06-09 17:00:52 +1000704 bhv_vnode_t *vp,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 xfs_inode_t *ip,
706 struct file *filp,
707 unsigned int cmd,
708 void __user *arg);
709
710STATIC int
711xfs_ioc_getbmap(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000712 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 int flags,
714 unsigned int cmd,
715 void __user *arg);
716
717STATIC int
718xfs_ioc_getbmapx(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000719 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 void __user *arg);
721
722int
723xfs_ioctl(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000724 xfs_inode_t *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 struct file *filp,
726 int ioflags,
727 unsigned int cmd,
728 void __user *arg)
729{
Christoph Hellwig993386c2007-08-28 16:12:30 +1000730 struct inode *inode = filp->f_path.dentry->d_inode;
731 bhv_vnode_t *vp = vn_from_inode(inode);
732 xfs_mount_t *mp = ip->i_mount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
735 vn_trace_entry(vp, "xfs_ioctl", (inst_t *)__return_address);
736
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 switch (cmd) {
738
739 case XFS_IOC_ALLOCSP:
740 case XFS_IOC_FREESP:
741 case XFS_IOC_RESVSP:
742 case XFS_IOC_UNRESVSP:
743 case XFS_IOC_ALLOCSP64:
744 case XFS_IOC_FREESP64:
745 case XFS_IOC_RESVSP64:
746 case XFS_IOC_UNRESVSP64:
747 /*
748 * Only allow the sys admin to reserve space unless
749 * unwritten extents are enabled.
750 */
751 if (!XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) &&
752 !capable(CAP_SYS_ADMIN))
753 return -EPERM;
754
Christoph Hellwig993386c2007-08-28 16:12:30 +1000755 return xfs_ioc_space(ip, inode, filp, ioflags, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757 case XFS_IOC_DIOINFO: {
758 struct dioattr da;
759 xfs_buftarg_t *target =
760 (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) ?
761 mp->m_rtdev_targp : mp->m_ddev_targp;
762
Nathan Scottce8e9222006-01-11 15:39:08 +1100763 da.d_mem = da.d_miniosz = 1 << target->bt_sshift;
Nathan Scott0d148242006-01-11 15:33:51 +1100764 da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 if (copy_to_user(arg, &da, sizeof(da)))
767 return -XFS_ERROR(EFAULT);
768 return 0;
769 }
770
771 case XFS_IOC_FSBULKSTAT_SINGLE:
772 case XFS_IOC_FSBULKSTAT:
773 case XFS_IOC_FSINUMBERS:
774 return xfs_ioc_bulkstat(mp, cmd, arg);
775
776 case XFS_IOC_FSGEOMETRY_V1:
777 return xfs_ioc_fsgeometry_v1(mp, arg);
778
779 case XFS_IOC_FSGEOMETRY:
780 return xfs_ioc_fsgeometry(mp, arg);
781
782 case XFS_IOC_GETVERSION:
Alexey Dobriyan87395de2006-09-28 10:56:01 +1000783 return put_user(inode->i_generation, (int __user *)arg);
784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 case XFS_IOC_GETXFLAGS:
786 case XFS_IOC_SETXFLAGS:
787 case XFS_IOC_FSGETXATTR:
788 case XFS_IOC_FSSETXATTR:
789 case XFS_IOC_FSGETXATTRA:
790 return xfs_ioc_xattr(vp, ip, filp, cmd, arg);
791
792 case XFS_IOC_FSSETDM: {
793 struct fsdmidata dmi;
794
795 if (copy_from_user(&dmi, arg, sizeof(dmi)))
796 return -XFS_ERROR(EFAULT);
797
Christoph Hellwig993386c2007-08-28 16:12:30 +1000798 error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
799 dmi.fsd_dmstate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 return -error;
801 }
802
803 case XFS_IOC_GETBMAP:
804 case XFS_IOC_GETBMAPA:
Christoph Hellwig993386c2007-08-28 16:12:30 +1000805 return xfs_ioc_getbmap(ip, ioflags, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
807 case XFS_IOC_GETBMAPX:
Christoph Hellwig993386c2007-08-28 16:12:30 +1000808 return xfs_ioc_getbmapx(ip, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
810 case XFS_IOC_FD_TO_HANDLE:
811 case XFS_IOC_PATH_TO_HANDLE:
812 case XFS_IOC_PATH_TO_FSHANDLE:
813 return xfs_find_handle(cmd, arg);
814
815 case XFS_IOC_OPEN_BY_HANDLE:
816 return xfs_open_by_handle(mp, arg, filp, inode);
817
818 case XFS_IOC_FSSETDM_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100819 return xfs_fssetdm_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
821 case XFS_IOC_READLINK_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100822 return xfs_readlink_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
824 case XFS_IOC_ATTRLIST_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100825 return xfs_attrlist_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
827 case XFS_IOC_ATTRMULTI_BY_HANDLE:
Lachlan McIlroy51806022007-02-10 18:35:46 +1100828 return xfs_attrmulti_by_handle(mp, arg, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
830 case XFS_IOC_SWAPEXT: {
831 error = xfs_swapext((struct xfs_swapext __user *)arg);
832 return -error;
833 }
834
835 case XFS_IOC_FSCOUNTS: {
836 xfs_fsop_counts_t out;
837
838 error = xfs_fs_counts(mp, &out);
839 if (error)
840 return -error;
841
842 if (copy_to_user(arg, &out, sizeof(out)))
843 return -XFS_ERROR(EFAULT);
844 return 0;
845 }
846
847 case XFS_IOC_SET_RESBLKS: {
848 xfs_fsop_resblks_t inout;
849 __uint64_t in;
850
851 if (!capable(CAP_SYS_ADMIN))
852 return -EPERM;
853
854 if (copy_from_user(&inout, arg, sizeof(inout)))
855 return -XFS_ERROR(EFAULT);
856
857 /* input parameter is passed in resblks field of structure */
858 in = inout.resblks;
859 error = xfs_reserve_blocks(mp, &in, &inout);
860 if (error)
861 return -error;
862
863 if (copy_to_user(arg, &inout, sizeof(inout)))
864 return -XFS_ERROR(EFAULT);
865 return 0;
866 }
867
868 case XFS_IOC_GET_RESBLKS: {
869 xfs_fsop_resblks_t out;
870
871 if (!capable(CAP_SYS_ADMIN))
872 return -EPERM;
873
874 error = xfs_reserve_blocks(mp, NULL, &out);
875 if (error)
876 return -error;
877
878 if (copy_to_user(arg, &out, sizeof(out)))
879 return -XFS_ERROR(EFAULT);
880
881 return 0;
882 }
883
884 case XFS_IOC_FSGROWFSDATA: {
885 xfs_growfs_data_t in;
886
887 if (!capable(CAP_SYS_ADMIN))
888 return -EPERM;
889
890 if (copy_from_user(&in, arg, sizeof(in)))
891 return -XFS_ERROR(EFAULT);
892
893 error = xfs_growfs_data(mp, &in);
894 return -error;
895 }
896
897 case XFS_IOC_FSGROWFSLOG: {
898 xfs_growfs_log_t in;
899
900 if (!capable(CAP_SYS_ADMIN))
901 return -EPERM;
902
903 if (copy_from_user(&in, arg, sizeof(in)))
904 return -XFS_ERROR(EFAULT);
905
906 error = xfs_growfs_log(mp, &in);
907 return -error;
908 }
909
910 case XFS_IOC_FSGROWFSRT: {
911 xfs_growfs_rt_t in;
912
913 if (!capable(CAP_SYS_ADMIN))
914 return -EPERM;
915
916 if (copy_from_user(&in, arg, sizeof(in)))
917 return -XFS_ERROR(EFAULT);
918
919 error = xfs_growfs_rt(mp, &in);
920 return -error;
921 }
922
923 case XFS_IOC_FREEZE:
924 if (!capable(CAP_SYS_ADMIN))
925 return -EPERM;
926
927 if (inode->i_sb->s_frozen == SB_UNFROZEN)
928 freeze_bdev(inode->i_sb->s_bdev);
929 return 0;
930
931 case XFS_IOC_THAW:
932 if (!capable(CAP_SYS_ADMIN))
933 return -EPERM;
934 if (inode->i_sb->s_frozen != SB_UNFROZEN)
935 thaw_bdev(inode->i_sb->s_bdev, inode->i_sb);
936 return 0;
937
938 case XFS_IOC_GOINGDOWN: {
939 __uint32_t in;
940
941 if (!capable(CAP_SYS_ADMIN))
942 return -EPERM;
943
944 if (get_user(in, (__uint32_t __user *)arg))
945 return -XFS_ERROR(EFAULT);
946
947 error = xfs_fs_goingdown(mp, in);
948 return -error;
949 }
950
951 case XFS_IOC_ERROR_INJECTION: {
952 xfs_error_injection_t in;
953
954 if (!capable(CAP_SYS_ADMIN))
955 return -EPERM;
956
957 if (copy_from_user(&in, arg, sizeof(in)))
958 return -XFS_ERROR(EFAULT);
959
960 error = xfs_errortag_add(in.errtag, mp);
961 return -error;
962 }
963
964 case XFS_IOC_ERROR_CLEARALL:
965 if (!capable(CAP_SYS_ADMIN))
966 return -EPERM;
967
968 error = xfs_errortag_clearall(mp);
969 return -error;
970
971 default:
972 return -ENOTTY;
973 }
974}
975
976STATIC int
977xfs_ioc_space(
Christoph Hellwig993386c2007-08-28 16:12:30 +1000978 struct xfs_inode *ip,
Alexey Dobriyanf37ea142006-09-28 10:52:04 +1000979 struct inode *inode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 struct file *filp,
981 int ioflags,
982 unsigned int cmd,
983 void __user *arg)
984{
985 xfs_flock64_t bf;
986 int attr_flags = 0;
987 int error;
988
Alexey Dobriyanf37ea142006-09-28 10:52:04 +1000989 if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 return -XFS_ERROR(EPERM);
991
Eric Sandeenad4a8ac2005-09-02 16:41:16 +1000992 if (!(filp->f_mode & FMODE_WRITE))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 return -XFS_ERROR(EBADF);
994
Alexey Dobriyanf37ea142006-09-28 10:52:04 +1000995 if (!S_ISREG(inode->i_mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 return -XFS_ERROR(EINVAL);
997
998 if (copy_from_user(&bf, arg, sizeof(bf)))
999 return -XFS_ERROR(EFAULT);
1000
1001 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1002 attr_flags |= ATTR_NONBLOCK;
1003 if (ioflags & IO_INVIS)
1004 attr_flags |= ATTR_DMI;
1005
Christoph Hellwig993386c2007-08-28 16:12:30 +10001006 error = xfs_change_file_space(ip, cmd, &bf, filp->f_pos,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 NULL, attr_flags);
1008 return -error;
1009}
1010
1011STATIC int
1012xfs_ioc_bulkstat(
1013 xfs_mount_t *mp,
1014 unsigned int cmd,
1015 void __user *arg)
1016{
1017 xfs_fsop_bulkreq_t bulkreq;
1018 int count; /* # of records returned */
1019 xfs_ino_t inlast; /* last inode number */
1020 int done;
1021 int error;
1022
1023 /* done = 1 if there are more stats to get and if bulkstat */
1024 /* should be called again (unused here, but used in dmapi) */
1025
1026 if (!capable(CAP_SYS_ADMIN))
1027 return -EPERM;
1028
1029 if (XFS_FORCED_SHUTDOWN(mp))
1030 return -XFS_ERROR(EIO);
1031
1032 if (copy_from_user(&bulkreq, arg, sizeof(xfs_fsop_bulkreq_t)))
1033 return -XFS_ERROR(EFAULT);
1034
1035 if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
1036 return -XFS_ERROR(EFAULT);
1037
1038 if ((count = bulkreq.icount) <= 0)
1039 return -XFS_ERROR(EINVAL);
1040
1041 if (cmd == XFS_IOC_FSINUMBERS)
1042 error = xfs_inumbers(mp, &inlast, &count,
Michal Marekfaa63e92007-07-11 11:10:19 +10001043 bulkreq.ubuffer, xfs_inumbers_fmt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
1045 error = xfs_bulkstat_single(mp, &inlast,
1046 bulkreq.ubuffer, &done);
1047 else { /* XFS_IOC_FSBULKSTAT */
1048 if (count == 1 && inlast != 0) {
1049 inlast++;
1050 error = xfs_bulkstat_single(mp, &inlast,
1051 bulkreq.ubuffer, &done);
1052 } else {
1053 error = xfs_bulkstat(mp, &inlast, &count,
1054 (bulkstat_one_pf)xfs_bulkstat_one, NULL,
1055 sizeof(xfs_bstat_t), bulkreq.ubuffer,
1056 BULKSTAT_FG_QUICK, &done);
1057 }
1058 }
1059
1060 if (error)
1061 return -error;
1062
1063 if (bulkreq.ocount != NULL) {
1064 if (copy_to_user(bulkreq.lastip, &inlast,
1065 sizeof(xfs_ino_t)))
1066 return -XFS_ERROR(EFAULT);
1067
1068 if (copy_to_user(bulkreq.ocount, &count, sizeof(count)))
1069 return -XFS_ERROR(EFAULT);
1070 }
1071
1072 return 0;
1073}
1074
1075STATIC int
1076xfs_ioc_fsgeometry_v1(
1077 xfs_mount_t *mp,
1078 void __user *arg)
1079{
1080 xfs_fsop_geom_v1_t fsgeo;
1081 int error;
1082
1083 error = xfs_fs_geometry(mp, (xfs_fsop_geom_t *)&fsgeo, 3);
1084 if (error)
1085 return -error;
1086
1087 if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
1088 return -XFS_ERROR(EFAULT);
1089 return 0;
1090}
1091
1092STATIC int
1093xfs_ioc_fsgeometry(
1094 xfs_mount_t *mp,
1095 void __user *arg)
1096{
1097 xfs_fsop_geom_t fsgeo;
1098 int error;
1099
1100 error = xfs_fs_geometry(mp, &fsgeo, 4);
1101 if (error)
1102 return -error;
1103
1104 if (copy_to_user(arg, &fsgeo, sizeof(fsgeo)))
1105 return -XFS_ERROR(EFAULT);
1106 return 0;
1107}
1108
1109/*
1110 * Linux extended inode flags interface.
1111 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112
1113STATIC unsigned int
1114xfs_merge_ioc_xflags(
1115 unsigned int flags,
1116 unsigned int start)
1117{
1118 unsigned int xflags = start;
1119
Eric Sandeen39058a02007-02-10 18:37:10 +11001120 if (flags & FS_IMMUTABLE_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 xflags |= XFS_XFLAG_IMMUTABLE;
1122 else
1123 xflags &= ~XFS_XFLAG_IMMUTABLE;
Eric Sandeen39058a02007-02-10 18:37:10 +11001124 if (flags & FS_APPEND_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 xflags |= XFS_XFLAG_APPEND;
1126 else
1127 xflags &= ~XFS_XFLAG_APPEND;
Eric Sandeen39058a02007-02-10 18:37:10 +11001128 if (flags & FS_SYNC_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 xflags |= XFS_XFLAG_SYNC;
1130 else
1131 xflags &= ~XFS_XFLAG_SYNC;
Eric Sandeen39058a02007-02-10 18:37:10 +11001132 if (flags & FS_NOATIME_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 xflags |= XFS_XFLAG_NOATIME;
1134 else
1135 xflags &= ~XFS_XFLAG_NOATIME;
Eric Sandeen39058a02007-02-10 18:37:10 +11001136 if (flags & FS_NODUMP_FL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 xflags |= XFS_XFLAG_NODUMP;
1138 else
1139 xflags &= ~XFS_XFLAG_NODUMP;
1140
1141 return xflags;
1142}
1143
1144STATIC unsigned int
1145xfs_di2lxflags(
1146 __uint16_t di_flags)
1147{
1148 unsigned int flags = 0;
1149
1150 if (di_flags & XFS_DIFLAG_IMMUTABLE)
Eric Sandeen39058a02007-02-10 18:37:10 +11001151 flags |= FS_IMMUTABLE_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 if (di_flags & XFS_DIFLAG_APPEND)
Eric Sandeen39058a02007-02-10 18:37:10 +11001153 flags |= FS_APPEND_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 if (di_flags & XFS_DIFLAG_SYNC)
Eric Sandeen39058a02007-02-10 18:37:10 +11001155 flags |= FS_SYNC_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 if (di_flags & XFS_DIFLAG_NOATIME)
Eric Sandeen39058a02007-02-10 18:37:10 +11001157 flags |= FS_NOATIME_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 if (di_flags & XFS_DIFLAG_NODUMP)
Eric Sandeen39058a02007-02-10 18:37:10 +11001159 flags |= FS_NODUMP_FL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 return flags;
1161}
1162
1163STATIC int
1164xfs_ioc_xattr(
Nathan Scott67fcaa72006-06-09 17:00:52 +10001165 bhv_vnode_t *vp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 xfs_inode_t *ip,
1167 struct file *filp,
1168 unsigned int cmd,
1169 void __user *arg)
1170{
1171 struct fsxattr fa;
Nathan Scott8285fb52006-06-09 17:07:12 +10001172 struct bhv_vattr *vattr;
Nathan Scott220b5282006-03-14 13:33:36 +11001173 int error = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 int attr_flags;
1175 unsigned int flags;
1176
Nathan Scott220b5282006-03-14 13:33:36 +11001177 vattr = kmalloc(sizeof(*vattr), GFP_KERNEL);
1178 if (unlikely(!vattr))
1179 return -ENOMEM;
1180
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 switch (cmd) {
1182 case XFS_IOC_FSGETXATTR: {
Nathan Scott220b5282006-03-14 13:33:36 +11001183 vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \
1184 XFS_AT_NEXTENTS | XFS_AT_PROJID;
Christoph Hellwig739bfb22007-08-29 10:58:01 +10001185 error = xfs_getattr(ip, vattr, 0);
Nathan Scott220b5282006-03-14 13:33:36 +11001186 if (unlikely(error)) {
1187 error = -error;
1188 break;
1189 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
Nathan Scott220b5282006-03-14 13:33:36 +11001191 fa.fsx_xflags = vattr->va_xflags;
1192 fa.fsx_extsize = vattr->va_extsize;
1193 fa.fsx_nextents = vattr->va_nextents;
1194 fa.fsx_projid = vattr->va_projid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
Nathan Scott220b5282006-03-14 13:33:36 +11001196 if (copy_to_user(arg, &fa, sizeof(fa))) {
1197 error = -EFAULT;
1198 break;
1199 }
1200 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 }
1202
1203 case XFS_IOC_FSSETXATTR: {
Nathan Scott220b5282006-03-14 13:33:36 +11001204 if (copy_from_user(&fa, arg, sizeof(fa))) {
1205 error = -EFAULT;
1206 break;
1207 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208
1209 attr_flags = 0;
1210 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1211 attr_flags |= ATTR_NONBLOCK;
1212
Nathan Scott220b5282006-03-14 13:33:36 +11001213 vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | XFS_AT_PROJID;
1214 vattr->va_xflags = fa.fsx_xflags;
1215 vattr->va_extsize = fa.fsx_extsize;
1216 vattr->va_projid = fa.fsx_projid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Christoph Hellwig739bfb22007-08-29 10:58:01 +10001218 error = xfs_setattr(ip, vattr, attr_flags, NULL);
Nathan Scott220b5282006-03-14 13:33:36 +11001219 if (likely(!error))
1220 __vn_revalidate(vp, vattr); /* update flags */
1221 error = -error;
1222 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 }
1224
1225 case XFS_IOC_FSGETXATTRA: {
Nathan Scott220b5282006-03-14 13:33:36 +11001226 vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \
1227 XFS_AT_ANEXTENTS | XFS_AT_PROJID;
Christoph Hellwig739bfb22007-08-29 10:58:01 +10001228 error = xfs_getattr(ip, vattr, 0);
Nathan Scott220b5282006-03-14 13:33:36 +11001229 if (unlikely(error)) {
1230 error = -error;
1231 break;
1232 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233
Nathan Scott220b5282006-03-14 13:33:36 +11001234 fa.fsx_xflags = vattr->va_xflags;
1235 fa.fsx_extsize = vattr->va_extsize;
1236 fa.fsx_nextents = vattr->va_anextents;
1237 fa.fsx_projid = vattr->va_projid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
Nathan Scott220b5282006-03-14 13:33:36 +11001239 if (copy_to_user(arg, &fa, sizeof(fa))) {
1240 error = -EFAULT;
1241 break;
1242 }
1243 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 }
1245
1246 case XFS_IOC_GETXFLAGS: {
1247 flags = xfs_di2lxflags(ip->i_d.di_flags);
1248 if (copy_to_user(arg, &flags, sizeof(flags)))
Nathan Scott220b5282006-03-14 13:33:36 +11001249 error = -EFAULT;
1250 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 }
1252
1253 case XFS_IOC_SETXFLAGS: {
Nathan Scott220b5282006-03-14 13:33:36 +11001254 if (copy_from_user(&flags, arg, sizeof(flags))) {
1255 error = -EFAULT;
1256 break;
1257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258
Eric Sandeen39058a02007-02-10 18:37:10 +11001259 if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
1260 FS_NOATIME_FL | FS_NODUMP_FL | \
1261 FS_SYNC_FL)) {
Nathan Scott220b5282006-03-14 13:33:36 +11001262 error = -EOPNOTSUPP;
1263 break;
1264 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
1266 attr_flags = 0;
1267 if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
1268 attr_flags |= ATTR_NONBLOCK;
1269
Nathan Scott220b5282006-03-14 13:33:36 +11001270 vattr->va_mask = XFS_AT_XFLAGS;
1271 vattr->va_xflags = xfs_merge_ioc_xflags(flags,
1272 xfs_ip2xflags(ip));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
Christoph Hellwig739bfb22007-08-29 10:58:01 +10001274 error = xfs_setattr(ip, vattr, attr_flags, NULL);
Nathan Scott220b5282006-03-14 13:33:36 +11001275 if (likely(!error))
1276 __vn_revalidate(vp, vattr); /* update flags */
1277 error = -error;
1278 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 }
1280
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 default:
Nathan Scott220b5282006-03-14 13:33:36 +11001282 error = -ENOTTY;
1283 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 }
Nathan Scott220b5282006-03-14 13:33:36 +11001285
1286 kfree(vattr);
1287 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288}
1289
1290STATIC int
1291xfs_ioc_getbmap(
Christoph Hellwig993386c2007-08-28 16:12:30 +10001292 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 int ioflags,
1294 unsigned int cmd,
1295 void __user *arg)
1296{
1297 struct getbmap bm;
1298 int iflags;
1299 int error;
1300
1301 if (copy_from_user(&bm, arg, sizeof(bm)))
1302 return -XFS_ERROR(EFAULT);
1303
1304 if (bm.bmv_count < 2)
1305 return -XFS_ERROR(EINVAL);
1306
1307 iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
1308 if (ioflags & IO_INVIS)
1309 iflags |= BMV_IF_NO_DMAPI_READ;
1310
Christoph Hellwig993386c2007-08-28 16:12:30 +10001311 error = xfs_getbmap(ip, &bm, (struct getbmap __user *)arg+1, iflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 if (error)
1313 return -error;
1314
1315 if (copy_to_user(arg, &bm, sizeof(bm)))
1316 return -XFS_ERROR(EFAULT);
1317 return 0;
1318}
1319
1320STATIC int
1321xfs_ioc_getbmapx(
Christoph Hellwig993386c2007-08-28 16:12:30 +10001322 struct xfs_inode *ip,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 void __user *arg)
1324{
1325 struct getbmapx bmx;
1326 struct getbmap bm;
1327 int iflags;
1328 int error;
1329
1330 if (copy_from_user(&bmx, arg, sizeof(bmx)))
1331 return -XFS_ERROR(EFAULT);
1332
1333 if (bmx.bmv_count < 2)
1334 return -XFS_ERROR(EINVAL);
1335
1336 /*
1337 * Map input getbmapx structure to a getbmap
1338 * structure for xfs_getbmap.
1339 */
1340 GETBMAP_CONVERT(bmx, bm);
1341
1342 iflags = bmx.bmv_iflags;
1343
1344 if (iflags & (~BMV_IF_VALID))
1345 return -XFS_ERROR(EINVAL);
1346
1347 iflags |= BMV_IF_EXTENDED;
1348
Christoph Hellwig993386c2007-08-28 16:12:30 +10001349 error = xfs_getbmap(ip, &bm, (struct getbmapx __user *)arg+1, iflags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 if (error)
1351 return -error;
1352
1353 GETBMAP_CONVERT(bm, bmx);
1354
1355 if (copy_to_user(arg, &bmx, sizeof(bmx)))
1356 return -XFS_ERROR(EFAULT);
1357
1358 return 0;
1359}