Skip to content

Commit 75a452d

Browse files
committed
Pull ntfs3 updates from Konstantin Komarov: "New code: - improve readahead for bitmap initialization and large directory scans - fsync files by syncing parent inodes - drop of preallocated clusters for sparse and compressed files - zero-fill folios beyond i_valid in ntfs_read_folio() - implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs - implement iomap-based file operations - allow explicit boolean acl/prealloc mount options - fall-through between switch labels - delayed-allocation (delalloc) support Fixes: - check return value of indx_find to avoid infinite loop - initialize new folios before use - infinite loop in attr_load_runs_range on inconsistent metadata - infinite loop triggered by zero-sized ATTR_LIST - ntfs_mount_options leak in ntfs_fill_super() - deadlock in ni_read_folio_cmpr - circular locking dependency in run_unpack_ex - prevent infinite loops caused by the next valid being the same - restore NULL folio initialization in ntfs_writepages() - slab-out-of-bounds read in DeleteIndexEntryRoot Updates: - allow readdir() to finish after directory mutations without rewinddir() - handle attr_set_size() errors when truncating files - make ntfs_writeback_ops static - refactor duplicate kmemdup pattern in do_action() - avoid calling run_get_entry() when run == NULL in ntfs_read_run_nb_ra() Replaced: - use wait_on_buffer() directly - rename ni_readpage_cmpr into ni_read_folio_cmpr" * tag 'ntfs3_for_7.0' of https://github.com/Paragon-Software-Group/linux-ntfs3: (26 commits) fs/ntfs3: add delayed-allocation (delalloc) support fs/ntfs3: avoid calling run_get_entry() when run == NULL in ntfs_read_run_nb_ra() fs/ntfs3: add fall-through between switch labels fs/ntfs3: allow explicit boolean acl/prealloc mount options fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot ntfs3: Restore NULL folio initialization in ntfs_writepages() ntfs3: Refactor duplicate kmemdup pattern in do_action() fs/ntfs3: prevent infinite loops caused by the next valid being the same fs/ntfs3: make ntfs_writeback_ops static ntfs3: fix circular locking dependency in run_unpack_ex fs/ntfs3: implement iomap-based file operations fs/ntfs3: fix deadlock in ni_read_folio_cmpr fs/ntfs3: implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs fs/ntfs3: zero-fill folios beyond i_valid in ntfs_read_folio() fs/ntfs3: handle attr_set_size() errors when truncating files fs/ntfs3: drop preallocated clusters for sparse and compressed files fs/ntfs3: fsync files by syncing parent inodes fs/ntfs3: fix ntfs_mount_options leak in ntfs_fill_super() fs/ntfs3: allow readdir() to finish after directory mutations without rewinddir() fs/ntfs3: improve readahead for bitmap initialization and large directory scans ...
2 parents 87a367f + 10d7c95 commit 75a452d

15 files changed

Lines changed: 1822 additions & 1134 deletions

File tree

fs/ntfs3/attrib.c

Lines changed: 282 additions & 130 deletions
Large diffs are not rendered by default.

fs/ntfs3/attrlist.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
5252

5353
if (!attr->non_res) {
5454
lsize = le32_to_cpu(attr->res.data_size);
55+
if (!lsize) {
56+
err = -EINVAL;
57+
goto out;
58+
}
59+
5560
/* attr is resident: lsize < record_size (1K or 4K) */
5661
le = kvmalloc(al_aligned(lsize), GFP_KERNEL);
5762
if (!le) {
@@ -66,6 +71,10 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
6671
u16 run_off = le16_to_cpu(attr->nres.run_off);
6772

6873
lsize = le64_to_cpu(attr->nres.data_size);
74+
if (!lsize) {
75+
err = -EINVAL;
76+
goto out;
77+
}
6978

7079
run_init(&ni->attr_list.run);
7180

@@ -336,8 +345,8 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
336345
le->id = id;
337346
memcpy(le->name, name, sizeof(short) * name_len);
338347

339-
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
340-
&new_size, true, &attr);
348+
err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
349+
&new_size, true, &attr, false);
341350
if (err) {
342351
/* Undo memmove above. */
343352
memmove(le, Add2Ptr(le, sz), old_size - off);
@@ -395,8 +404,8 @@ int al_update(struct ntfs_inode *ni, int sync)
395404
* Attribute list increased on demand in al_add_le.
396405
* Attribute list decreased here.
397406
*/
398-
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
399-
false, &attr);
407+
err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
408+
false, &attr, false);
400409
if (err)
401410
goto out;
402411

