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  }