- tdb_len_t freebucket_size;
- const tdb_off_t *oldf;
- tdb_off_t i, off, old_num_total, old_free_off;
- struct tdb_used_record fhdr;
-
- /* Updates tdb->map_size. */
- if (tdb->methods->expand_file(tdb, add) == -1)
- return -1;
-
- /* 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))
- return -1;
- if (tdb_write_convert(tdb, off, &fhdr, sizeof(fhdr)) == -1)
- return -1;
-
- /* 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), true);
- if (!oldf)
- return -1;
-
- /* Switch to using our new zone. */
- if (zero_out(tdb, off, freebucket_size) == -1)
- goto fail_release;
-
- tdb->header.v.free_off = off;
- tdb->header.v.num_zones = new_num_zones;
- tdb->header.v.zone_bits = new_zone_bits;
- 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;
-
- for (off = oldf[i]; off; off = next) {
- if (tdb_read_convert(tdb, off, &rec, sizeof(rec)))
- goto fail_release;
-
- list = zone_of(tdb, off)
- * (tdb->header.v.free_buckets+1)
- + size_to_bucket(tdb, rec.data_len);
- next = rec.next;
-
- if (enqueue_in_free(tdb, list, off, &rec) == -1)
- goto fail_release;
- }
- }
-
- /* Free up the old free buckets. */
- old_free_off -= sizeof(fhdr);
- if (tdb_read_convert(tdb, old_free_off, &fhdr, sizeof(fhdr)) == -1)
- goto fail_release;
- if (add_free_record(tdb, old_free_off,
- sizeof(fhdr)
- + rec_data_length(&fhdr)
- + rec_extra_padding(&fhdr)))
- goto fail_release;
-
- /* Add the rest as a new free record. */
- if (add_free_record(tdb, tdb->map_size - add, add) == -1)
- goto fail_release;
-
- /* Start allocating from where the new space is. */
- tdb->last_zone = zone_of(tdb, tdb->map_size - add);
- tdb_access_release(tdb, oldf);
- return write_header(tdb);
-
-fail_release:
- tdb_access_release(tdb, oldf);
- return -1;