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