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