storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/mountinfo/mountinfo_linux_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5   * MinIO Cloud Storage, (C) 2017 MinIO, Inc.
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   */
    19  
    20  package mountinfo
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  	"testing"
    29  )
    30  
    31  // Tests cross device mount verification function, for both failure
    32  // and success cases.
    33  func TestCrossDeviceMountPaths(t *testing.T) {
    34  	successCase :=
    35  		`/dev/0 /path/to/0/1 type0 flags 0 0
    36  		/dev/1    /path/to/1   type1	flags 1 1
    37  		/dev/2 /path/to/1/2 type2 flags,1,2=3 2 2
    38                  /dev/3 /path/to/1.1 type3 falgs,1,2=3 3 3
    39  		`
    40  	dir, err := ioutil.TempDir("", "TestReadProcmountInfos")
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	defer os.RemoveAll(dir)
    45  	mountsPath := filepath.Join(dir, "mounts")
    46  	if err = ioutil.WriteFile(mountsPath, []byte(successCase), 0666); err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	// Failure case where we detected successfully cross device mounts.
    50  	{
    51  		var absPaths = []string{"/path/to/1"}
    52  		if err = checkCrossDevice(absPaths, mountsPath); err == nil {
    53  			t.Fatal("Expected to fail, but found success")
    54  		}
    55  
    56  		mp := []mountInfo{
    57  			{"/dev/2", "/path/to/1/2", "type2", []string{"flags"}, "2", "2"},
    58  		}
    59  		msg := fmt.Sprintf("Cross-device mounts detected on path (/path/to/1) at following locations %s. Export path should not have any sub-mounts, refusing to start.", mp)
    60  		if err.Error() != msg {
    61  			t.Fatalf("Expected msg %s, got %s", msg, err)
    62  		}
    63  	}
    64  	// Failure case when input path is not absolute.
    65  	{
    66  		var absPaths = []string{"."}
    67  		if err = checkCrossDevice(absPaths, mountsPath); err == nil {
    68  			t.Fatal("Expected to fail for non absolute paths")
    69  		}
    70  		expectedErrMsg := fmt.Sprintf("Invalid argument, path (%s) is expected to be absolute", ".")
    71  		if err.Error() != expectedErrMsg {
    72  			t.Fatalf("Expected %s, got %s", expectedErrMsg, err)
    73  		}
    74  	}
    75  	// Success case, where path doesn't have any mounts.
    76  	{
    77  		var absPaths = []string{"/path/to/x"}
    78  		if err = checkCrossDevice(absPaths, mountsPath); err != nil {
    79  			t.Fatalf("Expected success, failed instead (%s)", err)
    80  		}
    81  	}
    82  }
    83  
    84  // Tests cross device mount verification function, for both failure
    85  // and success cases.
    86  func TestCrossDeviceMount(t *testing.T) {
    87  	successCase :=
    88  		`/dev/0 /path/to/0/1 type0 flags 0 0
    89  		/dev/1    /path/to/1   type1	flags 1 1
    90  		/dev/2 /path/to/1/2 type2 flags,1,2=3 2 2
    91                  /dev/3 /path/to/1.1 type3 falgs,1,2=3 3 3
    92  		`
    93  	dir, err := ioutil.TempDir("", "TestReadProcmountInfos")
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	defer os.RemoveAll(dir)
    98  	mountsPath := filepath.Join(dir, "mounts")
    99  	if err = ioutil.WriteFile(mountsPath, []byte(successCase), 0666); err != nil {
   100  		t.Fatal(err)
   101  	}
   102  	mounts, err := readProcMounts(mountsPath)
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	// Failure case where we detected successfully cross device mounts.
   107  	{
   108  		if err = mounts.checkCrossMounts("/path/to/1"); err == nil {
   109  			t.Fatal("Expected to fail, but found success")
   110  		}
   111  
   112  		mp := []mountInfo{
   113  			{"/dev/2", "/path/to/1/2", "type2", []string{"flags"}, "2", "2"},
   114  		}
   115  		msg := fmt.Sprintf("Cross-device mounts detected on path (/path/to/1) at following locations %s. Export path should not have any sub-mounts, refusing to start.", mp)
   116  		if err.Error() != msg {
   117  			t.Fatalf("Expected msg %s, got %s", msg, err)
   118  		}
   119  	}
   120  	// Failure case when input path is not absolute.
   121  	{
   122  		if err = mounts.checkCrossMounts("."); err == nil {
   123  			t.Fatal("Expected to fail for non absolute paths")
   124  		}
   125  		expectedErrMsg := fmt.Sprintf("Invalid argument, path (%s) is expected to be absolute", ".")
   126  		if err.Error() != expectedErrMsg {
   127  			t.Fatalf("Expected %s, got %s", expectedErrMsg, err)
   128  		}
   129  	}
   130  	// Success case, where path doesn't have any mounts.
   131  	{
   132  		if err = mounts.checkCrossMounts("/path/to/x"); err != nil {
   133  			t.Fatalf("Expected success, failed instead (%s)", err)
   134  		}
   135  	}
   136  }
   137  
   138  // Tests read proc mounts file.
   139  func TestReadProcmountInfos(t *testing.T) {
   140  	successCase :=
   141  		`/dev/0 /path/to/0 type0 flags 0 0
   142  		/dev/1    /path/to/1   type1	flags 1 1
   143  		/dev/2 /path/to/2 type2 flags,1,2=3 2 2
   144  		`
   145  	dir, err := ioutil.TempDir("", "TestReadProcmountInfos")
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	defer os.RemoveAll(dir)
   150  
   151  	mountsPath := filepath.Join(dir, "mounts")
   152  	if err = ioutil.WriteFile(mountsPath, []byte(successCase), 0666); err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	// Verifies if reading each line worked properly.
   156  	{
   157  		var mounts mountInfos
   158  		mounts, err = readProcMounts(mountsPath)
   159  		if err != nil {
   160  			t.Fatal(err)
   161  		}
   162  		if len(mounts) != 3 {
   163  			t.Fatalf("expected 3 mounts, got %d", len(mounts))
   164  		}
   165  		mp := mountInfo{"/dev/0", "/path/to/0", "type0", []string{"flags"}, "0", "0"}
   166  		if !mountPointsEqual(mounts[0], mp) {
   167  			t.Errorf("got unexpected MountPoint[0]: %#v", mounts[0])
   168  		}
   169  		mp = mountInfo{"/dev/1", "/path/to/1", "type1", []string{"flags"}, "1", "1"}
   170  		if !mountPointsEqual(mounts[1], mp) {
   171  			t.Errorf("got unexpected mountInfo[1]: %#v", mounts[1])
   172  		}
   173  		mp = mountInfo{"/dev/2", "/path/to/2", "type2", []string{"flags", "1", "2=3"}, "2", "2"}
   174  		if !mountPointsEqual(mounts[2], mp) {
   175  			t.Errorf("got unexpected mountInfo[2]: %#v", mounts[2])
   176  		}
   177  	}
   178  	// Failure case mounts path doesn't exist, if not fail.
   179  	{
   180  		if _, err = readProcMounts(filepath.Join(dir, "non-existent")); err != nil && !os.IsNotExist(err) {
   181  			t.Fatal(err)
   182  		}
   183  	}
   184  }
   185  
   186  // Tests read proc mounts reader.
   187  func TestReadProcMountFrom(t *testing.T) {
   188  	successCase :=
   189  		`/dev/0 /path/to/0 type0 flags 0 0
   190  		/dev/1    /path/to/1   type1	flags 1 1
   191  		/dev/2 /path/to/2 type2 flags,1,2=3 2 2
   192  		`
   193  	// Success case, verifies if parsing works properly.
   194  	{
   195  		mounts, err := parseMountFrom(strings.NewReader(successCase))
   196  		if err != nil {
   197  			t.Errorf("expected success")
   198  		}
   199  		if len(mounts) != 3 {
   200  			t.Fatalf("expected 3 mounts, got %d", len(mounts))
   201  		}
   202  		mp := mountInfo{"/dev/0", "/path/to/0", "type0", []string{"flags"}, "0", "0"}
   203  		if !mountPointsEqual(mounts[0], mp) {
   204  			t.Errorf("got unexpected mountInfo[0]: %#v", mounts[0])
   205  		}
   206  		mp = mountInfo{"/dev/1", "/path/to/1", "type1", []string{"flags"}, "1", "1"}
   207  		if !mountPointsEqual(mounts[1], mp) {
   208  			t.Errorf("got unexpected mountInfo[1]: %#v", mounts[1])
   209  		}
   210  		mp = mountInfo{"/dev/2", "/path/to/2", "type2", []string{"flags", "1", "2=3"}, "2", "2"}
   211  		if !mountPointsEqual(mounts[2], mp) {
   212  			t.Errorf("got unexpected mountInfo[2]: %#v", mounts[2])
   213  		}
   214  	}
   215  	// Error cases where parsing fails with invalid Freq and Pass params.
   216  	{
   217  		errorCases := []string{
   218  			"/dev/1 /path/to/mount type flags a 0\n",
   219  			"/dev/2 /path/to/mount type flags 0 b\n",
   220  		}
   221  		for _, ec := range errorCases {
   222  			_, rerr := parseMountFrom(strings.NewReader(ec))
   223  			if rerr == nil {
   224  				t.Errorf("expected error")
   225  			}
   226  		}
   227  	}
   228  }
   229  
   230  // Helpers for tests.
   231  
   232  // Check if two `mountInfo` are equal.
   233  func mountPointsEqual(a, b mountInfo) bool {
   234  	if a.Device != b.Device || a.Path != b.Path || a.FSType != b.FSType || !slicesEqual(a.Options, b.Options) || a.Pass != b.Pass || a.Freq != b.Freq {
   235  		return false
   236  	}
   237  	return true
   238  }
   239  
   240  // Checks if two string slices are equal.
   241  func slicesEqual(a, b []string) bool {
   242  	if len(a) != len(b) {
   243  		return false
   244  	}
   245  	for i := range a {
   246  		if a[i] != b[i] {
   247  			return false
   248  		}
   249  	}
   250  	return true
   251  }