diff --git a/lib/fs/util.go b/lib/fs/util.go index 8203160e6..2503fe270 100644 --- a/lib/fs/util.go +++ b/lib/fs/util.go @@ -44,13 +44,19 @@ func getHomeDir() (string, error) { return os.UserHomeDir() } -var windowsDisallowedCharacters = string([]rune{ - '<', '>', ':', '"', '|', '?', '*', - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, -}) +var ( + windowsDisallowedCharacters = string([]rune{ + '<', '>', ':', '"', '|', '?', '*', + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, + }) + windowsDisallowedNames = []string{"CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + } +) func WindowsInvalidFilename(name string) error { // None of the path components should end in space or period, or be a @@ -65,12 +71,15 @@ func WindowsInvalidFilename(name string) error { // Names ending in space or period are not valid. return errInvalidFilenameWindowsSpacePeriod } - switch strings.ToUpper(part) { - case "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9": - // These reserved names are not valid. - return errInvalidFilenameWindowsReservedName + upperCased := strings.ToUpper(part) + for _, disallowed := range windowsDisallowedNames { + if upperCased == disallowed { + return errInvalidFilenameWindowsReservedName + } + if strings.HasPrefix(upperCased, disallowed+".") { + // nul.txt.jpg is also disallowed + return errInvalidFilenameWindowsReservedName + } } } diff --git a/lib/fs/util_test.go b/lib/fs/util_test.go index 65a972334..93d62d0ee 100644 --- a/lib/fs/util_test.go +++ b/lib/fs/util_test.go @@ -44,3 +44,29 @@ func TestCommonPrefix(t *testing.T) { test(`Audrius`, `Audrius`, `Audrius`) test(`.`, `.`, `.`) } + +func TestWindowsInvalidFilename(t *testing.T) { + cases := []struct { + name string + err error + }{ + {`asdf.txt`, nil}, + {`nul`, errInvalidFilenameWindowsReservedName}, + {`nul.txt`, errInvalidFilenameWindowsReservedName}, + {`nul.jpg.txt`, errInvalidFilenameWindowsReservedName}, + {`some.nul.jpg`, nil}, + {`foo>bar.txt`, errInvalidFilenameWindowsReservedChar}, + {`foo \bar.txt`, errInvalidFilenameWindowsSpacePeriod}, + {`foo.\bar.txt`, errInvalidFilenameWindowsSpacePeriod}, + {`foo.d\bar.txt`, nil}, + {`foo.d\bar .txt`, nil}, + {`foo.d\bar. txt`, nil}, + } + + for _, tc := range cases { + err := WindowsInvalidFilename(tc.name) + if err != tc.err { + t.Errorf("For %q, got %v, expected %v", tc.name, err, tc.err) + } + } +}