fs/ntfs3: Improve checking of bad clusters
authorKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Tue, 11 Oct 2022 17:19:36 +0000 (20:19 +0300)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Mon, 14 Nov 2022 16:50:48 +0000 (19:50 +0300)
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>
fs/ntfs3/bitmap.c
fs/ntfs3/ntfs_fs.h
fs/ntfs3/run.c
fs/ntfs3/super.c

index a51805a3d91fad8e5edbf7ee89dc73799a530e45..723fb64e653160a5ce42194429fd7d71c4acbe1b 100644 (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
  *
index 1ab8bbaf8eaa87205ed641665b70da2a2d88743f..5cefcfa5211871da691396ecf91a13f528c334c7 100644 (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);
 
index 12d8682f33b538dea9dcd1120b6a59a8a7999ccd..a5af71cd8d14bb19ea7bc8146c6615609eb1d748 100644 (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;
index 59a831bd0c9b4a1e30fab8a1bf09bc3693cc1ed2..ef4ea3f21905fc12ccb6f0f57d9d10626b1e1d27 100644 (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);