7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Hyunchul Lee [ Upstream commit dfe7ed335b686d53e4441a15382024058b664d3a ] 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: 115380f9a2f9 ("ntfs: update mft operations") Cc: stable@vger.kernel.org Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Sasha Levin --- fs/ntfs/super.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++-- fs/ntfs/volume.h | 8 +++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 4066bacabe377..99a95ee32f228 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -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); diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index bc85a95922458..8c17a66b4f22c 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -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; -- 2.53.0