Changeset 745 for trunk/server/lib/tdb
- Timestamp:
- Nov 27, 2012, 4:43:17 PM (13 years ago)
- Location:
- trunk/server
- Files:
-
- 15 deleted
- 27 edited
- 20 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/server
- Property svn:mergeinfo changed
/vendor/current merged: 581,587,591,594,597,600,615,618,740
- Property svn:mergeinfo changed
-
trunk/server/lib/tdb/common/check.c
r414 r745 29 29 { 30 30 struct tdb_header hdr; 31 32 if (tdb->methods->tdb_read(tdb, 0, &hdr, sizeof(hdr), DOCONV()) == -1) 31 uint32_t h1, h2; 32 33 if (tdb->methods->tdb_read(tdb, 0, &hdr, sizeof(hdr), 0) == -1) 33 34 return false; 34 35 if (strcmp(hdr.magic_food, TDB_MAGIC_FOOD) != 0) … … 39 40 goto corrupt; 40 41 41 if (hdr.rwlocks != 0) 42 if (hdr.rwlocks != 0 && hdr.rwlocks != TDB_HASH_RWLOCK_MAGIC) 43 goto corrupt; 44 45 tdb_header_hash(tdb, &h1, &h2); 46 if (hdr.magic1_hash && hdr.magic2_hash && 47 (hdr.magic1_hash != h1 || hdr.magic2_hash != h2)) 42 48 goto corrupt; 43 49 … … 302 308 } 303 309 304 int tdb_check(struct tdb_context *tdb, 310 /* Slow, but should be very rare. */ 311 size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off) 312 { 313 size_t len; 314 315 for (len = 0; off + len < tdb->map_size; len++) { 316 char c; 317 if (tdb->methods->tdb_read(tdb, off, &c, 1, 0)) 318 return 0; 319 if (c != 0 && c != 0x42) 320 break; 321 } 322 return len; 323 } 324 325 _PUBLIC_ int tdb_check(struct tdb_context *tdb, 305 326 int (*check)(TDB_DATA key, TDB_DATA data, void *private_data), 306 327 void *private_data) … … 311 332 struct tdb_record rec; 312 333 bool found_recovery = false; 313 314 if (tdb_lockall(tdb) == -1) 315 return -1; 334 tdb_len_t dead; 335 bool locked; 336 337 /* Read-only databases use no locking at all: it's best-effort. 338 * We may have a write lock already, so skip that case too. */ 339 if (tdb->read_only || tdb->allrecord_lock.count != 0) { 340 locked = false; 341 } else { 342 if (tdb_lockall_read(tdb) == -1) 343 return -1; 344 locked = true; 345 } 316 346 317 347 /* Make sure we know true size of the underlying file. */ … … 370 400 goto free; 371 401 break; 402 /* If we crash after ftruncate, we can get zeroes or fill. */ 403 case TDB_RECOVERY_INVALID_MAGIC: 404 case 0x42424242: 405 if (recovery_start == off) { 406 found_recovery = true; 407 break; 408 } 409 dead = tdb_dead_space(tdb, off); 410 if (dead < sizeof(rec)) 411 goto corrupt; 412 413 TDB_LOG((tdb, TDB_DEBUG_ERROR, 414 "Dead space at %d-%d (of %u)\n", 415 off, off + dead, tdb->map_size)); 416 rec.rec_len = dead - sizeof(rec); 417 break; 372 418 case TDB_RECOVERY_MAGIC: 373 case 0: /* Used for invalid (or in-progress) recovery area. */374 419 if (recovery_start != off) { 375 420 TDB_LOG((tdb, TDB_DEBUG_ERROR, … … 380 425 found_recovery = true; 381 426 break; 382 default: 427 default: ; 428 corrupt: 383 429 tdb->ecode = TDB_ERR_CORRUPT; 384 430 TDB_LOG((tdb, TDB_DEBUG_ERROR, … … 406 452 if (recovery_start != 0 && !found_recovery) { 407 453 TDB_LOG((tdb, TDB_DEBUG_ERROR, 408 "Expected %s recovery area, got %s\n", 409 recovery_start ? "a" : "no", 410 found_recovery ? "one" : "none")); 454 "Expected a recovery area at %u\n", 455 recovery_start)); 411 456 goto free; 412 457 } 413 458 414 459 free(hashes); 415 tdb_unlockall(tdb); 460 if (locked) { 461 tdb_unlockall_read(tdb); 462 } 416 463 return 0; 417 464 … … 419 466 free(hashes); 420 467 unlock: 421 tdb_unlockall(tdb); 468 if (locked) { 469 tdb_unlockall_read(tdb); 470 } 422 471 return -1; 423 472 } -
trunk/server/lib/tdb/common/dump.c
r414 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 81 81 } 82 82 83 void tdb_dump_all(struct tdb_context *tdb)83 _PUBLIC_ void tdb_dump_all(struct tdb_context *tdb) 84 84 { 85 85 int i; … … 91 91 } 92 92 93 int tdb_printfreelist(struct tdb_context *tdb)93 _PUBLIC_ int tdb_printfreelist(struct tdb_context *tdb) 94 94 { 95 95 int ret; -
trunk/server/lib/tdb/common/error.c
r414 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 28 28 #include "tdb_private.h" 29 29 30 enum TDB_ERROR tdb_error(struct tdb_context *tdb)30 _PUBLIC_ enum TDB_ERROR tdb_error(struct tdb_context *tdb) 31 31 { 32 32 return tdb->ecode; … … 47 47 48 48 /* Error string for the last tdb error */ 49 const char *tdb_errorstr(struct tdb_context *tdb)49 _PUBLIC_ const char *tdb_errorstr(struct tdb_context *tdb) 50 50 { 51 51 uint32_t i; -
trunk/server/lib/tdb/common/freelist.c
r414 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 99 99 100 100 /* Add an element into the freelist. Merge adjacent records if 101 nec cessary. */101 necessary. */ 102 102 int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec) 103 103 { … … 144 144 struct tdb_record l; 145 145 tdb_off_t leftsize; 146 146 147 147 /* Read in tailer and jump back to header */ 148 148 if (tdb_ofs_read(tdb, left, &leftsize) == -1) { … … 335 335 break; 336 336 } 337 337 338 338 /* this multiplier means we only extremely rarely 339 339 search more than 50 or so records. At 50 records we … … 368 368 return the size of the freelist - used to decide if we should repack 369 369 */ 370 int tdb_freelist_size(struct tdb_context *tdb)370 _PUBLIC_ int tdb_freelist_size(struct tdb_context *tdb) 371 371 { 372 372 tdb_off_t ptr; -
trunk/server/lib/tdb/common/freelistcheck.c
r414 r745 44 44 } 45 45 46 int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries)46 _PUBLIC_ int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries) 47 47 { 48 48 struct tdb_context *mem_tdb = NULL; -
trunk/server/lib/tdb/common/io.c
r599 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 94 94 // the owner (us) is not allowed to write to the file (different from unix) 95 95 TDB_LOG((tdb, TDB_DEBUG_TRACE,"unlocking at %d len=%d before writing.\n", off, len)); 96 tdb_br lock( tdb, off, F_UNLCK, F_SETLK, 0, 1);96 tdb_brunlock( tdb, F_RDLCK, off, len); 97 97 // if a wider previous lock is in effect, we cannot write lock our segment 98 98 // (e.g. a lock_upgrade locks all the file), so we hope the previous lock 99 99 // is a write lock: do not wait for lock. 100 tdb_brlock( tdb, off, F_WRLCK, F_SETLK, 0, len);100 tdb_brlock( tdb, F_WRLCK, off, len, F_SETLK); 101 101 #endif 102 102 … … 126 126 "write %d bytes at %d in two attempts\n", 127 127 len, off)); 128 129 128 #ifdef __OS2__ // remove our lock 130 tdb_br lock( tdb, off, F_UNLCK, F_SETLK, 0, len);129 tdb_brunlock( tdb, F_WRLCK, off, len); 131 130 #endif 132 131 return -1; 133 132 } 134 133 } 135 136 134 #ifdef __OS2__ // remove our lock 137 tdb_brlock( tdb, off, F_UNLCK, F_SETLK, 0, len);135 tdb_brunlock( tdb, F_WRLCK, off, len); 138 136 #endif 139 140 137 return 0; 141 138 } … … 497 494 tdb_oob, 498 495 tdb_expand_file, 499 tdb_brlock500 496 }; 501 497 -
trunk/server/lib/tdb/common/lock.c
r647 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 27 27 28 28 #include "tdb_private.h" 29 30 #define TDB_MARK_LOCK 0x8000000031 32 29 #ifdef __OS2__ 33 30 34 static char* lock_type( int lck) 35 { 36 static char buffer[16]; 37 switch(lck) { 38 case F_GETLK: return "F_GETLK"; 39 case F_SETLK: return "F_SETLK"; 40 case F_SETLKW: return "F_SETLKW"; 41 default: 42 sprintf( buffer, "unknown %d", lck); 43 } 44 return buffer; 31 static char* lock_type( bool waitflag) 32 { 33 if (waitflag) 34 return "F_SETLKW"; 35 else 36 return "F_SETLK"; 45 37 } 46 38 … … 59 51 60 52 static int _mutex_brlock(struct tdb_context *tdb, tdb_off_t offset, 61 int rw_type, int lck_type, int probe, size_t len)53 int rw_type, bool waitflag, size_t len) 62 54 { 63 55 HMTX hSem; … … 66 58 67 59 switch( offset) { 68 case GLOBAL_LOCK:60 case OPEN_LOCK: 69 61 hSem = tdb->hGlobalLock; 70 62 break; … … 86 78 TDB_LOG((tdb, TDB_DEBUG_TRACE,"_mutex_brlock handle %d, offset %d\n", hSem, offset)); 87 79 88 if ( lck_type == F_SETLKW)80 if (waitflag) 89 81 ulTimeout = SEM_INDEFINITE_WAIT; 90 82 else … … 112 104 errno = EINVAL; 113 105 TDB_LOG(( tdb, TDB_DEBUG_ERROR, "_mutex_brlock pid %X, failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d, rc=%d\n", 114 getpid(), tdb->fd, offset, rw_type, lck_type, (int)len, rc));106 getpid(), tdb->fd, offset, rw_type, waitflag, (int)len, rc)); 115 107 tdb->ecode = TDB_ERR_LOCK; 116 108 return -1; … … 118 110 #endif 119 111 120 void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)112 _PUBLIC_ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr) 121 113 { 122 114 tdb->interrupt_sig_ptr = ptr; 123 115 } 124 116 125 /* a byte range locking function - return 0 on success 126 this functions locks/unlocks 1 byte at the specified offset. 127 128 On error, errno is also set so that errors are passed back properly 129 through tdb_open(). 130 131 note that a len of zero means lock to end of file 132 */ 133 int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, 134 int rw_type, int lck_type, int probe, size_t len) 135 { 136 117 static int fcntl_lock(struct tdb_context *tdb, 118 int rw, tdb_off_t off, size_t len, bool waitflag) 119 { 137 120 #ifdef __OS2__ 138 121 APIRET rc; 139 ULONG fAccess = 0; 140 int fLock = 0;122 ULONG fAccess = 0; // default exclusiv 123 int fLock = 1; // default lock 141 124 off_t cbFile; 142 125 off_t offStart; 143 126 off_t cbRange; 144 145 TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, fd %d, lck_type %s, rw_type %s, offset %d, len %d\n", 146 getpid(), tdb->fd, lock_type(lck_type), read_type(rw_type), offset, len)); 147 148 if (tdb->flags & TDB_NOLOCK) { 149 return 0; 150 } 151 152 if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) { 153 tdb->ecode = TDB_ERR_RDONLY; 154 return -1; 155 } 127 FILELOCK lockArea = {0}, unlockArea = {0}; 128 129 TDB_LOG((tdb, TDB_DEBUG_TRACE, "fcntl_lock in pid %X, fd %d, lck_type %s, rw_type %s, offset %d, len %d\n", 130 getpid(), tdb->fd, lock_type(waitflag), read_type(rw), off, len)); 156 131 157 switch( offset) {158 case GLOBAL_LOCK:132 switch(off) { 133 case OPEN_LOCK: 159 134 case ACTIVE_LOCK: 160 135 case TRANSACTION_LOCK: 161 return _mutex_brlock( tdb, offset, rw_type, lck_type, probe, len);136 return _mutex_brlock(tdb, off, rw, waitflag, len); 162 137 } 163 138 164 139 /* flags and order */ 165 fAccess = 0; /* exclusive */ 166 switch (rw_type) 140 switch (rw) 167 141 { 168 142 case F_UNLCK: 169 143 fLock = 0; 144 unlockArea.lOffset = off; 145 unlockArea.lRange = len ? len : tdb->header.hash_size *4; //was LONG_MAX 170 146 break; 171 147 case F_RDLCK: 148 lockArea.lOffset = off; 149 lockArea.lRange = len ? len : LONG_MAX; 172 150 fAccess = 1; /* read-only */ 151 break; 173 152 case F_WRLCK: 174 fLock = 1; 153 lockArea.lOffset = off; 154 lockArea.lRange = len ? len : LONG_MAX; 175 155 break; 176 156 default: … … 178 158 } 179 159 180 FILELOCK aflock[2]; 181 bzero(&aflock[(fLock + 1) & 1], sizeof(aflock[0])); 182 aflock[fLock].lOffset = offset; 183 aflock[fLock].lRange = len ? len : LONG_MAX; 184 rc = DosSetFileLocks(tdb->fd, &aflock[0], &aflock[1], SEM_IMMEDIATE_RETURN, fAccess); 185 186 if (rc != NO_ERROR && lck_type == F_SETLKW) { 160 rc = DosSetFileLocks(tdb->fd, &unlockArea, &lockArea, SEM_IMMEDIATE_RETURN, fAccess); 161 162 if (rc != NO_ERROR && waitflag) { 187 163 int count = 20; 188 164 do { 189 rc = DosSetFileLocks(tdb->fd, & aflock[0], &aflock[1], 100, fAccess);165 rc = DosSetFileLocks(tdb->fd, &unlockArea, &lockArea, 100, fAccess); 190 166 count--; 191 167 } while( count>0 && rc !=NO_ERROR); 192 168 193 169 } 170 171 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "fcntl_lock out pid %X, fd %d, lck_type %s, rw_type %s, offset %d, len %d, rc=%d\n", 172 getpid(), tdb->fd, lock_type(waitflag), read_type(rw), off, len, rc)); 173 194 174 if (rc != NO_ERROR) { 195 175 errno = EINVAL; 196 /* Generic lock error. errno set by fcntl. 197 * EAGAIN is an expected return from non-blocking 198 * locks. */ 199 if (!probe && lck_type != F_SETLK) { 200 /* Ensure error code is set for log fun to examine. */ 201 tdb->ecode = TDB_ERR_LOCK; 202 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", 203 tdb->fd, offset, rw_type, lck_type, (int)len)); 176 return -1; 177 } 178 179 #else 180 struct flock fl; 181 182 fl.l_type = rw; 183 fl.l_whence = SEEK_SET; 184 fl.l_start = off; 185 fl.l_len = len; 186 fl.l_pid = 0; 187 188 if (waitflag) 189 return fcntl(tdb->fd, F_SETLKW, &fl); 190 else 191 return fcntl(tdb->fd, F_SETLK, &fl); 192 #endif 193 } 194 195 static int fcntl_unlock(struct tdb_context *tdb, int rw, tdb_off_t off, size_t len) 196 { 197 struct flock fl; 198 #if 0 /* Check they matched up locks and unlocks correctly. */ 199 char line[80]; 200 FILE *locks; 201 bool found = false; 202 203 locks = fopen("/proc/locks", "r"); 204 205 while (fgets(line, 80, locks)) { 206 char *p; 207 int type, start, l; 208 209 /* eg. 1: FLOCK ADVISORY WRITE 2440 08:01:2180826 0 EOF */ 210 p = strchr(line, ':') + 1; 211 if (strncmp(p, " POSIX ADVISORY ", strlen(" POSIX ADVISORY "))) 212 continue; 213 p += strlen(" FLOCK ADVISORY "); 214 if (strncmp(p, "READ ", strlen("READ ")) == 0) 215 type = F_RDLCK; 216 else if (strncmp(p, "WRITE ", strlen("WRITE ")) == 0) 217 type = F_WRLCK; 218 else 219 abort(); 220 p += 6; 221 if (atoi(p) != getpid()) 222 continue; 223 p = strchr(strchr(p, ' ') + 1, ' ') + 1; 224 start = atoi(p); 225 p = strchr(p, ' ') + 1; 226 if (strncmp(p, "EOF", 3) == 0) 227 l = 0; 228 else 229 l = atoi(p) - start + 1; 230 231 if (off == start) { 232 if (len != l) { 233 fprintf(stderr, "Len %u should be %u: %s", 234 (int)len, l, line); 235 abort(); 236 } 237 if (type != rw) { 238 fprintf(stderr, "Type %s wrong: %s", 239 rw == F_RDLCK ? "READ" : "WRITE", line); 240 abort(); 241 } 242 found = true; 243 break; 204 244 } 205 206 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", 207 getpid(), tdb->fd, offset, rw_type, lck_type, (int)len)); 208 tdb->ecode = TDB_ERR_LOCK; 209 return -1; 210 } 211 212 TDB_LOG(( tdb, TDB_DEBUG_TRACE, "tdb_brlock pid %X, fd %d, lck_type %s, rw_type %s, offset %d, len %d DONE\n", 213 getpid(), tdb->fd, lock_type(lck_type), read_type(rw_type), offset, len)); 214 245 } 246 247 if (!found) { 248 fprintf(stderr, "Unlock on %u@%u not found!\n", 249 (int)off, (int)len); 250 abort(); 251 } 252 253 fclose(locks); 254 #endif 255 256 fl.l_type = F_UNLCK; 257 fl.l_whence = SEEK_SET; 258 fl.l_start = off; 259 fl.l_len = len; 260 fl.l_pid = 0; 261 #ifdef __OS2__ 262 return fcntl_lock(tdb, F_UNLCK, off, len, 1); 215 263 #else 216 217 struct flock fl; 264 return fcntl(tdb->fd, F_SETLKW, &fl); 265 #endif 266 } 267 268 /* list -1 is the alloc list, otherwise a hash chain. */ 269 static tdb_off_t lock_offset(int list) 270 { 271 return FREELIST_TOP + 4*list; 272 } 273 274 /* a byte range locking function - return 0 on success 275 this functions locks/unlocks 1 byte at the specified offset. 276 277 On error, errno is also set so that errors are passed back properly 278 through tdb_open(). 279 280 note that a len of zero means lock to end of file 281 */ 282 int tdb_brlock(struct tdb_context *tdb, 283 int rw_type, tdb_off_t offset, size_t len, 284 enum tdb_lock_flags flags) 285 { 218 286 int ret; 219 287 220 288 if (tdb->flags & TDB_NOLOCK) { 289 return 0; 290 } 291 292 if (flags & TDB_LOCK_MARK_ONLY) { 221 293 return 0; 222 294 } … … 227 299 } 228 300 229 fl.l_type = rw_type;230 fl.l_whence = SEEK_SET;231 fl.l_start = offset;232 fl.l_len = len;233 fl.l_pid = 0;234 235 301 do { 236 ret = fcntl (tdb->fd,lck_type,&fl);237 302 ret = fcntl_lock(tdb, rw_type, offset, len, 303 flags & TDB_LOCK_WAIT); 238 304 /* Check for a sigalarm break. */ 239 305 if (ret == -1 && errno == EINTR && … … 249 315 * EAGAIN is an expected return from non-blocking 250 316 * locks. */ 251 if (! probe && lck_type != F_SETLK) {252 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n",253 tdb->fd, offset, rw_type, lck_type, (int)len));317 if (!(flags & TDB_LOCK_PROBE) && errno != EAGAIN) { 318 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d flags=%d len=%d\n", 319 tdb->fd, offset, rw_type, flags, (int)len)); 254 320 } 255 321 return -1; 256 322 } 257 323 return 0; 258 #endif 259 } 260 324 } 325 326 int tdb_brunlock(struct tdb_context *tdb, 327 int rw_type, tdb_off_t offset, size_t len) 328 { 329 int ret; 330 331 if (tdb->flags & TDB_NOLOCK) { 332 return 0; 333 } 334 335 do { 336 ret = fcntl_unlock(tdb, rw_type, offset, len); 337 } while (ret == -1 && errno == EINTR); 338 339 if (ret == -1) { 340 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brunlock failed (fd=%d) at offset %d rw_type=%d len=%d\n", 341 tdb->fd, offset, rw_type, (int)len)); 342 } 343 return ret; 344 } 261 345 262 346 /* … … 266 350 made. For those OSes we may loop for a while. 267 351 */ 268 int tdb_ brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)352 int tdb_allrecord_upgrade(struct tdb_context *tdb) 269 353 { 270 354 int count = 1000; 355 356 if (tdb->allrecord_lock.count != 1) { 357 TDB_LOG((tdb, TDB_DEBUG_ERROR, 358 "tdb_allrecord_upgrade failed: count %u too high\n", 359 tdb->allrecord_lock.count)); 360 return -1; 361 } 362 363 if (tdb->allrecord_lock.off != 1) { 364 TDB_LOG((tdb, TDB_DEBUG_ERROR, 365 "tdb_allrecord_upgrade failed: already upgraded?\n")); 366 return -1; 367 } 368 271 369 while (count--) { 272 370 struct timeval tv; 273 274 371 #ifdef __OS2__ 275 372 // YD we cannot upgrade without an unlock first... 276 tdb_brlock(tdb, offset, F_UNLCK, F_SETLKW, 1, len);373 tdb_brlock(tdb, F_UNLCK, FREELIST_TOP, 0, TDB_LOCK_WAIT|TDB_LOCK_PROBE); 277 374 #endif 278 279 if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) { 375 if (tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0, 376 TDB_LOCK_WAIT|TDB_LOCK_PROBE) == 0) { 377 tdb->allrecord_lock.ltype = F_WRLCK; 378 tdb->allrecord_lock.off = 0; 280 379 return 0; 281 380 } … … 288 387 select(0, NULL, NULL, NULL, &tv); 289 388 } 290 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_ brlock_upgrade failed at offset %d\n", offset));389 TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_allrecord_upgrade failed\n")); 291 390 return -1; 292 391 } 293 392 294 295 /* lock a list in the database. list -1 is the alloc list */ 296 static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op) 393 static struct tdb_lock_type *find_nestlock(struct tdb_context *tdb, 394 tdb_off_t offset) 395 { 396 unsigned int i; 397 398 for (i=0; i<tdb->num_lockrecs; i++) { 399 if (tdb->lockrecs[i].off == offset) { 400 return &tdb->lockrecs[i]; 401 } 402 } 403 return NULL; 404 } 405 406 /* lock an offset in the database. */ 407 int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype, 408 enum tdb_lock_flags flags) 297 409 { 298 410 struct tdb_lock_type *new_lck; 299 int i; 300 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); 301 302 ltype &= ~TDB_MARK_LOCK; 303 304 /* a global lock allows us to avoid per chain locks */ 305 if (tdb->global_lock.count && 306 (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { 307 return 0; 308 } 309 310 if (tdb->global_lock.count) { 411 412 if (offset >= lock_offset(tdb->header.hash_size)) { 311 413 tdb->ecode = TDB_ERR_LOCK; 312 return -1; 313 } 314 315 if (list < -1 || list >= (int)tdb->header.hash_size) { 316 tdb->ecode = TDB_ERR_LOCK; 317 TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n", 318 list, ltype)); 414 TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid offset %u for ltype=%d\n", 415 offset, ltype)); 319 416 return -1; 320 417 } … … 322 419 return 0; 323 420 324 for (i=0; i<tdb->num_lockrecs; i++) { 325 if (tdb->lockrecs[i].list == list) { 326 if (tdb->lockrecs[i].count == 0) { 327 /* 328 * Can't happen, see tdb_unlock(). It should 329 * be an assert. 330 */ 331 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: " 332 "lck->count == 0 for list %d", list)); 333 } 334 /* 335 * Just increment the in-memory struct, posix locks 336 * don't stack. 337 */ 338 tdb->lockrecs[i].count++; 339 return 0; 340 } 421 new_lck = find_nestlock(tdb, offset); 422 if (new_lck) { 423 /* 424 * Just increment the in-memory struct, posix locks 425 * don't stack. 426 */ 427 new_lck->count++; 428 return 0; 341 429 } 342 430 … … 352 440 /* Since fcntl locks don't nest, we do a lock for the first one, 353 441 and simply bump the count for future ones */ 354 if (!mark_lock && 355 tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list, ltype, op, 356 0, 1)) { 357 return -1; 358 } 359 360 tdb->num_locks++; 361 362 tdb->lockrecs[tdb->num_lockrecs].list = list; 442 if (tdb_brlock(tdb, ltype, offset, 1, flags)) { 443 return -1; 444 } 445 446 tdb->lockrecs[tdb->num_lockrecs].off = offset; 363 447 tdb->lockrecs[tdb->num_lockrecs].count = 1; 364 448 tdb->lockrecs[tdb->num_lockrecs].ltype = ltype; 365 tdb->num_lockrecs += 1;449 tdb->num_lockrecs++; 366 450 367 451 return 0; 452 } 453 454 static int tdb_lock_and_recover(struct tdb_context *tdb) 455 { 456 int ret; 457 458 /* We need to match locking order in transaction commit. */ 459 if (tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0, TDB_LOCK_WAIT)) { 460 return -1; 461 } 462 463 if (tdb_brlock(tdb, F_WRLCK, OPEN_LOCK, 1, TDB_LOCK_WAIT)) { 464 tdb_brunlock(tdb, F_WRLCK, FREELIST_TOP, 0); 465 return -1; 466 } 467 468 ret = tdb_transaction_recover(tdb); 469 470 tdb_brunlock(tdb, F_WRLCK, OPEN_LOCK, 1); 471 tdb_brunlock(tdb, F_WRLCK, FREELIST_TOP, 0); 472 473 return ret; 474 } 475 476 static bool have_data_locks(const struct tdb_context *tdb) 477 { 478 unsigned int i; 479 480 for (i = 0; i < tdb->num_lockrecs; i++) { 481 if (tdb->lockrecs[i].off >= lock_offset(-1)) 482 return true; 483 } 484 return false; 485 } 486 487 static int tdb_lock_list(struct tdb_context *tdb, int list, int ltype, 488 enum tdb_lock_flags waitflag) 489 { 490 int ret; 491 bool check = false; 492 493 /* a allrecord lock allows us to avoid per chain locks */ 494 if (tdb->allrecord_lock.count && 495 (ltype == tdb->allrecord_lock.ltype || ltype == F_RDLCK)) { 496 return 0; 497 } 498 499 if (tdb->allrecord_lock.count) { 500 tdb->ecode = TDB_ERR_LOCK; 501 ret = -1; 502 } else { 503 /* Only check when we grab first data lock. */ 504 check = !have_data_locks(tdb); 505 ret = tdb_nest_lock(tdb, lock_offset(list), ltype, waitflag); 506 507 if (ret == 0 && check && tdb_needs_recovery(tdb)) { 508 tdb_nest_unlock(tdb, lock_offset(list), ltype, false); 509 510 if (tdb_lock_and_recover(tdb) == -1) { 511 return -1; 512 } 513 return tdb_lock_list(tdb, list, ltype, waitflag); 514 } 515 } 516 return ret; 368 517 } 369 518 … … 372 521 { 373 522 int ret; 374 ret = _tdb_lock(tdb, list, ltype, F_SETLKW); 523 524 ret = tdb_lock_list(tdb, list, ltype, TDB_LOCK_WAIT); 375 525 if (ret) { 376 526 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d " … … 383 533 int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype) 384 534 { 385 return _tdb_lock(tdb, list, ltype, F_SETLK); 386 } 387 388 389 /* unlock the database: returns void because it's too late for errors. */ 390 /* changed to return int it may be interesting to know there 391 has been an error --simo */ 392 int tdb_unlock(struct tdb_context *tdb, int list, int ltype) 535 return tdb_lock_list(tdb, list, ltype, TDB_LOCK_NOWAIT); 536 } 537 538 539 int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype, 540 bool mark_lock) 393 541 { 394 542 int ret = -1; 395 int i; 396 struct tdb_lock_type *lck = NULL; 397 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); 398 399 ltype &= ~TDB_MARK_LOCK; 400 401 /* a global lock allows us to avoid per chain locks */ 402 if (tdb->global_lock.count && 403 (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { 404 return 0; 405 } 406 407 if (tdb->global_lock.count) { 408 tdb->ecode = TDB_ERR_LOCK; 409 return -1; 410 } 543 struct tdb_lock_type *lck; 411 544 412 545 if (tdb->flags & TDB_NOLOCK) … … 414 547 415 548 /* Sanity checks */ 416 if ( list < -1 || list >= (int)tdb->header.hash_size) {417 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));549 if (offset >= lock_offset(tdb->header.hash_size)) { 550 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: offset %u invalid (%d)\n", offset, tdb->header.hash_size)); 418 551 return ret; 419 552 } 420 553 421 for (i=0; i<tdb->num_lockrecs; i++) { 422 if (tdb->lockrecs[i].list == list) { 423 lck = &tdb->lockrecs[i]; 424 break; 425 } 426 } 427 554 lck = find_nestlock(tdb, offset); 428 555 if ((lck == NULL) || (lck->count == 0)) { 429 556 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n")); … … 446 573 ret = 0; 447 574 } else { 448 ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, 449 F_SETLKW, 0, 1); 450 } 451 tdb->num_locks--; 575 ret = tdb_brunlock(tdb, ltype, offset, 1); 576 } 452 577 453 578 /* … … 455 580 * last array element. 456 581 */ 457 458 if (tdb->num_lockrecs > 1) { 459 *lck = tdb->lockrecs[tdb->num_lockrecs-1]; 460 } 461 tdb->num_lockrecs -= 1; 582 *lck = tdb->lockrecs[--tdb->num_lockrecs]; 462 583 463 584 /* … … 475 596 } 476 597 598 int tdb_unlock(struct tdb_context *tdb, int list, int ltype) 599 { 600 /* a global lock allows us to avoid per chain locks */ 601 if (tdb->allrecord_lock.count && 602 (ltype == tdb->allrecord_lock.ltype || ltype == F_RDLCK)) { 603 return 0; 604 } 605 606 if (tdb->allrecord_lock.count) { 607 tdb->ecode = TDB_ERR_LOCK; 608 return -1; 609 } 610 611 return tdb_nest_unlock(tdb, lock_offset(list), ltype, false); 612 } 613 477 614 /* 478 615 get the transaction lock 479 616 */ 480 int tdb_transaction_lock(struct tdb_context *tdb, int ltype) 481 { 482 if (tdb->global_lock.count) { 483 return 0; 484 } 485 if (tdb->transaction_lock_count > 0) { 486 tdb->transaction_lock_count++; 487 return 0; 488 } 489 490 if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype, 491 F_SETLKW, 0, 1) == -1) { 492 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n")); 493 tdb->ecode = TDB_ERR_LOCK; 494 return -1; 495 } 496 tdb->transaction_lock_count++; 497 return 0; 617 int tdb_transaction_lock(struct tdb_context *tdb, int ltype, 618 enum tdb_lock_flags lockflags) 619 { 620 return tdb_nest_lock(tdb, TRANSACTION_LOCK, ltype, lockflags); 498 621 } 499 622 … … 501 624 release the transaction lock 502 625 */ 503 int tdb_transaction_unlock(struct tdb_context *tdb) 504 { 505 int ret; 506 if (tdb->global_lock.count) { 507 return 0; 508 } 509 if (tdb->transaction_lock_count > 1) { 510 tdb->transaction_lock_count--; 511 return 0; 512 } 513 ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); 514 if (ret == 0) { 515 tdb->transaction_lock_count = 0; 516 } 517 return ret; 518 } 519 520 521 522 523 /* lock/unlock entire database */ 524 static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op) 525 { 526 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); 527 528 ltype &= ~TDB_MARK_LOCK; 529 626 int tdb_transaction_unlock(struct tdb_context *tdb, int ltype) 627 { 628 return tdb_nest_unlock(tdb, TRANSACTION_LOCK, ltype, false); 629 } 630 631 /* Returns 0 if all done, -1 if error, 1 if ok. */ 632 static int tdb_allrecord_check(struct tdb_context *tdb, int ltype, 633 enum tdb_lock_flags flags, bool upgradable) 634 { 530 635 /* There are no locks on read-only dbs */ 531 636 if (tdb->read_only || tdb->traverse_read) { … … 534 639 } 535 640 536 if (tdb-> global_lock.count && tdb->global_lock.ltype == ltype) {537 tdb-> global_lock.count++;538 return 0; 539 } 540 541 if (tdb-> global_lock.count) {641 if (tdb->allrecord_lock.count && tdb->allrecord_lock.ltype == ltype) { 642 tdb->allrecord_lock.count++; 643 return 0; 644 } 645 646 if (tdb->allrecord_lock.count) { 542 647 /* a global lock of a different type exists */ 543 648 tdb->ecode = TDB_ERR_LOCK; 544 649 return -1; 545 650 } 546 547 if (tdb ->num_locks != 0) {651 652 if (tdb_have_extra_locks(tdb)) { 548 653 /* can't combine global and chain locks */ 549 654 tdb->ecode = TDB_ERR_LOCK; … … 551 656 } 552 657 553 if (!mark_lock && 554 tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, op, 555 0, 4*tdb->header.hash_size)) { 556 if (op == F_SETLKW) { 557 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno))); 658 if (upgradable && ltype != F_RDLCK) { 659 /* tdb error: you can't upgrade a write lock! */ 660 tdb->ecode = TDB_ERR_LOCK; 661 return -1; 662 } 663 return 1; 664 } 665 666 /* We only need to lock individual bytes, but Linux merges consecutive locks 667 * so we lock in contiguous ranges. */ 668 static int tdb_chainlock_gradual(struct tdb_context *tdb, 669 int ltype, enum tdb_lock_flags flags, 670 size_t off, size_t len) 671 { 672 int ret; 673 enum tdb_lock_flags nb_flags = (flags & ~TDB_LOCK_WAIT); 674 675 if (len <= 4) { 676 /* Single record. Just do blocking lock. */ 677 return tdb_brlock(tdb, ltype, off, len, flags); 678 } 679 680 /* First we try non-blocking. */ 681 ret = tdb_brlock(tdb, ltype, off, len, nb_flags); 682 if (ret == 0) { 683 return 0; 684 } 685 686 /* Try locking first half, then second. */ 687 ret = tdb_chainlock_gradual(tdb, ltype, flags, off, len / 2); 688 if (ret == -1) 689 return -1; 690 691 ret = tdb_chainlock_gradual(tdb, ltype, flags, 692 off + len / 2, len - len / 2); 693 if (ret == -1) { 694 tdb_brunlock(tdb, ltype, off, len / 2); 695 return -1; 696 } 697 return 0; 698 } 699 700 /* lock/unlock entire database. It can only be upgradable if you have some 701 * other way of guaranteeing exclusivity (ie. transaction write lock). 702 * We do the locking gradually to avoid being starved by smaller locks. */ 703 int tdb_allrecord_lock(struct tdb_context *tdb, int ltype, 704 enum tdb_lock_flags flags, bool upgradable) 705 { 706 switch (tdb_allrecord_check(tdb, ltype, flags, upgradable)) { 707 case -1: 708 return -1; 709 case 0: 710 return 0; 711 } 712 713 /* We cover two kinds of locks: 714 * 1) Normal chain locks. Taken for almost all operations. 715 * 3) Individual records locks. Taken after normal or free 716 * chain locks. 717 * 718 * It is (1) which cause the starvation problem, so we're only 719 * gradual for that. */ 720 if (tdb_chainlock_gradual(tdb, ltype, flags, FREELIST_TOP, 721 tdb->header.hash_size * 4) == -1) { 722 return -1; 723 } 724 725 /* Grab individual record locks. */ 726 if (tdb_brlock(tdb, ltype, lock_offset(tdb->header.hash_size), 0, 727 flags) == -1) { 728 tdb_brunlock(tdb, ltype, FREELIST_TOP, 729 tdb->header.hash_size * 4); 730 return -1; 731 } 732 733 tdb->allrecord_lock.count = 1; 734 /* If it's upgradable, it's actually exclusive so we can treat 735 * it as a write lock. */ 736 tdb->allrecord_lock.ltype = upgradable ? F_WRLCK : ltype; 737 tdb->allrecord_lock.off = upgradable; 738 739 if (tdb_needs_recovery(tdb)) { 740 bool mark = flags & TDB_LOCK_MARK_ONLY; 741 tdb_allrecord_unlock(tdb, ltype, mark); 742 if (mark) { 743 tdb->ecode = TDB_ERR_LOCK; 744 TDB_LOG((tdb, TDB_DEBUG_ERROR, 745 "tdb_lockall_mark cannot do recovery\n")); 746 return -1; 558 747 } 559 return -1;560 }561 562 tdb->global_lock.count = 1;563 tdb->global_lock.ltype = ltype;748 if (tdb_lock_and_recover(tdb) == -1) { 749 return -1; 750 } 751 return tdb_allrecord_lock(tdb, ltype, flags, upgradable); 752 } 564 753 565 754 return 0; … … 569 758 570 759 /* unlock entire db */ 571 static int _tdb_unlockall(struct tdb_context *tdb, int ltype) 572 { 573 bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); 574 575 ltype &= ~TDB_MARK_LOCK; 576 760 int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock) 761 { 577 762 /* There are no locks on read-only dbs */ 578 763 if (tdb->read_only || tdb->traverse_read) { … … 581 766 } 582 767 583 if (tdb-> global_lock.ltype != ltype || tdb->global_lock.count == 0) {768 if (tdb->allrecord_lock.count == 0) { 584 769 tdb->ecode = TDB_ERR_LOCK; 585 770 return -1; 586 771 } 587 772 588 if (tdb->global_lock.count > 1) { 589 tdb->global_lock.count--; 590 return 0; 591 } 592 593 if (!mark_lock && 594 tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 595 0, 4*tdb->header.hash_size)) { 773 /* Upgradable locks are marked as write locks. */ 774 if (tdb->allrecord_lock.ltype != ltype 775 && (!tdb->allrecord_lock.off || ltype != F_RDLCK)) { 776 tdb->ecode = TDB_ERR_LOCK; 777 return -1; 778 } 779 780 if (tdb->allrecord_lock.count > 1) { 781 tdb->allrecord_lock.count--; 782 return 0; 783 } 784 785 if (!mark_lock && tdb_brunlock(tdb, ltype, FREELIST_TOP, 0)) { 596 786 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno))); 597 787 return -1; 598 788 } 599 789 600 tdb-> global_lock.count = 0;601 tdb-> global_lock.ltype = 0;790 tdb->allrecord_lock.count = 0; 791 tdb->allrecord_lock.ltype = 0; 602 792 603 793 return 0; … … 605 795 606 796 /* lock entire database with write lock */ 607 int tdb_lockall(struct tdb_context *tdb)797 _PUBLIC_ int tdb_lockall(struct tdb_context *tdb) 608 798 { 609 799 tdb_trace(tdb, "tdb_lockall"); 610 return _tdb_lockall(tdb, F_WRLCK, F_SETLKW);800 return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false); 611 801 } 612 802 613 803 /* lock entire database with write lock - mark only */ 614 int tdb_lockall_mark(struct tdb_context *tdb)804 _PUBLIC_ int tdb_lockall_mark(struct tdb_context *tdb) 615 805 { 616 806 tdb_trace(tdb, "tdb_lockall_mark"); 617 return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW);807 return tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_MARK_ONLY, false); 618 808 } 619 809 620 810 /* unlock entire database with write lock - unmark only */ 621 int tdb_lockall_unmark(struct tdb_context *tdb)811 _PUBLIC_ int tdb_lockall_unmark(struct tdb_context *tdb) 622 812 { 623 813 tdb_trace(tdb, "tdb_lockall_unmark"); 624 return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK);814 return tdb_allrecord_unlock(tdb, F_WRLCK, true); 625 815 } 626 816 627 817 /* lock entire database with write lock - nonblocking varient */ 628 int tdb_lockall_nonblock(struct tdb_context *tdb)629 { 630 int ret = _tdb_lockall(tdb, F_WRLCK, F_SETLK);818 _PUBLIC_ int tdb_lockall_nonblock(struct tdb_context *tdb) 819 { 820 int ret = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_NOWAIT, false); 631 821 tdb_trace_ret(tdb, "tdb_lockall_nonblock", ret); 632 822 return ret; … … 634 824 635 825 /* unlock entire database with write lock */ 636 int tdb_unlockall(struct tdb_context *tdb)826 _PUBLIC_ int tdb_unlockall(struct tdb_context *tdb) 637 827 { 638 828 tdb_trace(tdb, "tdb_unlockall"); 639 return _tdb_unlockall(tdb, F_WRLCK);829 return tdb_allrecord_unlock(tdb, F_WRLCK, false); 640 830 } 641 831 642 832 /* lock entire database with read lock */ 643 int tdb_lockall_read(struct tdb_context *tdb)833 _PUBLIC_ int tdb_lockall_read(struct tdb_context *tdb) 644 834 { 645 835 tdb_trace(tdb, "tdb_lockall_read"); 646 return _tdb_lockall(tdb, F_RDLCK, F_SETLKW);836 return tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, false); 647 837 } 648 838 649 839 /* lock entire database with read lock - nonblock varient */ 650 int tdb_lockall_read_nonblock(struct tdb_context *tdb)651 { 652 int ret = _tdb_lockall(tdb, F_RDLCK, F_SETLK);840 _PUBLIC_ int tdb_lockall_read_nonblock(struct tdb_context *tdb) 841 { 842 int ret = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_NOWAIT, false); 653 843 tdb_trace_ret(tdb, "tdb_lockall_read_nonblock", ret); 654 844 return ret; … … 656 846 657 847 /* unlock entire database with read lock */ 658 int tdb_unlockall_read(struct tdb_context *tdb)848 _PUBLIC_ int tdb_unlockall_read(struct tdb_context *tdb) 659 849 { 660 850 tdb_trace(tdb, "tdb_unlockall_read"); 661 return _tdb_unlockall(tdb, F_RDLCK);851 return tdb_allrecord_unlock(tdb, F_RDLCK, false); 662 852 } 663 853 664 854 /* lock/unlock one hash chain. This is meant to be used to reduce 665 855 contention - it cannot guarantee how many records will be locked */ 666 int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)856 _PUBLIC_ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key) 667 857 { 668 858 int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); … … 674 864 to reduce contention - it cannot guarantee how many records will be 675 865 locked */ 676 int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key)866 _PUBLIC_ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key) 677 867 { 678 868 int ret = tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); … … 682 872 683 873 /* mark a chain as locked without actually locking it. Warning! use with great caution! */ 684 int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key) 685 { 686 int ret = tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); 874 _PUBLIC_ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key) 875 { 876 int ret = tdb_nest_lock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))), 877 F_WRLCK, TDB_LOCK_MARK_ONLY); 687 878 tdb_trace_1rec(tdb, "tdb_chainlock_mark", key); 688 879 return ret; … … 690 881 691 882 /* unmark a chain as locked without actually locking it. Warning! use with great caution! */ 692 int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key)883 _PUBLIC_ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key) 693 884 { 694 885 tdb_trace_1rec(tdb, "tdb_chainlock_unmark", key); 695 return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); 696 } 697 698 int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) 886 return tdb_nest_unlock(tdb, lock_offset(BUCKET(tdb->hash_fn(&key))), 887 F_WRLCK, true); 888 } 889 890 _PUBLIC_ int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) 699 891 { 700 892 tdb_trace_1rec(tdb, "tdb_chainunlock", key); … … 702 894 } 703 895 704 int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)896 _PUBLIC_ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key) 705 897 { 706 898 int ret; … … 710 902 } 711 903 712 int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)904 _PUBLIC_ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key) 713 905 { 714 906 tdb_trace_1rec(tdb, "tdb_chainunlock_read", key); … … 716 908 } 717 909 718 719 720 910 /* record lock stops delete underneath */ 721 911 int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off) 722 912 { 723 if (tdb-> global_lock.count) {724 return 0; 725 } 726 return off ? tdb ->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0;913 if (tdb->allrecord_lock.count) { 914 return 0; 915 } 916 return off ? tdb_brlock(tdb, F_RDLCK, off, 1, TDB_LOCK_WAIT) : 0; 727 917 } 728 918 … … 738 928 if (i->off == off) 739 929 return -1; 740 return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1); 741 } 742 743 /* 744 Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not 745 an error to fail to get the lock here. 746 */ 930 if (tdb->allrecord_lock.count) { 931 if (tdb->allrecord_lock.ltype == F_WRLCK) { 932 return 0; 933 } 934 return -1; 935 } 936 return tdb_brlock(tdb, F_WRLCK, off, 1, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE); 937 } 938 747 939 int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off) 748 940 { 749 return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1); 941 if (tdb->allrecord_lock.count) { 942 return 0; 943 } 944 return tdb_brunlock(tdb, F_WRLCK, off, 1); 750 945 } 751 946 … … 756 951 uint32_t count = 0; 757 952 758 if (tdb-> global_lock.count) {953 if (tdb->allrecord_lock.count) { 759 954 return 0; 760 955 } … … 765 960 if (i->off == off) 766 961 count++; 767 return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0); 768 } 962 return (count == 1 ? tdb_brunlock(tdb, F_RDLCK, off, 1) : 0); 963 } 964 965 bool tdb_have_extra_locks(struct tdb_context *tdb) 966 { 967 unsigned int extra = tdb->num_lockrecs; 968 969 /* A transaction holds the lock for all records. */ 970 if (!tdb->transaction && tdb->allrecord_lock.count) { 971 return true; 972 } 973 974 /* We always hold the active lock if CLEAR_IF_FIRST. */ 975 if (find_nestlock(tdb, ACTIVE_LOCK)) { 976 extra--; 977 } 978 979 /* In a transaction, we expect to hold the transaction lock */ 980 if (tdb->transaction && find_nestlock(tdb, TRANSACTION_LOCK)) { 981 extra--; 982 } 983 984 return extra; 985 } 986 987 /* The transaction code uses this to remove all locks. */ 988 void tdb_release_transaction_locks(struct tdb_context *tdb) 989 { 990 unsigned int i, active = 0; 991 992 if (tdb->allrecord_lock.count != 0) { 993 tdb_brunlock(tdb, tdb->allrecord_lock.ltype, FREELIST_TOP, 0); 994 tdb->allrecord_lock.count = 0; 995 } 996 997 for (i=0;i<tdb->num_lockrecs;i++) { 998 struct tdb_lock_type *lck = &tdb->lockrecs[i]; 999 1000 /* Don't release the active lock! Copy it to first entry. */ 1001 if (lck->off == ACTIVE_LOCK) { 1002 tdb->lockrecs[active++] = *lck; 1003 } else { 1004 tdb_brunlock(tdb, lck->ltype, lck->off, 1); 1005 } 1006 } 1007 tdb->num_lockrecs = active; 1008 if (tdb->num_lockrecs == 0) { 1009 SAFE_FREE(tdb->lockrecs); 1010 } 1011 } -
trunk/server/lib/tdb/common/open.c
r456 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 36 36 static struct tdb_context *tdbs = NULL; 37 37 38 39 /* This is based on the hash algorithm from gdbm */ 40 static unsigned int default_tdb_hash(TDB_DATA *key) 41 { 42 uint32_t value; /* Used to compute the hash value. */ 43 uint32_t i; /* Used to cycle through random values. */ 44 45 /* Set the initial value from the key size. */ 46 for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++) 47 value = (value + (key->dptr[i] << (i*5 % 24))); 48 49 return (1103515243 * value + 12345); 50 } 51 38 /* We use two hashes to double-check they're using the right hash function. */ 39 void tdb_header_hash(struct tdb_context *tdb, 40 uint32_t *magic1_hash, uint32_t *magic2_hash) 41 { 42 TDB_DATA hash_key; 43 uint32_t tdb_magic = TDB_MAGIC; 44 45 hash_key.dptr = discard_const_p(unsigned char, TDB_MAGIC_FOOD); 46 hash_key.dsize = sizeof(TDB_MAGIC_FOOD); 47 *magic1_hash = tdb->hash_fn(&hash_key); 48 49 hash_key.dptr = (unsigned char *)CONVERT(tdb_magic); 50 hash_key.dsize = sizeof(tdb_magic); 51 *magic2_hash = tdb->hash_fn(&hash_key); 52 53 /* Make sure at least one hash is non-zero! */ 54 if (*magic1_hash == 0 && *magic2_hash == 0) 55 *magic1_hash = 1; 56 } 52 57 53 58 /* initialise a new database with a specified hash size */ … … 57 62 size_t size; 58 63 int ret = -1; 59 ssize_t written;60 64 61 65 /* We make it up in memory, then write it out if not internal */ … … 69 73 newdb->version = TDB_VERSION; 70 74 newdb->hash_size = hash_size; 75 76 tdb_header_hash(tdb, &newdb->magic1_hash, &newdb->magic2_hash); 77 78 /* Make sure older tdbs (which don't check the magic hash fields) 79 * will refuse to open this TDB. */ 80 if (tdb->flags & TDB_INCOMPATIBLE_HASH) 81 newdb->rwlocks = TDB_HASH_RWLOCK_MAGIC; 82 71 83 if (tdb->flags & TDB_INTERNAL) { 72 84 tdb->map_size = size; … … 89 101 memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); 90 102 /* we still have "ret == -1" here */ 91 written = write(tdb->fd, newdb, size); 92 if (written == size) { 103 if (tdb_write_all(tdb->fd, newdb, size)) 93 104 ret = 0; 94 } else if (written != -1) {95 /* call write once again, this usually should return -1 and96 * set errno appropriately */97 size -= written;98 written = write(tdb->fd, newdb+written, size);99 if (written == size) {100 ret = 0;101 } else if (written >= 0) {102 /* a second incomplete write - we give up.103 * guessing the errno... */104 errno = ENOSPC;105 }106 }107 105 108 106 fail: … … 117 115 { 118 116 struct tdb_context *i; 119 117 120 118 for (i = tdbs; i; i = i->next) { 121 119 if (i->device == device && i->inode == ino) { … … 137 135 138 136 @param name may be NULL for internal databases. */ 139 struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,137 _PUBLIC_ struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, 140 138 int open_flags, mode_t mode) 141 139 { … … 149 147 } 150 148 151 152 struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, 149 static bool check_header_hash(struct tdb_context *tdb, 150 bool default_hash, uint32_t *m1, uint32_t *m2) 151 { 152 tdb_header_hash(tdb, m1, m2); 153 if (tdb->header.magic1_hash == *m1 && 154 tdb->header.magic2_hash == *m2) { 155 return true; 156 } 157 158 /* If they explicitly set a hash, always respect it. */ 159 if (!default_hash) 160 return false; 161 162 /* Otherwise, try the other inbuilt hash. */ 163 if (tdb->hash_fn == tdb_old_hash) 164 tdb->hash_fn = tdb_jenkins_hash; 165 else 166 tdb->hash_fn = tdb_old_hash; 167 return check_header_hash(tdb, false, m1, m2); 168 } 169 170 _PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, 153 171 int open_flags, mode_t mode, 154 172 const struct tdb_logging_context *log_ctx, … … 161 179 uint32_t vertest; 162 180 unsigned v; 181 const char *hash_alg; 182 uint32_t magic1, magic2; 163 183 164 184 if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) { … … 182 202 tdb->log.log_private = NULL; 183 203 } 184 tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash; 204 205 if (name == NULL && (tdb_flags & TDB_INTERNAL)) { 206 name = "__TDB_INTERNAL__"; 207 } 208 209 if (name == NULL) { 210 tdb->name = discard_const_p(char, "__NULL__"); 211 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: called with name == NULL\n")); 212 tdb->name = NULL; 213 errno = EINVAL; 214 goto fail; 215 } 216 217 /* now make a copy of the name, as the caller memory might went away */ 218 if (!(tdb->name = (char *)strdup(name))) { 219 /* 220 * set the name as the given string, so that tdb_name() will 221 * work in case of an error. 222 */ 223 tdb->name = discard_const_p(char, name); 224 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't strdup(%s)\n", 225 name)); 226 tdb->name = NULL; 227 errno = ENOMEM; 228 goto fail; 229 } 230 231 if (hash_fn) { 232 tdb->hash_fn = hash_fn; 233 hash_alg = "the user defined"; 234 } else { 235 /* This controls what we use when creating a tdb. */ 236 if (tdb->flags & TDB_INCOMPATIBLE_HASH) { 237 tdb->hash_fn = tdb_jenkins_hash; 238 } else { 239 tdb->hash_fn = tdb_old_hash; 240 } 241 hash_alg = "either default"; 242 } 185 243 186 244 #ifdef __OS2__ … … 189 247 } 190 248 #endif 191 192 249 /* cache the page size */ 193 250 tdb->page_size = getpagesize(); … … 204 261 goto fail; 205 262 } 206 263 207 264 if (hash_size == 0) 208 265 hash_size = DEFAULT_HASH_SIZE; … … 223 280 } 224 281 282 if (getenv("TDB_NO_FSYNC")) { 283 tdb->flags |= TDB_NOSYNC; 284 } 285 225 286 /* 226 287 * TDB_ALLOW_NESTING is the default behavior. … … 253 314 254 315 /* ensure there is only one process initialising at once */ 255 if (tdb ->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {256 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get globallock on %s: %s\n",316 if (tdb_nest_lock(tdb, OPEN_LOCK, F_WRLCK, TDB_LOCK_WAIT) == -1) { 317 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get open lock on %s: %s\n", 257 318 name, strerror(errno))); 258 319 goto fail; /* errno set by tdb_brlock */ … … 261 322 /* we need to zero database if we are the only one with it open */ 262 323 if ((tdb_flags & TDB_CLEAR_IF_FIRST) && 263 (!tdb->read_only)264 324 #ifndef __OS2__ 265 && (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0, 1) == 0)) 266 #endif 267 ) { 325 (!tdb->read_only) && 326 (locked = (tdb_nest_lock(tdb, ACTIVE_LOCK, F_WRLCK, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE) == 0))) { 327 #else 328 (!tdb->read_only) ) { 329 #endif 268 330 open_flags |= O_CREAT; 269 331 if (ftruncate(tdb->fd, 0) == -1) { … … 304 366 goto fail; 305 367 306 if (tdb->header.rwlocks != 0) { 368 if (tdb->header.rwlocks != 0 && 369 tdb->header.rwlocks != TDB_HASH_RWLOCK_MAGIC) { 307 370 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n")); 371 goto fail; 372 } 373 374 if ((tdb->header.magic1_hash == 0) && (tdb->header.magic2_hash == 0)) { 375 /* older TDB without magic hash references */ 376 tdb->hash_fn = tdb_old_hash; 377 } else if (!check_header_hash(tdb, !hash_fn, &magic1, &magic2)) { 378 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " 379 "%s was not created with %s hash function we are using\n" 380 "magic1_hash[0x%08X %s 0x%08X] " 381 "magic2_hash[0x%08X %s 0x%08X]\n", 382 name, hash_alg, 383 tdb->header.magic1_hash, 384 (tdb->header.magic1_hash == magic1) ? "==" : "!=", 385 magic1, 386 tdb->header.magic2_hash, 387 (tdb->header.magic2_hash == magic2) ? "==" : "!=", 388 magic2)); 389 errno = EINVAL; 308 390 goto fail; 309 391 } … … 318 400 } 319 401 320 if (!(tdb->name = (char *)strdup(name))) {321 errno = ENOMEM;322 goto fail;323 }324 325 402 tdb->map_size = st.st_size; 326 403 tdb->device = st.st_dev; … … 329 406 if (locked) { 330 407 #ifndef __OS2__ 331 if (tdb ->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) {408 if (tdb_nest_unlock(tdb, ACTIVE_LOCK, F_WRLCK, false) == -1) { 332 409 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " 333 "failed to take ACTIVE_LOCK on %s: %s\n",410 "failed to release ACTIVE_LOCK on %s: %s\n", 334 411 name, strerror(errno))); 335 412 goto fail; 336 413 } 337 338 414 #endif 339 415 } … … 346 422 if (tdb_flags & TDB_CLEAR_IF_FIRST) { 347 423 /* leave this lock in place to indicate it's in use */ 348 if (tdb ->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)424 if (tdb_nest_lock(tdb, ACTIVE_LOCK, F_RDLCK, TDB_LOCK_WAIT) == -1) { 349 425 goto fail; 426 } 350 427 } 351 428 #endif … … 358 435 #ifdef __OS2__ 359 436 // YD internal databases do not get global lock! 360 if (tdb ->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1) == -1)437 if (tdb_nest_unlock(tdb, OPEN_LOCK, F_WRLCK, false) == -1) 361 438 goto fail; 362 439 #endif … … 381 458 /* Internal (memory-only) databases skip all the code above to 382 459 * do with disk files, and resume here by releasing their 383 * globallock and hooking into the active list. */460 * open lock and hooking into the active list. */ 384 461 #ifndef __OS2__ 385 if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1) == -1) 386 goto fail; 462 if (tdb_nest_unlock(tdb, OPEN_LOCK, F_WRLCK, false) == -1) { 463 goto fail; 464 } 387 465 #endif 388 466 tdb->next = tdbs; 389 467 tdbs = tdb; 390 468 return tdb; 391 469 … … 405 483 tdb_munmap(tdb); 406 484 } 407 SAFE_FREE(tdb->name); 485 if (tdb->fd != -1) 486 if (close(tdb->fd) != 0) 487 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n")); 408 488 #ifdef __OS2__ 409 489 DosCloseMutexSem( tdb->hGlobalLock); … … 414 494 tdb->hTransactionLock = 0; 415 495 #endif 416 if (tdb->fd != -1) 417 if (close(tdb->fd) != 0) 418 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n")); 496 SAFE_FREE(tdb->lockrecs); 497 SAFE_FREE(tdb->name); 419 498 SAFE_FREE(tdb); 420 499 errno = save_errno; … … 427 506 */ 428 507 429 void tdb_set_max_dead(struct tdb_context *tdb, int max_dead)508 _PUBLIC_ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead) 430 509 { 431 510 tdb->max_dead_records = max_dead; … … 437 516 * @returns -1 for error; 0 for success. 438 517 **/ 439 int tdb_close(struct tdb_context *tdb)518 _PUBLIC_ int tdb_close(struct tdb_context *tdb) 440 519 { 441 520 struct tdb_context **i; 442 521 int ret = 0; 443 522 523 if (tdb->transaction) { 524 tdb_transaction_cancel(tdb); 525 } 444 526 tdb_trace(tdb, "tdb_close"); 445 if (tdb->transaction) {446 _tdb_transaction_cancel(tdb);447 }448 527 449 528 if (tdb->map_ptr) { … … 457 536 // YD internal databases do not have a global lock 458 537 if (!(tdb->flags & TDB_INTERNAL)) 459 tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLK, 0, 1);538 tdb_nest_unlock(tdb, OPEN_LOCK, F_WRLCK, false); 460 539 #endif 461 540 if (tdb->fd != -1) { … … 491 570 492 571 /* register a loging function */ 493 void tdb_set_logging_function(struct tdb_context *tdb,494 const struct tdb_logging_context *log_ctx)572 _PUBLIC_ void tdb_set_logging_function(struct tdb_context *tdb, 573 const struct tdb_logging_context *log_ctx) 495 574 { 496 575 tdb->log = *log_ctx; 497 576 } 498 577 499 void *tdb_get_logging_private(struct tdb_context *tdb)578 _PUBLIC_ void *tdb_get_logging_private(struct tdb_context *tdb) 500 579 { 501 580 return tdb->log.log_private; … … 526 605 #endif 527 606 528 if (tdb ->num_locks != 0 || tdb->global_lock.count) {607 if (tdb_have_extra_locks(tdb)) { 529 608 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n")); 530 609 goto fail; … … 561 640 #endif /* fake pread or pwrite */ 562 641 563 if (active_lock && 564 (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) { 642 /* We may still think we hold the active lock. */ 643 tdb->num_lockrecs = 0; 644 SAFE_FREE(tdb->lockrecs); 645 646 if (active_lock && tdb_nest_lock(tdb, ACTIVE_LOCK, F_RDLCK, TDB_LOCK_WAIT) == -1) { 565 647 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n")); 566 648 goto fail; … … 576 658 /* reopen a tdb - this can be used after a fork to ensure that we have an independent 577 659 seek pointer from our parent and to re-establish locks */ 578 int tdb_reopen(struct tdb_context *tdb)660 _PUBLIC_ int tdb_reopen(struct tdb_context *tdb) 579 661 { 580 662 return tdb_reopen_internal(tdb, tdb->flags & TDB_CLEAR_IF_FIRST); … … 582 664 583 665 /* reopen all tdb's */ 584 int tdb_reopen_all(int parent_longlived)666 _PUBLIC_ int tdb_reopen_all(int parent_longlived) 585 667 { 586 668 struct tdb_context *tdb; … … 623 705 // extract path info 624 706 _splitpath( name, drive, dir, fname, ext); 625 sprintf( szSem, "\\SEM32\\TDB_ GL_%s%s%s%i", dir, fname, ext, global_Sem32Add);707 sprintf( szSem, "\\SEM32\\TDB_OL_%s%s%s%i", dir, fname, ext, global_Sem32Add); 626 708 rc = DosCreateMutexSem( szSem, &tdb->hGlobalLock, 0, FALSE); 627 709 if (rc == ERROR_DUPLICATE_NAME) … … 632 714 return -1; 633 715 } 634 TDB_LOG((tdb, TDB_DEBUG_TRACE,"%s pid %d globalhandle %d\n", caller, getpid(), tdb->hGlobalLock));716 TDB_LOG((tdb, TDB_DEBUG_TRACE,"%s pid %d open handle %d\n", caller, getpid(), tdb->hGlobalLock)); 635 717 636 718 sprintf( szSem, "\\SEM32\\TDB_AL_%s%s%s%i", dir, fname, ext, global_Sem32Add); … … 659 741 } 660 742 #endif 661 -
trunk/server/lib/tdb/common/tdb.c
r648 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 28 28 #include "tdb_private.h" 29 29 30 TDB_DATA tdb_null;30 _PUBLIC_ TDB_DATA tdb_null; 31 31 32 32 /* … … 34 34 the TDB_SEQNUM flag 35 35 */ 36 void tdb_increment_seqnum_nonblock(struct tdb_context *tdb)36 _PUBLIC_ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb) 37 37 { 38 38 tdb_off_t seqnum=0; 39 39 40 40 if (!(tdb->flags & TDB_SEQNUM)) { 41 41 return; … … 60 60 } 61 61 62 if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) { 62 if (tdb_nest_lock(tdb, TDB_SEQNUM_OFS, F_WRLCK, 63 TDB_LOCK_WAIT|TDB_LOCK_PROBE) != 0) { 63 64 return; 64 65 } … … 66 67 tdb_increment_seqnum_nonblock(tdb); 67 68 68 tdb_ brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1);69 tdb_nest_unlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, false); 69 70 } 70 71 … … 80 81 { 81 82 tdb_off_t rec_ptr; 82 83 83 84 /* read in the hash top */ 84 85 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) … … 154 155 } 155 156 } 156 157 157 158 158 /* must be long enough key, data and tailer */ … … 171 171 return tdb_rec_write(tdb, rec_ptr, &rec); 172 172 } 173 173 174 174 return 0; 175 175 } … … 200 200 } 201 201 202 TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)202 _PUBLIC_ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) 203 203 { 204 204 TDB_DATA ret = _tdb_fetch(tdb, key); … … 213 213 * should be fast and should not block on other syscalls. 214 214 * 215 * DON T CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.215 * DON'T CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS. 216 216 * 217 217 * For mmapped tdb's that do not have a transaction open it points the parsing … … 222 222 * This is interesting for all readers of potentially large data structures in 223 223 * the tdb records, ldb indexes being one example. 224 * 225 * Return -1 if the record was not found. 224 226 */ 225 227 226 int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,228 _PUBLIC_ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, 227 229 int (*parser)(TDB_DATA key, TDB_DATA data, 228 230 void *private_data), … … 238 240 239 241 if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) { 242 /* record not found */ 240 243 tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, -1); 241 244 tdb->ecode = TDB_ERR_NOEXIST; 242 return 0;245 return -1; 243 246 } 244 247 tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, 0); … … 261 264 { 262 265 struct tdb_record rec; 263 266 264 267 if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) 265 268 return 0; … … 268 271 } 269 272 270 int tdb_exists(struct tdb_context *tdb, TDB_DATA key)273 _PUBLIC_ int tdb_exists(struct tdb_context *tdb, TDB_DATA key) 271 274 { 272 275 uint32_t hash = tdb->hash_fn(&key); … … 319 322 tdb_off_t rec_ptr; 320 323 struct tdb_record rec; 321 324 322 325 /* read in the hash top */ 323 326 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) … … 348 351 return -1; 349 352 } 350 353 351 354 /* read in the hash top */ 352 355 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) … … 427 430 } 428 431 429 int tdb_delete(struct tdb_context *tdb, TDB_DATA key)432 _PUBLIC_ int tdb_delete(struct tdb_context *tdb, TDB_DATA key) 430 433 { 431 434 uint32_t hash = tdb->hash_fn(&key); … … 444 447 { 445 448 tdb_off_t rec_ptr; 446 449 447 450 /* read in the hash top */ 448 451 if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) … … 597 600 return 0 on success, -1 on failure 598 601 */ 599 int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag)602 _PUBLIC_ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) 600 603 { 601 604 uint32_t hash; … … 620 623 621 624 /* Append to an entry. Create if not exist. */ 622 int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf)625 _PUBLIC_ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) 623 626 { 624 627 uint32_t hash; … … 659 662 ret = _tdb_store(tdb, key, dbuf, 0, hash); 660 663 tdb_trace_2rec_retrec(tdb, "tdb_append", key, new_dbuf, dbuf); 661 664 662 665 failed: 663 666 tdb_unlock(tdb, BUCKET(hash), F_WRLCK); … … 671 674 useful for external logging functions 672 675 */ 673 const char *tdb_name(struct tdb_context *tdb)676 _PUBLIC_ const char *tdb_name(struct tdb_context *tdb) 674 677 { 675 678 return tdb->name; … … 681 684 of the fd 682 685 */ 683 int tdb_fd(struct tdb_context *tdb)686 _PUBLIC_ int tdb_fd(struct tdb_context *tdb) 684 687 { 685 688 return tdb->fd; … … 690 693 useful for external tdb routines that wish to log tdb errors 691 694 */ 692 tdb_log_func tdb_log_fn(struct tdb_context *tdb)695 _PUBLIC_ tdb_log_func tdb_log_fn(struct tdb_context *tdb) 693 696 { 694 697 return tdb->log.log_fn; … … 706 709 test of a possible tdb change. 707 710 */ 708 int tdb_get_seqnum(struct tdb_context *tdb)711 _PUBLIC_ int tdb_get_seqnum(struct tdb_context *tdb) 709 712 { 710 713 tdb_off_t seqnum=0; … … 714 717 } 715 718 716 int tdb_hash_size(struct tdb_context *tdb)719 _PUBLIC_ int tdb_hash_size(struct tdb_context *tdb) 717 720 { 718 721 return tdb->header.hash_size; 719 722 } 720 723 721 size_t tdb_map_size(struct tdb_context *tdb)724 _PUBLIC_ size_t tdb_map_size(struct tdb_context *tdb) 722 725 { 723 726 return tdb->map_size; 724 727 } 725 728 726 int tdb_get_flags(struct tdb_context *tdb)729 _PUBLIC_ int tdb_get_flags(struct tdb_context *tdb) 727 730 { 728 731 return tdb->flags; 729 732 } 730 733 731 void tdb_add_flags(struct tdb_context *tdb, unsigned flags)734 _PUBLIC_ void tdb_add_flags(struct tdb_context *tdb, unsigned flags) 732 735 { 733 736 if ((flags & TDB_ALLOW_NESTING) && … … 749 752 } 750 753 751 void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)754 _PUBLIC_ void tdb_remove_flags(struct tdb_context *tdb, unsigned flags) 752 755 { 753 756 if ((flags & TDB_ALLOW_NESTING) && … … 773 776 enable sequence number handling on an open tdb 774 777 */ 775 void tdb_enable_seqnum(struct tdb_context *tdb)778 _PUBLIC_ void tdb_enable_seqnum(struct tdb_context *tdb) 776 779 { 777 780 tdb->flags |= TDB_SEQNUM; … … 805 808 /* 806 809 wipe the entire database, deleting all records. This can be done 807 very fast by using a globallock. The entire data portion of the810 very fast by using a allrecord lock. The entire data portion of the 808 811 file becomes a single entry in the freelist. 809 812 810 813 This code carefully steps around the recovery area, leaving it alone 811 814 */ 812 int tdb_wipe_all(struct tdb_context *tdb)815 _PUBLIC_ int tdb_wipe_all(struct tdb_context *tdb) 813 816 { 814 817 int i; … … 917 920 repack a tdb 918 921 */ 919 int tdb_repack(struct tdb_context *tdb)922 _PUBLIC_ int tdb_repack(struct tdb_context *tdb) 920 923 { 921 924 struct tdb_context *tmp_db; … … 987 990 } 988 991 992 /* Even on files, we can get partial writes due to signals. */ 993 bool tdb_write_all(int fd, const void *buf, size_t count) 994 { 995 while (count) { 996 ssize_t ret; 997 ret = write(fd, buf, count); 998 if (ret < 0) 999 return false; 1000 buf = (const char *)buf + ret; 1001 count -= ret; 1002 } 1003 return true; 1004 } 1005 989 1006 #ifdef TDB_TRACE 990 1007 static void tdb_trace_write(struct tdb_context *tdb, const char *str) 991 1008 { 992 if ( write(tdb->tracefd, str, strlen(str)) !=strlen(str)) {1009 if (!tdb_write_alltdb->tracefd, str, strlen(str)) { 993 1010 close(tdb->tracefd); 994 1011 tdb->tracefd = -1; -
trunk/server/lib/tdb/common/tdb_private.h
r657 r745 5 5 6 6 Copyright (C) Andrew Tridgell 2005 7 7 8 8 ** NOTE! The following LGPL license applies to the tdb 9 9 ** library. This does NOT imply that all of Samba is released 10 10 ** under the LGPL 11 11 12 12 This library is free software; you can redistribute it and/or 13 13 modify it under the terms of the GNU Lesser General Public … … 23 23 License along with this library; if not, see <http://www.gnu.org/licenses/>. 24 24 */ 25 26 25 #ifdef __OS2__ 27 26 #define INCL_ERRORS … … 56 55 #define TDB_DEAD_MAGIC (0xFEE1DEAD) 57 56 #define TDB_RECOVERY_MAGIC (0xf53bc0e7U) 57 #define TDB_RECOVERY_INVALID_MAGIC (0x0) 58 #define TDB_HASH_RWLOCK_MAGIC (0xbad1a51U) 58 59 #define TDB_ALIGNMENT 4 59 60 #define DEFAULT_HASH_SIZE 131 … … 108 109 109 110 /* lock offsets */ 110 #define GLOBAL_LOCK0111 #define OPEN_LOCK 0 111 112 #define ACTIVE_LOCK 4 112 113 #define TRANSACTION_LOCK 8 … … 153 154 tdb_off_t recovery_start; /* offset of transaction recovery region */ 154 155 tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */ 155 tdb_off_t reserved[29]; 156 uint32_t magic1_hash; /* hash of TDB_MAGIC_FOOD. */ 157 uint32_t magic2_hash; /* hash of TDB_MAGIC. */ 158 tdb_off_t reserved[27]; 156 159 }; 157 160 158 161 struct tdb_lock_type { 159 int list;162 uint32_t off; 160 163 uint32_t count; 161 164 uint32_t ltype; … … 169 172 }; 170 173 174 enum tdb_lock_flags { 175 /* WAIT == F_SETLKW, NOWAIT == F_SETLK */ 176 TDB_LOCK_NOWAIT = 0, 177 TDB_LOCK_WAIT = 1, 178 /* If set, don't log an error on failure. */ 179 TDB_LOCK_PROBE = 2, 180 /* If set, don't actually lock at all. */ 181 TDB_LOCK_MARK_ONLY = 4, 182 }; 171 183 172 184 struct tdb_methods { … … 176 188 int (*tdb_oob)(struct tdb_context *, tdb_off_t , int ); 177 189 int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t ); 178 int (*tdb_brlock)(struct tdb_context *, tdb_off_t , int, int, int, size_t);179 190 }; 180 191 … … 187 198 int traverse_read; /* read-only traversal */ 188 199 int traverse_write; /* read-write traversal */ 189 struct tdb_lock_type global_lock;200 struct tdb_lock_type allrecord_lock; /* .offset == upgradable */ 190 201 int num_lockrecs; 191 202 struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */ … … 200 211 unsigned int (*hash_fn)(TDB_DATA *key); 201 212 int open_flags; /* flags used in the open - needed by reopen */ 202 unsigned int num_locks; /* number of chain locks held */203 213 const struct tdb_methods *methods; 204 214 struct tdb_transaction *transaction; 205 215 int page_size; 206 216 int max_dead_records; 207 int transaction_lock_count;208 217 #ifdef TDB_TRACE 209 218 int tracefd; … … 225 234 int tdb_lock(struct tdb_context *tdb, int list, int ltype); 226 235 int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype); 236 int tdb_nest_lock(struct tdb_context *tdb, uint32_t offset, int ltype, 237 enum tdb_lock_flags flags); 238 int tdb_nest_unlock(struct tdb_context *tdb, uint32_t offset, int ltype, 239 bool mark_lock); 227 240 int tdb_unlock(struct tdb_context *tdb, int list, int ltype); 228 int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len); 229 int tdb_transaction_lock(struct tdb_context *tdb, int ltype); 230 int tdb_transaction_unlock(struct tdb_context *tdb); 231 int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len); 241 int tdb_brlock(struct tdb_context *tdb, 242 int rw_type, tdb_off_t offset, size_t len, 243 enum tdb_lock_flags flags); 244 int tdb_brunlock(struct tdb_context *tdb, 245 int rw_type, tdb_off_t offset, size_t len); 246 bool tdb_have_extra_locks(struct tdb_context *tdb); 247 void tdb_release_transaction_locks(struct tdb_context *tdb); 248 int tdb_transaction_lock(struct tdb_context *tdb, int ltype, 249 enum tdb_lock_flags lockflags); 250 int tdb_transaction_unlock(struct tdb_context *tdb, int ltype); 251 int tdb_recovery_area(struct tdb_context *tdb, 252 const struct tdb_methods *methods, 253 tdb_off_t *recovery_offset, 254 struct tdb_record *rec); 255 int tdb_allrecord_lock(struct tdb_context *tdb, int ltype, 256 enum tdb_lock_flags flags, bool upgradable); 257 int tdb_allrecord_unlock(struct tdb_context *tdb, int ltype, bool mark_lock); 258 int tdb_allrecord_upgrade(struct tdb_context *tdb); 232 259 int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off); 233 260 int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); … … 241 268 int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off); 242 269 int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off); 243 int _tdb_transaction_cancel(struct tdb_context *tdb);270 bool tdb_needs_recovery(struct tdb_context *tdb); 244 271 int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec); 245 272 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec); … … 257 284 int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, 258 285 struct tdb_record *rec); 259 260 286 bool tdb_write_all(int fd, const void *buf, size_t count); 287 int tdb_transaction_recover(struct tdb_context *tdb); 288 void tdb_header_hash(struct tdb_context *tdb, 289 uint32_t *magic1_hash, uint32_t *magic2_hash); 290 unsigned int tdb_old_hash(TDB_DATA *key); 291 size_t tdb_dead_space(struct tdb_context *tdb, tdb_off_t off); -
trunk/server/lib/tdb/common/transaction.c
r657 r745 9 9 ** library. This does NOT imply that all of Samba is released 10 10 ** under the LGPL 11 11 12 12 This library is free software; you can redistribute it and/or 13 13 modify it under the terms of the GNU Lesser General Public … … 60 60 existing transaction record. If the inner transaction is cancelled 61 61 then a subsequent commit will fail 62 62 63 63 - keep a mirrored copy of the tdb hash chain heads to allow for the 64 64 fast hash heads scan on traverse, updating the mirrored copy in … … 77 77 78 78 - check for a valid recovery record on open of the tdb, while the 79 globallock is held. Automatically recover from the transaction79 open lock is held. Automatically recover from the transaction 80 80 recovery area if needed, then continue with the open as 81 81 usual. This allows for smooth crash recovery with no administrator … … 136 136 tdb_off_t magic_offset; 137 137 138 /* set when the GLOBAL_LOCK has been taken */139 bool global_lock_taken;140 141 138 /* old file size before transaction */ 142 139 tdb_len_t old_map_size; 143 140 144 /* we should re-pack on commit*/145 bool need_repack;141 /* did we expand in this transaction */ 142 bool expanded; 146 143 }; 147 144 … … 189 186 } 190 187 } 191 188 192 189 /* now copy it out of this block */ 193 190 memcpy(buf, tdb->transaction->blocks[blk] + (off % tdb->transaction->block_size), len); … … 296 293 } 297 294 } 298 295 299 296 /* overwrite part of an existing block */ 300 297 if (buf == NULL) { … … 407 404 } 408 405 409 tdb->transaction->need_repack = true; 410 411 return 0; 412 } 413 414 /* 415 brlock during a transaction - ignore them 416 */ 417 static int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset, 418 int rw_type, int lck_type, int probe, size_t len) 419 { 406 tdb->transaction->expanded = true; 407 420 408 return 0; 421 409 } … … 427 415 transaction_oob, 428 416 transaction_expand_file, 429 transaction_brlock430 417 }; 431 418 … … 435 422 transaction is allowed to be pending per tdb_context 436 423 */ 437 int tdb_transaction_start(struct tdb_context *tdb) 424 static int _tdb_transaction_start(struct tdb_context *tdb, 425 enum tdb_lock_flags lockflags) 438 426 { 439 427 /* some sanity checks */ … … 456 444 } 457 445 458 if (tdb ->num_locks != 0 || tdb->global_lock.count) {446 if (tdb_have_extra_locks(tdb)) { 459 447 /* the caller must not have any locks when starting a 460 448 transaction as otherwise we'll be screwed by lack … … 487 475 discussed with Volker, there are a number of ways we could 488 476 make this async, which we will probably do in the future */ 489 if (tdb_transaction_lock(tdb, F_WRLCK ) == -1) {477 if (tdb_transaction_lock(tdb, F_WRLCK, lockflags) == -1) { 490 478 SAFE_FREE(tdb->transaction->blocks); 491 479 SAFE_FREE(tdb->transaction); 492 return -1; 493 } 494 480 if ((lockflags & TDB_LOCK_WAIT) == 0) { 481 tdb->ecode = TDB_ERR_NOLOCK; 482 } 483 return -1; 484 } 485 495 486 /* get a read lock from the freelist to the end of file. This 496 487 is upgraded to a write lock during the commit */ 497 488 #ifndef __OS2__ // YD the transation lock is an exclusive lock for us, it is enough. 498 if (tdb_ brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) {489 if (tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, true) == -1) { 499 490 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n")); 500 tdb->ecode = TDB_ERR_LOCK; 501 goto fail; 491 goto fail_allrecord_lock; 502 492 } 503 493 #endif … … 531 521 tdb_trace(tdb, "tdb_transaction_start"); 532 522 return 0; 533 523 534 524 fail: 535 525 #ifndef __OS2__ // YD the transation lock is an exclusive lock for us, it is enough. 536 tdb_ brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);526 tdb_allrecord_unlock(tdb, F_RDLCK, false); 537 527 #endif 538 tdb_transaction_unlock(tdb); 528 fail_allrecord_lock: 529 tdb_transaction_unlock(tdb, F_WRLCK); 539 530 SAFE_FREE(tdb->transaction->blocks); 540 531 SAFE_FREE(tdb->transaction->hash_heads); … … 543 534 } 544 535 536 _PUBLIC_ int tdb_transaction_start(struct tdb_context *tdb) 537 { 538 return _tdb_transaction_start(tdb, TDB_LOCK_WAIT); 539 } 540 541 _PUBLIC_ int tdb_transaction_start_nonblock(struct tdb_context *tdb) 542 { 543 return _tdb_transaction_start(tdb, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE); 544 } 545 545 546 546 /* … … 553 553 } 554 554 555 #ifdef HAVE_FDATASYNC 556 if (fdatasync(tdb->fd) != 0) { 557 #else 555 558 if (fsync(tdb->fd) != 0) { 559 #endif 556 560 tdb->ecode = TDB_ERR_IO; 557 561 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n")); … … 574 578 575 579 576 int _tdb_transaction_cancel(struct tdb_context *tdb)580 static int _tdb_transaction_cancel(struct tdb_context *tdb) 577 581 { 578 582 int i, ret = 0; … … 601 605 if (tdb->transaction->magic_offset) { 602 606 const struct tdb_methods *methods = tdb->transaction->io_methods; 603 uint32_t zero = 0;607 const uint32_t invalid = TDB_RECOVERY_INVALID_MAGIC; 604 608 605 609 /* remove the recovery marker */ 606 if (methods->tdb_write(tdb, tdb->transaction->magic_offset, & zero, 4) == -1 ||610 if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &invalid, 4) == -1 || 607 611 transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) { 608 612 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_cancel: failed to remove recovery magic\n")); … … 611 615 } 612 616 613 if (tdb->transaction->global_lock_taken) { 614 tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); 615 tdb->transaction->global_lock_taken = false; 616 } 617 618 /* remove any global lock created during the transaction */ 619 if (tdb->global_lock.count != 0) { 620 tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size); 621 tdb->global_lock.count = 0; 622 } 623 624 /* remove any locks created during the transaction */ 625 if (tdb->num_locks != 0) { 626 for (i=0;i<tdb->num_lockrecs;i++) { 627 tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list, 628 F_UNLCK,F_SETLKW, 0, 1); 629 } 630 tdb->num_locks = 0; 631 tdb->num_lockrecs = 0; 632 SAFE_FREE(tdb->lockrecs); 633 } 617 /* This also removes the OPEN_LOCK, if we have it. */ 618 tdb_release_transaction_locks(tdb); 634 619 635 620 /* restore the normal io methods */ 636 621 tdb->methods = tdb->transaction->io_methods; 637 622 638 #ifndef __OS2__ // YD the transation lock is an exclusive lock for us, it is enough639 tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);640 #endif641 tdb_transaction_unlock(tdb);642 623 SAFE_FREE(tdb->transaction->hash_heads); 643 624 SAFE_FREE(tdb->transaction); 644 625 645 626 return ret; 646 627 } … … 649 630 cancel the current transaction 650 631 */ 651 int tdb_transaction_cancel(struct tdb_context *tdb)632 _PUBLIC_ int tdb_transaction_cancel(struct tdb_context *tdb) 652 633 { 653 634 tdb_trace(tdb, "tdb_transaction_cancel"); … … 682 663 } 683 664 665 int tdb_recovery_area(struct tdb_context *tdb, 666 const struct tdb_methods *methods, 667 tdb_off_t *recovery_offset, 668 struct tdb_record *rec) 669 { 670 if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, recovery_offset) == -1) { 671 return -1; 672 } 673 674 if (*recovery_offset == 0) { 675 rec->rec_len = 0; 676 return 0; 677 } 678 679 if (methods->tdb_read(tdb, *recovery_offset, rec, sizeof(*rec), 680 DOCONV()) == -1) { 681 return -1; 682 } 683 684 /* ignore invalid recovery regions: can happen in crash */ 685 if (rec->magic != TDB_RECOVERY_MAGIC && 686 rec->magic != TDB_RECOVERY_INVALID_MAGIC) { 687 *recovery_offset = 0; 688 rec->rec_len = 0; 689 } 690 return 0; 691 } 692 684 693 /* 685 694 allocate the recovery area, or use an existing recovery area if it is … … 695 704 tdb_off_t recovery_head; 696 705 697 if (tdb_ ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {706 if (tdb_recovery_area(tdb, methods, &recovery_head, &rec) == -1) { 698 707 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n")); 699 return -1;700 }701 702 rec.rec_len = 0;703 704 if (recovery_head != 0 &&705 methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {706 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));707 708 return -1; 708 709 } … … 800 801 memset(rec, 0, sizeof(*rec)); 801 802 802 rec->magic = 0;803 rec->magic = TDB_RECOVERY_INVALID_MAGIC; 803 804 rec->data_len = recovery_size; 804 805 rec->rec_len = recovery_max_size; 805 806 rec->key_len = old_map_size; 806 CONVERT( rec);807 CONVERT(*rec); 807 808 808 809 /* build the recovery data into a single blob to allow us to do a single … … 822 823 length = tdb->transaction->last_block_size; 823 824 } 824 825 825 826 if (offset >= old_map_size) { 826 827 continue; … … 851 852 tailer = sizeof(*rec) + recovery_max_size; 852 853 memcpy(p, &tailer, 4); 853 CONVERT(p); 854 if (DOCONV()) { 855 tdb_convert(p, 4); 856 } 854 857 855 858 /* write the recovery data to the recovery area */ … … 935 938 936 939 methods = tdb->transaction->io_methods; 937 940 938 941 /* if there are any locks pending then the caller has not 939 942 nested their locks properly, so fail the transaction */ 940 if (tdb ->num_locks || tdb->global_lock.count) {943 if (tdb_have_extra_locks(tdb)) { 941 944 tdb->ecode = TDB_ERR_LOCK; 942 945 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: locks pending on commit\n")); … … 947 950 /* upgrade the main transaction lock region to a write lock */ 948 951 #ifndef __OS2__ // YD the global lock is an exclusive lock for us, it is enough 949 if (tdb_ brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) {952 if (tdb_allrecord_upgrade(tdb) == -1) { 950 953 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to upgrade hash locks\n")); 951 tdb->ecode = TDB_ERR_LOCK;952 954 _tdb_transaction_cancel(tdb); 953 955 return -1; … … 955 957 #endif 956 958 957 /* get the globallock - this prevents new users attaching to the database959 /* get the open lock - this prevents new users attaching to the database 958 960 during the commit */ 959 if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { 960 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to get global lock\n")); 961 tdb->ecode = TDB_ERR_LOCK; 961 if (tdb_nest_lock(tdb, OPEN_LOCK, F_WRLCK, TDB_LOCK_WAIT) == -1) { 962 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to get open lock\n")); 962 963 _tdb_transaction_cancel(tdb); 963 964 return -1; 964 965 } 965 966 tdb->transaction->global_lock_taken = true;967 966 968 967 if (!(tdb->flags & TDB_NOSYNC)) { … … 991 990 } 992 991 993 /* Keep the globallock until the actual commit */992 /* Keep the open lock until the actual commit */ 994 993 995 994 return 0; … … 999 998 prepare to commit the current transaction 1000 999 */ 1001 int tdb_transaction_prepare_commit(struct tdb_context *tdb)1002 { 1000 _PUBLIC_ int tdb_transaction_prepare_commit(struct tdb_context *tdb) 1001 { 1003 1002 tdb_trace(tdb, "tdb_transaction_prepare_commit"); 1004 1003 return _tdb_transaction_prepare_commit(tdb); 1005 1004 } 1006 1005 1006 /* A repack is worthwhile if the largest is less than half total free. */ 1007 static bool repack_worthwhile(struct tdb_context *tdb) 1008 { 1009 tdb_off_t ptr; 1010 struct tdb_record rec; 1011 tdb_len_t total = 0, largest = 0; 1012 1013 if (tdb_ofs_read(tdb, FREELIST_TOP, &ptr) == -1) { 1014 return false; 1015 } 1016 1017 while (ptr != 0 && tdb_rec_free_read(tdb, ptr, &rec) == 0) { 1018 total += rec.rec_len; 1019 if (rec.rec_len > largest) { 1020 largest = rec.rec_len; 1021 } 1022 ptr = rec.next; 1023 } 1024 1025 return total > largest * 2; 1026 } 1027 1007 1028 /* 1008 1029 commit the current transaction 1009 1030 */ 1010 int tdb_transaction_commit(struct tdb_context *tdb)1011 { 1031 _PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb) 1032 { 1012 1033 const struct tdb_methods *methods; 1013 1034 int i; 1014 bool need_repack ;1035 bool need_repack = false; 1015 1036 1016 1037 if (tdb->transaction == NULL) { … … 1020 1041 1021 1042 tdb_trace(tdb, "tdb_transaction_commit"); 1043 1022 1044 if (tdb->transaction->transaction_error) { 1023 1045 tdb->ecode = TDB_ERR_IO; … … 1026 1048 return -1; 1027 1049 } 1050 1028 1051 1029 1052 if (tdb->transaction->nesting != 0) { … … 1060 1083 length = tdb->transaction->last_block_size; 1061 1084 } 1085 1062 1086 if (methods->tdb_write(tdb, offset, tdb->transaction->blocks[i], length) == -1) { 1063 1087 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n")); 1064 1088 1065 1089 /* we've overwritten part of the data and 1066 1090 possibly expanded the file, so we need to … … 1076 1100 SAFE_FREE(tdb->transaction->blocks[i]); 1077 1101 } 1102 1103 /* Do this before we drop lock or blocks. */ 1104 if (tdb->transaction->expanded) { 1105 need_repack = repack_worthwhile(tdb); 1106 } 1078 1107 1079 1108 SAFE_FREE(tdb->transaction->blocks); … … 1100 1129 #endif 1101 1130 1102 need_repack = tdb->transaction->need_repack;1103 1104 1131 /* use a transaction cancel to free memory and remove the 1105 1132 transaction locks */ … … 1116 1143 /* 1117 1144 recover from an aborted transaction. Must be called with exclusive 1118 database write access already established (including the global1145 database write access already established (including the open 1119 1146 lock to prevent new processes attaching) 1120 1147 */ … … 1217 1244 return -1; 1218 1245 } 1219 1220 /* reduce the file size to the old size */1221 tdb_munmap(tdb);1222 if (ftruncate(tdb->fd, recovery_eof) != 0) {1223 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to reduce to recovery size\n"));1224 tdb->ecode = TDB_ERR_IO;1225 return -1;1226 }1227 tdb->map_size = recovery_eof;1228 tdb_mmap(tdb);1229 1246 1230 1247 if (transaction_sync(tdb, 0, recovery_eof) == -1) { … … 1240 1257 return 0; 1241 1258 } 1259 1260 /* Any I/O failures we say "needs recovery". */ 1261 bool tdb_needs_recovery(struct tdb_context *tdb) 1262 { 1263 tdb_off_t recovery_head; 1264 struct tdb_record rec; 1265 1266 /* find the recovery area */ 1267 if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { 1268 return true; 1269 } 1270 1271 if (recovery_head == 0) { 1272 /* we have never allocated a recovery record */ 1273 return false; 1274 } 1275 1276 /* read the recovery record */ 1277 if (tdb->methods->tdb_read(tdb, recovery_head, &rec, 1278 sizeof(rec), DOCONV()) == -1) { 1279 return true; 1280 } 1281 1282 return (rec.magic == TDB_RECOVERY_MAGIC); 1283 } -
trunk/server/lib/tdb/common/traverse.c
r647 r745 7 7 Copyright (C) Paul `Rusty' Russell 2000 8 8 Copyright (C) Jeremy Allison 2000-2003 9 9 10 10 ** NOTE! The following LGPL license applies to the tdb 11 11 ** library. This does NOT imply that all of Samba is released 12 12 ** under the LGPL 13 13 14 14 This library is free software; you can redistribute it and/or 15 15 modify it under the terms of the GNU Lesser General Public … … 45 45 hashes are used. In that case we spend most of our 46 46 time in tdb_brlock(), locking empty hash chains. 47 47 48 48 To avoid this, we do an unlocked pre-check to see 49 49 if the hash chain is empty before starting to look … … 53 53 lock, so instead we get the lock and re-fetch the 54 54 value below. 55 55 56 56 Notice that not doing this optimisation on the 57 57 first hash chain is critical. We must guarantee … … 63 63 could miss them anyway without this trick, so the 64 64 semantics don't change. 65 65 66 66 With a non-indexed ldb search this trick gains us a 67 67 factor of around 80 in speed on a linux 2.6.x … … 213 213 a write style traverse - temporarily marks the db read only 214 214 */ 215 int tdb_traverse_read(struct tdb_context *tdb,215 _PUBLIC_ int tdb_traverse_read(struct tdb_context *tdb, 216 216 tdb_traverse_func fn, void *private_data) 217 217 { 218 219 218 struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; 220 219 int ret; … … 222 221 /* we need to get a read lock on the transaction lock here to 223 222 cope with the lock ordering semantics of solaris10 */ 224 if (tdb_transaction_lock(tdb, F_RDLCK )) {223 if (tdb_transaction_lock(tdb, F_RDLCK, TDB_LOCK_WAIT)) { 225 224 return -1; 226 225 } … … 231 230 tdb->traverse_read--; 232 231 233 tdb_transaction_unlock(tdb );232 tdb_transaction_unlock(tdb, F_RDLCK); 234 233 235 234 return ret; … … 243 242 alignment restrictions malloc gives you. 244 243 */ 245 int tdb_traverse(struct tdb_context *tdb,244 _PUBLIC_ int tdb_traverse(struct tdb_context *tdb, 246 245 tdb_traverse_func fn, void *private_data) 247 246 { … … 253 252 } 254 253 255 if (tdb_transaction_lock(tdb, F_WRLCK )) {254 if (tdb_transaction_lock(tdb, F_WRLCK, TDB_LOCK_WAIT)) { 256 255 return -1; 257 256 } … … 262 261 tdb->traverse_write--; 263 262 264 tdb_transaction_unlock(tdb );263 tdb_transaction_unlock(tdb, F_WRLCK); 265 264 266 265 return ret; … … 269 268 270 269 /* find the first entry in the database and return its key */ 271 TDB_DATA tdb_firstkey(struct tdb_context *tdb)270 _PUBLIC_ TDB_DATA tdb_firstkey(struct tdb_context *tdb) 272 271 { 273 272 TDB_DATA key; … … 300 299 301 300 /* find the next entry in the database, returning its key */ 302 TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey)301 _PUBLIC_ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) 303 302 { 304 303 uint32_t oldhash; -
trunk/server/lib/tdb/docs/README
r414 r745 106 106 107 107 ---------------------------------------------------------------------- 108 int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, 109 int (*parser)(TDB_DATA key, TDB_DATA data, 110 void *private_data), 111 void *private_data); 112 113 Hand a record to a parser function without allocating it. 114 115 This function is meant as a fast tdb_fetch alternative for large records 116 that are frequently read. The "key" and "data" arguments point directly 117 into the tdb shared memory, they are not aligned at any boundary. 118 119 WARNING: The parser is called while tdb holds a lock on the record. DO NOT 120 call other tdb routines from within the parser. Also, for good performance 121 you should make the parser fast to allow parallel operations. 122 123 tdb_parse_record returns -1 if the record was not found. If the record was 124 found, the return value of "parser" is passed up to the caller. 125 126 ---------------------------------------------------------------------- 108 127 int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key); 109 128 -
trunk/server/lib/tdb/include/tdb.h
r414 r745 31 31 #endif 32 32 33 #include "signal.h" 34 35 /* flags to tdb_store() */ 36 #define TDB_REPLACE 1 /* Unused */ 37 #define TDB_INSERT 2 /* Don't overwrite an existing entry */ 38 #define TDB_MODIFY 3 /* Don't create an existing entry */ 39 40 /* flags for tdb_open() */ 41 #define TDB_DEFAULT 0 /* just a readability place holder */ 42 #define TDB_CLEAR_IF_FIRST 1 43 #define TDB_INTERNAL 2 /* don't store on disk */ 44 #define TDB_NOLOCK 4 /* don't do any locking */ 45 #define TDB_NOMMAP 8 /* don't use mmap */ 46 #define TDB_CONVERT 16 /* convert endian (internal use) */ 47 #define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */ 48 #define TDB_NOSYNC 64 /* don't use synchronous transactions */ 49 #define TDB_SEQNUM 128 /* maintain a sequence number */ 50 #define TDB_VOLATILE 256 /* Activate the per-hashchain freelist, default 5 */ 51 #define TDB_ALLOW_NESTING 512 /* Allow transactions to nest */ 52 #define TDB_DISALLOW_NESTING 1024 /* Disallow transactions to nest */ 53 54 /* error codes */ 33 #include <signal.h> 34 35 /** 36 * @defgroup tdb The tdb API 37 * 38 * tdb is a Trivial database. In concept, it is very much like GDBM, and BSD's 39 * DB except that it allows multiple simultaneous writers and uses locking 40 * internally to keep writers from trampling on each other. tdb is also 41 * extremely small. 42 * 43 * @section tdb_interface Interface 44 * 45 * The interface is very similar to gdbm except for the following: 46 * 47 * <ul> 48 * <li>different open interface. The tdb_open call is more similar to a 49 * traditional open()</li> 50 * <li>no tdbm_reorganise() function</li> 51 * <li>no tdbm_sync() function. No operations are cached in the library 52 * anyway</li> 53 * <li>added a tdb_traverse() function for traversing the whole database</li> 54 * <li>added transactions support</li> 55 * </ul> 56 * 57 * A general rule for using tdb is that the caller frees any returned TDB_DATA 58 * structures. Just call free(p.dptr) to free a TDB_DATA return value called p. 59 * This is the same as gdbm. 60 * 61 * @{ 62 */ 63 64 /** Flags to tdb_store() */ 65 #define TDB_REPLACE 1 /** Unused */ 66 #define TDB_INSERT 2 /** Don't overwrite an existing entry */ 67 #define TDB_MODIFY 3 /** Don't create an existing entry */ 68 69 /** Flags for tdb_open() */ 70 #define TDB_DEFAULT 0 /** just a readability place holder */ 71 #define TDB_CLEAR_IF_FIRST 1 /** If this is the first open, wipe the db */ 72 #define TDB_INTERNAL 2 /** Don't store on disk */ 73 #define TDB_NOLOCK 4 /** Don't do any locking */ 74 #define TDB_NOMMAP 8 /** Don't use mmap */ 75 #define TDB_CONVERT 16 /** Convert endian (internal use) */ 76 #define TDB_BIGENDIAN 32 /** Header is big-endian (internal use) */ 77 #define TDB_NOSYNC 64 /** Don't use synchronous transactions */ 78 #define TDB_SEQNUM 128 /** Maintain a sequence number */ 79 #define TDB_VOLATILE 256 /** Activate the per-hashchain freelist, default 5 */ 80 #define TDB_ALLOW_NESTING 512 /** Allow transactions to nest */ 81 #define TDB_DISALLOW_NESTING 1024 /** Disallow transactions to nest */ 82 #define TDB_INCOMPATIBLE_HASH 2048 /** Better hashing: can't be opened by tdb < 1.2.6. */ 83 84 /** The tdb error codes */ 55 85 enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, 56 86 TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, … … 58 88 TDB_ERR_NESTING}; 59 89 60 /* debugging uses one of the following levels */90 /** Debugging uses one of the following levels */ 61 91 enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, 62 92 TDB_DEBUG_WARNING, TDB_DEBUG_TRACE}; 63 93 94 /** The tdb data structure */ 64 95 typedef struct TDB_DATA { 65 96 unsigned char *dptr; … … 79 110 #endif 80 111 81 /* this is the context structure that is returned from a db open*/112 /** This is the context structure that is returned from a db open. */ 82 113 typedef struct tdb_context TDB_CONTEXT; 83 114 … … 91 122 }; 92 123 124 /** 125 * @brief Open the database and creating it if necessary. 126 * 127 * @param[in] name The name of the db to open. 128 * 129 * @param[in] hash_size The hash size is advisory, use zero for a default 130 * value. 131 * 132 * @param[in] tdb_flags The flags to use to open the db:\n\n 133 * TDB_CLEAR_IF_FIRST - Clear database if we are the 134 * only one with it open\n 135 * TDB_INTERNAL - Don't use a file, instaed store the 136 * data in memory. The filename is 137 * ignored in this case.\n 138 * TDB_NOLOCK - Don't do any locking\n 139 * TDB_NOMMAP - Don't use mmap\n 140 * TDB_NOSYNC - Don't synchronise transactions to disk\n 141 * TDB_SEQNUM - Maintain a sequence number\n 142 * TDB_VOLATILE - activate the per-hashchain freelist, 143 * default 5.\n 144 * TDB_ALLOW_NESTING - Allow transactions to nest.\n 145 * TDB_DISALLOW_NESTING - Disallow transactions to nest.\n 146 * 147 * @param[in] open_flags Flags for the open(2) function. 148 * 149 * @param[in] mode The mode for the open(2) function. 150 * 151 * @return A tdb context structure, NULL on error. 152 */ 93 153 struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, 94 154 int open_flags, mode_t mode); 155 156 /** 157 * @brief Open the database and creating it if necessary. 158 * 159 * This is like tdb_open(), but allows you to pass an initial logging and 160 * hash function. Be careful when passing a hash function - all users of the 161 * database must use the same hash function or you will get data corruption. 162 * 163 * @param[in] name The name of the db to open. 164 * 165 * @param[in] hash_size The hash size is advisory, use zero for a default 166 * value. 167 * 168 * @param[in] tdb_flags The flags to use to open the db:\n\n 169 * TDB_CLEAR_IF_FIRST - Clear database if we are the 170 * only one with it open\n 171 * TDB_INTERNAL - Don't use a file, instaed store the 172 * data in memory. The filename is 173 * ignored in this case.\n 174 * TDB_NOLOCK - Don't do any locking\n 175 * TDB_NOMMAP - Don't use mmap\n 176 * TDB_NOSYNC - Don't synchronise transactions to disk\n 177 * TDB_SEQNUM - Maintain a sequence number\n 178 * TDB_VOLATILE - activate the per-hashchain freelist, 179 * default 5.\n 180 * TDB_ALLOW_NESTING - Allow transactions to nest.\n 181 * TDB_DISALLOW_NESTING - Disallow transactions to nest.\n 182 * 183 * @param[in] open_flags Flags for the open(2) function. 184 * 185 * @param[in] mode The mode for the open(2) function. 186 * 187 * @param[in] log_ctx The logging function to use. 188 * 189 * @param[in] hash_fn The hash function you want to use. 190 * 191 * @return A tdb context structure, NULL on error. 192 * 193 * @see tdb_open() 194 */ 95 195 struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, 96 196 int open_flags, mode_t mode, 97 197 const struct tdb_logging_context *log_ctx, 98 198 tdb_hash_func hash_fn); 199 200 /** 201 * @brief Set the maximum number of dead records per hash chain. 202 * 203 * @param[in] tdb The database handle to set the maximum. 204 * 205 * @param[in] max_dead The maximum number of dead records per hash chain. 206 */ 99 207 void tdb_set_max_dead(struct tdb_context *tdb, int max_dead); 100 208 209 /** 210 * @brief Reopen a tdb. 211 * 212 * This can be used after a fork to ensure that we have an independent seek 213 * pointer from our parent and to re-establish locks. 214 * 215 * @param[in] tdb The database to reopen. 216 * 217 * @return 0 on success, -1 on error. 218 */ 101 219 int tdb_reopen(struct tdb_context *tdb); 220 221 /** 222 * @brief Reopen all tdb's 223 * 224 * If the parent is longlived (ie. a parent daemon architecture), we know it 225 * will keep it's active lock on a tdb opened with CLEAR_IF_FIRST. Thus for 226 * child processes we don't have to add an active lock. This is essential to 227 * improve performance on systems that keep POSIX locks as a non-scalable data 228 * structure in the kernel. 229 * 230 * @param[in] parent_longlived Wether the parent is longlived or not. 231 * 232 * @return 0 on success, -1 on error. 233 */ 102 234 int tdb_reopen_all(int parent_longlived); 235 236 /** 237 * @brief Set a different tdb logging function. 238 * 239 * @param[in] tdb The tdb to set the logging function. 240 * 241 * @param[in] log_ctx The logging function to set. 242 */ 103 243 void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx); 244 245 /** 246 * @brief Get the tdb last error code. 247 * 248 * @param[in] tdb The tdb to get the error code from. 249 * 250 * @return A TDB_ERROR code. 251 * 252 * @see TDB_ERROR 253 */ 104 254 enum TDB_ERROR tdb_error(struct tdb_context *tdb); 255 256 /** 257 * @brief Get a error string for the last tdb error 258 * 259 * @param[in] tdb The tdb to get the error code from. 260 * 261 * @return An error string. 262 */ 105 263 const char *tdb_errorstr(struct tdb_context *tdb); 264 265 /** 266 * @brief Fetch an entry in the database given a key. 267 * 268 * The caller must free the resulting data. 269 * 270 * @param[in] tdb The tdb to fetch the key. 271 * 272 * @param[in] key The key to fetch. 273 * 274 * @return The key entry found in the database, NULL on error with 275 * TDB_ERROR set. 276 * 277 * @see tdb_error() 278 * @see tdb_errorstr() 279 */ 106 280 TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key); 281 282 /** 283 * @brief Hand a record to a parser function without allocating it. 284 * 285 * This function is meant as a fast tdb_fetch alternative for large records 286 * that are frequently read. The "key" and "data" arguments point directly 287 * into the tdb shared memory, they are not aligned at any boundary. 288 * 289 * @warning The parser is called while tdb holds a lock on the record. DO NOT 290 * call other tdb routines from within the parser. Also, for good performance 291 * you should make the parser fast to allow parallel operations. 292 * 293 * @param[in] tdb The tdb to parse the record. 294 * 295 * @param[in] key The key to parse. 296 * 297 * @param[in] parser The parser to use to parse the data. 298 * 299 * @param[in] private_data A private data pointer which is passed to the parser 300 * function. 301 * 302 * @return -1 if the record was not found. If the record was found, 303 * the return value of "parser" is passed up to the caller. 304 */ 107 305 int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, 108 int (*parser)(TDB_DATA key, TDB_DATA data, 109 void *private_data), 110 void *private_data); 306 int (*parser)(TDB_DATA key, TDB_DATA data, 307 void *private_data), 308 void *private_data); 309 310 /** 311 * @brief Delete an entry in the database given a key. 312 * 313 * @param[in] tdb The tdb to delete the key. 314 * 315 * @param[in] key The key to delete. 316 * 317 * @return 0 on success, -1 if the key doesn't exist. 318 */ 111 319 int tdb_delete(struct tdb_context *tdb, TDB_DATA key); 320 321 /** 322 * @brief Store an element in the database. 323 * 324 * This replaces any existing element with the same key. 325 * 326 * @param[in] tdb The tdb to store the entry. 327 * 328 * @param[in] key The key to use to store the entry. 329 * 330 * @param[in] dbuf The data to store under the key. 331 * 332 * @param[in] flag The flags to store the key:\n\n 333 * TDB_INSERT: Don't overwrite an existing entry.\n 334 * TDB_MODIFY: Don't create a new entry\n 335 * 336 * @return 0 on success, -1 on error with error code set. 337 * 338 * @see tdb_error() 339 * @see tdb_errorstr() 340 */ 112 341 int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); 342 343 /** 344 * @brief Append data to an entry. 345 * 346 * If the entry doesn't exist, it will create a new one. 347 * 348 * @param[in] tdb The database to use. 349 * 350 * @param[in] key The key to append the data. 351 * 352 * @param[in] new_dbuf The data to append to the key. 353 * 354 * @return 0 on success, -1 on error with error code set. 355 * 356 * @see tdb_error() 357 * @see tdb_errorstr() 358 */ 113 359 int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf); 360 361 /** 362 * @brief Close a database. 363 * 364 * @param[in] tdb The database to close. 365 * 366 * @return 0 for success, -1 on error. 367 */ 114 368 int tdb_close(struct tdb_context *tdb); 369 370 /** 371 * @brief Find the first entry in the database and return its key. 372 * 373 * The caller must free the returned data. 374 * 375 * @param[in] tdb The database to use. 376 * 377 * @return The first entry of the database, an empty TDB_DATA entry 378 * if the database is empty. 379 */ 115 380 TDB_DATA tdb_firstkey(struct tdb_context *tdb); 381 382 /** 383 * @brief Find the next entry in the database, returning its key. 384 * 385 * The caller must free the returned data. 386 * 387 * @param[in] tdb The database to use. 388 * 389 * @param[in] key The key from which you want the next key. 390 * 391 * @return The next entry of the current key, an empty TDB_DATA 392 * entry if there is no entry. 393 */ 116 394 TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key); 117 int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *); 118 int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *); 395 396 /** 397 * @brief Traverse the entire database. 398 * 399 * While travering the function fn(tdb, key, data, state) is called on each 400 * element. If fn is NULL then it is not called. A non-zero return value from 401 * fn() indicates that the traversal should stop. Traversal callbacks may not 402 * start transactions. 403 * 404 * @warning The data buffer given to the callback fn does NOT meet the alignment 405 * restrictions malloc gives you. 406 * 407 * @param[in] tdb The database to traverse. 408 * 409 * @param[in] fn The function to call on each entry. 410 * 411 * @param[in] private_data The private data which should be passed to the 412 * traversing function. 413 * 414 * @return The record count traversed, -1 on error. 415 */ 416 int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data); 417 418 /** 419 * @brief Traverse the entire database. 420 * 421 * While traversing the database the function fn(tdb, key, data, state) is 422 * called on each element, but marking the database read only during the 423 * traversal, so any write operations will fail. This allows tdb to use read 424 * locks, which increases the parallelism possible during the traversal. 425 * 426 * @param[in] tdb The database to traverse. 427 * 428 * @param[in] fn The function to call on each entry. 429 * 430 * @param[in] private_data The private data which should be passed to the 431 * traversing function. 432 * 433 * @return The record count traversed, -1 on error. 434 */ 435 int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data); 436 437 /** 438 * @brief Check if an entry in the database exists. 439 * 440 * @note 1 is returned if the key is found and 0 is returned if not found this 441 * doesn't match the conventions in the rest of this module, but is compatible 442 * with gdbm. 443 * 444 * @param[in] tdb The database to check if the entry exists. 445 * 446 * @param[in] key The key to check if the entry exists. 447 * 448 * @return 1 if the key is found, 0 if not. 449 */ 119 450 int tdb_exists(struct tdb_context *tdb, TDB_DATA key); 451 452 /** 453 * @brief Lock entire database with a write lock. 454 * 455 * @param[in] tdb The database to lock. 456 * 457 * @return 0 on success, -1 on error with error code set. 458 * 459 * @see tdb_error() 460 * @see tdb_errorstr() 461 */ 120 462 int tdb_lockall(struct tdb_context *tdb); 463 464 /** 465 * @brief Lock entire database with a write lock. 466 * 467 * This is the non-blocking call. 468 * 469 * @param[in] tdb The database to lock. 470 * 471 * @return 0 on success, -1 on error with error code set. 472 * 473 * @see tdb_lockall() 474 * @see tdb_error() 475 * @see tdb_errorstr() 476 */ 121 477 int tdb_lockall_nonblock(struct tdb_context *tdb); 478 479 /** 480 * @brief Unlock entire database with write lock. 481 * 482 * @param[in] tdb The database to unlock. 483 * 484 * @return 0 on success, -1 on error with error code set. 485 * 486 * @see tdb_lockall() 487 * @see tdb_error() 488 * @see tdb_errorstr() 489 */ 122 490 int tdb_unlockall(struct tdb_context *tdb); 491 492 /** 493 * @brief Lock entire database with a read lock. 494 * 495 * @param[in] tdb The database to lock. 496 * 497 * @return 0 on success, -1 on error with error code set. 498 * 499 * @see tdb_error() 500 * @see tdb_errorstr() 501 */ 123 502 int tdb_lockall_read(struct tdb_context *tdb); 503 504 /** 505 * @brief Lock entire database with a read lock. 506 * 507 * This is the non-blocking call. 508 * 509 * @param[in] tdb The database to lock. 510 * 511 * @return 0 on success, -1 on error with error code set. 512 * 513 * @see tdb_lockall_read() 514 * @see tdb_error() 515 * @see tdb_errorstr() 516 */ 124 517 int tdb_lockall_read_nonblock(struct tdb_context *tdb); 518 519 /** 520 * @brief Unlock entire database with read lock. 521 * 522 * @param[in] tdb The database to unlock. 523 * 524 * @return 0 on success, -1 on error with error code set. 525 * 526 * @see tdb_lockall_read() 527 * @see tdb_error() 528 * @see tdb_errorstr() 529 */ 125 530 int tdb_unlockall_read(struct tdb_context *tdb); 531 532 /** 533 * @brief Lock entire database with write lock - mark only. 534 * 535 * @todo Add more details. 536 * 537 * @param[in] tdb The database to mark. 538 * 539 * @return 0 on success, -1 on error with error code set. 540 * 541 * @see tdb_error() 542 * @see tdb_errorstr() 543 */ 126 544 int tdb_lockall_mark(struct tdb_context *tdb); 545 546 /** 547 * @brief Lock entire database with write lock - unmark only. 548 * 549 * @todo Add more details. 550 * 551 * @param[in] tdb The database to mark. 552 * 553 * @return 0 on success, -1 on error with error code set. 554 * 555 * @see tdb_error() 556 * @see tdb_errorstr() 557 */ 127 558 int tdb_lockall_unmark(struct tdb_context *tdb); 559 560 /** 561 * @brief Get the name of the current tdb file. 562 * 563 * This is useful for external logging functions. 564 * 565 * @param[in] tdb The database to get the name from. 566 * 567 * @return The name of the database. 568 */ 128 569 const char *tdb_name(struct tdb_context *tdb); 570 571 /** 572 * @brief Get the underlying file descriptor being used by tdb. 573 * 574 * This is useful for external routines that want to check the device/inode 575 * of the fd. 576 * 577 * @param[in] tdb The database to get the fd from. 578 * 579 * @return The file descriptor or -1. 580 */ 129 581 int tdb_fd(struct tdb_context *tdb); 582 583 /** 584 * @brief Get the current logging function. 585 * 586 * This is useful for external tdb routines that wish to log tdb errors. 587 * 588 * @param[in] tdb The database to get the logging function from. 589 * 590 * @return The logging function of the database. 591 * 592 * @see tdb_get_logging_private() 593 */ 130 594 tdb_log_func tdb_log_fn(struct tdb_context *tdb); 595 596 /** 597 * @brief Get the private data of the logging function. 598 * 599 * @param[in] tdb The database to get the data from. 600 * 601 * @return The private data pointer of the logging function. 602 * 603 * @see tdb_log_fn() 604 */ 131 605 void *tdb_get_logging_private(struct tdb_context *tdb); 606 607 /** 608 * @brief Start a transaction. 609 * 610 * All operations after the transaction start can either be committed with 611 * tdb_transaction_commit() or cancelled with tdb_transaction_cancel(). 612 * 613 * If you call tdb_transaction_start() again on the same tdb context while a 614 * transaction is in progress, then the same transaction buffer is re-used. The 615 * number of tdb_transaction_{commit,cancel} operations must match the number 616 * of successful tdb_transaction_start() calls. 617 * 618 * Note that transactions are by default disk synchronous, and use a recover 619 * area in the database to automatically recover the database on the next open 620 * if the system crashes during a transaction. You can disable the synchronous 621 * transaction recovery setup using the TDB_NOSYNC flag, which will greatly 622 * speed up operations at the risk of corrupting your database if the system 623 * crashes. 624 * 625 * Operations made within a transaction are not visible to other users of the 626 * database until a successful commit. 627 * 628 * @param[in] tdb The database to start the transaction. 629 * 630 * @return 0 on success, -1 on error with error code set. 631 * 632 * @see tdb_error() 633 * @see tdb_errorstr() 634 */ 132 635 int tdb_transaction_start(struct tdb_context *tdb); 636 637 /** 638 * @brief Start a transaction, non-blocking. 639 * 640 * @param[in] tdb The database to start the transaction. 641 * 642 * @return 0 on success, -1 on error with error code set. 643 * 644 * @see tdb_error() 645 * @see tdb_errorstr() 646 * @see tdb_transaction_start() 647 */ 648 int tdb_transaction_start_nonblock(struct tdb_context *tdb); 649 650 /** 651 * @brief Prepare to commit a current transaction, for two-phase commits. 652 * 653 * Once prepared for commit, the only allowed calls are tdb_transaction_commit() 654 * or tdb_transaction_cancel(). Preparing allocates disk space for the pending 655 * updates, so a subsequent commit should succeed (barring any hardware 656 * failures). 657 * 658 * @param[in] tdb The database to prepare the commit. 659 * 660 * @return 0 on success, -1 on error with error code set. 661 * 662 * @see tdb_error() 663 * @see tdb_errorstr() 664 */ 133 665 int tdb_transaction_prepare_commit(struct tdb_context *tdb); 666 667 /** 668 * @brief Commit a current transaction. 669 * 670 * This updates the database and releases the current transaction locks. 671 * 672 * @param[in] tdb The database to commit the transaction. 673 * 674 * @return 0 on success, -1 on error with error code set. 675 * 676 * @see tdb_error() 677 * @see tdb_errorstr() 678 */ 134 679 int tdb_transaction_commit(struct tdb_context *tdb); 680 681 /** 682 * @brief Cancel a current transaction. 683 * 684 * This discards all write and lock operations that have been made since the 685 * transaction started. 686 * 687 * @param[in] tdb The tdb to cancel the transaction on. 688 * 689 * @return 0 on success, -1 on error with error code set. 690 * 691 * @see tdb_error() 692 * @see tdb_errorstr() 693 */ 135 694 int tdb_transaction_cancel(struct tdb_context *tdb); 136 int tdb_transaction_recover(struct tdb_context *tdb); 695 696 /** 697 * @brief Get the tdb sequence number. 698 * 699 * Only makes sense if the writers opened with TDB_SEQNUM set. Note that this 700 * sequence number will wrap quite quickly, so it should only be used for a 701 * 'has something changed' test, not for code that relies on the count of the 702 * number of changes made. If you want a counter then use a tdb record. 703 * 704 * The aim of this sequence number is to allow for a very lightweight test of a 705 * possible tdb change. 706 * 707 * @param[in] tdb The database to get the sequence number from. 708 * 709 * @return The sequence number or 0. 710 * 711 * @see tdb_open() 712 * @see tdb_enable_seqnum() 713 */ 137 714 int tdb_get_seqnum(struct tdb_context *tdb); 715 716 /** 717 * @brief Get the hash size. 718 * 719 * @param[in] tdb The database to get the hash size from. 720 * 721 * @return The hash size. 722 */ 138 723 int tdb_hash_size(struct tdb_context *tdb); 724 725 /** 726 * @brief Get the map size. 727 * 728 * @param[in] tdb The database to get the map size from. 729 * 730 * @return The map size. 731 */ 139 732 size_t tdb_map_size(struct tdb_context *tdb); 733 734 /** 735 * @brief Get the tdb flags set during open. 736 * 737 * @param[in] tdb The database to get the flags form. 738 * 739 * @return The flags set to on the database. 740 */ 140 741 int tdb_get_flags(struct tdb_context *tdb); 742 743 /** 744 * @brief Add flags to the database. 745 * 746 * @param[in] tdb The database to add the flags. 747 * 748 * @param[in] flag The tdb flags to add. 749 */ 141 750 void tdb_add_flags(struct tdb_context *tdb, unsigned flag); 751 752 /** 753 * @brief Remove flags from the database. 754 * 755 * @param[in] tdb The database to remove the flags. 756 * 757 * @param[in] flag The tdb flags to remove. 758 */ 142 759 void tdb_remove_flags(struct tdb_context *tdb, unsigned flag); 760 761 /** 762 * @brief Enable sequence number handling on an open tdb. 763 * 764 * @param[in] tdb The database to enable sequence number handling. 765 * 766 * @see tdb_get_seqnum() 767 */ 143 768 void tdb_enable_seqnum(struct tdb_context *tdb); 769 770 /** 771 * @brief Increment the tdb sequence number. 772 * 773 * This only works if the tdb has been opened using the TDB_SEQNUM flag or 774 * enabled useing tdb_enable_seqnum(). 775 * 776 * @param[in] tdb The database to increment the sequence number. 777 * 778 * @see tdb_enable_seqnum() 779 * @see tdb_get_seqnum() 780 */ 144 781 void tdb_increment_seqnum_nonblock(struct tdb_context *tdb); 782 783 /** 784 * @brief Create a hash of the key. 785 * 786 * @param[in] key The key to hash 787 * 788 * @return The hash. 789 */ 790 unsigned int tdb_jenkins_hash(TDB_DATA *key); 791 792 /** 793 * @brief Check the consistency of the database. 794 * 795 * This check the consistency of the database calling back the check function 796 * (if non-NULL) on each record. If some consistency check fails, or the 797 * supplied check function returns -1, tdb_check returns -1, otherwise 0. 798 * 799 * @note The logging function (if set) will be called with additional 800 * information on the corruption found. 801 * 802 * @param[in] tdb The database to check. 803 * 804 * @param[in] check The check function to use. 805 * 806 * @param[in] private_data the private data to pass to the check function. 807 * 808 * @return 0 on success, -1 on error with error code set. 809 * 810 * @see tdb_error() 811 * @see tdb_errorstr() 812 */ 145 813 int tdb_check(struct tdb_context *tdb, 146 814 int (*check) (TDB_DATA key, TDB_DATA data, void *private_data), 147 815 void *private_data); 816 817 /* @} ******************************************************************/ 148 818 149 819 /* Low level locking functions: use with care */ … … 167 837 int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries); 168 838 int tdb_freelist_size(struct tdb_context *tdb); 839 char *tdb_summary(struct tdb_context *tdb); 169 840 170 841 extern TDB_DATA tdb_null; -
trunk/server/lib/tdb/libtdb.m4
r414 r745 14 14 fi 15 15 TDB_OBJ="common/tdb.o common/dump.o common/transaction.o common/error.o common/traverse.o" 16 TDB_OBJ="$TDB_OBJ common/freelist.o common/freelistcheck.o common/io.o common/lock.o common/open.o common/check.o "16 TDB_OBJ="$TDB_OBJ common/freelist.o common/freelistcheck.o common/io.o common/lock.o common/open.o common/check.o common/hash.o common/summary.o" 17 17 AC_SUBST(TDB_OBJ) 18 18 AC_SUBST(LIBREPLACEOBJ) … … 20 20 TDB_LIBS="" 21 21 AC_SUBST(TDB_LIBS) 22 23 TDB_DEPS="" 24 if test x$libreplace_cv_HAVE_FDATASYNC_IN_LIBRT = xyes ; then 25 TDB_DEPS="$TDB_DEPS -lrt" 26 fi 27 AC_SUBST(TDB_DEPS) 22 28 23 29 TDB_CFLAGS="-I$tdbdir/include" -
trunk/server/lib/tdb/manpages/tdbbackup.8.xml
r414 r745 1 1 <?xml version="1.0" encoding="iso-8859-1"?> 2 <!DOCTYPE refentry PUBLIC "-// Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">2 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> 3 3 <refentry id="tdbbackup.8"> 4 4 … … 8 8 <refmiscinfo class="source">Samba</refmiscinfo> 9 9 <refmiscinfo class="manual">System Administration tools</refmiscinfo> 10 <refmiscinfo class="version">3. 5</refmiscinfo>10 <refmiscinfo class="version">3.6</refmiscinfo> 11 11 </refmeta> 12 12 -
trunk/server/lib/tdb/manpages/tdbdump.8.xml
r414 r745 1 1 <?xml version="1.0" encoding="iso-8859-1"?> 2 <!DOCTYPE refentry PUBLIC "-// Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">2 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> 3 3 <refentry id="tdbdump.8"> 4 4 … … 8 8 <refmiscinfo class="source">Samba</refmiscinfo> 9 9 <refmiscinfo class="manual">System Administration tools</refmiscinfo> 10 <refmiscinfo class="version">3. 5</refmiscinfo>10 <refmiscinfo class="version">3.6</refmiscinfo> 11 11 </refmeta> 12 12 -
trunk/server/lib/tdb/manpages/tdbtool.8.xml
r414 r745 1 1 <?xml version="1.0" encoding="iso-8859-1"?> 2 <!DOCTYPE refentry PUBLIC "-// Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">2 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> 3 3 <refentry id="tdbtool.8"> 4 4 … … 8 8 <refmiscinfo class="source">Samba</refmiscinfo> 9 9 <refmiscinfo class="manual">System Administration tools</refmiscinfo> 10 <refmiscinfo class="version">3. 5</refmiscinfo>10 <refmiscinfo class="version">3.6</refmiscinfo> 11 11 </refmeta> 12 12 -
trunk/server/lib/tdb/pytdb.c
r414 r745 10 10 ** library. This does NOT imply that all of Samba is released 11 11 ** under the LGPL 12 12 13 13 This library is free software; you can redistribute it and/or 14 14 modify it under the terms of the GNU Lesser General Public … … 25 25 */ 26 26 27 #include <Python.h> 27 28 #include "replace.h" 28 29 #include "system/filesys.h" 29 30 30 #include <Python.h>31 31 #ifndef Py_RETURN_NONE 32 32 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None … … 42 42 } PyTdbObject; 43 43 44 PyAPI_DATA(PyTypeObject)PyTdb;44 staticforward PyTypeObject PyTdb; 45 45 46 46 static void PyErr_SetTDBError(TDB_CONTEXT *tdb) … … 78 78 static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs) 79 79 { 80 char *name ;80 char *name = NULL; 81 81 int hash_size = 0, tdb_flags = TDB_DEFAULT, flags = O_RDWR, mode = 0600; 82 82 TDB_CONTEXT *ctx; … … 84 84 const char *kwnames[] = { "name", "hash_size", "tdb_flags", "flags", "mode", NULL }; 85 85 86 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iiii", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode)) 87 return NULL; 86 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiii", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode)) 87 return NULL; 88 89 if (name == NULL) { 90 tdb_flags |= TDB_INTERNAL; 91 } 88 92 89 93 ctx = tdb_open(name, hash_size, tdb_flags, flags, mode); … … 94 98 95 99 ret = PyObject_New(PyTdbObject, &PyTdb); 100 if (!ret) { 101 tdb_close(ctx); 102 return NULL; 103 } 104 96 105 ret->ctx = ctx; 97 106 ret->closed = false; … … 113 122 } 114 123 115 static PyObject *obj_transaction_ recover(PyTdbObject *self)116 { 117 int ret = tdb_transaction_ recover(self->ctx);124 static PyObject *obj_transaction_prepare_commit(PyTdbObject *self) 125 { 126 int ret = tdb_transaction_prepare_commit(self->ctx); 118 127 PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); 119 128 Py_RETURN_NONE; … … 267 276 } 268 277 278 static PyObject *obj_add_flags(PyTdbObject *self, PyObject *args) 279 { 280 unsigned flags; 281 282 if (!PyArg_ParseTuple(args, "I", &flags)) 283 return NULL; 284 285 tdb_add_flags(self->ctx, flags); 286 Py_RETURN_NONE; 287 } 288 289 static PyObject *obj_remove_flags(PyTdbObject *self, PyObject *args) 290 { 291 unsigned flags; 292 293 if (!PyArg_ParseTuple(args, "I", &flags)) 294 return NULL; 295 296 tdb_remove_flags(self->ctx, flags); 297 Py_RETURN_NONE; 298 } 269 299 270 300 typedef struct { … … 306 336 307 337 ret = PyObject_New(PyTdbIteratorObject, &PyTdbIterator); 338 if (!ret) 339 return NULL; 308 340 ret->current = tdb_firstkey(self->ctx); 309 341 ret->iteratee = self; … … 316 348 int ret = tdb_wipe_all(self->ctx); 317 349 PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); 350 Py_RETURN_NONE; 351 } 352 353 static PyObject *obj_repack(PyTdbObject *self) 354 { 355 int ret = tdb_repack(self->ctx); 356 PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); 357 Py_RETURN_NONE; 358 } 359 360 static PyObject *obj_enable_seqnum(PyTdbObject *self) 361 { 362 tdb_enable_seqnum(self->ctx); 363 Py_RETURN_NONE; 364 } 365 366 static PyObject *obj_increment_seqnum_nonblock(PyTdbObject *self) 367 { 368 tdb_increment_seqnum_nonblock(self->ctx); 318 369 Py_RETURN_NONE; 319 370 } … … 326 377 "S.transaction_commit() -> None\n" 327 378 "Commit the currently active transaction." }, 328 { "transaction_ recover", (PyCFunction)obj_transaction_recover, METH_NOARGS,329 "S.transaction_ recover() -> None\n"330 " Recover the currently active transaction." },379 { "transaction_prepare_commit", (PyCFunction)obj_transaction_prepare_commit, METH_NOARGS, 380 "S.transaction_prepare_commit() -> None\n" 381 "Prepare to commit the currently active transaction" }, 331 382 { "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS, 332 383 "S.transaction_start() -> None\n" … … 352 403 { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None" 353 404 "Store data." }, 405 { "add_flags", (PyCFunction)obj_add_flags, METH_VARARGS, "S.add_flags(flags) -> None" }, 406 { "remove_flags", (PyCFunction)obj_remove_flags, METH_VARARGS, "S.remove_flags(flags) -> None" }, 354 407 { "iterkeys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" }, 355 408 { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n" 356 409 "Wipe the entire database." }, 410 { "repack", (PyCFunction)obj_repack, METH_NOARGS, "S.repack() -> None\n" 411 "Repack the entire database." }, 412 { "enable_seqnum", (PyCFunction)obj_enable_seqnum, METH_NOARGS, 413 "S.enable_seqnum() -> None" }, 414 { "increment_seqnum_nonblock", (PyCFunction)obj_increment_seqnum_nonblock, METH_NOARGS, 415 "S.increment_seqnum_nonblock() -> None" }, 357 416 { NULL } 358 417 }; … … 376 435 } 377 436 437 static PyObject *obj_get_freelist_size(PyTdbObject *self, void *closure) 438 { 439 return PyInt_FromLong(tdb_freelist_size(self->ctx)); 440 } 441 378 442 static PyObject *obj_get_flags(PyTdbObject *self, void *closure) 379 443 { … … 385 449 return PyString_FromString(tdb_name(self->ctx)); 386 450 } 451 452 static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure) 453 { 454 return PyInt_FromLong(tdb_get_seqnum(self->ctx)); 455 } 456 387 457 388 458 static PyGetSetDef tdb_object_getsetters[] = { 389 459 { (char *)"hash_size", (getter)obj_get_hash_size, NULL, NULL }, 390 460 { (char *)"map_size", (getter)obj_get_map_size, NULL, NULL }, 461 { (char *)"freelist_size", (getter)obj_get_freelist_size, NULL, NULL }, 391 462 { (char *)"flags", (getter)obj_get_flags, NULL, NULL }, 392 463 { (char *)"max_dead", NULL, (setter)obj_set_max_dead, NULL }, 393 464 { (char *)"filename", (getter)obj_get_filename, NULL, (char *)"The filename of this TDB file."}, 465 { (char *)"seqnum", (getter)obj_get_seqnum, NULL, NULL }, 394 466 { NULL } 395 467 }; … … 397 469 static PyObject *tdb_object_repr(PyTdbObject *self) 398 470 { 399 return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx)); 471 if (tdb_get_flags(self->ctx) & TDB_INTERNAL) { 472 return PyString_FromString("Tdb(<internal>)"); 473 } else { 474 return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx)); 475 } 400 476 } 401 477 … … 404 480 if (!self->closed) 405 481 tdb_close(self->ctx); 406 PyObject_Del(self);482 self->ob_type->tp_free(self); 407 483 } 408 484 … … 463 539 .mp_ass_subscript = (objobjargproc)obj_setitem, 464 540 }; 465 PyTypeObject PyTdb = {541 static PyTypeObject PyTdb = { 466 542 .tp_name = "Tdb", 467 543 .tp_basicsize = sizeof(PyTdbObject), … … 483 559 }; 484 560 561 void inittdb(void); 485 562 void inittdb(void) 486 563 { … … 508 585 PyModule_AddObject(m, "CONVERT", PyInt_FromLong(TDB_CONVERT)); 509 586 PyModule_AddObject(m, "BIGENDIAN", PyInt_FromLong(TDB_BIGENDIAN)); 587 PyModule_AddObject(m, "NOSYNC", PyInt_FromLong(TDB_NOSYNC)); 588 PyModule_AddObject(m, "SEQNUM", PyInt_FromLong(TDB_SEQNUM)); 589 PyModule_AddObject(m, "VOLATILE", PyInt_FromLong(TDB_VOLATILE)); 590 PyModule_AddObject(m, "ALLOW_NESTING", PyInt_FromLong(TDB_ALLOW_NESTING)); 591 PyModule_AddObject(m, "DISALLOW_NESTING", PyInt_FromLong(TDB_DISALLOW_NESTING)); 592 PyModule_AddObject(m, "INCOMPATIBLE_HASH", PyInt_FromLong(TDB_INCOMPATIBLE_HASH)); 593 510 594 PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText")); 595 596 PyModule_AddObject(m, "__version__", PyString_FromString(PACKAGE_VERSION)); 511 597 512 598 Py_INCREF(&PyTdb); -
trunk/server/lib/tdb/python/tdbdump.py
r414 r745 1 #!/usr/bin/ python1 #!/usr/bin/env python 2 2 # Trivial reimplementation of tdbdump in Python 3 3 -
trunk/server/lib/tdb/python/tests/simple.py
r414 r745 1 #!/usr/bin/ python1 #!/usr/bin/env python 2 2 # Some simple tests for the Python bindings for TDB 3 3 # Note that this tests the interface of the Python bindings … … 13 13 14 14 class OpenTdbTests(TestCase): 15 15 16 def test_nonexistant_read(self): 16 self.assertRaises(IOError, tdb.Tdb, "/some/nonexistant/file", 0, tdb.DEFAULT, os.O_RDWR) 17 self.assertRaises(IOError, tdb.Tdb, "/some/nonexistant/file", 0, 18 tdb.DEFAULT, os.O_RDWR) 17 19 18 20 class CloseTdbTests(TestCase): 21 19 22 def test_double_close(self): 20 self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR) 23 self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, 24 os.O_CREAT|os.O_RDWR) 21 25 self.assertNotEqual(None, self.tdb) 22 26 … … 26 30 27 31 32 class InternalTdbTests(TestCase): 33 34 def test_repr(self): 35 self.tdb = tdb.Tdb() 36 37 # repr used to crash on internal db 38 self.assertEquals(repr(self.tdb), "Tdb(<internal>)") 39 40 28 41 class SimpleTdbTests(TestCase): 42 29 43 def setUp(self): 30 44 super(SimpleTdbTests, self).setUp() 31 self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR) 45 self.tdb = tdb.Tdb(tempfile.mkstemp()[1], 0, tdb.DEFAULT, 46 os.O_CREAT|os.O_RDWR) 32 47 self.assertNotEqual(None, self.tdb) 33 48 … … 82 97 self.tdb.map_size 83 98 99 def test_freelist_size(self): 100 self.tdb.freelist_size 101 84 102 def test_name(self): 85 103 self.tdb.filename … … 104 122 self.assertEquals("1", self.tdb["bloe"]) 105 123 106 def test_ iterator(self):124 def test_transaction_prepare_commit(self): 107 125 self.tdb["bloe"] = "2" 108 self.tdb["bla"] = "hoi" 109 i = iter(self.tdb) 110 self.assertEquals(set(["bloe", "bla"]), set([i.next(), i.next()])) 126 self.tdb.transaction_start() 127 self.tdb["bloe"] = "1" 128 self.tdb.transaction_prepare_commit() 129 self.tdb.transaction_commit() 130 self.assertEquals("1", self.tdb["bloe"]) 111 131 112 132 def test_iterkeys(self): … … 123 143 self.assertEquals(0, len(list(self.tdb))) 124 144 145 def test_repack(self): 146 self.tdb["foo"] = "abc" 147 self.tdb["bar"] = "def" 148 del self.tdb["foo"] 149 self.tdb.repack() 150 151 def test_seqnum(self): 152 self.tdb.enable_seqnum() 153 seq1 = self.tdb.seqnum 154 self.tdb.increment_seqnum_nonblock() 155 seq2 = self.tdb.seqnum 156 self.assertEquals(seq2-seq1, 1) 157 125 158 def test_len(self): 126 159 self.assertEquals(0, len(list(self.tdb))) … … 128 161 self.assertEquals(1, len(list(self.tdb))) 129 162 163 def test_add_flags(self): 164 self.tdb.add_flags(tdb.NOMMAP) 165 self.tdb.remove_flags(tdb.NOMMAP) 166 167 168 class VersionTests(TestCase): 169 170 def test_present(self): 171 self.assertTrue(isinstance(tdb.__version__, str)) 172 130 173 131 174 if __name__ == '__main__': -
trunk/server/lib/tdb/tdb.pc.in
r414 r745 7 7 Description: A trivial database 8 8 Version: @PACKAGE_VERSION@ 9 Libs: -L${libdir} -ltdb9 Libs: @LIB_RPATH@ -L${libdir} -ltdb 10 10 Cflags: -I${includedir} 11 11 URL: http://tdb.samba.org/ -
trunk/server/lib/tdb/tools/tdbbackup.c
r414 r745 153 153 } 154 154 155 if (tdb_transaction_start(tdb_new) != 0) { 156 printf("Failed to start transaction on new tdb\n"); 155 /* lock the backup tdb so that nobody else can change it */ 156 if (tdb_lockall(tdb_new) != 0) { 157 printf("Failed to lock backup tdb\n"); 157 158 tdb_close(tdb); 158 159 tdb_close(tdb_new); … … 178 179 tdb_close(tdb); 179 180 180 if (tdb_transaction_commit(tdb_new) != 0) { 181 fprintf(stderr, "Failed to commit new tdb\n"); 182 tdb_close(tdb_new); 183 unlink(tmp_name); 184 free(tmp_name); 185 return 1; 181 /* copy done, unlock the backup tdb */ 182 tdb_unlockall(tdb_new); 183 184 #ifdef HAVE_FDATASYNC 185 if (fdatasync(tdb_fd(tdb_new)) != 0) { 186 #else 187 if (fsync(tdb_fd(tdb_new)) != 0) { 188 #endif 189 /* not fatal */ 190 fprintf(stderr, "failed to fsync backup file\n"); 186 191 } 187 192 -
trunk/server/lib/tdb/tools/tdbtest.c
r414 r745 216 216 } 217 217 218 static char *test_path(const char *filename) 219 { 220 const char *prefix = getenv("TEST_DATA_PREFIX"); 221 222 if (prefix) { 223 char *path = NULL; 224 int ret; 225 226 ret = asprintf(&path, "%s/%s", prefix, filename); 227 if (ret == -1) { 228 return NULL; 229 } 230 return path; 231 } 232 233 return strdup(filename); 234 } 235 218 236 int main(int argc, const char *argv[]) 219 237 { … … 221 239 int loops = 10000; 222 240 int num_entries; 223 char test_gdbm[] = "test.gdbm"; 224 225 unlink("test.gdbm"); 226 227 db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST, 241 char test_gdbm[1] = "test.gdbm"; 242 char *test_tdb; 243 244 test_gdbm[0] = test_path("test.gdbm"); 245 test_tdb = test_path("test.tdb"); 246 247 unlink(test_gdbm[0]); 248 249 db = tdb_open(test_tdb, 0, TDB_CLEAR_IF_FIRST, 228 250 O_RDWR | O_CREAT | O_TRUNC, 0600); 229 251 gdbm = gdbm_open(test_gdbm, 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, … … 262 284 gdbm_close(gdbm); 263 285 286 free(test_gdbm[0]); 287 free(test_tdb); 288 264 289 return 0; 265 290 } -
trunk/server/lib/tdb/tools/tdbtool.c
r456 r745 418 418 static void info_tdb(void) 419 419 { 420 int count;421 total_bytes = 0; 422 if ( (count = tdb_traverse(tdb, traverse_fn, NULL)) == -1)420 char *summary = tdb_summary(tdb); 421 422 if (!summary) { 423 423 printf("Error = %s\n", tdb_errorstr(tdb)); 424 else 425 printf("%d records totalling %d bytes\n", count, total_bytes); 424 } else { 425 printf("%s", summary); 426 free(summary); 427 } 426 428 } 427 429 … … 574 576 if (cmdname) { 575 577 #endif 576 577 578 if (cmdname && strlen(cmdname) == 0) { 578 579 mycmd = CMD_NEXT; -
trunk/server/lib/tdb/tools/tdbtorture.c
r414 r745 31 31 static int error_count; 32 32 static int always_transaction = 0; 33 static int hash_size = 2; 34 static int loopnum; 35 static int count_pipe; 36 static struct tdb_logging_context log_ctx; 33 37 34 38 #ifdef PRINTF_ATTRIBUTE … … 49 53 fflush(stdout); 50 54 #if 0 51 {55 if (level != TDB_DEBUG_TRACE) { 52 56 char *ptr; 57 signal(SIGUSR1, SIG_IGN); 53 58 asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid()); 54 59 system(ptr); … … 212 217 static void usage(void) 213 218 { 214 printf("Usage: tdbtorture [-t] [- n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n");219 printf("Usage: tdbtorture [-t] [-k] [-n NUM_PROCS] [-l NUM_LOOPS] [-s SEED] [-H HASH_SIZE]\n"); 215 220 exit(0); 216 221 } 217 222 218 int main(int argc, char * const *argv) 219 { 220 int i, seed = -1; 221 int num_procs = 3; 222 int num_loops = 5000; 223 int hash_size = 2; 224 int c; 225 extern char *optarg; 226 pid_t *pids; 227 228 struct tdb_logging_context log_ctx; 229 log_ctx.log_fn = tdb_log; 230 231 while ((c = getopt(argc, argv, "n:l:s:H:th")) != -1) { 232 switch (c) { 233 case 'n': 234 num_procs = strtol(optarg, NULL, 0); 235 break; 236 case 'l': 237 num_loops = strtol(optarg, NULL, 0); 238 break; 239 case 'H': 240 hash_size = strtol(optarg, NULL, 0); 241 break; 242 case 's': 243 seed = strtol(optarg, NULL, 0); 244 break; 245 case 't': 246 always_transaction = 1; 247 break; 248 default: 249 usage(); 250 } 251 } 252 253 unlink("torture.tdb"); 254 255 pids = (pid_t *)calloc(sizeof(pid_t), num_procs); 256 pids[0] = getpid(); 257 258 for (i=0;i<num_procs-1;i++) { 259 if ((pids[i+1]=fork()) == 0) break; 260 } 261 262 db = tdb_open_ex("torture.tdb", hash_size, TDB_CLEAR_IF_FIRST, 223 static void send_count_and_suicide(int sig) 224 { 225 /* This ensures our successor can continue where we left off. */ 226 write(count_pipe, &loopnum, sizeof(loopnum)); 227 /* This gives a unique signature. */ 228 kill(getpid(), SIGUSR2); 229 } 230 231 static int run_child(const char *filename, int i, int seed, unsigned num_loops, unsigned start) 232 { 233 db = tdb_open_ex(filename, hash_size, TDB_DEFAULT, 263 234 O_RDWR | O_CREAT, 0600, &log_ctx, NULL); 264 235 if (!db) { … … 266 237 } 267 238 268 if (seed == -1) {269 seed = (getpid() + time(NULL)) & 0x7FFFFFFF;270 }271 272 if (i == 0) {273 printf("testing with %d processes, %d loops, %d hash_size, seed=%d%s\n",274 num_procs, num_loops, hash_size, seed, always_transaction ? " (all within transactions)" : "");275 }276 277 239 srand(seed + i); 278 240 srandom(seed + i); 279 241 280 for (i=0;i<num_loops && error_count == 0;i++) { 242 /* Set global, then we're ready to handle being killed. */ 243 loopnum = start; 244 signal(SIGUSR1, send_count_and_suicide); 245 246 for (;loopnum<num_loops && error_count == 0;loopnum++) { 281 247 addrec_db(); 282 248 } … … 302 268 tdb_close(db); 303 269 304 if (getpid() != pids[0]) { 305 return error_count; 306 } 307 308 for (i=1;i<num_procs;i++) { 270 return (error_count < 100 ? error_count : 100); 271 } 272 273 static char *test_path(const char *filename) 274 { 275 const char *prefix = getenv("TEST_DATA_PREFIX"); 276 277 if (prefix) { 278 char *path = NULL; 279 int ret; 280 281 ret = asprintf(&path, "%s/%s", prefix, filename); 282 if (ret == -1) { 283 return NULL; 284 } 285 return path; 286 } 287 288 return strdup(filename); 289 } 290 291 int main(int argc, char * const *argv) 292 { 293 int i, seed = -1; 294 int num_loops = 5000; 295 int num_procs = 3; 296 int c, pfds[2]; 297 extern char *optarg; 298 pid_t *pids; 299 int kill_random = 0; 300 int *done; 301 char *test_tdb; 302 303 log_ctx.log_fn = tdb_log; 304 305 while ((c = getopt(argc, argv, "n:l:s:H:thk")) != -1) { 306 switch (c) { 307 case 'n': 308 num_procs = strtol(optarg, NULL, 0); 309 break; 310 case 'l': 311 num_loops = strtol(optarg, NULL, 0); 312 break; 313 case 'H': 314 hash_size = strtol(optarg, NULL, 0); 315 break; 316 case 's': 317 seed = strtol(optarg, NULL, 0); 318 break; 319 case 't': 320 always_transaction = 1; 321 break; 322 case 'k': 323 kill_random = 1; 324 break; 325 default: 326 usage(); 327 } 328 } 329 330 test_tdb = test_path("torture.tdb"); 331 332 unlink(test_tdb); 333 334 if (seed == -1) { 335 seed = (getpid() + time(NULL)) & 0x7FFFFFFF; 336 } 337 338 if (num_procs == 1 && !kill_random) { 339 /* Don't fork for this case, makes debugging easier. */ 340 error_count = run_child(test_tdb, 0, seed, num_loops, 0); 341 goto done; 342 } 343 344 pids = (pid_t *)calloc(sizeof(pid_t), num_procs); 345 done = (int *)calloc(sizeof(int), num_procs); 346 347 if (pipe(pfds) != 0) { 348 perror("Creating pipe"); 349 exit(1); 350 } 351 count_pipe = pfds[1]; 352 353 for (i=0;i<num_procs;i++) { 354 if ((pids[i]=fork()) == 0) { 355 close(pfds[0]); 356 if (i == 0) { 357 printf("Testing with %d processes, %d loops, %d hash_size, seed=%d%s\n", 358 num_procs, num_loops, hash_size, seed, always_transaction ? " (all within transactions)" : ""); 359 } 360 exit(run_child(test_tdb, i, seed, num_loops, 0)); 361 } 362 } 363 364 while (num_procs) { 309 365 int status, j; 310 366 pid_t pid; 367 311 368 if (error_count != 0) { 312 369 /* try and stop the test on any failure */ 313 for (j= 1;j<num_procs;j++) {370 for (j=0;j<num_procs;j++) { 314 371 if (pids[j] != 0) { 315 372 kill(pids[j], SIGTERM); … … 317 374 } 318 375 } 319 pid = waitpid(-1, &status, 0); 376 377 pid = waitpid(-1, &status, kill_random ? WNOHANG : 0); 378 if (pid == 0) { 379 struct timeval tv; 380 381 /* Sleep for 1/10 second. */ 382 tv.tv_sec = 0; 383 tv.tv_usec = 100000; 384 select(0, NULL, NULL, NULL, &tv); 385 386 /* Kill someone. */ 387 kill(pids[random() % num_procs], SIGUSR1); 388 continue; 389 } 390 320 391 if (pid == -1) { 321 392 perror("failed to wait for child\n"); 322 393 exit(1); 323 394 } 324 for (j=1;j<num_procs;j++) { 395 396 for (j=0;j<num_procs;j++) { 325 397 if (pids[j] == pid) break; 326 398 } … … 329 401 exit(1); 330 402 } 331 if (WEXITSTATUS(status) != 0) { 332 printf("child %d exited with status %d\n", 333 (int)pid, WEXITSTATUS(status)); 403 if (WIFSIGNALED(status)) { 404 if (WTERMSIG(status) == SIGUSR2 405 || WTERMSIG(status) == SIGUSR1) { 406 /* SIGUSR2 means they wrote to pipe. */ 407 if (WTERMSIG(status) == SIGUSR2) { 408 read(pfds[0], &done[j], 409 sizeof(done[j])); 410 } 411 pids[j] = fork(); 412 if (pids[j] == 0) 413 exit(run_child(test_tdb, j, seed, 414 num_loops, done[j])); 415 printf("Restarting child %i for %u-%u\n", 416 j, done[j], num_loops); 417 continue; 418 } 419 printf("child %d exited with signal %d\n", 420 (int)pid, WTERMSIG(status)); 334 421 error_count++; 335 } 336 pids[j] = 0; 422 } else { 423 if (WEXITSTATUS(status) != 0) { 424 printf("child %d exited with status %d\n", 425 (int)pid, WEXITSTATUS(status)); 426 error_count++; 427 } 428 } 429 memmove(&pids[j], &pids[j+1], 430 (num_procs - j - 1)*sizeof(pids[0])); 431 num_procs--; 337 432 } 338 433 339 434 free(pids); 340 435 436 done: 341 437 if (error_count == 0) { 438 db = tdb_open_ex(test_tdb, hash_size, TDB_DEFAULT, 439 O_RDWR, 0, &log_ctx, NULL); 440 if (!db) { 441 fatal("db open failed"); 442 } 443 if (tdb_check(db, NULL, NULL) == -1) { 444 printf("db check failed"); 445 exit(1); 446 } 447 tdb_close(db); 342 448 printf("OK\n"); 343 449 } 344 450 451 free(test_tdb); 345 452 return error_count; 346 453 }
Note:
See TracChangeset
for help on using the changeset viewer.