mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			169 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package symlink // import "github.com/docker/docker/pkg/symlink"
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"errors"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/docker/docker/pkg/longpath"
 | 
						|
	"golang.org/x/sys/windows"
 | 
						|
)
 | 
						|
 | 
						|
func toShort(path string) (string, error) {
 | 
						|
	p, err := windows.UTF16FromString(path)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	b := p // GetShortPathName says we can reuse buffer
 | 
						|
	n, err := windows.GetShortPathName(&p[0], &b[0], uint32(len(b)))
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	if n > uint32(len(b)) {
 | 
						|
		b = make([]uint16, n)
 | 
						|
		if _, err = windows.GetShortPathName(&p[0], &b[0], uint32(len(b))); err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return windows.UTF16ToString(b), nil
 | 
						|
}
 | 
						|
 | 
						|
func toLong(path string) (string, error) {
 | 
						|
	p, err := windows.UTF16FromString(path)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	b := p // GetLongPathName says we can reuse buffer
 | 
						|
	n, err := windows.GetLongPathName(&p[0], &b[0], uint32(len(b)))
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	if n > uint32(len(b)) {
 | 
						|
		b = make([]uint16, n)
 | 
						|
		n, err = windows.GetLongPathName(&p[0], &b[0], uint32(len(b)))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	b = b[:n]
 | 
						|
	return windows.UTF16ToString(b), nil
 | 
						|
}
 | 
						|
 | 
						|
func evalSymlinks(path string) (string, error) {
 | 
						|
	path, err := walkSymlinks(path)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
 | 
						|
	p, err := toShort(path)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	p, err = toLong(p)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	// windows.GetLongPathName does not change the case of the drive letter,
 | 
						|
	// but the result of EvalSymlinks must be unique, so we have
 | 
						|
	// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
 | 
						|
	// Make drive letter upper case.
 | 
						|
	if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
 | 
						|
		p = string(p[0]+'A'-'a') + p[1:]
 | 
						|
	} else if len(p) >= 6 && p[5] == ':' && 'a' <= p[4] && p[4] <= 'z' {
 | 
						|
		p = p[:3] + string(p[4]+'A'-'a') + p[5:]
 | 
						|
	}
 | 
						|
	return filepath.Clean(p), nil
 | 
						|
}
 | 
						|
 | 
						|
const utf8RuneSelf = 0x80
 | 
						|
 | 
						|
func walkSymlinks(path string) (string, error) {
 | 
						|
	const maxIter = 255
 | 
						|
	originalPath := path
 | 
						|
	// consume path by taking each frontmost path element,
 | 
						|
	// expanding it if it's a symlink, and appending it to b
 | 
						|
	var b bytes.Buffer
 | 
						|
	for n := 0; path != ""; n++ {
 | 
						|
		if n > maxIter {
 | 
						|
			return "", errors.New("EvalSymlinks: too many links in " + originalPath)
 | 
						|
		}
 | 
						|
 | 
						|
		// A path beginning with `\\?\` represents the root, so automatically
 | 
						|
		// skip that part and begin processing the next segment.
 | 
						|
		if strings.HasPrefix(path, longpath.Prefix) {
 | 
						|
			b.WriteString(longpath.Prefix)
 | 
						|
			path = path[4:]
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// find next path component, p
 | 
						|
		var i = -1
 | 
						|
		for j, c := range path {
 | 
						|
			if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) {
 | 
						|
				i = j
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
		var p string
 | 
						|
		if i == -1 {
 | 
						|
			p, path = path, ""
 | 
						|
		} else {
 | 
						|
			p, path = path[:i], path[i+1:]
 | 
						|
		}
 | 
						|
 | 
						|
		if p == "" {
 | 
						|
			if b.Len() == 0 {
 | 
						|
				// must be absolute path
 | 
						|
				b.WriteRune(filepath.Separator)
 | 
						|
			}
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// If this is the first segment after the long path prefix, accept the
 | 
						|
		// current segment as a volume root or UNC share and move on to the next.
 | 
						|
		if b.String() == longpath.Prefix {
 | 
						|
			b.WriteString(p)
 | 
						|
			b.WriteRune(filepath.Separator)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		fi, err := os.Lstat(b.String() + p)
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if fi.Mode()&os.ModeSymlink == 0 {
 | 
						|
			b.WriteString(p)
 | 
						|
			if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') {
 | 
						|
				b.WriteRune(filepath.Separator)
 | 
						|
			}
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// it's a symlink, put it at the front of path
 | 
						|
		dest, err := os.Readlink(b.String() + p)
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if filepath.IsAbs(dest) || os.IsPathSeparator(dest[0]) {
 | 
						|
			b.Reset()
 | 
						|
		}
 | 
						|
		path = dest + string(filepath.Separator) + path
 | 
						|
	}
 | 
						|
	return filepath.Clean(b.String()), nil
 | 
						|
}
 | 
						|
 | 
						|
func isDriveOrRoot(p string) bool {
 | 
						|
	if p == string(filepath.Separator) {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	length := len(p)
 | 
						|
	if length >= 2 {
 | 
						|
		if p[length-1] == ':' && (('a' <= p[length-2] && p[length-2] <= 'z') || ('A' <= p[length-2] && p[length-2] <= 'Z')) {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 |