tdb: Make the freelist walk circular-safe
[samba.git] / lib / tdb / common / freelist.c
index a19289a399c8c72f975d27c938c58c9ea1554bcf..8870a418328bc1682059c805f9aa2d673994b525 100644 (file)
@@ -444,6 +444,8 @@ static tdb_off_t tdb_allocate_from_freelist(
        struct tdb_context *tdb, tdb_len_t length, struct tdb_record *rec)
 {
        tdb_off_t rec_ptr, last_ptr, newrec_ptr;
+       struct tdb_chainwalk_ctx chainwalk;
+       bool modified;
        struct {
                tdb_off_t rec_ptr, last_ptr;
                tdb_len_t rec_len;
@@ -466,6 +468,9 @@ static tdb_off_t tdb_allocate_from_freelist(
        if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
                return 0;
 
+       modified = false;
+       tdb_chainwalk_init(&chainwalk, rec_ptr);
+
        bestfit.rec_ptr = 0;
        bestfit.last_ptr = 0;
        bestfit.rec_len = 0;
@@ -526,6 +531,8 @@ static tdb_off_t tdb_allocate_from_freelist(
                                merge_created_candidate = true;
                        }
 
+                       modified = true;
+
                        continue;
                }
 
@@ -542,6 +549,14 @@ static tdb_off_t tdb_allocate_from_freelist(
                last_ptr = rec_ptr;
                rec_ptr = rec->next;
 
+               if (!modified) {
+                       bool ok;
+                       ok = tdb_chainwalk_check(tdb, &chainwalk, rec_ptr);
+                       if (!ok) {
+                               return 0;
+                       }
+               }
+
                /* if we've found a record that is big enough, then
                   stop searching if its also not too big. The
                   definition of 'too big' changes as we scan