blob: 9a69f6b35a0ea37b8af2f464e5b0f71447e48381 [file] [log] [blame]
/*
* GPL HEADER START
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 only,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is included
* in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; If not, see
* http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
* GPL HEADER END
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 2012, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
* Lustre is a trademark of Sun Microsystems, Inc.
*
* lustre/obdclass/acl.c
*
* Lustre Access Control List.
*
* Author: Fan Yong <fanyong@clusterfs.com>
*/
#define DEBUG_SUBSYSTEM S_SEC
#include "../include/lu_object.h"
#include "../include/lustre_acl.h"
#include "../include/lustre_eacl.h"
#include "../include/obd_support.h"
#ifdef CONFIG_FS_POSIX_ACL
#define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
enum {
ES_UNK = 0, /* unknown stat */
ES_UNC = 1, /* ACL entry is not changed */
ES_MOD = 2, /* ACL entry is modified */
ES_ADD = 3, /* ACL entry is added */
ES_DEL = 4 /* ACL entry is deleted */
};
static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
ext_acl_xattr_entry *s)
{
d->e_tag = le16_to_cpu(s->e_tag);
d->e_perm = le16_to_cpu(s->e_perm);
d->e_id = le32_to_cpu(s->e_id);
d->e_stat = le32_to_cpu(s->e_stat);
}
static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
ext_acl_xattr_entry *s)
{
d->e_tag = cpu_to_le16(s->e_tag);
d->e_perm = cpu_to_le16(s->e_perm);
d->e_id = cpu_to_le32(s->e_id);
d->e_stat = cpu_to_le32(s->e_stat);
}
static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
posix_acl_xattr_entry *s)
{
d->e_tag = le16_to_cpu(s->e_tag);
d->e_perm = le16_to_cpu(s->e_perm);
d->e_id = le32_to_cpu(s->e_id);
}
static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
posix_acl_xattr_entry *s)
{
d->e_tag = cpu_to_le16(s->e_tag);
d->e_perm = cpu_to_le16(s->e_perm);
d->e_id = cpu_to_le32(s->e_id);
}
/* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
int old_count, int new_count)
{
int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
posix_acl_xattr_header *new;
if (unlikely(old_count <= new_count))
return old_size;
OBD_ALLOC(new, new_size);
if (unlikely(new == NULL))
return -ENOMEM;
memcpy(new, *header, new_size);
OBD_FREE(*header, old_size);
*header = new;
return new_size;
}
/* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
int old_count)
{
int ext_count = le32_to_cpu((*header)->a_count);
int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
int old_size = CFS_ACL_XATTR_SIZE(old_count, ext_acl_xattr);
ext_acl_xattr_header *new;
if (unlikely(old_count <= ext_count))
return 0;
OBD_ALLOC(new, ext_size);
if (unlikely(new == NULL))
return -ENOMEM;
memcpy(new, *header, ext_size);
OBD_FREE(*header, old_size);
*header = new;
return 0;
}
/*
* Generate new extended ACL based on the posix ACL.
*/
ext_acl_xattr_header *
lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
{
int count, i, esize;
ext_acl_xattr_header *new;
if (unlikely(size < 0))
return ERR_PTR(-EINVAL);
else if (!size)
count = 0;
else
count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
OBD_ALLOC(new, esize);
if (unlikely(new == NULL))
return ERR_PTR(-ENOMEM);
new->a_count = cpu_to_le32(count);
for (i = 0; i < count; i++) {
new->a_entries[i].e_tag = header->a_entries[i].e_tag;
new->a_entries[i].e_perm = header->a_entries[i].e_perm;
new->a_entries[i].e_id = header->a_entries[i].e_id;
new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
}
return new;
}
EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
/*
* Filter out the "nobody" entries in the posix ACL.
*/
int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
posix_acl_xattr_header **out)
{
int count, i, j, rc = 0;
__u32 id;
posix_acl_xattr_header *new;
if (!size)
return 0;
if (size < sizeof(*new))
return -EINVAL;
OBD_ALLOC(new, size);
if (unlikely(new == NULL))
return -ENOMEM;
new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
for (i = 0, j = 0; i < count; i++) {
id = le32_to_cpu(header->a_entries[i].e_id);
switch (le16_to_cpu(header->a_entries[i].e_tag)) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
if (id != ACL_UNDEFINED_ID) {
rc = -EIO;
goto _out;
}
memcpy(&new->a_entries[j++], &header->a_entries[i],
sizeof(posix_acl_xattr_entry));
break;
case ACL_USER:
if (id != NOBODY_UID)
memcpy(&new->a_entries[j++],
&header->a_entries[i],
sizeof(posix_acl_xattr_entry));
break;
case ACL_GROUP:
if (id != NOBODY_GID)
memcpy(&new->a_entries[j++],
&header->a_entries[i],
sizeof(posix_acl_xattr_entry));
break;
default:
rc = -EIO;
goto _out;
}
}
/* free unused space. */
rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
if (rc >= 0) {
size = rc;
*out = new;
rc = 0;
}
_out:
if (rc) {
OBD_FREE(new, size);
size = rc;
}
return size;
}
EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
/*
* Release the posix ACL space.
*/
void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
{
OBD_FREE(header, size);
}
EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
/*
* Release the extended ACL space.
*/
void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
{
OBD_FREE(header, CFS_ACL_XATTR_SIZE(le32_to_cpu(header->a_count), \
ext_acl_xattr));
}
EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
static ext_acl_xattr_entry *
lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
posix_acl_xattr_entry *entry, int *pos)
{
int once, start, end, i, j, count = le32_to_cpu(header->a_count);
once = 0;
start = *pos;
end = count;
again:
for (i = start; i < end; i++) {
if (header->a_entries[i].e_tag == entry->e_tag &&
header->a_entries[i].e_id == entry->e_id) {
j = i;
if (++i >= count)
i = 0;
*pos = i;
return &header->a_entries[j];
}
}
if (!once) {
once = 1;
start = 0;
end = *pos;
goto again;
}
return NULL;
}
/*
* Merge the posix ACL and the extended ACL into new posix ACL.
*/
int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
ext_acl_xattr_header *ext_header,
posix_acl_xattr_header **out)
{
int posix_count, posix_size, i, j;
int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
posix_acl_xattr_header *new;
ext_acl_xattr_entry *ee, ae;
lustre_posix_acl_cpu_to_le(&pe, &pe);
ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
/* there are only base ACL entries at most. */
posix_count = 3;
posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
OBD_ALLOC(new, posix_size);
if (unlikely(new == NULL))
return -ENOMEM;
new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
for (i = 0, j = 0; i < ext_count; i++) {
lustre_ext_acl_le_to_cpu(&ae,
&ext_header->a_entries[i]);
switch (ae.e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_OTHER:
if (ae.e_id != ACL_UNDEFINED_ID) {
rc = -EIO;
goto _out;
}
if (ae.e_stat != ES_DEL) {
new->a_entries[j].e_tag =
ext_header->a_entries[i].e_tag;
new->a_entries[j].e_perm =
ext_header->a_entries[i].e_perm;
new->a_entries[j++].e_id =
ext_header->a_entries[i].e_id;
}
break;
case ACL_MASK:
case ACL_USER:
case ACL_GROUP:
if (ae.e_stat == ES_DEL)
break;
default:
rc = -EIO;
goto _out;
}
}
} else {
/* maybe there are valid ACL_USER or ACL_GROUP entries in the
* original server-side ACL, they are regarded as ES_UNC stat.*/
int ori_posix_count;
if (unlikely(size < 0))
return -EINVAL;
else if (!size)
ori_posix_count = 0;
else
ori_posix_count =
CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
posix_count = ori_posix_count + ext_count;
posix_size =
CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
OBD_ALLOC(new, posix_size);
if (unlikely(new == NULL))
return -ENOMEM;
new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
/* 1. process the unchanged ACL entries
* in the original server-side ACL. */
pos = 0;
for (i = 0, j = 0; i < ori_posix_count; i++) {
ee = lustre_ext_acl_xattr_search(ext_header,
&posix_header->a_entries[i], &pos);
if (ee == NULL)
memcpy(&new->a_entries[j++],
&posix_header->a_entries[i],
sizeof(posix_acl_xattr_entry));
}
/* 2. process the non-deleted entries
* from client-side extended ACL. */
for (i = 0; i < ext_count; i++) {
if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
ES_DEL) {
new->a_entries[j].e_tag =
ext_header->a_entries[i].e_tag;
new->a_entries[j].e_perm =
ext_header->a_entries[i].e_perm;
new->a_entries[j++].e_id =
ext_header->a_entries[i].e_id;
}
}
}
/* free unused space. */
rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
if (rc >= 0) {
posix_size = rc;
*out = new;
rc = 0;
}
_out:
if (rc) {
OBD_FREE(new, posix_size);
posix_size = rc;
}
return posix_size;
}
EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
/*
* Merge the posix ACL and the extended ACL into new extended ACL.
*/
ext_acl_xattr_header *
lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
ext_acl_xattr_header *ext_header)
{
int ori_ext_count, posix_count, ext_count, ext_size;
int i, j, pos = 0, rc = 0;
posix_acl_xattr_entry pae;
ext_acl_xattr_header *new;
ext_acl_xattr_entry *ee, eae;
if (unlikely(size < 0))
return ERR_PTR(-EINVAL);
else if (!size)
posix_count = 0;
else
posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
ori_ext_count = le32_to_cpu(ext_header->a_count);
ext_count = posix_count + ori_ext_count;
ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
OBD_ALLOC(new, ext_size);
if (unlikely(new == NULL))
return ERR_PTR(-ENOMEM);
for (i = 0, j = 0; i < posix_count; i++) {
lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
switch (pae.e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
if (pae.e_id != ACL_UNDEFINED_ID) {
rc = -EIO;
goto out;
}
case ACL_USER:
/* ignore "nobody" entry. */
if (pae.e_id == NOBODY_UID)
break;
new->a_entries[j].e_tag =
posix_header->a_entries[i].e_tag;
new->a_entries[j].e_perm =
posix_header->a_entries[i].e_perm;
new->a_entries[j].e_id =
posix_header->a_entries[i].e_id;
ee = lustre_ext_acl_xattr_search(ext_header,
&posix_header->a_entries[i], &pos);
if (ee) {
if (posix_header->a_entries[i].e_perm !=
ee->e_perm)
/* entry modified. */
ee->e_stat =
new->a_entries[j++].e_stat =
cpu_to_le32(ES_MOD);
else
/* entry unchanged. */
ee->e_stat =
new->a_entries[j++].e_stat =
cpu_to_le32(ES_UNC);
} else {
/* new entry. */
new->a_entries[j++].e_stat =
cpu_to_le32(ES_ADD);
}
break;
case ACL_GROUP:
/* ignore "nobody" entry. */
if (pae.e_id == NOBODY_GID)
break;
new->a_entries[j].e_tag =
posix_header->a_entries[i].e_tag;
new->a_entries[j].e_perm =
posix_header->a_entries[i].e_perm;
new->a_entries[j].e_id =
posix_header->a_entries[i].e_id;
ee = lustre_ext_acl_xattr_search(ext_header,
&posix_header->a_entries[i], &pos);
if (ee) {
if (posix_header->a_entries[i].e_perm !=
ee->e_perm)
/* entry modified. */
ee->e_stat =
new->a_entries[j++].e_stat =
cpu_to_le32(ES_MOD);
else
/* entry unchanged. */
ee->e_stat =
new->a_entries[j++].e_stat =
cpu_to_le32(ES_UNC);
} else {
/* new entry. */
new->a_entries[j++].e_stat =
cpu_to_le32(ES_ADD);
}
break;
default:
rc = -EIO;
goto out;
}
}
/* process deleted entries. */
for (i = 0; i < ori_ext_count; i++) {
lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
if (eae.e_stat == ES_UNK) {
/* ignore "nobody" entry. */
if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
(eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
continue;
new->a_entries[j].e_tag =
ext_header->a_entries[i].e_tag;
new->a_entries[j].e_perm =
ext_header->a_entries[i].e_perm;
new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
}
}
new->a_count = cpu_to_le32(j);
/* free unused space. */
rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
out:
if (rc) {
OBD_FREE(new, ext_size);
new = ERR_PTR(rc);
}
return new;
}
EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
#endif