diff --git a/kmod/src/btree.c b/kmod/src/btree.c index 069a50bf..23878451 100644 --- a/kmod/src/btree.c +++ b/kmod/src/btree.c @@ -953,3 +953,38 @@ void scoutfs_btree_release(struct scoutfs_btree_cursor *curs) } curs->bl = NULL; } + +/* + * Find the first missing key between the caller's keys, inclusive. Set + * the caller's hole key and return 0 if we find a missing key. Return + * -ENOSPC if all the keys in the range were present or -errno on errors. + * + * The caller ensures that it's safe for us to be walking this region + * of the tree. + */ +int scoutfs_btree_hole(struct super_block *sb, struct scoutfs_key *first, + struct scoutfs_key *last, struct scoutfs_key *hole) +{ + DECLARE_SCOUTFS_BTREE_CURSOR(curs); + int ret; + + *hole = *first; + while ((ret = scoutfs_btree_next(sb, first, last, &curs)) > 0) { + /* return our expected hole if we skipped it */ + if (scoutfs_key_cmp(hole, curs.key) < 0) + break; + + *hole = *curs.key; + scoutfs_inc_key(hole); + } + scoutfs_btree_release(&curs); + + if (ret >= 0) { + if (scoutfs_key_cmp(hole, last) <= 0) + ret = 0; + else + ret = -ENOSPC; + } + + return ret; +} diff --git a/kmod/src/btree.h b/kmod/src/btree.h index e902a6bd..5804cbc3 100644 --- a/kmod/src/btree.h +++ b/kmod/src/btree.h @@ -28,6 +28,8 @@ int scoutfs_btree_next(struct super_block *sb, struct scoutfs_key *first, int scoutfs_btree_dirty(struct super_block *sb, struct scoutfs_key *key); void scoutfs_btree_update(struct super_block *sb, struct scoutfs_key *key, struct scoutfs_btree_cursor *curs); +int scoutfs_btree_hole(struct super_block *sb, struct scoutfs_key *first, + struct scoutfs_key *last, struct scoutfs_key *hole); void scoutfs_btree_release(struct scoutfs_btree_cursor *curs); diff --git a/kmod/src/dir.c b/kmod/src/dir.c index 730cc7b2..39745fb7 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -341,21 +341,10 @@ static int scoutfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, scoutfs_set_key(&last, scoutfs_ino(dir), SCOUTFS_DIRENT_KEY, last_dirent_key_offset(h)); - /* find the first unoccupied key offset after the hashed name */ - key = first; - while ((ret = scoutfs_btree_next(sb, &first, &last, &curs)) > 0) { - key = *curs.key; - scoutfs_inc_key(&key); - } - scoutfs_btree_release(&curs); - if (ret < 0) + ret = scoutfs_btree_hole(sb, &first, &last, &key); + if (ret) goto out; - if (scoutfs_key_cmp(&key, &last) > 0) { - ret = -ENOSPC; - goto out; - } - ret = scoutfs_btree_insert(sb, &key, bytes, &curs); if (ret) goto out;