Ignore:
Timestamp:
Nov 27, 2012, 4:43:17 PM (13 years ago)
Author:
Silvan Scherrer
Message:

Samba Server: updated trunk to 3.6.0

Location:
trunk/server
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/server

  • trunk/server/lib/tdb/common/transaction.c

    r657 r745  
    99     ** library. This does NOT imply that all of Samba is released
    1010     ** under the LGPL
    11    
     11
    1212   This library is free software; you can redistribute it and/or
    1313   modify it under the terms of the GNU Lesser General Public
     
    6060    existing transaction record. If the inner transaction is cancelled
    6161    then a subsequent commit will fail
    62  
     62
    6363  - keep a mirrored copy of the tdb hash chain heads to allow for the
    6464    fast hash heads scan on traverse, updating the mirrored copy in
     
    7777
    7878  - check for a valid recovery record on open of the tdb, while the
    79     global lock is held. Automatically recover from the transaction
     79    open lock is held. Automatically recover from the transaction
    8080    recovery area if needed, then continue with the open as
    8181    usual. This allows for smooth crash recovery with no administrator
     
    136136        tdb_off_t magic_offset;
    137137
    138         /* set when the GLOBAL_LOCK has been taken */
    139         bool global_lock_taken;
    140 
    141138        /* old file size before transaction */
    142139        tdb_len_t old_map_size;
    143140
    144         /* we should re-pack on commit */
    145         bool need_repack;
     141        /* did we expand in this transaction */
     142        bool expanded;
    146143};
    147144
     
    189186                }
    190187        }
    191        
     188
    192189        /* now copy it out of this block */
    193190        memcpy(buf, tdb->transaction->blocks[blk] + (off % tdb->transaction->block_size), len);
     
    296293                }
    297294        }
    298        
     295
    299296        /* overwrite part of an existing block */
    300297        if (buf == NULL) {
     
    407404        }
    408405
    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
    420408        return 0;
    421409}
     
    427415        transaction_oob,
    428416        transaction_expand_file,
    429         transaction_brlock
    430417};
    431418
     
    435422  transaction is allowed to be pending per tdb_context
    436423*/
    437 int tdb_transaction_start(struct tdb_context *tdb)
     424static int _tdb_transaction_start(struct tdb_context *tdb,
     425                                  enum tdb_lock_flags lockflags)
    438426{
    439427        /* some sanity checks */
     
    456444        }
    457445
    458         if (tdb->num_locks != 0 || tdb->global_lock.count) {
     446        if (tdb_have_extra_locks(tdb)) {
    459447                /* the caller must not have any locks when starting a
    460448                   transaction as otherwise we'll be screwed by lack
     
    487475           discussed with Volker, there are a number of ways we could
    488476           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) {
    490478                SAFE_FREE(tdb->transaction->blocks);
    491479                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
    495486        /* get a read lock from the freelist to the end of file. This
    496487           is upgraded to a write lock during the commit */
    497488#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) {
    499490                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;
    502492        }
    503493#endif
     
    531521        tdb_trace(tdb, "tdb_transaction_start");
    532522        return 0;
    533        
     523
    534524fail:
    535525#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);
    537527#endif
    538         tdb_transaction_unlock(tdb);
     528fail_allrecord_lock:
     529        tdb_transaction_unlock(tdb, F_WRLCK);
    539530        SAFE_FREE(tdb->transaction->blocks);
    540531        SAFE_FREE(tdb->transaction->hash_heads);
     
    543534}
    544535
     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}
    545545
    546546/*
     
    553553        }
    554554
     555#ifdef HAVE_FDATASYNC
     556        if (fdatasync(tdb->fd) != 0) {
     557#else
    555558        if (fsync(tdb->fd) != 0) {
     559#endif
    556560                tdb->ecode = TDB_ERR_IO;
    557561                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
     
    574578
    575579
    576 int _tdb_transaction_cancel(struct tdb_context *tdb)
     580static int _tdb_transaction_cancel(struct tdb_context *tdb)
    577581{       
    578582        int i, ret = 0;
     
    601605        if (tdb->transaction->magic_offset) {
    602606                const struct tdb_methods *methods = tdb->transaction->io_methods;
    603                 uint32_t zero = 0;
     607                const uint32_t invalid = TDB_RECOVERY_INVALID_MAGIC;
    604608
    605609                /* 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 ||
    607611                transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) {
    608612                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_cancel: failed to remove recovery magic\n"));
     
    611615        }
    612616
    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);
    634619
    635620        /* restore the normal io methods */
    636621        tdb->methods = tdb->transaction->io_methods;
    637622
    638 #ifndef __OS2__ // YD the transation lock is an exclusive lock for us, it is enough
    639         tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);
    640 #endif
    641         tdb_transaction_unlock(tdb);
    642623        SAFE_FREE(tdb->transaction->hash_heads);
    643624        SAFE_FREE(tdb->transaction);
    644        
     625
    645626        return ret;
    646627}
     
    649630  cancel the current transaction
    650631*/
    651 int tdb_transaction_cancel(struct tdb_context *tdb)
     632_PUBLIC_ int tdb_transaction_cancel(struct tdb_context *tdb)
    652633{
    653634        tdb_trace(tdb, "tdb_transaction_cancel");
     
    682663}
    683664
     665int 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
    684693/*
    685694  allocate the recovery area, or use an existing recovery area if it is
     
    695704        tdb_off_t recovery_head;
    696705
    697         if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
     706        if (tdb_recovery_area(tdb, methods, &recovery_head, &rec) == -1) {
    698707                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"));
    707708                return -1;
    708709        }
     
    800801        memset(rec, 0, sizeof(*rec));
    801802
    802         rec->magic    = 0;
     803        rec->magic    = TDB_RECOVERY_INVALID_MAGIC;
    803804        rec->data_len = recovery_size;
    804805        rec->rec_len  = recovery_max_size;
    805806        rec->key_len  = old_map_size;
    806         CONVERT(rec);
     807        CONVERT(*rec);
    807808
    808809        /* build the recovery data into a single blob to allow us to do a single
     
    822823                        length = tdb->transaction->last_block_size;
    823824                }
    824                
     825
    825826                if (offset >= old_map_size) {
    826827                        continue;
     
    851852        tailer = sizeof(*rec) + recovery_max_size;
    852853        memcpy(p, &tailer, 4);
    853         CONVERT(p);
     854        if (DOCONV()) {
     855                tdb_convert(p, 4);
     856        }
    854857
    855858        /* write the recovery data to the recovery area */
     
    935938
    936939        methods = tdb->transaction->io_methods;
    937        
     940
    938941        /* if there are any locks pending then the caller has not
    939942           nested their locks properly, so fail the transaction */
    940         if (tdb->num_locks || tdb->global_lock.count) {
     943        if (tdb_have_extra_locks(tdb)) {
    941944                tdb->ecode = TDB_ERR_LOCK;
    942945                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: locks pending on commit\n"));
     
    947950        /* upgrade the main transaction lock region to a write lock */
    948951#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) {
    950953                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to upgrade hash locks\n"));
    951                 tdb->ecode = TDB_ERR_LOCK;
    952954                _tdb_transaction_cancel(tdb);
    953955                return -1;
     
    955957#endif
    956958
    957         /* get the global lock - this prevents new users attaching to the database
     959        /* get the open lock - this prevents new users attaching to the database
    958960           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"));
    962963                _tdb_transaction_cancel(tdb);
    963964                return -1;
    964965        }
    965 
    966         tdb->transaction->global_lock_taken = true;
    967966
    968967        if (!(tdb->flags & TDB_NOSYNC)) {
     
    991990        }
    992991
    993         /* Keep the global lock until the actual commit */
     992        /* Keep the open lock until the actual commit */
    994993
    995994        return 0;
     
    999998   prepare to commit the current transaction
    1000999*/
    1001 int tdb_transaction_prepare_commit(struct tdb_context *tdb)
    1002 {       
     1000_PUBLIC_ int tdb_transaction_prepare_commit(struct tdb_context *tdb)
     1001{
    10031002        tdb_trace(tdb, "tdb_transaction_prepare_commit");
    10041003        return _tdb_transaction_prepare_commit(tdb);
    10051004}
    10061005
     1006/* A repack is worthwhile if the largest is less than half total free. */
     1007static 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
    10071028/*
    10081029  commit the current transaction
    10091030*/
    1010 int tdb_transaction_commit(struct tdb_context *tdb)
    1011 {       
     1031_PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb)
     1032{
    10121033        const struct tdb_methods *methods;
    10131034        int i;
    1014         bool need_repack;
     1035        bool need_repack = false;
    10151036
    10161037        if (tdb->transaction == NULL) {
     
    10201041
    10211042        tdb_trace(tdb, "tdb_transaction_commit");
     1043
    10221044        if (tdb->transaction->transaction_error) {
    10231045                tdb->ecode = TDB_ERR_IO;
     
    10261048                return -1;
    10271049        }
     1050
    10281051
    10291052        if (tdb->transaction->nesting != 0) {
     
    10601083                        length = tdb->transaction->last_block_size;
    10611084                }
     1085
    10621086                if (methods->tdb_write(tdb, offset, tdb->transaction->blocks[i], length) == -1) {
    10631087                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n"));
    1064                        
     1088
    10651089                        /* we've overwritten part of the data and
    10661090                           possibly expanded the file, so we need to
     
    10761100                SAFE_FREE(tdb->transaction->blocks[i]);
    10771101        }
     1102
     1103        /* Do this before we drop lock or blocks. */
     1104        if (tdb->transaction->expanded) {
     1105                need_repack = repack_worthwhile(tdb);
     1106        }
    10781107
    10791108        SAFE_FREE(tdb->transaction->blocks);
     
    11001129#endif
    11011130
    1102         need_repack = tdb->transaction->need_repack;
    1103 
    11041131        /* use a transaction cancel to free memory and remove the
    11051132           transaction locks */
     
    11161143/*
    11171144  recover from an aborted transaction. Must be called with exclusive
    1118   database write access already established (including the global
     1145  database write access already established (including the open
    11191146  lock to prevent new processes attaching)
    11201147*/
     
    12171244                return -1;                     
    12181245        }
    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);
    12291246
    12301247        if (transaction_sync(tdb, 0, recovery_eof) == -1) {
     
    12401257        return 0;
    12411258}
     1259
     1260/* Any I/O failures we say "needs recovery". */
     1261bool 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}
Note: See TracChangeset for help on using the changeset viewer.