mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 00:10:51 +07:00
5fa3ea047a
Signed-off-by: AuxXxilium <info@auxxxilium.tech>
627 lines
15 KiB
C
627 lines
15 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2000-2021 Synology Inc.
|
|
*/
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/syno_acl.h>
|
|
#include <linux/syno_acl_xattr.h>
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/syno.h>
|
|
|
|
#include "syno_acl.h"
|
|
|
|
struct syno_acl *syno_acl_alloc(int count, gfp_t flags)
|
|
{
|
|
size_t size = sizeof(struct syno_acl) + count * sizeof(struct syno_acl_entry);
|
|
struct syno_acl *acl = kmalloc(size, flags);
|
|
|
|
if (acl) {
|
|
refcount_set(&acl->a_refcount, 1);
|
|
acl->a_count = count;
|
|
}
|
|
|
|
return acl;
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_alloc);
|
|
|
|
struct syno_acl *syno_acl_clone(const struct syno_acl *acl, gfp_t flags)
|
|
{
|
|
struct syno_acl *clone = NULL;
|
|
|
|
if (acl) {
|
|
size_t size = sizeof(struct syno_acl) +
|
|
acl->a_count * sizeof(struct syno_acl_entry);
|
|
clone = kmemdup(acl, size, flags);
|
|
if (clone)
|
|
refcount_set(&clone->a_refcount, 1);
|
|
}
|
|
return clone;
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_clone);
|
|
|
|
/*
|
|
* Check if an ACL is valid. Returns 0 if it is, or -ERRNO for otherwise.
|
|
*/
|
|
int syno_acl_valid(const struct syno_acl *acl)
|
|
{
|
|
const struct syno_acl_entry *pa, *pe;
|
|
|
|
if (!acl)
|
|
return -EINVAL;
|
|
|
|
FOREACH_SYNOACL_ENTRY(pa, acl, pe) {
|
|
if (pa->e_perm & ~(SYNO_PERM_FULL_CONTROL))
|
|
return -EINVAL;
|
|
if (pa->e_tag & ~(SYNO_ACL_TAG_ALL))
|
|
return -EINVAL;
|
|
if (SYNO_ACL_ALLOW != pa->e_allow && SYNO_ACL_DENY != pa->e_allow)
|
|
return -EINVAL;
|
|
if (pa->e_inherit & ~(SYNO_ACL_INHERIT_ALL))
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_valid);
|
|
|
|
/*
|
|
* Re-allocate a new ACL with the specified number of entries.
|
|
*
|
|
* The caller must ensure the acl is only referenced once.
|
|
*/
|
|
struct syno_acl *syno_acl_realloc(struct syno_acl *acl, unsigned int counts,
|
|
gfp_t flags)
|
|
{
|
|
struct syno_acl *acl_re;
|
|
size_t size = sizeof(struct syno_acl) + counts * sizeof(struct syno_acl_entry);
|
|
|
|
if (!acl)
|
|
return NULL;
|
|
|
|
if (refcount_read(&acl->a_refcount) != 1) {
|
|
printk(KERN_ERR " acl reference count: %d \n ", refcount_read(&acl->a_refcount));
|
|
return NULL;
|
|
}
|
|
|
|
/* assert(refcount_read(acl->a_refcount) == 1); */
|
|
|
|
acl_re = krealloc(acl, size, flags);
|
|
if (acl_re)
|
|
acl_re->a_count = counts;
|
|
|
|
return acl_re;
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_realloc);
|
|
|
|
static inline int ace_syno_from_xattr(struct syno_acl_entry *pAce,
|
|
syno_acl_xattr_entry *pEntry)
|
|
{
|
|
unsigned short tag = le16_to_cpu(pEntry->e_tag);
|
|
|
|
// ID: user/group/everyone
|
|
if (SYNO_ACL_XATTR_TAG_ID_GROUP & tag) {
|
|
pAce->e_tag = SYNO_ACL_GROUP;
|
|
pAce->e_id = le32_to_cpu(pEntry->e_id);
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_EVERYONE & tag) {
|
|
pAce->e_tag = SYNO_ACL_EVERYONE;
|
|
pAce->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_USER & tag) {
|
|
pAce->e_tag = SYNO_ACL_USER;
|
|
pAce->e_id = le32_to_cpu(pEntry->e_id);
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_OWNER & tag) {
|
|
pAce->e_tag = SYNO_ACL_OWNER;
|
|
pAce->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_AUTHENTICATEDUSER & tag) {
|
|
pAce->e_tag = SYNO_ACL_AUTHENTICATEDUSER;
|
|
pAce->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_SYSTEM & tag) {
|
|
pAce->e_tag = SYNO_ACL_SYSTEM;
|
|
pAce->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
// Allow/Deny
|
|
if (SYNO_ACL_XATTR_TAG_IS_DENY & tag)
|
|
pAce->e_allow = SYNO_ACL_DENY;
|
|
else if (SYNO_ACL_XATTR_TAG_IS_ALLOW & tag)
|
|
pAce->e_allow = SYNO_ACL_ALLOW;
|
|
else
|
|
return -1;
|
|
|
|
pAce->e_perm = le32_to_cpu(pEntry->e_perm);
|
|
pAce->e_inherit = le16_to_cpu(pEntry->e_inherit);
|
|
pAce->e_level = le32_to_cpu(pEntry->e_level);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int ace_syno_to_xattr(const struct syno_acl_entry *pAce,
|
|
syno_acl_xattr_entry *pEntry)
|
|
{
|
|
int ret = 0;
|
|
unsigned short tag = 0;
|
|
|
|
// ID: user/group/everyone
|
|
switch (pAce->e_tag) {
|
|
case SYNO_ACL_GROUP:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_GROUP;
|
|
break;
|
|
case SYNO_ACL_EVERYONE:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_EVERYONE;
|
|
break;
|
|
case SYNO_ACL_USER:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_USER;
|
|
break;
|
|
case SYNO_ACL_OWNER:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_OWNER;
|
|
break;
|
|
case SYNO_ACL_AUTHENTICATEDUSER:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_AUTHENTICATEDUSER;
|
|
break;
|
|
case SYNO_ACL_SYSTEM:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_SYSTEM;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto Err;
|
|
}
|
|
|
|
// Allow/Deny
|
|
switch (pAce->e_allow) {
|
|
case SYNO_ACL_DENY:
|
|
tag |= SYNO_ACL_XATTR_TAG_IS_DENY;
|
|
break;
|
|
case SYNO_ACL_ALLOW:
|
|
tag |= SYNO_ACL_XATTR_TAG_IS_ALLOW;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto Err;
|
|
}
|
|
|
|
pEntry->e_tag = cpu_to_le16(tag);
|
|
pEntry->e_inherit = cpu_to_le16(pAce->e_inherit);
|
|
pEntry->e_perm = cpu_to_le32(pAce->e_perm);
|
|
pEntry->e_id = cpu_to_le32(pAce->e_id);
|
|
pEntry->e_level = cpu_to_le32(pAce->e_level);
|
|
|
|
Err:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Convert from extended attribute to in-memory representation.
|
|
*/
|
|
struct syno_acl *syno_acl_from_xattr(const void *value, size_t size)
|
|
{
|
|
syno_acl_xattr_header *header;
|
|
syno_acl_xattr_entry *entry, *end;
|
|
int count;
|
|
struct syno_acl *acl;
|
|
struct syno_acl_entry *acl_e;
|
|
|
|
if (!value)
|
|
return NULL;
|
|
|
|
if (size < sizeof(syno_acl_xattr_header))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
header = (syno_acl_xattr_header *)value;
|
|
entry = (syno_acl_xattr_entry *)(header + 1);
|
|
|
|
if (header->a_version != cpu_to_le16(SYNO_ACL_XATTR_VERSION))
|
|
return ERR_PTR(-EOPNOTSUPP);
|
|
|
|
count = syno_acl_xattr_count(size);
|
|
if (count < 0)
|
|
return ERR_PTR(-EINVAL);
|
|
if (count == 0)
|
|
return NULL;
|
|
|
|
acl = syno_acl_alloc(count, GFP_KERNEL);
|
|
if (!acl)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
acl_e = acl->a_entries;
|
|
end = entry + count;
|
|
for (; entry != end; acl_e++, entry++) {
|
|
if (0 > ace_syno_from_xattr(acl_e, entry))
|
|
goto fail;
|
|
}
|
|
return acl;
|
|
|
|
fail:
|
|
syno_acl_release(acl);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_from_xattr);
|
|
|
|
/*
|
|
* Convert from in-memory to extended attribute representation.
|
|
*/
|
|
int syno_acl_to_xattr(const struct syno_acl *acl, void *buffer, size_t size)
|
|
{
|
|
syno_acl_xattr_header *ext_acl = NULL;
|
|
syno_acl_xattr_entry *ext_entry = NULL;
|
|
int real_size, i, ret;
|
|
|
|
if (!acl)
|
|
return 0;
|
|
|
|
real_size = syno_acl_xattr_size(acl->a_count);
|
|
if (!buffer)
|
|
return real_size;
|
|
if (real_size > size)
|
|
return -ERANGE;
|
|
|
|
ext_acl = (syno_acl_xattr_header *)buffer;
|
|
ext_entry = ext_acl->a_entries;
|
|
ext_acl->a_version = cpu_to_le16(SYNO_ACL_XATTR_VERSION);
|
|
|
|
for (i = 0; i < acl->a_count; i++, ext_entry++) {
|
|
ret = ace_syno_to_xattr(&(acl->a_entries[i]), ext_entry);
|
|
if (0 > ret)
|
|
return ret;
|
|
}
|
|
return real_size;
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_to_xattr);
|
|
|
|
/*
|
|
* Inode operations of SynoACL
|
|
*/
|
|
int synoacl_op_permission(struct dentry *dentry, int perm)
|
|
{
|
|
int ret;
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (perm & MAY_NOT_BLOCK)
|
|
return -ECHILD;
|
|
|
|
if (inode->i_op->syno_permission)
|
|
ret = inode->i_op->syno_permission(dentry, perm);
|
|
else
|
|
ret = synoacl_mod_permission(dentry, perm);
|
|
|
|
trace_synoacl_permission(dentry, perm, ret);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_permission);
|
|
|
|
int synoacl_op_exec_permission(struct dentry *dentry, struct inode *inode)
|
|
{
|
|
int ret;
|
|
|
|
if (inode->i_op->syno_exec_permission)
|
|
ret = inode->i_op->syno_exec_permission(dentry);
|
|
else
|
|
ret = synoacl_mod_exec_permission(dentry);
|
|
|
|
trace_synoacl_exec_permission(dentry, ret);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_exec_permission);
|
|
|
|
int synoacl_op_archive_bit_change_ok(struct dentry *dentry,
|
|
unsigned int cmd, int tag, int mask)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode->i_op->syno_archive_bit_change_ok)
|
|
return inode->i_op->syno_archive_bit_change_ok(dentry, cmd, tag, mask);
|
|
|
|
return synoacl_mod_archive_bit_change_ok(dentry, cmd, tag, mask);
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_archive_bit_change_ok);
|
|
|
|
/**
|
|
* synoacl_op_setattr_prepare - check if attribute changes to a dentry are allowed
|
|
* @dentry: dentry to check
|
|
* @attr: attributes to change
|
|
*
|
|
* this is corresponding to setattr_prepare(), which was named as inode_change_ok()
|
|
* before v4.9
|
|
*/
|
|
int synoacl_op_setattr_prepare(struct dentry *dentry, struct iattr *attr)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode->i_op->syno_setattr_prepare)
|
|
return inode->i_op->syno_setattr_prepare(dentry, attr);
|
|
|
|
return synoacl_mod_setattr_prepare(dentry, attr);
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_setattr_prepare);
|
|
|
|
int synoacl_op_setattr_post(struct dentry *dentry, struct iattr *attr)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode->i_op->syno_setattr_post)
|
|
return inode->i_op->syno_setattr_post(dentry, attr);
|
|
|
|
return synoacl_mod_setattr_post(dentry, attr);
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_setattr_post);
|
|
|
|
int synoacl_op_may_delete(struct dentry *victim, struct inode *dir)
|
|
{
|
|
int ret;
|
|
|
|
if (dir->i_op->syno_may_delete)
|
|
ret = dir->i_op->syno_may_delete(victim, dir);
|
|
else
|
|
ret = synoacl_mod_may_delete(victim, dir);
|
|
|
|
trace_synoacl_may_delete(victim, dir, ret);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_may_delete);
|
|
|
|
int synoacl_op_may_access(struct dentry *dentry, int mode)
|
|
{
|
|
int ret;
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode->i_op->syno_may_access)
|
|
ret = inode->i_op->syno_may_access(dentry, mode);
|
|
else
|
|
ret = synoacl_mod_may_access(dentry, mode);
|
|
|
|
trace_synoacl_may_access(dentry, mode, ret);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_may_access);
|
|
|
|
int synoacl_op_acl_xattr_get(struct dentry *dentry, int cmd, void *value, size_t size)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode->i_op->syno_acl_xattr_get)
|
|
return inode->i_op->syno_acl_xattr_get(dentry, cmd, value, size);
|
|
|
|
return synoacl_mod_acl_xattr_get(dentry, cmd, value, size);
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_acl_xattr_get);
|
|
|
|
void synoacl_op_to_mode(struct dentry *dentry, struct kstat *stat)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode->i_op->syno_acl_to_mode)
|
|
inode->i_op->syno_acl_to_mode(dentry, stat);
|
|
else
|
|
synoacl_mod_to_mode(dentry, stat);
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_to_mode);
|
|
|
|
int synoacl_op_init(struct dentry *dentry)
|
|
{
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
if (inode->i_op->syno_acl_init)
|
|
return inode->i_op->syno_acl_init(dentry, inode);
|
|
|
|
return synoacl_mod_init(dentry, inode);
|
|
}
|
|
EXPORT_SYMBOL(synoacl_op_init);
|
|
|
|
int synoacl_op_xattr_permission(const char *name, struct dentry *dentry, unsigned int perm)
|
|
{
|
|
int error = 0;
|
|
|
|
if (!name || strcmp(name, SYNO_ACL_XATTR_ACCESS))
|
|
return 0; // skip xattr except ACL.
|
|
|
|
switch (perm) {
|
|
case MAY_READ_PERMISSION:
|
|
if (!IS_SYNOACL(dentry))
|
|
return -EOPNOTSUPP;
|
|
break;
|
|
|
|
case MAY_WRITE_PERMISSION:
|
|
if (!IS_FS_SYNOACL(dentry->d_inode))
|
|
return -EOPNOTSUPP;
|
|
break;
|
|
|
|
default:
|
|
return 0; // invalid parameters, just skip it.
|
|
}
|
|
|
|
error = synoacl_op_permission(dentry, perm);
|
|
if (error)
|
|
return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
syno_acl_entry_from_disk(struct syno_acl_entry *sae, syno_acl_entry_t *bsae)
|
|
{
|
|
unsigned short tag = le16_to_cpu(bsae->e_tag);
|
|
|
|
// ID: user/group/everyone
|
|
if (SYNO_ACL_XATTR_TAG_ID_GROUP & tag) {
|
|
sae->e_tag = SYNO_ACL_GROUP;
|
|
sae->e_id = le32_to_cpu(bsae->e_id);
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_EVERYONE & tag) {
|
|
sae->e_tag = SYNO_ACL_EVERYONE;
|
|
sae->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_USER & tag) {
|
|
sae->e_tag = SYNO_ACL_USER;
|
|
sae->e_id = le32_to_cpu(bsae->e_id);
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_OWNER & tag) {
|
|
sae->e_tag = SYNO_ACL_OWNER;
|
|
sae->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_AUTHENTICATEDUSER & tag) {
|
|
sae->e_tag = SYNO_ACL_AUTHENTICATEDUSER;
|
|
sae->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else if (SYNO_ACL_XATTR_TAG_ID_SYSTEM & tag) {
|
|
sae->e_tag = SYNO_ACL_SYSTEM;
|
|
sae->e_id = SYNO_ACL_UNDEFINED_ID;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
// Allow/Deny
|
|
if (SYNO_ACL_XATTR_TAG_IS_DENY & tag) {
|
|
sae->e_allow = SYNO_ACL_DENY;
|
|
} else if (SYNO_ACL_XATTR_TAG_IS_ALLOW & tag){
|
|
sae->e_allow = SYNO_ACL_ALLOW;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
sae->e_perm = le32_to_cpu(bsae->e_perm);
|
|
sae->e_inherit = le16_to_cpu(bsae->e_inherit);
|
|
sae->e_level = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
syno_acl_entry_to_disk(const struct syno_acl_entry *sae, syno_acl_entry_t *bsae)
|
|
{
|
|
unsigned short tag = 0;
|
|
|
|
//ID: user/group/everyone
|
|
switch(sae->e_tag){
|
|
case SYNO_ACL_GROUP:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_GROUP;
|
|
break;
|
|
case SYNO_ACL_EVERYONE:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_EVERYONE;
|
|
break;
|
|
case SYNO_ACL_USER:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_USER;
|
|
break;
|
|
case SYNO_ACL_OWNER:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_OWNER;
|
|
break;
|
|
case SYNO_ACL_AUTHENTICATEDUSER:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_AUTHENTICATEDUSER;
|
|
break;
|
|
case SYNO_ACL_SYSTEM:
|
|
tag |= SYNO_ACL_XATTR_TAG_ID_SYSTEM;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
// Allow/Deny
|
|
switch(sae->e_allow){
|
|
case SYNO_ACL_DENY:
|
|
tag |= SYNO_ACL_XATTR_TAG_IS_DENY;
|
|
break;
|
|
case SYNO_ACL_ALLOW:
|
|
tag |= SYNO_ACL_XATTR_TAG_IS_ALLOW;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
bsae->e_tag = cpu_to_le16(tag);
|
|
bsae->e_inherit = cpu_to_le16(sae->e_inherit);
|
|
bsae->e_perm = cpu_to_le32(sae->e_perm);
|
|
bsae->e_id = cpu_to_le32(sae->e_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Convert from filesystem to in-memory representation.
|
|
*/
|
|
struct syno_acl *syno_acl_from_disk(const void *value, size_t size)
|
|
{
|
|
int i, count;
|
|
struct syno_acl *acl;
|
|
|
|
if (!value)
|
|
return NULL;
|
|
if (size < sizeof(syno_acl_header_t))
|
|
return ERR_PTR(-EINVAL);
|
|
if ((size - sizeof(syno_acl_header_t)) % sizeof(syno_acl_entry_t))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
count = (size - sizeof(syno_acl_header_t)) / sizeof(syno_acl_entry_t);
|
|
if (count < 0)
|
|
return ERR_PTR(-EINVAL);
|
|
if (count == 0)
|
|
return NULL;
|
|
|
|
if (((syno_acl_header_t *)value)->a_version != cpu_to_le16(SYNO_ACL_VERSION))
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
acl = syno_acl_alloc(count, GFP_NOFS);
|
|
if (!acl)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
value = (char *)value + sizeof(syno_acl_header_t);
|
|
for (i = 0; i < count; i++) {
|
|
if (syno_acl_entry_from_disk(&(acl->a_entries[i]), (syno_acl_entry_t *)value))
|
|
goto fail;
|
|
value = (char *)value + sizeof(syno_acl_entry_t);
|
|
}
|
|
return acl;
|
|
|
|
fail:
|
|
syno_acl_release(acl);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_from_disk);
|
|
|
|
/*
|
|
* Convert from in-memory to filesystem representation.
|
|
*/
|
|
void * syno_acl_to_disk(const struct syno_acl *acl, size_t *size)
|
|
{
|
|
char *ent;
|
|
size_t i;
|
|
syno_acl_header_t *b_acl;
|
|
|
|
*size = sizeof(syno_acl_header_t) + acl->a_count * sizeof(syno_acl_entry_t);
|
|
b_acl = kmalloc(*size, GFP_NOFS);
|
|
if (!b_acl)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
b_acl->a_version = cpu_to_le16(SYNO_ACL_VERSION);
|
|
ent = (char *)b_acl + sizeof(syno_acl_header_t);
|
|
|
|
for (i = 0; i < acl->a_count; i++) {
|
|
if (0 > syno_acl_entry_to_disk(&(acl->a_entries[i]), (syno_acl_entry_t *)ent))
|
|
goto fail;
|
|
ent += sizeof(syno_acl_entry_t);
|
|
}
|
|
|
|
return (char *)b_acl;
|
|
|
|
fail:
|
|
kfree(b_acl);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
EXPORT_SYMBOL(syno_acl_to_disk);
|
|
|
|
static void __forget_cached_syno_acl(struct syno_acl **p)
|
|
{
|
|
struct syno_acl *old;
|
|
|
|
old = xchg(p, ACL_NOT_CACHED);
|
|
if (!is_uncached_syno_acl(old))
|
|
syno_acl_release(old);
|
|
}
|
|
|
|
void forget_cached_syno_acl(struct inode *inode)
|
|
{
|
|
__forget_cached_syno_acl(&inode->i_syno_acl);
|
|
}
|
|
EXPORT_SYMBOL(forget_cached_syno_acl);
|