aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/go/_std_1.21/src/path/filepath/path_windows.go
diff options
context:
space:
mode:
authorhiddenpath <hiddenpath@yandex-team.com>2024-01-26 19:49:19 +0300
committerhiddenpath <hiddenpath@yandex-team.com>2024-01-26 20:08:56 +0300
commitf6d7ec162ce757a854f4a619d6b0c7084ad48f3e (patch)
treed3176e091caaa7b6cdebda4944ac265de533a6da /contrib/go/_std_1.21/src/path/filepath/path_windows.go
parent27b1811df443f5a3d16e2ae0f1744f39b348109d (diff)
downloadydb-f6d7ec162ce757a854f4a619d6b0c7084ad48f3e.tar.gz
Update golang to 1.21.6
Diffstat (limited to 'contrib/go/_std_1.21/src/path/filepath/path_windows.go')
-rw-r--r--contrib/go/_std_1.21/src/path/filepath/path_windows.go188
1 files changed, 115 insertions, 73 deletions
diff --git a/contrib/go/_std_1.21/src/path/filepath/path_windows.go b/contrib/go/_std_1.21/src/path/filepath/path_windows.go
index 4dca9e0f55..eacab0e5ce 100644
--- a/contrib/go/_std_1.21/src/path/filepath/path_windows.go
+++ b/contrib/go/_std_1.21/src/path/filepath/path_windows.go
@@ -5,6 +5,8 @@
package filepath
import (
+ "internal/safefilepath"
+ "os"
"strings"
"syscall"
)
@@ -20,34 +22,6 @@ func toUpper(c byte) byte {
return c
}
-// isReservedName reports if name is a Windows reserved device name or a console handle.
-// It does not detect names with an extension, which are also reserved on some Windows versions.
-//
-// For details, search for PRN in
-// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file.
-func isReservedName(name string) bool {
- if 3 <= len(name) && len(name) <= 4 {
- switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
- case "CON", "PRN", "AUX", "NUL":
- return len(name) == 3
- case "COM", "LPT":
- return len(name) == 4 && '1' <= name[3] && name[3] <= '9'
- }
- }
- // Passing CONIN$ or CONOUT$ to CreateFile opens a console handle.
- // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#consoles
- //
- // While CONIN$ and CONOUT$ aren't documented as being files,
- // they behave the same as CON. For example, ./CONIN$ also opens the console input.
- if len(name) == 6 && name[5] == '$' && strings.EqualFold(name, "CONIN$") {
- return true
- }
- if len(name) == 7 && name[6] == '$' && strings.EqualFold(name, "CONOUT$") {
- return true
- }
- return false
-}
-
func isLocal(path string) bool {
if path == "" {
return false
@@ -68,25 +42,8 @@ func isLocal(path string) bool {
if part == "." || part == ".." {
hasDots = true
}
- // Trim the extension and look for a reserved name.
- base, _, hasExt := strings.Cut(part, ".")
- if isReservedName(base) {
- if !hasExt {
- return false
- }
- // The path element is a reserved name with an extension. Some Windows
- // versions consider this a reserved name, while others do not. Use
- // FullPath to see if the name is reserved.
- //
- // FullPath will convert references to reserved device names to their
- // canonical form: \\.\${DEVICE_NAME}
- //
- // FullPath does not perform this conversion for paths which contain
- // a reserved device name anywhere other than in the last element,
- // so check the part rather than the full path.
- if p, _ := syscall.FullPath(part); len(p) >= 4 && p[:4] == `\\.\` {
- return false
- }
+ if safefilepath.IsReservedName(part) {
+ return false
}
}
if hasDots {
@@ -118,40 +75,93 @@ func IsAbs(path string) (b bool) {
// volumeNameLen returns length of the leading volume name on Windows.
// It returns 0 elsewhere.
//
-// See: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
+// See:
+// https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
+// https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
func volumeNameLen(path string) int {
- if len(path) < 2 {
- return 0
- }
- // with drive letter
- c := path[0]
- if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
+ switch {
+ case len(path) >= 2 && path[1] == ':':
+ // Path starts with a drive letter.
+ //
+ // Not all Windows functions necessarily enforce the requirement that
+ // drive letters be in the set A-Z, and we don't try to here.
+ //
+ // We don't handle the case of a path starting with a non-ASCII character,
+ // in which case the "drive letter" might be multiple bytes long.
return 2
- }
- // UNC and DOS device paths start with two slashes.
- if !isSlash(path[0]) || !isSlash(path[1]) {
+
+ case len(path) == 0 || !isSlash(path[0]):
+ // Path does not have a volume component.
return 0
+
+ case pathHasPrefixFold(path, `\\.\UNC`):
+ // We're going to treat the UNC host and share as part of the volume
+ // prefix for historical reasons, but this isn't really principled;
+ // Windows's own GetFullPathName will happily remove the first
+ // component of the path in this space, converting
+ // \\.\unc\a\b\..\c into \\.\unc\a\c.
+ return uncLen(path, len(`\\.\UNC\`))
+
+ case pathHasPrefixFold(path, `\\.`) ||
+ pathHasPrefixFold(path, `\\?`) || pathHasPrefixFold(path, `\??`):
+ // Path starts with \\.\, and is a Local Device path; or
+ // path starts with \\?\ or \??\ and is a Root Local Device path.
+ //
+ // We treat the next component after the \\.\ prefix as
+ // part of the volume name, which means Clean(`\\?\c:\`)
+ // won't remove the trailing \. (See #64028.)
+ if len(path) == 3 {
+ return 3 // exactly \\.
+ }
+ _, rest, ok := cutPath(path[4:])
+ if !ok {
+ return len(path)
+ }
+ return len(path) - len(rest) - 1
+
+ case len(path) >= 2 && isSlash(path[1]):
+ // Path starts with \\, and is a UNC path.
+ return uncLen(path, 2)
}
- rest := path[2:]
- p1, rest, _ := cutPath(rest)
- p2, rest, ok := cutPath(rest)
- if !ok {
- return len(path)
+ return 0
+}
+
+// pathHasPrefixFold tests whether the path s begins with prefix,
+// ignoring case and treating all path separators as equivalent.
+// If s is longer than prefix, then s[len(prefix)] must be a path separator.
+func pathHasPrefixFold(s, prefix string) bool {
+ if len(s) < len(prefix) {
+ return false
}
- if p1 != "." && p1 != "?" {
- // This is a UNC path: \\${HOST}\${SHARE}\
- return len(path) - len(rest) - 1
+ for i := 0; i < len(prefix); i++ {
+ if isSlash(prefix[i]) {
+ if !isSlash(s[i]) {
+ return false
+ }
+ } else if toUpper(prefix[i]) != toUpper(s[i]) {
+ return false
+ }
}
- // This is a DOS device path.
- if len(p2) == 3 && toUpper(p2[0]) == 'U' && toUpper(p2[1]) == 'N' && toUpper(p2[2]) == 'C' {
- // This is a DOS device path that links to a UNC: \\.\UNC\${HOST}\${SHARE}\
- _, rest, _ = cutPath(rest) // host
- _, rest, ok = cutPath(rest) // share
- if !ok {
- return len(path)
+ if len(s) > len(prefix) && !isSlash(s[len(prefix)]) {
+ return false
+ }
+ return true
+}
+
+// uncLen returns the length of the volume prefix of a UNC path.
+// prefixLen is the prefix prior to the start of the UNC host;
+// for example, for "//host/share", the prefixLen is len("//")==2.
+func uncLen(path string, prefixLen int) int {
+ count := 0
+ for i := prefixLen; i < len(path); i++ {
+ if isSlash(path[i]) {
+ count++
+ if count == 2 {
+ return i
+ }
}
}
- return len(path) - len(rest) - 1
+ return len(path)
}
// cutPath slices path around the first path separator.
@@ -238,6 +248,12 @@ func join(elem []string) string {
for len(e) > 0 && isSlash(e[0]) {
e = e[1:]
}
+ // If the path is \ and the next path element is ??,
+ // add an extra .\ to create \.\?? rather than \??\
+ // (a Root Local Device path).
+ if b.Len() == 1 && pathHasPrefixFold(e, "??") {
+ b.WriteString(`.\`)
+ }
case lastChar == ':':
// If the path ends in a colon, keep the path relative to the current directory
// on a drive and don't add a separator. Preserve leading slashes in the next
@@ -304,3 +320,29 @@ func isUNC(path string) bool {
func sameWord(a, b string) bool {
return strings.EqualFold(a, b)
}
+
+// postClean adjusts the results of Clean to avoid turning a relative path
+// into an absolute or rooted one.
+func postClean(out *lazybuf) {
+ if out.volLen != 0 || out.buf == nil {
+ return
+ }
+ // If a ':' appears in the path element at the start of a path,
+ // insert a .\ at the beginning to avoid converting relative paths
+ // like a/../c: into c:.
+ for _, c := range out.buf {
+ if os.IsPathSeparator(c) {
+ break
+ }
+ if c == ':' {
+ out.prepend('.', Separator)
+ return
+ }
+ }
+ // If a path begins with \??\, insert a \. at the beginning
+ // to avoid converting paths like \a\..\??\c:\x into \??\c:\x
+ // (equivalent to c:\x).
+ if len(out.buf) >= 3 && os.IsPathSeparator(out.buf[0]) && out.buf[1] == '?' && out.buf[2] == '?' {
+ out.prepend(Separator, '.')
+ }
+}