mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 14:14:24 +08:00
fs/ntfs3: Alternative boot if primary boot is corrupted
Some code refactoring added also. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
parent
e0f363a988
commit
6a4cd3ea7d
@ -724,6 +724,8 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
struct MFT_REC *rec;
|
||||
u16 fn, ao;
|
||||
u8 cluster_bits;
|
||||
u32 boot_off = 0;
|
||||
const char *hint = "Primary boot";
|
||||
|
||||
sbi->volume.blocks = dev_size >> PAGE_SHIFT;
|
||||
|
||||
@ -731,11 +733,12 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
|
||||
check_boot:
|
||||
err = -EINVAL;
|
||||
boot = (struct NTFS_BOOT *)bh->b_data;
|
||||
boot = (struct NTFS_BOOT *)Add2Ptr(bh->b_data, boot_off);
|
||||
|
||||
if (memcmp(boot->system_id, "NTFS ", sizeof("NTFS ") - 1)) {
|
||||
ntfs_err(sb, "Boot's signature is not NTFS.");
|
||||
ntfs_err(sb, "%s signature is not NTFS.", hint);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -748,14 +751,16 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
boot->bytes_per_sector[0];
|
||||
if (boot_sector_size < SECTOR_SIZE ||
|
||||
!is_power_of_2(boot_sector_size)) {
|
||||
ntfs_err(sb, "Invalid bytes per sector %u.", boot_sector_size);
|
||||
ntfs_err(sb, "%s: invalid bytes per sector %u.", hint,
|
||||
boot_sector_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* cluster size: 512, 1K, 2K, 4K, ... 2M */
|
||||
sct_per_clst = true_sectors_per_clst(boot);
|
||||
if ((int)sct_per_clst < 0 || !is_power_of_2(sct_per_clst)) {
|
||||
ntfs_err(sb, "Invalid sectors per cluster %u.", sct_per_clst);
|
||||
ntfs_err(sb, "%s: invalid sectors per cluster %u.", hint,
|
||||
sct_per_clst);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -771,8 +776,8 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
if (mlcn * sct_per_clst >= sectors || mlcn2 * sct_per_clst >= sectors) {
|
||||
ntfs_err(
|
||||
sb,
|
||||
"Start of MFT 0x%llx (0x%llx) is out of volume 0x%llx.",
|
||||
mlcn, mlcn2, sectors);
|
||||
"%s: start of MFT 0x%llx (0x%llx) is out of volume 0x%llx.",
|
||||
hint, mlcn, mlcn2, sectors);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -784,7 +789,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
/* Check MFT record size. */
|
||||
if (record_size < SECTOR_SIZE || !is_power_of_2(record_size)) {
|
||||
ntfs_err(sb, "Invalid bytes per MFT record %u (%d).",
|
||||
ntfs_err(sb, "%s: invalid bytes per MFT record %u (%d).", hint,
|
||||
record_size, boot->record_size);
|
||||
goto out;
|
||||
}
|
||||
@ -801,13 +806,13 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
/* Check index record size. */
|
||||
if (sbi->index_size < SECTOR_SIZE || !is_power_of_2(sbi->index_size)) {
|
||||
ntfs_err(sb, "Invalid bytes per index %u(%d).", sbi->index_size,
|
||||
boot->index_size);
|
||||
ntfs_err(sb, "%s: invalid bytes per index %u(%d).", hint,
|
||||
sbi->index_size, boot->index_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sbi->index_size > MAXIMUM_BYTES_PER_INDEX) {
|
||||
ntfs_err(sb, "Unsupported bytes per index %u.",
|
||||
ntfs_err(sb, "%s: unsupported bytes per index %u.", hint,
|
||||
sbi->index_size);
|
||||
goto out;
|
||||
}
|
||||
@ -834,7 +839,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
/* Compare boot's cluster and sector. */
|
||||
if (sbi->cluster_size < boot_sector_size) {
|
||||
ntfs_err(sb, "Invalid bytes per cluster (%u).",
|
||||
ntfs_err(sb, "%s: invalid bytes per cluster (%u).", hint,
|
||||
sbi->cluster_size);
|
||||
goto out;
|
||||
}
|
||||
@ -930,7 +935,46 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
err = 0;
|
||||
|
||||
if (bh->b_blocknr && !sb_rdonly(sb)) {
|
||||
/*
|
||||
* Alternative boot is ok but primary is not ok.
|
||||
* Update primary boot.
|
||||
*/
|
||||
struct buffer_head *bh0 = sb_getblk(sb, 0);
|
||||
if (bh0) {
|
||||
if (buffer_locked(bh0))
|
||||
__wait_on_buffer(bh0);
|
||||
|
||||
lock_buffer(bh0);
|
||||
memcpy(bh0->b_data, boot, sizeof(*boot));
|
||||
set_buffer_uptodate(bh0);
|
||||
mark_buffer_dirty(bh0);
|
||||
unlock_buffer(bh0);
|
||||
if (!sync_dirty_buffer(bh0))
|
||||
ntfs_warn(sb, "primary boot is updated");
|
||||
put_bh(bh0);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (err == -EINVAL && !bh->b_blocknr && dev_size > PAGE_SHIFT) {
|
||||
u32 block_size = min_t(u32, sector_size, PAGE_SIZE);
|
||||
u64 lbo = dev_size - sizeof(*boot);
|
||||
|
||||
/*
|
||||
* Try alternative boot (last sector)
|
||||
*/
|
||||
brelse(bh);
|
||||
|
||||
sb_set_blocksize(sb, block_size);
|
||||
bh = ntfs_bread(sb, lbo >> blksize_bits(block_size));
|
||||
if (!bh)
|
||||
return -EINVAL;
|
||||
|
||||
boot_off = lbo & (block_size - 1);
|
||||
hint = "Alternative boot";
|
||||
goto check_boot;
|
||||
}
|
||||
brelse(bh);
|
||||
|
||||
return err;
|
||||
@ -955,6 +999,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
struct ATTR_DEF_ENTRY *t;
|
||||
u16 *shared;
|
||||
struct MFT_REF ref;
|
||||
bool ro = sb_rdonly(sb);
|
||||
|
||||
ref.high = 0;
|
||||
|
||||
@ -1035,6 +1080,10 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sbi->volume.minor_ver = info->minor_ver;
|
||||
sbi->volume.flags = info->flags;
|
||||
sbi->volume.ni = ni;
|
||||
if (info->flags & VOLUME_FLAG_DIRTY) {
|
||||
sbi->volume.real_dirty = true;
|
||||
ntfs_info(sb, "It is recommened to use chkdsk.");
|
||||
}
|
||||
|
||||
/* Load $MFTMirr to estimate recs_mirr. */
|
||||
ref.low = cpu_to_le32(MFT_REC_MIRR);
|
||||
@ -1069,21 +1118,16 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
iput(inode);
|
||||
|
||||
if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) {
|
||||
if (!sb_rdonly(sb)) {
|
||||
ntfs_warn(sb,
|
||||
"failed to replay log file. Can't mount rw!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else if (sbi->volume.flags & VOLUME_FLAG_DIRTY) {
|
||||
if (!sb_rdonly(sb) && !options->force) {
|
||||
ntfs_warn(
|
||||
sb,
|
||||
"volume is dirty and \"force\" flag is not set!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if ((sbi->flags & NTFS_FLAGS_NEED_REPLAY) && !ro) {
|
||||
ntfs_warn(sb, "failed to replay log file. Can't mount rw!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((sbi->volume.flags & VOLUME_FLAG_DIRTY) && !ro && !options->force) {
|
||||
ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Load $MFT. */
|
||||
@ -1173,7 +1217,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
bad_len += len;
|
||||
bad_frags += 1;
|
||||
if (sb_rdonly(sb))
|
||||
if (ro)
|
||||
continue;
|
||||
|
||||
if (wnd_set_used_safe(&sbi->used.bitmap, lcn, len, &tt) || tt) {
|
||||
|
Loading…
Reference in New Issue
Block a user