Skip to content

Fix casenorm ZTS tests - #19028

Open
ryao wants to merge 1 commit into
openzfs:masterfrom
ryao:casenorm
Open

ryao wants to merge 1 commit into
openzfs:masterfrom
ryao:casenorm

Conversation

@ryao

@ryao ryao commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Motivation and Context

It is about time we closed #7633.

Description

mixed_formd_lookup_ci and mixed_none_lookup_ci rely on the VFS supporting FIGNORECASE, which is only supported on illumos-gate. As for the others, we ->d_hash and ->d_compare in dentry_operations so the dentry cache works properly on case-insensitive filesystems on Linux.

How Has This Been Tested?

The ZTS casenorm group has been run against it on Linux and FreeBSD.

Types of Changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance enhancement (non-breaking change which improves efficiency)
  • Code cleanup (non-breaking change which makes code smaller or more readable)
  • Quality assurance (non-breaking change which makes the code more robust against bugs)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Library ABI change (libzfs, libzfs_core, libnvpair and libzfsbootenv)
  • Documentation (a change to man pages or other documentation)

Checklist

@behlendorf behlendorf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These has been disabled forever, I'm glad to see them get sorted out!

Comment thread module/os/linux/zfs/zpl_inode.c Outdated
Comment thread tests/zfs-tests/tests/functional/casenorm/mixed_formd_lookup_ci.ksh Outdated
@behlendorf behlendorf added Status: Code Review Needed Ready for review and testing Status: Revision Needed Changes are required for the PR to be accepted labels Sep 2, 2026
@github-actions github-actions Bot removed the Status: Revision Needed Changes are required for the PR to be accepted label Sep 7, 2026
@ryao

ryao commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor Author

I was inspired by #19074 to tackle this another way. Instead of bypassing the dentry cache like FreeBSD and illumos-gate do for namecache and DNLC, respectively, I implemented ->d_hash and ->d_compare in dentry_operations so the dentry cache works properly on case-insensitive filesystems on Linux.

Doing this in a Unicode-compatible way while operating without dynamic memory allocations in Linux's RCU context and keeping stack space usage down was challenging.

@ryao
ryao force-pushed the casenorm branch 2 times, most recently from 69d1806 to c5f2a5a Compare September 7, 2026 19:24
@ryao

This comment was marked as outdated.

@ryao

This comment was marked as outdated.

@ryao
ryao force-pushed the casenorm branch 6 times, most recently from 7d2eaa9 to 29748bf Compare September 8, 2026 16:58
@ryao

ryao commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Long story short, I caught some edge cases and fixed them.

The long story is: Without ->d_revalidate(), a cached negative dentry on a folded (normalized or case-insensitive) filesystem could change a file's name to something that the user did not intend in create/mkdir/link/symlink/mknod/rename. Implementing ->d_revalidate() to invalidate the dentry when it would be a problem works around that, but some in-tree stacking filesystems, such as NFSv2/3, do not pass the proper flags to us, so we need another workaround to invalidate when flags == 0 that unfortunately catches some cases where we do not want to invalidate.

Additionally, overlayfs checks whether a filesystem implements ->d_hash() and ->d_compare() and returns -EREMOTE, since stacking does not work properly when it has a different idea of what a dentry name is than the filesystem beneath it.

To work around the overlayfs issue on non-folding filesystems, I have implemented a version of dentry_operations for them that omits the new operations. This will still cause a regression for anyone using overlayfs on top of a folding filesystem, but that was subtly broken in the first place. That said, this change is useful since it prevents performance regressions on non-folded filesystems, and it let us remove some branching from these hooks on folded filesystems.

@ryao
ryao force-pushed the casenorm branch 3 times, most recently from 159d2ce to f3fc3e4 Compare September 8, 2026 19:38
mixed_formd_lookup_ci and mixed_none_lookup_ci rely on the VFS
supporting FIGNORECASE, which is only supported on illumos-gate, so we
move them to sunos.run.

