github.com/demonoid81/containerd@v1.3.4/sys/filesys_windows.go (about) 1 // +build windows 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package sys 20 21 import ( 22 "os" 23 "path/filepath" 24 "regexp" 25 "strings" 26 "syscall" 27 "unsafe" 28 29 winio "github.com/Microsoft/go-winio" 30 "github.com/Microsoft/hcsshim" 31 ) 32 33 const ( 34 // SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System 35 SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" 36 ) 37 38 // MkdirAllWithACL is a wrapper for MkdirAll that creates a directory 39 // ACL'd for Builtin Administrators and Local System. 40 func MkdirAllWithACL(path string, perm os.FileMode) error { 41 return mkdirall(path, true) 42 } 43 44 // MkdirAll implementation that is volume path aware for Windows. 45 func MkdirAll(path string, _ os.FileMode) error { 46 return mkdirall(path, false) 47 } 48 49 // mkdirall is a custom version of os.MkdirAll modified for use on Windows 50 // so that it is both volume path aware, and can create a directory with 51 // a DACL. 52 func mkdirall(path string, adminAndLocalSystem bool) error { 53 if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) { 54 return nil 55 } 56 57 // The rest of this method is largely copied from os.MkdirAll and should be kept 58 // as-is to ensure compatibility. 59 60 // Fast path: if we can tell whether path is a directory or file, stop with success or error. 61 dir, err := os.Stat(path) 62 if err == nil { 63 if dir.IsDir() { 64 return nil 65 } 66 return &os.PathError{ 67 Op: "mkdir", 68 Path: path, 69 Err: syscall.ENOTDIR, 70 } 71 } 72 73 // Slow path: make sure parent exists and then call Mkdir for path. 74 i := len(path) 75 for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. 76 i-- 77 } 78 79 j := i 80 for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. 81 j-- 82 } 83 84 if j > 1 { 85 // Create parent 86 err = mkdirall(path[0:j-1], adminAndLocalSystem) 87 if err != nil { 88 return err 89 } 90 } 91 92 // Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result. 93 if adminAndLocalSystem { 94 err = mkdirWithACL(path) 95 } else { 96 err = os.Mkdir(path, 0) 97 } 98 99 if err != nil { 100 // Handle arguments like "foo/." by 101 // double-checking that directory doesn't exist. 102 dir, err1 := os.Lstat(path) 103 if err1 == nil && dir.IsDir() { 104 return nil 105 } 106 return err 107 } 108 return nil 109 } 110 111 // mkdirWithACL creates a new directory. If there is an error, it will be of 112 // type *PathError. . 113 // 114 // This is a modified and combined version of os.Mkdir and syscall.Mkdir 115 // in golang to cater for creating a directory am ACL permitting full 116 // access, with inheritance, to any subfolder/file for Built-in Administrators 117 // and Local System. 118 func mkdirWithACL(name string) error { 119 sa := syscall.SecurityAttributes{Length: 0} 120 sd, err := winio.SddlToSecurityDescriptor(SddlAdministratorsLocalSystem) 121 if err != nil { 122 return &os.PathError{Op: "mkdir", Path: name, Err: err} 123 } 124 sa.Length = uint32(unsafe.Sizeof(sa)) 125 sa.InheritHandle = 1 126 sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) 127 128 namep, err := syscall.UTF16PtrFromString(name) 129 if err != nil { 130 return &os.PathError{Op: "mkdir", Path: name, Err: err} 131 } 132 133 e := syscall.CreateDirectory(namep, &sa) 134 if e != nil { 135 return &os.PathError{Op: "mkdir", Path: name, Err: e} 136 } 137 return nil 138 } 139 140 // IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows, 141 // golang filepath.IsAbs does not consider a path \windows\system32 as absolute 142 // as it doesn't start with a drive-letter/colon combination. However, in 143 // docker we need to verify things such as WORKDIR /windows/system32 in 144 // a Dockerfile (which gets translated to \windows\system32 when being processed 145 // by the daemon. This SHOULD be treated as absolute from a docker processing 146 // perspective. 147 func IsAbs(path string) bool { 148 if !filepath.IsAbs(path) { 149 if !strings.HasPrefix(path, string(os.PathSeparator)) { 150 return false 151 } 152 } 153 return true 154 } 155 156 // The origin of the functions below here are the golang OS and syscall packages, 157 // slightly modified to only cope with files, not directories due to the 158 // specific use case. 159 // 160 // The alteration is to allow a file on Windows to be opened with 161 // FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating 162 // the standby list, particularly when accessing large files such as layer.tar. 163 164 // CreateSequential creates the named file with mode 0666 (before umask), truncating 165 // it if it already exists. If successful, methods on the returned 166 // File can be used for I/O; the associated file descriptor has mode 167 // O_RDWR. 168 // If there is an error, it will be of type *PathError. 169 func CreateSequential(name string) (*os.File, error) { 170 return OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0) 171 } 172 173 // OpenSequential opens the named file for reading. If successful, methods on 174 // the returned file can be used for reading; the associated file 175 // descriptor has mode O_RDONLY. 176 // If there is an error, it will be of type *PathError. 177 func OpenSequential(name string) (*os.File, error) { 178 return OpenFileSequential(name, os.O_RDONLY, 0) 179 } 180 181 // OpenFileSequential is the generalized open call; most users will use Open 182 // or Create instead. 183 // If there is an error, it will be of type *PathError. 184 func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) { 185 if name == "" { 186 return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} 187 } 188 r, errf := syscallOpenFileSequential(name, flag, 0) 189 if errf == nil { 190 return r, nil 191 } 192 return nil, &os.PathError{Op: "open", Path: name, Err: errf} 193 } 194 195 func syscallOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { 196 r, e := syscallOpenSequential(name, flag|syscall.O_CLOEXEC, 0) 197 if e != nil { 198 return nil, e 199 } 200 return os.NewFile(uintptr(r), name), nil 201 } 202 203 func makeInheritSa() *syscall.SecurityAttributes { 204 var sa syscall.SecurityAttributes 205 sa.Length = uint32(unsafe.Sizeof(sa)) 206 sa.InheritHandle = 1 207 return &sa 208 } 209 210 func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, err error) { 211 if len(path) == 0 { 212 return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND 213 } 214 pathp, err := syscall.UTF16PtrFromString(path) 215 if err != nil { 216 return syscall.InvalidHandle, err 217 } 218 var access uint32 219 switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) { 220 case syscall.O_RDONLY: 221 access = syscall.GENERIC_READ 222 case syscall.O_WRONLY: 223 access = syscall.GENERIC_WRITE 224 case syscall.O_RDWR: 225 access = syscall.GENERIC_READ | syscall.GENERIC_WRITE 226 } 227 if mode&syscall.O_CREAT != 0 { 228 access |= syscall.GENERIC_WRITE 229 } 230 if mode&syscall.O_APPEND != 0 { 231 access &^= syscall.GENERIC_WRITE 232 access |= syscall.FILE_APPEND_DATA 233 } 234 sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE) 235 var sa *syscall.SecurityAttributes 236 if mode&syscall.O_CLOEXEC == 0 { 237 sa = makeInheritSa() 238 } 239 var createmode uint32 240 switch { 241 case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): 242 createmode = syscall.CREATE_NEW 243 case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC): 244 createmode = syscall.CREATE_ALWAYS 245 case mode&syscall.O_CREAT == syscall.O_CREAT: 246 createmode = syscall.OPEN_ALWAYS 247 case mode&syscall.O_TRUNC == syscall.O_TRUNC: 248 createmode = syscall.TRUNCATE_EXISTING 249 default: 250 createmode = syscall.OPEN_EXISTING 251 } 252 // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. 253 //https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx 254 const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN 255 h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) 256 return h, e 257 } 258 259 // ForceRemoveAll is the same as os.RemoveAll, but uses hcsshim.DestroyLayer in order 260 // to delete container layers. 261 func ForceRemoveAll(path string) error { 262 info := hcsshim.DriverInfo{ 263 HomeDir: filepath.Dir(path), 264 } 265 266 return hcsshim.DestroyLayer(info, filepath.Base(path)) 267 }