Skip to content

Conversation

rafjaf
Copy link
Contributor

@rafjaf rafjaf commented Jul 20, 2025

I rewrote PR #936 (where more information is given) to adapt it to version 2.6.0 (the dirstream fix had become obsolete since switching to the new go-fuse directory API).

@rfjakob
Copy link
Owner

rfjakob commented Jul 22, 2025

Traveling right now, can only review in detail in 2 weeks.

Some points already:

  • Commit message needs work
  • Please drop the migrate logic for now, this is too risky
  • Needs tests

This commit resolves rfjakob#850
by addressing Unicode normalization mismatches on macOS between NFC
(used by CLI tools) and NFD (used by GUI apps). The solution is inspired
by Cryptomator's approach.

Forward mode now enforces NFC for storage and adds fallback lookups in NFD.
Reverse mode adds fallback lookups without modifying the plaintext FS.
@rafjaf rafjaf force-pushed the rebase-onto-2.6.0 branch from 1867ecb to 58f1ec0 Compare July 23, 2025 20:22
@rafjaf
Copy link
Contributor Author

rafjaf commented Jul 23, 2025

Thanks, I have already changed the commit message and will implement the other changes asap

@rafjaf
Copy link
Contributor Author

rafjaf commented Jul 24, 2025

@rfjakob As requested, I have:

  • rewritten the commit message to make it more explicit
  • Dropped the migration logic
  • Added testing in /tests/macos_filename_encoding

Also, after further reflection, I have cancelled all changes that I had made to the reverse mode. Indeed, in reverse mode, the plaintext folder is just an ordinary folder, meaning that macos is already doing the normalization required; and as far as I can tell, the existing version of gocrypts has no problem to encode both NFC and NFD filenames to the ciphertext folder. The PR is therefore much simplified.

I hope this works for you and remain at your disposal to discuss this.
Thank you for your time

@rafjaf
Copy link
Contributor Author

rafjaf commented Jul 24, 2025

Sorry, I had forgotten to revert the changes in /internal/fusefrontend_reverse/node_dirs_ops.go, now all the changes in reverse mode should effectively be cancelled.

@rafjaf rafjaf changed the title Applying macos fix to version 2.6.0 Fix macos filename encoding (applied to version 2.6.0) Jul 25, 2025
@rafjaf rafjaf changed the title Fix macos filename encoding (applied to version 2.6.0) Fix macos filename encoding Jul 25, 2025
@rfjakob rfjakob requested a review from Copilot August 5, 2025 20:25
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes macOS filename encoding issues by implementing Unicode normalization fallback to handle the difference between NFC and NFD character encodings that macOS uses. The fix ensures files created with either normalization form can be accessed using the other form, improving compatibility with macOS filesystem conventions.

  • Implements Unicode normalization logic in prepareAtSyscall to try both NFC and NFD forms when accessing files
  • Normalizes filenames to NFC for internal storage while displaying them as NFD for macOS GUI compatibility
  • Adds comprehensive test suite covering various Unicode characters and edge cases

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/macos_filename_encoding/test.bash Test runner script for macOS filename encoding tests
tests/macos_filename_encoding/nfc_nfd_test.go Comprehensive test suite covering Unicode normalization scenarios
internal/fusefrontend/node_prepare_syscall.go Core Unicode normalization logic with NFC/NFD fallback
internal/fusefrontend/node_open_create.go Normalize filenames during file creation
internal/fusefrontend/node_dir_ops.go Directory operations and filename normalization utilities
internal/fusefrontend/node.go Normalize filenames in various file operations
internal/fusefrontend/file_dir_ops.go Directory listing with NFD display normalization
go.mod Updated Go version and added text/unicode dependency
Comments suppressed due to low confidence (2)

go.mod:3

  • Go version 1.23.0 does not exist. The latest stable Go version as of my knowledge cutoff is 1.22.x. Please use a valid Go version like 1.22 or 1.21.
go 1.23.0

go.mod:5

  • Go toolchain version 1.24.4 does not exist. The toolchain version should match an actual Go release version.
toolchain go1.24.4

}
}

return n.prepareAtSyscallDirect(child) // Non-macOS or fallback
Copy link

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

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

[nitpick] The logic for trying different normalization forms is complex and could be simplified. Consider extracting the normalization attempts into a separate helper function to improve readability and maintainability.

Suggested change
return n.prepareAtSyscallDirect(child) // Non-macOS or fallback
return n.tryNormalizationForms(child)
}
return n.prepareAtSyscallDirect(child) // Non-macOS or fallback
}
// tryNormalizationForms tries different Unicode normalization forms for macOS
func (n *Node) tryNormalizationForms(child string) (dirfd int, cName string, errno syscall.Errno) {
// Step 1: Always try NFC first (canonical storage form)
normalizedChild := norm.NFC.String(child)
dirfd, cName, errno = n.prepareAtSyscallDirect(normalizedChild)
if errno == 0 {
return dirfd, cName, 0 // Found NFC version
}
// Step 2: Try alternate form if input was different
if normalizedChild != child {
// Input was NFD, try original NFD form
dirfdNFD, cNameNFD, errnoNFD := n.prepareAtSyscallDirect(child)
if errnoNFD == 0 {
return dirfdNFD, cNameNFD, 0
}
}
// Step 3: If input was NFC, also try NFD as fallback
if normalizedChild == child {
nfdChild := norm.NFD.String(child)
if nfdChild != child {
dirfdNFD, cNameNFD, errnoNFD := n.prepareAtSyscallDirect(nfdChild)
if errnoNFD == 0 {
return dirfdNFD, cNameNFD, 0
}
}
}
return n.prepareAtSyscallDirect(child)

Copilot uses AI. Check for mistakes.

}

// Step 3: If input was NFC, also try NFD as fallback
if normalizedChild == child {
Copy link

Copilot AI Aug 5, 2025

Choose a reason for hiding this comment

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

This condition checks if input was NFC after already trying NFC and NFD. This creates redundant NFD normalization when the input was already NFC. Consider restructuring to avoid duplicate normalization calls.

Suggested change
if normalizedChild == child {
if normalizedChild != child {
// Input was NFD, try original NFD form
dirfdNFD, cNameNFD, errnoNFD := n.prepareAtSyscallDirect(child)
if errnoNFD == 0 {
return dirfdNFD, cNameNFD, 0
}
} else {
// Input was NFC, try NFD as fallback if different

Copilot uses AI. Check for mistakes.

@rafjaf
Copy link
Contributor Author

rafjaf commented Sep 13, 2025

@rfjakob Just to be sure, do you wish me to implement all or part of Copilot's suggestions, or do you prefer to take over from here?

@rfjakob
Copy link
Owner

rfjakob commented Sep 16, 2025

@rfjakob Just to be sure, do you wish me to implement all or part of Copilot's suggestions, or do you prefer to take over from here?

I'll take over

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants