...

Source file src/github.com/Microsoft/go-winio/reparse.go

Documentation: github.com/Microsoft/go-winio

     1  //go:build windows
     2  // +build windows
     3  
     4  package winio
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"strings"
    11  	"unicode/utf16"
    12  	"unsafe"
    13  )
    14  
    15  const (
    16  	reparseTagMountPoint = 0xA0000003
    17  	reparseTagSymlink    = 0xA000000C
    18  )
    19  
    20  type reparseDataBuffer struct {
    21  	ReparseTag           uint32
    22  	ReparseDataLength    uint16
    23  	Reserved             uint16
    24  	SubstituteNameOffset uint16
    25  	SubstituteNameLength uint16
    26  	PrintNameOffset      uint16
    27  	PrintNameLength      uint16
    28  }
    29  
    30  // ReparsePoint describes a Win32 symlink or mount point.
    31  type ReparsePoint struct {
    32  	Target       string
    33  	IsMountPoint bool
    34  }
    35  
    36  // UnsupportedReparsePointError is returned when trying to decode a non-symlink or
    37  // mount point reparse point.
    38  type UnsupportedReparsePointError struct {
    39  	Tag uint32
    40  }
    41  
    42  func (e *UnsupportedReparsePointError) Error() string {
    43  	return fmt.Sprintf("unsupported reparse point %x", e.Tag)
    44  }
    45  
    46  // DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
    47  // or a mount point.
    48  func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
    49  	tag := binary.LittleEndian.Uint32(b[0:4])
    50  	return DecodeReparsePointData(tag, b[8:])
    51  }
    52  
    53  func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
    54  	isMountPoint := false
    55  	switch tag {
    56  	case reparseTagMountPoint:
    57  		isMountPoint = true
    58  	case reparseTagSymlink:
    59  	default:
    60  		return nil, &UnsupportedReparsePointError{tag}
    61  	}
    62  	nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
    63  	if !isMountPoint {
    64  		nameOffset += 4
    65  	}
    66  	nameLength := binary.LittleEndian.Uint16(b[6:8])
    67  	name := make([]uint16, nameLength/2)
    68  	err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
    73  }
    74  
    75  func isDriveLetter(c byte) bool {
    76  	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
    77  }
    78  
    79  // EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
    80  // mount point.
    81  func EncodeReparsePoint(rp *ReparsePoint) []byte {
    82  	// Generate an NT path and determine if this is a relative path.
    83  	var ntTarget string
    84  	relative := false
    85  	if strings.HasPrefix(rp.Target, `\\?\`) {
    86  		ntTarget = `\??\` + rp.Target[4:]
    87  	} else if strings.HasPrefix(rp.Target, `\\`) {
    88  		ntTarget = `\??\UNC\` + rp.Target[2:]
    89  	} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
    90  		ntTarget = `\??\` + rp.Target
    91  	} else {
    92  		ntTarget = rp.Target
    93  		relative = true
    94  	}
    95  
    96  	// The paths must be NUL-terminated even though they are counted strings.
    97  	target16 := utf16.Encode([]rune(rp.Target + "\x00"))
    98  	ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
    99  
   100  	size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
   101  	size += len(ntTarget16)*2 + len(target16)*2
   102  
   103  	tag := uint32(reparseTagMountPoint)
   104  	if !rp.IsMountPoint {
   105  		tag = reparseTagSymlink
   106  		size += 4 // Add room for symlink flags
   107  	}
   108  
   109  	data := reparseDataBuffer{
   110  		ReparseTag:           tag,
   111  		ReparseDataLength:    uint16(size),
   112  		SubstituteNameOffset: 0,
   113  		SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
   114  		PrintNameOffset:      uint16(len(ntTarget16) * 2),
   115  		PrintNameLength:      uint16((len(target16) - 1) * 2),
   116  	}
   117  
   118  	var b bytes.Buffer
   119  	_ = binary.Write(&b, binary.LittleEndian, &data)
   120  	if !rp.IsMountPoint {
   121  		flags := uint32(0)
   122  		if relative {
   123  			flags |= 1
   124  		}
   125  		_ = binary.Write(&b, binary.LittleEndian, flags)
   126  	}
   127  
   128  	_ = binary.Write(&b, binary.LittleEndian, ntTarget16)
   129  	_ = binary.Write(&b, binary.LittleEndian, target16)
   130  	return b.Bytes()
   131  }
   132  

View as plain text