github.com/gdevillele/moby@v1.13.0/daemon/graphdriver/overlay2/overlay.go (about)

     1  // +build linux
     2  
     3  package overlay2
     4  
     5  import (
     6  	"bufio"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path"
    14  	"path/filepath"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  	"syscall"
    19  
    20  	"github.com/Sirupsen/logrus"
    21  
    22  	"github.com/docker/docker/daemon/graphdriver"
    23  	"github.com/docker/docker/daemon/graphdriver/overlayutils"
    24  	"github.com/docker/docker/daemon/graphdriver/quota"
    25  	"github.com/docker/docker/pkg/archive"
    26  	"github.com/docker/docker/pkg/chrootarchive"
    27  	"github.com/docker/docker/pkg/directory"
    28  	"github.com/docker/docker/pkg/fsutils"
    29  	"github.com/docker/docker/pkg/idtools"
    30  	"github.com/docker/docker/pkg/mount"
    31  	"github.com/docker/docker/pkg/parsers"
    32  	"github.com/docker/docker/pkg/parsers/kernel"
    33  	"github.com/docker/go-units"
    34  
    35  	"github.com/opencontainers/runc/libcontainer/label"
    36  )
    37  
    38  var (
    39  	// untar defines the untar method
    40  	untar = chrootarchive.UntarUncompressed
    41  )
    42  
    43  // This backend uses the overlay union filesystem for containers
    44  // with diff directories for each layer.
    45  
    46  // This version of the overlay driver requires at least kernel
    47  // 4.0.0 in order to support mounting multiple diff directories.
    48  
    49  // Each container/image has at least a "diff" directory and "link" file.
    50  // If there is also a "lower" file when there are diff layers
    51  // below as well as "merged" and "work" directories. The "diff" directory
    52  // has the upper layer of the overlay and is used to capture any
    53  // changes to the layer. The "lower" file contains all the lower layer
    54  // mounts separated by ":" and ordered from uppermost to lowermost
    55  // layers. The overlay itself is mounted in the "merged" directory,
    56  // and the "work" dir is needed for overlay to work.
    57  
    58  // The "link" file for each layer contains a unique string for the layer.
    59  // Under the "l" directory at the root there will be a symbolic link
    60  // with that unique string pointing the "diff" directory for the layer.
    61  // The symbolic links are used to reference lower layers in the "lower"
    62  // file and on mount. The links are used to shorten the total length
    63  // of a layer reference without requiring changes to the layer identifier
    64  // or root directory. Mounts are always done relative to root and
    65  // referencing the symbolic links in order to ensure the number of
    66  // lower directories can fit in a single page for making the mount
    67  // syscall. A hard upper limit of 128 lower layers is enforced to ensure
    68  // that mounts do not fail due to length.
    69  
    70  const (
    71  	driverName = "overlay2"
    72  	linkDir    = "l"
    73  	lowerFile  = "lower"
    74  	maxDepth   = 128
    75  
    76  	// idLength represents the number of random characters
    77  	// which can be used to create the unique link identifer
    78  	// for every layer. If this value is too long then the
    79  	// page size limit for the mount command may be exceeded.
    80  	// The idLength should be selected such that following equation
    81  	// is true (512 is a buffer for label metadata).
    82  	// ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512)
    83  	idLength = 26
    84  )
    85  
    86  type overlayOptions struct {
    87  	overrideKernelCheck bool
    88  	quota               quota.Quota
    89  }
    90  
    91  // Driver contains information about the home directory and the list of active mounts that are created using this driver.
    92  type Driver struct {
    93  	home          string
    94  	uidMaps       []idtools.IDMap
    95  	gidMaps       []idtools.IDMap
    96  	ctr           *graphdriver.RefCounter
    97  	quotaCtl      *quota.Control
    98  	options       overlayOptions
    99  	naiveDiff     graphdriver.DiffDriver
   100  	supportsDType bool
   101  }
   102  
   103  var (
   104  	backingFs             = "<unknown>"
   105  	projectQuotaSupported = false
   106  
   107  	useNaiveDiffLock sync.Once
   108  	useNaiveDiffOnly bool
   109  )
   110  
   111  func init() {
   112  	graphdriver.Register(driverName, Init)
   113  }
   114  
   115  // Init returns the a native diff driver for overlay filesystem.
   116  // If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error.
   117  // If an overlay filesystem is not supported over an existing filesystem then error graphdriver.ErrIncompatibleFS is returned.
   118  func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
   119  	opts, err := parseOptions(options)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	if err := supportsOverlay(); err != nil {
   125  		return nil, graphdriver.ErrNotSupported
   126  	}
   127  
   128  	// require kernel 4.0.0 to ensure multiple lower dirs are supported
   129  	v, err := kernel.GetKernelVersion()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 {
   134  		if !opts.overrideKernelCheck {
   135  			return nil, graphdriver.ErrNotSupported
   136  		}
   137  		logrus.Warn("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update")
   138  	}
   139  
   140  	fsMagic, err := graphdriver.GetFSMagic(home)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	if fsName, ok := graphdriver.FsNames[fsMagic]; ok {
   145  		backingFs = fsName
   146  	}
   147  
   148  	// check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs
   149  	switch fsMagic {
   150  	case graphdriver.FsMagicBtrfs, graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs:
   151  		logrus.Errorf("'overlay2' is not supported over %s", backingFs)
   152  		return nil, graphdriver.ErrIncompatibleFS
   153  	}
   154  
   155  	rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	// Create the driver home dir
   160  	if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
   161  		return nil, err
   162  	}
   163  
   164  	if err := mount.MakePrivate(home); err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	supportsDType, err := fsutils.SupportsDType(home)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	if !supportsDType {
   173  		// not a fatal error until v1.16 (#27443)
   174  		logrus.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs))
   175  	}
   176  
   177  	d := &Driver{
   178  		home:          home,
   179  		uidMaps:       uidMaps,
   180  		gidMaps:       gidMaps,
   181  		ctr:           graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
   182  		supportsDType: supportsDType,
   183  	}
   184  
   185  	d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps)
   186  
   187  	if backingFs == "xfs" {
   188  		// Try to enable project quota support over xfs.
   189  		if d.quotaCtl, err = quota.NewControl(home); err == nil {
   190  			projectQuotaSupported = true
   191  		}
   192  	}
   193  
   194  	logrus.Debugf("backingFs=%s,  projectQuotaSupported=%v", backingFs, projectQuotaSupported)
   195  
   196  	return d, nil
   197  }
   198  
   199  func parseOptions(options []string) (*overlayOptions, error) {
   200  	o := &overlayOptions{}
   201  	for _, option := range options {
   202  		key, val, err := parsers.ParseKeyValueOpt(option)
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  		key = strings.ToLower(key)
   207  		switch key {
   208  		case "overlay2.override_kernel_check":
   209  			o.overrideKernelCheck, err = strconv.ParseBool(val)
   210  			if err != nil {
   211  				return nil, err
   212  			}
   213  
   214  		default:
   215  			return nil, fmt.Errorf("overlay2: Unknown option %s\n", key)
   216  		}
   217  	}
   218  	return o, nil
   219  }
   220  
   221  func supportsOverlay() error {
   222  	// We can try to modprobe overlay first before looking at
   223  	// proc/filesystems for when overlay is supported
   224  	exec.Command("modprobe", "overlay").Run()
   225  
   226  	f, err := os.Open("/proc/filesystems")
   227  	if err != nil {
   228  		return err
   229  	}
   230  	defer f.Close()
   231  
   232  	s := bufio.NewScanner(f)
   233  	for s.Scan() {
   234  		if s.Text() == "nodev\toverlay" {
   235  			return nil
   236  		}
   237  	}
   238  	logrus.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
   239  	return graphdriver.ErrNotSupported
   240  }
   241  
   242  func useNaiveDiff(home string) bool {
   243  	useNaiveDiffLock.Do(func() {
   244  		if err := hasOpaqueCopyUpBug(home); err != nil {
   245  			logrus.Warnf("Not using native diff for overlay2: %v", err)
   246  			useNaiveDiffOnly = true
   247  		}
   248  	})
   249  	return useNaiveDiffOnly
   250  }
   251  
   252  func (d *Driver) String() string {
   253  	return driverName
   254  }
   255  
   256  // Status returns current driver information in a two dimensional string array.
   257  // Output contains "Backing Filesystem" used in this implementation.
   258  func (d *Driver) Status() [][2]string {
   259  	return [][2]string{
   260  		{"Backing Filesystem", backingFs},
   261  		{"Supports d_type", strconv.FormatBool(d.supportsDType)},
   262  		{"Native Overlay Diff", strconv.FormatBool(!useNaiveDiff(d.home))},
   263  	}
   264  }
   265  
   266  // GetMetadata returns meta data about the overlay driver such as
   267  // LowerDir, UpperDir, WorkDir and MergeDir used to store data.
   268  func (d *Driver) GetMetadata(id string) (map[string]string, error) {
   269  	dir := d.dir(id)
   270  	if _, err := os.Stat(dir); err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	metadata := map[string]string{
   275  		"WorkDir":   path.Join(dir, "work"),
   276  		"MergedDir": path.Join(dir, "merged"),
   277  		"UpperDir":  path.Join(dir, "diff"),
   278  	}
   279  
   280  	lowerDirs, err := d.getLowerDirs(id)
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  	if len(lowerDirs) > 0 {
   285  		metadata["LowerDir"] = strings.Join(lowerDirs, ":")
   286  	}
   287  
   288  	return metadata, nil
   289  }
   290  
   291  // Cleanup any state created by overlay which should be cleaned when daemon
   292  // is being shutdown. For now, we just have to unmount the bind mounted
   293  // we had created.
   294  func (d *Driver) Cleanup() error {
   295  	return mount.Unmount(d.home)
   296  }
   297  
   298  // CreateReadWrite creates a layer that is writable for use as a container
   299  // file system.
   300  func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
   301  	return d.Create(id, parent, opts)
   302  }
   303  
   304  // Create is used to create the upper, lower, and merge directories required for overlay fs for a given id.
   305  // The parent filesystem is used to configure these directories for the overlay.
   306  func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) {
   307  
   308  	if opts != nil && len(opts.StorageOpt) != 0 && !projectQuotaSupported {
   309  		return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option")
   310  	}
   311  
   312  	dir := d.dir(id)
   313  
   314  	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
   315  	if err != nil {
   316  		return err
   317  	}
   318  	if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil {
   319  		return err
   320  	}
   321  	if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil {
   322  		return err
   323  	}
   324  
   325  	defer func() {
   326  		// Clean up on failure
   327  		if retErr != nil {
   328  			os.RemoveAll(dir)
   329  		}
   330  	}()
   331  
   332  	if opts != nil && len(opts.StorageOpt) > 0 {
   333  		driver := &Driver{}
   334  		if err := d.parseStorageOpt(opts.StorageOpt, driver); err != nil {
   335  			return err
   336  		}
   337  
   338  		if driver.options.quota.Size > 0 {
   339  			// Set container disk quota limit
   340  			if err := d.quotaCtl.SetQuota(dir, driver.options.quota); err != nil {
   341  				return err
   342  			}
   343  		}
   344  	}
   345  
   346  	if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil {
   347  		return err
   348  	}
   349  
   350  	lid := generateID(idLength)
   351  	if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil {
   352  		return err
   353  	}
   354  
   355  	// Write link id to link file
   356  	if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil {
   357  		return err
   358  	}
   359  
   360  	// if no parent directory, done
   361  	if parent == "" {
   362  		return nil
   363  	}
   364  
   365  	if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
   366  		return err
   367  	}
   368  	if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
   369  		return err
   370  	}
   371  
   372  	lower, err := d.getLower(parent)
   373  	if err != nil {
   374  		return err
   375  	}
   376  	if lower != "" {
   377  		if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil {
   378  			return err
   379  		}
   380  	}
   381  
   382  	return nil
   383  }
   384  
   385  // Parse overlay storage options
   386  func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) error {
   387  	// Read size to set the disk project quota per container
   388  	for key, val := range storageOpt {
   389  		key := strings.ToLower(key)
   390  		switch key {
   391  		case "size":
   392  			size, err := units.RAMInBytes(val)
   393  			if err != nil {
   394  				return err
   395  			}
   396  			driver.options.quota.Size = uint64(size)
   397  		default:
   398  			return fmt.Errorf("Unknown option %s", key)
   399  		}
   400  	}
   401  
   402  	return nil
   403  }
   404  
   405  func (d *Driver) getLower(parent string) (string, error) {
   406  	parentDir := d.dir(parent)
   407  
   408  	// Ensure parent exists
   409  	if _, err := os.Lstat(parentDir); err != nil {
   410  		return "", err
   411  	}
   412  
   413  	// Read Parent link fileA
   414  	parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link"))
   415  	if err != nil {
   416  		return "", err
   417  	}
   418  	lowers := []string{path.Join(linkDir, string(parentLink))}
   419  
   420  	parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile))
   421  	if err == nil {
   422  		parentLowers := strings.Split(string(parentLower), ":")
   423  		lowers = append(lowers, parentLowers...)
   424  	}
   425  	if len(lowers) > maxDepth {
   426  		return "", errors.New("max depth exceeded")
   427  	}
   428  	return strings.Join(lowers, ":"), nil
   429  }
   430  
   431  func (d *Driver) dir(id string) string {
   432  	return path.Join(d.home, id)
   433  }
   434  
   435  func (d *Driver) getLowerDirs(id string) ([]string, error) {
   436  	var lowersArray []string
   437  	lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile))
   438  	if err == nil {
   439  		for _, s := range strings.Split(string(lowers), ":") {
   440  			lp, err := os.Readlink(path.Join(d.home, s))
   441  			if err != nil {
   442  				return nil, err
   443  			}
   444  			lowersArray = append(lowersArray, path.Clean(path.Join(d.home, linkDir, lp)))
   445  		}
   446  	} else if !os.IsNotExist(err) {
   447  		return nil, err
   448  	}
   449  	return lowersArray, nil
   450  }
   451  
   452  // Remove cleans the directories that are created for this id.
   453  func (d *Driver) Remove(id string) error {
   454  	dir := d.dir(id)
   455  	lid, err := ioutil.ReadFile(path.Join(dir, "link"))
   456  	if err == nil {
   457  		if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil {
   458  			logrus.Debugf("Failed to remove link: %v", err)
   459  		}
   460  	}
   461  
   462  	if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
   463  		return err
   464  	}
   465  	return nil
   466  }
   467  
   468  // Get creates and mounts the required file system for the given id and returns the mount path.
   469  func (d *Driver) Get(id string, mountLabel string) (s string, err error) {
   470  	dir := d.dir(id)
   471  	if _, err := os.Stat(dir); err != nil {
   472  		return "", err
   473  	}
   474  
   475  	diffDir := path.Join(dir, "diff")
   476  	lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile))
   477  	if err != nil {
   478  		// If no lower, just return diff directory
   479  		if os.IsNotExist(err) {
   480  			return diffDir, nil
   481  		}
   482  		return "", err
   483  	}
   484  
   485  	mergedDir := path.Join(dir, "merged")
   486  	if count := d.ctr.Increment(mergedDir); count > 1 {
   487  		return mergedDir, nil
   488  	}
   489  	defer func() {
   490  		if err != nil {
   491  			if c := d.ctr.Decrement(mergedDir); c <= 0 {
   492  				syscall.Unmount(mergedDir, 0)
   493  			}
   494  		}
   495  	}()
   496  
   497  	workDir := path.Join(dir, "work")
   498  	splitLowers := strings.Split(string(lowers), ":")
   499  	absLowers := make([]string, len(splitLowers))
   500  	for i, s := range splitLowers {
   501  		absLowers[i] = path.Join(d.home, s)
   502  	}
   503  	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), path.Join(dir, "diff"), path.Join(dir, "work"))
   504  	mountData := label.FormatMountLabel(opts, mountLabel)
   505  	mount := syscall.Mount
   506  	mountTarget := mergedDir
   507  
   508  	pageSize := syscall.Getpagesize()
   509  
   510  	// Go can return a larger page size than supported by the system
   511  	// as of go 1.7. This will be fixed in 1.8 and this block can be
   512  	// removed when building with 1.8.
   513  	// See https://github.com/golang/go/commit/1b9499b06989d2831e5b156161d6c07642926ee1
   514  	// See https://github.com/docker/docker/issues/27384
   515  	if pageSize > 4096 {
   516  		pageSize = 4096
   517  	}
   518  
   519  	// Use relative paths and mountFrom when the mount data has exceeded
   520  	// the page size. The mount syscall fails if the mount data cannot
   521  	// fit within a page and relative links make the mount data much
   522  	// smaller at the expense of requiring a fork exec to chroot.
   523  	if len(mountData) > pageSize {
   524  		opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work"))
   525  		mountData = label.FormatMountLabel(opts, mountLabel)
   526  		if len(mountData) > pageSize {
   527  			return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
   528  		}
   529  
   530  		mount = func(source string, target string, mType string, flags uintptr, label string) error {
   531  			return mountFrom(d.home, source, target, mType, flags, label)
   532  		}
   533  		mountTarget = path.Join(id, "merged")
   534  	}
   535  
   536  	if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil {
   537  		return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
   538  	}
   539  
   540  	// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
   541  	// user namespace requires this to move a directory from lower to upper.
   542  	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
   543  	if err != nil {
   544  		return "", err
   545  	}
   546  
   547  	if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
   548  		return "", err
   549  	}
   550  
   551  	return mergedDir, nil
   552  }
   553  
   554  // Put unmounts the mount path created for the give id.
   555  func (d *Driver) Put(id string) error {
   556  	mountpoint := path.Join(d.dir(id), "merged")
   557  	if count := d.ctr.Decrement(mountpoint); count > 0 {
   558  		return nil
   559  	}
   560  	if err := syscall.Unmount(mountpoint, 0); err != nil {
   561  		logrus.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err)
   562  	}
   563  	return nil
   564  }
   565  
   566  // Exists checks to see if the id is already mounted.
   567  func (d *Driver) Exists(id string) bool {
   568  	_, err := os.Stat(d.dir(id))
   569  	return err == nil
   570  }
   571  
   572  // isParent returns if the passed in parent is the direct parent of the passed in layer
   573  func (d *Driver) isParent(id, parent string) bool {
   574  	lowers, err := d.getLowerDirs(id)
   575  	if err != nil {
   576  		return false
   577  	}
   578  	if parent == "" && len(lowers) > 0 {
   579  		return false
   580  	}
   581  
   582  	parentDir := d.dir(parent)
   583  	var ld string
   584  	if len(lowers) > 0 {
   585  		ld = filepath.Dir(lowers[0])
   586  	}
   587  	if ld == "" && parent == "" {
   588  		return true
   589  	}
   590  	return ld == parentDir
   591  }
   592  
   593  // ApplyDiff applies the new layer into a root
   594  func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) {
   595  	if !d.isParent(id, parent) {
   596  		return d.naiveDiff.ApplyDiff(id, parent, diff)
   597  	}
   598  
   599  	applyDir := d.getDiffPath(id)
   600  
   601  	logrus.Debugf("Applying tar in %s", applyDir)
   602  	// Overlay doesn't need the parent id to apply the diff
   603  	if err := untar(diff, applyDir, &archive.TarOptions{
   604  		UIDMaps:        d.uidMaps,
   605  		GIDMaps:        d.gidMaps,
   606  		WhiteoutFormat: archive.OverlayWhiteoutFormat,
   607  	}); err != nil {
   608  		return 0, err
   609  	}
   610  
   611  	return directory.Size(applyDir)
   612  }
   613  
   614  func (d *Driver) getDiffPath(id string) string {
   615  	dir := d.dir(id)
   616  
   617  	return path.Join(dir, "diff")
   618  }
   619  
   620  // DiffSize calculates the changes between the specified id
   621  // and its parent and returns the size in bytes of the changes
   622  // relative to its base filesystem directory.
   623  func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
   624  	if useNaiveDiff(d.home) || !d.isParent(id, parent) {
   625  		return d.naiveDiff.DiffSize(id, parent)
   626  	}
   627  	return directory.Size(d.getDiffPath(id))
   628  }
   629  
   630  // Diff produces an archive of the changes between the specified
   631  // layer and its parent layer which may be "".
   632  func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) {
   633  	if useNaiveDiff(d.home) || !d.isParent(id, parent) {
   634  		return d.naiveDiff.Diff(id, parent)
   635  	}
   636  
   637  	diffPath := d.getDiffPath(id)
   638  	logrus.Debugf("Tar with options on %s", diffPath)
   639  	return archive.TarWithOptions(diffPath, &archive.TarOptions{
   640  		Compression:    archive.Uncompressed,
   641  		UIDMaps:        d.uidMaps,
   642  		GIDMaps:        d.gidMaps,
   643  		WhiteoutFormat: archive.OverlayWhiteoutFormat,
   644  	})
   645  }
   646  
   647  // Changes produces a list of changes between the specified layer
   648  // and its parent layer. If parent is "", then all changes will be ADD changes.
   649  func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
   650  	if useNaiveDiff(d.home) || !d.isParent(id, parent) {
   651  		return d.naiveDiff.Changes(id, parent)
   652  	}
   653  	// Overlay doesn't have snapshots, so we need to get changes from all parent
   654  	// layers.
   655  	diffPath := d.getDiffPath(id)
   656  	layers, err := d.getLowerDirs(id)
   657  	if err != nil {
   658  		return nil, err
   659  	}
   660  
   661  	return archive.OverlayChanges(layers, diffPath)
   662  }