github.com/josephvusich/fdf@v0.0.0-20230522095411-9326dd32e33f/path_windows.go (about) 1 package main 2 3 // Adapted from os/path_windows.go 4 5 import "os" 6 7 // fixLongPath returns the extended-length (\\?\-prefixed) form of 8 // path when needed, in order to avoid the default 260 character file 9 // path limit imposed by Windows. If path is not easily converted to 10 // the extended-length form (for example, if path is a relative path 11 // or contains .. elements), or is short enough, fixLongPath returns 12 // path unmodified. 13 // 14 // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath 15 func fixLongPath(path string) string { 16 // Do nothing (and don't allocate) if the path is "short". 17 // Empirically (at least on the Windows Server 2013 builder), 18 // the kernel is arbitrarily okay with < 248 bytes. That 19 // matches what the docs above say: 20 // "When using an API to create a directory, the specified 21 // path cannot be so long that you cannot append an 8.3 file 22 // name (that is, the directory name cannot exceed MAX_PATH 23 // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. 24 // 25 // The MSDN docs appear to say that a normal path that is 248 bytes long 26 // will work; empirically the path must be less then 248 bytes long. 27 if len(path) < 248 { 28 // Don't fix. (This is how Go 1.7 and earlier worked, 29 // not automatically generating the \\?\ form) 30 return path 31 } 32 33 // The extended form begins with \\?\, as in 34 // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. 35 // The extended form disables evaluation of . and .. path 36 // elements and disables the interpretation of / as equivalent 37 // to \. The conversion here rewrites / to \ and elides 38 // . elements as well as trailing or duplicate separators. For 39 // simplicity it avoids the conversion entirely for relative 40 // paths or paths containing .. elements. For now, 41 // \\server\share paths are not converted to 42 // \\?\UNC\server\share paths because the rules for doing so 43 // are less well-specified. 44 if len(path) >= 2 && path[:2] == `\\` { 45 // Don't canonicalize UNC paths. 46 return path 47 } 48 if !isAbs(path) { 49 // Relative path 50 return path 51 } 52 53 const prefix = `\\?` 54 55 pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) 56 copy(pathbuf, prefix) 57 n := len(path) 58 r, w := 0, len(prefix) 59 for r < n { 60 switch { 61 case os.IsPathSeparator(path[r]): 62 // empty block 63 r++ 64 case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): 65 // /./ 66 r++ 67 case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): 68 // /../ is currently unhandled 69 return path 70 default: 71 pathbuf[w] = '\\' 72 w++ 73 for ; r < n && !os.IsPathSeparator(path[r]); r++ { 74 pathbuf[w] = path[r] 75 w++ 76 } 77 } 78 } 79 // A drive's root directory needs a trailing \ 80 if w == len(`\\?\c:`) { 81 pathbuf[w] = '\\' 82 w++ 83 } 84 return string(pathbuf[:w]) 85 } 86 87 func isAbs(path string) (b bool) { 88 v := volumeName(path) 89 if v == "" { 90 return false 91 } 92 path = path[len(v):] 93 if path == "" { 94 return false 95 } 96 return os.IsPathSeparator(path[0]) 97 } 98 99 func volumeName(path string) (v string) { 100 if len(path) < 2 { 101 return "" 102 } 103 // with drive letter 104 c := path[0] 105 if path[1] == ':' && 106 ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 107 'A' <= c && c <= 'Z') { 108 return path[:2] 109 } 110 // is it UNC 111 if l := len(path); l >= 5 && os.IsPathSeparator(path[0]) && os.IsPathSeparator(path[1]) && 112 !os.IsPathSeparator(path[2]) && path[2] != '.' { 113 // first, leading `\\` and next shouldn't be `\`. its server name. 114 for n := 3; n < l-1; n++ { 115 // second, next '\' shouldn't be repeated. 116 if os.IsPathSeparator(path[n]) { 117 n++ 118 // third, following something characters. its share name. 119 if !os.IsPathSeparator(path[n]) { 120 if path[n] == '.' { 121 break 122 } 123 for ; n < l; n++ { 124 if os.IsPathSeparator(path[n]) { 125 break 126 } 127 } 128 return path[:n] 129 } 130 break 131 } 132 } 133 } 134 return "" 135 }