fs/ntfs3/bitmap.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,8 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
508508
size_t wpos, wbit, iw, vbo;
509509
struct buffer_head *bh = NULL;
510510
CLST lcn, clen;
511+
struct file_ra_state *ra;
512+
struct address_space *mapping = sb->s_bdev->bd_mapping;
511513

512514
wnd->uptodated = 0;
513515
wnd->extent_max = 0;
@@ -516,6 +518,13 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
516518

517519
vbo = 0;
518520

521+
/* Allocate in memory instead of stack. Not critical if failed. */
522+
ra = kzalloc(sizeof(*ra), GFP_NOFS);
523+
if (ra) {
524+
file_ra_state_init(ra, mapping);
525+
ra->ra_pages = (wnd->nbits / 8 + PAGE_SIZE - 1) >> PAGE_SHIFT;
526+
}
527+
519528
for (iw = 0; iw < wnd->nwnd; iw++) {
520529
if (iw + 1 == wnd->nwnd)
521530
wbits = wnd->bits_last;
@@ -552,6 +561,13 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
552561
len = ((u64)clen << cluster_bits) - off;
553562
}
554563

564+
if (ra) {
565+
pgoff_t idx = lbo >> PAGE_SHIFT;
566+
if (!ra_has_index(ra, idx))
567+
page_cache_sync_readahead(mapping, ra, NULL,
568+
idx, 1);
569+
}
570+
555571
bh = ntfs_bread(sb, lbo >> sb->s_blocksize_bits);
556572
if (!bh) {
557573
err = -EIO;
@@ -638,6 +654,7 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
638654
}
639655

640656
out:
657+
kfree(ra);
641658
return err;
642659
}
643660

