]> Cypherpunks.ru repositories - gostls13.git/blob - src/internal/safefilepath/path_windows.go
909c150edc87a281d94f648ae760163418e8edb1
[gostls13.git] / src / internal / safefilepath / path_windows.go
1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
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                 // Find the next path element.
22                 i := 0
23                 dot := -1
24                 for i < len(p) && p[i] != '/' {
25                         switch p[i] {
26                         case 0, '\\', ':':
27                                 return "", errInvalidPath
28                         case '.':
29                                 if dot < 0 {
30                                         dot = i
31                                 }
32                         }
33                         i++
34                 }
35                 part := p[:i]
36                 if i < len(p) {
37                         containsSlash = true
38                         p = p[i+1:]
39                 } else {
40                         p = ""
41                 }
42                 // Trim the extension and look for a reserved name.
43                 base := part
44                 if dot >= 0 {
45                         base = part[:dot]
46                 }
47                 if isReservedName(base) {
48                         if dot < 0 {
49                                 return "", errInvalidPath
50                         }
51                         // The path element is a reserved name with an extension.
52                         // Some Windows versions consider this a reserved name,
53                         // while others do not. Use FullPath to see if the name is
54                         // reserved.
55                         if p, _ := syscall.FullPath(part); len(p) >= 4 && p[:4] == `\\.\` {
56                                 return "", errInvalidPath
57                         }
58                 }
59         }
60         if containsSlash {
61                 // We can't depend on strings, so substitute \ for / manually.
62                 buf := []byte(path)
63                 for i, b := range buf {
64                         if b == '/' {
65                                 buf[i] = '\\'
66                         }
67                 }
68                 path = string(buf)
69         }
70         return path, nil
71 }
72
73 // isReservedName reports if name is a Windows reserved device name.
74 // It does not detect names with an extension, which are also reserved on some Windows versions.
75 //
76 // For details, search for PRN in
77 // https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file.
78 func isReservedName(name string) bool {
79         if 3 <= len(name) && len(name) <= 4 {
80                 switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
81                 case "CON", "PRN", "AUX", "NUL":
82                         return len(name) == 3
83                 case "COM", "LPT":
84                         return len(name) == 4 && '1' <= name[3] && name[3] <= '9'
85                 }
86         }
87         return false
88 }
89
90 func toUpper(c byte) byte {
91         if 'a' <= c && c <= 'z' {
92                 return c - ('a' - 'A')
93         }
94         return c
95 }