fs/ntfs3: Fix case when unmarked clusters intersect with zone

[ Upstream commit 5fc982fe7e ]

Reported-by: syzbot+7f3761b790fa41d0f3d5@syzkaller.appspotmail.com
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Konstantin Komarov
2024-10-10 20:09:24 +03:00
committed by Greg Kroah-Hartman
parent c5f89458a2
commit 57f7979aef
+30 -10
View File
@@ -1053,8 +1053,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
{
int ret, err;
CLST next_vcn, lcn, len;
size_t index;
bool ok;
size_t index, done;
bool ok, zone;
struct wnd_bitmap *wnd;
ret = run_unpack(run, sbi, ino, svcn, evcn, vcn, run_buf, run_buf_size);
@@ -1085,8 +1085,9 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
continue;
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
zone = max(wnd->zone_bit, lcn) < min(wnd->zone_end, lcn + len);
/* Check for free blocks. */
ok = wnd_is_used(wnd, lcn, len);
ok = !zone && wnd_is_used(wnd, lcn, len);
up_read(&wnd->rw_lock);
if (ok)
continue;
@@ -1094,14 +1095,33 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
/* Looks like volume is corrupted. */
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
if (down_write_trylock(&wnd->rw_lock)) {
/* Mark all zero bits as used in range [lcn, lcn+len). */
size_t done;
err = wnd_set_used_safe(wnd, lcn, len, &done);
up_write(&wnd->rw_lock);
if (err)
return err;
if (!down_write_trylock(&wnd->rw_lock))
continue;
if (zone) {
/*
* Range [lcn, lcn + len) intersects with zone.
* To avoid complex with zone just turn it off.
*/
wnd_zone_set(wnd, 0, 0);
}
/* Mark all zero bits as used in range [lcn, lcn+len). */
err = wnd_set_used_safe(wnd, lcn, len, &done);
if (zone) {
/* Restore zone. Lock mft run. */
struct rw_semaphore *lock;
lock = is_mounted(sbi) ? &sbi->mft.ni->file.run_lock :
NULL;
if (lock)
down_read(lock);
ntfs_refresh_zone(sbi);
if (lock)
up_read(lock);
}
up_write(&wnd->rw_lock);
if (err)
return err;
}
return ret;