...

Source file src/github.com/Microsoft/hcsshim/internal/guest/storage/devicemapper/devicemapper_test.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/storage/devicemapper

     1  //go:build linux
     2  // +build linux
     3  
     4  package devicemapper
     5  
     6  import (
     7  	"errors"
     8  	"flag"
     9  	"os"
    10  	"syscall"
    11  	"testing"
    12  	"unsafe"
    13  
    14  	"golang.org/x/sys/unix"
    15  )
    16  
    17  var (
    18  	integration = flag.Bool("integration", false, "run integration tests")
    19  )
    20  
    21  func clearTestDependencies() {
    22  	removeDeviceWrapper = removeDevice
    23  	openMapperWrapper = openMapper
    24  }
    25  
    26  func TestMain(m *testing.M) {
    27  	flag.Parse()
    28  	m.Run()
    29  }
    30  
    31  func validateDevice(t *testing.T, p string, sectors int64, writable bool) {
    32  	t.Helper()
    33  	dev, err := os.OpenFile(p, os.O_RDWR|os.O_SYNC, 0)
    34  	if err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	defer dev.Close()
    38  
    39  	var size int64
    40  	_, _, errno := unix.Syscall(unix.SYS_IOCTL, dev.Fd(), unix.BLKGETSIZE64, uintptr(unsafe.Pointer(&size)))
    41  	if errno != 0 {
    42  		t.Fatal(errno)
    43  	}
    44  	if size != sectors*512 {
    45  		t.Fatalf("expected %d bytes, got %d", sectors*512, size)
    46  	}
    47  
    48  	var b [512]byte
    49  	_, err = unix.Read(int(dev.Fd()), b[:])
    50  	if !errors.Is(err, unix.EIO) {
    51  		t.Fatalf("expected EIO, got %s", err)
    52  	}
    53  	_, err = unix.Write(int(dev.Fd()), b[:])
    54  	if writable && !errors.Is(err, unix.EIO) {
    55  		t.Fatalf("expected EIO, got %s", err)
    56  	} else if !errors.Is(err, unix.EPERM) {
    57  		t.Fatalf("expected EPERM, got %s", err)
    58  	}
    59  }
    60  
    61  type device struct {
    62  	Name, Path string
    63  }
    64  
    65  func (d *device) Close() (err error) {
    66  	if d.Name != "" {
    67  		err = RemoveDevice(d.Name)
    68  		if err == nil {
    69  			d.Name = ""
    70  		}
    71  	}
    72  	return err
    73  }
    74  
    75  func createDevice(name string, flags CreateFlags, targets []Target) (*device, error) {
    76  	p, err := CreateDevice(name, flags, targets)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return &device{Name: name, Path: p}, nil
    81  }
    82  
    83  func TestCreateError(t *testing.T) {
    84  	clearTestDependencies()
    85  
    86  	if !*integration {
    87  		t.Skip()
    88  	}
    89  	d, err := createDevice("test-device", 0, []Target{
    90  		{Type: "error", SectorStart: 0, LengthInBlocks: 1},
    91  		{Type: "error", SectorStart: 1, LengthInBlocks: 2},
    92  	})
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  	defer d.Close()
    97  	validateDevice(t, d.Path, 3, true)
    98  	err = d.Close()
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  }
   103  
   104  func TestReadOnlyError(t *testing.T) {
   105  	clearTestDependencies()
   106  
   107  	if !*integration {
   108  		t.Skip()
   109  	}
   110  	d, err := createDevice("test-device", CreateReadOnly, []Target{
   111  		{Type: "error", SectorStart: 0, LengthInBlocks: 1},
   112  		{Type: "error", SectorStart: 1, LengthInBlocks: 2},
   113  	})
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	defer d.Close()
   118  	validateDevice(t, d.Path, 3, false)
   119  	err = d.Close()
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  }
   124  
   125  func TestLinearError(t *testing.T) {
   126  	clearTestDependencies()
   127  
   128  	if !*integration {
   129  		t.Skip()
   130  	}
   131  	b, err := createDevice("base-device", 0, []Target{
   132  		{Type: "error", SectorStart: 0, LengthInBlocks: 100},
   133  	})
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  	defer b.Close()
   138  	d, err := createDevice("linear-device", 0, []Target{
   139  		LinearTarget(0, 50, b.Path, 50),
   140  	})
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	defer d.Close()
   145  	validateDevice(t, d.Path, 50, true)
   146  	err = d.Close()
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  	err = b.Close()
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  }
   155  
   156  func TestRemoveDeviceRetriesOnSyscallEBUSY(t *testing.T) {
   157  	clearTestDependencies()
   158  
   159  	rmDeviceCalled := false
   160  	retryDone := false
   161  	// Overrides openMapper to return temp file handle
   162  	openMapperWrapper = func() (*os.File, error) {
   163  		return os.CreateTemp("", "")
   164  	}
   165  	removeDeviceWrapper = func(_ *os.File, _ string) error {
   166  		if !rmDeviceCalled {
   167  			rmDeviceCalled = true
   168  			return &dmError{
   169  				Op:  1,
   170  				Err: syscall.EBUSY,
   171  			}
   172  		}
   173  		if !retryDone {
   174  			retryDone = true
   175  			return nil
   176  		}
   177  		return nil
   178  	}
   179  
   180  	if err := RemoveDevice("test"); err != nil {
   181  		t.Fatalf("expected no error, got: %s", err)
   182  	}
   183  	if !rmDeviceCalled {
   184  		t.Fatalf("expected removeDevice to be called at least once")
   185  	}
   186  	if !retryDone {
   187  		t.Fatalf("expected removeDevice to be retried after initial failure")
   188  	}
   189  }
   190  
   191  func TestRemoveDeviceFailsOnNonSyscallEBUSY(t *testing.T) {
   192  	clearTestDependencies()
   193  
   194  	expectedError := &dmError{
   195  		Op:  0,
   196  		Err: syscall.EACCES,
   197  	}
   198  	rmDeviceCalled := false
   199  	retryDone := false
   200  	openMapperWrapper = func() (*os.File, error) {
   201  		return os.CreateTemp("", "")
   202  	}
   203  	removeDeviceWrapper = func(_ *os.File, _ string) error {
   204  		if !rmDeviceCalled {
   205  			rmDeviceCalled = true
   206  			return expectedError
   207  		}
   208  		if !retryDone {
   209  			retryDone = true
   210  			return nil
   211  		}
   212  		return nil
   213  	}
   214  
   215  	if err := RemoveDevice("test"); err != expectedError { //nolint:errorlint
   216  		t.Fatalf("expected error %q, instead got %q", expectedError, err)
   217  	}
   218  	if !rmDeviceCalled {
   219  		t.Fatalf("expected removeDevice to be called once")
   220  	}
   221  	if retryDone {
   222  		t.Fatalf("no retries should've been attempted")
   223  	}
   224  }
   225  

View as plain text