github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/daemon/graphdriver/overlay2/check.go (about)

     1  // +build linux
     2  
     3  package overlay2
     4  
     5  import (
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"syscall"
    12  
    13  	"github.com/docker/docker/pkg/system"
    14  	"github.com/pkg/errors"
    15  	"github.com/sirupsen/logrus"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  // doesSupportNativeDiff checks whether the filesystem has a bug
    20  // which copies up the opaque flag when copying up an opaque
    21  // directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR.
    22  // When these exist naive diff should be used.
    23  func doesSupportNativeDiff(d string) error {
    24  	td, err := ioutil.TempDir(d, "opaque-bug-check")
    25  	if err != nil {
    26  		return err
    27  	}
    28  	defer func() {
    29  		if err := os.RemoveAll(td); err != nil {
    30  			logrus.Warnf("Failed to remove check directory %v: %v", td, err)
    31  		}
    32  	}()
    33  
    34  	// Make directories l1/d, l1/d1, l2/d, l3, work, merged
    35  	if err := os.MkdirAll(filepath.Join(td, "l1", "d"), 0755); err != nil {
    36  		return err
    37  	}
    38  	if err := os.MkdirAll(filepath.Join(td, "l1", "d1"), 0755); err != nil {
    39  		return err
    40  	}
    41  	if err := os.MkdirAll(filepath.Join(td, "l2", "d"), 0755); err != nil {
    42  		return err
    43  	}
    44  	if err := os.Mkdir(filepath.Join(td, "l3"), 0755); err != nil {
    45  		return err
    46  	}
    47  	if err := os.Mkdir(filepath.Join(td, "work"), 0755); err != nil {
    48  		return err
    49  	}
    50  	if err := os.Mkdir(filepath.Join(td, "merged"), 0755); err != nil {
    51  		return err
    52  	}
    53  
    54  	// Mark l2/d as opaque
    55  	if err := system.Lsetxattr(filepath.Join(td, "l2", "d"), "trusted.overlay.opaque", []byte("y"), 0); err != nil {
    56  		return errors.Wrap(err, "failed to set opaque flag on middle layer")
    57  	}
    58  
    59  	opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, "work"))
    60  	if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil {
    61  		return errors.Wrap(err, "failed to mount overlay")
    62  	}
    63  	defer func() {
    64  		if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil {
    65  			logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err)
    66  		}
    67  	}()
    68  
    69  	// Touch file in d to force copy up of opaque directory "d" from "l2" to "l3"
    70  	if err := ioutil.WriteFile(filepath.Join(td, "merged", "d", "f"), []byte{}, 0644); err != nil {
    71  		return errors.Wrap(err, "failed to write to merged directory")
    72  	}
    73  
    74  	// Check l3/d does not have opaque flag
    75  	xattrOpaque, err := system.Lgetxattr(filepath.Join(td, "l3", "d"), "trusted.overlay.opaque")
    76  	if err != nil {
    77  		return errors.Wrap(err, "failed to read opaque flag on upper layer")
    78  	}
    79  	if string(xattrOpaque) == "y" {
    80  		return errors.New("opaque flag erroneously copied up, consider update to kernel 4.8 or later to fix")
    81  	}
    82  
    83  	// rename "d1" to "d2"
    84  	if err := os.Rename(filepath.Join(td, "merged", "d1"), filepath.Join(td, "merged", "d2")); err != nil {
    85  		// if rename failed with syscall.EXDEV, the kernel doesn't have CONFIG_OVERLAY_FS_REDIRECT_DIR enabled
    86  		if err.(*os.LinkError).Err == syscall.EXDEV {
    87  			return nil
    88  		}
    89  		return errors.Wrap(err, "failed to rename dir in merged directory")
    90  	}
    91  	// get the xattr of "d2"
    92  	xattrRedirect, err := system.Lgetxattr(filepath.Join(td, "l3", "d2"), "trusted.overlay.redirect")
    93  	if err != nil {
    94  		return errors.Wrap(err, "failed to read redirect flag on upper layer")
    95  	}
    96  
    97  	if string(xattrRedirect) == "d1" {
    98  		return errors.New("kernel has CONFIG_OVERLAY_FS_REDIRECT_DIR enabled")
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  // supportsMultipleLowerDir checks if the system supports multiple lowerdirs,
   105  // which is required for the overlay2 driver. On 4.x kernels, multiple lowerdirs
   106  // are always available (so this check isn't needed), and backported to RHEL and
   107  // CentOS 3.x kernels (3.10.0-693.el7.x86_64 and up). This function is to detect
   108  // support on those kernels, without doing a kernel version compare.
   109  func supportsMultipleLowerDir(d string) error {
   110  	td, err := ioutil.TempDir(d, "multiple-lowerdir-check")
   111  	if err != nil {
   112  		return err
   113  	}
   114  	defer func() {
   115  		if err := os.RemoveAll(td); err != nil {
   116  			logrus.Warnf("Failed to remove check directory %v: %v", td, err)
   117  		}
   118  	}()
   119  
   120  	for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
   121  		if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
   122  			return err
   123  		}
   124  	}
   125  
   126  	opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "lower2"), path.Join(td, "lower1"), path.Join(td, "upper"), path.Join(td, "work"))
   127  	if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil {
   128  		return errors.Wrap(err, "failed to mount overlay")
   129  	}
   130  	if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil {
   131  		logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err)
   132  	}
   133  	return nil
   134  }