static int ext2_xattr_set_acl(struct _inode *inode, int type, const void *value, size_t size) { struct posix_acl *acl; int error; if (!test_opt(i_get_sb(inode), POSIX_ACL)) return -EOPNOTSUPP; if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) return -EPERM; if (value) { acl = posix_acl_from_xattr(value, size); if (IS_ERR(acl)) return PTR_ERR(acl); else if (acl) { error = posix_acl_valid(acl); if (error) goto release_and_out; } } else acl = NULL; error = ext2_set_acl(inode, type, acl); release_and_out: posix_acl_release(acl); return error; }
/* * inode->i_mutex: don't care */ static struct posix_acl * ext2_get_acl(const struct _inode *inode, int type) { struct ext2_inode_info *ei = EXT2_I(parent(inode)); int name_index; char *value = NULL; struct posix_acl *acl; int retval; if (!test_opt(i_get_sb(inode), POSIX_ACL)) return NULL; switch(type) { case ACL_TYPE_ACCESS: acl = ext2_iget_acl(parent(inode), &ei->i_acl); if (acl != EXT2_ACL_NOT_CACHED) return acl; name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; break; case ACL_TYPE_DEFAULT: acl = ext2_iget_acl(parent(inode), &ei->i_default_acl); if (acl != EXT2_ACL_NOT_CACHED) return acl; name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; break; default: return ERR_PTR(-EINVAL); } retval = ext2_xattr_get(inode, name_index, "", NULL, 0); if (retval > 0) { value = kmalloc(retval, GFP_KERNEL); if (!value) return ERR_PTR(-ENOMEM); retval = ext2_xattr_get(inode, name_index, "", value, retval); } if (retval > 0) acl = ext2_acl_from_disk(value, retval); else if (retval == -ENODATA || retval == -ENOSYS) acl = NULL; else acl = ERR_PTR(retval); kfree(value); if (!IS_ERR(acl)) { switch(type) { case ACL_TYPE_ACCESS: ext2_iset_acl(parent(inode), &ei->i_acl, acl); break; case ACL_TYPE_DEFAULT: ext2_iset_acl(parent(inode), &ei->i_default_acl, acl); break; } } return acl; }
/* * Initialize the ACLs of a new inode. Called from ext2_new_inode. * * dir->i_mutex: down * inode->i_mutex: up (access to inode is still exclusive) */ int ext2_init_acl(struct _inode *inode, struct _inode *dir) { struct posix_acl *acl = NULL; int error = 0; if (!S_ISLNK(inode->i_mode)) { if (test_opt(i_get_sb(dir), POSIX_ACL)) { acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); if (IS_ERR(acl)) return PTR_ERR(acl); } if (!acl) inode->i_mode &= ~current->fs->umask; } if (test_opt(i_get_sb(inode), POSIX_ACL) && acl) { struct posix_acl *clone; mode_t mode; if (S_ISDIR(inode->i_mode)) { error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); if (error) goto cleanup; } clone = posix_acl_clone(acl, GFP_KERNEL); error = -ENOMEM; if (!clone) goto cleanup; mode = inode->i_mode; error = posix_acl_create_masq(clone, &mode); if (error >= 0) { inode->i_mode = mode; if (error > 0) { /* This is an extended ACL */ error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone); } } posix_acl_release(clone); } cleanup: posix_acl_release(acl); return error; }
static int ext2_xattr_user_get(struct _inode *inode, const char *name, void *buffer, size_t size) { if (strcmp(name, "") == 0) return -EINVAL; if (!test_opt(i_get_sb(inode), XATTR_USER)) return -EOPNOTSUPP; return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name, buffer, size); }
static int ext2_xattr_get_acl(struct _inode *inode, int type, void *buffer, size_t size) { struct posix_acl *acl; int error; if (!test_opt(i_get_sb(inode), POSIX_ACL)) return -EOPNOTSUPP; acl = ext2_get_acl(inode, type); if (IS_ERR(acl)) return PTR_ERR(acl); if (acl == NULL) return -ENODATA; error = posix_acl_to_xattr(acl, buffer, size); posix_acl_release(acl); return error; }
/* * inode->i_mutex: down */ static int ext2_set_acl(struct _inode *inode, int type, struct posix_acl *acl) { struct ext2_inode_info *ei = EXT2_I(parent(inode)); int name_index; void *value = NULL; size_t size = 0; int error; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; if (!test_opt(i_get_sb(inode), POSIX_ACL)) return 0; switch(type) { case ACL_TYPE_ACCESS: name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; if (acl) { mode_t mode = inode->i_mode; error = posix_acl_equiv_mode(acl, &mode); if (error < 0) return error; else { inode->i_mode = mode; mark_inode_dirty(parent(inode)); if (error == 0) acl = NULL; } } break; case ACL_TYPE_DEFAULT: name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; if (!S_ISDIR(inode->i_mode)) return acl ? -EACCES : 0; break; default: return -EINVAL; } if (acl) { value = ext2_acl_to_disk(acl, &size); if (IS_ERR(value)) return (int)PTR_ERR(value); } error = ext2_xattr_set(inode, name_index, "", value, size, 0); kfree(value); if (!error) { switch(type) { case ACL_TYPE_ACCESS: ext2_iset_acl(parent(inode), &ei->i_acl, acl); break; case ACL_TYPE_DEFAULT: ext2_iset_acl(parent(inode), &ei->i_default_acl, acl); break; } } return error; }