github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/path/filepath/symlink.go (about)

     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  	"errors"
     9  	"os"
    10  	"runtime"
    11  )
    12  
    13  func walkSymlinks(path string) (string, error) {
    14  	volLen := volumeNameLen(path)
    15  	pathSeparator := string(os.PathSeparator)
    16  
    17  	if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
    18  		volLen++
    19  	}
    20  	vol := path[:volLen]
    21  	dest := vol
    22  	linksWalked := 0
    23  	for start, end := volLen, volLen; start < len(path); start = end {
    24  		for start < len(path) && os.IsPathSeparator(path[start]) {
    25  			start++
    26  		}
    27  		end = start
    28  		for end < len(path) && !os.IsPathSeparator(path[end]) {
    29  			end++
    30  		}
    31  
    32  		// On Windows, "." can be a symlink.
    33  		// We look it up, and use the value if it is absolute.
    34  		// If not, we just return ".".
    35  		isWindowsDot := runtime.GOOS == "windows" && path[volumeNameLen(path):] == "."
    36  
    37  		// The next path component is in path[start:end].
    38  		if end == start {
    39  			// No more path components.
    40  			break
    41  		} else if path[start:end] == "." && !isWindowsDot {
    42  			// Ignore path component ".".
    43  			continue
    44  		} else if path[start:end] == ".." {
    45  			// Back up to previous component if possible.
    46  			// Note that volLen includes any leading slash.
    47  			var r int
    48  			for r = len(dest) - 1; r >= volLen; r-- {
    49  				if os.IsPathSeparator(dest[r]) {
    50  					break
    51  				}
    52  			}
    53  			if r < volLen {
    54  				if len(dest) > volLen {
    55  					dest += pathSeparator
    56  				}
    57  				dest += ".."
    58  			} else {
    59  				dest = dest[:r]
    60  			}
    61  			continue
    62  		}
    63  
    64  		// Ordinary path component. Add it to result.
    65  
    66  		if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
    67  			dest += pathSeparator
    68  		}
    69  
    70  		dest += path[start:end]
    71  
    72  		// Resolve symlink.
    73  
    74  		fi, err := os.Lstat(dest)
    75  		if err != nil {
    76  			return "", err
    77  		}
    78  
    79  		if fi.Mode()&os.ModeSymlink == 0 {
    80  			if !fi.Mode().IsDir() && end < len(path) {
    81  				return "", slashAfterFilePathError
    82  			}
    83  			continue
    84  		}
    85  
    86  		// Found symlink.
    87  
    88  		linksWalked++
    89  		if linksWalked > 255 {
    90  			return "", errors.New("EvalSymlinks: too many links")
    91  		}
    92  
    93  		link, err := os.Readlink(dest)
    94  		if err != nil {
    95  			return "", err
    96  		}
    97  
    98  		if isWindowsDot && !IsAbs(link) {
    99  			// On Windows, if "." is a relative symlink,
   100  			// just return ".".
   101  			break
   102  		}
   103  
   104  		path = link + path[end:]
   105  
   106  		v := volumeNameLen(link)
   107  		if v > 0 {
   108  			// Symlink to drive name is an absolute path.
   109  			if v < len(link) && os.IsPathSeparator(link[v]) {
   110  				v++
   111  			}
   112  			vol = link[:v]
   113  			dest = vol
   114  			end = len(vol)
   115  		} else if len(link) > 0 && os.IsPathSeparator(link[0]) {
   116  			// Symlink to absolute path.
   117  			dest = link[:1]
   118  			end = 1
   119  		} else {
   120  			// Symlink to relative path; replace last
   121  			// path component in dest.
   122  			var r int
   123  			for r = len(dest) - 1; r >= volLen; r-- {
   124  				if os.IsPathSeparator(dest[r]) {
   125  					break
   126  				}
   127  			}
   128  			if r < volLen {
   129  				dest = vol
   130  			} else {
   131  				dest = dest[:r]
   132  			}
   133  			end = 0
   134  		}
   135  	}
   136  	return Clean(dest), nil
   137  }