...

Source file src/path/filepath/symlink_windows.go

     1	// Copyright 2012 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 filepath
     6	
     7	import (
     8		"strings"
     9		"syscall"
    10	)
    11	
    12	// normVolumeName is like VolumeName, but makes drive letter upper case.
    13	// result of EvalSymlinks must be unique, so we have
    14	// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
    15	func normVolumeName(path string) string {
    16		volume := VolumeName(path)
    17	
    18		if len(volume) > 2 { // isUNC
    19			return volume
    20		}
    21	
    22		return strings.ToUpper(volume)
    23	}
    24	
    25	// normBase returns the last element of path with correct case.
    26	func normBase(path string) (string, error) {
    27		p, err := syscall.UTF16PtrFromString(path)
    28		if err != nil {
    29			return "", err
    30		}
    31	
    32		var data syscall.Win32finddata
    33	
    34		h, err := syscall.FindFirstFile(p, &data)
    35		if err != nil {
    36			return "", err
    37		}
    38		syscall.FindClose(h)
    39	
    40		return syscall.UTF16ToString(data.FileName[:]), nil
    41	}
    42	
    43	// baseIsDotDot reports whether the last element of path is "..".
    44	// The given path should be 'Clean'-ed in advance.
    45	func baseIsDotDot(path string) bool {
    46		i := strings.LastIndexByte(path, Separator)
    47		return path[i+1:] == ".."
    48	}
    49	
    50	// toNorm returns the normalized path that is guaranteed to be unique.
    51	// It should accept the following formats:
    52	//   * UNC paths                              (e.g \\server\share\foo\bar)
    53	//   * absolute paths                         (e.g C:\foo\bar)
    54	//   * relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.)
    55	//   * relative paths begin with '\'          (e.g \foo\bar)
    56	//   * relative paths begin without '\'       (e.g foo\bar, ..\foo\bar, .., .)
    57	// The returned normalized path will be in the same form (of 5 listed above) as the input path.
    58	// If two paths A and B are indicating the same file with the same format, toNorm(A) should be equal to toNorm(B).
    59	// The normBase parameter should be equal to the normBase func, except for in tests.  See docs on the normBase func.
    60	func toNorm(path string, normBase func(string) (string, error)) (string, error) {
    61		if path == "" {
    62			return path, nil
    63		}
    64	
    65		path = Clean(path)
    66	
    67		volume := normVolumeName(path)
    68		path = path[len(volume):]
    69	
    70		// skip special cases
    71		if path == "." || path == `\` {
    72			return volume + path, nil
    73		}
    74	
    75		var normPath string
    76	
    77		for {
    78			if baseIsDotDot(path) {
    79				normPath = path + `\` + normPath
    80	
    81				break
    82			}
    83	
    84			name, err := normBase(volume + path)
    85			if err != nil {
    86				return "", err
    87			}
    88	
    89			normPath = name + `\` + normPath
    90	
    91			i := strings.LastIndexByte(path, Separator)
    92			if i == -1 {
    93				break
    94			}
    95			if i == 0 { // `\Go` or `C:\Go`
    96				normPath = `\` + normPath
    97	
    98				break
    99			}
   100	
   101			path = path[:i]
   102		}
   103	
   104		normPath = normPath[:len(normPath)-1] // remove trailing '\'
   105	
   106		return volume + normPath, nil
   107	}
   108	
   109	func evalSymlinks(path string) (string, error) {
   110		newpath, err := walkSymlinks(path)
   111		if err != nil {
   112			return "", err
   113		}
   114		newpath, err = toNorm(newpath, normBase)
   115		if err != nil {
   116			return "", err
   117		}
   118		return newpath, nil
   119	}
   120	

View as plain text