STATIC int xfs_attrlist_by_handle( xfs_mount_t *mp, unsigned long arg, struct file *parfilp, struct inode *parinode) { int error; attrlist_cursor_kern_t *cursor; xfs_fsop_attrlist_handlereq_t al_hreq; struct inode *inode; vnode_t *vp; error = xfs_vget_fsop_handlereq(mp, parinode, CAP_SYS_ADMIN, arg, sizeof(xfs_fsop_attrlist_handlereq_t), (xfs_fsop_handlereq_t *)&al_hreq, &vp, &inode); if (error) return -error; cursor = (attrlist_cursor_kern_t *)&al_hreq.pos; VOP_ATTR_LIST(vp, al_hreq.buffer, al_hreq.buflen, al_hreq.flags, cursor, NULL, error); VN_RELE(vp); if (error) return -error; return 0; }
STATIC int xfs_readlink_by_handle( xfs_mount_t *mp, void __user *arg, struct inode *parinode) { struct inode *inode; xfs_fsop_handlereq_t hreq; __u32 olen; void *link; int error; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t))) return -XFS_ERROR(EFAULT); error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &inode); if (error) return -error; /* Restrict this handle operation to symlinks only. */ if (!S_ISLNK(inode->i_mode)) { error = -XFS_ERROR(EINVAL); goto out_iput; } if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) { error = -XFS_ERROR(EFAULT); goto out_iput; } link = kmalloc(MAXPATHLEN+1, GFP_KERNEL); if (!link) goto out_iput; error = -xfs_readlink(XFS_I(inode), link); if (error) goto out_kfree; error = do_readlink(hreq.ohandle, olen, link); if (error) goto out_kfree; out_kfree: kfree(link); out_iput: iput(inode); return error; }
STATIC int xfs_attrlist_by_handle( xfs_mount_t *mp, void __user *arg, struct inode *parinode) { int error; attrlist_cursor_kern_t *cursor; xfs_fsop_attrlist_handlereq_t al_hreq; struct inode *inode; char *kbuf; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t))) return -XFS_ERROR(EFAULT); if (al_hreq.buflen > XATTR_LIST_MAX) return -XFS_ERROR(EINVAL); /* * Reject flags, only allow namespaces. */ if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE)) return -XFS_ERROR(EINVAL); error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq, &inode); if (error) goto out; kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL); if (!kbuf) goto out_vn_rele; cursor = (attrlist_cursor_kern_t *)&al_hreq.pos; error = xfs_attr_list(XFS_I(inode), kbuf, al_hreq.buflen, al_hreq.flags, cursor); if (error) goto out_kfree; if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen)) error = -EFAULT; out_kfree: kfree(kbuf); out_vn_rele: iput(inode); out: return -error; }
STATIC int xfs_readlink_by_handle( xfs_mount_t *mp, void __user *arg, struct file *parfilp, struct inode *parinode) { int error; struct iovec aiov; struct uio auio; struct inode *inode; xfs_fsop_handlereq_t hreq; vnode_t *vp; __u32 olen; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t))) return -XFS_ERROR(EFAULT); error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &vp, &inode); if (error) return -error; /* Restrict this handle operation to symlinks only. */ if (!S_ISLNK(inode->i_mode)) { VN_RELE(vp); return -XFS_ERROR(EINVAL); } if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) { VN_RELE(vp); return -XFS_ERROR(EFAULT); } aiov.iov_len = olen; aiov.iov_base = hreq.ohandle; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_segflg = UIO_USERSPACE; auio.uio_resid = olen; VOP_READLINK(vp, &auio, IO_INVIS, NULL, error); VN_RELE(vp); return (olen - auio.uio_resid); }
STATIC int xfs_attrlist_by_handle( xfs_mount_t *mp, void __user *arg, struct file *parfilp, struct inode *parinode) { int error; attrlist_cursor_kern_t *cursor; xfs_fsop_attrlist_handlereq_t al_hreq; struct inode *inode; vnode_t *vp; char *kbuf; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t))) return -XFS_ERROR(EFAULT); if (al_hreq.buflen > XATTR_LIST_MAX) return -XFS_ERROR(EINVAL); error = xfs_vget_fsop_handlereq(mp, parinode, &al_hreq.hreq, &vp, &inode); if (error) goto out; kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL); if (!kbuf) goto out_vn_rele; cursor = (attrlist_cursor_kern_t *)&al_hreq.pos; VOP_ATTR_LIST(vp, kbuf, al_hreq.buflen, al_hreq.flags, cursor, NULL, error); if (error) goto out_kfree; if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen)) error = -EFAULT; out_kfree: kfree(kbuf); out_vn_rele: VN_RELE(vp); out: return -error; }
STATIC int xfs_fssetdm_by_handle( xfs_mount_t *mp, void __user *arg, struct file *parfilp, struct inode *parinode) { int error; struct fsdmidata fsd; xfs_fsop_setdm_handlereq_t dmhreq; struct inode *inode; bhv_desc_t *bdp; vnode_t *vp; if (!capable(CAP_MKNOD)) return -XFS_ERROR(EPERM); if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t))) return -XFS_ERROR(EFAULT); error = xfs_vget_fsop_handlereq(mp, parinode, &dmhreq.hreq, &vp, &inode); if (error) return -error; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { VN_RELE(vp); return -XFS_ERROR(EPERM); } if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) { VN_RELE(vp); return -XFS_ERROR(EFAULT); } bdp = bhv_base_unlocked(VN_BHV_HEAD(vp)); error = xfs_set_dmattrs(bdp, fsd.fsd_dmevmask, fsd.fsd_dmstate, NULL); VN_RELE(vp); if (error) return -error; return 0; }
STATIC int xfs_fssetdm_by_handle( xfs_mount_t *mp, void __user *arg, struct inode *parinode) { int error; struct fsdmidata fsd; xfs_fsop_setdm_handlereq_t dmhreq; struct inode *inode; if (!capable(CAP_MKNOD)) return -XFS_ERROR(EPERM); if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t))) return -XFS_ERROR(EFAULT); error = xfs_vget_fsop_handlereq(mp, parinode, &dmhreq.hreq, &inode); if (error) return -error; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { error = -XFS_ERROR(EPERM); goto out; } if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) { error = -XFS_ERROR(EFAULT); goto out; } error = -xfs_set_dmattrs(XFS_I(inode), fsd.fsd_dmevmask, fsd.fsd_dmstate); out: iput(inode); return error; }
STATIC int xfs_attrmulti_by_handle( xfs_mount_t *mp, unsigned long arg, struct file *parfilp, struct inode *parinode) { int error; xfs_attr_multiop_t *ops; xfs_fsop_attrmulti_handlereq_t am_hreq; struct inode *inode; vnode_t *vp; unsigned int i, size; error = xfs_vget_fsop_handlereq(mp, parinode, CAP_SYS_ADMIN, arg, sizeof(xfs_fsop_attrmulti_handlereq_t), (xfs_fsop_handlereq_t *)&am_hreq, &vp, &inode); if (error) return -error; size = am_hreq.opcount * sizeof(attr_multiop_t); if (!size || size > 16 * PAGE_SIZE) { VN_RELE(vp); return -XFS_ERROR(E2BIG); } ops = (xfs_attr_multiop_t *)kmalloc(size, GFP_KERNEL); if (!ops) { VN_RELE(vp); return -XFS_ERROR(ENOMEM); } if (copy_from_user(ops, am_hreq.ops, size)) { kfree(ops); VN_RELE(vp); return -XFS_ERROR(EFAULT); } for (i = 0; i < am_hreq.opcount; i++) { switch(ops[i].am_opcode) { case ATTR_OP_GET: VOP_ATTR_GET(vp,ops[i].am_attrname, ops[i].am_attrvalue, &ops[i].am_length, ops[i].am_flags, NULL, ops[i].am_error); break; case ATTR_OP_SET: if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { ops[i].am_error = EPERM; break; } VOP_ATTR_SET(vp,ops[i].am_attrname, ops[i].am_attrvalue, ops[i].am_length, ops[i].am_flags, NULL, ops[i].am_error); break; case ATTR_OP_REMOVE: if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { ops[i].am_error = EPERM; break; } VOP_ATTR_REMOVE(vp, ops[i].am_attrname, ops[i].am_flags, NULL, ops[i].am_error); break; default: ops[i].am_error = EINVAL; } } if (copy_to_user(am_hreq.ops, ops, size)) error = -XFS_ERROR(EFAULT); kfree(ops); VN_RELE(vp); return error; }
STATIC int xfs_open_by_handle( xfs_mount_t *mp, unsigned long arg, struct file *parfilp, struct inode *parinode) { int error; int new_fd; int permflag; struct file *filp; struct inode *inode; struct dentry *dentry; vnode_t *vp; xfs_fsop_handlereq_t hreq; error = xfs_vget_fsop_handlereq(mp, parinode, CAP_SYS_ADMIN, arg, sizeof(xfs_fsop_handlereq_t), &hreq, &vp, &inode); if (error) return -error; /* Restrict xfs_open_by_handle to directories & regular files. */ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { iput(inode); return -XFS_ERROR(EINVAL); } #if BITS_PER_LONG != 32 hreq.oflags |= O_LARGEFILE; #endif /* Put open permission in namei format. */ permflag = hreq.oflags; if ((permflag+1) & O_ACCMODE) permflag++; if (permflag & O_TRUNC) permflag |= 2; if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) && (permflag & FMODE_WRITE) && IS_APPEND(inode)) { iput(inode); return -XFS_ERROR(EPERM); } if ((permflag & FMODE_WRITE) && IS_IMMUTABLE(inode)) { iput(inode); return -XFS_ERROR(EACCES); } /* Can't write directories. */ if ( S_ISDIR(inode->i_mode) && (permflag & FMODE_WRITE)) { iput(inode); return -XFS_ERROR(EISDIR); } if ((new_fd = get_unused_fd()) < 0) { iput(inode); return new_fd; } dentry = d_alloc_anon(inode); if (dentry == NULL) { iput(inode); put_unused_fd(new_fd); return -XFS_ERROR(ENOMEM); } /* Ensure umount returns EBUSY on umounts while this file is open. */ mntget(parfilp->f_vfsmnt); /* Create file pointer. */ filp = dentry_open(dentry, parfilp->f_vfsmnt, hreq.oflags); if (IS_ERR(filp)) { put_unused_fd(new_fd); return -XFS_ERROR(-PTR_ERR(filp)); } if (inode->i_mode & S_IFREG) filp->f_op = &linvfs_invis_file_operations; fd_install(new_fd, filp); return new_fd; }
STATIC int xfs_attrmulti_by_handle( xfs_mount_t *mp, void __user *arg, struct inode *parinode) { int error; xfs_attr_multiop_t *ops; xfs_fsop_attrmulti_handlereq_t am_hreq; struct inode *inode; unsigned int i, size; char *attr_name; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t))) return -XFS_ERROR(EFAULT); error = xfs_vget_fsop_handlereq(mp, parinode, &am_hreq.hreq, &inode); if (error) goto out; error = E2BIG; size = am_hreq.opcount * sizeof(xfs_attr_multiop_t); if (!size || size > 16 * PAGE_SIZE) goto out_vn_rele; error = ENOMEM; ops = memdup_user(am_hreq.ops, size); if (IS_ERR(ops)) { error = PTR_ERR(ops); goto out_kfree_ops; } attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL); if (!attr_name) goto out_kfree_ops; error = 0; for (i = 0; i < am_hreq.opcount; i++) { ops[i].am_error = strncpy_from_user(attr_name, ops[i].am_attrname, MAXNAMELEN); if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN) error = -ERANGE; if (ops[i].am_error < 0) break; switch (ops[i].am_opcode) { case ATTR_OP_GET: ops[i].am_error = xfs_attrmulti_attr_get(inode, attr_name, ops[i].am_attrvalue, &ops[i].am_length, ops[i].am_flags); break; case ATTR_OP_SET: ops[i].am_error = xfs_attrmulti_attr_set(inode, attr_name, ops[i].am_attrvalue, ops[i].am_length, ops[i].am_flags); break; case ATTR_OP_REMOVE: ops[i].am_error = xfs_attrmulti_attr_remove(inode, attr_name, ops[i].am_flags); break; default: ops[i].am_error = EINVAL; } } if (copy_to_user(am_hreq.ops, ops, size)) error = XFS_ERROR(EFAULT); kfree(attr_name); out_kfree_ops: kfree(ops); out_vn_rele: iput(inode); out: return -error; }
STATIC int xfs_open_by_handle( xfs_mount_t *mp, void __user *arg, struct file *parfilp, struct inode *parinode) { int error; int new_fd; int permflag; struct file *filp; struct inode *inode; struct dentry *dentry; xfs_fsop_handlereq_t hreq; if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t))) return -XFS_ERROR(EFAULT); error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &inode); if (error) return -error; /* Restrict xfs_open_by_handle to directories & regular files. */ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { iput(inode); return -XFS_ERROR(EINVAL); } #if BITS_PER_LONG != 32 hreq.oflags |= O_LARGEFILE; #endif /* Put open permission in namei format. */ permflag = hreq.oflags; if ((permflag+1) & O_ACCMODE) permflag++; if (permflag & O_TRUNC) permflag |= 2; if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) && (permflag & FMODE_WRITE) && IS_APPEND(inode)) { iput(inode); return -XFS_ERROR(EPERM); } if ((permflag & FMODE_WRITE) && IS_IMMUTABLE(inode)) { iput(inode); return -XFS_ERROR(EACCES); } /* Can't write directories. */ if ( S_ISDIR(inode->i_mode) && (permflag & FMODE_WRITE)) { iput(inode); return -XFS_ERROR(EISDIR); } if ((new_fd = get_unused_fd()) < 0) { iput(inode); return new_fd; } dentry = d_obtain_alias(inode); if (IS_ERR(dentry)) { put_unused_fd(new_fd); return PTR_ERR(dentry); } /* Ensure umount returns EBUSY on umounts while this file is open. */ mntget(parfilp->f_path.mnt); /* Create file pointer. */ filp = dentry_open(dentry, parfilp->f_path.mnt, hreq.oflags); if (IS_ERR(filp)) { put_unused_fd(new_fd); return -XFS_ERROR(-PTR_ERR(filp)); } if (inode->i_mode & S_IFREG) { /* invisible operation should not change atime */ filp->f_flags |= O_NOATIME; filp->f_op = &xfs_invis_file_operations; } fd_install(new_fd, filp); return new_fd; }