blob: 8a26afd206e9e3737e7960cdd06de2b5cd58fbe7 [file] [log] [blame]
/*
* Copyright IBM Corporation, 2010
* Copyright (C) 2015 Red Hat, Inc.
* Author: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>,
* Andreas Gruenbacher <agruenba@redhat.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/richacl_xattr.h>
#include "ext4.h"
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
#include "richacl.h"
struct richacl *
ext4_get_richacl(struct inode *inode)
{
const int name_index = EXT4_XATTR_INDEX_RICHACL;
void *value = NULL;
struct richacl *acl = NULL;
int retval;
retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
if (retval > 0) {
value = kmalloc(retval, GFP_NOFS);
if (!value)
return ERR_PTR(-ENOMEM);
retval = ext4_xattr_get(inode, name_index, "", value, retval);
}
if (retval > 0)
acl = richacl_from_xattr(&init_user_ns, value, retval, -EIO);
else if (retval != -ENODATA && retval != -ENOSYS)
acl = ERR_PTR(retval);
kfree(value);
return acl;
}
static int
__ext4_remove_richacl(handle_t *handle, struct inode *inode)
{
const int name_index = EXT4_XATTR_INDEX_RICHACL;
int retval;
retval = ext4_xattr_set_handle(handle, inode, name_index, "",
NULL, 0, 0);
if (!retval)
set_cached_richacl(inode, NULL);
return retval;
}
static int
__ext4_set_richacl(handle_t *handle, struct inode *inode, struct richacl *acl)
{
const int name_index = EXT4_XATTR_INDEX_RICHACL;
umode_t mode = inode->i_mode;
int retval, size;
void *value;
/* Don't allow acls with unmapped identifiers. */
if (richacl_has_unmapped_identifiers(acl))
return -EINVAL;
if (richacl_equiv_mode(acl, &mode) == 0) {
inode->i_ctime = ext4_current_time(inode);
inode->i_mode = mode;
ext4_mark_inode_dirty(handle, inode);
return __ext4_remove_richacl(handle, inode);
}
mode &= ~S_IRWXUGO;
mode |= richacl_masks_to_mode(acl);
size = richacl_xattr_size(acl);
value = kmalloc(size, GFP_NOFS);
if (!value)
return -ENOMEM;
richacl_to_xattr(&init_user_ns, acl, value, size);
inode->i_mode = mode;
retval = ext4_xattr_set_handle(handle, inode, name_index, "",
value, size, 0);
kfree(value);
if (retval)
return retval;
set_cached_richacl(inode, acl);
return 0;
}
int
ext4_set_richacl(struct inode *inode, struct richacl *acl)
{
handle_t *handle;
int retval, retries = 0;
retry:
handle = ext4_journal_start(inode, EXT4_HT_XATTR,
ext4_jbd2_credits_xattr(inode));
if (IS_ERR(handle))
return PTR_ERR(handle);
if (acl)
retval = __ext4_set_richacl(handle, inode, acl);
else
retval = __ext4_remove_richacl(handle, inode);
ext4_journal_stop(handle);
if (retval == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
goto retry;
return retval;
}
int
ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir)
{
struct richacl *acl = richacl_create(&inode->i_mode, dir);
int error;
error = PTR_ERR(acl);
if (!IS_ERR_OR_NULL(acl)) {
error = __ext4_set_richacl(handle, inode, acl);
richacl_put(acl);
}
return error;
}