Tizen_4.0 base
[platform/upstream/docker-engine.git] / pkg / symlink / fs_windows.go
1 package symlink
2
3 import (
4         "bytes"
5         "errors"
6         "os"
7         "path/filepath"
8         "strings"
9         "syscall"
10
11         "github.com/docker/docker/pkg/longpath"
12 )
13
14 func toShort(path string) (string, error) {
15         p, err := syscall.UTF16FromString(path)
16         if err != nil {
17                 return "", err
18         }
19         b := p // GetShortPathName says we can reuse buffer
20         n, err := syscall.GetShortPathName(&p[0], &b[0], uint32(len(b)))
21         if err != nil {
22                 return "", err
23         }
24         if n > uint32(len(b)) {
25                 b = make([]uint16, n)
26                 if _, err = syscall.GetShortPathName(&p[0], &b[0], uint32(len(b))); err != nil {
27                         return "", err
28                 }
29         }
30         return syscall.UTF16ToString(b), nil
31 }
32
33 func toLong(path string) (string, error) {
34         p, err := syscall.UTF16FromString(path)
35         if err != nil {
36                 return "", err
37         }
38         b := p // GetLongPathName says we can reuse buffer
39         n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
40         if err != nil {
41                 return "", err
42         }
43         if n > uint32(len(b)) {
44                 b = make([]uint16, n)
45                 n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b)))
46                 if err != nil {
47                         return "", err
48                 }
49         }
50         b = b[:n]
51         return syscall.UTF16ToString(b), nil
52 }
53
54 func evalSymlinks(path string) (string, error) {
55         path, err := walkSymlinks(path)
56         if err != nil {
57                 return "", err
58         }
59
60         p, err := toShort(path)
61         if err != nil {
62                 return "", err
63         }
64         p, err = toLong(p)
65         if err != nil {
66                 return "", err
67         }
68         // syscall.GetLongPathName does not change the case of the drive letter,
69         // but the result of EvalSymlinks must be unique, so we have
70         // EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
71         // Make drive letter upper case.
72         if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
73                 p = string(p[0]+'A'-'a') + p[1:]
74         } else if len(p) >= 6 && p[5] == ':' && 'a' <= p[4] && p[4] <= 'z' {
75                 p = p[:3] + string(p[4]+'A'-'a') + p[5:]
76         }
77         return filepath.Clean(p), nil
78 }
79
80 const utf8RuneSelf = 0x80
81
82 func walkSymlinks(path string) (string, error) {
83         const maxIter = 255
84         originalPath := path
85         // consume path by taking each frontmost path element,
86         // expanding it if it's a symlink, and appending it to b
87         var b bytes.Buffer
88         for n := 0; path != ""; n++ {
89                 if n > maxIter {
90                         return "", errors.New("EvalSymlinks: too many links in " + originalPath)
91                 }
92
93                 // A path beginning with `\\?\` represents the root, so automatically
94                 // skip that part and begin processing the next segment.
95                 if strings.HasPrefix(path, longpath.Prefix) {
96                         b.WriteString(longpath.Prefix)
97                         path = path[4:]
98                         continue
99                 }
100
101                 // find next path component, p
102                 var i = -1
103                 for j, c := range path {
104                         if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) {
105                                 i = j
106                                 break
107                         }
108                 }
109                 var p string
110                 if i == -1 {
111                         p, path = path, ""
112                 } else {
113                         p, path = path[:i], path[i+1:]
114                 }
115
116                 if p == "" {
117                         if b.Len() == 0 {
118                                 // must be absolute path
119                                 b.WriteRune(filepath.Separator)
120                         }
121                         continue
122                 }
123
124                 // If this is the first segment after the long path prefix, accept the
125                 // current segment as a volume root or UNC share and move on to the next.
126                 if b.String() == longpath.Prefix {
127                         b.WriteString(p)
128                         b.WriteRune(filepath.Separator)
129                         continue
130                 }
131
132                 fi, err := os.Lstat(b.String() + p)
133                 if err != nil {
134                         return "", err
135                 }
136                 if fi.Mode()&os.ModeSymlink == 0 {
137                         b.WriteString(p)
138                         if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') {
139                                 b.WriteRune(filepath.Separator)
140                         }
141                         continue
142                 }
143
144                 // it's a symlink, put it at the front of path
145                 dest, err := os.Readlink(b.String() + p)
146                 if err != nil {
147                         return "", err
148                 }
149                 if filepath.IsAbs(dest) || os.IsPathSeparator(dest[0]) {
150                         b.Reset()
151                 }
152                 path = dest + string(filepath.Separator) + path
153         }
154         return filepath.Clean(b.String()), nil
155 }
156
157 func isDriveOrRoot(p string) bool {
158         if p == string(filepath.Separator) {
159                 return true
160         }
161
162         length := len(p)
163         if length >= 2 {
164                 if p[length-1] == ':' && (('a' <= p[length-2] && p[length-2] <= 'z') || ('A' <= p[length-2] && p[length-2] <= 'Z')) {
165                         return true
166                 }
167         }
168         return false
169 }