1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/lib/util/util_ntdb.c
Rusty Russell 02bacf1f95 util: util_ntdb ntdb_fetch_int32/ntdb_store_int32 and ntdb_add_int32_atomic
Similar to the util_tdb versions, but return the error code.

ntdb_add_int32_atomic seems a clearer name than tdb_change_int32_atomic.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2012-06-22 07:35:17 +02:00

347 lines
7.9 KiB
C

/*
Unix SMB/CIFS implementation.
ntdb utility functions
Copyright (C) Rusty Russell 2012
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
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 for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "util_ntdb.h"
#include "lib/param/param.h"
#include "replace.h"
#include "system/filesys.h"
/*
* This handles NTDB_CLEAR_IF_FIRST.
*
* It's a bad idea for new code, but S3 uses it quite a bit.
*/
static enum NTDB_ERROR clear_if_first(int fd, void *unused)
{
/* We hold a lock offset 4 always, so we can tell if anyone else is. */
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 4; /* ACTIVE_LOCK */
fl.l_len = 1;
if (fcntl(fd, F_SETLK, &fl) == 0) {
/* We must be first ones to open it w/ NTDB_CLEAR_IF_FIRST! */
if (ftruncate(fd, 0) != 0) {
return NTDB_ERR_IO;
}
}
fl.l_type = F_RDLCK;
if (fcntl(fd, F_SETLKW, &fl) != 0) {
return NTDB_ERR_IO;
}
return NTDB_SUCCESS;
}
/* We only need these for the CLEAR_IF_FIRST lock. */
static int reacquire_cif_lock(struct ntdb_context *ntdb, bool *fail)
{
struct flock fl;
union ntdb_attribute cif;
cif.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
cif.openhook.base.next = NULL;
if (ntdb_get_attribute(ntdb, &cif) != NTDB_SUCCESS
|| cif.openhook.fn != clear_if_first) {
return 0;
}
/* We hold a lock offset 4 always, so we can tell if anyone else is. */
fl.l_type = F_RDLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 4; /* ACTIVE_LOCK */
fl.l_len = 1;
if (fcntl(ntdb_fd(ntdb), F_SETLKW, &fl) != 0) {
*fail = true;
return -1;
}
return 0;
}
/* You only need this on databases with NTDB_CLEAR_IF_FIRST */
int ntdb_reopen(struct ntdb_context *ntdb)
{
bool unused;
return reacquire_cif_lock(ntdb, &unused);
}
/* You only need to do this if you have NTDB_CLEAR_IF_FIRST databases, and
* the parent will go away before this child. */
int ntdb_reopen_all(void)
{
bool fail = false;
ntdb_foreach(reacquire_cif_lock, &fail);
if (fail)
return -1;
return 0;
}
static void *ntdb_talloc(const void *owner, size_t len, void *priv_data)
{
return talloc_size(owner, len);
}
static void *ntdb_expand(void *old, size_t newlen, void *priv_data)
{
return talloc_realloc_size(NULL, old, newlen);
}
static void ntdb_free(void *old, void *priv_data)
{
talloc_free(old);
}
static int ntdb_destroy(struct ntdb_context *ntdb)
{
ntdb_close(ntdb);
return 0;
}
static void ntdb_log(struct ntdb_context *ntdb,
enum ntdb_log_level level,
enum NTDB_ERROR ecode,
const char *message,
void *unused)
{
int dl;
const char *name = ntdb_name(ntdb);
switch (level) {
case NTDB_LOG_USE_ERROR:
case NTDB_LOG_ERROR:
dl = 0;
break;
case NTDB_LOG_WARNING:
dl = 2;
break;
default:
dl = 0;
}
DEBUG(dl, ("ntdb(%s):%s: %s\n", name ? name : "unnamed",
ntdb_errorstr(ecode), message));
}
struct ntdb_context *ntdb_new(TALLOC_CTX *ctx,
const char *name, int ntdb_flags,
int open_flags, mode_t mode,
union ntdb_attribute *attr,
struct loadparm_context *lp_ctx)
{
union ntdb_attribute log_attr, alloc_attr, open_attr;
struct ntdb_context *ntdb;
if (lp_ctx && !lpcfg_use_mmap(lp_ctx)) {
ntdb_flags |= NTDB_NOMMAP;
}
/* Great hack for speeding testing! */
if (getenv("TDB_NO_FSYNC")) {
ntdb_flags |= NTDB_NOSYNC;
}
log_attr.base.next = attr;
log_attr.base.attr = NTDB_ATTRIBUTE_LOG;
log_attr.log.fn = ntdb_log;
alloc_attr.base.next = &log_attr;
alloc_attr.base.attr = NTDB_ATTRIBUTE_ALLOCATOR;
alloc_attr.alloc.alloc = ntdb_talloc;
alloc_attr.alloc.expand = ntdb_expand;
alloc_attr.alloc.free = ntdb_free;
if (ntdb_flags & NTDB_CLEAR_IF_FIRST) {
log_attr.base.next = &open_attr;
open_attr.openhook.base.attr = NTDB_ATTRIBUTE_OPENHOOK;
open_attr.openhook.base.next = attr;
open_attr.openhook.fn = clear_if_first;
ntdb_flags &= ~NTDB_CLEAR_IF_FIRST;
}
ntdb = ntdb_open(name, ntdb_flags, open_flags, mode, &alloc_attr);
if (!ntdb) {
return NULL;
}
/* We can re-use the tdb's path name for the talloc name */
name = ntdb_name(ntdb);
if (name) {
talloc_set_name_const(ntdb, name);
} else {
talloc_set_name_const(ntdb, "unnamed ntdb");
}
talloc_set_destructor(ntdb, ntdb_destroy);
return talloc_steal(ctx, ntdb);
}
enum NTDB_ERROR ntdb_lock_bystring(struct ntdb_context *ntdb,
const char *keyval)
{
NTDB_DATA key = string_term_ntdb_data(keyval);
return ntdb_chainlock(ntdb, key);
}
void ntdb_unlock_bystring(struct ntdb_context *ntdb, const char *keyval)
{
NTDB_DATA key = string_term_ntdb_data(keyval);
ntdb_chainunlock(ntdb, key);
}
enum NTDB_ERROR ntdb_delete_bystring(struct ntdb_context *ntdb,
const char *keystr)
{
NTDB_DATA key = string_term_ntdb_data(keystr);
return ntdb_delete(ntdb, key);
}
enum NTDB_ERROR ntdb_store_bystring(struct ntdb_context *ntdb,
const char *keystr,
NTDB_DATA data, int nflags)
{
NTDB_DATA key = string_term_ntdb_data(keystr);
return ntdb_store(ntdb, key, data, nflags);
}
enum NTDB_ERROR ntdb_fetch_bystring(struct ntdb_context *ntdb,
const char *keystr,
NTDB_DATA *data)
{
NTDB_DATA key = string_term_ntdb_data(keystr);
return ntdb_fetch(ntdb, key, data);
}
enum NTDB_ERROR ntdb_fetch_int32(struct ntdb_context *ntdb,
const char *keystr, int32_t *val)
{
NTDB_DATA data;
enum NTDB_ERROR err;
err = ntdb_fetch(ntdb, string_term_ntdb_data(keystr), &data);
if (err == NTDB_SUCCESS) {
if (data.dsize != sizeof(*val)) {
err = NTDB_ERR_CORRUPT;
} else {
*val = IVAL(data.dptr,0);
}
talloc_free(data.dptr);
}
return NTDB_SUCCESS;
}
enum NTDB_ERROR ntdb_store_int32(struct ntdb_context *ntdb,
const char *keystr, int32_t val)
{
NTDB_DATA data, key;
int32_t v_store;
SIVAL(&v_store, 0, val);
data = ntdb_mkdata(&v_store, sizeof(v_store));
key = string_term_ntdb_data(keystr);
return ntdb_store(ntdb, key, data, NTDB_REPLACE);
}
enum NTDB_ERROR ntdb_add_int32_atomic(struct ntdb_context *ntdb,
const char *keystr,
int32_t *oldval, int32_t addval)
{
int32_t val;
enum NTDB_ERROR err;
err = ntdb_lock_bystring(ntdb, keystr);
if (err) {
return err;
}
err = ntdb_fetch_int32(ntdb, keystr, &val);
if (err) {
if (err == NTDB_ERR_NOEXIST) {
/* Start with 'old' value */
val = *oldval;
} else {
goto err_out;
}
} else {
/* It worked, set return value (oldval) to tdb data */
*oldval = val;
}
/* Increase value and store for next time. */
val += addval;
err = ntdb_store_int32(ntdb, keystr, val);
err_out:
ntdb_unlock_bystring(ntdb, keystr);
return err;
}
NTSTATUS map_nt_error_from_ntdb(enum NTDB_ERROR err)
{
NTSTATUS result = NT_STATUS_INTERNAL_ERROR;
switch (err) {
case NTDB_SUCCESS:
result = NT_STATUS_OK;
break;
case NTDB_ERR_CORRUPT:
result = NT_STATUS_INTERNAL_DB_CORRUPTION;
break;
case NTDB_ERR_IO:
result = NT_STATUS_UNEXPECTED_IO_ERROR;
break;
case NTDB_ERR_OOM:
result = NT_STATUS_NO_MEMORY;
break;
case NTDB_ERR_EXISTS:
result = NT_STATUS_OBJECT_NAME_COLLISION;
break;
case NTDB_ERR_LOCK:
/*
* NTDB_ERR_LOCK is very broad, we could for example
* distinguish between fcntl locks and invalid lock
* sequences. So NT_STATUS_FILE_LOCK_CONFLICT is a
* compromise.
*/
result = NT_STATUS_FILE_LOCK_CONFLICT;
break;
case NTDB_ERR_NOEXIST:
result = NT_STATUS_NOT_FOUND;
break;
case NTDB_ERR_EINVAL:
result = NT_STATUS_INVALID_PARAMETER;
break;
case NTDB_ERR_RDONLY:
result = NT_STATUS_ACCESS_DENIED;
break;
};
return result;
}