github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/daemon/logger/loggerutils/file_windows.go (about) 1 package loggerutils 2 3 import ( 4 "os" 5 "syscall" 6 "unsafe" 7 ) 8 9 func open(name string) (*os.File, error) { 10 return openFile(name, os.O_RDONLY, 0) 11 } 12 13 func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { 14 if name == "" { 15 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} 16 } 17 h, err := syscallOpen(fixLongPath(name), flag|syscall.O_CLOEXEC, syscallMode(perm)) 18 if err != nil { 19 return nil, &os.PathError{Op: "open", Path: name, Err: err} 20 } 21 return os.NewFile(uintptr(h), name), nil 22 } 23 24 // syscallOpen is copied from syscall.Open but is modified to 25 // always open a file with FILE_SHARE_DELETE 26 func syscallOpen(path string, mode int, perm uint32) (fd syscall.Handle, err error) { 27 if len(path) == 0 { 28 return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND 29 } 30 31 pathp, err := syscall.UTF16PtrFromString(path) 32 if err != nil { 33 return syscall.InvalidHandle, err 34 } 35 var access uint32 36 switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) { 37 case syscall.O_RDONLY: 38 access = syscall.GENERIC_READ 39 case syscall.O_WRONLY: 40 access = syscall.GENERIC_WRITE 41 case syscall.O_RDWR: 42 access = syscall.GENERIC_READ | syscall.GENERIC_WRITE 43 } 44 if mode&syscall.O_CREAT != 0 { 45 access |= syscall.GENERIC_WRITE 46 } 47 if mode&syscall.O_APPEND != 0 { 48 access &^= syscall.GENERIC_WRITE 49 access |= syscall.FILE_APPEND_DATA 50 } 51 sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE) 52 var sa *syscall.SecurityAttributes 53 if mode&syscall.O_CLOEXEC == 0 { 54 sa = makeInheritSa() 55 } 56 var createmode uint32 57 switch { 58 case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): 59 createmode = syscall.CREATE_NEW 60 case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC): 61 createmode = syscall.CREATE_ALWAYS 62 case mode&syscall.O_CREAT == syscall.O_CREAT: 63 createmode = syscall.OPEN_ALWAYS 64 case mode&syscall.O_TRUNC == syscall.O_TRUNC: 65 createmode = syscall.TRUNCATE_EXISTING 66 default: 67 createmode = syscall.OPEN_EXISTING 68 } 69 h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0) 70 return h, e 71 } 72 73 func makeInheritSa() *syscall.SecurityAttributes { 74 var sa syscall.SecurityAttributes 75 sa.Length = uint32(unsafe.Sizeof(sa)) 76 sa.InheritHandle = 1 77 return &sa 78 } 79 80 // fixLongPath returns the extended-length (\\?\-prefixed) form of 81 // path when needed, in order to avoid the default 260 character file 82 // path limit imposed by Windows. If path is not easily converted to 83 // the extended-length form (for example, if path is a relative path 84 // or contains .. elements), or is short enough, fixLongPath returns 85 // path unmodified. 86 // 87 // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath 88 // 89 // Copied from os.OpenFile 90 func fixLongPath(path string) string { 91 // Do nothing (and don't allocate) if the path is "short". 92 // Empirically (at least on the Windows Server 2013 builder), 93 // the kernel is arbitrarily okay with < 248 bytes. That 94 // matches what the docs above say: 95 // "When using an API to create a directory, the specified 96 // path cannot be so long that you cannot append an 8.3 file 97 // name (that is, the directory name cannot exceed MAX_PATH 98 // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. 99 // 100 // The MSDN docs appear to say that a normal path that is 248 bytes long 101 // will work; empirically the path must be less then 248 bytes long. 102 if len(path) < 248 { 103 // Don't fix. (This is how Go 1.7 and earlier worked, 104 // not automatically generating the \\?\ form) 105 return path 106 } 107 108 // The extended form begins with \\?\, as in 109 // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. 110 // The extended form disables evaluation of . and .. path 111 // elements and disables the interpretation of / as equivalent 112 // to \. The conversion here rewrites / to \ and elides 113 // . elements as well as trailing or duplicate separators. For 114 // simplicity it avoids the conversion entirely for relative 115 // paths or paths containing .. elements. For now, 116 // \\server\share paths are not converted to 117 // \\?\UNC\server\share paths because the rules for doing so 118 // are less well-specified. 119 if len(path) >= 2 && path[:2] == `\\` { 120 // Don't canonicalize UNC paths. 121 return path 122 } 123 if !isAbs(path) { 124 // Relative path 125 return path 126 } 127 128 const prefix = `\\?` 129 130 pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) 131 copy(pathbuf, prefix) 132 n := len(path) 133 r, w := 0, len(prefix) 134 for r < n { 135 switch { 136 case os.IsPathSeparator(path[r]): 137 // empty block 138 r++ 139 case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): 140 // /./ 141 r++ 142 case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): 143 // /../ is currently unhandled 144 return path 145 default: 146 pathbuf[w] = '\\' 147 w++ 148 for ; r < n && !os.IsPathSeparator(path[r]); r++ { 149 pathbuf[w] = path[r] 150 w++ 151 } 152 } 153 } 154 // A drive's root directory needs a trailing \ 155 if w == len(`\\?\c:`) { 156 pathbuf[w] = '\\' 157 w++ 158 } 159 return string(pathbuf[:w]) 160 } 161 162 // copied from os package for os.OpenFile 163 func syscallMode(i os.FileMode) (o uint32) { 164 o |= uint32(i.Perm()) 165 if i&os.ModeSetuid != 0 { 166 o |= syscall.S_ISUID 167 } 168 if i&os.ModeSetgid != 0 { 169 o |= syscall.S_ISGID 170 } 171 if i&os.ModeSticky != 0 { 172 o |= syscall.S_ISVTX 173 } 174 // No mapping for Go's ModeTemporary (plan9 only). 175 return 176 } 177 178 func isAbs(path string) (b bool) { 179 v := volumeName(path) 180 if v == "" { 181 return false 182 } 183 path = path[len(v):] 184 if path == "" { 185 return false 186 } 187 return os.IsPathSeparator(path[0]) 188 } 189 190 func volumeName(path string) (v string) { 191 if len(path) < 2 { 192 return "" 193 } 194 // with drive letter 195 c := path[0] 196 if path[1] == ':' && 197 ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 198 'A' <= c && c <= 'Z') { 199 return path[:2] 200 } 201 // is it UNC 202 if l := len(path); l >= 5 && os.IsPathSeparator(path[0]) && os.IsPathSeparator(path[1]) && 203 !os.IsPathSeparator(path[2]) && path[2] != '.' { 204 // first, leading `\\` and next shouldn't be `\`. its server name. 205 for n := 3; n < l-1; n++ { 206 // second, next '\' shouldn't be repeated. 207 if os.IsPathSeparator(path[n]) { 208 n++ 209 // third, following something characters. its share name. 210 if !os.IsPathSeparator(path[n]) { 211 if path[n] == '.' { 212 break 213 } 214 for ; n < l; n++ { 215 if os.IsPathSeparator(path[n]) { 216 break 217 } 218 } 219 return path[:n] 220 } 221 break 222 } 223 } 224 } 225 return "" 226 }