...
1
2
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
31 type ReparsePoint struct {
32 Target string
33 IsMountPoint bool
34 }
35
36
37
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
47
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
80
81 func EncodeReparsePoint(rp *ReparsePoint) []byte {
82
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
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
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