github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/daemon/graphdriver/windows/windows.go (about) 1 //+build windows 2 3 package windows 4 5 import ( 6 "bufio" 7 "bytes" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "path" 15 "path/filepath" 16 "strconv" 17 "strings" 18 "sync" 19 "syscall" 20 "unsafe" 21 22 "github.com/Microsoft/go-winio" 23 "github.com/Microsoft/go-winio/archive/tar" 24 "github.com/Microsoft/go-winio/backuptar" 25 "github.com/Microsoft/hcsshim" 26 "github.com/Sirupsen/logrus" 27 "github.com/docker/docker/daemon/graphdriver" 28 "github.com/docker/docker/pkg/archive" 29 "github.com/docker/docker/pkg/idtools" 30 "github.com/docker/docker/pkg/ioutils" 31 "github.com/docker/docker/pkg/longpath" 32 "github.com/docker/docker/pkg/reexec" 33 "github.com/docker/go-units" 34 ) 35 36 // filterDriver is an HCSShim driver type for the Windows Filter driver. 37 const filterDriver = 1 38 39 var ( 40 vmcomputedll = syscall.NewLazyDLL("vmcompute.dll") 41 hcsExpandSandboxSize = vmcomputedll.NewProc("ExpandSandboxSize") 42 hcsSandboxSizeSupported = hcsExpandSandboxSize.Find() == nil 43 44 // mutatedFiles is a list of files that are mutated by the import process 45 // and must be backed up and restored. 46 mutatedFiles = map[string]string{ 47 "UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak", 48 "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak", 49 "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak", 50 "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak", 51 } 52 ) 53 54 // init registers the windows graph drivers to the register. 55 func init() { 56 graphdriver.Register("windowsfilter", InitFilter) 57 reexec.Register("docker-windows-write-layer", writeLayer) 58 } 59 60 type checker struct { 61 } 62 63 func (c *checker) IsMounted(path string) bool { 64 return false 65 } 66 67 // Driver represents a windows graph driver. 68 type Driver struct { 69 // info stores the shim driver information 70 info hcsshim.DriverInfo 71 ctr *graphdriver.RefCounter 72 // it is safe for windows to use a cache here because it does not support 73 // restoring containers when the daemon dies. 74 cacheMu sync.Mutex 75 cache map[string]string 76 } 77 78 // InitFilter returns a new Windows storage filter driver. 79 func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 80 logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) 81 82 fsType, err := getFileSystemType(string(home[0])) 83 if err != nil { 84 return nil, err 85 } 86 if strings.ToLower(fsType) == "refs" { 87 return nil, fmt.Errorf("%s is on an ReFS volume - ReFS volumes are not supported", home) 88 } 89 90 d := &Driver{ 91 info: hcsshim.DriverInfo{ 92 HomeDir: home, 93 Flavour: filterDriver, 94 }, 95 cache: make(map[string]string), 96 ctr: graphdriver.NewRefCounter(&checker{}), 97 } 98 return d, nil 99 } 100 101 // win32FromHresult is a helper function to get the win32 error code from an HRESULT 102 func win32FromHresult(hr uintptr) uintptr { 103 if hr&0x1fff0000 == 0x00070000 { 104 return hr & 0xffff 105 } 106 return hr 107 } 108 109 // getFileSystemType obtains the type of a file system through GetVolumeInformation 110 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx 111 func getFileSystemType(drive string) (fsType string, hr error) { 112 var ( 113 modkernel32 = syscall.NewLazyDLL("kernel32.dll") 114 procGetVolumeInformation = modkernel32.NewProc("GetVolumeInformationW") 115 buf = make([]uint16, 255) 116 size = syscall.MAX_PATH + 1 117 ) 118 if len(drive) != 1 { 119 hr = errors.New("getFileSystemType must be called with a drive letter") 120 return 121 } 122 drive += `:\` 123 n := uintptr(unsafe.Pointer(nil)) 124 r0, _, _ := syscall.Syscall9(procGetVolumeInformation.Addr(), 8, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(drive))), n, n, n, n, n, uintptr(unsafe.Pointer(&buf[0])), uintptr(size), 0) 125 if int32(r0) < 0 { 126 hr = syscall.Errno(win32FromHresult(r0)) 127 } 128 fsType = syscall.UTF16ToString(buf) 129 return 130 } 131 132 // String returns the string representation of a driver. This should match 133 // the name the graph driver has been registered with. 134 func (d *Driver) String() string { 135 return "windowsfilter" 136 } 137 138 // Status returns the status of the driver. 139 func (d *Driver) Status() [][2]string { 140 return [][2]string{ 141 {"Windows", ""}, 142 } 143 } 144 145 // Exists returns true if the given id is registered with this driver. 146 func (d *Driver) Exists(id string) bool { 147 rID, err := d.resolveID(id) 148 if err != nil { 149 return false 150 } 151 result, err := hcsshim.LayerExists(d.info, rID) 152 if err != nil { 153 return false 154 } 155 return result 156 } 157 158 // CreateReadWrite creates a layer that is writable for use as a container 159 // file system. 160 func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error { 161 return d.create(id, parent, mountLabel, false, storageOpt) 162 } 163 164 // Create creates a new read-only layer with the given id. 165 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error { 166 return d.create(id, parent, mountLabel, true, storageOpt) 167 } 168 169 func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error { 170 rPId, err := d.resolveID(parent) 171 if err != nil { 172 return err 173 } 174 175 parentChain, err := d.getLayerChain(rPId) 176 if err != nil { 177 return err 178 } 179 180 var layerChain []string 181 182 if rPId != "" { 183 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 184 if err != nil { 185 return err 186 } 187 if _, err := os.Stat(filepath.Join(parentPath, "Files")); err == nil { 188 // This is a legitimate parent layer (not the empty "-init" layer), 189 // so include it in the layer chain. 190 layerChain = []string{parentPath} 191 } 192 } 193 194 layerChain = append(layerChain, parentChain...) 195 196 if readOnly { 197 if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil { 198 return err 199 } 200 } else { 201 var parentPath string 202 if len(layerChain) != 0 { 203 parentPath = layerChain[0] 204 } 205 206 if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil { 207 return err 208 } 209 210 storageOptions, err := parseStorageOpt(storageOpt) 211 if err != nil { 212 return fmt.Errorf("Failed to parse storage options - %s", err) 213 } 214 215 if hcsSandboxSizeSupported { 216 if err := hcsshim.ExpandSandboxSize(d.info, id, storageOptions.size); err != nil { 217 return err 218 } 219 } 220 } 221 222 if _, err := os.Lstat(d.dir(parent)); err != nil { 223 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 224 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 225 } 226 return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err) 227 } 228 229 if err := d.setLayerChain(id, layerChain); err != nil { 230 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 231 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 232 } 233 return err 234 } 235 236 return nil 237 } 238 239 // dir returns the absolute path to the layer. 240 func (d *Driver) dir(id string) string { 241 return filepath.Join(d.info.HomeDir, filepath.Base(id)) 242 } 243 244 // Remove unmounts and removes the dir information. 245 func (d *Driver) Remove(id string) error { 246 rID, err := d.resolveID(id) 247 if err != nil { 248 return err 249 } 250 os.RemoveAll(filepath.Join(d.info.HomeDir, "sysfile-backups", rID)) // ok to fail 251 return hcsshim.DestroyLayer(d.info, rID) 252 } 253 254 // Get returns the rootfs path for the id. This will mount the dir at its given path. 255 func (d *Driver) Get(id, mountLabel string) (string, error) { 256 logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel) 257 var dir string 258 259 rID, err := d.resolveID(id) 260 if err != nil { 261 return "", err 262 } 263 if count := d.ctr.Increment(rID); count > 1 { 264 return d.cache[rID], nil 265 } 266 267 // Getting the layer paths must be done outside of the lock. 268 layerChain, err := d.getLayerChain(rID) 269 if err != nil { 270 d.ctr.Decrement(rID) 271 return "", err 272 } 273 274 if err := hcsshim.ActivateLayer(d.info, rID); err != nil { 275 d.ctr.Decrement(rID) 276 return "", err 277 } 278 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 279 d.ctr.Decrement(rID) 280 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 281 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 282 } 283 return "", err 284 } 285 286 mountPath, err := hcsshim.GetLayerMountPath(d.info, rID) 287 if err != nil { 288 d.ctr.Decrement(rID) 289 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 290 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 291 } 292 return "", err 293 } 294 d.cacheMu.Lock() 295 d.cache[rID] = mountPath 296 d.cacheMu.Unlock() 297 298 // If the layer has a mount path, use that. Otherwise, use the 299 // folder path. 300 if mountPath != "" { 301 dir = mountPath 302 } else { 303 dir = d.dir(id) 304 } 305 306 return dir, nil 307 } 308 309 // Put adds a new layer to the driver. 310 func (d *Driver) Put(id string) error { 311 logrus.Debugf("WindowsGraphDriver Put() id %s", id) 312 313 rID, err := d.resolveID(id) 314 if err != nil { 315 return err 316 } 317 if count := d.ctr.Decrement(rID); count > 0 { 318 return nil 319 } 320 d.cacheMu.Lock() 321 delete(d.cache, rID) 322 d.cacheMu.Unlock() 323 324 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 325 return err 326 } 327 return hcsshim.DeactivateLayer(d.info, rID) 328 } 329 330 // Cleanup ensures the information the driver stores is properly removed. 331 func (d *Driver) Cleanup() error { 332 return nil 333 } 334 335 // Diff produces an archive of the changes between the specified 336 // layer and its parent layer which may be "". 337 // The layer should be mounted when calling this function 338 func (d *Driver) Diff(id, parent string) (_ archive.Archive, err error) { 339 rID, err := d.resolveID(id) 340 if err != nil { 341 return 342 } 343 344 layerChain, err := d.getLayerChain(rID) 345 if err != nil { 346 return 347 } 348 349 // this is assuming that the layer is unmounted 350 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 351 return nil, err 352 } 353 prepare := func() { 354 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 355 logrus.Warnf("Failed to Deactivate %s: %s", rID, err) 356 } 357 } 358 359 arch, err := d.exportLayer(rID, layerChain) 360 if err != nil { 361 prepare() 362 return 363 } 364 return ioutils.NewReadCloserWrapper(arch, func() error { 365 err := arch.Close() 366 prepare() 367 return err 368 }), nil 369 } 370 371 // Changes produces a list of changes between the specified layer 372 // and its parent layer. If parent is "", then all changes will be ADD changes. 373 // The layer should be mounted when calling this function 374 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 375 rID, err := d.resolveID(id) 376 if err != nil { 377 return nil, err 378 } 379 parentChain, err := d.getLayerChain(rID) 380 if err != nil { 381 return nil, err 382 } 383 384 // this is assuming that the layer is unmounted 385 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 386 return nil, err 387 } 388 defer func() { 389 if err := hcsshim.PrepareLayer(d.info, rID, parentChain); err != nil { 390 logrus.Warnf("Failed to Deactivate %s: %s", rID, err) 391 } 392 }() 393 394 var changes []archive.Change 395 err = winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 396 r, err := hcsshim.NewLayerReader(d.info, id, parentChain) 397 if err != nil { 398 return err 399 } 400 defer r.Close() 401 402 for { 403 name, _, fileInfo, err := r.Next() 404 if err == io.EOF { 405 return nil 406 } 407 if err != nil { 408 return err 409 } 410 name = filepath.ToSlash(name) 411 if fileInfo == nil { 412 changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeDelete}) 413 } else { 414 // Currently there is no way to tell between an add and a modify. 415 changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeModify}) 416 } 417 } 418 }) 419 if err != nil { 420 return nil, err 421 } 422 423 return changes, nil 424 } 425 426 // ApplyDiff extracts the changeset from the given diff into the 427 // layer with the specified id and parent, returning the size of the 428 // new layer in bytes. 429 // The layer should not be mounted when calling this function 430 func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) { 431 var layerChain []string 432 if parent != "" { 433 rPId, err := d.resolveID(parent) 434 if err != nil { 435 return 0, err 436 } 437 parentChain, err := d.getLayerChain(rPId) 438 if err != nil { 439 return 0, err 440 } 441 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 442 if err != nil { 443 return 0, err 444 } 445 layerChain = append(layerChain, parentPath) 446 layerChain = append(layerChain, parentChain...) 447 } 448 449 size, err := d.importLayer(id, diff, layerChain) 450 if err != nil { 451 return 0, err 452 } 453 454 if err = d.setLayerChain(id, layerChain); err != nil { 455 return 0, err 456 } 457 458 return size, nil 459 } 460 461 // DiffSize calculates the changes between the specified layer 462 // and its parent and returns the size in bytes of the changes 463 // relative to its base filesystem directory. 464 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 465 rPId, err := d.resolveID(parent) 466 if err != nil { 467 return 468 } 469 470 changes, err := d.Changes(id, rPId) 471 if err != nil { 472 return 473 } 474 475 layerFs, err := d.Get(id, "") 476 if err != nil { 477 return 478 } 479 defer d.Put(id) 480 481 return archive.ChangesSize(layerFs, changes), nil 482 } 483 484 // GetMetadata returns custom driver information. 485 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 486 m := make(map[string]string) 487 m["dir"] = d.dir(id) 488 return m, nil 489 } 490 491 func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error { 492 t := tar.NewWriter(w) 493 for { 494 name, size, fileInfo, err := r.Next() 495 if err == io.EOF { 496 break 497 } 498 if err != nil { 499 return err 500 } 501 if fileInfo == nil { 502 // Write a whiteout file. 503 hdr := &tar.Header{ 504 Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), archive.WhiteoutPrefix+filepath.Base(name))), 505 } 506 err := t.WriteHeader(hdr) 507 if err != nil { 508 return err 509 } 510 } else { 511 err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo) 512 if err != nil { 513 return err 514 } 515 } 516 } 517 return t.Close() 518 } 519 520 // exportLayer generates an archive from a layer based on the given ID. 521 func (d *Driver) exportLayer(id string, parentLayerPaths []string) (archive.Archive, error) { 522 archive, w := io.Pipe() 523 go func() { 524 err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 525 r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths) 526 if err != nil { 527 return err 528 } 529 530 err = writeTarFromLayer(r, w) 531 cerr := r.Close() 532 if err == nil { 533 err = cerr 534 } 535 return err 536 }) 537 w.CloseWithError(err) 538 }() 539 540 return archive, nil 541 } 542 543 // writeBackupStreamFromTarAndSaveMutatedFiles reads data from a tar stream and 544 // writes it to a backup stream, and also saves any files that will be mutated 545 // by the import layer process to a backup location. 546 func writeBackupStreamFromTarAndSaveMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) { 547 var bcdBackup *os.File 548 var bcdBackupWriter *winio.BackupFileWriter 549 if backupPath, ok := mutatedFiles[hdr.Name]; ok { 550 bcdBackup, err = os.Create(filepath.Join(root, backupPath)) 551 if err != nil { 552 return nil, err 553 } 554 defer func() { 555 cerr := bcdBackup.Close() 556 if err == nil { 557 err = cerr 558 } 559 }() 560 561 bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false) 562 defer func() { 563 cerr := bcdBackupWriter.Close() 564 if err == nil { 565 err = cerr 566 } 567 }() 568 569 buf.Reset(io.MultiWriter(w, bcdBackupWriter)) 570 } else { 571 buf.Reset(w) 572 } 573 574 defer func() { 575 ferr := buf.Flush() 576 if err == nil { 577 err = ferr 578 } 579 }() 580 581 return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr) 582 } 583 584 func writeLayerFromTar(r archive.Reader, w hcsshim.LayerWriter, root string) (int64, error) { 585 t := tar.NewReader(r) 586 hdr, err := t.Next() 587 totalSize := int64(0) 588 buf := bufio.NewWriter(nil) 589 for err == nil { 590 base := path.Base(hdr.Name) 591 if strings.HasPrefix(base, archive.WhiteoutPrefix) { 592 name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):]) 593 err = w.Remove(filepath.FromSlash(name)) 594 if err != nil { 595 return 0, err 596 } 597 hdr, err = t.Next() 598 } else if hdr.Typeflag == tar.TypeLink { 599 err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)) 600 if err != nil { 601 return 0, err 602 } 603 hdr, err = t.Next() 604 } else { 605 var ( 606 name string 607 size int64 608 fileInfo *winio.FileBasicInfo 609 ) 610 name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr) 611 if err != nil { 612 return 0, err 613 } 614 err = w.Add(filepath.FromSlash(name), fileInfo) 615 if err != nil { 616 return 0, err 617 } 618 hdr, err = writeBackupStreamFromTarAndSaveMutatedFiles(buf, w, t, hdr, root) 619 totalSize += size 620 } 621 } 622 if err != io.EOF { 623 return 0, err 624 } 625 return totalSize, nil 626 } 627 628 // importLayer adds a new layer to the tag and graph store based on the given data. 629 func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) { 630 cmd := reexec.Command(append([]string{"docker-windows-write-layer", d.info.HomeDir, id}, parentLayerPaths...)...) 631 output := bytes.NewBuffer(nil) 632 cmd.Stdin = layerData 633 cmd.Stdout = output 634 cmd.Stderr = output 635 636 if err = cmd.Start(); err != nil { 637 return 638 } 639 640 if err = cmd.Wait(); err != nil { 641 return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output) 642 } 643 644 return strconv.ParseInt(output.String(), 10, 64) 645 } 646 647 // writeLayer is the re-exec entry point for writing a layer from a tar file 648 func writeLayer() { 649 home := os.Args[1] 650 id := os.Args[2] 651 parentLayerPaths := os.Args[3:] 652 653 err := func() error { 654 err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}) 655 if err != nil { 656 return err 657 } 658 659 info := hcsshim.DriverInfo{ 660 Flavour: filterDriver, 661 HomeDir: home, 662 } 663 664 w, err := hcsshim.NewLayerWriter(info, id, parentLayerPaths) 665 if err != nil { 666 return err 667 } 668 669 size, err := writeLayerFromTar(os.Stdin, w, filepath.Join(home, id)) 670 if err != nil { 671 return err 672 } 673 674 err = w.Close() 675 if err != nil { 676 return err 677 } 678 679 fmt.Fprint(os.Stdout, size) 680 return nil 681 }() 682 683 if err != nil { 684 fmt.Fprint(os.Stderr, err) 685 os.Exit(1) 686 } 687 } 688 689 // resolveID computes the layerID information based on the given id. 690 func (d *Driver) resolveID(id string) (string, error) { 691 content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID")) 692 if os.IsNotExist(err) { 693 return id, nil 694 } else if err != nil { 695 return "", err 696 } 697 return string(content), nil 698 } 699 700 // setID stores the layerId in disk. 701 func (d *Driver) setID(id, altID string) error { 702 err := ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600) 703 if err != nil { 704 return err 705 } 706 return nil 707 } 708 709 // getLayerChain returns the layer chain information. 710 func (d *Driver) getLayerChain(id string) ([]string, error) { 711 jPath := filepath.Join(d.dir(id), "layerchain.json") 712 content, err := ioutil.ReadFile(jPath) 713 if os.IsNotExist(err) { 714 return nil, nil 715 } else if err != nil { 716 return nil, fmt.Errorf("Unable to read layerchain file - %s", err) 717 } 718 719 var layerChain []string 720 err = json.Unmarshal(content, &layerChain) 721 if err != nil { 722 return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err) 723 } 724 725 return layerChain, nil 726 } 727 728 // setLayerChain stores the layer chain information in disk. 729 func (d *Driver) setLayerChain(id string, chain []string) error { 730 content, err := json.Marshal(&chain) 731 if err != nil { 732 return fmt.Errorf("Failed to marshall layerchain json - %s", err) 733 } 734 735 jPath := filepath.Join(d.dir(id), "layerchain.json") 736 err = ioutil.WriteFile(jPath, content, 0600) 737 if err != nil { 738 return fmt.Errorf("Unable to write layerchain file - %s", err) 739 } 740 741 return nil 742 } 743 744 type fileGetCloserWithBackupPrivileges struct { 745 path string 746 } 747 748 func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser, error) { 749 if backupPath, ok := mutatedFiles[filename]; ok { 750 return os.Open(filepath.Join(fg.path, backupPath)) 751 } 752 753 var f *os.File 754 // Open the file while holding the Windows backup privilege. This ensures that the 755 // file can be opened even if the caller does not actually have access to it according 756 // to the security descriptor. 757 err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 758 path := longpath.AddPrefix(filepath.Join(fg.path, filename)) 759 p, err := syscall.UTF16FromString(path) 760 if err != nil { 761 return err 762 } 763 h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) 764 if err != nil { 765 return &os.PathError{Op: "open", Path: path, Err: err} 766 } 767 f = os.NewFile(uintptr(h), path) 768 return nil 769 }) 770 return f, err 771 } 772 773 func (fg *fileGetCloserWithBackupPrivileges) Close() error { 774 return nil 775 } 776 777 // DiffGetter returns a FileGetCloser that can read files from the directory that 778 // contains files for the layer differences. Used for direct access for tar-split. 779 func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { 780 id, err := d.resolveID(id) 781 if err != nil { 782 return nil, err 783 } 784 785 return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil 786 } 787 788 type storageOptions struct { 789 size uint64 790 } 791 792 func parseStorageOpt(storageOpt map[string]string) (*storageOptions, error) { 793 options := storageOptions{} 794 795 // Read size to change the block device size per container. 796 for key, val := range storageOpt { 797 key := strings.ToLower(key) 798 switch key { 799 case "size": 800 size, err := units.RAMInBytes(val) 801 if err != nil { 802 return nil, err 803 } 804 options.size = uint64(size) 805 default: 806 return nil, fmt.Errorf("Unknown storage option: %s", key) 807 } 808 } 809 return &options, nil 810 }