github.com/cdoern/storage@v1.12.13/pkg/idtools/idtools_unix_test.go (about)

     1  // +build !windows
     2  
     3  package idtools
     4  
     5  import (
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  type node struct {
    17  	uid int
    18  	gid int
    19  }
    20  
    21  func TestMkdirAllAs(t *testing.T) {
    22  	dirName, err := ioutil.TempDir("", "mkdirall")
    23  	if err != nil {
    24  		t.Fatalf("Couldn't create temp dir: %v", err)
    25  	}
    26  	defer os.RemoveAll(dirName)
    27  
    28  	testTree := map[string]node{
    29  		"usr":              {0, 0},
    30  		"usr/bin":          {0, 0},
    31  		"lib":              {33, 33},
    32  		"lib/x86_64":       {45, 45},
    33  		"lib/x86_64/share": {1, 1},
    34  	}
    35  
    36  	if err := buildTree(dirName, testTree); err != nil {
    37  		t.Fatal(err)
    38  	}
    39  
    40  	// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
    41  	if err := MkdirAllAs(filepath.Join(dirName, "usr", "share"), 0755, 99, 99); err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	testTree["usr/share"] = node{99, 99}
    45  	verifyTree, err := readTree(dirName, "")
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	if err := compareTrees(testTree, verifyTree); err != nil {
    50  		t.Fatal(err)
    51  	}
    52  
    53  	// test 2-deep new directories--both should be owned by the uid/gid pair
    54  	if err := MkdirAllAs(filepath.Join(dirName, "lib", "some", "other"), 0755, 101, 101); err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	testTree["lib/some"] = node{101, 101}
    58  	testTree["lib/some/other"] = node{101, 101}
    59  	verifyTree, err = readTree(dirName, "")
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	if err := compareTrees(testTree, verifyTree); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	// test a directory that already exists; should be chowned, but nothing else
    68  	if err := MkdirAllAs(filepath.Join(dirName, "usr"), 0755, 102, 102); err != nil {
    69  		t.Fatal(err)
    70  	}
    71  	testTree["usr"] = node{102, 102}
    72  	verifyTree, err = readTree(dirName, "")
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  	if err := compareTrees(testTree, verifyTree); err != nil {
    77  		t.Fatal(err)
    78  	}
    79  }
    80  
    81  func TestMkdirAllAndChownNew(t *testing.T) {
    82  	dirName, err := ioutil.TempDir("", "mkdirnew")
    83  	require.NoError(t, err)
    84  	defer os.RemoveAll(dirName)
    85  
    86  	testTree := map[string]node{
    87  		"usr":              {0, 0},
    88  		"usr/bin":          {0, 0},
    89  		"lib":              {33, 33},
    90  		"lib/x86_64":       {45, 45},
    91  		"lib/x86_64/share": {1, 1},
    92  	}
    93  	require.NoError(t, buildTree(dirName, testTree))
    94  
    95  	// test adding a directory to a pre-existing dir; only the new dir is owned by the uid/gid
    96  	err = MkdirAllAndChownNew(filepath.Join(dirName, "usr", "share"), 0755, IDPair{99, 99})
    97  	require.NoError(t, err)
    98  
    99  	testTree["usr/share"] = node{99, 99}
   100  	verifyTree, err := readTree(dirName, "")
   101  	require.NoError(t, err)
   102  	require.NoError(t, compareTrees(testTree, verifyTree))
   103  
   104  	// test 2-deep new directories--both should be owned by the uid/gid pair
   105  	err = MkdirAllAndChownNew(filepath.Join(dirName, "lib", "some", "other"), 0755, IDPair{101, 101})
   106  	require.NoError(t, err)
   107  	testTree["lib/some"] = node{101, 101}
   108  	testTree["lib/some/other"] = node{101, 101}
   109  	verifyTree, err = readTree(dirName, "")
   110  	require.NoError(t, err)
   111  	require.NoError(t, compareTrees(testTree, verifyTree))
   112  
   113  	// test a directory that already exists; should NOT be chowned
   114  	err = MkdirAllAndChownNew(filepath.Join(dirName, "usr"), 0755, IDPair{102, 102})
   115  	require.NoError(t, err)
   116  	verifyTree, err = readTree(dirName, "")
   117  	require.NoError(t, err)
   118  	require.NoError(t, compareTrees(testTree, verifyTree))
   119  }
   120  
   121  func TestMkdirAs(t *testing.T) {
   122  
   123  	dirName, err := ioutil.TempDir("", "mkdir")
   124  	if err != nil {
   125  		t.Fatalf("Couldn't create temp dir: %v", err)
   126  	}
   127  	defer os.RemoveAll(dirName)
   128  
   129  	testTree := map[string]node{
   130  		"usr": {0, 0},
   131  	}
   132  	if err := buildTree(dirName, testTree); err != nil {
   133  		t.Fatal(err)
   134  	}
   135  
   136  	// test a directory that already exists; should just chown to the requested uid/gid
   137  	if err := MkdirAs(filepath.Join(dirName, "usr"), 0755, 99, 99); err != nil {
   138  		t.Fatal(err)
   139  	}
   140  	testTree["usr"] = node{99, 99}
   141  	verifyTree, err := readTree(dirName, "")
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  	if err := compareTrees(testTree, verifyTree); err != nil {
   146  		t.Fatal(err)
   147  	}
   148  
   149  	// create a subdir under a dir which doesn't exist--should fail
   150  	if err := MkdirAs(filepath.Join(dirName, "usr", "bin", "subdir"), 0755, 102, 102); err == nil {
   151  		t.Fatalf("Trying to create a directory with Mkdir where the parent doesn't exist should have failed")
   152  	}
   153  
   154  	// create a subdir under an existing dir; should only change the ownership of the new subdir
   155  	if err := MkdirAs(filepath.Join(dirName, "usr", "bin"), 0755, 102, 102); err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	testTree["usr/bin"] = node{102, 102}
   159  	verifyTree, err = readTree(dirName, "")
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  	if err := compareTrees(testTree, verifyTree); err != nil {
   164  		t.Fatal(err)
   165  	}
   166  }
   167  
   168  func buildTree(base string, tree map[string]node) error {
   169  	for path, node := range tree {
   170  		fullPath := filepath.Join(base, path)
   171  		if err := os.MkdirAll(fullPath, 0755); err != nil {
   172  			return fmt.Errorf("Couldn't create path: %s; error: %v", fullPath, err)
   173  		}
   174  		if err := os.Chown(fullPath, node.uid, node.gid); err != nil {
   175  			return fmt.Errorf("Couldn't chown path: %s; error: %v", fullPath, err)
   176  		}
   177  	}
   178  	return nil
   179  }
   180  
   181  func readTree(base, root string) (map[string]node, error) {
   182  	tree := make(map[string]node)
   183  
   184  	dirInfos, err := ioutil.ReadDir(base)
   185  	if err != nil {
   186  		return nil, fmt.Errorf("Couldn't read directory entries for %q: %v", base, err)
   187  	}
   188  
   189  	for _, info := range dirInfos {
   190  		s := &unix.Stat_t{}
   191  		if err := unix.Stat(filepath.Join(base, info.Name()), s); err != nil {
   192  			return nil, fmt.Errorf("Can't stat file %q: %v", filepath.Join(base, info.Name()), err)
   193  		}
   194  		tree[filepath.Join(root, info.Name())] = node{int(s.Uid), int(s.Gid)}
   195  		if info.IsDir() {
   196  			// read the subdirectory
   197  			subtree, err := readTree(filepath.Join(base, info.Name()), filepath.Join(root, info.Name()))
   198  			if err != nil {
   199  				return nil, err
   200  			}
   201  			for path, nodeinfo := range subtree {
   202  				tree[path] = nodeinfo
   203  			}
   204  		}
   205  	}
   206  	return tree, nil
   207  }
   208  
   209  func compareTrees(left, right map[string]node) error {
   210  	if len(left) != len(right) {
   211  		return fmt.Errorf("Trees aren't the same size")
   212  	}
   213  	for path, nodeLeft := range left {
   214  		if nodeRight, ok := right[path]; ok {
   215  			if nodeRight.uid != nodeLeft.uid || nodeRight.gid != nodeLeft.gid {
   216  				// mismatch
   217  				return fmt.Errorf("mismatched ownership for %q: expected: %d:%d, got: %d:%d", path,
   218  					nodeLeft.uid, nodeLeft.gid, nodeRight.uid, nodeRight.gid)
   219  			}
   220  			continue
   221  		}
   222  		return fmt.Errorf("right tree didn't contain path %q", path)
   223  	}
   224  	return nil
   225  }
   226  
   227  func TestParseSubidFileWithNewlinesAndComments(t *testing.T) {
   228  	tmpDir, err := ioutil.TempDir("", "parsesubid")
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  	fnamePath := filepath.Join(tmpDir, "testsubuid")
   233  	fcontent := `tss:100000:65536
   234  # empty default subuid/subgid file
   235  
   236  dockremap:231072:65536`
   237  	if err := ioutil.WriteFile(fnamePath, []byte(fcontent), 0644); err != nil {
   238  		t.Fatal(err)
   239  	}
   240  	ranges, err := parseSubidFile(fnamePath, "dockremap")
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	if len(ranges) != 1 {
   245  		t.Fatalf("wanted 1 element in ranges, got %d instead", len(ranges))
   246  	}
   247  	if ranges[0].Start != 231072 {
   248  		t.Fatalf("wanted 231072, got %d instead", ranges[0].Start)
   249  	}
   250  	if ranges[0].Length != 65536 {
   251  		t.Fatalf("wanted 65536, got %d instead", ranges[0].Length)
   252  	}
   253  }