As for the others, we only fail on Linux. To address that, we implement
the .d_hash and .d_compare dentry_operations functions so that the
dentry cache properly does case folding and normalization. This should
give case insensitive filesystems better lookup performance on Linux
than on FreeBSD, where the namecache is bypassed on case insensitive
filesystems due to a lack of support for case insensitivity. Closing the
performance gap on FreeBSD will either require changes to FreeBSD's VFS
or require us to bypass FreeBSD's namecache in favor of implementing our
own dentry cache.

To make this work, we extend u8_textprep_str() to properly update the
input size when encountering E2BIG, rather than leaving it undefined.

Overlayfs is not compatible with filesystems that implement folding, so
we maintain a version of dentry_operations for filesystems that do not
require it. This will cause a regression for those who have been using
overlayfs on top of folded filesystems. Those instances suffered from
edge cases that would likely break it. The only way to avoid this is to
add a dataset property that supports turning off the dentry_cache such
that we could use the regular zpl_dentry_operations struct. That would
allow overlayfs to work on folded filesystems again, although it would
still be broken on edge cases, so we do not do that.

Finally, we implement ->d_revalidate so that negative dentries do not
cause us to override the name given by a user when creating a file. For
example, we do not want stat FOO; touch foo to create a file named FOO.
This does not apply to renames replacing a file, but typically, a user
would want the file name to remain whatever was already there, so this
is considered okay.

Assisted-by: Grok 4.6 Build Beta
Assisted-by: Claude 5 Opus Max

Closes openzfs#7633
Signed-off-by: Richard Yao <richard@ryao.dev>

error = -zfs_lookup(ITOZ(dir), dname(dentry), &zp,
zfs_flags, cr, NULL, ppn);
zfs_flags, cr, NULL, NULL);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the only location where a pathname_t is passed. It'd be good to review the code and see if we can further simplify things by dropping this argument from zfs_lookup.

@ryao ryao Sep 17, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, but the current zfs_lookup() prototype is from illumos-gate. I am inclined to leave changing that to a future refactor of the platform-dependent / platform-independent line. I think @robn has some work in this area planned. Would you prefer we change that prototype as part of this, or leave it for a future refactor?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the Linux and FreeBSD prototypes have diverged from illumos-gate so it's already been changed. That said, I'm fine with leaving it for a future refactor. Let's just update the comment for the Linux version of zfs_lookup() to mention realpnp is unused and may be removed in a future refactoring.

/*
* Overlayfs is incompatible with filesystems that fold filenames, so we
* maintain a non-folding version for non-folding filesystems, so that it might
* work on top of them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update the casesensitivity / normalization descriptions in the zfsprops.7 man page to describe from a high-level user perspective how these settings interact with overlayfs on Linux.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I will do that in the next push.

}

if (error == -ENOENT)
return (d_splice_alias(NULL, dentry));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can shadow .zfs on case insensitive datasets. zfs_dirlook() matches .zfs with exact strcmp, so a lookup of .ZFS goes to the ZAP, gets ENOENT and now we cache a negative dentry for it. d_hash/d_compare treat .ZFS and .zfs as the same name, so the next lookup of .zfs hits that negative dentry and fails too, and d_revalidate keeps it since it is a plain lookup. Reproducer:

zfs create -o casesensitivity=insensitive tank/ci
stat /tank/ci/.ZFS    # ENOENT, fine
stat /tank/ci/.zfs    # ENOENT, wrong, works without this PR

Same for .zfs/SNAPSHOT then .zfs/snapshot via zpl_root_lookup(). Stays broken until the dentry is evicted. On TrueNAS this breaks SMB previous versions served from .zfs/snapshot.
Maybe skip caching the negative dentry here when the dir has ctldir and the name folds to .zfs, and return NULL on ENOENT in zpl_root_lookup().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. I am inclined to agree with your proposed solution. I will think about it a bit more and publish a revision to address it.

@behlendorf behlendorf added Status: Revision Needed Changes are required for the PR to be accepted and removed Status: Code Review Needed Ready for review and testing labels Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Status: Revision Needed Changes are required for the PR to be accepted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test case: casenorm test group

4 participants