mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-10 05:37:25 +00:00
scoutfs: hold the alloc sem during truncate
The super info's alloc_rwsem protects the local node free segment and block bitmap items. The truncate code wasn't holding using the rwsem so it could race with other local node allocator item users and corrupt the bitmaps. In the best case this could corrupt structures that trigger EIO. The corrupt items could also create duplicate block allocations that clobber each other and corrupt data. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -612,13 +612,14 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
|
||||
u64 ino, u64 iblock, u64 last, bool offline,
|
||||
struct scoutfs_lock *lock)
|
||||
{
|
||||
DECLARE_DATA_INFO(sb, datinf);
|
||||
struct scoutfs_key_buf last_key;
|
||||
struct scoutfs_key_buf key;
|
||||
struct scoutfs_block_mapping_key last_bmk;
|
||||
struct scoutfs_block_mapping_key bmk;
|
||||
struct block_mapping *map;
|
||||
SCOUTFS_DECLARE_KVEC(val);
|
||||
bool holding;
|
||||
bool holding = false;
|
||||
bool dirtied;
|
||||
bool modified;
|
||||
u64 blkno;
|
||||
@@ -642,6 +643,13 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
|
||||
init_mapping_key(&key, &bmk, ino, iblock);
|
||||
scoutfs_kvec_init(val, map->encoded, sizeof(map->encoded));
|
||||
|
||||
ret = scoutfs_hold_trans(sb, SIC_TRUNC_BLOCK());
|
||||
if (ret)
|
||||
break;
|
||||
holding = true;
|
||||
|
||||
down_write(&datinf->alloc_rwsem);
|
||||
|
||||
ret = scoutfs_item_next(sb, &key, &last_key, val, lock);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT)
|
||||
@@ -657,7 +665,6 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
|
||||
iblock = max(iblock, be64_to_cpu(bmk.base) <<
|
||||
SCOUTFS_BLOCK_MAPPING_SHIFT);
|
||||
|
||||
holding = false;
|
||||
dirtied = false;
|
||||
modified = false;
|
||||
for_each_block(i, iblock, last) {
|
||||
@@ -669,13 +676,6 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
|
||||
!!offline == !!test_bit(i, map->offline))
|
||||
continue;
|
||||
|
||||
if (!holding) {
|
||||
ret = scoutfs_hold_trans(sb, SIC_TRUNC_BLOCK());
|
||||
if (ret)
|
||||
break;
|
||||
holding = true;
|
||||
}
|
||||
|
||||
if (!dirtied) {
|
||||
/* dirty item with full size encoded */
|
||||
ret = scoutfs_item_update(sb, &key, val, lock);
|
||||
@@ -721,15 +721,19 @@ int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
|
||||
}
|
||||
}
|
||||
|
||||
if (holding) {
|
||||
scoutfs_release_trans(sb);
|
||||
holding = false;
|
||||
}
|
||||
up_write(&datinf->alloc_rwsem);
|
||||
scoutfs_release_trans(sb);
|
||||
holding = false;
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (holding) {
|
||||
up_write(&datinf->alloc_rwsem);
|
||||
scoutfs_release_trans(sb);
|
||||
}
|
||||
|
||||
kfree(map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user