fs/ntfs3: Improve checking of bad clusters

Added new function wnd_set_used_safe.
Load $BadClus before $AttrDef instead of before $Bitmap.

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
Konstantin Komarov 2022-10-11 20:19:36 +03:00
parent 60ce8dfde0
commit ec5fc72013
No known key found for this signature in database
GPG Key ID: A9B0331F832407B6
4 changed files with 81 additions and 44 deletions

View File

@ -800,6 +800,44 @@ int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits)
return err;
}
/*
* wnd_set_used_safe - Mark the bits range from bit to bit + bits as used.
*
* Unlikely wnd_set_used/wnd_set_free this function is not full trusted.
* It scans every bit in bitmap and marks free bit as used.
* @done - how many bits were marked as used.
*
* NOTE: normally *done should be 0.
*/
int wnd_set_used_safe(struct wnd_bitmap *wnd, size_t bit, size_t bits,
size_t *done)
{
size_t i, from = 0, len = 0;
int err = 0;
*done = 0;
for (i = 0; i < bits; i++) {
if (wnd_is_free(wnd, bit + i, 1)) {
if (!len)
from = bit + i;
len += 1;
} else if (len) {
err = wnd_set_used(wnd, from, len);
*done += len;
len = 0;
if (err)
break;
}
}
if (len) {
/* last fragment. */
err = wnd_set_used(wnd, from, len);
*done += len;
}
return err;
}
/*
* wnd_is_free_hlp
*

View File

@ -828,6 +828,8 @@ static inline size_t wnd_zeroes(const struct wnd_bitmap *wnd)
int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits);
int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits);
int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits);
int wnd_set_used_safe(struct wnd_bitmap *wnd, size_t bit, size_t bits,
size_t *done);
bool wnd_is_free(struct wnd_bitmap *wnd, size_t bit, size_t bits);
bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits);

View File

@ -1096,25 +1096,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
if (down_write_trylock(&wnd->rw_lock)) {
/* Mark all zero bits as used in range [lcn, lcn+len). */
CLST i, lcn_f = 0, len_f = 0;
err = 0;
for (i = 0; i < len; i++) {
if (wnd_is_free(wnd, lcn + i, 1)) {
if (!len_f)
lcn_f = lcn + i;
len_f += 1;
} else if (len_f) {
err = wnd_set_used(wnd, lcn_f, len_f);
len_f = 0;
if (err)
break;
}
}
if (len_f)
err = wnd_set_used(wnd, lcn_f, len_f);
size_t done;
err = wnd_set_used_safe(wnd, lcn, len, &done);
up_write(&wnd->rw_lock);
if (err)
return err;

View File

@ -930,7 +930,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
struct block_device *bdev = sb->s_bdev;
struct inode *inode;
struct ntfs_inode *ni;
size_t i, tt;
size_t i, tt, bad_len, bad_frags;
CLST vcn, lcn, len;
struct ATTRIB *attr;
const struct VOLUME_INFO *info;
@ -1100,30 +1100,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
sbi->mft.ni = ni;
/* Load $BadClus. */
ref.low = cpu_to_le32(MFT_REC_BADCLUST);
ref.seq = cpu_to_le16(MFT_REC_BADCLUST);
inode = ntfs_iget5(sb, &ref, &NAME_BADCLUS);
if (IS_ERR(inode)) {
ntfs_err(sb, "Failed to load $BadClus.");
err = PTR_ERR(inode);
goto out;
}
ni = ntfs_i(inode);
for (i = 0; run_get_entry(&ni->file.run, i, &vcn, &lcn, &len); i++) {
if (lcn == SPARSE_LCN)
continue;
if (!sbi->bad_clusters)
ntfs_notice(sb, "Volume contains bad blocks");
sbi->bad_clusters += len;
}
iput(inode);
/* Load $Bitmap. */
ref.low = cpu_to_le32(MFT_REC_BITMAP);
ref.seq = cpu_to_le16(MFT_REC_BITMAP);
@ -1161,6 +1137,44 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
if (err)
goto out;
/* Load $BadClus. */
ref.low = cpu_to_le32(MFT_REC_BADCLUST);
ref.seq = cpu_to_le16(MFT_REC_BADCLUST);
inode = ntfs_iget5(sb, &ref, &NAME_BADCLUS);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load $BadClus (%d).", err);
goto out;
}
ni = ntfs_i(inode);
bad_len = bad_frags = 0;
for (i = 0; run_get_entry(&ni->file.run, i, &vcn, &lcn, &len); i++) {
if (lcn == SPARSE_LCN)
continue;
bad_len += len;
bad_frags += 1;
if (sb_rdonly(sb))
continue;
if (wnd_set_used_safe(&sbi->used.bitmap, lcn, len, &tt) || tt) {
/* Bad blocks marked as free in bitmap. */
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
}
}
if (bad_len) {
/*
* Notice about bad blocks.
* In normal cases these blocks are marked as used in bitmap.
* And we never allocate space in it.
*/
ntfs_notice(sb,
"Volume contains %zu bad blocks in %zu fragments.",
bad_len, bad_frags);
}
iput(inode);
/* Load $AttrDef. */
ref.low = cpu_to_le32(MFT_REC_ATTR);
ref.seq = cpu_to_le16(MFT_REC_ATTR);