From e10e72f69734a90c8719d160e8efb164ce5d9e26 Mon Sep 17 00:00:00 2001 From: Ziyi Guo Date: Tue, 10 Feb 2026 15:56:34 +0000 Subject: [PATCH 01/18] ntfs3: reject inodes with zero non-DOS link count ntfs_read_mft() counts file name attributes into two variables: names (all names including DOS 8.3) and links (non-DOS names only). The validation at line 424 checks names but set_nlink() at line 436 uses links. A corrupted NTFS image where all file name attributes have type FILE_NAME_DOS passes the names check but results in set_nlink(inode, 0). When such an inode is loaded via a code path that passes name=NULL to ntfs_iget5() and the nlink=0 inode enters the VFS. The subsequent unlink, rmdir, or rename targeting this inode calls drop_nlink() which triggers WARN_ON(inode->i_nlink == 0) in fs/inode.c. An all-DOS-name MFT record cannot exist on a valid NTFS volume. Reject such records by checking for links == 0 before calling set_nlink(). Signed-off-by: Ziyi Guo Signed-off-by: Konstantin Komarov --- fs/ntfs3/inode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 6e65066ebcc1..398913595a55 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -432,6 +432,11 @@ static struct inode *ntfs_read_mft(struct inode *inode, ni->mi.dirty = true; } + if (!links) { + err = -EINVAL; + goto out; + } + set_nlink(inode, links); if (S_ISDIR(mode)) { From 3a2141b2f1c34fda9a4e5af6df519656b5c47013 Mon Sep 17 00:00:00 2001 From: Adarsh Das Date: Sun, 8 Feb 2026 14:09:04 +0530 Subject: [PATCH 02/18] fs/ntfs3: resolve compare function in public index APIs Previously the comparator was stored in struct ntfs_index and used by low-level helpers such as hdr_find_e(). This creates a dependency on index state in private helpers. Resolve the compare function in the public index APIs and pass it explicitly to internal helpers. This should make the ownership of the comparator explicit and keeps low-level index code independent of index-root policy. This also resolves the TODO comment about dropping the stored comparator from struct ntfs_index. Signed-off-by: Adarsh Das Signed-off-by: Konstantin Komarov --- fs/ntfs3/index.c | 76 ++++++++++++++++++++++++++++++---------------- fs/ntfs3/ntfs_fs.h | 3 -- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 97f06c26fe1a..8b107b6714ce 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -714,10 +714,10 @@ static bool fnd_is_empty(struct ntfs_fnd *fnd) */ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, const struct INDEX_HDR *hdr, const void *key, - size_t key_len, const void *ctx, int *diff) + size_t key_len, const void *ctx, int *diff, + NTFS_CMP_FUNC cmp) { struct NTFS_DE *e, *found = NULL; - NTFS_CMP_FUNC cmp = indx->cmp; int min_idx = 0, mid_idx, max_idx = 0; int diff2; int table_size = 8; @@ -727,9 +727,6 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, u32 total = le32_to_cpu(hdr->total); u16 offs[128]; - if (unlikely(!cmp)) - return NULL; - fill_table: if (end > total) return NULL; @@ -800,7 +797,8 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, struct INDEX_HDR *hdr, const struct NTFS_DE *de, - struct NTFS_DE *before, const void *ctx) + struct NTFS_DE *before, const void *ctx, + NTFS_CMP_FUNC cmp) { int diff; size_t off = PtrOffset(hdr, before); @@ -823,7 +821,7 @@ static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, } /* No insert point is applied. Get it manually. */ before = hdr_find_e(indx, hdr, de + 1, le16_to_cpu(de->key_size), ctx, - &diff); + &diff, cmp); if (!before) return NULL; off = PtrOffset(hdr, before); @@ -915,10 +913,6 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi, init_rwsem(&indx->run_lock); - indx->cmp = get_cmp_func(root); - if (!indx->cmp) - goto out; - return 0; out: @@ -1141,6 +1135,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, int err; struct NTFS_DE *e; struct indx_node *node; + NTFS_CMP_FUNC cmp; if (!root) root = indx_get_root(&ni->dir, ni, NULL, NULL); @@ -1150,10 +1145,16 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, return -EINVAL; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + /* Check cache. */ e = fnd->level ? fnd->de[fnd->level - 1] : fnd->root_de; if (e && !de_is_last(e) && - !(*indx->cmp)(key, key_len, e + 1, le16_to_cpu(e->key_size), ctx)) { + !(*cmp)(key, key_len, e + 1, le16_to_cpu(e->key_size), ctx)) { *entry = e; *diff = 0; return 0; @@ -1163,7 +1164,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, fnd_clear(fnd); /* Lookup entry that is <= to the search value. */ - e = hdr_find_e(indx, &root->ihdr, key, key_len, ctx, diff); + e = hdr_find_e(indx, &root->ihdr, key, key_len, ctx, diff, cmp); if (!e) return -EINVAL; @@ -1183,7 +1184,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, /* Lookup entry that is <= to the search value. */ e = hdr_find_e(indx, &node->index->ihdr, key, key_len, ctx, - diff); + diff, cmp); if (!e) { put_indx_node(node); return -EINVAL; @@ -1585,7 +1586,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, const struct NTFS_DE *new_de, struct NTFS_DE *root_de, const void *ctx, - struct ntfs_fnd *fnd, bool undo) + struct ntfs_fnd *fnd, bool undo, NTFS_CMP_FUNC cmp) { int err = 0; struct NTFS_DE *e, *e0, *re; @@ -1626,7 +1627,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, if ((undo || asize + ds_root < sbi->max_bytes_per_attr) && mi_resize_attr(mi, attr, ds_root)) { hdr->total = cpu_to_le32(hdr_total + ds_root); - e = hdr_insert_de(indx, hdr, new_de, root_de, ctx); + e = hdr_insert_de(indx, hdr, new_de, root_de, ctx, cmp); WARN_ON(!e); fnd_clear(fnd); fnd->root_de = e; @@ -1767,7 +1768,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, * Now root is a parent for new index buffer. * Insert NewEntry a new buffer. */ - e = hdr_insert_de(indx, hdr, new_de, NULL, ctx); + e = hdr_insert_de(indx, hdr, new_de, NULL, ctx, cmp); if (!e) { err = -EINVAL; goto out_put_n; @@ -1797,7 +1798,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, static int indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, struct INDEX_ROOT *root, const struct NTFS_DE *new_de, - const void *ctx, int level, struct ntfs_fnd *fnd) + const void *ctx, int level, struct ntfs_fnd *fnd, NTFS_CMP_FUNC cmp) { int err; const struct NTFS_DE *sp; @@ -1814,7 +1815,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, /* Try the most easy case. */ e = fnd->level - 1 == level ? fnd->de[level] : NULL; - e = hdr_insert_de(indx, hdr1, new_de, e, ctx); + e = hdr_insert_de(indx, hdr1, new_de, e, ctx, cmp); fnd->de[level] = e; if (e) { /* Just write updated index into disk. */ @@ -1891,12 +1892,12 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, * (depending on sp <=> new_de). */ hdr_insert_de(indx, - (*indx->cmp)(new_de + 1, le16_to_cpu(new_de->key_size), + (*cmp)(new_de + 1, le16_to_cpu(new_de->key_size), up_e + 1, le16_to_cpu(up_e->key_size), ctx) < 0 ? hdr2 : hdr1, - new_de, NULL, ctx); + new_de, NULL, ctx, cmp); indx_mark_used(indx, ni, new_vbn >> indx->idx2vbn_bits); @@ -1911,14 +1912,14 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, */ if (!level) { /* Insert in root. */ - err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0); + err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0, cmp); } else { /* * The target buffer's parent is another index buffer. * TODO: Remove recursion. */ err = indx_insert_into_buffer(indx, ni, root, up_e, ctx, - level - 1, fnd); + level - 1, fnd, cmp); } if (err) { @@ -1952,6 +1953,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, struct NTFS_DE *e; struct ntfs_fnd *fnd_a = NULL; struct INDEX_ROOT *root; + NTFS_CMP_FUNC cmp; if (!fnd) { fnd_a = fnd_get(); @@ -1968,6 +1970,12 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, goto out; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + if (fnd_is_empty(fnd)) { /* * Find the spot the tree where we want to @@ -1991,13 +1999,13 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, * new entry into it. */ err = indx_insert_into_root(indx, ni, new_de, fnd->root_de, ctx, - fnd, undo); + fnd, undo, cmp); } else { /* * Found a leaf buffer, so we'll insert the new entry into it. */ err = indx_insert_into_buffer(indx, ni, root, new_de, ctx, - fnd->level - 1, fnd); + fnd->level - 1, fnd, cmp); } indx->version += 1; @@ -2291,6 +2299,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, u32 e_size, root_size, new_root_size; size_t trim_bit; const struct INDEX_NAMES *in; + NTFS_CMP_FUNC cmp; fnd = fnd_get(); if (!fnd) { @@ -2310,6 +2319,12 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, goto out; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + /* Locate the entry to remove. */ err = indx_find(indx, ni, root, key, key_len, ctx, &diff, &e, fnd); if (err) @@ -2376,9 +2391,9 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, err = level ? indx_insert_into_buffer(indx, ni, root, re, ctx, fnd->level - 1, - fnd) : + fnd, cmp) : indx_insert_into_root(indx, ni, re, e, - ctx, fnd, 0); + ctx, fnd, 0, cmp); kfree(re); if (err) @@ -2673,6 +2688,7 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, struct INDEX_ROOT *root; struct mft_inode *mi; struct ntfs_index *indx = &ni->dir; + NTFS_CMP_FUNC cmp; fnd = fnd_get(); if (!fnd) @@ -2684,6 +2700,12 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, goto out; } + cmp = get_cmp_func(root); + if (unlikely(!cmp)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + /* Find entry in directory. */ err = indx_find(indx, ni, root, fname, fname_full_size(fname), sbi, &diff, &e, fnd); diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index daf5a1f47275..3aa1d14479c8 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -196,9 +196,6 @@ struct ntfs_index { struct rw_semaphore run_lock; size_t version; /* increment each change */ - /*TODO: Remove 'cmp'. */ - NTFS_CMP_FUNC cmp; - u8 index_bits; // log2(root->index_block_size) u8 idx2vbn_bits; // log2(root->index_block_clst) u8 vbn2vbo_bits; // index_block_size < cluster? 9 : cluster_bits From e8619bcb08b3012117cb2dbac8d02e78a39646cc Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Sat, 7 Feb 2026 22:45:52 +0800 Subject: [PATCH 03/18] fs/ntfs3: return folios from ntfs_lock_new_page() ntfs_lock_new_page() currently returns a struct page * but it primarily operates on folios via __filemap_get_folio(). Convert it to return a struct folio * and use folio_alloc() + __folio_set_locked() for the temporary page used to avoid data corruption during decompression. When the cached folio is not uptodate, preserve the existing behavior by using folio_file_page() and converting the returned page back to a folio. Update ni_readpage_cmpr() and ni_decompress_file() to handle the new return type while keeping the existing struct page * array and the unlock_page()/put_page() cleanup paths unchanged. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202602072013.jwrURE2e-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202602071921.nGIiI1J5-lkp@intel.com/ Signed-off-by: Sun Jian [almaz.alexandrovich@paragon-software.com: removed ni_fiemap function, added reported-by and closes tags to commit] Signed-off-by: Konstantin Komarov --- fs/ntfs3/frecord.c | 47 +++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 2e901d073fe9..c0b9ca2426ab 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -1852,27 +1852,31 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, return REPARSE_LINK; } -static struct page *ntfs_lock_new_page(struct address_space *mapping, - pgoff_t index, gfp_t gfp) +static struct folio *ntfs_lock_new_page(struct address_space *mapping, + pgoff_t index, gfp_t gfp) { - struct folio *folio = __filemap_get_folio( - mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); - struct page *page; + struct folio *folio = __filemap_get_folio(mapping, index, + FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp); if (IS_ERR(folio)) - return ERR_CAST(folio); + return folio; - if (!folio_test_uptodate(folio)) - return folio_file_page(folio, index); + if (!folio_test_uptodate(folio)) { + struct page *page = folio_file_page(folio, index); + + if (IS_ERR(page)) + return ERR_CAST(page); + return page_folio(page); + } /* Use a temporary page to avoid data corruption */ folio_unlock(folio); folio_put(folio); - page = alloc_page(gfp); - if (!page) + folio = folio_alloc(gfp, 0); + if (!folio) return ERR_PTR(-ENOMEM); - __SetPageLocked(page); - return page; + __folio_set_locked(folio); + return folio; } /* @@ -1894,6 +1898,7 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio) u32 i, idx, frame_size, pages_per_frame; gfp_t gfp_mask; struct page *pg; + struct folio *f; if (vbo >= i_size_read(&ni->vfs_inode)) { folio_zero_range(folio, 0, folio_size(folio)); @@ -1929,12 +1934,12 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio) if (i == idx) continue; - pg = ntfs_lock_new_page(mapping, index, gfp_mask); - if (IS_ERR(pg)) { - err = PTR_ERR(pg); + f = ntfs_lock_new_page(mapping, index, gfp_mask); + if (IS_ERR(f)) { + err = PTR_ERR(f); goto out1; } - pages[i] = pg; + pages[i] = &f->page; } ni_lock(ni); @@ -2023,18 +2028,18 @@ int ni_decompress_file(struct ntfs_inode *ni) } for (i = 0; i < pages_per_frame; i++, index++) { - struct page *pg; + struct folio *f; - pg = ntfs_lock_new_page(mapping, index, gfp_mask); - if (IS_ERR(pg)) { + f = ntfs_lock_new_page(mapping, index, gfp_mask); + if (IS_ERR(f)) { while (i--) { unlock_page(pages[i]); put_page(pages[i]); } - err = PTR_ERR(pg); + err = PTR_ERR(f); goto out; } - pages[i] = pg; + pages[i] = &f->page; } err = ni_read_frame(ni, vbo, pages, pages_per_frame, 1); From 48d9b57b169fb39d7362034a32706453d107ed6e Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Mon, 2 Mar 2026 14:03:05 +0100 Subject: [PATCH 04/18] fs/ntfs3: add a subset of W=1 warnings for stricter checks Enable a subset of W=1-style compiler warnings for the ntfs3 tree so we catch small bugs early (unused symbols, missing declarations/prototypes, possible uninitialized/mis-sized uses, etc). Signed-off-by: Konstantin Komarov --- fs/ntfs3/Makefile | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fs/ntfs3/Makefile b/fs/ntfs3/Makefile index 279701b62bbe..53bf2c17ac28 100644 --- a/fs/ntfs3/Makefile +++ b/fs/ntfs3/Makefile @@ -3,6 +3,26 @@ # Makefile for the ntfs3 filesystem support. # +# Subset of W=1 warnings +subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter +subdir-ccflags-y += -Wmissing-declarations +subdir-ccflags-y += -Wmissing-format-attribute +subdir-ccflags-y += -Wmissing-prototypes +subdir-ccflags-y += -Wold-style-definition +subdir-ccflags-y += -Wmissing-include-dirs +condflags := \ + $(call cc-option, -Wunused-but-set-variable) \ + $(call cc-option, -Wunused-const-variable) \ + $(call cc-option, -Wpacked-not-aligned) \ + $(call cc-option, -Wstringop-truncation) \ + $(call cc-option, -Wmaybe-uninitialized) +subdir-ccflags-y += $(condflags) +# The following turn off the warnings enabled by -Wextra +subdir-ccflags-y += -Wno-missing-field-initializers +subdir-ccflags-y += -Wno-sign-compare +subdir-ccflags-y += -Wno-type-limits +subdir-ccflags-y += -Wno-shift-negative-value + # to check robot warnings ccflags-y += -Wint-to-pointer-cast \ $(call cc-option,-Wunused-but-set-variable,-Wunused-const-variable) \ From e98266e823a1fa06fe6499df61aeaac2fd6f7a49 Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Mon, 23 Feb 2026 16:01:13 +0800 Subject: [PATCH 05/18] fs/ntfs3: prevent uninitialized lcn caused by zero len syzbot reported a uninit-value in ntfs_iomap_begin [1]. Since runs was not touched yet, run_lookup_entry() immediately fails and returns false, which makes the value of "*len" 0. Simultaneously, the new value and err value are also 0, causing the logic in attr_data_get_block_locked() to jump directly to ok, ultimately resulting in *lcn being triggered before it is set [1]. In ntfs_iomap_begin(), the check for a 0 value in clen is moved forward to before updating lcn to avoid this [1]. [1] BUG: KMSAN: uninit-value in ntfs_iomap_begin+0x8c0/0x1460 fs/ntfs3/inode.c:825 ntfs_iomap_begin+0x8c0/0x1460 fs/ntfs3/inode.c:825 iomap_iter+0x9b7/0x1540 fs/iomap/iter.c:110 Local variable lcn created at: ntfs_iomap_begin+0x15d/0x1460 fs/ntfs3/inode.c:786 Fixes: 10d7c95af043 ("fs/ntfs3: add delayed-allocation (delalloc) support") Reported-by: syzbot+7be88937363ac7ab7bb0@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=7be88937363ac7ab7bb0 Tested-by: syzbot+7be88937363ac7ab7bb0@syzkaller.appspotmail.com Signed-off-by: Edward Adam Davis Signed-off-by: Konstantin Komarov --- fs/ntfs3/inode.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c index 398913595a55..733d4c86edba 100644 --- a/fs/ntfs3/inode.c +++ b/fs/ntfs3/inode.c @@ -827,6 +827,11 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, return err; } + if (!clen) { + /* broken file? */ + return -EINVAL; + } + if (lcn == EOF_LCN) { /* request out of file. */ if (flags & IOMAP_REPORT) { @@ -860,11 +865,6 @@ static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length, return 0; } - if (!clen) { - /* broken file? */ - return -EINVAL; - } - iomap->bdev = inode->i_sb->s_bdev; iomap->offset = offset; iomap->length = ((loff_t)clen << cluster_bits) - off; From 81ad9e67eccc0b094a6eef55a19ee56c761416dc Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Thu, 19 Mar 2026 14:29:26 +0100 Subject: [PATCH 06/18] fs/ntfs3: increase CLIENT_REC name field size This patch increases the size of the CLIENT_REC name field from 32 utf-16 chars to 64 utf-16 chars. It fixes the buffer overflow problem in log_replay() reported by Robbert Morris. Reported-by: Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 272e45276143..10dbe9922bf1 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -45,10 +45,10 @@ struct CLIENT_REC { __le16 seq_num; // 0x14: u8 align[6]; // 0x16: __le32 name_bytes; // 0x1C: In bytes. - __le16 name[32]; // 0x20: Name of client. + __le16 name[64]; // 0x20: Name of client. }; -static_assert(sizeof(struct CLIENT_REC) == 0x60); +static_assert(sizeof(struct CLIENT_REC) == 0xa0); /* Two copies of these will exist at the beginning of the log file */ struct RESTART_AREA { From d7ea8495fd307b58f8867acd81a1b40075b1d3ba Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Thu, 19 Mar 2026 13:15:46 +0530 Subject: [PATCH 07/18] fs/ntfs3: fix missing run load for vcn0 in attr_data_get_block_locked() When a compressed or sparse attribute has its clusters frame-aligned, vcn is rounded down to the frame start using cmask, which can result in vcn != vcn0. In this case, vcn and vcn0 may reside in different attribute segments. The code already handles the case where vcn is in a different segment by loading its runs before allocation. However, it fails to load runs for vcn0 when vcn0 resides in a different segment than vcn. This causes run_lookup_entry() to return SPARSE_LCN for vcn0 since its segment was never loaded into the in-memory run list, triggering the WARN_ON(1). Fix this by adding a missing check for vcn0 after the existing vcn segment check. If vcn0 falls outside the current segment range [svcn, evcn1), find and load the attribute segment containing vcn0 before performing the run lookup. The following scenario triggers the bug: attr_data_get_block_locked() vcn = vcn0 & cmask <- vcn != vcn0 after frame alignment load runs for vcn segment <- vcn0 segment not loaded! attr_allocate_clusters() <- allocation succeeds run_lookup_entry(vcn0) <- vcn0 not in run -> SPARSE_LCN WARN_ON(1) <- bug fires here! Reported-by: syzbot+c1e9aedbd913fadad617@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c1e9aedbd913fadad617 Fixes: c380b52f6c57 ("fs/ntfs3: Change new sparse cluster processing") Signed-off-by: Deepanshu Kartikey Signed-off-by: Konstantin Komarov --- fs/ntfs3/attrib.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 6cb9bc5d605c..76e581d3961d 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -1152,6 +1152,21 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, if (err) goto out; } + + if (vcn0 < svcn || evcn1 <= vcn0) { + struct ATTRIB *attr2; + + attr2 = ni_find_attr(ni, attr_b, &le_b, ATTR_DATA, NULL, + 0, &vcn0, &mi); + if (!attr2) { + err = -EINVAL; + goto out; + } + err = attr_load_runs(attr2, ni, run, NULL); + if (err) + goto out; + } + da = false; /* no delalloc for compressed file. */ } From f9963deaa891479da24e32fc614c08f158fe1608 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 12 Mar 2026 17:49:33 +0100 Subject: [PATCH 08/18] ntfs3: work around false-postive -Wmaybe-uninitialized warnings gcc sometimes fails to analyse how two local variables in ntfs_write_bh() are initialized, as the initialization happens only in the first pass through the main loop: fs/ntfs3/fsntfs.c: In function 'ntfs_write_bh': fs/ntfs3/fsntfs.c:1443:17: error: 'fixup' may be used uninitialized [-Werror=maybe-uninitialized] 1443 | __le16 *fixup; | ^~~~~ fs/ntfs3/fsntfs.c:1443:17: note: 'fixup' was declared here 1443 | __le16 *fixup; | ^~~~~ fs/ntfs3/fsntfs.c:1487:30: error: 'sample' may be used uninitialized [-Werror=maybe-uninitialized] 1487 | *ptr = sample; | ~~~~~^~~~~~~~ fs/ntfs3/fsntfs.c:1444:16: note: 'sample' was declared here 1444 | __le16 sample; Initializing the two variables to bogus values shuts up the warning and makes it clear that those cannot be used. I tried rearranging the loop to move the initialization in front of it, but couldn't quite figure it out. Fixes: 48d9b57b169f ("fs/ntfs3: add a subset of W=1 warnings for stricter checks") Signed-off-by: Arnd Bergmann Signed-off-by: Konstantin Komarov --- fs/ntfs3/fsntfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 0df2aa81d884..d0434756029b 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -1440,8 +1440,8 @@ int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr, u16 fo = le16_to_cpu(rhdr->fix_off); u16 fn = le16_to_cpu(rhdr->fix_num); u32 idx; - __le16 *fixup; - __le16 sample; + __le16 *fixup = NULL; + __le16 sample = cpu_to_le16(-1u); if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || fn * SECTOR_SIZE > bytes) { From 87ac077d6ea8613b7c1debdf3b5e92c78618fd23 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Mon, 23 Mar 2026 10:51:48 +0530 Subject: [PATCH 09/18] ntfs3: fix memory leak in indx_create_allocate() When indx_create_allocate() fails after attr_allocate_clusters() succeeds, run_deallocate() frees the disk clusters but never frees the memory allocated by run_add_entry() via kvmalloc() for the runs_tree structure. Fix this by adding run_close() at the out: label to free the run.runs memory on all error paths. The success path is unaffected as it returns 0 directly without going through out:, transferring ownership of the run memory to indx->alloc_run via memcpy(). Reported-by: syzbot+7adcddaeeb860e5d3f2f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=7adcddaeeb860e5d3f2f Signed-off-by: Deepanshu Kartikey Signed-off-by: Konstantin Komarov --- fs/ntfs3/index.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 8b107b6714ce..5344b29b0577 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1482,6 +1482,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, run_deallocate(sbi, &run, false); out: + run_close(&run); return err; } From 859d777646b56dd878b136392f3d03fb8153b559 Mon Sep 17 00:00:00 2001 From: 0xkato <0xkkato@gmail.com> Date: Sun, 29 Mar 2026 13:57:57 +0200 Subject: [PATCH 10/18] ntfs3: fix OOB write in attr_wof_frame_info() In attr_wof_frame_info(), the offset-table read range for a nonresident WofCompressedData stream is: u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1); u64 to = min(from + PAGE_SIZE, wof_size); ... ntfs_read_run(sbi, run, addr, from, to - from); A crafted image sets WofCompressedData.nres.data_size to 0xfff while the file is large enough to request frame 1024 (offset 0x400000). This gives from=0x1000, to=0xfff. The unsigned (to - from) wraps to 0xffffffffffffffff and ntfs_read_write_run() overflows the single-page offs_folio via memcpy. Triggered by pread() on a mounted NTFS image. Depending on adjacent memory layout at the time of the overflow, KASAN reports this as slab-out-of-bounds, use-after-free, or slab-use-after-free all at ntfs_read_write_run(). Secondary corruption/panic paths were also observed. Reject the read when the offset-table page is outside the stream. Signed-off-by: 0xkato <0xkkato@gmail.com> Signed-off-by: Konstantin Komarov --- fs/ntfs3/attrib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 76e581d3961d..6b5b58ebbf85 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -1591,6 +1591,12 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1); u64 to = min(from + PAGE_SIZE, wof_size); + if (from >= wof_size) { + _ntfs_bad_inode(&ni->vfs_inode); + err = -EINVAL; + goto out1; + } + err = attr_load_runs_range(ni, ATTR_DATA, WOF_NAME, ARRAY_SIZE(WOF_NAME), run, from, to); From bb82fe0872de867f87fd4f64c9cb157903ac78db Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Fri, 27 Mar 2026 11:24:54 +0800 Subject: [PATCH 11/18] fs/ntfs3: fix $LXDEV xattr lookup Use correct xattr name ("$LXDEV") and buffer size when calling ntfs_get_ea(), otherwise the attribute may not be read. Signed-off-by: Zhan Xusheng Signed-off-by: Konstantin Komarov --- fs/ntfs3/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c index 3fffda784892..9eeac0ab2b71 100644 --- a/fs/ntfs3/xattr.c +++ b/fs/ntfs3/xattr.c @@ -1031,7 +1031,7 @@ void ntfs_get_wsl_perm(struct inode *inode) i_gid_write(inode, (gid_t)le32_to_cpu(value[1])); inode->i_mode = le32_to_cpu(value[2]); - if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1, + if (ntfs_get_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value[0], sizeof(value), &sz) == sizeof(value[0])) { inode->i_rdev = le32_to_cpu(value[0]); From 6d979b64287fb051642fe0101f28c839be8ca837 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Tue, 31 Mar 2026 20:20:04 +0200 Subject: [PATCH 12/18] ntfs3: fix mount failure on volumes with fragmented MFT bitmap When the $MFT's $BITMAP attribute is fragmented across multiple MFT records (base record + extent records), ntfs_fill_super() fails with -ENOENT during wnd_init() because the MFT bitmap's run list only contains runs from the base MFT record. The issue is that wnd_init() (which calls wnd_rescan()) is invoked before ni_load_all_mi(), so the extent MFT records containing additional $BITMAP runs have not been loaded yet. When wnd_rescan() tries to look up a VCN beyond the base record's runs, run_lookup_entry() fails and returns -ENOENT. This affects NTFS volumes with a large or heavily fragmented MFT, which is common on long-used Windows systems where the MFT bitmap's run list doesn't fit in the base MFT record and spills into extent records. Fix this by: 1. Moving ni_load_all_mi() before wnd_init() so all extent records are available. 2. After ni_load_all_mi(), iterating through the attribute list to find any $BITMAP extent attributes and unpacking their runs into sbi->mft.bitmap.run before wnd_init() is called. Tested on a 664GB NTFS volume with 86 MFT bitmap runs spanning records 0 (VCN 0-105) and 17 (VCN 106-165). Before the fix, mount fails with -ENOENT. After the fix, mount succeeds and all read/write operations work correctly. Stress-tested with 8 test categories (large file integrity, 10K small files, copy, move, delete/recreate cycles, concurrent writes, deep directories, overwrite persistence). Signed-off-by: Ruslan Elishev Signed-off-by: Konstantin Komarov --- fs/ntfs3/super.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 174a7cb202a0..46160b06b635 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -1426,16 +1426,47 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) tt = inode->i_size >> sbi->record_bits; sbi->mft.next_free = MFT_REC_USER; - err = wnd_init(&sbi->mft.bitmap, sb, tt); - if (err) - goto put_inode_out; - err = ni_load_all_mi(ni); if (err) { ntfs_err(sb, "Failed to load $MFT's subrecords (%d).", err); goto put_inode_out; } + /* Merge MFT bitmap runs from extent records loaded by ni_load_all_mi. */ + { + struct ATTRIB *a = NULL; + struct ATTR_LIST_ENTRY *le = NULL; + + while ((a = ni_enum_attr_ex(ni, a, &le, NULL))) { + CLST svcn, evcn; + u16 roff; + + if (a->type != ATTR_BITMAP || !a->non_res) + continue; + + svcn = le64_to_cpu(a->nres.svcn); + if (!svcn) + continue; /* Base record runs already loaded. */ + + evcn = le64_to_cpu(a->nres.evcn); + roff = le16_to_cpu(a->nres.run_off); + + err = run_unpack_ex(&sbi->mft.bitmap.run, sbi, + MFT_REC_MFT, svcn, evcn, svcn, + Add2Ptr(a, roff), + le32_to_cpu(a->size) - roff); + if (err < 0) { + ntfs_err(sb, "Failed to unpack $MFT bitmap extent (%d).", err); + goto put_inode_out; + } + err = 0; + } + } + + err = wnd_init(&sbi->mft.bitmap, sb, tt); + if (err) + goto put_inode_out; + sbi->mft.ni = ni; /* Load $Bitmap. */ From b62567bca47408e6739dee75f02a2113548af875 Mon Sep 17 00:00:00 2001 From: Tobias Gaertner Date: Sun, 29 Mar 2026 04:17:02 -0700 Subject: [PATCH 13/18] ntfs3: add buffer boundary checks to run_unpack() run_unpack() checks `run_buf < run_last` at the top of the while loop but then reads size_size and offset_size bytes via run_unpack_s64() without verifying they fit within the remaining buffer. A crafted NTFS image with truncated run data in an MFT attribute triggers an OOB heap read of up to 15 bytes when the filesystem is mounted. Add boundary checks before each run_unpack_s64() call to ensure the declared field size does not exceed the remaining buffer. Found by fuzzing with a source-patched harness (LibAFL + QEMU). Fixes: 82cae269cfa95 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Signed-off-by: Tobias Gaertner Signed-off-by: Konstantin Komarov --- fs/ntfs3/run.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index c0324cdc174d..29817ecb1c7d 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1008,6 +1008,9 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, if (size_size > sizeof(len)) return -EINVAL; + if (run_buf + size_size > run_last) + return -EINVAL; + len = run_unpack_s64(run_buf, size_size, 0); /* Skip size_size. */ run_buf += size_size; @@ -1020,6 +1023,9 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, else if (offset_size <= sizeof(s64)) { s64 dlcn; + if (run_buf + offset_size > run_last) + return -EINVAL; + /* Initial value of dlcn is -1 or 0. */ dlcn = (run_buf[offset_size - 1] & 0x80) ? (s64)-1 : 0; dlcn = run_unpack_s64(run_buf, offset_size, dlcn); From 984a415f019536ea2d24de9010744e5302a9a948 Mon Sep 17 00:00:00 2001 From: Tobias Gaertner Date: Sun, 29 Mar 2026 04:17:03 -0700 Subject: [PATCH 14/18] ntfs3: fix integer overflow in run_unpack() volume boundary check The volume boundary check `lcn + len > sbi->used.bitmap.nbits` uses raw addition which can wrap around for large lcn and len values, bypassing the validation. Use check_add_overflow() as is already done for the adjacent prev_lcn + dlcn and vcn64 + len checks added by commit 3ac37e100385 ("ntfs3: Fix integer overflow in run_unpack()"). Found by fuzzing with a source-patched harness (LibAFL + QEMU). Fixes: 82cae269cfa95 ("fs/ntfs3: Add initialization of super block") Cc: stable@vger.kernel.org Signed-off-by: Tobias Gaertner Signed-off-by: Konstantin Komarov --- fs/ntfs3/run.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 29817ecb1c7d..1ce7d92fb274 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1065,9 +1065,15 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, return -EOPNOTSUPP; } #endif - if (lcn != SPARSE_LCN64 && lcn + len > sbi->used.bitmap.nbits) { - /* LCN range is out of volume. */ - return -EINVAL; + if (lcn != SPARSE_LCN64) { + u64 lcn_end; + + if (check_add_overflow(lcn, len, &lcn_end)) + return -EINVAL; + if (lcn_end > sbi->used.bitmap.nbits) { + /* LCN range is out of volume. */ + return -EINVAL; + } } if (!run) From d1062683bf6b560b31f287eb0ebde4841bc72376 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Thu, 26 Mar 2026 17:12:32 +0800 Subject: [PATCH 15/18] fs/ntfs3: fix potential double iput on d_make_root() failure d_make_root() consumes the reference to the passed inode: it either attaches it to the newly created dentry on success, or drops it via iput() on failure. In the error path, the code currently does: sb->s_root = d_make_root(inode); if (!sb->s_root) goto put_inode_out; which leads to a second iput(inode) in put_inode_out. This results in a double iput and may trigger a use-after-free if the inode gets freed after the first iput(). Fix this by jumping directly to the common cleanup path, avoiding the extra iput(inode). Signed-off-by: Zhan Xusheng Signed-off-by: Konstantin Komarov --- fs/ntfs3/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 46160b06b635..57922edf1ae1 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -1704,7 +1704,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; - goto put_inode_out; + goto out; } if (boot2) { From a6cd43fe9b083fa23fe1595666d5738856cb261a Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 27 Mar 2026 14:19:55 +0800 Subject: [PATCH 16/18] fs/ntfs3: terminate the cached volume label after UTF-8 conversion ntfs_fill_super() loads the on-disk volume label with utf16s_to_utf8s() and stores the result in sbi->volume.label. The converted label is later exposed through ntfs3_label_show() using %s, but utf16s_to_utf8s() only returns the number of bytes written and does not add a trailing NUL. If the converted label fills the entire fixed buffer, ntfs3_label_show() can read past the end of sbi->volume.label while looking for a terminator. Terminate the cached label explicitly after a successful conversion and clamp the exact-full case to the last byte of the buffer. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Signed-off-by: Pengpeng Hou Signed-off-by: Konstantin Komarov --- fs/ntfs3/super.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 57922edf1ae1..11027be3ee94 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -1339,8 +1339,13 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) le32_to_cpu(attr->res.data_size) >> 1, UTF16_LITTLE_ENDIAN, sbi->volume.label, sizeof(sbi->volume.label)); - if (err < 0) + if (err < 0) { sbi->volume.label[0] = 0; + } else if (err >= sizeof(sbi->volume.label)) { + sbi->volume.label[sizeof(sbi->volume.label) - 1] = 0; + } else { + sbi->volume.label[err] = 0; + } } else { /* Should we break mounting here? */ //err = -EINVAL; From 0ca0485e4b2e837ebb6cbd4f2451aba665a03e4b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 16:37:15 +0200 Subject: [PATCH 17/18] fs/ntfs3: validate rec->used in journal-replay file record check check_file_record() validates rec->total against the record size but never validates rec->used. The do_action() journal-replay handlers read rec->used from disk and use it to compute memmove lengths: DeleteAttribute: memmove(attr, ..., used - asize - roff) CreateAttribute: memmove(..., attr, used - roff) change_attr_size: memmove(..., used - PtrOffset(rec, next)) When rec->used is smaller than the offset of a validated attribute, or larger than the record size, these subtractions can underflow allowing us to copy huge amounts of memory in to a 4kb buffer, generally considered a bad idea overall. This requires a corrupted filesystem, which isn't a threat model the kernel really needs to worry about, but checking for such an obvious out-of-bounds value is good to keep things robust, especially on journal replay Fix this up by bounding rec->used correctly. This is much like commit b2bc7c44ed17 ("fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot") which checked different values in this same switch statement. Cc: Konstantin Komarov Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 10dbe9922bf1..acfa18b84401 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -2791,13 +2791,14 @@ static inline bool check_file_record(const struct MFT_REC *rec, u16 fn = le16_to_cpu(rec->rhdr.fix_num); u16 ao = le16_to_cpu(rec->attr_off); u32 rs = sbi->record_size; + u32 used = le32_to_cpu(rec->used); /* Check the file record header for consistency. */ if (rec->rhdr.sign != NTFS_FILE_SIGNATURE || fo > (SECTOR_SIZE - ((rs >> SECTOR_SHIFT) + 1) * sizeof(short)) || (fn - 1) * SECTOR_SIZE != rs || ao < MFTRECORD_FIXUP_OFFSET_1 || ao > sbi->record_size - SIZEOF_RESIDENT || !is_rec_inuse(rec) || - le32_to_cpu(rec->total) != rs) { + le32_to_cpu(rec->total) != rs || used > rs || used < ao) { return false; } @@ -2809,6 +2810,15 @@ static inline bool check_file_record(const struct MFT_REC *rec, return false; } + /* + * The do_action() handlers compute memmove lengths as + * "rec->used - ", which underflows when + * rec->used is smaller than the attribute walk reached. At this + * point attr is the ATTR_END marker; rec->used must cover it. + */ + if (used < PtrOffset(rec, attr) + sizeof(attr->type)) + return false; + return true; } From 819bd270abf9de3b7f306e233054b85a07c47820 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Wed, 15 Apr 2026 17:43:47 +0200 Subject: [PATCH 18/18] fs/ntfs3: fix Smatch warnings Initialize err in ni_allocate_da_blocks_locked() and correct the pre_alloc condition in attr_allocate_clusters(). Suggested-by: Dan Carpenter Signed-off-by: Konstantin Komarov --- fs/ntfs3/attrib.c | 2 +- fs/ntfs3/frecord.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 6b5b58ebbf85..e61c5bf7e27e 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -173,7 +173,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, if (err == -ENOSPC && pre) { pre = 0; - if (*pre_alloc) + if (pre_alloc) *pre_alloc = 0; continue; } diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index c0b9ca2426ab..7b035da63c12 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -3267,7 +3267,7 @@ int ni_allocate_da_blocks(struct ntfs_inode *ni) */ int ni_allocate_da_blocks_locked(struct ntfs_inode *ni) { - int err; + int err = 0; if (!ni->file.run_da.count) return 0;