...

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

View as plain text