github.com/JimmyHuang454/JLS-go@v0.0.0-20230831150107-90d536585ba0/internal/safefilepath/path_windows.go (about) 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 }