- /* 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;
-
- /* Did we enlarge zones without enlarging file? */
- if (tdb->map_size < tdb->header.v.num_zones<<tdb->header.v.zone_bits) {
- add = (tdb->header.v.num_zones<<tdb->header.v.zone_bits)
- - tdb->map_size;
- /* Updates tdb->map_size. */
- if (tdb->methods->expand_file(tdb, add) == -1)
- goto fail;
- if (add_free_record(tdb, tdb->map_size - add, add) == -1)
- 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(fhdr)
- + ((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 zones to meet the need. */
- add = (needed + (1ULL << new_zone_bits)-1)
- & ~((1ULL << new_zone_bits)-1);
- }
-
- /* Updates tdb->map_size. */
- if (tdb->methods->expand_file(tdb, add) == -1)
- goto fail;
-
- /* Use first part as new free bucket array. */
- off = tdb->map_size - add;
- freebucket_size = new_num_zones
- * (new_num_buckets + 1) * sizeof(tdb_off_t);
-
- /* Write header. */
- if (set_header(tdb, &fhdr, 0, freebucket_size, freebucket_size, 0))
- goto fail;
- if (tdb_write_convert(tdb, off, &fhdr, sizeof(fhdr)) == -1)
- goto fail;
-
- /* Adjust off to point to start of buckets, add to be remainder. */
- add -= freebucket_size + sizeof(fhdr);
- off += sizeof(fhdr);
-
- /* Access the old zones. */
- old_num_total = tdb->header.v.num_zones*(tdb->header.v.free_buckets+1);
- old_free_off = tdb->header.v.free_off;
- oldf = tdb_access_read(tdb, old_free_off,
- old_num_total * sizeof(tdb_off_t));
- if (!oldf)
- goto fail;
-
- /* Switch to using our new zone. */
- if (zero_out(tdb, off, new_num_zones * (new_num_buckets + 1)) == -1)
- goto fail_release;
- tdb->header.v.free_off = off;
- tdb->header.v.num_zones = new_num_zones;
- tdb->header.v.free_buckets = new_num_buckets;
-
- /* FIXME: If zone size hasn't changed, can simply copy pointers. */
- /* FIXME: Coalesce? */
- for (i = 0; i < old_num_total; i++) {
- tdb_off_t next;
- struct tdb_free_record rec;
- tdb_off_t list;