From fa8da6d4b5cdc704dc4db188de89cb23e89c8651 Mon Sep 17 00:00:00 2001 From: rootkiller6788 Date: Fri, 21 Aug 2026 21:30:14 +0800 Subject: [PATCH] cmd/fscrypt: accept non-ASCII mountpoints in MOUNTPOINT:ID flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The idFlagRegex only allowed ASCII printable characters in the mountpoint portion, so flags like --protector=/mnt/miroir-données:d77aa788ff9d1931 were rejected even though the filesystem path is valid. Use a character class that accepts any non-control Unicode character instead. Fixes #446 --- cmd/fscrypt/flags.go | 6 ++-- cmd/fscrypt/fscrypt_test.go | 67 ++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/cmd/fscrypt/flags.go b/cmd/fscrypt/flags.go index 3d3c51d5..022deb75 100644 --- a/cmd/fscrypt/flags.go +++ b/cmd/fscrypt/flags.go @@ -257,8 +257,10 @@ var ( ) // The first group is optional and corresponds to the mountpoint. The second -// group is required and corresponds to the descriptor. -var idFlagRegex = regexp.MustCompile("^([[:print:]]+):([[:alnum:]]+)$") +// group is required and corresponds to the descriptor. The mountpoint may +// contain any non-control Unicode character (e.g. accented letters in paths), +// so we use [^\p{Cc}] rather than the ASCII-only [[:print:]]. +var idFlagRegex = regexp.MustCompile(`^([^\p{Cc}]+):([[:alnum:]]+)$`) func matchMetadataFlag(flagValue string) (mountpoint, descriptor string, err error) { matches := idFlagRegex.FindStringSubmatch(flagValue) diff --git a/cmd/fscrypt/fscrypt_test.go b/cmd/fscrypt/fscrypt_test.go index 1d09bf8d..5148cb7b 100644 --- a/cmd/fscrypt/fscrypt_test.go +++ b/cmd/fscrypt/fscrypt_test.go @@ -19,6 +19,71 @@ package main -import "testing" +import ( + "strings" + "testing" +) func TestTrivial(t *testing.T) {} + +func TestMatchMetadataFlag(t *testing.T) { + testCases := []struct { + flagValue string + wantMountpoint string + wantDescriptor string + wantErrSubstring string + }{ + { + flagValue: "/mnt/ext4:c198cb2e6ceb4a12", + wantMountpoint: "/mnt/ext4", + wantDescriptor: "c198cb2e6ceb4a12", + }, + { + // The mountpoint may contain non-ASCII characters. + flagValue: "/mnt/miroir-données:d77aa788ff9d1931", + wantMountpoint: "/mnt/miroir-données", + wantDescriptor: "d77aa788ff9d1931", + }, + { + flagValue: "/mnt/naïve:abc123", + wantMountpoint: "/mnt/naïve", + wantDescriptor: "abc123", + }, + { + // Missing separator or descriptor is rejected. + flagValue: "abc", + wantErrSubstring: "does not have format", + }, + { + flagValue: ":abc", + wantErrSubstring: "does not have format", + }, + { + flagValue: "abc:", + wantErrSubstring: "does not have format", + }, + { + // Control characters in the mountpoint are rejected. + flagValue: "/mnt/path\nwithnewline:abc", + wantErrSubstring: "does not have format", + }, + } + for _, tc := range testCases { + mountpoint, descriptor, err := matchMetadataFlag(tc.flagValue) + if tc.wantErrSubstring != "" { + if err == nil || !strings.Contains(err.Error(), tc.wantErrSubstring) { + t.Errorf("matchMetadataFlag(%q) error = %v, want error containing %q", + tc.flagValue, err, tc.wantErrSubstring) + } + continue + } + if err != nil { + t.Errorf("matchMetadataFlag(%q) returned unexpected error: %v", tc.flagValue, err) + continue + } + if mountpoint != tc.wantMountpoint || descriptor != tc.wantDescriptor { + t.Errorf("matchMetadataFlag(%q) = (%q, %q), want (%q, %q)", + tc.flagValue, mountpoint, descriptor, tc.wantMountpoint, tc.wantDescriptor) + } + } +}