...
1
2
3
4
5 package safefilepath
6
7 import (
8 "syscall"
9 "unicode/utf8"
10 )
11
12 func fromFS(path string) (string, error) {
13 if !utf8.ValidString(path) {
14 return "", errInvalidPath
15 }
16 for len(path) > 1 && path[0] == '/' && path[1] == '/' {
17 path = path[1:]
18 }
19 containsSlash := false
20 for p := path; p != ""; {
21
22 i := 0
23 for i < len(p) && p[i] != '/' {
24 switch p[i] {
25 case 0, '\\', ':':
26 return "", errInvalidPath
27 }
28 i++
29 }
30 part := p[:i]
31 if i < len(p) {
32 containsSlash = true
33 p = p[i+1:]
34 } else {
35 p = ""
36 }
37 if IsReservedName(part) {
38 return "", errInvalidPath
39 }
40 }
41 if containsSlash {
42
43 buf := []byte(path)
44 for i, b := range buf {
45 if b == '/' {
46 buf[i] = '\\'
47 }
48 }
49 path = string(buf)
50 }
51 return path, nil
52 }
53
54
55
56
57
58
59 func IsReservedName(name string) bool {
60
61 base := name
62 for i := 0; i < len(base); i++ {
63 switch base[i] {
64 case ':', '.':
65 base = base[:i]
66 }
67 }
68
69 for len(base) > 0 && base[len(base)-1] == ' ' {
70 base = base[:len(base)-1]
71 }
72 if !isReservedBaseName(base) {
73 return false
74 }
75 if len(base) == len(name) {
76 return true
77 }
78
79
80
81
82 if p, _ := syscall.FullPath(name); len(p) >= 4 && p[:4] == `\\.\` {
83 return true
84 }
85 return false
86 }
87
88 func isReservedBaseName(name string) bool {
89 if len(name) == 3 {
90 switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
91 case "CON", "PRN", "AUX", "NUL":
92 return true
93 }
94 }
95 if len(name) >= 4 {
96 switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
97 case "COM", "LPT":
98 if len(name) == 4 && '1' <= name[3] && name[3] <= '9' {
99 return true
100 }
101
102 switch name[3:] {
103 case "\u00b2", "\u00b3", "\u00b9":
104 return true
105 }
106 return false
107 }
108 }
109
110
111
112
113
114
115 if len(name) == 6 && name[5] == '$' && equalFold(name, "CONIN$") {
116 return true
117 }
118 if len(name) == 7 && name[6] == '$' && equalFold(name, "CONOUT$") {
119 return true
120 }
121 return false
122 }
123
124 func equalFold(a, b string) bool {
125 if len(a) != len(b) {
126 return false
127 }
128 for i := 0; i < len(a); i++ {
129 if toUpper(a[i]) != toUpper(b[i]) {
130 return false
131 }
132 }
133 return true
134 }
135
136 func toUpper(c byte) byte {
137 if 'a' <= c && c <= 'z' {
138 return c - ('a' - 'A')
139 }
140 return c
141 }
142
View as plain text