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