--- /dev/null
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package cache
+
+import (
+ "fmt"
+ "path/filepath"
+ "syscall"
+)
+
+func init() {
+ checkPathCase = windowsCheckPathCase
+}
+
+func windowsCheckPathCase(path string) error {
+ // Back in the day, Windows used to have short and long filenames, and
+ // it still supports those APIs. GetLongPathName gets the real case for a
+ // path, so we can use it here. Inspired by
+ // http://stackoverflow.com/q/2113822.
+
+ // Short paths can be longer than long paths, and unicode, so be generous.
+ buflen := 4 * len(path)
+ namep, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return err
+ }
+ short := make([]uint16, buflen)
+ n, err := syscall.GetShortPathName(namep, &short[0], uint32(len(short)*2)) // buflen is in bytes.
+ if err != nil {
+ return err
+ }
+ if int(n) > len(short)*2 {
+ return fmt.Errorf("short buffer too short: %v vs %v*2", n, len(short))
+ }
+ long := make([]uint16, buflen)
+ n, err = syscall.GetLongPathName(&short[0], &long[0], uint32(len(long)*2))
+ if err != nil {
+ return err
+ }
+ if int(n) > len(long)*2 {
+ return fmt.Errorf("long buffer too short: %v vs %v*2", n, len(long))
+ }
+ longstr := syscall.UTF16ToString(long)
+
+ isRoot := func(p string) bool {
+ return p[len(p)-1] == filepath.Separator
+ }
+ for got, want := path, longstr; !isRoot(got) && !isRoot(want); got, want = filepath.Dir(got), filepath.Dir(want) {
+ if g, w := filepath.Base(got), filepath.Base(want); g != w {
+ return fmt.Errorf("case mismatch in path %q: component %q should be %q", path, g, w)
+ }
+ }
+ return nil
+}