/* * Copyright (C) 2006-2010 Red Hat, Inc. * * Author: Steven Dake * * This file is part of libqb. * * libqb is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * libqb 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libqb. If not, see . */ #include "os_base.h" #include #include enum QB_HDB_HANDLE_STATE { QB_HDB_HANDLE_STATE_EMPTY, QB_HDB_HANDLE_STATE_PENDINGREMOVAL, QB_HDB_HANDLE_STATE_ACTIVE }; static void qb_hdb_create_first_run(struct qb_hdb *hdb) { if (hdb->first_run == QB_TRUE) { hdb->first_run = QB_FALSE; qb_atomic_init(); hdb->handles = qb_array_create(32, sizeof(struct qb_hdb_handle)); } } void qb_hdb_create(struct qb_hdb *hdb) { memset(hdb, 0, sizeof(struct qb_hdb)); hdb->first_run = QB_TRUE; qb_hdb_create_first_run(hdb); } void qb_hdb_destroy(struct qb_hdb *hdb) { qb_array_free(hdb->handles); memset(hdb, 0, sizeof(struct qb_hdb)); } int32_t qb_hdb_handle_create(struct qb_hdb *hdb, int32_t instance_size, qb_handle_t * handle_id_out) { int32_t handle; int32_t res = 0; int32_t check; int32_t found = QB_FALSE; void *instance; int32_t i; struct qb_hdb_handle *entry = NULL; int32_t handle_count; qb_hdb_create_first_run(hdb); handle_count = qb_atomic_int_get(&hdb->handle_count); for (handle = 0; handle < handle_count; handle++) { if (qb_array_index(hdb->handles, handle, (void**)&entry) == 0 && entry->state == QB_HDB_HANDLE_STATE_EMPTY) { found = QB_TRUE; qb_atomic_int_inc(&entry->ref_count); break; } } if (found == QB_FALSE) { res = qb_array_grow(hdb->handles, handle_count + 1U); if (res != 0) { return res; } res = qb_array_index(hdb->handles, handle_count, (void **)&entry); if (res != 0) { return res; } /* NB: qb_array_grow above guarantees that handle_count will not overflow INT32_MAX */ qb_atomic_int_inc((int32_t *)&hdb->handle_count); } instance = malloc(instance_size); if (instance == 0) { return -ENOMEM; } /* * Make sure just positive integers are used for the integrity(?) * checks within 2^32 address space, if we miss 200 times in a row * (just 0 is concerned per specification of random), the PRNG may be * broken -> the value is unspecified, subject of stack allocation. */ for (i = 0; i < 200; i++) { check = random(); if (check > 0) { break; /* covers also check == UINT32_MAX */ } } memset(instance, 0, instance_size); entry->state = QB_HDB_HANDLE_STATE_ACTIVE; entry->instance = instance; entry->ref_count = 1; entry->check = check; *handle_id_out = (((uint64_t) (check)) << 32) | handle; return res; } int32_t qb_hdb_handle_get(struct qb_hdb * hdb, qb_handle_t handle_in, void **instance) { int32_t check = handle_in >> 32; int32_t handle = handle_in & UINT32_MAX; struct qb_hdb_handle *entry; int32_t handle_count; qb_hdb_create_first_run(hdb); *instance = NULL; handle_count = qb_atomic_int_get(&hdb->handle_count); if (handle >= handle_count) { return (-EBADF); } if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 || entry->state != QB_HDB_HANDLE_STATE_ACTIVE) { return (-EBADF); } if (check != (int32_t) UINT32_MAX && check != entry->check) { return (-EBADF); } qb_atomic_int_inc(&entry->ref_count); *instance = entry->instance; return (0); } int32_t qb_hdb_handle_get_always(struct qb_hdb * hdb, qb_handle_t handle_in, void **instance) { return qb_hdb_handle_get(hdb, handle_in, instance); } int32_t qb_hdb_handle_put(struct qb_hdb * hdb, qb_handle_t handle_in) { int32_t check = handle_in >> 32; int32_t handle = handle_in & UINT32_MAX; struct qb_hdb_handle *entry; int32_t handle_count; qb_hdb_create_first_run(hdb); handle_count = qb_atomic_int_get(&hdb->handle_count); if (handle >= handle_count) { return (-EBADF); } if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 || (check != (int32_t) UINT32_MAX && check != entry->check)) { return (-EBADF); } if (qb_atomic_int_dec_and_test(&entry->ref_count)) { if (hdb->destructor) { hdb->destructor(entry->instance); } free(entry->instance); memset(entry, 0, sizeof(struct qb_hdb_handle)); } return (0); } int32_t qb_hdb_handle_destroy(struct qb_hdb * hdb, qb_handle_t handle_in) { int32_t check = handle_in >> 32; int32_t handle = handle_in & UINT32_MAX; int32_t res; struct qb_hdb_handle *entry; int32_t handle_count; qb_hdb_create_first_run(hdb); handle_count = qb_atomic_int_get(&hdb->handle_count); if (handle >= handle_count) { return (-EBADF); } if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 || (check != (int32_t) UINT32_MAX && check != entry->check)) { return (-EBADF); } entry->state = QB_HDB_HANDLE_STATE_PENDINGREMOVAL; res = qb_hdb_handle_put(hdb, handle_in); return (res); } int32_t qb_hdb_handle_refcount_get(struct qb_hdb * hdb, qb_handle_t handle_in) { int32_t check = handle_in >> 32; int32_t handle = handle_in & UINT32_MAX; struct qb_hdb_handle *entry; int32_t handle_count; int32_t refcount = 0; qb_hdb_create_first_run(hdb); handle_count = qb_atomic_int_get(&hdb->handle_count); if (handle >= handle_count) { return (-EBADF); } if (qb_array_index(hdb->handles, handle, (void **)&entry) != 0 || (check != (int32_t) UINT32_MAX && check != entry->check)) { return (-EBADF); } refcount = qb_atomic_int_get(&entry->ref_count); return (refcount); } void qb_hdb_iterator_reset(struct qb_hdb *hdb) { hdb->iterator = 0; } int32_t qb_hdb_iterator_next(struct qb_hdb *hdb, void **instance, qb_handle_t * handle) { int32_t res = -1; uint64_t checker; struct qb_hdb_handle *entry; uint32_t handle_count; handle_count = qb_atomic_int_get(&hdb->handle_count); while (hdb->iterator < handle_count) { res = qb_array_index(hdb->handles, hdb->iterator, (void **)&entry); if (res != 0) { break; } checker = (uint64_t) (entry->check); *handle = (checker << 32) | hdb->iterator; res = qb_hdb_handle_get(hdb, *handle, instance); hdb->iterator += 1; if (res == 0) { break; } } return (res); } uint32_t qb_hdb_base_convert(qb_handle_t handle) { return (handle & UINT32_MAX); } uint64_t qb_hdb_nocheck_convert(uint32_t handle) { uint64_t retvalue = ((uint64_t) UINT32_MAX) << 32 | handle; return (retvalue); }