...

Source file src/path/filepath/symlink.go

Documentation: path/filepath

     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  
    48  			// Set r to the index of the last slash in dest,
    49  			// after the volume.
    50  			var r int
    51  			for r = len(dest) - 1; r >= volLen; r-- {
    52  				if os.IsPathSeparator(dest[r]) {
    53  					break
    54  				}
    55  			}
    56  			if r < volLen || dest[r+1:] == ".." {
    57  				// Either path has no slashes
    58  				// (it's empty or just "C:")
    59  				// or it ends in a ".." we had to keep.
    60  				// Either way, keep this "..".
    61  				if len(dest) > volLen {
    62  					dest += pathSeparator
    63  				}
    64  				dest += ".."
    65  			} else {
    66  				// Discard everything since the last slash.
    67  				dest = dest[:r]
    68  			}
    69  			continue
    70  		}
    71  
    72  		// Ordinary path component. Add it to result.
    73  
    74  		if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
    75  			dest += pathSeparator
    76  		}
    77  
    78  		dest += path[start:end]
    79  
    80  		// Resolve symlink.
    81  
    82  		fi, err := os.Lstat(dest)
    83  		if err != nil {
    84  			return "", err
    85  		}
    86  
    87  		if fi.Mode()&os.ModeSymlink == 0 {
    88  			if !fi.Mode().IsDir() && end < len(path) {
    89  				return "", slashAfterFilePathError
    90  			}
    91  			continue
    92  		}
    93  
    94  		// Found symlink.
    95  
    96  		linksWalked++
    97  		if linksWalked > 255 {
    98  			return "", errors.New("EvalSymlinks: too many links")
    99  		}
   100  
   101  		link, err := os.Readlink(dest)
   102  		if err != nil {
   103  			return "", err
   104  		}
   105  
   106  		if isWindowsDot && !IsAbs(link) {
   107  			// On Windows, if "." is a relative symlink,
   108  			// just return ".".
   109  			break
   110  		}
   111  
   112  		path = link + path[end:]
   113  
   114  		v := volumeNameLen(link)
   115  		if v > 0 {
   116  			// Symlink to drive name is an absolute path.
   117  			if v < len(link) && os.IsPathSeparator(link[v]) {
   118  				v++
   119  			}
   120  			vol = link[:v]
   121  			dest = vol
   122  			end = len(vol)
   123  		} else if len(link) > 0 && os.IsPathSeparator(link[0]) {
   124  			// Symlink to absolute path.
   125  			dest = link[:1]
   126  			end = 1
   127  		} else {
   128  			// Symlink to relative path; replace last
   129  			// path component in dest.
   130  			var r int
   131  			for r = len(dest) - 1; r >= volLen; r-- {
   132  				if os.IsPathSeparator(dest[r]) {
   133  					break
   134  				}
   135  			}
   136  			if r < volLen {
   137  				dest = vol
   138  			} else {
   139  				dest = dest[:r]
   140  			}
   141  			end = 0
   142  		}
   143  	}
   144  	return Clean(dest), nil
   145  }
   146  

View as plain text