mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
ntfs: validate the MFT I/O geometry
MFT writes must consider both the device logical block size and the NTFS
record geometry. Validate those values at mount time, then cache the MFT
I/O unit used by all MFT write paths.
Use the larger of the device logical block and MFT record sizes as the MFT
I/O unit. Keep valid geometries that the current write paths cannot safely
handle read-only until their mapping support is generalized.
Fixes: 115380f9a2 ("ntfs: update mft operations")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
50ed298b6a
commit
dfe7ed335b
|
|
@ -286,6 +286,12 @@ static int ntfs_reconfigure(struct fs_context *fc)
|
|||
static const char *es = ". Cannot remount read-write.";
|
||||
|
||||
/* Remounting read-write. */
|
||||
if (!vol->mft_write_supported) {
|
||||
ntfs_error(sb,
|
||||
"MFT writeback is unsupported for this geometry%s",
|
||||
es);
|
||||
return -EROFS;
|
||||
}
|
||||
if (NVolErrors(vol)) {
|
||||
ntfs_error(sb, "Volume has errors and is read-only%s",
|
||||
es);
|
||||
|
|
@ -530,6 +536,8 @@ int ntfs_write_volume_label(struct ntfs_volume *vol, char *label)
|
|||
static bool is_boot_sector_ntfs(const struct super_block *sb,
|
||||
const struct ntfs_boot_sector *b, const bool silent)
|
||||
{
|
||||
u16 sector_size = le16_to_cpu(b->bpb.bytes_per_sector);
|
||||
|
||||
/*
|
||||
* Check that checksum == sum of u32 values from b to the checksum
|
||||
* field. If checksum is zero, no checking is done. We will work when
|
||||
|
|
@ -550,8 +558,8 @@ static bool is_boot_sector_ntfs(const struct super_block *sb,
|
|||
if (b->oem_id != magicNTFS)
|
||||
goto not_ntfs;
|
||||
/* Check bytes per sector value is between 256 and 4096. */
|
||||
if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 ||
|
||||
le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000)
|
||||
if (sector_size < 0x100 || sector_size > 0x1000 ||
|
||||
!is_power_of_2(sector_size))
|
||||
goto not_ntfs;
|
||||
/*
|
||||
* Check sectors per cluster value is valid and the cluster size
|
||||
|
|
@ -632,6 +640,60 @@ static char *read_ntfs_boot_sector(struct super_block *sb,
|
|||
return boot_sector;
|
||||
}
|
||||
|
||||
static bool ntfs_validate_mft_io_geometry(struct ntfs_volume *vol,
|
||||
const unsigned int logical_block_size)
|
||||
{
|
||||
struct super_block *sb = vol->sb;
|
||||
u32 mft_io_unit_size = 0;
|
||||
bool mft_write_supported = true;
|
||||
|
||||
if (!is_power_of_2(logical_block_size) ||
|
||||
logical_block_size > PAGE_SIZE ||
|
||||
!is_power_of_2(vol->sector_size) ||
|
||||
vol->sector_size < logical_block_size ||
|
||||
vol->sector_size % logical_block_size ||
|
||||
!is_power_of_2(vol->cluster_size) ||
|
||||
!is_power_of_2(vol->mft_record_size) ||
|
||||
vol->mft_record_size > PAGE_SIZE)
|
||||
goto err;
|
||||
|
||||
mft_io_unit_size = max(logical_block_size, vol->mft_record_size);
|
||||
if (mft_io_unit_size > PAGE_SIZE ||
|
||||
mft_io_unit_size % logical_block_size ||
|
||||
mft_io_unit_size % vol->mft_record_size)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* The current direct-write path stores at most two MFT runlist
|
||||
* segments. Keep valid but larger records read-only until that path
|
||||
* can map an arbitrary number of segments.
|
||||
*/
|
||||
if (vol->mft_record_size > 2 * (u64)vol->cluster_size)
|
||||
mft_write_supported = false;
|
||||
|
||||
/*
|
||||
* A containing device block is currently mapped through one MFT
|
||||
* runlist element. Keep valid geometries that require crossing a
|
||||
* cluster read-only until the mapping is generalized.
|
||||
*/
|
||||
if (mft_io_unit_size > vol->mft_record_size &&
|
||||
(vol->cluster_size < mft_io_unit_size ||
|
||||
vol->cluster_size % mft_io_unit_size))
|
||||
mft_write_supported = false;
|
||||
|
||||
vol->mft_io_unit_size = mft_io_unit_size;
|
||||
vol->mft_write_supported = mft_write_supported;
|
||||
return true;
|
||||
|
||||
err:
|
||||
ntfs_error(sb,
|
||||
"Unsupported MFT I/O geometry (logical %u, block %lu, sector %u, cluster %u, MFT record %u, I/O unit %u).",
|
||||
logical_block_size, sb->s_blocksize,
|
||||
(unsigned int)vol->sector_size, vol->cluster_size,
|
||||
vol->mft_record_size, mft_io_unit_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_ntfs_boot_sector - parse the boot sector and store the data in @vol
|
||||
* @vol: volume structure to initialise with data from boot sector
|
||||
|
|
@ -644,9 +706,11 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
|
|||
const struct ntfs_boot_sector *b)
|
||||
{
|
||||
unsigned int sectors_per_cluster, sectors_per_cluster_bits, nr_hidden_sects;
|
||||
unsigned int logical_block_size;
|
||||
int clusters_per_mft_record, clusters_per_index_record;
|
||||
u64 ll;
|
||||
|
||||
logical_block_size = bdev_logical_block_size(vol->sb->s_bdev);
|
||||
vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector);
|
||||
vol->sector_size_bits = ffs(vol->sector_size) - 1;
|
||||
ntfs_debug("vol->sector_size = %i (0x%x)", vol->sector_size,
|
||||
|
|
@ -719,6 +783,9 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
|
|||
ntfs_warning(vol->sb, "Mft record size (%i) is smaller than the sector size (%i).",
|
||||
vol->mft_record_size, vol->sector_size);
|
||||
}
|
||||
if (!ntfs_validate_mft_io_geometry(vol, logical_block_size))
|
||||
return false;
|
||||
|
||||
clusters_per_index_record = b->clusters_per_index_record;
|
||||
ntfs_debug("clusters_per_index_record = %i (0x%x)",
|
||||
clusters_per_index_record, clusters_per_index_record);
|
||||
|
|
@ -2290,6 +2357,11 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
|||
ntfs_error(sb, "Unsupported NTFS filesystem.");
|
||||
goto err_out_now;
|
||||
}
|
||||
if (!vol->mft_write_supported && !sb_rdonly(sb)) {
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
ntfs_warning(sb,
|
||||
"MFT writeback is unsupported for this geometry. Mounting read-only.");
|
||||
}
|
||||
|
||||
if (vol->sector_size > blocksize) {
|
||||
blocksize = sb_set_blocksize(sb, vol->sector_size);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
* @mft_record_size: in bytes
|
||||
* @mft_record_size_mask: mft_record_size - 1
|
||||
* @mft_record_size_bits: log2(mft_record_size)
|
||||
* @mft_write_supported: Whether the MFT write paths support the geometry.
|
||||
* @index_record_size: in bytes
|
||||
* @index_record_size_mask: index_record_size - 1
|
||||
* @index_record_size_bits: log2(index_record_size)
|
||||
|
|
@ -111,6 +112,13 @@ struct ntfs_volume {
|
|||
u32 mft_record_size;
|
||||
u32 mft_record_size_mask;
|
||||
u8 mft_record_size_bits;
|
||||
bool mft_write_supported;
|
||||
/*
|
||||
* Unit size used for MFT I/O. This is the MFT record size when
|
||||
* it is at least as large as the device logical block, or the
|
||||
* containing device logical block when the record is smaller.
|
||||
*/
|
||||
u32 mft_io_unit_size;
|
||||
u32 index_record_size;
|
||||
u32 index_record_size_mask;
|
||||
u8 index_record_size_bits;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user