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