mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			113 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   Copyright The containerd Authors.
 | 
						|
 | 
						|
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
   you may not use this file except in compliance with the License.
 | 
						|
   You may obtain a copy of the License at
 | 
						|
 | 
						|
       http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
   Unless required by applicable law or agreed to in writing, software
 | 
						|
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
   See the License for the specific language governing permissions and
 | 
						|
   limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package syscallx
 | 
						|
 | 
						|
import (
 | 
						|
	"syscall"
 | 
						|
	"unsafe"
 | 
						|
)
 | 
						|
 | 
						|
type reparseDataBuffer struct {
 | 
						|
	ReparseTag        uint32
 | 
						|
	ReparseDataLength uint16
 | 
						|
	Reserved          uint16
 | 
						|
 | 
						|
	// GenericReparseBuffer
 | 
						|
	reparseBuffer byte
 | 
						|
}
 | 
						|
 | 
						|
type mountPointReparseBuffer struct {
 | 
						|
	SubstituteNameOffset uint16
 | 
						|
	SubstituteNameLength uint16
 | 
						|
	PrintNameOffset      uint16
 | 
						|
	PrintNameLength      uint16
 | 
						|
	PathBuffer           [1]uint16
 | 
						|
}
 | 
						|
 | 
						|
type symbolicLinkReparseBuffer struct {
 | 
						|
	SubstituteNameOffset uint16
 | 
						|
	SubstituteNameLength uint16
 | 
						|
	PrintNameOffset      uint16
 | 
						|
	PrintNameLength      uint16
 | 
						|
	Flags                uint32
 | 
						|
	PathBuffer           [1]uint16
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	_IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
 | 
						|
	_SYMLINK_FLAG_RELATIVE      = 1
 | 
						|
)
 | 
						|
 | 
						|
// Readlink returns the destination of the named symbolic link.
 | 
						|
func Readlink(path string, buf []byte) (n int, err error) {
 | 
						|
	fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING,
 | 
						|
		syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
 | 
						|
	if err != nil {
 | 
						|
		return -1, err
 | 
						|
	}
 | 
						|
	defer syscall.CloseHandle(fd)
 | 
						|
 | 
						|
	rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
 | 
						|
	var bytesReturned uint32
 | 
						|
	err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
 | 
						|
	if err != nil {
 | 
						|
		return -1, err
 | 
						|
	}
 | 
						|
 | 
						|
	rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0]))
 | 
						|
	var s string
 | 
						|
	switch rdb.ReparseTag {
 | 
						|
	case syscall.IO_REPARSE_TAG_SYMLINK:
 | 
						|
		data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
 | 
						|
		p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
 | 
						|
		s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
 | 
						|
		if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 {
 | 
						|
			if len(s) >= 4 && s[:4] == `\??\` {
 | 
						|
				s = s[4:]
 | 
						|
				switch {
 | 
						|
				case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
 | 
						|
					// do nothing
 | 
						|
				case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
 | 
						|
					s = `\\` + s[4:]
 | 
						|
				default:
 | 
						|
					// unexpected; do nothing
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				// unexpected; do nothing
 | 
						|
			}
 | 
						|
		}
 | 
						|
	case _IO_REPARSE_TAG_MOUNT_POINT:
 | 
						|
		data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
 | 
						|
		p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
 | 
						|
		s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
 | 
						|
		if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar
 | 
						|
			if len(s) < 48 || s[:11] != `\??\Volume{` {
 | 
						|
				s = s[4:]
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			// unexpected; do nothing
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		// the path is not a symlink or junction but another type of reparse
 | 
						|
		// point
 | 
						|
		return -1, syscall.ENOENT
 | 
						|
	}
 | 
						|
	n = copy(buf, []byte(s))
 | 
						|
 | 
						|
	return n, nil
 | 
						|
}
 |