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:
Zach Brown
2018-03-15 15:32:13 -07:00
committed by Zach Brown
parent 3818f72776
commit 995e43aa18

View File

@@ -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;
}