tdb: Make tdb_find circular-safe
[samba.git] / lib / tdb / common / summary.c
index b222a5993d75b75b6a930c4aa10a79c0e7b8b0b2..c9b5bc4c1d0b490b9948bde4ad6c93e9fc13aa04 100644 (file)
@@ -1,7 +1,7 @@
- /* 
+ /*
    Trivial Database: human-readable summary code
    Copyright (C) Rusty Russell 2010
-   
+
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
 #include "tdb_private.h"
 
 #define SUMMARY_FORMAT \
-       "Size of file/data: %u/%zu\n" \
+       "Size of file/data: %llu/%zu\n" \
+       "Header offset/logical size: %zu/%zu\n" \
        "Number of records: %zu\n" \
+       "Incompatible hash: %s\n" \
+       "Active/supported feature flags: 0x%08x/0x%08x\n" \
+       "Robust mutexes locking: %s\n" \
        "Smallest/average/largest keys: %zu/%zu/%zu\n" \
        "Smallest/average/largest data: %zu/%zu/%zu\n" \
        "Smallest/average/largest padding: %zu/%zu/%zu\n" \
@@ -86,12 +90,15 @@ static size_t get_hash_length(struct tdb_context *tdb, unsigned int i)
 
 _PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
 {
-       tdb_off_t off;
-       struct tally freet, keys, data, dead, extra, hash, uncoal;
+       off_t file_size;
+       tdb_off_t off, rec_off;
+       struct tally freet, keys, data, dead, extra, hashval, uncoal;
        struct tdb_record rec;
        char *ret = NULL;
        bool locked;
-       size_t len, unc = 0;
+       size_t unc = 0;
+       int len;
+       struct tdb_record recovery;
 
        /* Read-only databases use no locking at all: it's best-effort.
         * We may have a write lock already, so skip that case too. */
@@ -103,15 +110,19 @@ _PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
                locked = true;
        }
 
+       if (tdb_recovery_area(tdb, tdb->methods, &rec_off, &recovery) != 0) {
+               goto unlock;
+       }
+
        tally_init(&freet);
        tally_init(&keys);
        tally_init(&data);
        tally_init(&dead);
        tally_init(&extra);
-       tally_init(&hash);
+       tally_init(&hashval);
        tally_init(&uncoal);
 
-       for (off = TDB_DATA_START(tdb->header.hash_size);
+       for (off = TDB_DATA_START(tdb->hash_size);
             off < tdb->map_size - 1;
             off += sizeof(rec) + rec.rec_len) {
                if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
@@ -135,14 +146,19 @@ _PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
                case TDB_RECOVERY_INVALID_MAGIC:
                case 0x42424242:
                        unc++;
-                       rec.rec_len = tdb_dead_space(tdb, off) - sizeof(rec);
-                       /* Fall through */
+                       /* If it's a valid recovery, we can trust rec_len. */
+                       if (off != rec_off) {
+                               rec.rec_len = tdb_dead_space(tdb, off)
+                                       - sizeof(rec);
+                       }
+
+                       FALL_THROUGH;
                case TDB_DEAD_MAGIC:
                        tally_add(&dead, rec.rec_len);
                        break;
                default:
                        TDB_LOG((tdb, TDB_DEBUG_ERROR,
-                                "Unexpected record magic 0x%x at offset %d\n",
+                                "Unexpected record magic 0x%x at offset %u\n",
                                 rec.magic, off));
                        goto unlock;
                }
@@ -150,18 +166,18 @@ _PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
        if (unc > 1)
                tally_add(&uncoal, unc - 1);
 
-       for (off = 0; off < tdb->header.hash_size; off++)
-               tally_add(&hash, get_hash_length(tdb, off));
+       for (off = 0; off < tdb->hash_size; off++)
+               tally_add(&hashval, get_hash_length(tdb, off));
 
-       /* 20 is max length of a %zu. */
-       len = strlen(SUMMARY_FORMAT) + 35*20 + 1;
-       ret = (char *)malloc(len);
-       if (!ret)
-               goto unlock;
+       file_size = tdb->hdr_ofs + tdb->map_size;
 
-       snprintf(ret, len, SUMMARY_FORMAT,
-                tdb->map_size, keys.total+data.total,
+       len = asprintf(&ret, SUMMARY_FORMAT,
+                (unsigned long long)file_size, keys.total+data.total,
+                (size_t)tdb->hdr_ofs, (size_t)tdb->map_size,
                 keys.num,
+                (tdb->hash_fn == tdb_jenkins_hash)?"yes":"no",
+                (unsigned)tdb->feature_flags, TDB_SUPPORTED_FEATURE_FLAGS,
+                (tdb->feature_flags & TDB_FEATURE_FLAG_MUTEX)?"yes":"no",
                 keys.min, tally_mean(&keys), keys.max,
                 data.min, tally_mean(&data), data.max,
                 extra.min, tally_mean(&extra), extra.max,
@@ -169,20 +185,23 @@ _PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
                 dead.min, tally_mean(&dead), dead.max,
                 freet.num,
                 freet.min, tally_mean(&freet), freet.max,
-                hash.num,
-                hash.min, tally_mean(&hash), hash.max,
+                hashval.num,
+                hashval.min, tally_mean(&hashval), hashval.max,
                 uncoal.total,
                 uncoal.min, tally_mean(&uncoal), uncoal.max,
-                keys.total * 100.0 / tdb->map_size,
-                data.total * 100.0 / tdb->map_size,
-                extra.total * 100.0 / tdb->map_size,
-                freet.total * 100.0 / tdb->map_size,
-                dead.total * 100.0 / tdb->map_size,
+                keys.total * 100.0 / file_size,
+                data.total * 100.0 / file_size,
+                extra.total * 100.0 / file_size,
+                freet.total * 100.0 / file_size,
+                dead.total * 100.0 / file_size,
                 (keys.num + freet.num + dead.num)
                 * (sizeof(struct tdb_record) + sizeof(uint32_t))
-                * 100.0 / tdb->map_size,
-                tdb->header.hash_size * sizeof(tdb_off_t)
-                * 100.0 / tdb->map_size);
+                * 100.0 / file_size,
+                tdb->hash_size * sizeof(tdb_off_t)
+                * 100.0 / file_size);
+       if (len == -1) {
+               goto unlock;
+       }
 
 unlock:
        if (locked) {