int xfs_cap_vset( vnode_t *vp, void *cap, size_t size) { posix_cap_xattr *xattr_cap = cap; xfs_cap_set_t xfs_cap; int error; if (!cap) return -EINVAL; error = posix_cap_xattr_to_xfs(xattr_cap, size, &xfs_cap); if (error) return -error; VN_HOLD(vp); error = xfs_cap_allow_set(vp); if (error) goto out; VOP_ATTR_SET(vp, SGI_CAP_LINUX, (char *)&xfs_cap, sizeof(xfs_cap_set_t), ATTR_ROOT, sys_cred, error); out: VN_RELE(vp); return -error; }
STATIC int xfs_attrmulti_attr_set( struct vnode *vp, char *name, const char __user *ubuf, __uint32_t len, __uint32_t flags) { char *kbuf; int error = EFAULT; if (IS_RDONLY(&vp->v_inode)) return -EROFS; if (IS_IMMUTABLE(&vp->v_inode) || IS_APPEND(&vp->v_inode)) return EPERM; if (len > XATTR_SIZE_MAX) return EINVAL; kbuf = kmalloc(len, GFP_KERNEL); if (!kbuf) return ENOMEM; if (copy_from_user(kbuf, ubuf, len)) goto out_kfree; VOP_ATTR_SET(vp, name, kbuf, len, flags, NULL, error); out_kfree: kfree(kbuf); return error; }
/* * Set the EA with the ACL and do endian conversion. */ STATIC void xfs_acl_set_attr( vnode_t *vp, xfs_acl_t *aclp, int kind, int *error) { xfs_acl_entry_t *ace, *newace, *end; xfs_acl_t *newacl; int len; if (!(_ACL_ALLOC(newacl))) { *error = ENOMEM; return; } len = sizeof(xfs_acl_t) - (sizeof(xfs_acl_entry_t) * (XFS_ACL_MAX_ENTRIES - aclp->acl_cnt)); end = &aclp->acl_entry[0]+aclp->acl_cnt; for (ace = &aclp->acl_entry[0], newace = &newacl->acl_entry[0]; ace < end; ace++, newace++) { INT_SET(newace->ae_tag, ARCH_CONVERT, ace->ae_tag); INT_SET(newace->ae_id, ARCH_CONVERT, ace->ae_id); INT_SET(newace->ae_perm, ARCH_CONVERT, ace->ae_perm); } INT_SET(newacl->acl_cnt, ARCH_CONVERT, aclp->acl_cnt); VOP_ATTR_SET(vp, kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE: SGI_ACL_DEFAULT, (char *)newacl, len, ATTR_ROOT, sys_cred, *error); _ACL_FREE(newacl); }
/* * Hook in SELinux. This is not quite correct yet, what we really need * here (as we do for default ACLs) is a mechanism by which creation of * these attrs can be journalled at inode creation time (along with the * inode, of course, such that log replay can't cause these to be lost). */ STATIC int linvfs_init_security( struct vnode *vp, struct inode *dir) { struct inode *ip = LINVFS_GET_IP(vp); size_t length; void *value; char *name; int error; error = security_inode_init_security(ip, dir, &name, &value, &length); if (error) { if (error == -EOPNOTSUPP) return 0; return -error; } VOP_ATTR_SET(vp, name, value, length, ATTR_SECURE, NULL, error); if (!error) VMODIFY(vp); kfree(name); kfree(value); 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 linvfs_setxattr( struct dentry *dentry, const char *name, void *data, size_t size, int flags) { int error; int xflags = 0; char *p = (char *)name; struct inode *inode = dentry->d_inode; vnode_t *vp = LINVFS_GET_VP(inode); if (strncmp(name, xfs_namespaces[SYSTEM_NAMES].name, xfs_namespaces[SYSTEM_NAMES].namelen) == 0) { error = -EINVAL; if (flags & XATTR_CREATE) return error; error = -ENOATTR; p += xfs_namespaces[SYSTEM_NAMES].namelen; if (strcmp(p, POSIXACL_ACCESS) == 0) { if (vp->v_flag & VMODIFIED) { error = linvfs_revalidate_core(inode, 0); if (error) return error; } error = xfs_acl_vset(vp, data, size, _ACL_TYPE_ACCESS); if (!error) { VMODIFY(vp); error = linvfs_revalidate_core(inode, 0); } } else if (strcmp(p, POSIXACL_DEFAULT) == 0) { error = linvfs_revalidate_core(inode, 0); if (error) return error; error = xfs_acl_vset(vp, data, size, _ACL_TYPE_DEFAULT); if (!error) { VMODIFY(vp); error = linvfs_revalidate_core(inode, 0); } } else if (strcmp(p, POSIXCAP) == 0) { error = xfs_cap_vset(vp, data, size); } return error; } /* Convert Linux syscall to XFS internal ATTR flags */ if (flags & XATTR_CREATE) xflags |= ATTR_CREATE; if (flags & XATTR_REPLACE) xflags |= ATTR_REPLACE; if (strncmp(name, xfs_namespaces[ROOT_NAMES].name, xfs_namespaces[ROOT_NAMES].namelen) == 0) { if (!capable(CAP_SYS_ADMIN)) return -EPERM; xflags |= ATTR_ROOT; p += xfs_namespaces[ROOT_NAMES].namelen; VOP_ATTR_SET(vp, p, data, size, xflags, NULL, error); return -error; } if (strncmp(name, xfs_namespaces[USER_NAMES].name, xfs_namespaces[USER_NAMES].namelen) == 0) { if (!capable_user_xattr(inode)) return -EPERM; p += xfs_namespaces[USER_NAMES].namelen; VOP_ATTR_SET(vp, p, data, size, xflags, NULL, error); return -error; } return -ENOATTR; }