...

Source file src/github.com/Microsoft/hcsshim/ext4/internal/compactext4/verify_linux_test.go

Documentation: github.com/Microsoft/hcsshim/ext4/internal/compactext4

     1  //go:build linux
     2  // +build linux
     3  
     4  package compactext4
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"syscall"
    14  	"testing"
    15  	"time"
    16  	"unsafe"
    17  
    18  	"github.com/Microsoft/hcsshim/ext4/internal/format"
    19  )
    20  
    21  func timeEqual(ts syscall.Timespec, t time.Time) bool {
    22  	sec, nsec := t.Unix(), t.Nanosecond()
    23  	if t.IsZero() {
    24  		sec, nsec = 0, 0
    25  	}
    26  	return ts.Sec == sec && int(ts.Nsec) == nsec
    27  }
    28  
    29  func expectedDevice(f *File) uint64 {
    30  	return uint64(f.Devminor&0xff | f.Devmajor<<8 | (f.Devminor&0xffffff00)<<12)
    31  }
    32  
    33  func llistxattr(path string, b []byte) (int, error) {
    34  	pathp, err := syscall.BytePtrFromString(path)
    35  	if err != nil {
    36  		return 0, &os.PathError{Path: path, Op: "llistxattr", Err: err}
    37  	}
    38  	var p unsafe.Pointer
    39  	if len(b) > 0 {
    40  		p = unsafe.Pointer(&b[0])
    41  	}
    42  	r, _, e := syscall.Syscall(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(pathp)), uintptr(p), uintptr(len(b)))
    43  	if e != 0 {
    44  		return 0, &os.PathError{Path: path, Op: "llistxattr", Err: syscall.Errno(e)}
    45  	}
    46  	return int(r), nil
    47  }
    48  
    49  func lgetxattr(path string, name string, b []byte) (int, error) {
    50  	pathp, err := syscall.BytePtrFromString(path)
    51  	if err != nil {
    52  		return 0, &os.PathError{Path: path, Op: "llistxattr", Err: err}
    53  	}
    54  	namep, err := syscall.BytePtrFromString(name)
    55  	if err != nil {
    56  		return 0, &os.PathError{Path: path, Op: "llistxattr", Err: err}
    57  	}
    58  	var p unsafe.Pointer
    59  	if len(b) > 0 {
    60  		p = unsafe.Pointer(&b[0])
    61  	}
    62  	r, _, e := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathp)), uintptr(unsafe.Pointer(namep)), uintptr(p), uintptr(len(b)), 0, 0)
    63  	if e != 0 {
    64  		return 0, &os.PathError{Path: path, Op: "lgetxattr", Err: syscall.Errno(e)}
    65  	}
    66  	return int(r), nil
    67  }
    68  
    69  func readXattrs(path string) (map[string][]byte, error) {
    70  	xattrs := make(map[string][]byte)
    71  	var buf [4096]byte
    72  	var buf2 [4096]byte
    73  	b := buf[:]
    74  	n, err := llistxattr(path, b)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	b = b[:n]
    79  	for len(b) != 0 {
    80  		nn := bytes.IndexByte(b, 0)
    81  		name := string(b[:nn])
    82  		b = b[nn+1:]
    83  		vn, err := lgetxattr(path, name, buf2[:])
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  		value := buf2[:vn]
    88  		xattrs[name] = value
    89  	}
    90  	return xattrs, nil
    91  }
    92  
    93  func streamEqual(r1, r2 io.Reader) (bool, error) {
    94  	var b [4096]byte
    95  	var b2 [4096]byte
    96  	for {
    97  		n, err := r1.Read(b[:])
    98  		if n == 0 {
    99  			if err == io.EOF {
   100  				break
   101  			}
   102  			if err == nil {
   103  				continue
   104  			}
   105  			return false, err
   106  		}
   107  		_, err = io.ReadFull(r2, b2[:n])
   108  		if err == io.EOF || err == io.ErrUnexpectedEOF {
   109  			return false, nil
   110  		}
   111  		if err != nil {
   112  			return false, err
   113  		}
   114  		if !bytes.Equal(b[n:], b2[n:]) {
   115  			return false, nil
   116  		}
   117  	}
   118  	// Check the tail of r2
   119  	_, err := r2.Read(b[:1])
   120  	if err == nil {
   121  		return false, nil
   122  	}
   123  	if err != io.EOF {
   124  		return false, err
   125  	}
   126  	return true, nil
   127  }
   128  
   129  func verifyTestFile(t *testing.T, mountPath string, tf testFile) {
   130  	t.Helper()
   131  	name := path.Join(mountPath, tf.Path)
   132  	fi, err := os.Lstat(name)
   133  	if err != nil {
   134  		t.Error(err)
   135  		return
   136  	}
   137  	st := fi.Sys().(*syscall.Stat_t)
   138  	if tf.File != nil {
   139  		if st.Mode != uint32(expectedMode(tf.File)) ||
   140  			st.Uid != tf.File.Uid ||
   141  			st.Gid != tf.File.Gid ||
   142  			(!fi.IsDir() && st.Size != expectedSize(tf.File)) ||
   143  			st.Rdev != expectedDevice(tf.File) ||
   144  			!timeEqual(st.Atim, tf.File.Atime) ||
   145  			!timeEqual(st.Mtim, tf.File.Mtime) ||
   146  			!timeEqual(st.Ctim, tf.File.Ctime) {
   147  			t.Errorf("%s: stat mismatch, expected: %#v got: %#v", tf.Path, tf.File, st)
   148  		}
   149  
   150  		xattrs, err := readXattrs(name)
   151  		if err != nil {
   152  			t.Error(err)
   153  		} else if !xattrsEqual(xattrs, tf.File.Xattrs) {
   154  			t.Errorf("%s: xattr mismatch, expected: %#v got: %#v", tf.Path, tf.File.Xattrs, xattrs)
   155  		}
   156  
   157  		switch tf.File.Mode & format.TypeMask {
   158  		case S_IFREG:
   159  			if f, err := os.Open(name); err != nil {
   160  				t.Error(err)
   161  			} else {
   162  				same, err := streamEqual(f, tf.Reader())
   163  				if err != nil {
   164  					t.Error(err)
   165  				} else if !same {
   166  					t.Errorf("%s: data mismatch", tf.Path)
   167  				}
   168  				f.Close()
   169  			}
   170  		case S_IFLNK:
   171  			if link, err := os.Readlink(name); err != nil {
   172  				t.Error(err)
   173  			} else if link != tf.File.Linkname {
   174  				t.Errorf("%s: link mismatch, expected: %s got: %s", tf.Path, tf.File.Linkname, link)
   175  			}
   176  		}
   177  	} else {
   178  		lfi, err := os.Lstat(path.Join(mountPath, tf.Link))
   179  		if err != nil {
   180  			t.Error(err)
   181  			return
   182  		}
   183  
   184  		lst := lfi.Sys().(*syscall.Stat_t)
   185  		if lst.Ino != st.Ino {
   186  			t.Errorf("%s: hard link mismatch with %s, expected inode: %d got inode: %d", tf.Path, tf.Link, lst.Ino, st.Ino)
   187  		}
   188  	}
   189  }
   190  
   191  type capHeader struct {
   192  	version uint32
   193  	pid     int
   194  }
   195  
   196  type capData struct {
   197  	effective   uint32
   198  	permitted   uint32
   199  	inheritable uint32
   200  }
   201  
   202  const CAP_SYS_ADMIN = 21
   203  
   204  type caps struct {
   205  	hdr  capHeader
   206  	data [2]capData
   207  }
   208  
   209  func getCaps() (caps, error) {
   210  	var c caps
   211  
   212  	// Get capability version
   213  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 {
   214  		return c, fmt.Errorf("SYS_CAPGET: %v", errno)
   215  	}
   216  
   217  	// Get current capabilities
   218  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 {
   219  		return c, fmt.Errorf("SYS_CAPGET: %v", errno)
   220  	}
   221  
   222  	return c, nil
   223  }
   224  
   225  func mountImage(t *testing.T, image string, mountPath string) bool {
   226  	t.Helper()
   227  	caps, err := getCaps()
   228  	if err != nil || caps.data[0].effective&(1<<uint(CAP_SYS_ADMIN)) == 0 {
   229  		t.Log("cannot mount to run verification tests without CAP_SYS_ADMIN")
   230  		return false
   231  	}
   232  
   233  	err = os.MkdirAll(mountPath, 0777)
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  
   238  	out, err := exec.Command("mount", "-o", "loop,ro", "-t", "ext4", image, mountPath).CombinedOutput()
   239  	t.Logf("%s", out)
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	return true
   244  }
   245  
   246  func unmountImage(t *testing.T, mountPath string) {
   247  	t.Helper()
   248  	out, err := exec.Command("umount", mountPath).CombinedOutput()
   249  	t.Logf("%s", out)
   250  	if err != nil {
   251  		t.Log(err)
   252  	}
   253  }
   254  
   255  func fsck(t *testing.T, image string) {
   256  	t.Helper()
   257  	cmd := exec.Command("e2fsck", "-v", "-f", "-n", image)
   258  	out, err := cmd.CombinedOutput()
   259  	t.Logf("%s", out)
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  }
   264  

View as plain text