[PATCH] Added tdb code from latest cvs version in the samba tree
This commit is contained in:
parent
e092920616
commit
5ac4a56b39
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Makefile for tdb directory
|
||||
#
|
||||
|
||||
CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
|
||||
CC = gcc
|
||||
|
||||
PROGS = tdbtest tdbtool tdbtorture
|
||||
TDB_OBJ = tdb.o spinlock.o
|
||||
|
||||
default: $(PROGS)
|
||||
|
||||
tdbtest: tdbtest.o $(TDB_OBJ)
|
||||
$(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm
|
||||
|
||||
tdbtool: tdbtool.o $(TDB_OBJ)
|
||||
$(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ)
|
||||
|
||||
tdbtorture: tdbtorture.o $(TDB_OBJ)
|
||||
$(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
|
||||
|
||||
tdbdump: tdbdump.o $(TDB_OBJ)
|
||||
$(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ)
|
||||
|
||||
tdbbackup: tdbbackup.o $(TDB_OBJ)
|
||||
$(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ)
|
||||
|
||||
clean:
|
||||
rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
|
|
@ -0,0 +1,167 @@
|
|||
tdb - a trivial database system
|
||||
tridge@linuxcare.com December 1999
|
||||
==================================
|
||||
|
||||
This is a simple database API. It was inspired by the realisation that
|
||||
in Samba we have several ad-hoc bits of code that essentially
|
||||
implement small databases for sharing structures between parts of
|
||||
Samba. As I was about to add another I realised that a generic
|
||||
database module was called for to replace all the ad-hoc bits.
|
||||
|
||||
I based the interface on gdbm. I couldn't use gdbm as we need to be
|
||||
able to have multiple writers to the databases at one time.
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
add HAVE_MMAP=1 to use mmap instead of read/write
|
||||
add TDB_DEBUG=1 for verbose debug info
|
||||
add NOLOCK=1 to disable locking code
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
|
||||
identical operations via tdb and gdbm then make sure the result is the
|
||||
same
|
||||
|
||||
Also included is tdbtool, which allows simple database manipulation
|
||||
on the commandline.
|
||||
|
||||
tdbtest and tdbtool are not built as part of Samba, but are included
|
||||
for completeness.
|
||||
|
||||
Interface
|
||||
---------
|
||||
|
||||
The interface is very similar to gdbm except for the following:
|
||||
|
||||
- different open interface. The tdb_open call is more similar to a
|
||||
traditional open()
|
||||
- no tdbm_reorganise() function
|
||||
- no tdbm_sync() function. No operations are cached in the library anyway
|
||||
- added a tdb_traverse() function for traversing the whole database
|
||||
|
||||
A general rule for using tdb is that the caller frees any returned
|
||||
TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA
|
||||
return value called p. This is the same as gdbm.
|
||||
|
||||
here is a full list of tdb functions with brief descriptions.
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
|
||||
int open_flags, mode_t mode)
|
||||
|
||||
open the database, creating it if necessary
|
||||
|
||||
The open_flags and mode are passed straight to the open call on the database
|
||||
file. A flags value of O_WRONLY is invalid
|
||||
|
||||
The hash size is advisory, use zero for a default value.
|
||||
|
||||
return is NULL on error
|
||||
|
||||
possible tdb_flags are:
|
||||
TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open
|
||||
TDB_INTERNAL - don't use a file, instaed store the data in
|
||||
memory. The filename is ignored in this case.
|
||||
TDB_NOLOCK - don't do any locking
|
||||
TDB_NOMMAP - don't use mmap
|
||||
|
||||
----------------------------------------------------------------------
|
||||
char *tdb_error(TDB_CONTEXT *tdb);
|
||||
|
||||
return a error string for the last tdb error
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_close(TDB_CONTEXT *tdb);
|
||||
|
||||
close a database
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf);
|
||||
|
||||
update an entry in place - this only works if the new data size
|
||||
is <= the old data size and the key exists.
|
||||
on failure return -1
|
||||
|
||||
----------------------------------------------------------------------
|
||||
TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
||||
fetch an entry in the database given a key
|
||||
if the return value has a null dptr then a error occurred
|
||||
|
||||
caller must free the resulting data
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
||||
check if an entry in the database exists
|
||||
|
||||
note that 1 is returned if the key is found and 0 is returned if not found
|
||||
this doesn't match the conventions in the rest of this module, but is
|
||||
compatible with gdbm
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb,
|
||||
TDB_DATA key, TDB_DATA dbuf, void *state), void *state);
|
||||
|
||||
traverse the entire database - calling fn(tdb, key, data, state) on each
|
||||
element.
|
||||
|
||||
return -1 on error or the record count traversed
|
||||
|
||||
if fn is NULL then it is not called
|
||||
|
||||
a non-zero return value from fn() indicates that the traversal should stop
|
||||
|
||||
----------------------------------------------------------------------
|
||||
TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
|
||||
|
||||
find the first entry in the database and return its key
|
||||
|
||||
the caller must free the returned data
|
||||
|
||||
----------------------------------------------------------------------
|
||||
TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
||||
find the next entry in the database, returning its key
|
||||
|
||||
the caller must free the returned data
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
||||
delete an entry in the database given a key
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
|
||||
|
||||
store an element in the database, replacing any existing element
|
||||
with the same key
|
||||
|
||||
If flag==TDB_INSERT then don't overwrite an existing entry
|
||||
If flag==TDB_MODIFY then don't create a new entry
|
||||
|
||||
return 0 on success, -1 on failure
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_writelock(TDB_CONTEXT *tdb);
|
||||
|
||||
lock the database. If we already have it locked then don't do anything
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_writeunlock(TDB_CONTEXT *tdb);
|
||||
unlock the database
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
||||
lock one hash chain. This is meant to be used to reduce locking
|
||||
contention - it cannot guarantee how many records will be locked
|
||||
|
||||
----------------------------------------------------------------------
|
||||
int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
||||
unlock one hash chain
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba database functions
|
||||
Copyright (C) Anton Blanchard 2001
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#if STANDALONE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include "tdb.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
#define DEBUG
|
||||
#else
|
||||
#include "includes.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_SPINLOCKS
|
||||
|
||||
/*
|
||||
* ARCH SPECIFIC
|
||||
*/
|
||||
|
||||
#if defined(SPARC_SPINLOCKS)
|
||||
|
||||
static inline int __spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
asm volatile("ldstub [%1], %0"
|
||||
: "=r" (result)
|
||||
: "r" (lock)
|
||||
: "memory");
|
||||
|
||||
return (result == 0) ? 0 : EBUSY;
|
||||
}
|
||||
|
||||
static inline void __spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
asm volatile("":::"memory");
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline void __spin_lock_init(spinlock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline int __spin_is_locked(spinlock_t *lock)
|
||||
{
|
||||
return (*lock != 0);
|
||||
}
|
||||
|
||||
#elif defined(POWERPC_SPINLOCKS)
|
||||
|
||||
static inline int __spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"1: lwarx %0,0,%1\n\
|
||||
cmpwi 0,%0,0\n\
|
||||
li %0,0\n\
|
||||
bne- 2f\n\
|
||||
li %0,1\n\
|
||||
stwcx. %0,0,%1\n\
|
||||
bne- 1b\n\
|
||||
isync\n\
|
||||
2:" : "=&r"(result)
|
||||
: "r"(lock)
|
||||
: "cr0", "memory");
|
||||
|
||||
return (result == 1) ? 0 : EBUSY;
|
||||
}
|
||||
|
||||
static inline void __spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
asm volatile("eieio":::"memory");
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline void __spin_lock_init(spinlock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline int __spin_is_locked(spinlock_t *lock)
|
||||
{
|
||||
return (*lock != 0);
|
||||
}
|
||||
|
||||
#elif defined(INTEL_SPINLOCKS)
|
||||
|
||||
static inline int __spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
int oldval;
|
||||
|
||||
asm volatile("xchgl %0,%1"
|
||||
: "=r" (oldval), "=m" (*lock)
|
||||
: "0" (0)
|
||||
: "memory");
|
||||
|
||||
return oldval > 0 ? 0 : EBUSY;
|
||||
}
|
||||
|
||||
static inline void __spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
asm volatile("":::"memory");
|
||||
*lock = 1;
|
||||
}
|
||||
|
||||
static inline void __spin_lock_init(spinlock_t *lock)
|
||||
{
|
||||
*lock = 1;
|
||||
}
|
||||
|
||||
static inline int __spin_is_locked(spinlock_t *lock)
|
||||
{
|
||||
return (*lock != 1);
|
||||
}
|
||||
|
||||
#elif defined(MIPS_SPINLOCKS)
|
||||
|
||||
static inline unsigned int load_linked(unsigned long addr)
|
||||
{
|
||||
unsigned int res;
|
||||
|
||||
__asm__ __volatile__("ll\t%0,(%1)"
|
||||
: "=r" (res)
|
||||
: "r" (addr));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
|
||||
{
|
||||
unsigned int res;
|
||||
|
||||
__asm__ __volatile__("sc\t%0,(%2)"
|
||||
: "=r" (res)
|
||||
: "0" (value), "r" (addr));
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline int __spin_trylock(spinlock_t *lock)
|
||||
{
|
||||
unsigned int mw;
|
||||
|
||||
do {
|
||||
mw = load_linked(lock);
|
||||
if (mw)
|
||||
return EBUSY;
|
||||
} while (!store_conditional(lock, 1));
|
||||
|
||||
asm volatile("":::"memory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __spin_unlock(spinlock_t *lock)
|
||||
{
|
||||
asm volatile("":::"memory");
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline void __spin_lock_init(spinlock_t *lock)
|
||||
{
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline int __spin_is_locked(spinlock_t *lock)
|
||||
{
|
||||
return (*lock != 0);
|
||||
}
|
||||
|
||||
#else
|
||||
#error Need to implement spinlock code in spinlock.c
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OS SPECIFIC
|
||||
*/
|
||||
|
||||
static void yield_cpu(void)
|
||||
{
|
||||
struct timespec tm;
|
||||
|
||||
#ifdef USE_SCHED_YIELD
|
||||
sched_yield();
|
||||
#else
|
||||
/* Linux will busy loop for delays < 2ms on real time tasks */
|
||||
tm.tv_sec = 0;
|
||||
tm.tv_nsec = 2000000L + 1;
|
||||
nanosleep(&tm, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int this_is_smp(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* GENERIC
|
||||
*/
|
||||
|
||||
static int smp_machine = 0;
|
||||
|
||||
static inline void __spin_lock(spinlock_t *lock)
|
||||
{
|
||||
int ntries = 0;
|
||||
|
||||
while(__spin_trylock(lock)) {
|
||||
while(__spin_is_locked(lock)) {
|
||||
if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
|
||||
continue;
|
||||
yield_cpu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __read_lock(tdb_rwlock_t *rwlock)
|
||||
{
|
||||
int ntries = 0;
|
||||
|
||||
while(1) {
|
||||
__spin_lock(&rwlock->lock);
|
||||
|
||||
if (!(rwlock->count & RWLOCK_BIAS)) {
|
||||
rwlock->count++;
|
||||
__spin_unlock(&rwlock->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
__spin_unlock(&rwlock->lock);
|
||||
|
||||
while(rwlock->count & RWLOCK_BIAS) {
|
||||
if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
|
||||
continue;
|
||||
yield_cpu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __write_lock(tdb_rwlock_t *rwlock)
|
||||
{
|
||||
int ntries = 0;
|
||||
|
||||
while(1) {
|
||||
__spin_lock(&rwlock->lock);
|
||||
|
||||
if (rwlock->count == 0) {
|
||||
rwlock->count |= RWLOCK_BIAS;
|
||||
__spin_unlock(&rwlock->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
__spin_unlock(&rwlock->lock);
|
||||
|
||||
while(rwlock->count != 0) {
|
||||
if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
|
||||
continue;
|
||||
yield_cpu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __write_unlock(tdb_rwlock_t *rwlock)
|
||||
{
|
||||
__spin_lock(&rwlock->lock);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!(rwlock->count & RWLOCK_BIAS))
|
||||
fprintf(stderr, "bug: write_unlock\n");
|
||||
#endif
|
||||
|
||||
rwlock->count &= ~RWLOCK_BIAS;
|
||||
__spin_unlock(&rwlock->lock);
|
||||
}
|
||||
|
||||
static void __read_unlock(tdb_rwlock_t *rwlock)
|
||||
{
|
||||
__spin_lock(&rwlock->lock);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!rwlock->count)
|
||||
fprintf(stderr, "bug: read_unlock\n");
|
||||
|
||||
if (rwlock->count & RWLOCK_BIAS)
|
||||
fprintf(stderr, "bug: read_unlock\n");
|
||||
#endif
|
||||
|
||||
rwlock->count--;
|
||||
__spin_unlock(&rwlock->lock);
|
||||
}
|
||||
|
||||
/* TDB SPECIFIC */
|
||||
|
||||
/* lock a list in the database. list -1 is the alloc list */
|
||||
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
|
||||
{
|
||||
tdb_rwlock_t *rwlocks;
|
||||
|
||||
if (!tdb->map_ptr) return -1;
|
||||
rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
|
||||
|
||||
switch(rw_type) {
|
||||
case F_RDLCK:
|
||||
__read_lock(&rwlocks[list+1]);
|
||||
break;
|
||||
|
||||
case F_WRLCK:
|
||||
__write_lock(&rwlocks[list+1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return TDB_ERRCODE(TDB_ERR_LOCK, -1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unlock the database. */
|
||||
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
|
||||
{
|
||||
tdb_rwlock_t *rwlocks;
|
||||
|
||||
if (!tdb->map_ptr) return -1;
|
||||
rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
|
||||
|
||||
switch(rw_type) {
|
||||
case F_RDLCK:
|
||||
__read_unlock(&rwlocks[list+1]);
|
||||
break;
|
||||
|
||||
case F_WRLCK:
|
||||
__write_unlock(&rwlocks[list+1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return TDB_ERRCODE(TDB_ERR_LOCK, -1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdb_create_rwlocks(int fd, unsigned int hash_size)
|
||||
{
|
||||
unsigned size, i;
|
||||
tdb_rwlock_t *rwlocks;
|
||||
|
||||
size = (hash_size + 1) * sizeof(tdb_rwlock_t);
|
||||
rwlocks = malloc(size);
|
||||
if (!rwlocks)
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < hash_size+1; i++) {
|
||||
__spin_lock_init(&rwlocks[i].lock);
|
||||
rwlocks[i].count = 0;
|
||||
}
|
||||
|
||||
/* Write it out (appending to end) */
|
||||
if (write(fd, rwlocks, size) != size) {
|
||||
free(rwlocks);
|
||||
return -1;
|
||||
}
|
||||
smp_machine = this_is_smp();
|
||||
free(rwlocks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
|
||||
{
|
||||
tdb_rwlock_t *rwlocks;
|
||||
unsigned i;
|
||||
|
||||
if (tdb->header.rwlocks == 0) return 0;
|
||||
if (!tdb->map_ptr) return -1;
|
||||
|
||||
/* We're mmapped here */
|
||||
rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
|
||||
for(i = 0; i < tdb->header.hash_size+1; i++) {
|
||||
__spin_lock_init(&rwlocks[i].lock);
|
||||
rwlocks[i].count = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
|
||||
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
|
||||
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
|
||||
|
||||
/* Non-spinlock version: remove spinlock pointer */
|
||||
int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
|
||||
{
|
||||
tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
|
||||
- (char *)&tdb->header);
|
||||
|
||||
tdb->header.rwlocks = 0;
|
||||
if (lseek(tdb->fd, off, SEEK_SET) != off
|
||||
|| write(tdb->fd, (void *)&tdb->header.rwlocks,
|
||||
sizeof(tdb->header.rwlocks))
|
||||
!= sizeof(tdb->header.rwlocks))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef __SPINLOCK_H__
|
||||
#define __SPINLOCK_H__
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "tdb.h"
|
||||
|
||||
#ifdef USE_SPINLOCKS
|
||||
|
||||
#define RWLOCK_BIAS 0x1000UL
|
||||
|
||||
/* OS SPECIFIC */
|
||||
#define MAX_BUSY_LOOPS 1000
|
||||
#undef USE_SCHED_YIELD
|
||||
|
||||
/* ARCH SPECIFIC */
|
||||
/* We should make sure these are padded to a cache line */
|
||||
#if defined(SPARC_SPINLOCKS)
|
||||
typedef volatile char spinlock_t;
|
||||
#elif defined(POWERPC_SPINLOCKS)
|
||||
typedef volatile unsigned long spinlock_t;
|
||||
#elif defined(INTEL_SPINLOCKS)
|
||||
typedef volatile int spinlock_t;
|
||||
#elif defined(MIPS_SPINLOCKS)
|
||||
typedef volatile unsigned long spinlock_t;
|
||||
#else
|
||||
#error Need to implement spinlock code in spinlock.h
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
spinlock_t lock;
|
||||
volatile int count;
|
||||
} tdb_rwlock_t;
|
||||
|
||||
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
|
||||
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
|
||||
int tdb_create_rwlocks(int fd, unsigned int hash_size);
|
||||
int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
|
||||
|
||||
#else /* !USE_SPINLOCKS */
|
||||
#if 0
|
||||
#define tdb_create_rwlocks(fd, hash_size) 0
|
||||
#define tdb_spinlock(tdb, list, rw_type) (-1)
|
||||
#define tdb_spinunlock(tdb, list, rw_type) (-1)
|
||||
#else
|
||||
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
|
||||
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
|
||||
int tdb_create_rwlocks(int fd, unsigned int hash_size);
|
||||
#endif
|
||||
int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,144 @@
|
|||
#ifndef __TDB_H__
|
||||
#define __TDB_H__
|
||||
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba database functions
|
||||
Copyright (C) Andrew Tridgell 1999
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* flags to tdb_store() */
|
||||
#define TDB_REPLACE 1
|
||||
#define TDB_INSERT 2
|
||||
#define TDB_MODIFY 3
|
||||
|
||||
/* flags for tdb_open() */
|
||||
#define TDB_DEFAULT 0 /* just a readability place holder */
|
||||
#define TDB_CLEAR_IF_FIRST 1
|
||||
#define TDB_INTERNAL 2 /* don't store on disk */
|
||||
#define TDB_NOLOCK 4 /* don't do any locking */
|
||||
#define TDB_NOMMAP 8 /* don't use mmap */
|
||||
#define TDB_CONVERT 16 /* convert endian (internal use) */
|
||||
#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
|
||||
|
||||
#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
|
||||
|
||||
/* error codes */
|
||||
enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK,
|
||||
TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOEXIST, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT };
|
||||
|
||||
#ifndef u32
|
||||
#define u32 unsigned
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char *dptr;
|
||||
size_t dsize;
|
||||
} TDB_DATA;
|
||||
|
||||
typedef u32 tdb_len;
|
||||
typedef u32 tdb_off;
|
||||
|
||||
/* this is stored at the front of every database */
|
||||
struct tdb_header {
|
||||
char magic_food[32]; /* for /etc/magic */
|
||||
u32 version; /* version of the code */
|
||||
u32 hash_size; /* number of hash entries */
|
||||
tdb_off rwlocks;
|
||||
tdb_off reserved[31];
|
||||
};
|
||||
|
||||
struct tdb_lock_type {
|
||||
u32 count;
|
||||
u32 ltype;
|
||||
};
|
||||
|
||||
struct tdb_traverse_lock {
|
||||
struct tdb_traverse_lock *next;
|
||||
u32 off;
|
||||
u32 hash;
|
||||
};
|
||||
|
||||
/* this is the context structure that is returned from a db open */
|
||||
typedef struct tdb_context {
|
||||
char *name; /* the name of the database */
|
||||
void *map_ptr; /* where it is currently mapped */
|
||||
int fd; /* open file descriptor for the database */
|
||||
tdb_len map_size; /* how much space has been mapped */
|
||||
int read_only; /* opened read-only */
|
||||
struct tdb_lock_type *locked; /* array of chain locks */
|
||||
enum TDB_ERROR ecode; /* error code for last tdb error */
|
||||
struct tdb_header header; /* a cached copy of the header */
|
||||
u32 flags; /* the flags passed to tdb_open */
|
||||
u32 *lockedkeys; /* array of locked keys: first is #keys */
|
||||
struct tdb_traverse_lock travlocks; /* current traversal locks */
|
||||
struct tdb_context *next; /* all tdbs to avoid multiple opens */
|
||||
dev_t device; /* uniquely identifies this tdb */
|
||||
ino_t inode; /* uniquely identifies this tdb */
|
||||
void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...); /* logging function */
|
||||
int open_flags; /* flags used in the open - needed by reopen */
|
||||
} TDB_CONTEXT;
|
||||
|
||||
typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
|
||||
typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
|
||||
|
||||
TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
|
||||
int open_flags, mode_t mode);
|
||||
TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
|
||||
int open_flags, mode_t mode,
|
||||
tdb_log_func log_fn);
|
||||
|
||||
int tdb_reopen(TDB_CONTEXT *tdb);
|
||||
int tdb_reopen_all(void);
|
||||
void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
|
||||
enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
|
||||
const char *tdb_errorstr(TDB_CONTEXT *tdb);
|
||||
TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
|
||||
int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
|
||||
int tdb_close(TDB_CONTEXT *tdb);
|
||||
TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
|
||||
TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state);
|
||||
int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
|
||||
void tdb_unlockkeys(TDB_CONTEXT *tdb);
|
||||
int tdb_lockall(TDB_CONTEXT *tdb);
|
||||
void tdb_unlockall(TDB_CONTEXT *tdb);
|
||||
|
||||
/* Low level locking functions: use with care */
|
||||
void tdb_set_lock_alarm(sig_atomic_t *palarm);
|
||||
int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
|
||||
|
||||
/* Debug functions. Not used in production. */
|
||||
void tdb_dump_all(TDB_CONTEXT *tdb);
|
||||
int tdb_printfreelist(TDB_CONTEXT *tdb);
|
||||
|
||||
extern TDB_DATA tdb_null;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* tdb.h */
|
|
@ -0,0 +1,10 @@
|
|||
# Magic file(1) information about tdb files.
|
||||
#
|
||||
# Install this into /etc/magic or the corresponding location for your
|
||||
# system, or pass as a -m argument to file(1).
|
||||
|
||||
# You may use and redistribute this file without restriction.
|
||||
|
||||
0 string TDB\ file TDB database
|
||||
>32 lelong =0x2601196D version 6, little-endian
|
||||
>>36 lelong x hash size %d bytes
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
low level tdb backup and restore utility
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include "tdb.h"
|
||||
|
||||
static int failed;
|
||||
|
||||
char *add_suffix(const char *name, const char *suffix)
|
||||
{
|
||||
char *ret;
|
||||
int len = strlen(name) + strlen(suffix) + 1;
|
||||
ret = malloc(len);
|
||||
if (!ret) {
|
||||
fprintf(stderr,"Out of memory!\n");
|
||||
exit(1);
|
||||
}
|
||||
strncpy(ret, name, len);
|
||||
strncat(ret, suffix, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
|
||||
|
||||
if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
|
||||
fprintf(stderr,"Failed to insert into %s\n", tdb_new->name);
|
||||
failed = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
carefully backup a tdb, validating the contents and
|
||||
only doing the backup if its OK
|
||||
this function is also used for restore
|
||||
*/
|
||||
int backup_tdb(const char *old_name, const char *new_name)
|
||||
{
|
||||
TDB_CONTEXT *tdb;
|
||||
TDB_CONTEXT *tdb_new;
|
||||
char *tmp_name;
|
||||
struct stat st;
|
||||
int count1, count2;
|
||||
|
||||
tmp_name = add_suffix(new_name, ".tmp");
|
||||
|
||||
/* stat the old tdb to find its permissions */
|
||||
if (stat(old_name, &st) != 0) {
|
||||
perror(old_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* open the old tdb */
|
||||
tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
|
||||
if (!tdb) {
|
||||
printf("Failed to open %s\n", old_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create the new tdb */
|
||||
unlink(tmp_name);
|
||||
tdb_new = tdb_open(tmp_name, tdb->header.hash_size,
|
||||
TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL,
|
||||
st.st_mode & 0777);
|
||||
if (!tdb_new) {
|
||||
perror(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* lock the old tdb */
|
||||
if (tdb_lockall(tdb) != 0) {
|
||||
fprintf(stderr,"Failed to lock %s\n", old_name);
|
||||
tdb_close(tdb);
|
||||
tdb_close(tdb_new);
|
||||
unlink(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
failed = 0;
|
||||
|
||||
/* traverse and copy */
|
||||
count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
|
||||
if (count1 < 0 || failed) {
|
||||
fprintf(stderr,"failed to copy %s\n", old_name);
|
||||
tdb_close(tdb);
|
||||
tdb_close(tdb_new);
|
||||
unlink(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* close the old tdb */
|
||||
tdb_close(tdb);
|
||||
|
||||
/* close the new tdb and re-open read-only */
|
||||
tdb_close(tdb_new);
|
||||
tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
|
||||
if (!tdb_new) {
|
||||
fprintf(stderr,"failed to reopen %s\n", tmp_name);
|
||||
unlink(tmp_name);
|
||||
perror(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* traverse the new tdb to confirm */
|
||||
count2 = tdb_traverse(tdb_new, test_fn, 0);
|
||||
if (count2 != count1) {
|
||||
fprintf(stderr,"failed to copy %s\n", old_name);
|
||||
tdb_close(tdb_new);
|
||||
unlink(tmp_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* make sure the new tdb has reached stable storage */
|
||||
fsync(tdb_new->fd);
|
||||
|
||||
/* close the new tdb and rename it to .bak */
|
||||
tdb_close(tdb_new);
|
||||
unlink(new_name);
|
||||
if (rename(tmp_name, new_name) != 0) {
|
||||
perror(new_name);
|
||||
free(tmp_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(tmp_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
verify a tdb and if it is corrupt then restore from *.bak
|
||||
*/
|
||||
int verify_tdb(const char *fname, const char *bak_name)
|
||||
{
|
||||
TDB_CONTEXT *tdb;
|
||||
int count = -1;
|
||||
|
||||
/* open the tdb */
|
||||
tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
|
||||
|
||||
/* traverse the tdb, then close it */
|
||||
if (tdb) {
|
||||
count = tdb_traverse(tdb, test_fn, NULL);
|
||||
tdb_close(tdb);
|
||||
}
|
||||
|
||||
/* count is < 0 means an error */
|
||||
if (count < 0) {
|
||||
printf("restoring %s\n", fname);
|
||||
return backup_tdb(bak_name, fname);
|
||||
}
|
||||
|
||||
printf("%s : %d records\n", fname, count);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
low level tdb backup and restore utility
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
char *add_suffix(const char *name, const char *suffix);
|
||||
int backup_tdb(const char *old_name, const char *new_name);
|
||||
int verify_tdb(const char *fname, const char *bak_name);
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
low level tdb backup and restore utility
|
||||
Copyright (C) Andrew Tridgell 2002
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This program is meant for backup/restore of tdb databases. Typical usage would be:
|
||||
tdbbackup *.tdb
|
||||
when Samba shuts down cleanly, which will make a backup of all the local databases
|
||||
to *.bak files. Then on Samba startup you would use:
|
||||
tdbbackup -v *.tdb
|
||||
and this will check the databases for corruption and if corruption is detected then
|
||||
the backup will be restored.
|
||||
|
||||
You may also like to do a backup on a regular basis while Samba is
|
||||
running, perhaps using cron.
|
||||
|
||||
The reason this program is needed is to cope with power failures
|
||||
while Samba is running. A power failure could lead to database
|
||||
corruption and Samba will then not start correctly.
|
||||
|
||||
Note that many of the databases in Samba are transient and thus
|
||||
don't need to be backed up, so you can optimise the above a little
|
||||
by only running the backup on the critical databases.
|
||||
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include "tdb.h"
|
||||
#include "tdbback.h"
|
||||
|
||||
/*
|
||||
see if one file is newer than another
|
||||
*/
|
||||
static int file_newer(const char *fname1, const char *fname2)
|
||||
{
|
||||
struct stat st1, st2;
|
||||
if (stat(fname1, &st1) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (stat(fname2, &st2) != 0) {
|
||||
return 1;
|
||||
}
|
||||
return (st1.st_mtime > st2.st_mtime);
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: tdbbackup [options] <fname...>\n\n");
|
||||
printf(" -h this help message\n");
|
||||
printf(" -s suffix set the backup suffix\n");
|
||||
printf(" -v verify mode (restore if corrupt)\n");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int ret = 0;
|
||||
int c;
|
||||
int verify = 0;
|
||||
const char *suffix = ".bak";
|
||||
extern int optind;
|
||||
extern char *optarg;
|
||||
|
||||
while ((c = getopt(argc, argv, "vhs:")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
case 'v':
|
||||
verify = 1;
|
||||
break;
|
||||
case 's':
|
||||
suffix = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc < 1) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i=0; i<argc; i++) {
|
||||
const char *fname = argv[i];
|
||||
char *bak_name;
|
||||
|
||||
bak_name = add_suffix(fname, suffix);
|
||||
|
||||
if (verify) {
|
||||
if (verify_tdb(fname, bak_name) != 0) {
|
||||
ret = 1;
|
||||
}
|
||||
} else {
|
||||
if (file_newer(fname, bak_name) &&
|
||||
backup_tdb(fname, bak_name) != 0) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
free(bak_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple tdb dump util
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include "tdb.h"
|
||||
|
||||
static void print_data(TDB_DATA d)
|
||||
{
|
||||
unsigned char *p = d.dptr;
|
||||
int len = d.dsize;
|
||||
while (len--) {
|
||||
if (isprint(*p) && !strchr("\"\\", *p)) {
|
||||
fputc(*p, stdout);
|
||||
} else {
|
||||
printf("\\%02X", *p);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
printf("{\n");
|
||||
printf("key = \"");
|
||||
print_data(key);
|
||||
printf("\"\n");
|
||||
printf("data = \"");
|
||||
print_data(dbuf);
|
||||
printf("\"\n");
|
||||
printf("}\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_tdb(const char *fname)
|
||||
{
|
||||
TDB_CONTEXT *tdb;
|
||||
|
||||
tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
|
||||
if (!tdb) {
|
||||
printf("Failed to open %s\n", fname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tdb_traverse(tdb, traverse_fn, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *fname;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: tdbdump <fname>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fname = argv[1];
|
||||
|
||||
return dump_tdb(fname);
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
#include "tdb.h"
|
||||
#include <gdbm.h>
|
||||
|
||||
/* a test program for tdb - the trivial database */
|
||||
|
||||
|
||||
|
||||
#define DELETE_PROB 7
|
||||
#define STORE_PROB 5
|
||||
|
||||
static TDB_CONTEXT *db;
|
||||
static GDBM_FILE gdbm;
|
||||
|
||||
struct timeval tp1,tp2;
|
||||
|
||||
static void start_timer(void)
|
||||
{
|
||||
gettimeofday(&tp1,NULL);
|
||||
}
|
||||
|
||||
static double end_timer(void)
|
||||
{
|
||||
gettimeofday(&tp2,NULL);
|
||||
return((tp2.tv_sec - tp1.tv_sec) +
|
||||
(tp2.tv_usec - tp1.tv_usec)*1.0e-6);
|
||||
}
|
||||
|
||||
static void fatal(char *why)
|
||||
{
|
||||
perror(why);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stdout, format, ap);
|
||||
va_end(ap);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void compare_db(void)
|
||||
{
|
||||
TDB_DATA d, key, nextkey;
|
||||
datum gd, gkey, gnextkey;
|
||||
|
||||
key = tdb_firstkey(db);
|
||||
while (key.dptr) {
|
||||
d = tdb_fetch(db, key);
|
||||
gkey.dptr = key.dptr;
|
||||
gkey.dsize = key.dsize;
|
||||
|
||||
gd = gdbm_fetch(gdbm, gkey);
|
||||
|
||||
if (!gd.dptr) fatal("key not in gdbm");
|
||||
if (gd.dsize != d.dsize) fatal("data sizes differ");
|
||||
if (memcmp(gd.dptr, d.dptr, d.dsize)) {
|
||||
fatal("data differs");
|
||||
}
|
||||
|
||||
nextkey = tdb_nextkey(db, key);
|
||||
free(key.dptr);
|
||||
free(d.dptr);
|
||||
free(gd.dptr);
|
||||
key = nextkey;
|
||||
}
|
||||
|
||||
gkey = gdbm_firstkey(gdbm);
|
||||
while (gkey.dptr) {
|
||||
gd = gdbm_fetch(gdbm, gkey);
|
||||
key.dptr = gkey.dptr;
|
||||
key.dsize = gkey.dsize;
|
||||
|
||||
d = tdb_fetch(db, key);
|
||||
|
||||
if (!d.dptr) fatal("key not in db");
|
||||
if (d.dsize != gd.dsize) fatal("data sizes differ");
|
||||
if (memcmp(d.dptr, gd.dptr, gd.dsize)) {
|
||||
fatal("data differs");
|
||||
}
|
||||
|
||||
gnextkey = gdbm_nextkey(gdbm, gkey);
|
||||
free(gkey.dptr);
|
||||
free(gd.dptr);
|
||||
free(d.dptr);
|
||||
gkey = gnextkey;
|
||||
}
|
||||
}
|
||||
|
||||
static char *randbuf(int len)
|
||||
{
|
||||
char *buf;
|
||||
int i;
|
||||
buf = (char *)malloc(len+1);
|
||||
|
||||
for (i=0;i<len;i++) {
|
||||
buf[i] = 'a' + (rand() % 26);
|
||||
}
|
||||
buf[i] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void addrec_db(void)
|
||||
{
|
||||
int klen, dlen;
|
||||
char *k, *d;
|
||||
TDB_DATA key, data;
|
||||
|
||||
klen = 1 + (rand() % 4);
|
||||
dlen = 1 + (rand() % 100);
|
||||
|
||||
k = randbuf(klen);
|
||||
d = randbuf(dlen);
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = klen+1;
|
||||
|
||||
data.dptr = d;
|
||||
data.dsize = dlen+1;
|
||||
|
||||
if (rand() % DELETE_PROB == 0) {
|
||||
tdb_delete(db, key);
|
||||
} else if (rand() % STORE_PROB == 0) {
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
} else {
|
||||
data = tdb_fetch(db, key);
|
||||
if (data.dptr) free(data.dptr);
|
||||
}
|
||||
|
||||
free(k);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static void addrec_gdbm(void)
|
||||
{
|
||||
int klen, dlen;
|
||||
char *k, *d;
|
||||
datum key, data;
|
||||
|
||||
klen = 1 + (rand() % 4);
|
||||
dlen = 1 + (rand() % 100);
|
||||
|
||||
k = randbuf(klen);
|
||||
d = randbuf(dlen);
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = klen+1;
|
||||
|
||||
data.dptr = d;
|
||||
data.dsize = dlen+1;
|
||||
|
||||
if (rand() % DELETE_PROB == 0) {
|
||||
gdbm_delete(gdbm, key);
|
||||
} else if (rand() % STORE_PROB == 0) {
|
||||
if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) {
|
||||
fatal("gdbm_store failed");
|
||||
}
|
||||
} else {
|
||||
data = gdbm_fetch(gdbm, key);
|
||||
if (data.dptr) free(data.dptr);
|
||||
}
|
||||
|
||||
free(k);
|
||||
free(d);
|
||||
}
|
||||
|
||||
static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
#if 0
|
||||
printf("[%s] [%s]\n", key.dptr, dbuf.dptr);
|
||||
#endif
|
||||
tdb_delete(tdb, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void merge_test(void)
|
||||
{
|
||||
int i;
|
||||
char keys[5][2];
|
||||
TDB_DATA key, data;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
sprintf(keys[i], "%d", i);
|
||||
key.dptr = keys[i];
|
||||
key.dsize = 2;
|
||||
|
||||
data.dptr = "test";
|
||||
data.dsize = 4;
|
||||
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
}
|
||||
|
||||
key.dptr = keys[0];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[4];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[2];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[1];
|
||||
tdb_delete(db, key);
|
||||
key.dptr = keys[3];
|
||||
tdb_delete(db, key);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, seed=0;
|
||||
int loops = 10000;
|
||||
|
||||
unlink("test.gdbm");
|
||||
|
||||
db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST,
|
||||
O_RDWR | O_CREAT | O_TRUNC, 0600);
|
||||
gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST,
|
||||
0600, NULL);
|
||||
|
||||
if (!db || !gdbm) {
|
||||
fatal("db open failed");
|
||||
}
|
||||
|
||||
tdb_logging_function(db, tdb_log);
|
||||
|
||||
#if 1
|
||||
srand(seed);
|
||||
start_timer();
|
||||
for (i=0;i<loops;i++) addrec_gdbm();
|
||||
printf("gdbm got %.2f ops/sec\n", i/end_timer());
|
||||
#endif
|
||||
|
||||
merge_test();
|
||||
|
||||
srand(seed);
|
||||
start_timer();
|
||||
for (i=0;i<loops;i++) addrec_db();
|
||||
printf("tdb got %.2f ops/sec\n", i/end_timer());
|
||||
|
||||
compare_db();
|
||||
|
||||
printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
|
||||
printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL));
|
||||
|
||||
tdb_close(db);
|
||||
gdbm_close(gdbm);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba database functions
|
||||
Copyright (C) Andrew Tridgell 1999-2000
|
||||
Copyright (C) Paul `Rusty' Russell 2000
|
||||
Copyright (C) Jeremy Allison 2000
|
||||
Copyright (C) Andrew Esh 2001
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include "tdb.h"
|
||||
|
||||
/* a tdb tool for manipulating a tdb database */
|
||||
|
||||
#define FSTRING_LEN 256
|
||||
typedef char fstring[FSTRING_LEN];
|
||||
|
||||
typedef struct connections_key {
|
||||
pid_t pid;
|
||||
int cnum;
|
||||
fstring name;
|
||||
} connections_key;
|
||||
|
||||
typedef struct connections_data {
|
||||
int magic;
|
||||
pid_t pid;
|
||||
int cnum;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
char name[24];
|
||||
char addr[24];
|
||||
char machine[128];
|
||||
time_t start;
|
||||
} connections_data;
|
||||
|
||||
static TDB_CONTEXT *tdb;
|
||||
|
||||
static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
|
||||
|
||||
static void print_asc(unsigned char *buf,int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* We're probably printing ASCII strings so don't try to display
|
||||
the trailing NULL character. */
|
||||
|
||||
if (buf[len - 1] == 0)
|
||||
len--;
|
||||
|
||||
for (i=0;i<len;i++)
|
||||
printf("%c",isprint(buf[i])?buf[i]:'.');
|
||||
}
|
||||
|
||||
static void print_data(unsigned char *buf,int len)
|
||||
{
|
||||
int i=0;
|
||||
if (len<=0) return;
|
||||
printf("[%03X] ",i);
|
||||
for (i=0;i<len;) {
|
||||
printf("%02X ",(int)buf[i]);
|
||||
i++;
|
||||
if (i%8 == 0) printf(" ");
|
||||
if (i%16 == 0) {
|
||||
print_asc(&buf[i-16],8); printf(" ");
|
||||
print_asc(&buf[i-8],8); printf("\n");
|
||||
if (i<len) printf("[%03X] ",i);
|
||||
}
|
||||
}
|
||||
if (i%16) {
|
||||
int n;
|
||||
|
||||
n = 16 - (i%16);
|
||||
printf(" ");
|
||||
if (n>8) printf(" ");
|
||||
while (n--) printf(" ");
|
||||
|
||||
n = i%16;
|
||||
if (n > 8) n = 8;
|
||||
print_asc(&buf[i-(i%16)],n); printf(" ");
|
||||
n = (i%16) - n;
|
||||
if (n>0) print_asc(&buf[i-n],n);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void help(void)
|
||||
{
|
||||
printf("\n"
|
||||
"tdbtool: \n"
|
||||
" create dbname : create a database\n"
|
||||
" open dbname : open an existing database\n"
|
||||
" erase : erase the database\n"
|
||||
" dump : dump the database as strings\n"
|
||||
" insert key data : insert a record\n"
|
||||
" move key file : move a record to a destination tdb\n"
|
||||
" store key data : store a record (replace)\n"
|
||||
" show key : show a record by key\n"
|
||||
" delete key : delete a record by key\n"
|
||||
" list : print the database hash table and freelist\n"
|
||||
" free : print the database freelist\n"
|
||||
" 1 | first : print the first record\n"
|
||||
" n | next : print the next record\n"
|
||||
" q | quit : terminate\n"
|
||||
" \\n : repeat 'next' command\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void terror(char *why)
|
||||
{
|
||||
printf("%s\n", why);
|
||||
}
|
||||
|
||||
static char *get_token(int startover)
|
||||
{
|
||||
static char tmp[1024];
|
||||
static char *cont = NULL;
|
||||
char *insert, *start;
|
||||
char *k = strtok(NULL, " ");
|
||||
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
if (startover)
|
||||
start = tmp;
|
||||
else
|
||||
start = cont;
|
||||
|
||||
strcpy(start, k);
|
||||
insert = start + strlen(start) - 1;
|
||||
while (*insert == '\\') {
|
||||
*insert++ = ' ';
|
||||
k = strtok(NULL, " ");
|
||||
if (!k)
|
||||
break;
|
||||
strcpy(insert, k);
|
||||
insert = start + strlen(start) - 1;
|
||||
}
|
||||
|
||||
/* Get ready for next call */
|
||||
cont = start + strlen(start) + 1;
|
||||
return start;
|
||||
}
|
||||
|
||||
static void create_tdb(void)
|
||||
{
|
||||
char *tok = get_token(1);
|
||||
if (!tok) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
if (tdb) tdb_close(tdb);
|
||||
tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST,
|
||||
O_RDWR | O_CREAT | O_TRUNC, 0600);
|
||||
if (!tdb) {
|
||||
printf("Could not create %s: %s\n", tok, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void open_tdb(void)
|
||||
{
|
||||
char *tok = get_token(1);
|
||||
if (!tok) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
if (tdb) tdb_close(tdb);
|
||||
tdb = tdb_open(tok, 0, 0, O_RDWR, 0600);
|
||||
if (!tdb) {
|
||||
printf("Could not open %s: %s\n", tok, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
char *d = get_token(0);
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
if (!k || !d) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = strlen(k)+1;
|
||||
dbuf.dptr = d;
|
||||
dbuf.dsize = strlen(d)+1;
|
||||
|
||||
if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
|
||||
terror("insert failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void store_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
char *d = get_token(0);
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
if (!k || !d) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = strlen(k)+1;
|
||||
dbuf.dptr = d;
|
||||
dbuf.dsize = strlen(d)+1;
|
||||
|
||||
printf("Storing key:\n");
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
|
||||
if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
|
||||
terror("store failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void show_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
TDB_DATA key, dbuf;
|
||||
|
||||
if (!k) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = strlen(k)+1;
|
||||
|
||||
dbuf = tdb_fetch(tdb, key);
|
||||
if (!dbuf.dptr) {
|
||||
/* maybe it is non-NULL terminated key? */
|
||||
key.dsize = strlen(k);
|
||||
dbuf = tdb_fetch(tdb, key);
|
||||
|
||||
if ( !dbuf.dptr ) {
|
||||
terror("fetch failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
|
||||
free( dbuf.dptr );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void delete_tdb(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
TDB_DATA key;
|
||||
|
||||
if (!k) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = strlen(k)+1;
|
||||
|
||||
if (tdb_delete(tdb, key) != 0) {
|
||||
terror("delete failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void move_rec(void)
|
||||
{
|
||||
char *k = get_token(1);
|
||||
char *file = get_token(0);
|
||||
TDB_DATA key, dbuf;
|
||||
TDB_CONTEXT *dst_tdb;
|
||||
|
||||
if (!k) {
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !file ) {
|
||||
terror("need destination tdb name");
|
||||
return;
|
||||
}
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = strlen(k)+1;
|
||||
|
||||
dbuf = tdb_fetch(tdb, key);
|
||||
if (!dbuf.dptr) {
|
||||
/* maybe it is non-NULL terminated key? */
|
||||
key.dsize = strlen(k);
|
||||
dbuf = tdb_fetch(tdb, key);
|
||||
|
||||
if ( !dbuf.dptr ) {
|
||||
terror("fetch failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
print_rec(tdb, key, dbuf, NULL);
|
||||
|
||||
dst_tdb = tdb_open(file, 0, 0, O_RDWR, 0600);
|
||||
if ( !dst_tdb ) {
|
||||
terror("unable to open destination tdb");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
|
||||
terror("failed to move record");
|
||||
}
|
||||
else
|
||||
printf("record moved\n");
|
||||
|
||||
tdb_close( dst_tdb );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int print_conn_key(TDB_DATA key)
|
||||
{
|
||||
printf( "pid =%5d ", ((connections_key*)key.dptr)->pid);
|
||||
printf( "cnum =%10d ", ((connections_key*)key.dptr)->cnum);
|
||||
printf( "name =[%s]\n", ((connections_key*)key.dptr)->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_conn_data(TDB_DATA dbuf)
|
||||
{
|
||||
printf( "pid =%5d ", ((connections_data*)dbuf.dptr)->pid);
|
||||
printf( "cnum =%10d ", ((connections_data*)dbuf.dptr)->cnum);
|
||||
printf( "name =[%s]\n", ((connections_data*)dbuf.dptr)->name);
|
||||
|
||||
printf( "uid =%5d ", ((connections_data*)dbuf.dptr)->uid);
|
||||
printf( "addr =[%s]\n", ((connections_data*)dbuf.dptr)->addr);
|
||||
printf( "gid =%5d ", ((connections_data*)dbuf.dptr)->gid);
|
||||
printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine);
|
||||
printf( "start = %s\n", ctime(&((connections_data*)dbuf.dptr)->start));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
#if 0
|
||||
print_conn_key(key);
|
||||
print_conn_data(dbuf);
|
||||
return 0;
|
||||
#else
|
||||
printf("\nkey %d bytes\n", key.dsize);
|
||||
print_asc(key.dptr, key.dsize);
|
||||
printf("\ndata %d bytes\n", dbuf.dsize);
|
||||
print_data(dbuf.dptr, dbuf.dsize);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
print_asc(key.dptr, key.dsize);
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int total_bytes;
|
||||
|
||||
static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
|
||||
{
|
||||
total_bytes += dbuf.dsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void info_tdb(void)
|
||||
{
|
||||
int count;
|
||||
total_bytes = 0;
|
||||
if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1))
|
||||
printf("Error = %s\n", tdb_errorstr(tdb));
|
||||
else
|
||||
printf("%d records totalling %d bytes\n", count, total_bytes);
|
||||
}
|
||||
|
||||
static char *tdb_getline(char *prompt)
|
||||
{
|
||||
static char line[1024];
|
||||
char *p;
|
||||
fputs(prompt, stdout);
|
||||
line[0] = 0;
|
||||
p = fgets(line, sizeof(line)-1, stdin);
|
||||
if (p) p = strchr(p, '\n');
|
||||
if (p) *p = 0;
|
||||
return p?line:NULL;
|
||||
}
|
||||
|
||||
static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
return tdb_delete(the_tdb, key);
|
||||
}
|
||||
|
||||
static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
*pkey = tdb_firstkey(the_tdb);
|
||||
|
||||
dbuf = tdb_fetch(the_tdb, *pkey);
|
||||
if (!dbuf.dptr) terror("fetch failed");
|
||||
else {
|
||||
/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
|
||||
print_rec(the_tdb, *pkey, dbuf, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
*pkey = tdb_nextkey(the_tdb, *pkey);
|
||||
|
||||
dbuf = tdb_fetch(the_tdb, *pkey);
|
||||
if (!dbuf.dptr)
|
||||
terror("fetch failed");
|
||||
else
|
||||
/* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */
|
||||
print_rec(the_tdb, *pkey, dbuf, NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int bIterate = 0;
|
||||
char *line;
|
||||
char *tok;
|
||||
TDB_DATA iterate_kbuf;
|
||||
|
||||
if (argv[1]) {
|
||||
static char tmp[1024];
|
||||
sprintf(tmp, "open %s", argv[1]);
|
||||
tok=strtok(tmp," ");
|
||||
open_tdb();
|
||||
}
|
||||
|
||||
while ((line = tdb_getline("tdb> "))) {
|
||||
|
||||
/* Shell command */
|
||||
|
||||
if (line[0] == '!') {
|
||||
system(line + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tok = strtok(line," "))==NULL) {
|
||||
if (bIterate)
|
||||
next_record(tdb, &iterate_kbuf);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(tok,"create") == 0) {
|
||||
bIterate = 0;
|
||||
create_tdb();
|
||||
continue;
|
||||
} else if (strcmp(tok,"open") == 0) {
|
||||
open_tdb();
|
||||
continue;
|
||||
} else if ((strcmp(tok, "q") == 0) ||
|
||||
(strcmp(tok, "quit") == 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* all the rest require a open database */
|
||||
if (!tdb) {
|
||||
bIterate = 0;
|
||||
terror("database not open");
|
||||
help();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(tok,"insert") == 0) {
|
||||
bIterate = 0;
|
||||
insert_tdb();
|
||||
} else if (strcmp(tok,"store") == 0) {
|
||||
bIterate = 0;
|
||||
store_tdb();
|
||||
} else if (strcmp(tok,"show") == 0) {
|
||||
bIterate = 0;
|
||||
show_tdb();
|
||||
} else if (strcmp(tok,"erase") == 0) {
|
||||
bIterate = 0;
|
||||
tdb_traverse(tdb, do_delete_fn, NULL);
|
||||
} else if (strcmp(tok,"delete") == 0) {
|
||||
bIterate = 0;
|
||||
delete_tdb();
|
||||
} else if (strcmp(tok,"dump") == 0) {
|
||||
bIterate = 0;
|
||||
tdb_traverse(tdb, print_rec, NULL);
|
||||
} else if (strcmp(tok,"move") == 0) {
|
||||
bIterate = 0;
|
||||
move_rec();
|
||||
} else if (strcmp(tok,"list") == 0) {
|
||||
tdb_dump_all(tdb);
|
||||
} else if (strcmp(tok, "free") == 0) {
|
||||
tdb_printfreelist(tdb);
|
||||
} else if (strcmp(tok,"info") == 0) {
|
||||
info_tdb();
|
||||
} else if ( (strcmp(tok, "1") == 0) ||
|
||||
(strcmp(tok, "first") == 0)) {
|
||||
bIterate = 1;
|
||||
first_record(tdb, &iterate_kbuf);
|
||||
} else if ((strcmp(tok, "n") == 0) ||
|
||||
(strcmp(tok, "next") == 0)) {
|
||||
next_record(tdb, &iterate_kbuf);
|
||||
} else if ((strcmp(tok, "keys") == 0)) {
|
||||
bIterate = 0;
|
||||
tdb_traverse(tdb, print_key, NULL);
|
||||
} else {
|
||||
help();
|
||||
}
|
||||
}
|
||||
|
||||
if (tdb) tdb_close(tdb);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include "tdb.h"
|
||||
|
||||
/* this tests tdb by doing lots of ops from several simultaneous
|
||||
writers - that stresses the locking code. Build with TDB_DEBUG=1
|
||||
for best effect */
|
||||
|
||||
|
||||
|
||||
#define REOPEN_PROB 30
|
||||
#define DELETE_PROB 8
|
||||
#define STORE_PROB 4
|
||||
#define APPEND_PROB 6
|
||||
#define LOCKSTORE_PROB 0
|
||||
#define TRAVERSE_PROB 20
|
||||
#define CULL_PROB 100
|
||||
#define KEYLEN 3
|
||||
#define DATALEN 100
|
||||
#define LOCKLEN 20
|
||||
|
||||
static TDB_CONTEXT *db;
|
||||
|
||||
static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stdout, format, ap);
|
||||
va_end(ap);
|
||||
fflush(stdout);
|
||||
#if 0
|
||||
{
|
||||
char *ptr;
|
||||
asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid());
|
||||
system(ptr);
|
||||
free(ptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fatal(char *why)
|
||||
{
|
||||
perror(why);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static char *randbuf(int len)
|
||||
{
|
||||
char *buf;
|
||||
int i;
|
||||
buf = (char *)malloc(len+1);
|
||||
|
||||
for (i=0;i<len;i++) {
|
||||
buf[i] = 'a' + (rand() % 26);
|
||||
}
|
||||
buf[i] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
if (random() % CULL_PROB == 0) {
|
||||
tdb_delete(tdb, key);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void addrec_db(void)
|
||||
{
|
||||
int klen, dlen, slen;
|
||||
char *k, *d, *s;
|
||||
TDB_DATA key, data, lockkey;
|
||||
|
||||
klen = 1 + (rand() % KEYLEN);
|
||||
dlen = 1 + (rand() % DATALEN);
|
||||
slen = 1 + (rand() % LOCKLEN);
|
||||
|
||||
k = randbuf(klen);
|
||||
d = randbuf(dlen);
|
||||
s = randbuf(slen);
|
||||
|
||||
key.dptr = k;
|
||||
key.dsize = klen+1;
|
||||
|
||||
data.dptr = d;
|
||||
data.dsize = dlen+1;
|
||||
|
||||
lockkey.dptr = s;
|
||||
lockkey.dsize = slen+1;
|
||||
|
||||
#if REOPEN_PROB
|
||||
if (random() % REOPEN_PROB == 0) {
|
||||
tdb_reopen_all();
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DELETE_PROB
|
||||
if (random() % DELETE_PROB == 0) {
|
||||
tdb_delete(db, key);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STORE_PROB
|
||||
if (random() % STORE_PROB == 0) {
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if APPEND_PROB
|
||||
if (random() % APPEND_PROB == 0) {
|
||||
if (tdb_append(db, key, data) != 0) {
|
||||
fatal("tdb_append failed");
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LOCKSTORE_PROB
|
||||
if (random() % LOCKSTORE_PROB == 0) {
|
||||
tdb_chainlock(db, lockkey);
|
||||
data = tdb_fetch(db, key);
|
||||
if (tdb_store(db, key, data, TDB_REPLACE) != 0) {
|
||||
fatal("tdb_store failed");
|
||||
}
|
||||
if (data.dptr) free(data.dptr);
|
||||
tdb_chainunlock(db, lockkey);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TRAVERSE_PROB
|
||||
if (random() % TRAVERSE_PROB == 0) {
|
||||
tdb_traverse(db, cull_traverse, NULL);
|
||||
goto next;
|
||||
}
|
||||
#endif
|
||||
|
||||
data = tdb_fetch(db, key);
|
||||
if (data.dptr) free(data.dptr);
|
||||
|
||||
next:
|
||||
free(k);
|
||||
free(d);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
tdb_delete(tdb, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef NPROC
|
||||
#define NPROC 6
|
||||
#endif
|
||||
|
||||
#ifndef NLOOPS
|
||||
#define NLOOPS 200000
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, seed=0;
|
||||
int loops = NLOOPS;
|
||||
pid_t pids[NPROC];
|
||||
|
||||
pids[0] = getpid();
|
||||
|
||||
for (i=0;i<NPROC-1;i++) {
|
||||
if ((pids[i+1]=fork()) == 0) break;
|
||||
}
|
||||
|
||||
db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST,
|
||||
O_RDWR | O_CREAT, 0600);
|
||||
if (!db) {
|
||||
fatal("db open failed");
|
||||
}
|
||||
tdb_logging_function(db, tdb_log);
|
||||
|
||||
srand(seed + getpid());
|
||||
srandom(seed + getpid() + time(NULL));
|
||||
for (i=0;i<loops;i++) addrec_db();
|
||||
|
||||
tdb_traverse(db, NULL, NULL);
|
||||
tdb_traverse(db, traverse_fn, NULL);
|
||||
tdb_traverse(db, traverse_fn, NULL);
|
||||
|
||||
tdb_close(db);
|
||||
|
||||
if (getpid() == pids[0]) {
|
||||
for (i=0;i<NPROC-1;i++) {
|
||||
int status;
|
||||
if (waitpid(pids[i+1], &status, 0) != pids[i+1]) {
|
||||
printf("failed to wait for %d\n",
|
||||
(int)pids[i+1]);
|
||||
exit(1);
|
||||
}
|
||||
if (WEXITSTATUS(status) != 0) {
|
||||
printf("child %d exited with status %d\n",
|
||||
(int)pids[i+1], WEXITSTATUS(status));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("OK\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,830 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
tdb utility functions
|
||||
Copyright (C) Andrew Tridgell 1992-1998
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include <fnmatch.h>
|
||||
|
||||
/* these are little tdb utility functions that are meant to make
|
||||
dealing with a tdb database a little less cumbersome in Samba */
|
||||
|
||||
static SIG_ATOMIC_T gotalarm;
|
||||
|
||||
/***************************************************************
|
||||
Signal function to tell us we timed out.
|
||||
****************************************************************/
|
||||
|
||||
static void gotalarm_sig(void)
|
||||
{
|
||||
gotalarm = 1;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
Make a TDB_DATA and keep the const warning in one place
|
||||
****************************************************************/
|
||||
|
||||
static TDB_DATA make_tdb_data(const char *dptr, size_t dsize)
|
||||
{
|
||||
TDB_DATA ret;
|
||||
ret.dptr = dptr;
|
||||
ret.dsize = dsize;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Lock a chain with timeout (in seconds).
|
||||
****************************************************************************/
|
||||
|
||||
static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
|
||||
{
|
||||
/* Allow tdb_chainlock to be interrupted by an alarm. */
|
||||
int ret;
|
||||
gotalarm = 0;
|
||||
tdb_set_lock_alarm(&gotalarm);
|
||||
|
||||
if (timeout) {
|
||||
CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
|
||||
alarm(timeout);
|
||||
}
|
||||
|
||||
if (rw_type == F_RDLCK)
|
||||
ret = tdb_chainlock_read(tdb, key);
|
||||
else
|
||||
ret = tdb_chainlock(tdb, key);
|
||||
|
||||
if (timeout) {
|
||||
alarm(0);
|
||||
CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
|
||||
if (gotalarm) {
|
||||
DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
|
||||
timeout, key.dptr, tdb->name ));
|
||||
/* TODO: If we time out waiting for a lock, it might
|
||||
* be nice to use F_GETLK to get the pid of the
|
||||
* process currently holding the lock and print that
|
||||
* as part of the debugging message. -- mbp */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Write lock a chain. Return -1 if timeout or lock failed.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
|
||||
{
|
||||
return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Lock a chain by string. Return -1 if timeout or lock failed.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
|
||||
|
||||
return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Unlock a chain by string.
|
||||
****************************************************************************/
|
||||
|
||||
void tdb_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
|
||||
|
||||
tdb_chainunlock(tdb, key);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Read lock a chain by string. Return -1 if timeout or lock failed.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_read_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
|
||||
|
||||
return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Read unlock a chain by string.
|
||||
****************************************************************************/
|
||||
|
||||
void tdb_read_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
|
||||
|
||||
tdb_chainunlock_read(tdb, key);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Fetch a int32 value by a arbitrary blob key, return -1 if not found.
|
||||
Output is int32 in native byte order.
|
||||
****************************************************************************/
|
||||
|
||||
int32 tdb_fetch_int32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keyval, len);
|
||||
TDB_DATA data;
|
||||
int32 ret;
|
||||
|
||||
data = tdb_fetch(tdb, key);
|
||||
if (!data.dptr || data.dsize != sizeof(int32)) {
|
||||
SAFE_FREE(data.dptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = IVAL(data.dptr,0);
|
||||
SAFE_FREE(data.dptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Fetch a int32 value by string key, return -1 if not found.
|
||||
Output is int32 in native byte order.
|
||||
****************************************************************************/
|
||||
|
||||
int32 tdb_fetch_int32(TDB_CONTEXT *tdb, const char *keystr)
|
||||
{
|
||||
return tdb_fetch_int32_byblob(tdb, keystr, strlen(keystr) + 1);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Store a int32 value by an arbitary blob key, return 0 on success, -1 on failure.
|
||||
Input is int32 in native byte order. Output in tdb is in little-endian.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_store_int32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, int32 v)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keystr, len);
|
||||
TDB_DATA data;
|
||||
int32 v_store;
|
||||
|
||||
SIVAL(&v_store,0,v);
|
||||
data.dptr = (void *)&v_store;
|
||||
data.dsize = sizeof(int32);
|
||||
|
||||
return tdb_store(tdb, key, data, TDB_REPLACE);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Store a int32 value by string key, return 0 on success, -1 on failure.
|
||||
Input is int32 in native byte order. Output in tdb is in little-endian.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_store_int32(TDB_CONTEXT *tdb, const char *keystr, int32 v)
|
||||
{
|
||||
return tdb_store_int32_byblob(tdb, keystr, strlen(keystr) + 1, v);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Fetch a uint32 value by a arbitrary blob key, return -1 if not found.
|
||||
Output is uint32 in native byte order.
|
||||
****************************************************************************/
|
||||
|
||||
BOOL tdb_fetch_uint32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len, uint32 *value)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keyval, len);
|
||||
TDB_DATA data;
|
||||
|
||||
data = tdb_fetch(tdb, key);
|
||||
if (!data.dptr || data.dsize != sizeof(uint32)) {
|
||||
SAFE_FREE(data.dptr);
|
||||
return False;
|
||||
}
|
||||
|
||||
*value = IVAL(data.dptr,0);
|
||||
SAFE_FREE(data.dptr);
|
||||
return True;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Fetch a uint32 value by string key, return -1 if not found.
|
||||
Output is uint32 in native byte order.
|
||||
****************************************************************************/
|
||||
|
||||
BOOL tdb_fetch_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 *value)
|
||||
{
|
||||
return tdb_fetch_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Store a uint32 value by an arbitary blob key, return 0 on success, -1 on failure.
|
||||
Input is uint32 in native byte order. Output in tdb is in little-endian.
|
||||
****************************************************************************/
|
||||
|
||||
BOOL tdb_store_uint32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, uint32 value)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keystr, len);
|
||||
TDB_DATA data;
|
||||
uint32 v_store;
|
||||
BOOL ret = True;
|
||||
|
||||
SIVAL(&v_store, 0, value);
|
||||
data.dptr = (void *)&v_store;
|
||||
data.dsize = sizeof(uint32);
|
||||
|
||||
if (tdb_store(tdb, key, data, TDB_REPLACE) == -1)
|
||||
ret = False;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Store a uint32 value by string key, return 0 on success, -1 on failure.
|
||||
Input is uint32 in native byte order. Output in tdb is in little-endian.
|
||||
****************************************************************************/
|
||||
|
||||
BOOL tdb_store_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 value)
|
||||
{
|
||||
return tdb_store_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
|
||||
}
|
||||
/****************************************************************************
|
||||
Store a buffer by a null terminated string key. Return 0 on success, -1
|
||||
on failure.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_store_bystring(TDB_CONTEXT *tdb, const char *keystr, TDB_DATA data, int flags)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
|
||||
|
||||
return tdb_store(tdb, key, data, flags);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Fetch a buffer using a null terminated string key. Don't forget to call
|
||||
free() on the result dptr.
|
||||
****************************************************************************/
|
||||
|
||||
TDB_DATA tdb_fetch_bystring(TDB_CONTEXT *tdb, const char *keystr)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
|
||||
|
||||
return tdb_fetch(tdb, key);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Delete an entry using a null terminated string key.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_delete_bystring(TDB_CONTEXT *tdb, const char *keystr)
|
||||
{
|
||||
TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
|
||||
|
||||
return tdb_delete(tdb, key);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Atomic integer change. Returns old value. To create, set initial value in *oldval.
|
||||
****************************************************************************/
|
||||
|
||||
int32 tdb_change_int32_atomic(TDB_CONTEXT *tdb, const char *keystr, int32 *oldval, int32 change_val)
|
||||
{
|
||||
int32 val;
|
||||
int32 ret = -1;
|
||||
|
||||
if (tdb_lock_bystring(tdb, keystr,0) == -1)
|
||||
return -1;
|
||||
|
||||
if ((val = tdb_fetch_int32(tdb, keystr)) == -1) {
|
||||
/* The lookup failed */
|
||||
if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
|
||||
/* but not because it didn't exist */
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Start with 'old' value */
|
||||
val = *oldval;
|
||||
|
||||
} else {
|
||||
/* It worked, set return value (oldval) to tdb data */
|
||||
*oldval = val;
|
||||
}
|
||||
|
||||
/* Increment value for storage and return next time */
|
||||
val += change_val;
|
||||
|
||||
if (tdb_store_int32(tdb, keystr, val) == -1)
|
||||
goto err_out;
|
||||
|
||||
ret = 0;
|
||||
|
||||
err_out:
|
||||
|
||||
tdb_unlock_bystring(tdb, keystr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval.
|
||||
****************************************************************************/
|
||||
|
||||
BOOL tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val)
|
||||
{
|
||||
uint32 val;
|
||||
BOOL ret = False;
|
||||
|
||||
if (tdb_lock_bystring(tdb, keystr,0) == -1)
|
||||
return False;
|
||||
|
||||
if (!tdb_fetch_uint32(tdb, keystr, &val)) {
|
||||
/* It failed */
|
||||
if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
|
||||
/* and not because it didn't exist */
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Start with 'old' value */
|
||||
val = *oldval;
|
||||
|
||||
} else {
|
||||
/* it worked, set return value (oldval) to tdb data */
|
||||
*oldval = val;
|
||||
|
||||
}
|
||||
|
||||
/* get a new value to store */
|
||||
val += change_val;
|
||||
|
||||
if (!tdb_store_uint32(tdb, keystr, val))
|
||||
goto err_out;
|
||||
|
||||
ret = True;
|
||||
|
||||
err_out:
|
||||
|
||||
tdb_unlock_bystring(tdb, keystr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Useful pair of routines for packing/unpacking data consisting of
|
||||
integers and strings.
|
||||
****************************************************************************/
|
||||
|
||||
size_t tdb_pack(char *buf, int bufsize, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
uint8 bt;
|
||||
uint16 w;
|
||||
uint32 d;
|
||||
int i;
|
||||
void *p;
|
||||
int len;
|
||||
char *s;
|
||||
char c;
|
||||
char *buf0 = buf;
|
||||
const char *fmt0 = fmt;
|
||||
int bufsize0 = bufsize;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
while (*fmt) {
|
||||
switch ((c = *fmt++)) {
|
||||
case 'b': /* unsigned 8-bit integer */
|
||||
len = 1;
|
||||
bt = (uint8)va_arg(ap, int);
|
||||
if (bufsize && bufsize >= len)
|
||||
SSVAL(buf, 0, bt);
|
||||
break;
|
||||
case 'w': /* unsigned 16-bit integer */
|
||||
len = 2;
|
||||
w = (uint16)va_arg(ap, int);
|
||||
if (bufsize && bufsize >= len)
|
||||
SSVAL(buf, 0, w);
|
||||
break;
|
||||
case 'd': /* signed 32-bit integer (standard int in most systems) */
|
||||
len = 4;
|
||||
d = va_arg(ap, uint32);
|
||||
if (bufsize && bufsize >= len)
|
||||
SIVAL(buf, 0, d);
|
||||
break;
|
||||
case 'p': /* pointer */
|
||||
len = 4;
|
||||
p = va_arg(ap, void *);
|
||||
d = p?1:0;
|
||||
if (bufsize && bufsize >= len)
|
||||
SIVAL(buf, 0, d);
|
||||
break;
|
||||
case 'P': /* null-terminated string */
|
||||
s = va_arg(ap,char *);
|
||||
w = strlen(s);
|
||||
len = w + 1;
|
||||
if (bufsize && bufsize >= len)
|
||||
memcpy(buf, s, len);
|
||||
break;
|
||||
case 'f': /* null-terminated string */
|
||||
s = va_arg(ap,char *);
|
||||
w = strlen(s);
|
||||
len = w + 1;
|
||||
if (bufsize && bufsize >= len)
|
||||
memcpy(buf, s, len);
|
||||
break;
|
||||
case 'B': /* fixed-length string */
|
||||
i = va_arg(ap, int);
|
||||
s = va_arg(ap, char *);
|
||||
len = 4+i;
|
||||
if (bufsize && bufsize >= len) {
|
||||
SIVAL(buf, 0, i);
|
||||
memcpy(buf+4, s, i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG(0,("Unknown tdb_pack format %c in %s\n",
|
||||
c, fmt));
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
buf += len;
|
||||
if (bufsize)
|
||||
bufsize -= len;
|
||||
if (bufsize < 0)
|
||||
bufsize = 0;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
DEBUG(18,("tdb_pack(%s, %d) -> %d\n",
|
||||
fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
|
||||
|
||||
return PTR_DIFF(buf, buf0);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Useful pair of routines for packing/unpacking data consisting of
|
||||
integers and strings.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_unpack(char *buf, int bufsize, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
uint8 *bt;
|
||||
uint16 *w;
|
||||
uint32 *d;
|
||||
int len;
|
||||
int *i;
|
||||
void **p;
|
||||
char *s, **b;
|
||||
char c;
|
||||
char *buf0 = buf;
|
||||
const char *fmt0 = fmt;
|
||||
int bufsize0 = bufsize;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
while (*fmt) {
|
||||
switch ((c=*fmt++)) {
|
||||
case 'b':
|
||||
len = 1;
|
||||
bt = va_arg(ap, uint8 *);
|
||||
if (bufsize < len)
|
||||
goto no_space;
|
||||
*bt = SVAL(buf, 0);
|
||||
break;
|
||||
case 'w':
|
||||
len = 2;
|
||||
w = va_arg(ap, uint16 *);
|
||||
if (bufsize < len)
|
||||
goto no_space;
|
||||
*w = SVAL(buf, 0);
|
||||
break;
|
||||
case 'd':
|
||||
len = 4;
|
||||
d = va_arg(ap, uint32 *);
|
||||
if (bufsize < len)
|
||||
goto no_space;
|
||||
*d = IVAL(buf, 0);
|
||||
break;
|
||||
case 'p':
|
||||
len = 4;
|
||||
p = va_arg(ap, void **);
|
||||
if (bufsize < len)
|
||||
goto no_space;
|
||||
*p = (void *)IVAL(buf, 0);
|
||||
break;
|
||||
case 'P':
|
||||
s = va_arg(ap,char *);
|
||||
len = strlen(buf) + 1;
|
||||
if (bufsize < len || len > sizeof(pstring))
|
||||
goto no_space;
|
||||
memcpy(s, buf, len);
|
||||
break;
|
||||
case 'f':
|
||||
s = va_arg(ap,char *);
|
||||
len = strlen(buf) + 1;
|
||||
if (bufsize < len || len > sizeof(fstring))
|
||||
goto no_space;
|
||||
memcpy(s, buf, len);
|
||||
break;
|
||||
case 'B':
|
||||
i = va_arg(ap, int *);
|
||||
b = va_arg(ap, char **);
|
||||
len = 4;
|
||||
if (bufsize < len)
|
||||
goto no_space;
|
||||
*i = IVAL(buf, 0);
|
||||
if (! *i) {
|
||||
*b = NULL;
|
||||
break;
|
||||
}
|
||||
len += *i;
|
||||
if (bufsize < len)
|
||||
goto no_space;
|
||||
*b = (char *)malloc(*i);
|
||||
if (! *b)
|
||||
goto no_space;
|
||||
memcpy(*b, buf+4, *i);
|
||||
break;
|
||||
default:
|
||||
DEBUG(0,("Unknown tdb_unpack format %c in %s\n",
|
||||
c, fmt));
|
||||
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
buf += len;
|
||||
bufsize -= len;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
DEBUG(18,("tdb_unpack(%s, %d) -> %d\n",
|
||||
fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
|
||||
|
||||
return PTR_DIFF(buf, buf0);
|
||||
|
||||
no_space:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pack SID passed by pointer
|
||||
*
|
||||
* @param pack_buf pointer to buffer which is to be filled with packed data
|
||||
* @param bufsize size of packing buffer
|
||||
* @param sid pointer to sid to be packed
|
||||
*
|
||||
* @return length of the packed representation of the whole structure
|
||||
**/
|
||||
size_t tdb_sid_pack(char* pack_buf, int bufsize, DOM_SID* sid)
|
||||
{
|
||||
int idx;
|
||||
size_t len = 0;
|
||||
|
||||
if (!sid || !pack_buf) return -1;
|
||||
|
||||
len += tdb_pack(pack_buf + len, bufsize - len, "bb", sid->sid_rev_num,
|
||||
sid->num_auths);
|
||||
|
||||
for (idx = 0; idx < 6; idx++) {
|
||||
len += tdb_pack(pack_buf + len, bufsize - len, "b", sid->id_auth[idx]);
|
||||
}
|
||||
|
||||
for (idx = 0; idx < MAXSUBAUTHS; idx++) {
|
||||
len += tdb_pack(pack_buf + len, bufsize - len, "d", sid->sub_auths[idx]);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unpack SID into a pointer
|
||||
*
|
||||
* @param pack_buf pointer to buffer with packed representation
|
||||
* @param bufsize size of the buffer
|
||||
* @param sid pointer to sid structure to be filled with unpacked data
|
||||
*
|
||||
* @return size of structure unpacked from buffer
|
||||
**/
|
||||
size_t tdb_sid_unpack(char* pack_buf, int bufsize, DOM_SID* sid)
|
||||
{
|
||||
int idx, len = 0;
|
||||
|
||||
if (!sid || !pack_buf) return -1;
|
||||
|
||||
len += tdb_unpack(pack_buf + len, bufsize - len, "bb",
|
||||
&sid->sid_rev_num, &sid->num_auths);
|
||||
|
||||
for (idx = 0; idx < 6; idx++) {
|
||||
len += tdb_unpack(pack_buf + len, bufsize - len, "b", &sid->id_auth[idx]);
|
||||
}
|
||||
|
||||
for (idx = 0; idx < MAXSUBAUTHS; idx++) {
|
||||
len += tdb_unpack(pack_buf + len, bufsize - len, "d", &sid->sub_auths[idx]);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pack TRUSTED_DOM_PASS passed by pointer
|
||||
*
|
||||
* @param pack_buf pointer to buffer which is to be filled with packed data
|
||||
* @param bufsize size of the buffer
|
||||
* @param pass pointer to trusted domain password to be packed
|
||||
*
|
||||
* @return length of the packed representation of the whole structure
|
||||
**/
|
||||
size_t tdb_trusted_dom_pass_pack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
|
||||
{
|
||||
int idx, len = 0;
|
||||
|
||||
if (!pack_buf || !pass) return -1;
|
||||
|
||||
/* packing unicode domain name and password */
|
||||
len += tdb_pack(pack_buf + len, bufsize - len, "d", pass->uni_name_len);
|
||||
|
||||
for (idx = 0; idx < 32; idx++)
|
||||
len += tdb_pack(pack_buf + len, bufsize - len, "w", pass->uni_name[idx]);
|
||||
|
||||
len += tdb_pack(pack_buf + len, bufsize - len, "dPd", pass->pass_len,
|
||||
pass->pass, pass->mod_time);
|
||||
|
||||
/* packing SID structure */
|
||||
len += tdb_sid_pack(pack_buf + len, bufsize - len, &pass->domain_sid);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unpack TRUSTED_DOM_PASS passed by pointer
|
||||
*
|
||||
* @param pack_buf pointer to buffer with packed representation
|
||||
* @param bufsize size of the buffer
|
||||
* @param pass pointer to trusted domain password to be filled with unpacked data
|
||||
*
|
||||
* @return size of structure unpacked from buffer
|
||||
**/
|
||||
size_t tdb_trusted_dom_pass_unpack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
|
||||
{
|
||||
int idx, len = 0;
|
||||
|
||||
if (!pack_buf || !pass) return -1;
|
||||
|
||||
/* unpack unicode domain name and plaintext password */
|
||||
len += tdb_unpack(pack_buf, bufsize - len, "d", &pass->uni_name_len);
|
||||
|
||||
for (idx = 0; idx < 32; idx++)
|
||||
len += tdb_unpack(pack_buf + len, bufsize - len, "w", &pass->uni_name[idx]);
|
||||
|
||||
len += tdb_unpack(pack_buf + len, bufsize - len, "dPd", &pass->pass_len, &pass->pass,
|
||||
&pass->mod_time);
|
||||
|
||||
/* unpack domain sid */
|
||||
len += tdb_sid_unpack(pack_buf + len, bufsize - len, &pass->domain_sid);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Log tdb messages via DEBUG().
|
||||
****************************************************************************/
|
||||
|
||||
static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *ptr = NULL;
|
||||
|
||||
va_start(ap, format);
|
||||
vasprintf(&ptr, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (!ptr || !*ptr)
|
||||
return;
|
||||
|
||||
DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
|
||||
SAFE_FREE(ptr);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Like tdb_open() but also setup a logging function that redirects to
|
||||
the samba DEBUG() system.
|
||||
****************************************************************************/
|
||||
|
||||
TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags,
|
||||
int open_flags, mode_t mode)
|
||||
{
|
||||
TDB_CONTEXT *tdb;
|
||||
|
||||
if (!lp_use_mmap())
|
||||
tdb_flags |= TDB_NOMMAP;
|
||||
|
||||
tdb = tdb_open_ex(name, hash_size, tdb_flags,
|
||||
open_flags, mode, tdb_log);
|
||||
if (!tdb)
|
||||
return NULL;
|
||||
|
||||
return tdb;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Allow tdb_delete to be used as a tdb_traversal_fn.
|
||||
****************************************************************************/
|
||||
|
||||
int tdb_traverse_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
|
||||
void *state)
|
||||
{
|
||||
return tdb_delete(the_tdb, key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Search across the whole tdb for keys that match the given pattern
|
||||
* return the result as a list of keys
|
||||
*
|
||||
* @param tdb pointer to opened tdb file context
|
||||
* @param pattern searching pattern used by fnmatch(3) functions
|
||||
*
|
||||
* @return list of keys found by looking up with given pattern
|
||||
**/
|
||||
TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
|
||||
{
|
||||
TDB_DATA key, next;
|
||||
TDB_LIST_NODE *list = NULL;
|
||||
TDB_LIST_NODE *rec = NULL;
|
||||
TDB_LIST_NODE *tmp = NULL;
|
||||
|
||||
for (key = tdb_firstkey(tdb); key.dptr; key = next) {
|
||||
/* duplicate key string to ensure null-termination */
|
||||
char *key_str = (char*) strndup(key.dptr, key.dsize);
|
||||
if (!key_str) {
|
||||
DEBUG(0, ("tdb_search_keys: strndup() failed!\n"));
|
||||
smb_panic("strndup failed!\n");
|
||||
}
|
||||
|
||||
DEBUG(18, ("checking %s for match to pattern %s\n", key_str, pattern));
|
||||
|
||||
next = tdb_nextkey(tdb, key);
|
||||
|
||||
/* do the pattern checking */
|
||||
if (fnmatch(pattern, key_str, 0) == 0) {
|
||||
rec = (TDB_LIST_NODE*) malloc(sizeof(*rec));
|
||||
ZERO_STRUCTP(rec);
|
||||
|
||||
rec->node_key = key;
|
||||
|
||||
DLIST_ADD_END(list, rec, tmp);
|
||||
|
||||
DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
|
||||
} else {
|
||||
free(key.dptr);
|
||||
}
|
||||
|
||||
/* free duplicated key string */
|
||||
free(key_str);
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Free the list returned by tdb_search_keys
|
||||
*
|
||||
* @param node list of results found by tdb_search_keys
|
||||
**/
|
||||
void tdb_search_list_free(TDB_LIST_NODE* node)
|
||||
{
|
||||
TDB_LIST_NODE *next_node;
|
||||
|
||||
while (node) {
|
||||
next_node = node->next;
|
||||
SAFE_FREE(node->node_key.dptr);
|
||||
SAFE_FREE(node);
|
||||
node = next_node;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
tdb utility functions
|
||||
Copyright (C) Andrew Tridgell 1999
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __TDBUTIL_H__
|
||||
#define __TDBUTIL_H__
|
||||
|
||||
|
||||
/* single node of a list returned by tdb_search_keys */
|
||||
typedef struct keys_node
|
||||
{
|
||||
struct keys_node *prev, *next;
|
||||
TDB_DATA node_key;
|
||||
} TDB_LIST_NODE;
|
||||
|
||||
|
||||
TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT*, const char*);
|
||||
void tdb_search_list_free(TDB_LIST_NODE*);
|
||||
|
||||
|
||||
#endif /* __TDBUTIL_H__ */
|
Loading…
Reference in New Issue