github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/graphdriver/devmapper/devmapper_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper"
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"os/exec"
    10  	"syscall"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/docker/docker/daemon/graphdriver"
    15  	"github.com/docker/docker/daemon/graphdriver/graphtest"
    16  	"github.com/docker/docker/pkg/idtools"
    17  	"github.com/docker/docker/pkg/parsers/kernel"
    18  	"golang.org/x/sys/unix"
    19  )
    20  
    21  func init() {
    22  	// Reduce the size of the base fs and loopback for the tests
    23  	defaultDataLoopbackSize = 300 * 1024 * 1024
    24  	defaultMetaDataLoopbackSize = 200 * 1024 * 1024
    25  	defaultBaseFsSize = 300 * 1024 * 1024
    26  	defaultUdevSyncOverride = true
    27  	if err := initLoopbacks(); err != nil {
    28  		panic(err)
    29  	}
    30  }
    31  
    32  // initLoopbacks ensures that the loopback devices are properly created within
    33  // the system running the device mapper tests.
    34  func initLoopbacks() error {
    35  	statT, err := getBaseLoopStats()
    36  	if err != nil {
    37  		return err
    38  	}
    39  	// create at least 128 loopback files, since a few first ones
    40  	// might be already in use by the host OS
    41  	for i := 0; i < 128; i++ {
    42  		loopPath := fmt.Sprintf("/dev/loop%d", i)
    43  		// only create new loopback files if they don't exist
    44  		if _, err := os.Stat(loopPath); err != nil {
    45  			if mkerr := syscall.Mknod(loopPath,
    46  				uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil { //nolint: unconvert
    47  				return mkerr
    48  			}
    49  			os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
    50  		}
    51  	}
    52  	return nil
    53  }
    54  
    55  // getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
    56  // loop0 device on the system.  If it does not exist we assume 0,0,0660 for the
    57  // stat data
    58  func getBaseLoopStats() (*syscall.Stat_t, error) {
    59  	loop0, err := os.Stat("/dev/loop0")
    60  	if err != nil {
    61  		if os.IsNotExist(err) {
    62  			return &syscall.Stat_t{
    63  				Uid:  0,
    64  				Gid:  0,
    65  				Mode: 0660,
    66  			}, nil
    67  		}
    68  		return nil, err
    69  	}
    70  	return loop0.Sys().(*syscall.Stat_t), nil
    71  }
    72  
    73  // This avoids creating a new driver for each test if all tests are run
    74  // Make sure to put new tests between TestDevmapperSetup and TestDevmapperTeardown
    75  func TestDevmapperSetup(t *testing.T) {
    76  	graphtest.GetDriver(t, "devicemapper")
    77  }
    78  
    79  func TestDevmapperCreateEmpty(t *testing.T) {
    80  	graphtest.DriverTestCreateEmpty(t, "devicemapper")
    81  }
    82  
    83  func TestDevmapperCreateBase(t *testing.T) {
    84  	graphtest.DriverTestCreateBase(t, "devicemapper")
    85  }
    86  
    87  func TestDevmapperCreateSnap(t *testing.T) {
    88  	graphtest.DriverTestCreateSnap(t, "devicemapper")
    89  }
    90  
    91  func TestDevmapperTeardown(t *testing.T) {
    92  	graphtest.PutDriver(t)
    93  }
    94  
    95  func TestDevmapperReduceLoopBackSize(t *testing.T) {
    96  	tenMB := int64(10 * 1024 * 1024)
    97  	testChangeLoopBackSize(t, -tenMB, defaultDataLoopbackSize, defaultMetaDataLoopbackSize)
    98  }
    99  
   100  func TestDevmapperIncreaseLoopBackSize(t *testing.T) {
   101  	tenMB := int64(10 * 1024 * 1024)
   102  	testChangeLoopBackSize(t, tenMB, defaultDataLoopbackSize+tenMB, defaultMetaDataLoopbackSize+tenMB)
   103  }
   104  
   105  func testChangeLoopBackSize(t *testing.T, delta, expectDataSize, expectMetaDataSize int64) {
   106  	driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
   107  	defer graphtest.PutDriver(t)
   108  	// make sure data or metadata loopback size are the default size
   109  	if s := driver.DeviceSet.Status(); s.Data.Total != uint64(defaultDataLoopbackSize) || s.Metadata.Total != uint64(defaultMetaDataLoopbackSize) {
   110  		t.Fatal("data or metadata loop back size is incorrect")
   111  	}
   112  	if err := driver.Cleanup(); err != nil {
   113  		t.Fatal(err)
   114  	}
   115  	// Reload
   116  	d, err := Init(driver.home, []string{
   117  		fmt.Sprintf("dm.loopdatasize=%d", defaultDataLoopbackSize+delta),
   118  		fmt.Sprintf("dm.loopmetadatasize=%d", defaultMetaDataLoopbackSize+delta),
   119  	}, idtools.IdentityMapping{})
   120  	if err != nil {
   121  		t.Fatalf("error creating devicemapper driver: %v", err)
   122  	}
   123  	driver = d.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
   124  	if s := driver.DeviceSet.Status(); s.Data.Total != uint64(expectDataSize) || s.Metadata.Total != uint64(expectMetaDataSize) {
   125  		t.Fatal("data or metadata loop back size is incorrect")
   126  	}
   127  	if err := driver.Cleanup(); err != nil {
   128  		t.Fatal(err)
   129  	}
   130  }
   131  
   132  // Make sure devices.Lock() has been release upon return from cleanupDeletedDevices() function
   133  func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) {
   134  	driver := graphtest.GetDriver(t, "devicemapper").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
   135  	defer graphtest.PutDriver(t)
   136  
   137  	// Call cleanupDeletedDevices() and after the call take and release
   138  	// DeviceSet Lock. If lock has not been released, this will hang.
   139  	driver.DeviceSet.cleanupDeletedDevices()
   140  
   141  	doneChan := make(chan bool, 1)
   142  
   143  	go func() {
   144  		driver.DeviceSet.Lock()
   145  		defer driver.DeviceSet.Unlock()
   146  		doneChan <- true
   147  	}()
   148  
   149  	select {
   150  	case <-time.After(time.Second * 5):
   151  		// Timer expired. That means lock was not released upon
   152  		// function return and we are deadlocked. Release lock
   153  		// here so that cleanup could succeed and fail the test.
   154  		driver.DeviceSet.Unlock()
   155  		t.Fatal("Could not acquire devices lock after call to cleanupDeletedDevices()")
   156  	case <-doneChan:
   157  	}
   158  }
   159  
   160  // Ensure that mounts aren't leakedriver. It's non-trivial for us to test the full
   161  // reproducer of #34573 in a unit test, but we can at least make sure that a
   162  // simple command run in a new namespace doesn't break things horribly.
   163  func TestDevmapperMountLeaks(t *testing.T) {
   164  	if !kernel.CheckKernelVersion(3, 18, 0) {
   165  		t.Skipf("kernel version <3.18.0 and so is missing torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe.")
   166  	}
   167  
   168  	driver := graphtest.GetDriver(t, "devicemapper", "dm.use_deferred_removal=false", "dm.use_deferred_deletion=false").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver)
   169  	defer graphtest.PutDriver(t)
   170  
   171  	// We need to create a new (dummy) device.
   172  	if err := driver.Create("some-layer", "", nil); err != nil {
   173  		t.Fatalf("setting up some-layer: %v", err)
   174  	}
   175  
   176  	// Mount the device.
   177  	_, err := driver.Get("some-layer", "")
   178  	if err != nil {
   179  		t.Fatalf("mounting some-layer: %v", err)
   180  	}
   181  
   182  	// Create a new subprocess which will inherit our mountpoint, then
   183  	// intentionally leak it and stick around. We can't do this entirely within
   184  	// Go because forking and namespaces in Go are really not handled well at
   185  	// all.
   186  	cmd := exec.Cmd{
   187  		Path: "/bin/sh",
   188  		Args: []string{
   189  			"/bin/sh", "-c",
   190  			"mount --make-rprivate / && sleep 1000s",
   191  		},
   192  		SysProcAttr: &syscall.SysProcAttr{
   193  			Unshareflags: syscall.CLONE_NEWNS,
   194  		},
   195  	}
   196  	if err := cmd.Start(); err != nil {
   197  		t.Fatalf("starting sub-command: %v", err)
   198  	}
   199  	defer func() {
   200  		unix.Kill(cmd.Process.Pid, unix.SIGKILL)
   201  		cmd.Wait()
   202  	}()
   203  
   204  	// Now try to "drop" the device.
   205  	if err := driver.Put("some-layer"); err != nil {
   206  		t.Fatalf("unmounting some-layer: %v", err)
   207  	}
   208  }