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