github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/sys/mount_linux_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package sys
    18  
    19  import (
    20  	"io/ioutil"
    21  	"os"
    22  	"path/filepath"
    23  	"syscall"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/containerd/continuity/fs/fstest"
    28  	"github.com/pkg/errors"
    29  	"golang.org/x/sys/unix"
    30  )
    31  
    32  type fMountatCaseFunc func(t *testing.T, root string)
    33  
    34  func TestFMountat(t *testing.T) {
    35  	if RunningUnprivileged() {
    36  		t.Skip("Needs to be run as root")
    37  		return
    38  	}
    39  
    40  	t.Run("Normal", makeTestForFMountat(testFMountatNormal))
    41  	t.Run("ChdirWithFileFd", makeTestForFMountat(testFMountatWithFileFd))
    42  	t.Run("MountWithInvalidSource", makeTestForFMountat(testFMountatWithInvalidSource))
    43  }
    44  
    45  func makeTestForFMountat(fn fMountatCaseFunc) func(t *testing.T) {
    46  	return func(t *testing.T) {
    47  		t.Parallel()
    48  
    49  		suiteDir, err := ioutil.TempDir("", "fmountat-test-")
    50  		if err != nil {
    51  			t.Fatal(err)
    52  		}
    53  		defer os.RemoveAll(suiteDir)
    54  
    55  		fn(t, suiteDir)
    56  	}
    57  }
    58  
    59  func testFMountatNormal(t *testing.T, root string) {
    60  	expectedContent := "bye re-exec!\n"
    61  	apply := fstest.Apply(
    62  		fstest.CreateFile("/hi", []byte(expectedContent), 0777),
    63  	)
    64  
    65  	workdir := filepath.Join(root, "work")
    66  	if err := os.MkdirAll(workdir, 0777); err != nil {
    67  		t.Fatalf("failed to create dir(%s): %+v", workdir, err)
    68  	}
    69  
    70  	if err := apply.Apply(workdir); err != nil {
    71  		t.Fatalf("failed to prepare source dir: %+v", err)
    72  	}
    73  
    74  	atdir := filepath.Join(root, "at")
    75  	if err := os.MkdirAll(atdir, 0777); err != nil {
    76  		t.Fatalf("failed to create working dir(%s): %+v", atdir, err)
    77  	}
    78  
    79  	fsdir := filepath.Join(atdir, "fs")
    80  	if err := os.MkdirAll(fsdir, 0777); err != nil {
    81  		t.Fatalf("failed to create mount point dir(%s): %+v", fsdir, err)
    82  	}
    83  
    84  	f, err := os.Open(atdir)
    85  	if err != nil {
    86  		t.Fatalf("failed to open dir(%s): %+v", atdir, err)
    87  	}
    88  	defer f.Close()
    89  
    90  	// mount work to fs
    91  	if err = FMountat(f.Fd(), workdir, "fs", "bind", unix.MS_BIND|unix.MS_RDONLY, ""); err != nil {
    92  		t.Fatalf("expected no error here, but got error: %+v", err)
    93  	}
    94  	defer umount(t, fsdir)
    95  
    96  	// check hi file
    97  	content, err := ioutil.ReadFile(filepath.Join(fsdir, "hi"))
    98  	if err != nil {
    99  		t.Fatalf("failed to read file: %+v", err)
   100  	}
   101  	if got := string(content); got != expectedContent {
   102  		t.Fatalf("expected to get(%v), but got(%v)", expectedContent, got)
   103  	}
   104  
   105  	// check the working directory
   106  	cwd, err := os.Getwd()
   107  	if err != nil {
   108  		t.Fatalf("failed to get current working dir: %+v", err)
   109  	}
   110  
   111  	if cwd == atdir {
   112  		t.Fatal("should not change the current working directory")
   113  	}
   114  }
   115  
   116  func testFMountatWithFileFd(t *testing.T, root string) {
   117  	// not a directory
   118  	expectedErr := syscall.Errno(20)
   119  
   120  	emptyFile := filepath.Join(root, "emptyFile")
   121  	f, err := os.Create(emptyFile)
   122  	if err != nil {
   123  		t.Fatalf("failed to create file(%s): %+v", emptyFile, err)
   124  	}
   125  	defer f.Close()
   126  
   127  	err = FMountat(f.Fd(), filepath.Join(root, "empty"), filepath.Join(root, "work"), "", 0, "")
   128  	if !errors.Is(err, expectedErr) {
   129  		t.Fatalf("expected error %v, but got %v", expectedErr, errors.Cause(err))
   130  	}
   131  }
   132  
   133  func testFMountatWithInvalidSource(t *testing.T, root string) {
   134  	// no such file or directory
   135  	expectedErr := syscall.Errno(2)
   136  
   137  	atdir := filepath.Join(root, "at")
   138  	if err := os.MkdirAll(atdir, 0777); err != nil {
   139  		t.Fatalf("failed to create dir(%s): %+v", atdir, err)
   140  	}
   141  
   142  	f, err := os.Open(root)
   143  	if err != nil {
   144  		t.Fatalf("failed to open dir(%s): %+v", atdir, err)
   145  	}
   146  	defer f.Close()
   147  
   148  	err = FMountat(f.Fd(), filepath.Join(root, "oops"), "at", "bind", unix.MS_BIND, "")
   149  	if !errors.Is(err, expectedErr) {
   150  		t.Fatalf("expected error %v, but got %v", expectedErr, err)
   151  	}
   152  }
   153  
   154  func umount(t *testing.T, target string) {
   155  	for i := 0; i < 50; i++ {
   156  		if err := unix.Unmount(target, unix.MNT_DETACH); err != nil {
   157  			switch err {
   158  			case unix.EBUSY:
   159  				time.Sleep(50 * time.Millisecond)
   160  				continue
   161  			case unix.EINVAL:
   162  				return
   163  			default:
   164  				continue
   165  			}
   166  		}
   167  	}
   168  	t.Fatalf("failed to unmount target %s", target)
   169  }