Unverified Commit ec5fc720 authored by Konstantin Komarov's avatar Konstantin Komarov
Browse files

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: default avatarKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
parent 60ce8dfd
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -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
 *
+2 −0
Original line number Diff line number Diff line
@@ -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);

+2 −19
Original line number Diff line number Diff line
@@ -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;
+39 −25
Original line number Diff line number Diff line
@@ -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);