...

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

View as plain text