00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "tdb_private.h"
00030
00031
00032 static struct tdb_context *tdbs = NULL;
00033
00034
00035
00036 static unsigned int default_tdb_hash(TDB_DATA *key)
00037 {
00038 u32 value;
00039 u32 i;
00040
00041
00042 for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
00043 value = (value + (key->dptr[i] << (i*5 % 24)));
00044
00045 return (1103515243 * value + 12345);
00046 }
00047
00048
00049
00050 static int tdb_new_database(struct tdb_context *tdb, int hash_size)
00051 {
00052 struct tdb_header *newdb;
00053 size_t size;
00054 int ret = -1;
00055 ssize_t written;
00056
00057
00058 size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t);
00059 if (!(newdb = (struct tdb_header *)calloc(size, 1)))
00060 return TDB_ERRCODE(TDB_ERR_OOM, -1);
00061
00062
00063 newdb->version = TDB_VERSION;
00064 newdb->hash_size = hash_size;
00065 if (tdb->flags & TDB_INTERNAL) {
00066 tdb->map_size = size;
00067 tdb->map_ptr = (char *)newdb;
00068 memcpy(&tdb->header, newdb, sizeof(tdb->header));
00069
00070 CONVERT(*newdb);
00071 return 0;
00072 }
00073 if (lseek(tdb->fd, 0, SEEK_SET) == -1)
00074 goto fail;
00075
00076 if (ftruncate(tdb->fd, 0) == -1)
00077 goto fail;
00078
00079
00080 CONVERT(*newdb);
00081 memcpy(&tdb->header, newdb, sizeof(tdb->header));
00082
00083 memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1);
00084
00085 written = write(tdb->fd, newdb, size);
00086 if (written == size) {
00087 ret = 0;
00088 } else if (written != -1) {
00089
00090
00091 size -= written;
00092 written = write(tdb->fd, newdb+written, size);
00093 if (written == size) {
00094 ret = 0;
00095 } else if (written >= 0) {
00096
00097
00098 errno = ENOSPC;
00099 }
00100 }
00101
00102 fail:
00103 SAFE_FREE(newdb);
00104 return ret;
00105 }
00106
00107
00108
00109 static int tdb_already_open(dev_t device,
00110 ino_t ino)
00111 {
00112 struct tdb_context *i;
00113
00114 for (i = tdbs; i; i = i->next) {
00115 if (i->device == device && i->inode == ino) {
00116 return 1;
00117 }
00118 }
00119
00120 return 0;
00121 }
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
00134 int open_flags, mode_t mode)
00135 {
00136 return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
00137 }
00138
00139
00140 static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
00141 static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
00142 {
00143 }
00144
00145
00146 struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
00147 int open_flags, mode_t mode,
00148 const struct tdb_logging_context *log_ctx,
00149 tdb_hash_func hash_fn)
00150 {
00151 struct tdb_context *tdb;
00152 struct stat st;
00153 int rev = 0, locked = 0;
00154 unsigned char *vp;
00155 u32 vertest;
00156
00157 if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
00158
00159 errno = ENOMEM;
00160 goto fail;
00161 }
00162 tdb_io_init(tdb);
00163 tdb->fd = -1;
00164 tdb->name = NULL;
00165 tdb->map_ptr = NULL;
00166 tdb->flags = tdb_flags;
00167 tdb->open_flags = open_flags;
00168 if (log_ctx) {
00169 tdb->log = *log_ctx;
00170 } else {
00171 tdb->log.log_fn = null_log_fn;
00172 tdb->log.log_private = NULL;
00173 }
00174 tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash;
00175
00176
00177 tdb->page_size = getpagesize();
00178 if (tdb->page_size <= 0) {
00179 tdb->page_size = 0x2000;
00180 }
00181
00182 if ((open_flags & O_ACCMODE) == O_WRONLY) {
00183 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n",
00184 name));
00185 errno = EINVAL;
00186 goto fail;
00187 }
00188
00189 if (hash_size == 0)
00190 hash_size = DEFAULT_HASH_SIZE;
00191 if ((open_flags & O_ACCMODE) == O_RDONLY) {
00192 tdb->read_only = 1;
00193
00194 tdb->flags |= TDB_NOLOCK;
00195 tdb->flags &= ~TDB_CLEAR_IF_FIRST;
00196 }
00197
00198
00199 if (tdb->flags & TDB_INTERNAL) {
00200 tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP);
00201 tdb->flags &= ~TDB_CLEAR_IF_FIRST;
00202 if (tdb_new_database(tdb, hash_size) != 0) {
00203 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: tdb_new_database failed!"));
00204 goto fail;
00205 }
00206 goto internal;
00207 }
00208
00209 if ((tdb->fd = open(name, open_flags, mode)) == -1) {
00210 TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_open_ex: could not open file %s: %s\n",
00211 name, strerror(errno)));
00212 goto fail;
00213 }
00214
00215
00216 if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
00217 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n",
00218 name, strerror(errno)));
00219 goto fail;
00220 }
00221
00222
00223 if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
00224 (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0, 1) == 0))) {
00225 open_flags |= O_CREAT;
00226 if (ftruncate(tdb->fd, 0) == -1) {
00227 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
00228 "failed to truncate %s: %s\n",
00229 name, strerror(errno)));
00230 goto fail;
00231 }
00232 }
00233
00234 errno = 0;
00235 if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header)
00236 || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0
00237 || (tdb->header.version != TDB_VERSION
00238 && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) {
00239
00240 if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
00241 if (errno == 0) {
00242 errno = EIO;
00243 }
00244 goto fail;
00245 }
00246 rev = (tdb->flags & TDB_CONVERT);
00247 }
00248 vp = (unsigned char *)&tdb->header.version;
00249 vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) |
00250 (((u32)vp[2]) << 8) | (u32)vp[3];
00251 tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0;
00252 if (!rev)
00253 tdb->flags &= ~TDB_CONVERT;
00254 else {
00255 tdb->flags |= TDB_CONVERT;
00256 tdb_convert(&tdb->header, sizeof(tdb->header));
00257 }
00258 if (fstat(tdb->fd, &st) == -1)
00259 goto fail;
00260
00261 if (tdb->header.rwlocks != 0) {
00262 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n"));
00263 goto fail;
00264 }
00265
00266
00267 if (tdb_already_open(st.st_dev, st.st_ino)) {
00268 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
00269 "%s (%d,%d) is already open in this process\n",
00270 name, (int)st.st_dev, (int)st.st_ino));
00271 errno = EBUSY;
00272 goto fail;
00273 }
00274
00275 if (!(tdb->name = (char *)strdup(name))) {
00276 errno = ENOMEM;
00277 goto fail;
00278 }
00279
00280 tdb->map_size = st.st_size;
00281 tdb->device = st.st_dev;
00282 tdb->inode = st.st_ino;
00283 tdb->max_dead_records = 0;
00284 tdb_mmap(tdb);
00285 if (locked) {
00286 if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) {
00287 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
00288 "failed to take ACTIVE_LOCK on %s: %s\n",
00289 name, strerror(errno)));
00290 goto fail;
00291 }
00292
00293 }
00294
00295
00296
00297
00298
00299 if (tdb_flags & TDB_CLEAR_IF_FIRST) {
00300
00301 if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)
00302 goto fail;
00303 }
00304
00305
00306 if (tdb_transaction_recover(tdb) == -1) {
00307 goto fail;
00308 }
00309
00310 internal:
00311
00312
00313
00314 if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1) == -1)
00315 goto fail;
00316 tdb->next = tdbs;
00317 tdbs = tdb;
00318 return tdb;
00319
00320 fail:
00321 { int save_errno = errno;
00322
00323 if (!tdb)
00324 return NULL;
00325
00326 if (tdb->map_ptr) {
00327 if (tdb->flags & TDB_INTERNAL)
00328 SAFE_FREE(tdb->map_ptr);
00329 else
00330 tdb_munmap(tdb);
00331 }
00332 SAFE_FREE(tdb->name);
00333 if (tdb->fd != -1)
00334 if (close(tdb->fd) != 0)
00335 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
00336 SAFE_FREE(tdb);
00337 errno = save_errno;
00338 return NULL;
00339 }
00340 }
00341
00342
00343
00344
00345
00346 void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)
00347 {
00348 tdb->max_dead_records = max_dead;
00349 }
00350
00351
00352
00353
00354
00355
00356 int tdb_close(struct tdb_context *tdb)
00357 {
00358 struct tdb_context **i;
00359 int ret = 0;
00360
00361 if (tdb->transaction) {
00362 tdb_transaction_cancel(tdb);
00363 }
00364
00365 if (tdb->map_ptr) {
00366 if (tdb->flags & TDB_INTERNAL)
00367 SAFE_FREE(tdb->map_ptr);
00368 else
00369 tdb_munmap(tdb);
00370 }
00371 SAFE_FREE(tdb->name);
00372 if (tdb->fd != -1)
00373 ret = close(tdb->fd);
00374 SAFE_FREE(tdb->lockrecs);
00375
00376
00377 for (i = &tdbs; *i; i = &(*i)->next) {
00378 if (*i == tdb) {
00379 *i = tdb->next;
00380 break;
00381 }
00382 }
00383
00384 memset(tdb, 0, sizeof(*tdb));
00385 SAFE_FREE(tdb);
00386
00387 return ret;
00388 }
00389
00390
00391 void tdb_set_logging_function(struct tdb_context *tdb,
00392 const struct tdb_logging_context *log_ctx)
00393 {
00394 tdb->log = *log_ctx;
00395 }
00396
00397 void *tdb_get_logging_private(struct tdb_context *tdb)
00398 {
00399 return tdb->log.log_private;
00400 }
00401
00402
00403
00404 int tdb_reopen(struct tdb_context *tdb)
00405 {
00406 struct stat st;
00407
00408 if (tdb->flags & TDB_INTERNAL) {
00409 return 0;
00410 }
00411
00412 if (tdb->num_locks != 0 || tdb->global_lock.count) {
00413 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n"));
00414 goto fail;
00415 }
00416
00417 if (tdb->transaction != 0) {
00418 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed inside a transaction\n"));
00419 goto fail;
00420 }
00421
00422 if (tdb_munmap(tdb) != 0) {
00423 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno)));
00424 goto fail;
00425 }
00426 if (close(tdb->fd) != 0)
00427 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: WARNING closing tdb->fd failed!\n"));
00428 tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0);
00429 if (tdb->fd == -1) {
00430 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno)));
00431 goto fail;
00432 }
00433 if ((tdb->flags & TDB_CLEAR_IF_FIRST) &&
00434 (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) {
00435 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n"));
00436 goto fail;
00437 }
00438 if (fstat(tdb->fd, &st) != 0) {
00439 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno)));
00440 goto fail;
00441 }
00442 if (st.st_ino != tdb->inode || st.st_dev != tdb->device) {
00443 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n"));
00444 goto fail;
00445 }
00446 tdb_mmap(tdb);
00447
00448 return 0;
00449
00450 fail:
00451 tdb_close(tdb);
00452 return -1;
00453 }
00454
00455
00456 int tdb_reopen_all(int parent_longlived)
00457 {
00458 struct tdb_context *tdb;
00459
00460 for (tdb=tdbs; tdb; tdb = tdb->next) {
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472 if (parent_longlived) {
00473
00474 tdb->flags &= ~TDB_CLEAR_IF_FIRST;
00475 }
00476
00477 if (tdb_reopen(tdb) != 0)
00478 return -1;
00479 }
00480
00481 return 0;
00482 }