mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-11 05:43:16 -04:00
Merge tag 'ntfs-for-7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs
Pull ntfs fixes from Namjae Jeon: - Serialize truncate, fallocate, and mmap fault paths with invalidate_lock, avoiding mmap failures during concurrent size changes and exposure of uninitialized data during allocation - Correct fallocate signal and zeroing error handling - Fix FITRIM range alignment to prevent discard requests from extending into allocated clusters - Fix free-cluster accounting when cluster-freeing rollback or bitmap clearing fails - Keep volumes marked dirty when ntfs errors have been recorded - Compute bi_sector in 512-byte units, preventing silent corruption on 4Kn devices - Validate sectors_per_cluster values and prevent undefined shifts when parsing MFT and index record sizes - Bound $AttrDef traversal to the loaded table size - Fix MFT record resizing, memmove overlap, and kmap_local cleanup issues - Improve error propagation across attribute, EA, and reparse operations, including returning -ERANGE for undersized xattr buffers - Avoid modifying the HasEA flag when setxattr fails and return DT_UNKNOWN when directory inode lookup fails - Reduce contention in WOF decompression by performing block reads outside the decompression lock * tag 'ntfs-for-7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs: (23 commits) ntfs: take invalidate_lock in ntfs_filemap_page_mkwrite() ntfs: take invalidate_lock in ntfs_setattr_size() ntfs: handle signal interruption in fallocate ntfs: fix FITRIM range alignment ntfs: read WOF chunks outside the decompression lock ntfs: leave HasEA flag untouched on setxattr failure ntfs: fix race between fallocate and mmap reads ntfs: fix memmove overlap in ntfs_new_attr_flags ntfs: compute bi_sector in 512-byte units ntfs: reject invalid sectors_per_cluster in the boot sector ntfs: bound $AttrDef table walk to the loaded table size ntfs: fix undefined behavior in mft/index record size calculation ntfs: treat any nonzero dio zero-range return as an error ntfs: fix incorrect MFT record pointer passed to ntfs_attr_record_resize ntfs: do not mark the volume clean in sync_fs when errors were recorded ntfs: skip free cluster decrement when rollback fails ntfs: only count successfully cleared runs when freeing clusters ntfs: fix kmap_local leak in write_mft_record_nolock() error paths ntfs: return real error from ntfs_non_resident_attr_record_add() ntfs: preserve error code in ntfs_resident_attr_record_add() ...
This commit is contained in:
@@ -1737,8 +1737,8 @@ static struct attr_def *ntfs_attr_find_in_attrdef(const struct ntfs_volume *vol,
|
||||
struct attr_def *ad;
|
||||
|
||||
WARN_ON(!type);
|
||||
for (ad = vol->attrdef; (u8 *)ad - (u8 *)vol->attrdef <
|
||||
vol->attrdef_size && ad->type; ++ad) {
|
||||
for (ad = vol->attrdef; (u8 *)ad - (u8 *)vol->attrdef <=
|
||||
vol->attrdef_size - (s32)sizeof(*ad) && ad->type; ++ad) {
|
||||
/* We have not found it yet, carry on searching. */
|
||||
if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type)))
|
||||
continue;
|
||||
@@ -2500,7 +2500,7 @@ int ntfs_resident_attr_record_add(struct ntfs_inode *ni, __le32 type,
|
||||
return offset;
|
||||
put_err_out:
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return -EIO;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2639,7 +2639,7 @@ static int ntfs_non_resident_attr_record_add(struct ntfs_inode *ni, __le32 type,
|
||||
return offset;
|
||||
put_err_out:
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return -1;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5704,12 +5704,12 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
|
||||
lcn << vol->cluster_size_bits,
|
||||
alloc_cnt <<
|
||||
vol->cluster_size_bits);
|
||||
if (err > 0)
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (signal_pending(current))
|
||||
goto out;
|
||||
goto signal_out;
|
||||
|
||||
vcn += alloc_cnt;
|
||||
try_alloc_cnt -= alloc_cnt;
|
||||
@@ -5730,7 +5730,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
|
||||
up_write(&ni->runlist.lock);
|
||||
mutex_unlock(&ni->mrec_lock);
|
||||
if (err || signal_pending(current))
|
||||
goto out;
|
||||
goto signal_out;
|
||||
|
||||
vcn += alloc_cnt;
|
||||
try_alloc_cnt -= alloc_cnt;
|
||||
@@ -5756,4 +5756,8 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
|
||||
mutex_unlock(&ni->mrec_lock);
|
||||
out:
|
||||
return err >= 0 ? 0 : err;
|
||||
signal_out:
|
||||
if (!err)
|
||||
err = -EINTR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t s
|
||||
int error;
|
||||
struct bio *bio;
|
||||
blk_opf_t op;
|
||||
sector_t sector = start >> SECTOR_SHIFT;
|
||||
sector_t sector = ntfs_bytes_to_bio_sector(start);
|
||||
|
||||
if (start & (SECTOR_SIZE - 1))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -64,7 +64,7 @@ int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range)
|
||||
|
||||
end = start_buf;
|
||||
while (end < end_buf) {
|
||||
u64 aligned_start, aligned_count;
|
||||
u64 aligned_start, aligned_end, aligned_count;
|
||||
u64 start = find_next_zero_bit(bitmap, end_buf - start_buf,
|
||||
end - start_buf) + start_buf;
|
||||
if (start >= end_buf)
|
||||
@@ -74,8 +74,10 @@ int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range)
|
||||
start - start_buf) + start_buf;
|
||||
|
||||
aligned_start = ALIGN(ntfs_cluster_to_bytes(vol, start), dq);
|
||||
aligned_count =
|
||||
ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end - start), dq);
|
||||
aligned_end = ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end), dq);
|
||||
if (aligned_start >= aligned_end)
|
||||
continue;
|
||||
aligned_count = aligned_end - aligned_start;
|
||||
if (aligned_count >= range->minlen) {
|
||||
ret = blkdev_issue_discard(vol->sb->s_bdev, aligned_start >> 9,
|
||||
aligned_count >> 9, GFP_NOFS);
|
||||
|
||||
@@ -1414,7 +1414,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
|
||||
bio_pos = ntfs_cluster_to_bytes(vol, bio_lcn);
|
||||
bio = bio_alloc(vol->sb->s_bdev, DIV_ROUND_UP(bio_size, PAGE_SIZE),
|
||||
REQ_OP_WRITE, GFP_NOIO);
|
||||
bio->bi_iter.bi_sector = ntfs_bytes_to_sector(vol, bio_pos);
|
||||
bio->bi_iter.bi_sector = ntfs_bytes_to_bio_sector(bio_pos);
|
||||
|
||||
for (i = 0; bio_size; i++) {
|
||||
unsigned int len = min_t(unsigned int, bio_size, PAGE_SIZE);
|
||||
|
||||
49
fs/ntfs/ea.c
49
fs/ntfs/ea.c
@@ -404,10 +404,12 @@ static int ntfs_set_ea(struct inode *inode, const char *name, size_t name_len,
|
||||
*packed_ea_size = p_ea_info->ea_length;
|
||||
mark_mft_record_dirty(ni);
|
||||
out:
|
||||
if (ea_info_qsize > 0)
|
||||
NInoSetHasEA(ni);
|
||||
else
|
||||
NInoClearHasEA(ni);
|
||||
if (!err) {
|
||||
if (ea_info_qsize > 0)
|
||||
NInoSetHasEA(ni);
|
||||
else
|
||||
NInoClearHasEA(ni);
|
||||
}
|
||||
|
||||
kvfree(ea_buf);
|
||||
kvfree(old_ea_buf);
|
||||
@@ -615,7 +617,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler,
|
||||
if (!buffer) {
|
||||
err = sizeof(u8);
|
||||
} else if (size < sizeof(u8)) {
|
||||
err = -ENODATA;
|
||||
err = -ERANGE;
|
||||
} else {
|
||||
err = sizeof(u8);
|
||||
*(u8 *)buffer = (u8)(le32_to_cpu(ni->flags) & 0x3F);
|
||||
@@ -628,7 +630,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler,
|
||||
if (!buffer) {
|
||||
err = sizeof(u32);
|
||||
} else if (size < sizeof(u32)) {
|
||||
err = -ENODATA;
|
||||
err = -ERANGE;
|
||||
} else {
|
||||
err = sizeof(u32);
|
||||
*(u32 *)buffer = le32_to_cpu(ni->flags);
|
||||
@@ -753,18 +755,39 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
|
||||
old_arec_size = le32_to_cpu(a->length);
|
||||
|
||||
/*
|
||||
* Move payloads before shrinking the record. Otherwise resizing moves
|
||||
* Move payloads before shrinking the record. Otherwise resizing moves
|
||||
* the following attribute over the old payload before it can be copied.
|
||||
*
|
||||
* When offsets increase, move mapping_pairs first to avoid name
|
||||
* overwriting the start of mapping_pairs.
|
||||
*/
|
||||
if (arec_size < old_arec_size) {
|
||||
if (a->name_length && name_ofs != old_name_ofs)
|
||||
memmove((u8 *)a + name_ofs, (u8 *)a + old_name_ofs,
|
||||
a->name_length * sizeof(__le16));
|
||||
if (mp_ofs != old_mp_ofs)
|
||||
memmove((u8 *)a + mp_ofs, (u8 *)a + old_mp_ofs, mp_size);
|
||||
if (name_ofs > old_name_ofs) {
|
||||
/* Payload offsets increased: move mapping pairs first. */
|
||||
if (mp_ofs != old_mp_ofs)
|
||||
memmove((u8 *)a + mp_ofs,
|
||||
(u8 *)a + old_mp_ofs,
|
||||
mp_size);
|
||||
if (a->name_length && name_ofs != old_name_ofs)
|
||||
memmove((u8 *)a + name_ofs,
|
||||
(u8 *)a + old_name_ofs,
|
||||
a->name_length *
|
||||
sizeof(__le16));
|
||||
} else {
|
||||
/* Payload offsets decreased or unchanged: move name first. */
|
||||
if (a->name_length && name_ofs != old_name_ofs)
|
||||
memmove((u8 *)a + name_ofs,
|
||||
(u8 *)a + old_name_ofs,
|
||||
a->name_length *
|
||||
sizeof(__le16));
|
||||
if (mp_ofs != old_mp_ofs)
|
||||
memmove((u8 *)a + mp_ofs,
|
||||
(u8 *)a + old_mp_ofs,
|
||||
mp_size);
|
||||
}
|
||||
}
|
||||
|
||||
err = ntfs_attr_record_resize(m, a, arec_size);
|
||||
err = ntfs_attr_record_resize(ctx->mrec, a, arec_size);
|
||||
if (unlikely(err))
|
||||
goto err_out;
|
||||
|
||||
|
||||
@@ -270,18 +270,25 @@ static int ntfs_setattr_size(struct inode *vi, struct iattr *attr)
|
||||
return err;
|
||||
|
||||
inode_dio_wait(vi);
|
||||
|
||||
/*
|
||||
* Serialize with page faults and pagecache instantiation so that
|
||||
* readers cannot observe the size change until the attribute
|
||||
* updates below have completed.
|
||||
*/
|
||||
filemap_invalidate_lock(vi->i_mapping);
|
||||
if (attr->ia_size > old_size) {
|
||||
truncate_pagecache(vi, old_size);
|
||||
i_size_write(vi, attr->ia_size);
|
||||
pagecache_isize_extended(vi, old_size, attr->ia_size);
|
||||
} else
|
||||
} else {
|
||||
truncate_setsize(vi, attr->ia_size);
|
||||
}
|
||||
|
||||
err = ntfs_truncate_vfs(vi, attr->ia_size, old_size);
|
||||
if (err) {
|
||||
if (err)
|
||||
i_size_write(vi, old_size);
|
||||
return err;
|
||||
}
|
||||
filemap_invalidate_unlock(vi->i_mapping);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -669,6 +676,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
static vm_fault_t ntfs_filemap_page_mkwrite(struct vm_fault *vmf)
|
||||
{
|
||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
vm_fault_t ret;
|
||||
|
||||
if (NInoWofCompressed(NTFS_I(inode)))
|
||||
@@ -677,7 +685,14 @@ static vm_fault_t ntfs_filemap_page_mkwrite(struct vm_fault *vmf)
|
||||
sb_start_pagefault(inode->i_sb);
|
||||
file_update_time(vmf->vma->vm_file);
|
||||
|
||||
/*
|
||||
* Serialize against truncate/fallocate which hold the lock
|
||||
* exclusively while invalidating pagecache and changing extents.
|
||||
*/
|
||||
filemap_invalidate_lock_shared(mapping);
|
||||
ret = iomap_page_mkwrite(vmf, &ntfs_page_mkwrite_iomap_ops, NULL);
|
||||
filemap_invalidate_unlock_shared(mapping);
|
||||
|
||||
sb_end_pagefault(inode->i_sb);
|
||||
return ret;
|
||||
}
|
||||
@@ -1116,7 +1131,6 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le
|
||||
struct ntfs_volume *vol = ni->vol;
|
||||
int err = 0;
|
||||
loff_t old_size;
|
||||
bool map_locked = false;
|
||||
|
||||
if (mode & ~(NTFS_FALLOC_FL_SUPPORTED))
|
||||
return -EOPNOTSUPP;
|
||||
@@ -1148,16 +1162,13 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le
|
||||
|
||||
inode_lock(vi);
|
||||
if (NInoCompressed(ni) || NInoEncrypted(ni) || NInoWofCompressed(ni)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
inode_unlock(vi);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
inode_dio_wait(vi);
|
||||
if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE |
|
||||
FALLOC_FL_INSERT_RANGE)) {
|
||||
filemap_invalidate_lock(vi->i_mapping);
|
||||
map_locked = true;
|
||||
}
|
||||
/* Take invalidate_lock for all fallocate operations to prevent races */
|
||||
filemap_invalidate_lock(vi->i_mapping);
|
||||
|
||||
switch (mode & FALLOC_FL_MODE_MASK) {
|
||||
case FALLOC_FL_ALLOCATE_RANGE:
|
||||
@@ -1182,14 +1193,15 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le
|
||||
|
||||
err = file_modified(file);
|
||||
out:
|
||||
if (map_locked)
|
||||
filemap_invalidate_unlock(vi->i_mapping);
|
||||
if (!err && mode == 0 && NInoNonResident(ni) &&
|
||||
offset > old_size) {
|
||||
truncate_pagecache(vi, old_size);
|
||||
pagecache_isize_extended(vi, old_size, offset);
|
||||
}
|
||||
|
||||
filemap_invalidate_unlock(vi->i_mapping);
|
||||
|
||||
if (!err) {
|
||||
if (mode == 0 && NInoNonResident(ni) &&
|
||||
offset > old_size) {
|
||||
truncate_pagecache(vi, old_size);
|
||||
pagecache_isize_extended(vi, old_size, offset);
|
||||
}
|
||||
NInoSetFileNameDirty(ni);
|
||||
inode_set_mtime_to_ts(vi, inode_set_ctime_current(vi));
|
||||
mark_inode_dirty(vi);
|
||||
|
||||
@@ -1852,7 +1852,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
||||
struct mft_record *m = NULL;
|
||||
struct attr_record *a;
|
||||
struct ntfs_attr_search_ctx *ctx;
|
||||
unsigned int i, nr_blocks;
|
||||
unsigned int i;
|
||||
int err;
|
||||
size_t new_rl_count;
|
||||
|
||||
@@ -1896,11 +1896,6 @@ int ntfs_read_inode_mount(struct inode *vi)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Determine the first block of the $MFT/$DATA attribute. */
|
||||
nr_blocks = ntfs_bytes_to_sector(vol, vol->mft_record_size);
|
||||
if (!nr_blocks)
|
||||
nr_blocks = 1;
|
||||
|
||||
/* Load $MFT/$DATA's first mft record. */
|
||||
err = ntfs_bdev_read(sb->s_bdev, (char *)m,
|
||||
ntfs_cluster_to_bytes(vol, vol->mft_lcn), i);
|
||||
@@ -3780,8 +3775,7 @@ static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi,
|
||||
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE,
|
||||
GFP_NOIO);
|
||||
bio->bi_iter.bi_sector =
|
||||
ntfs_bytes_to_sector(vol,
|
||||
ntfs_cluster_to_bytes(vol, lcn) +
|
||||
ntfs_bytes_to_bio_sector(ntfs_cluster_to_bytes(vol, lcn) +
|
||||
lcn_folio_off);
|
||||
|
||||
length = min_t(unsigned long,
|
||||
|
||||
@@ -53,10 +53,10 @@ int ntfs_cluster_free_from_rl_nolock(struct ntfs_volume *vol,
|
||||
if (rl->lcn < 0)
|
||||
continue;
|
||||
err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length);
|
||||
if (unlikely(err && (!ret || ret == -ENOMEM) && ret != err))
|
||||
ret = err;
|
||||
else
|
||||
if (likely(!err))
|
||||
nr_freed += rl->length;
|
||||
else if (!ret || ret == -ENOMEM)
|
||||
ret = err;
|
||||
}
|
||||
ntfs_inc_free_clusters(vol, nr_freed);
|
||||
ntfs_debug("Done.");
|
||||
@@ -1045,8 +1045,9 @@ s64 __ntfs_cluster_free(struct ntfs_inode *ni, const s64 start_vcn, s64 count,
|
||||
"Failed to rollback (error %i). Leaving inconsistent metadata! Unmount and run chkdsk.",
|
||||
(int)delta);
|
||||
NVolSetErrors(vol);
|
||||
} else {
|
||||
ntfs_dec_free_clusters(vol, delta);
|
||||
}
|
||||
ntfs_dec_free_clusters(vol, delta);
|
||||
up_write(&vol->lcnbmp_lock);
|
||||
memalloc_nofs_restore(memalloc_flags);
|
||||
ntfs_error(vol->sb, "Aborting (error %i).", err);
|
||||
|
||||
@@ -499,8 +499,8 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no,
|
||||
|
||||
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO);
|
||||
bio->bi_iter.bi_sector =
|
||||
NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) +
|
||||
lcn_folio_off + folio_ofs);
|
||||
ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) +
|
||||
lcn_folio_off + folio_ofs);
|
||||
|
||||
if (bio_add_folio(bio, folio, vol->mft_record_size, folio_ofs))
|
||||
err = submit_bio_wait(bio);
|
||||
@@ -580,7 +580,7 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn
|
||||
err = pre_write_mst_fixup((struct ntfs_record *)fixup_m, vol->mft_record_size);
|
||||
if (err) {
|
||||
ntfs_error(vol->sb, "Failed to apply mst fixups!");
|
||||
goto err_out;
|
||||
goto unmap_err_out;
|
||||
}
|
||||
|
||||
folio_size = vol->mft_record_size / ni->mft_lcn_count;
|
||||
@@ -592,8 +592,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn
|
||||
|
||||
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO);
|
||||
bio->bi_iter.bi_sector =
|
||||
NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) +
|
||||
clu_off);
|
||||
ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) +
|
||||
clu_off);
|
||||
|
||||
if (!bio_add_folio(bio, folio, folio_size,
|
||||
ni->folio_ofs + offset)) {
|
||||
@@ -645,6 +645,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn
|
||||
return 0;
|
||||
put_bio_out:
|
||||
bio_put(bio);
|
||||
unmap_err_out:
|
||||
kunmap_local(kaddr);
|
||||
err_out:
|
||||
/*
|
||||
* The caller should mark the base inode as bad so no more I/O
|
||||
@@ -2740,8 +2742,8 @@ static int ntfs_write_mft_block(struct folio *folio, struct writeback_control *w
|
||||
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE,
|
||||
GFP_NOIO);
|
||||
bio->bi_iter.bi_sector =
|
||||
ntfs_bytes_to_sector(vol,
|
||||
ntfs_cluster_to_bytes(vol, lcn) + off);
|
||||
ntfs_bytes_to_bio_sector(
|
||||
ntfs_cluster_to_bytes(vol, lcn) + off);
|
||||
}
|
||||
|
||||
if (vol->cluster_size == NTFS_BLOCK_SIZE &&
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/nls.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/blk_types.h>
|
||||
#include <linux/uidgid.h>
|
||||
|
||||
#include "volume.h"
|
||||
@@ -71,8 +72,6 @@
|
||||
#define NTFS_CLU_TO_POFS(vol, clu) (((u64)(clu) << (vol)->cluster_size_bits) & \
|
||||
~PAGE_MASK)
|
||||
|
||||
#define NTFS_B_TO_SECTOR(vol, b) ((b) >> ((vol)->sb)->s_blocksize_bits)
|
||||
|
||||
enum {
|
||||
NTFS_BLOCK_SIZE = 512,
|
||||
NTFS_BLOCK_SIZE_BITS = 9,
|
||||
@@ -154,11 +153,10 @@ static inline u64 ntfs_cluster_to_poff(const struct ntfs_volume *vol,
|
||||
return (clu << vol->cluster_size_bits) & ~PAGE_MASK;
|
||||
}
|
||||
|
||||
/* Convert byte offset to sector (block) number. */
|
||||
static inline sector_t ntfs_bytes_to_sector(const struct ntfs_volume *vol,
|
||||
u64 bytes)
|
||||
/* Convert a byte offset on the volume to a bio sector number. */
|
||||
static inline sector_t ntfs_bytes_to_bio_sector(u64 bytes)
|
||||
{
|
||||
return bytes >> vol->sb->s_blocksize_bits;
|
||||
return bytes >> SECTOR_SHIFT;
|
||||
}
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
@@ -405,7 +405,7 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr
|
||||
|
||||
vi = ntfs_iget(vol->sb, mref);
|
||||
if (IS_ERR(vi))
|
||||
return PTR_ERR(vi);
|
||||
return DT_UNKNOWN;
|
||||
|
||||
reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi),
|
||||
AT_REPARSE_POINT, NULL, 0, &attr_size);
|
||||
@@ -694,8 +694,9 @@ static int update_reparse_data(struct ntfs_inode *ni, struct ntfs_index_context
|
||||
goto put_rp_inode;
|
||||
}
|
||||
|
||||
if (set_reparse_index(ni, xr, ((const struct reparse_point *)value)->reparse_tag) &&
|
||||
oldsize > 0) {
|
||||
err = set_reparse_index(ni, xr,
|
||||
((const struct reparse_point *)value)->reparse_tag);
|
||||
if (err && oldsize > 0) {
|
||||
/*
|
||||
* If cannot index, try to remove the reparse
|
||||
* data and log the error. There will be an
|
||||
|
||||
@@ -557,8 +557,8 @@ static bool is_boot_sector_ntfs(const struct super_block *sb,
|
||||
* Check sectors per cluster value is valid and the cluster size
|
||||
* is not above the maximum (2MB).
|
||||
*/
|
||||
if (b->bpb.sectors_per_cluster > 0x80 &&
|
||||
b->bpb.sectors_per_cluster < 0xf4)
|
||||
if (b->bpb.sectors_per_cluster < 0xf4 &&
|
||||
!is_power_of_2(b->bpb.sectors_per_cluster))
|
||||
goto not_ntfs;
|
||||
|
||||
/* Check reserved/unused fields are really zero. */
|
||||
@@ -695,7 +695,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
|
||||
* = -log2(mft_record_size) bytes. mft_record_size normaly is
|
||||
* 1024 bytes, which is encoded as 0xF6 (-10 in decimal).
|
||||
*/
|
||||
vol->mft_record_size = 1 << -clusters_per_mft_record;
|
||||
vol->mft_record_size = 1U << -clusters_per_mft_record;
|
||||
vol->mft_record_size_mask = vol->mft_record_size - 1;
|
||||
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
|
||||
ntfs_debug("vol->mft_record_size = %i (0x%x)", vol->mft_record_size,
|
||||
@@ -732,7 +732,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
|
||||
* index_record_size normaly equals 4096 bytes, which is
|
||||
* encoded as 0xF4 (-12 in decimal).
|
||||
*/
|
||||
vol->index_record_size = 1 << -clusters_per_index_record;
|
||||
vol->index_record_size = 1U << -clusters_per_index_record;
|
||||
vol->index_record_size_mask = vol->index_record_size - 1;
|
||||
vol->index_record_size_bits = ffs(vol->index_record_size) - 1;
|
||||
ntfs_debug("vol->index_record_size = %i (0x%x)",
|
||||
@@ -1241,9 +1241,9 @@ static bool load_and_init_attrdef(struct ntfs_volume *vol)
|
||||
goto failed;
|
||||
}
|
||||
NInoSetSparseDisabled(NTFS_I(ino));
|
||||
/* The size of FILE_AttrDef must be above 0 and fit inside 31 bits. */
|
||||
/* FILE_AttrDef must hold at least one entry and fit inside 31 bits. */
|
||||
i_size = i_size_read(ino);
|
||||
if (i_size <= 0 || i_size > 0x7fffffff)
|
||||
if (i_size < (s64)sizeof(struct attr_def) || i_size > 0x7fffffff)
|
||||
goto iput_failed;
|
||||
vol->attrdef = kvzalloc(i_size, GFP_NOFS);
|
||||
if (!vol->attrdef)
|
||||
@@ -1862,7 +1862,8 @@ static int ntfs_sync_fs(struct super_block *sb, int wait)
|
||||
return 0;
|
||||
|
||||
/* If there are some dirty buffers in the bdev inode */
|
||||
if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) {
|
||||
if (!NVolErrors(vol) &&
|
||||
ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) {
|
||||
ntfs_warning(sb, "Failed to clear dirty bit in volume information flags. Run chkdsk.");
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
127
fs/ntfs/wof.c
127
fs/ntfs/wof.c
@@ -39,8 +39,6 @@ struct ntfs_wof_workspace {
|
||||
struct mutex *lock;
|
||||
const struct ntfs_codec_ops *codec;
|
||||
u32 comp_unit;
|
||||
void *input;
|
||||
size_t input_size;
|
||||
void *output;
|
||||
void *scratch;
|
||||
};
|
||||
@@ -97,30 +95,36 @@ static struct ntfs_wof_workspace *ntfs_wof_workspace(u8 block_size_bits)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Size of the buffer a chunk is read into. A chunk is read straight off the
|
||||
* device, so the buffer has to hold @comp_unit bytes plus the leading partial
|
||||
* sector.
|
||||
*/
|
||||
static size_t ntfs_wof_input_size(const struct ntfs_wof_workspace *ws)
|
||||
{
|
||||
return round_up((size_t)ws->comp_unit + 511, 512);
|
||||
}
|
||||
|
||||
static int ntfs_wof_workspace_prepare(struct ntfs_wof_workspace *ws)
|
||||
{
|
||||
void *input, *output, *scratch;
|
||||
void *output, *scratch;
|
||||
size_t scratch_size;
|
||||
|
||||
if (ws->input)
|
||||
if (ws->output)
|
||||
return 0;
|
||||
|
||||
ws->input_size = round_up((size_t)ws->comp_unit + 511, 512);
|
||||
scratch_size = ws->codec->scratch_size(ws->comp_unit);
|
||||
if (!scratch_size)
|
||||
return -EINVAL;
|
||||
|
||||
input = kvmalloc(ws->input_size, GFP_NOFS);
|
||||
output = kvmalloc(ws->comp_unit, GFP_NOFS);
|
||||
scratch = kvzalloc(scratch_size, GFP_NOFS);
|
||||
if (!input || !output || !scratch) {
|
||||
kvfree(input);
|
||||
if (!output || !scratch) {
|
||||
kvfree(output);
|
||||
kvfree(scratch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ws->input = input;
|
||||
ws->output = output;
|
||||
ws->scratch = scratch;
|
||||
return 0;
|
||||
@@ -134,10 +138,8 @@ void ntfs_wof_free_workspaces(void)
|
||||
struct ntfs_wof_workspace *ws = ntfs_wof_workspaces[i];
|
||||
|
||||
mutex_lock(ws->lock);
|
||||
kvfree(ws->input);
|
||||
kvfree(ws->output);
|
||||
kvfree(ws->scratch);
|
||||
ws->input = NULL;
|
||||
ws->output = NULL;
|
||||
ws->scratch = NULL;
|
||||
mutex_unlock(ws->lock);
|
||||
@@ -602,6 +604,51 @@ static int ntfs_wof_try_direct(struct ntfs_wof_workspace *ws,
|
||||
chunk_end, src, src_len, dst_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decompress one chunk into @folio. Only this step needs the workspace, so it
|
||||
* is the only step that takes the workspace lock.
|
||||
*/
|
||||
static int ntfs_wof_decompress_chunk(struct ntfs_wof_workspace *ws,
|
||||
struct ntfs_volume *vol,
|
||||
struct address_space *mapping,
|
||||
struct folio *folio, loff_t folio_start,
|
||||
loff_t folio_end, u64 chunk_file_offset,
|
||||
char *chunk_mem, u32 chunk_size,
|
||||
u32 decomp_size)
|
||||
{
|
||||
loff_t chunk_end = chunk_file_offset + decomp_size;
|
||||
loff_t copy_start, copy_end;
|
||||
int err;
|
||||
|
||||
mutex_lock(ws->lock);
|
||||
err = ntfs_wof_workspace_prepare(ws);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
err = ntfs_wof_try_direct(ws, mapping, folio, chunk_file_offset,
|
||||
chunk_end, chunk_mem, chunk_size,
|
||||
decomp_size);
|
||||
if (err != -EAGAIN)
|
||||
goto out_unlock;
|
||||
|
||||
err = ntfs_wof_decode(ws, chunk_mem, chunk_size, ws->output,
|
||||
decomp_size);
|
||||
if (err) {
|
||||
ntfs_error(vol->sb, "Decompression failed: %d", err);
|
||||
err = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
copy_start = max_t(loff_t, folio_start, chunk_file_offset);
|
||||
copy_end = min_t(loff_t, folio_end, chunk_file_offset + decomp_size);
|
||||
memcpy_to_folio(folio, copy_start - folio_start,
|
||||
ws->output + copy_start - chunk_file_offset,
|
||||
copy_end - copy_start);
|
||||
out_unlock:
|
||||
mutex_unlock(ws->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ntfs_read_wof_compressed_block(struct folio *folio)
|
||||
{
|
||||
struct address_space *mapping = folio->mapping;
|
||||
@@ -613,6 +660,8 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
|
||||
loff_t folio_start = folio_pos(folio);
|
||||
loff_t folio_end = folio_next_pos(folio);
|
||||
char *chunk_mem;
|
||||
void *input;
|
||||
size_t input_size;
|
||||
u32 decomp_size;
|
||||
u64 chunk_count, chunk_idx, last_chunk, chunk_offset;
|
||||
int err = 0;
|
||||
@@ -652,10 +701,12 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
|
||||
goto out_iput;
|
||||
}
|
||||
|
||||
mutex_lock(ws->lock);
|
||||
err = ntfs_wof_workspace_prepare(ws);
|
||||
if (err)
|
||||
goto out_unlock_ws;
|
||||
input_size = ntfs_wof_input_size(ws);
|
||||
input = kvmalloc(input_size, GFP_NOFS);
|
||||
if (!input) {
|
||||
err = -ENOMEM;
|
||||
goto out_iput;
|
||||
}
|
||||
|
||||
chunk_idx = div_u64(folio_start, ws->comp_unit);
|
||||
last_chunk =
|
||||
@@ -663,55 +714,35 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
|
||||
chunk_count = DIV_ROUND_UP_ULL(i_size, ws->comp_unit);
|
||||
for (; chunk_idx <= last_chunk; chunk_idx++) {
|
||||
u32 chunk_size;
|
||||
u64 chunk_file_offset;
|
||||
loff_t chunk_end, copy_start, copy_end;
|
||||
|
||||
decomp_size = chunk_idx + 1 == chunk_count ?
|
||||
i_size - chunk_idx * ws->comp_unit :
|
||||
ws->comp_unit;
|
||||
err = parse_wof_chunk_table(ni, wof_ni, chunk_idx, chunk_count,
|
||||
decomp_size, &chunk_offset,
|
||||
&chunk_size, ws->input,
|
||||
ws->input_size);
|
||||
&chunk_size, input, input_size);
|
||||
if (err)
|
||||
goto out_unlock_ws;
|
||||
goto out_free_input;
|
||||
|
||||
err = ntfs_read_wof_chunk(vol, wof_ni, chunk_offset, chunk_size,
|
||||
ws->input, ws->input_size,
|
||||
&chunk_mem);
|
||||
input, input_size, &chunk_mem);
|
||||
if (err)
|
||||
goto out_unlock_ws;
|
||||
goto out_free_input;
|
||||
|
||||
chunk_file_offset = chunk_idx * ws->comp_unit;
|
||||
chunk_end = chunk_file_offset + decomp_size;
|
||||
err = ntfs_wof_try_direct(ws, mapping, folio, chunk_file_offset,
|
||||
chunk_end, chunk_mem, chunk_size,
|
||||
decomp_size);
|
||||
if (!err)
|
||||
continue;
|
||||
if (err != -EAGAIN)
|
||||
goto out_unlock_ws;
|
||||
|
||||
err = ntfs_wof_decode(ws, chunk_mem, chunk_size, ws->output,
|
||||
decomp_size);
|
||||
if (err) {
|
||||
ntfs_error(vol->sb, "Decompression failed: %d", err);
|
||||
err = -EINVAL;
|
||||
goto out_unlock_ws;
|
||||
}
|
||||
copy_start = max_t(loff_t, folio_start, chunk_file_offset);
|
||||
copy_end = min_t(loff_t, folio_end,
|
||||
chunk_file_offset + decomp_size);
|
||||
memcpy_to_folio(folio, copy_start - folio_start,
|
||||
ws->output + copy_start - chunk_file_offset,
|
||||
copy_end - copy_start);
|
||||
err = ntfs_wof_decompress_chunk(ws, vol, mapping, folio,
|
||||
folio_start, folio_end,
|
||||
chunk_idx * ws->comp_unit,
|
||||
chunk_mem, chunk_size,
|
||||
decomp_size);
|
||||
if (err)
|
||||
goto out_free_input;
|
||||
}
|
||||
|
||||
if (folio_end > i_size)
|
||||
folio_zero_segment(folio, i_size - folio_start,
|
||||
folio_size(folio));
|
||||
out_unlock_ws:
|
||||
mutex_unlock(ws->lock);
|
||||
out_free_input:
|
||||
kvfree(input);
|
||||
out_iput:
|
||||
iput(wof_inode);
|
||||
out:
|
||||
|
||||
Reference in New Issue
Block a user