mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
02bacf1f95
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>
347 lines
7.9 KiB
C
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;
|
|
}
|