github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/daemon/graphdriver/windows/windows.go (about) 1 //+build windows 2 3 package windows 4 5 import ( 6 "bufio" 7 "crypto/sha512" 8 "encoding/json" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path" 14 "path/filepath" 15 "strings" 16 "syscall" 17 "time" 18 "unsafe" 19 20 "github.com/Microsoft/go-winio" 21 "github.com/Microsoft/go-winio/archive/tar" 22 "github.com/Microsoft/go-winio/backuptar" 23 "github.com/Microsoft/hcsshim" 24 "github.com/Sirupsen/logrus" 25 "github.com/docker/docker/daemon/graphdriver" 26 "github.com/docker/docker/pkg/archive" 27 "github.com/docker/docker/pkg/chrootarchive" 28 "github.com/docker/docker/pkg/idtools" 29 "github.com/docker/docker/pkg/ioutils" 30 "github.com/docker/docker/pkg/longpath" 31 "github.com/docker/docker/pkg/system" 32 "github.com/vbatts/tar-split/tar/storage" 33 ) 34 35 // init registers the windows graph drivers to the register. 36 func init() { 37 graphdriver.Register("windowsfilter", InitFilter) 38 graphdriver.Register("windowsdiff", InitDiff) 39 } 40 41 const ( 42 // diffDriver is an hcsshim driver type 43 diffDriver = iota 44 // filterDriver is an hcsshim driver type 45 filterDriver 46 ) 47 48 // Driver represents a windows graph driver. 49 type Driver struct { 50 // info stores the shim driver information 51 info hcsshim.DriverInfo 52 } 53 54 var _ graphdriver.DiffGetterDriver = &Driver{} 55 56 func isTP5OrOlder() bool { 57 return system.GetOSVersion().Build <= 14300 58 } 59 60 // InitFilter returns a new Windows storage filter driver. 61 func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 62 logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) 63 d := &Driver{ 64 info: hcsshim.DriverInfo{ 65 HomeDir: home, 66 Flavour: filterDriver, 67 }, 68 } 69 return d, nil 70 } 71 72 // InitDiff returns a new Windows differencing disk driver. 73 func InitDiff(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { 74 logrus.Debugf("WindowsGraphDriver InitDiff at %s", home) 75 d := &Driver{ 76 info: hcsshim.DriverInfo{ 77 HomeDir: home, 78 Flavour: diffDriver, 79 }, 80 } 81 return d, nil 82 } 83 84 // String returns the string representation of a driver. 85 func (d *Driver) String() string { 86 switch d.info.Flavour { 87 case diffDriver: 88 return "windowsdiff" 89 case filterDriver: 90 return "windowsfilter" 91 default: 92 return "Unknown driver flavour" 93 } 94 } 95 96 // Status returns the status of the driver. 97 func (d *Driver) Status() [][2]string { 98 return [][2]string{ 99 {"Windows", ""}, 100 } 101 } 102 103 // Exists returns true if the given id is registered with this driver. 104 func (d *Driver) Exists(id string) bool { 105 rID, err := d.resolveID(id) 106 if err != nil { 107 return false 108 } 109 result, err := hcsshim.LayerExists(d.info, rID) 110 if err != nil { 111 return false 112 } 113 return result 114 } 115 116 // CreateReadWrite creates a layer that is writable for use as a container 117 // file system. 118 func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error { 119 return d.create(id, parent, mountLabel, false, storageOpt) 120 } 121 122 // Create creates a new read-only layer with the given id. 123 func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error { 124 return d.create(id, parent, mountLabel, true, storageOpt) 125 } 126 127 func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error { 128 if len(storageOpt) != 0 { 129 return fmt.Errorf("--storage-opt is not supported for windows") 130 } 131 132 rPId, err := d.resolveID(parent) 133 if err != nil { 134 return err 135 } 136 137 parentChain, err := d.getLayerChain(rPId) 138 if err != nil { 139 return err 140 } 141 142 var layerChain []string 143 144 if rPId != "" { 145 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 146 if err != nil { 147 return err 148 } 149 if _, err := os.Stat(filepath.Join(parentPath, "Files")); err == nil { 150 // This is a legitimate parent layer (not the empty "-init" layer), 151 // so include it in the layer chain. 152 layerChain = []string{parentPath} 153 } 154 } 155 156 layerChain = append(layerChain, parentChain...) 157 158 if readOnly { 159 if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil { 160 return err 161 } 162 } else { 163 var parentPath string 164 if len(layerChain) != 0 { 165 parentPath = layerChain[0] 166 } 167 168 if isTP5OrOlder() { 169 // Pre-create the layer directory, providing an ACL to give the Hyper-V Virtual Machines 170 // group access. This is necessary to ensure that Hyper-V containers can access the 171 // virtual machine data. This is not necessary post-TP5. 172 path, err := syscall.UTF16FromString(filepath.Join(d.info.HomeDir, id)) 173 if err != nil { 174 return err 175 } 176 // Give system and administrators full control, and VMs read, write, and execute. 177 // Mark these ACEs as inherited. 178 sd, err := winio.SddlToSecurityDescriptor("D:(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FRFWFX;;;S-1-5-83-0)") 179 if err != nil { 180 return err 181 } 182 err = syscall.CreateDirectory(&path[0], &syscall.SecurityAttributes{ 183 Length: uint32(unsafe.Sizeof(syscall.SecurityAttributes{})), 184 SecurityDescriptor: uintptr(unsafe.Pointer(&sd[0])), 185 }) 186 if err != nil { 187 return err 188 } 189 } 190 191 if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil { 192 return err 193 } 194 } 195 196 if _, err := os.Lstat(d.dir(parent)); err != nil { 197 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 198 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 199 } 200 return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err) 201 } 202 203 if err := d.setLayerChain(id, layerChain); err != nil { 204 if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil { 205 logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2) 206 } 207 return err 208 } 209 210 return nil 211 } 212 213 // dir returns the absolute path to the layer. 214 func (d *Driver) dir(id string) string { 215 return filepath.Join(d.info.HomeDir, filepath.Base(id)) 216 } 217 218 // Remove unmounts and removes the dir information. 219 func (d *Driver) Remove(id string) error { 220 rID, err := d.resolveID(id) 221 if err != nil { 222 return err 223 } 224 os.RemoveAll(filepath.Join(d.info.HomeDir, "sysfile-backups", rID)) // ok to fail 225 return hcsshim.DestroyLayer(d.info, rID) 226 } 227 228 // Get returns the rootfs path for the id. This will mount the dir at it's given path. 229 func (d *Driver) Get(id, mountLabel string) (string, error) { 230 logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel) 231 var dir string 232 233 rID, err := d.resolveID(id) 234 if err != nil { 235 return "", err 236 } 237 238 // Getting the layer paths must be done outside of the lock. 239 layerChain, err := d.getLayerChain(rID) 240 if err != nil { 241 return "", err 242 } 243 244 if err := hcsshim.ActivateLayer(d.info, rID); err != nil { 245 return "", err 246 } 247 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 248 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 249 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 250 } 251 return "", err 252 } 253 254 mountPath, err := hcsshim.GetLayerMountPath(d.info, rID) 255 if err != nil { 256 if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil { 257 logrus.Warnf("Failed to Deactivate %s: %s", id, err) 258 } 259 return "", err 260 } 261 262 // If the layer has a mount path, use that. Otherwise, use the 263 // folder path. 264 if mountPath != "" { 265 dir = mountPath 266 } else { 267 dir = d.dir(id) 268 } 269 270 return dir, nil 271 } 272 273 // Put adds a new layer to the driver. 274 func (d *Driver) Put(id string) error { 275 logrus.Debugf("WindowsGraphDriver Put() id %s", id) 276 277 rID, err := d.resolveID(id) 278 if err != nil { 279 return err 280 } 281 282 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 283 return err 284 } 285 return hcsshim.DeactivateLayer(d.info, rID) 286 } 287 288 // Cleanup ensures the information the driver stores is properly removed. 289 func (d *Driver) Cleanup() error { 290 return nil 291 } 292 293 // Diff produces an archive of the changes between the specified 294 // layer and its parent layer which may be "". 295 // The layer should be mounted when calling this function 296 func (d *Driver) Diff(id, parent string) (_ archive.Archive, err error) { 297 rID, err := d.resolveID(id) 298 if err != nil { 299 return 300 } 301 302 layerChain, err := d.getLayerChain(rID) 303 if err != nil { 304 return 305 } 306 307 // this is assuming that the layer is unmounted 308 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 309 return nil, err 310 } 311 defer func() { 312 if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil { 313 logrus.Warnf("Failed to Deactivate %s: %s", rID, err) 314 } 315 }() 316 317 arch, err := d.exportLayer(rID, layerChain) 318 if err != nil { 319 return 320 } 321 return ioutils.NewReadCloserWrapper(arch, func() error { 322 return arch.Close() 323 }), nil 324 } 325 326 // Changes produces a list of changes between the specified layer 327 // and its parent layer. If parent is "", then all changes will be ADD changes. 328 // The layer should be mounted when calling this function 329 func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { 330 rID, err := d.resolveID(id) 331 if err != nil { 332 return nil, err 333 } 334 parentChain, err := d.getLayerChain(rID) 335 if err != nil { 336 return nil, err 337 } 338 339 // this is assuming that the layer is unmounted 340 if err := hcsshim.UnprepareLayer(d.info, rID); err != nil { 341 return nil, err 342 } 343 defer func() { 344 if err := hcsshim.PrepareLayer(d.info, rID, parentChain); err != nil { 345 logrus.Warnf("Failed to Deactivate %s: %s", rID, err) 346 } 347 }() 348 349 r, err := hcsshim.NewLayerReader(d.info, id, parentChain) 350 if err != nil { 351 return nil, err 352 } 353 defer r.Close() 354 355 var changes []archive.Change 356 for { 357 name, _, fileInfo, err := r.Next() 358 if err == io.EOF { 359 break 360 } 361 if err != nil { 362 return nil, err 363 } 364 name = filepath.ToSlash(name) 365 if fileInfo == nil { 366 changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeDelete}) 367 } else { 368 // Currently there is no way to tell between an add and a modify. 369 changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeModify}) 370 } 371 } 372 return changes, nil 373 } 374 375 // ApplyDiff extracts the changeset from the given diff into the 376 // layer with the specified id and parent, returning the size of the 377 // new layer in bytes. 378 // The layer should not be mounted when calling this function 379 func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) { 380 if d.info.Flavour == diffDriver { 381 start := time.Now().UTC() 382 logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer") 383 destination := d.dir(id) 384 destination = filepath.Dir(destination) 385 size, err := chrootarchive.ApplyUncompressedLayer(destination, diff, nil) 386 if err != nil { 387 return 0, err 388 } 389 logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds()) 390 391 return size, nil 392 } 393 394 var layerChain []string 395 if parent != "" { 396 rPId, err := d.resolveID(parent) 397 if err != nil { 398 return 0, err 399 } 400 parentChain, err := d.getLayerChain(rPId) 401 if err != nil { 402 return 0, err 403 } 404 parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId) 405 if err != nil { 406 return 0, err 407 } 408 layerChain = append(layerChain, parentPath) 409 layerChain = append(layerChain, parentChain...) 410 } 411 412 size, err := d.importLayer(id, diff, layerChain) 413 if err != nil { 414 return 0, err 415 } 416 417 if err = d.setLayerChain(id, layerChain); err != nil { 418 return 0, err 419 } 420 421 return size, nil 422 } 423 424 // DiffSize calculates the changes between the specified layer 425 // and its parent and returns the size in bytes of the changes 426 // relative to its base filesystem directory. 427 func (d *Driver) DiffSize(id, parent string) (size int64, err error) { 428 rPId, err := d.resolveID(parent) 429 if err != nil { 430 return 431 } 432 433 changes, err := d.Changes(id, rPId) 434 if err != nil { 435 return 436 } 437 438 layerFs, err := d.Get(id, "") 439 if err != nil { 440 return 441 } 442 defer d.Put(id) 443 444 return archive.ChangesSize(layerFs, changes), nil 445 } 446 447 // CustomImageInfo is the object returned by the driver describing the base 448 // image. 449 type CustomImageInfo struct { 450 ID string 451 Name string 452 Version string 453 Path string 454 Size int64 455 CreatedTime time.Time 456 OSVersion string `json:"-"` 457 OSFeatures []string `json:"-"` 458 } 459 460 // GetCustomImageInfos returns the image infos for window specific 461 // base images which should always be present. 462 func (d *Driver) GetCustomImageInfos() ([]CustomImageInfo, error) { 463 strData, err := hcsshim.GetSharedBaseImages() 464 if err != nil { 465 return nil, fmt.Errorf("Failed to restore base images: %s", err) 466 } 467 468 type customImageInfoList struct { 469 Images []CustomImageInfo 470 } 471 472 var infoData customImageInfoList 473 474 if err = json.Unmarshal([]byte(strData), &infoData); err != nil { 475 err = fmt.Errorf("JSON unmarshal returned error=%s", err) 476 logrus.Error(err) 477 return nil, err 478 } 479 480 var images []CustomImageInfo 481 482 for _, imageData := range infoData.Images { 483 folderName := filepath.Base(imageData.Path) 484 485 // Use crypto hash of the foldername to generate a docker style id. 486 h := sha512.Sum384([]byte(folderName)) 487 id := fmt.Sprintf("%x", h[:32]) 488 489 if err := d.Create(id, "", "", nil); err != nil { 490 return nil, err 491 } 492 // Create the alternate ID file. 493 if err := d.setID(id, folderName); err != nil { 494 return nil, err 495 } 496 497 imageData.ID = id 498 499 // For now, hard code that all base images except nanoserver depend on win32k support 500 if imageData.Name != "NanoServer" { 501 imageData.OSFeatures = append(imageData.OSFeatures, "win32k") 502 } 503 504 versionData := strings.Split(imageData.Version, ".") 505 if len(versionData) != 4 { 506 logrus.Warn("Could not parse Windows version %s", imageData.Version) 507 } else { 508 // Include just major.minor.build, skip the fourth version field, which does not influence 509 // OS compatibility. 510 imageData.OSVersion = strings.Join(versionData[:3], ".") 511 } 512 513 images = append(images, imageData) 514 } 515 516 return images, nil 517 } 518 519 // GetMetadata returns custom driver information. 520 func (d *Driver) GetMetadata(id string) (map[string]string, error) { 521 m := make(map[string]string) 522 m["dir"] = d.dir(id) 523 return m, nil 524 } 525 526 func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error { 527 t := tar.NewWriter(w) 528 for { 529 name, size, fileInfo, err := r.Next() 530 if err == io.EOF { 531 break 532 } 533 if err != nil { 534 return err 535 } 536 if fileInfo == nil { 537 // Write a whiteout file. 538 hdr := &tar.Header{ 539 Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), archive.WhiteoutPrefix+filepath.Base(name))), 540 } 541 err := t.WriteHeader(hdr) 542 if err != nil { 543 return err 544 } 545 } else { 546 err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo) 547 if err != nil { 548 return err 549 } 550 } 551 } 552 return t.Close() 553 } 554 555 // exportLayer generates an archive from a layer based on the given ID. 556 func (d *Driver) exportLayer(id string, parentLayerPaths []string) (archive.Archive, error) { 557 var r hcsshim.LayerReader 558 r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths) 559 if err != nil { 560 return nil, err 561 } 562 563 archive, w := io.Pipe() 564 go func() { 565 err := writeTarFromLayer(r, w) 566 cerr := r.Close() 567 if err == nil { 568 err = cerr 569 } 570 w.CloseWithError(err) 571 }() 572 573 return archive, nil 574 } 575 576 func writeLayerFromTar(r archive.Reader, w hcsshim.LayerWriter) (int64, error) { 577 t := tar.NewReader(r) 578 hdr, err := t.Next() 579 totalSize := int64(0) 580 buf := bufio.NewWriter(nil) 581 for err == nil { 582 base := path.Base(hdr.Name) 583 if strings.HasPrefix(base, archive.WhiteoutPrefix) { 584 name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):]) 585 err = w.Remove(filepath.FromSlash(name)) 586 if err != nil { 587 return 0, err 588 } 589 hdr, err = t.Next() 590 } else if hdr.Typeflag == tar.TypeLink { 591 err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)) 592 if err != nil { 593 return 0, err 594 } 595 hdr, err = t.Next() 596 } else { 597 var ( 598 name string 599 size int64 600 fileInfo *winio.FileBasicInfo 601 ) 602 name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr) 603 if err != nil { 604 return 0, err 605 } 606 err = w.Add(filepath.FromSlash(name), fileInfo) 607 if err != nil { 608 return 0, err 609 } 610 buf.Reset(w) 611 612 // Add the Hyper-V Virutal Machine group ACE to the security descriptor 613 // for TP5 so that Xenons can access all files. This is not necessary 614 // for post-TP5 builds. 615 if isTP5OrOlder() { 616 if sddl, ok := hdr.Winheaders["sd"]; ok { 617 var ace string 618 if hdr.Typeflag == tar.TypeDir { 619 ace = "(A;OICI;0x1200a9;;;S-1-5-83-0)" 620 } else { 621 ace = "(A;;0x1200a9;;;S-1-5-83-0)" 622 } 623 if hdr.Winheaders["sd"], ok = addAceToSddlDacl(sddl, ace); !ok { 624 logrus.Debugf("failed to add VM ACE to %s", sddl) 625 } 626 } 627 } 628 629 hdr, err = backuptar.WriteBackupStreamFromTarFile(buf, t, hdr) 630 ferr := buf.Flush() 631 if ferr != nil { 632 err = ferr 633 } 634 totalSize += size 635 } 636 } 637 if err != io.EOF { 638 return 0, err 639 } 640 return totalSize, nil 641 } 642 643 func addAceToSddlDacl(sddl, ace string) (string, bool) { 644 daclStart := strings.Index(sddl, "D:") 645 if daclStart < 0 { 646 return sddl, false 647 } 648 649 dacl := sddl[daclStart:] 650 daclEnd := strings.Index(dacl, "S:") 651 if daclEnd < 0 { 652 daclEnd = len(dacl) 653 } 654 dacl = dacl[:daclEnd] 655 656 if strings.Contains(dacl, ace) { 657 return sddl, true 658 } 659 660 i := 2 661 for i+1 < len(dacl) { 662 if dacl[i] != '(' { 663 return sddl, false 664 } 665 666 if dacl[i+1] == 'A' { 667 break 668 } 669 670 i += 2 671 for p := 1; i < len(dacl) && p > 0; i++ { 672 if dacl[i] == '(' { 673 p++ 674 } else if dacl[i] == ')' { 675 p-- 676 } 677 } 678 } 679 680 return sddl[:daclStart+i] + ace + sddl[daclStart+i:], true 681 } 682 683 // importLayer adds a new layer to the tag and graph store based on the given data. 684 func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) { 685 var w hcsshim.LayerWriter 686 w, err = hcsshim.NewLayerWriter(d.info, id, parentLayerPaths) 687 if err != nil { 688 return 689 } 690 size, err = writeLayerFromTar(layerData, w) 691 if err != nil { 692 w.Close() 693 return 694 } 695 err = w.Close() 696 if err != nil { 697 return 698 } 699 return 700 } 701 702 // resolveID computes the layerID information based on the given id. 703 func (d *Driver) resolveID(id string) (string, error) { 704 content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID")) 705 if os.IsNotExist(err) { 706 return id, nil 707 } else if err != nil { 708 return "", err 709 } 710 return string(content), nil 711 } 712 713 // setID stores the layerId in disk. 714 func (d *Driver) setID(id, altID string) error { 715 err := ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600) 716 if err != nil { 717 return err 718 } 719 return nil 720 } 721 722 // getLayerChain returns the layer chain information. 723 func (d *Driver) getLayerChain(id string) ([]string, error) { 724 jPath := filepath.Join(d.dir(id), "layerchain.json") 725 content, err := ioutil.ReadFile(jPath) 726 if os.IsNotExist(err) { 727 return nil, nil 728 } else if err != nil { 729 return nil, fmt.Errorf("Unable to read layerchain file - %s", err) 730 } 731 732 var layerChain []string 733 err = json.Unmarshal(content, &layerChain) 734 if err != nil { 735 return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err) 736 } 737 738 return layerChain, nil 739 } 740 741 // setLayerChain stores the layer chain information in disk. 742 func (d *Driver) setLayerChain(id string, chain []string) error { 743 content, err := json.Marshal(&chain) 744 if err != nil { 745 return fmt.Errorf("Failed to marshall layerchain json - %s", err) 746 } 747 748 jPath := filepath.Join(d.dir(id), "layerchain.json") 749 err = ioutil.WriteFile(jPath, content, 0600) 750 if err != nil { 751 return fmt.Errorf("Unable to write layerchain file - %s", err) 752 } 753 754 return nil 755 } 756 757 type fileGetCloserWithBackupPrivileges struct { 758 path string 759 } 760 761 func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser, error) { 762 var f *os.File 763 // Open the file while holding the Windows backup privilege. This ensures that the 764 // file can be opened even if the caller does not actually have access to it according 765 // to the security descriptor. 766 err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error { 767 path := longpath.AddPrefix(filepath.Join(fg.path, filename)) 768 p, err := syscall.UTF16FromString(path) 769 if err != nil { 770 return err 771 } 772 h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) 773 if err != nil { 774 return &os.PathError{Op: "open", Path: path, Err: err} 775 } 776 f = os.NewFile(uintptr(h), path) 777 return nil 778 }) 779 return f, err 780 } 781 782 func (fg *fileGetCloserWithBackupPrivileges) Close() error { 783 return nil 784 } 785 786 type fileGetDestroyCloser struct { 787 storage.FileGetter 788 path string 789 } 790 791 func (f *fileGetDestroyCloser) Close() error { 792 // TODO: activate layers and release here? 793 return os.RemoveAll(f.path) 794 } 795 796 // DiffGetter returns a FileGetCloser that can read files from the directory that 797 // contains files for the layer differences. Used for direct access for tar-split. 798 func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { 799 id, err := d.resolveID(id) 800 if err != nil { 801 return nil, err 802 } 803 804 return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil 805 }