blob: 7f395299797bed4bf877f8c502f11dd6c9365218 [file] [log] [blame]
#include "config.h"
#include "abstract_mem.h"
#include "fsal_types.h"
#include "hashtable.h"
#include "log.h"
#include "nfs4_acls.h"
#include "city.h"
#include "common_utils.h"
pool_t *fsal_acl_pool;
static int fsal_acl_hash_both(hash_parameter_t *, struct gsh_buffdesc *,
uint32_t *, uint64_t *);
static int compare_fsal_acl(struct gsh_buffdesc *, struct gsh_buffdesc *);
/* DEFAULT PARAMETERS for hash table */
static hash_parameter_t fsal_acl_hash_config = {
.index_size = 67,
.hash_func_key = NULL,
.hash_func_rbt = NULL,
.hash_func_both = fsal_acl_hash_both,
.compare_key = compare_fsal_acl,
.ht_name = "ACL Table",
.flags = HT_FLAG_CACHE,
.ht_log_component = COMPONENT_NFS_V4_ACL
};
static hash_table_t *fsal_acl_hash;
/* hash table functions */
static int fsal_acl_hash_both(hash_parameter_t *hparam,
struct gsh_buffdesc *key, uint32_t *index,
uint64_t *rbthash)
{
*rbthash = CityHash64(key->addr, key->len);
*index = *rbthash % hparam->index_size;
return 1;
}
static int compare_fsal_acl(struct gsh_buffdesc *key1,
struct gsh_buffdesc *keya)
{
if (key1->len != keya->len)
return -1;
return memcmp(key1->addr, keya->addr, key1->len);
}
fsal_ace_t *nfs4_ace_alloc(int nace)
{
fsal_ace_t *ace = NULL;
ace = gsh_calloc(nace, sizeof(fsal_ace_t));
return ace;
}
fsal_acl_t *nfs4_acl_alloc()
{
return pool_alloc(fsal_acl_pool);
}
void nfs4_ace_free(fsal_ace_t *ace)
{
if (!ace)
return;
LogDebug(COMPONENT_NFS_V4_ACL, "free ace %p", ace);
gsh_free(ace);
}
void nfs4_acl_free(fsal_acl_t *acl)
{
if (!acl)
return;
if (acl->aces)
nfs4_ace_free(acl->aces);
pool_free(fsal_acl_pool, acl);
}
void nfs4_acl_entry_inc_ref(fsal_acl_t *acl)
{
/* Increase ref counter */
PTHREAD_RWLOCK_wrlock(&acl->lock);
acl->ref++;
LogDebug(COMPONENT_NFS_V4_ACL, "(acl, ref) = (%p, %u)", acl, acl->ref);
PTHREAD_RWLOCK_unlock(&acl->lock);
}
/* Should be called with lock held. */
static void nfs4_acl_entry_dec_ref(fsal_acl_t *acl)
{
/* Decrease ref counter */
acl->ref--;
LogDebug(COMPONENT_NFS_V4_ACL, "(acl, ref) = (%p, %u)", acl, acl->ref);
}
fsal_acl_t *nfs4_acl_new_entry(fsal_acl_data_t *acldata,
fsal_acl_status_t *status)
{
fsal_acl_t *acl = NULL;
struct gsh_buffdesc key;
struct gsh_buffdesc value;
int rc;
struct hash_latch latch;
/* Set the return default to NFS_V4_ACL_SUCCESS */
*status = NFS_V4_ACL_SUCCESS;
key.addr = acldata->aces;
key.len = acldata->naces * sizeof(fsal_ace_t);
/* Check if the entry already exists */
rc = hashtable_getlatch(fsal_acl_hash, &key, &value, true, &latch);
if (rc == HASHTABLE_SUCCESS) {
/* Entry is already in the cache, do not add it */
acl = value.addr;
*status = NFS_V4_ACL_EXISTS;
nfs4_ace_free(acldata->aces);
nfs4_acl_entry_inc_ref(acl);
hashtable_releaselatched(fsal_acl_hash, &latch);
return acl;
}
/* Any other result other than no such key is an error */
if (rc != HASHTABLE_ERROR_NO_SUCH_KEY) {
*status = NFS_V4_ACL_INIT_ENTRY_FAILED;
nfs4_ace_free(acldata->aces);
return NULL;
}
/* Adding the entry in the cache */
acl = nfs4_acl_alloc();
if (pthread_rwlock_init(&(acl->lock), NULL) != 0) {
nfs4_acl_free(acl);
LogCrit(COMPONENT_NFS_V4_ACL,
"New ACL RW lock init returned %d (%s)", errno,
strerror(errno));
*status = NFS_V4_ACL_INIT_ENTRY_FAILED;
nfs4_ace_free(acldata->aces);
hashtable_releaselatched(fsal_acl_hash, &latch);
return NULL;
}
acl->naces = acldata->naces;
acl->aces = acldata->aces;
acl->ref = 1; /* We give out one reference */
/* Build the value */
value.addr = acl;
value.len = sizeof(fsal_acl_t);
rc = hashtable_setlatched(fsal_acl_hash, &key, &value, &latch,
HASHTABLE_SET_HOW_SET_NO_OVERWRITE, NULL,
NULL);
if (rc != HASHTABLE_SUCCESS) {
/* Put the entry back in its pool */
nfs4_acl_free(acl);
LogWarn(COMPONENT_NFS_V4_ACL,
"New ACL entry could not be added to hash, rc=%s",
hash_table_err_to_str(rc));
*status = NFS_V4_ACL_HASH_SET_ERROR;
return NULL;
}
return acl;
}
fsal_acl_status_t nfs4_acl_release_entry(fsal_acl_t *acl)
{
struct gsh_buffdesc key, old_key;
struct gsh_buffdesc old_value;
int rc;
struct hash_latch latch;
fsal_acl_status_t status = NFS_V4_ACL_SUCCESS;
if (!acl)
return status;
PTHREAD_RWLOCK_wrlock(&acl->lock);
if (acl->ref > 1) {
nfs4_acl_entry_dec_ref(acl);
PTHREAD_RWLOCK_unlock(&acl->lock);
return status;
} else
LogDebug(COMPONENT_NFS_V4_ACL, "Free ACL %p", acl);
key.addr = acl->aces;
key.len = acl->naces * sizeof(fsal_ace_t);
PTHREAD_RWLOCK_unlock(&acl->lock);
/* Get the hash table entry and hold latch */
rc = hashtable_getlatch(fsal_acl_hash, &key, &old_value, true, &latch);
switch (rc) {
case HASHTABLE_ERROR_NO_SUCH_KEY:
hashtable_releaselatched(fsal_acl_hash, &latch);
return status;
case HASHTABLE_SUCCESS:
PTHREAD_RWLOCK_wrlock(&acl->lock);
nfs4_acl_entry_dec_ref(acl);
if (acl->ref != 0) {
/* Did not actually release last reference */
hashtable_releaselatched(fsal_acl_hash, &latch);
PTHREAD_RWLOCK_unlock(&acl->lock);
return status;
}
/* use the key to delete the entry */
hashtable_deletelatched(fsal_acl_hash, &key, &latch,
&old_key, &old_value);
/* Release the latch */
hashtable_releaselatched(fsal_acl_hash, &latch);
break;
default:
LogCrit(COMPONENT_NFS_V4_ACL,
"ACL entry could not be deleted, status=%s",
hash_table_err_to_str(rc));
return NFS_V4_ACL_ERROR;
}
/* Sanity check: old_value.addr is expected to be equal to acl,
* and is released later in this function */
assert(old_value.addr == acl);
PTHREAD_RWLOCK_unlock(&acl->lock);
/* Release acl */
nfs4_acl_free(acl);
return status;
}
static void nfs4_acls_test(void)
{
int i = 0;
fsal_acl_data_t acldata, acldata2;
fsal_ace_t *ace = NULL;
fsal_acl_t *acl = NULL;
fsal_acl_status_t status;
acldata.naces = 3;
acldata.aces = nfs4_ace_alloc(3);
LogDebug(COMPONENT_NFS_V4_ACL, "acldata.aces = %p", acldata.aces);
ace = acldata.aces;
for (i = 0; i < 3; i++) {
ace->type = i;
ace->perm = i;
ace->flag = i;
ace->who.uid = i;
ace++;
}
acl = nfs4_acl_new_entry(&acldata, &status);
PTHREAD_RWLOCK_rdlock(&acl->lock);
LogDebug(COMPONENT_NFS_V4_ACL, "acl = %p, ref = %u, status = %u", acl,
acl->ref, status);
PTHREAD_RWLOCK_unlock(&acl->lock);
acldata2.naces = 3;
acldata2.aces = nfs4_ace_alloc(3);
LogDebug(COMPONENT_NFS_V4_ACL, "acldata2.aces = %p", acldata2.aces);
ace = acldata2.aces;
for (i = 0; i < 3; i++) {
ace->type = i;
ace->perm = i;
ace->flag = i;
ace->who.uid = i;
ace++;
}
acl = nfs4_acl_new_entry(&acldata2, &status);
PTHREAD_RWLOCK_rdlock(&acl->lock);
LogDebug(COMPONENT_NFS_V4_ACL,
"re-access: acl = %p, ref = %u, status = %u", acl, acl->ref,
status);
PTHREAD_RWLOCK_unlock(&acl->lock);
status = nfs4_acl_release_entry(acl);
PTHREAD_RWLOCK_rdlock(&acl->lock);
LogDebug(COMPONENT_NFS_V4_ACL,
"release: acl = %p, ref = %u, status = %u", acl, acl->ref,
status);
PTHREAD_RWLOCK_unlock(&acl->lock);
status = nfs4_acl_release_entry(acl);
}
int nfs4_acls_init(void)
{
LogDebug(COMPONENT_NFS_V4_ACL, "Initialize NFSv4 ACLs");
LogDebug(COMPONENT_NFS_V4_ACL,
"sizeof(fsal_ace_t)=%zu, sizeof(fsal_acl_t)=%zu",
sizeof(fsal_ace_t), sizeof(fsal_acl_t));
/* Initialize memory pool of ACLs. */
fsal_acl_pool = pool_basic_init("acl_pool", sizeof(fsal_acl_t));
/* Create hash table. */
fsal_acl_hash = hashtable_init(&fsal_acl_hash_config);
if (!fsal_acl_hash) {
LogCrit(COMPONENT_NFS_V4_ACL,
"ERROR creating hash table for NFSv4 ACLs");
return NFS_V4_ACL_INTERNAL_ERROR;
}
nfs4_acls_test();
return NFS_V4_ACL_SUCCESS;
}