fs/ntfs3/dir.c

Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -393,33 +393,77 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
393393
* ntfs_readdir - file_operations::iterate_shared
394394
*
395395
* Use non sorted enumeration.
396-
* We have an example of broken volume where sorted enumeration
397-
* counts each name twice.
396+
* Sorted enumeration may result infinite loop if names tree contains loop.
398397
*/
399398
static int ntfs_readdir(struct file *file, struct dir_context *ctx)
400399
{
401400
const struct INDEX_ROOT *root;
402-
u64 vbo;
403401
size_t bit;
404-
loff_t eod;
405402
int err = 0;
406403
struct inode *dir = file_inode(file);
407404
struct ntfs_inode *ni = ntfs_i(dir);
408405
struct super_block *sb = dir->i_sb;
409406
struct ntfs_sb_info *sbi = sb->s_fs_info;
410407
loff_t i_size = i_size_read(dir);
411-
u32 pos = ctx->pos;
408+
u64 pos = ctx->pos;
412409
u8 *name = NULL;
413410
struct indx_node *node = NULL;
414411
u8 index_bits = ni->dir.index_bits;
412+
size_t max_bit = i_size >> ni->dir.index_bits;
413+
loff_t eod = i_size + sbi->record_size;
415414

416415
/* Name is a buffer of PATH_MAX length. */
417416
static_assert(NTFS_NAME_LEN * 4 < PATH_MAX);
418417

419-
eod = i_size + sbi->record_size;
418+
if (!pos) {
419+
/*
420+
* ni->dir.version increments each directory change.
421+
* Save the initial value of ni->dir.version.
422+
*/
423+
file->private_data = (void *)ni->dir.version;
424+
}
420425

421-
if (pos >= eod)
422-
return 0;
426+
if (pos >= eod) {
427+
if (file->private_data == (void *)ni->dir.version) {
428+
/* No changes since first readdir. */
429+
return 0;
430+
}
431+
432+
/*
433+
* Handle directories that changed after the initial readdir().
434+
*
435+
* Some user space code implements recursive removal like this instead
436+
* of calling rmdir(2) directly:
437+
*
438+
* fd = opendir(path);
439+
* while ((dent = readdir(fd)))
440+
* unlinkat(dirfd(fd), dent->d_name, 0);
441+
* closedir(fd);
442+
*
443+
* POSIX leaves unspecified what readdir() should return once the
444+
* directory has been modified after opendir()/rewinddir(), so this
445+
* pattern is not guaranteed to work on all filesystems or platforms.
446+
*
447+
* In ntfs3 the internal name tree may be reshaped while entries are
448+
* being removed, so there is no stable anchor for continuing a
449+
* single-pass walk based on the original readdir() order.
450+
*
451+
* In practice some widely used tools (for example certain rm(1)
452+
* implementations) have used this readdir()/unlink() loop, and some
453+
* filesystems behave in a way that effectively makes it work in the
454+
* common case.
455+
*
456+
* The code below follows that practice and tries to provide
457+
* "rmdir-like" behaviour for such callers on ntfs3, even though the
458+
* situation is not strictly defined by the APIs.
459+
*
460+
* Apple documents the same readdir()/unlink() issue and a workaround
461+
* for HFS file systems in:
462+
* https://web.archive.org/web/20220122122948/https:/support.apple.com/kb/TA21420?locale=en_US
463+
*/
464+
ctx->pos = pos = 3;
465+
file->private_data = (void *)ni->dir.version;
466+
}
423467

424468
if (!dir_emit_dots(file, ctx))
425469
return 0;
@@ -454,58 +498,58 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
454498
if (pos >= sbi->record_size) {
455499
bit = (pos - sbi->record_size) >> index_bits;
456500
} else {
501+
/*
502+
* Add each name from root in 'ctx'.
503+
*/
457504
err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
458505
if (err)
459506
goto out;
460507
bit = 0;
461508
}
462509

463-
if (!i_size) {
464-
ctx->pos = eod;
465-
goto out;
466-
}
467-
468-
for (;;) {
469-
vbo = (u64)bit << index_bits;
470-
if (vbo >= i_size) {
471-
ctx->pos = eod;
472-
goto out;
473-
}
474-
510+
/*
511+
* Enumerate indexes until the end of dir.
512+
*/
513+
for (; bit < max_bit; bit += 1) {
514+
/* Get the next used index. */
475515
err = indx_used_bit(&ni->dir, ni, &bit);
476516
if (err)
477517
goto out;
478518

479519
if (bit == MINUS_ONE_T) {
480-
ctx->pos = eod;
481-
goto out;
520+
/* no more used indexes. end of dir. */
521+
break;
482522
}
483523

484-
vbo = (u64)bit << index_bits;
485-
if (vbo >= i_size) {
524+
if (bit >= max_bit) {
525+
/* Corrupted directory. */
486526
err = -EINVAL;
487527
goto out;
488528
}
489529

490-
err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
491-
&node);
530+
err = indx_read_ra(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
531+
&node, &file->f_ra);
492532
if (err)
493533
goto out;
494534

535+
/*
536+
* Add each name from index in 'ctx'.
537+
*/
495538
err = ntfs_read_hdr(sbi, ni, &node->index->ihdr,
496-
vbo + sbi->record_size, pos, name, ctx);
539+
((u64)bit << index_bits) + sbi->record_size,
540+
pos, name, ctx);
497541
if (err)
498542
goto out;
499-
500-
bit += 1;
501543
}
502544

503545
out:
504-
505546
kfree(name);
506547
put_indx_node(node);
507548

508-
if (err == 1) {
549+
if (!err) {
550+
/* End of directory. */
551+
ctx->pos = eod;
552+
} else if (err == 1) {
509553
/* 'ctx' is full. */
510554
err = 0;
511555
} else if (err == -ENOENT) {
@@ -624,7 +668,7 @@ const struct file_operations ntfs_dir_operations = {
624668
.llseek = generic_file_llseek,
625669
.read = generic_read_dir,
626670
.iterate_shared = ntfs_readdir,
627-
.fsync = generic_file_fsync,
671+
.fsync = ntfs_file_fsync,
628672
.open = ntfs_file_open,
629673
.unlocked_ioctl = ntfs_ioctl,
630674
#ifdef CONFIG_COMPAT

0 commit comments

Comments
 (0)