...

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

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

     1  package compactext4
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/Microsoft/hcsshim/ext4/internal/format"
    14  	"github.com/Microsoft/hcsshim/internal/memory"
    15  )
    16  
    17  type testFile struct {
    18  	Path        string
    19  	File        *File
    20  	Data        []byte
    21  	DataSize    int64
    22  	Link        string
    23  	ExpectError bool
    24  }
    25  
    26  var (
    27  	data []byte
    28  	name string
    29  )
    30  
    31  func init() {
    32  	data = make([]byte, BlockSize*2)
    33  	for i := range data {
    34  		data[i] = uint8(i)
    35  	}
    36  
    37  	nameb := make([]byte, 300)
    38  	for i := range nameb {
    39  		nameb[i] = byte('0' + i%10)
    40  	}
    41  	name = string(nameb)
    42  }
    43  
    44  type largeData struct {
    45  	pos int64
    46  }
    47  
    48  func (d *largeData) Read(b []byte) (int, error) {
    49  	p := d.pos
    50  	var pb [8]byte
    51  	for i := range b {
    52  		binary.LittleEndian.PutUint64(pb[:], uint64(p+int64(i)))
    53  		b[i] = pb[i%8]
    54  	}
    55  	d.pos += int64(len(b))
    56  	return len(b), nil
    57  }
    58  
    59  func (tf *testFile) Reader() io.Reader {
    60  	if tf.DataSize != 0 {
    61  		return io.LimitReader(&largeData{}, tf.DataSize)
    62  	}
    63  	return bytes.NewReader(tf.Data)
    64  }
    65  
    66  func createTestFile(t *testing.T, w *Writer, tf testFile) {
    67  	t.Helper()
    68  	var err error
    69  	if tf.File != nil {
    70  		tf.File.Size = int64(len(tf.Data))
    71  		if tf.File.Size == 0 {
    72  			tf.File.Size = tf.DataSize
    73  		}
    74  		err = w.Create(tf.Path, tf.File)
    75  	} else {
    76  		err = w.Link(tf.Link, tf.Path)
    77  	}
    78  	if tf.ExpectError && err == nil {
    79  		t.Errorf("%s: expected error", tf.Path)
    80  	} else if !tf.ExpectError && err != nil {
    81  		t.Error(err)
    82  	} else {
    83  		_, err := io.Copy(w, tf.Reader())
    84  		if err != nil {
    85  			t.Error(err)
    86  		}
    87  	}
    88  }
    89  
    90  func expectedMode(f *File) uint16 {
    91  	switch f.Mode & format.TypeMask {
    92  	case 0:
    93  		return f.Mode | S_IFREG
    94  	case S_IFLNK:
    95  		return f.Mode | 0777
    96  	default:
    97  		return f.Mode
    98  	}
    99  }
   100  
   101  func expectedSize(f *File) int64 {
   102  	switch f.Mode & format.TypeMask {
   103  	case 0, S_IFREG:
   104  		return f.Size
   105  	case S_IFLNK:
   106  		return int64(len(f.Linkname))
   107  	default:
   108  		return 0
   109  	}
   110  }
   111  
   112  func xattrsEqual(x1, x2 map[string][]byte) bool {
   113  	if len(x1) != len(x2) {
   114  		return false
   115  	}
   116  	for name, value := range x1 {
   117  		if !bytes.Equal(x2[name], value) {
   118  			return false
   119  		}
   120  	}
   121  	return true
   122  }
   123  
   124  func fileEqual(f1, f2 *File) bool {
   125  	return f1.Linkname == f2.Linkname &&
   126  		expectedSize(f1) == expectedSize(f2) &&
   127  		expectedMode(f1) == expectedMode(f2) &&
   128  		f1.Uid == f2.Uid &&
   129  		f1.Gid == f2.Gid &&
   130  		f1.Atime.Equal(f2.Atime) &&
   131  		f1.Ctime.Equal(f2.Ctime) &&
   132  		f1.Mtime.Equal(f2.Mtime) &&
   133  		f1.Crtime.Equal(f2.Crtime) &&
   134  		f1.Devmajor == f2.Devmajor &&
   135  		f1.Devminor == f2.Devminor &&
   136  		xattrsEqual(f1.Xattrs, f2.Xattrs)
   137  }
   138  
   139  func runTestsOnFiles(t *testing.T, testFiles []testFile, opts ...Option) {
   140  	t.Helper()
   141  	image := "testfs.img"
   142  	imagef, err := os.Create(image)
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	defer os.Remove(image)
   147  	defer imagef.Close()
   148  
   149  	w := NewWriter(imagef, opts...)
   150  	for _, tf := range testFiles {
   151  		createTestFile(t, w, tf)
   152  		if !tf.ExpectError && tf.File != nil {
   153  			f, err := w.Stat(tf.Path)
   154  			if err != nil {
   155  				if !strings.Contains(err.Error(), "cannot retrieve") {
   156  					t.Error(err)
   157  				}
   158  			} else if !fileEqual(f, tf.File) {
   159  				t.Errorf("%s: stat mismatch: %#v %#v", tf.Path, tf.File, f)
   160  			}
   161  		}
   162  	}
   163  
   164  	if t.Failed() {
   165  		return
   166  	}
   167  
   168  	if err := w.Close(); err != nil {
   169  		t.Fatal(err)
   170  	}
   171  
   172  	fsck(t, image)
   173  
   174  	mountPath := "testmnt"
   175  
   176  	if mountImage(t, image, mountPath) {
   177  		defer unmountImage(t, mountPath)
   178  		validated := make(map[string]*testFile)
   179  		for i := range testFiles {
   180  			tf := testFiles[len(testFiles)-i-1]
   181  			if validated[tf.Link] != nil {
   182  				// The link target was subsequently replaced. Find the
   183  				// earlier instance.
   184  				for j := range testFiles[:len(testFiles)-i-1] {
   185  					otf := testFiles[j]
   186  					if otf.Path == tf.Link && !otf.ExpectError {
   187  						tf = otf
   188  						break
   189  					}
   190  				}
   191  			}
   192  			if !tf.ExpectError && validated[tf.Path] == nil {
   193  				verifyTestFile(t, mountPath, tf)
   194  				validated[tf.Path] = &tf
   195  			}
   196  		}
   197  	}
   198  }
   199  
   200  func TestBasic(t *testing.T) {
   201  	now := time.Now()
   202  	testFiles := []testFile{
   203  		{Path: "empty", File: &File{Mode: 0644}},
   204  		{Path: "small", File: &File{Mode: 0644}, Data: data[:40]},
   205  		{Path: "time", File: &File{Atime: now, Ctime: now.Add(time.Second), Mtime: now.Add(time.Hour)}},
   206  		{Path: "block_1", File: &File{Mode: 0644}, Data: data[:BlockSize]},
   207  		{Path: "block_2", File: &File{Mode: 0644}, Data: data[:BlockSize*2]},
   208  		{Path: "symlink", File: &File{Linkname: "block_1", Mode: format.S_IFLNK}},
   209  		{Path: "symlink_59", File: &File{Linkname: name[:59], Mode: format.S_IFLNK}},
   210  		{Path: "symlink_60", File: &File{Linkname: name[:60], Mode: format.S_IFLNK}},
   211  		{Path: "symlink_120", File: &File{Linkname: name[:120], Mode: format.S_IFLNK}},
   212  		{Path: "symlink_300", File: &File{Linkname: name[:300], Mode: format.S_IFLNK}},
   213  		{Path: "dir", File: &File{Mode: format.S_IFDIR | 0755}},
   214  		{Path: "dir/fifo", File: &File{Mode: format.S_IFIFO}},
   215  		{Path: "dir/sock", File: &File{Mode: format.S_IFSOCK}},
   216  		{Path: "dir/blk", File: &File{Mode: format.S_IFBLK, Devmajor: 0x5678, Devminor: 0x1234}},
   217  		{Path: "dir/chr", File: &File{Mode: format.S_IFCHR, Devmajor: 0x5678, Devminor: 0x1234}},
   218  		{Path: "dir/hard_link", Link: "small"},
   219  	}
   220  
   221  	runTestsOnFiles(t, testFiles)
   222  }
   223  
   224  func TestLargeDirectory(t *testing.T) {
   225  	testFiles := []testFile{
   226  		{Path: "bigdir", File: &File{Mode: format.S_IFDIR | 0755}},
   227  	}
   228  	for i := 0; i < 50000; i++ {
   229  		testFiles = append(testFiles, testFile{
   230  			Path: fmt.Sprintf("bigdir/%d", i), File: &File{Mode: 0644},
   231  		})
   232  	}
   233  
   234  	runTestsOnFiles(t, testFiles)
   235  }
   236  
   237  func TestInlineData(t *testing.T) {
   238  	testFiles := []testFile{
   239  		{Path: "inline_30", File: &File{Mode: 0644}, Data: data[:30]},
   240  		{Path: "inline_60", File: &File{Mode: 0644}, Data: data[:60]},
   241  		{Path: "inline_120", File: &File{Mode: 0644}, Data: data[:120]},
   242  		{Path: "inline_full", File: &File{Mode: 0644}, Data: data[:inlineDataSize]},
   243  		{Path: "block_min", File: &File{Mode: 0644}, Data: data[:inlineDataSize+1]},
   244  	}
   245  
   246  	runTestsOnFiles(t, testFiles, InlineData)
   247  }
   248  
   249  func TestXattrs(t *testing.T) {
   250  	testFiles := []testFile{
   251  		{Path: "withsmallxattrs",
   252  			File: &File{
   253  				Mode: format.S_IFREG | 0644,
   254  				Xattrs: map[string][]byte{
   255  					"user.foo": []byte("test"),
   256  					"user.bar": []byte("test2"),
   257  				},
   258  			},
   259  		},
   260  		{Path: "withlargexattrs",
   261  			File: &File{
   262  				Mode: format.S_IFREG | 0644,
   263  				Xattrs: map[string][]byte{
   264  					"user.foo": data[:100],
   265  					"user.bar": data[:50],
   266  				},
   267  			},
   268  		},
   269  	}
   270  	runTestsOnFiles(t, testFiles)
   271  }
   272  
   273  func TestReplace(t *testing.T) {
   274  	testFiles := []testFile{
   275  		{Path: "lost+found", ExpectError: true, File: &File{}}, // can't change type
   276  		{Path: "lost+found", File: &File{Mode: format.S_IFDIR | 0777}},
   277  
   278  		{Path: "dir", File: &File{Mode: format.S_IFDIR | 0777}},
   279  		{Path: "dir/file", File: &File{}},
   280  		{Path: "dir", File: &File{Mode: format.S_IFDIR | 0700}},
   281  
   282  		{Path: "file", File: &File{}},
   283  		{Path: "file", File: &File{Mode: 0600}},
   284  		{Path: "file2", File: &File{}},
   285  		{Path: "link", Link: "file2"},
   286  		{Path: "file2", File: &File{Mode: 0600}},
   287  
   288  		{Path: "nolinks", File: &File{}},
   289  		{Path: "nolinks", ExpectError: true, Link: "file"}, // would orphan nolinks
   290  
   291  		{Path: "onelink", File: &File{}},
   292  		{Path: "onelink2", Link: "onelink"},
   293  		{Path: "onelink", Link: "file"},
   294  
   295  		{Path: "", ExpectError: true, File: &File{}},
   296  		{Path: "", ExpectError: true, Link: "file"},
   297  		{Path: "", File: &File{Mode: format.S_IFDIR | 0777}},
   298  
   299  		{Path: "smallxattr", File: &File{Xattrs: map[string][]byte{"user.foo": data[:4]}}},
   300  		{Path: "smallxattr", File: &File{Xattrs: map[string][]byte{"user.foo": data[:8]}}},
   301  
   302  		{Path: "smallxattr_delete", File: &File{Xattrs: map[string][]byte{"user.foo": data[:4]}}},
   303  		{Path: "smallxattr_delete", File: &File{}},
   304  
   305  		{Path: "largexattr", File: &File{Xattrs: map[string][]byte{"user.small": data[:8], "user.foo": data[:200]}}},
   306  		{Path: "largexattr", File: &File{Xattrs: map[string][]byte{"user.small": data[:12], "user.foo": data[:400]}}},
   307  
   308  		{Path: "largexattr", File: &File{Xattrs: map[string][]byte{"user.foo": data[:200]}}},
   309  		{Path: "largexattr_delete", File: &File{}},
   310  	}
   311  	runTestsOnFiles(t, testFiles)
   312  }
   313  
   314  func TestTime(t *testing.T) {
   315  	now := time.Now()
   316  	now2 := fsTimeToTime(timeToFsTime(now))
   317  	if now.UnixNano() != now2.UnixNano() {
   318  		t.Fatalf("%s != %s", now, now2)
   319  	}
   320  }
   321  
   322  func TestLargeFile(t *testing.T) {
   323  	testFiles := []testFile{
   324  		{Path: "small", File: &File{}, DataSize: memory.MiB},        // can't change type
   325  		{Path: "medium", File: &File{}, DataSize: 200 * memory.MiB}, // can't change type
   326  		{Path: "large", File: &File{}, DataSize: 600 * memory.MiB},  // can't change type
   327  	}
   328  	runTestsOnFiles(t, testFiles)
   329  }
   330  
   331  func TestFileLinkLimit(t *testing.T) {
   332  	testFiles := []testFile{
   333  		{Path: "file", File: &File{}},
   334  	}
   335  	for i := 0; i < format.MaxLinks; i++ {
   336  		testFiles = append(testFiles, testFile{Path: fmt.Sprintf("link%d", i), Link: "file"})
   337  	}
   338  	testFiles[len(testFiles)-1].ExpectError = true
   339  	runTestsOnFiles(t, testFiles)
   340  }
   341  
   342  func TestDirLinkLimit(t *testing.T) {
   343  	testFiles := []testFile{
   344  		{Path: "dir", File: &File{Mode: S_IFDIR}},
   345  	}
   346  	for i := 0; i < format.MaxLinks-1; i++ {
   347  		testFiles = append(testFiles, testFile{Path: fmt.Sprintf("dir/%d", i), File: &File{Mode: S_IFDIR}})
   348  	}
   349  	testFiles[len(testFiles)-1].ExpectError = true
   350  	runTestsOnFiles(t, testFiles)
   351  }
   352  
   353  func TestLargeDisk(t *testing.T) {
   354  	testFiles := []testFile{
   355  		{Path: "file", File: &File{}},
   356  	}
   357  	runTestsOnFiles(t, testFiles, MaximumDiskSize(maxMaxDiskSize))
   358  }
   359  

View as plain text