+ return write_header(tdb);
+
+fail_release:
+ tdb_access_release(tdb, oldf);
+ return -1;
+}
+
+/* Expand the database. */
+int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen,
+ bool growing)
+{
+ uint64_t new_num_buckets, new_num_zones, new_zone_bits;
+ uint64_t old_num_zones, old_size, old_zone_bits;
+ tdb_len_t add, needed;
+
+ /* We need room for the record header too. */
+ needed = sizeof(struct tdb_used_record)
+ + adjust_size(klen, dlen, growing);
+
+ /* tdb_allrecord_lock will update header; did zones change? */
+ old_zone_bits = tdb->header.v.zone_bits;
+ old_num_zones = tdb->header.v.num_zones;
+
+ /* FIXME: this is overkill. An expand lock? */
+ if (tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false) == -1)
+ return -1;
+
+ /* Someone may have expanded for us. */
+ if (old_zone_bits != tdb->header.v.zone_bits
+ || old_num_zones != tdb->header.v.num_zones)
+ goto success;
+
+ /* They may have also expanded the underlying size (otherwise we'd
+ * have expanded our mmap to look at those offsets already). */
+ old_size = tdb->map_size;
+ tdb->methods->oob(tdb, tdb->map_size + 1, true);
+ if (tdb->map_size != old_size)
+ goto success;
+
+ add = expand_to_fill_zones(tdb);
+ if (add == TDB_OFF_ERR)
+ goto fail;
+
+ if (add >= needed) {
+ /* Allocate from this zone. */
+ tdb->last_zone = zone_of(tdb, tdb->map_size - add);
+ goto success;
+ }
+
+ /* Slow path. Should we increase the number of buckets? */
+ new_num_buckets = tdb->header.v.free_buckets;
+ if (larger_buckets_might_help(tdb))
+ new_num_buckets++;
+
+ /* Now we'll need room for the new free buckets, too. Assume
+ * worst case (zones expand). */
+ needed += sizeof(struct tdb_used_record)
+ + ((tdb->header.v.num_zones+1)
+ * (new_num_buckets+1) * sizeof(tdb_off_t));
+
+ /* If we need less that one zone, and they're working well, just add
+ * another one. */
+ if (needed < (1UL<<tdb->header.v.zone_bits) && zones_happy(tdb)) {
+ new_num_zones = tdb->header.v.num_zones+1;
+ new_zone_bits = tdb->header.v.zone_bits;
+ add = 1ULL << tdb->header.v.zone_bits;
+ } else {
+ /* Increase the zone size. */
+ new_num_zones = tdb->header.v.num_zones;
+ new_zone_bits = tdb->header.v.zone_bits+1;
+ while ((new_num_zones << new_zone_bits)
+ < tdb->map_size + needed) {
+ new_zone_bits++;
+ }
+
+ /* We expand by enough full zones to meet the need. */
+ add = ((tdb->map_size + needed + (1ULL << new_zone_bits)-1)
+ & ~((1ULL << new_zone_bits)-1))
+ - tdb->map_size;
+ }
+
+ if (update_zones(tdb, new_num_zones, new_zone_bits, new_num_buckets,
+ add) == -1)
+ goto fail;
+