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