golang.org/x/tools/gopls@v0.15.3/internal/cache/os_windows.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cache 6 7 import ( 8 "fmt" 9 "path/filepath" 10 "syscall" 11 ) 12 13 func init() { 14 checkPathValid = windowsCheckPathValid 15 } 16 17 func windowsCheckPathValid(path string) error { 18 // Back in the day, Windows used to have short and long filenames, and 19 // it still supports those APIs. GetLongPathName gets the real case for a 20 // path, so we can use it here. Inspired by 21 // http://stackoverflow.com/q/2113822. 22 23 // Short paths can be longer than long paths, and unicode, so be generous. 24 buflen := 4 * len(path) 25 namep, err := syscall.UTF16PtrFromString(path) 26 if err != nil { 27 return err 28 } 29 short := make([]uint16, buflen) 30 n, err := syscall.GetShortPathName(namep, &short[0], uint32(len(short)*2)) // buflen is in bytes. 31 if err != nil { 32 return err 33 } 34 if int(n) > len(short)*2 { 35 return fmt.Errorf("short buffer too short: %v vs %v*2", n, len(short)) 36 } 37 long := make([]uint16, buflen) 38 n, err = syscall.GetLongPathName(&short[0], &long[0], uint32(len(long)*2)) 39 if err != nil { 40 return err 41 } 42 if int(n) > len(long)*2 { 43 return fmt.Errorf("long buffer too short: %v vs %v*2", n, len(long)) 44 } 45 longstr := syscall.UTF16ToString(long) 46 47 isRoot := func(p string) bool { 48 return p[len(p)-1] == filepath.Separator 49 } 50 for got, want := path, longstr; !isRoot(got) && !isRoot(want); got, want = filepath.Dir(got), filepath.Dir(want) { 51 if g, w := filepath.Base(got), filepath.Base(want); g != w { 52 return fmt.Errorf("invalid path %q: component %q is listed by Windows as %q", path, g, w) 53 } 54 } 55 return nil 56 }