github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/devtmpfs/devtmpfs_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package devtmpfs
    16  
    17  import (
    18  	"path"
    19  	"testing"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/fspath"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/contexttest"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/fsimpl/tmpfs"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    28  )
    29  
    30  const devPath = "/dev"
    31  
    32  func setupDevtmpfs(t *testing.T) (context.Context, *auth.Credentials, *vfs.VirtualFilesystem, vfs.VirtualDentry, func()) {
    33  	t.Helper()
    34  
    35  	ctx := contexttest.Context(t)
    36  	creds := auth.CredentialsFromContext(ctx)
    37  	vfsObj := &vfs.VirtualFilesystem{}
    38  	if err := vfsObj.Init(ctx); err != nil {
    39  		t.Fatalf("VFS init: %v", err)
    40  	}
    41  	// Register tmpfs just so that we can have a root filesystem that isn't
    42  	// devtmpfs.
    43  	vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
    44  		AllowUserMount: true,
    45  	})
    46  	vfsObj.MustRegisterFilesystemType("devtmpfs", &FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
    47  		AllowUserMount: true,
    48  	})
    49  
    50  	// Create a test mount namespace with devtmpfs mounted at "/dev".
    51  	mntns, err := vfsObj.NewMountNamespace(ctx, creds, "tmpfs" /* source */, "tmpfs" /* fsTypeName */, &vfs.MountOptions{})
    52  	if err != nil {
    53  		t.Fatalf("failed to create tmpfs root mount: %v", err)
    54  	}
    55  	root := mntns.Root()
    56  	root.IncRef()
    57  	devpop := vfs.PathOperation{
    58  		Root:  root,
    59  		Start: root,
    60  		Path:  fspath.Parse(devPath),
    61  	}
    62  	if err := vfsObj.MkdirAt(ctx, creds, &devpop, &vfs.MkdirOptions{
    63  		Mode: 0755,
    64  	}); err != nil {
    65  		t.Fatalf("failed to create mount point: %v", err)
    66  	}
    67  	if _, err := vfsObj.MountAt(ctx, creds, "devtmpfs" /* source */, &devpop, "devtmpfs" /* fsTypeName */, &vfs.MountOptions{}); err != nil {
    68  		t.Fatalf("failed to mount devtmpfs: %v", err)
    69  	}
    70  
    71  	return ctx, creds, vfsObj, root, func() {
    72  		root.DecRef(ctx)
    73  		mntns.DecRef(ctx)
    74  	}
    75  }
    76  
    77  func TestUserspaceInit(t *testing.T) {
    78  	ctx, creds, vfsObj, root, cleanup := setupDevtmpfs(t)
    79  	defer cleanup()
    80  
    81  	a, err := NewAccessor(ctx, vfsObj, creds, "devtmpfs")
    82  	if err != nil {
    83  		t.Fatalf("failed to create devtmpfs.Accessor: %v", err)
    84  	}
    85  	defer a.Release(ctx)
    86  
    87  	// Create "userspace-initialized" files using a devtmpfs.Accessor.
    88  	if err := a.UserspaceInit(ctx); err != nil {
    89  		t.Fatalf("failed to userspace-initialize devtmpfs: %v", err)
    90  	}
    91  
    92  	// Created files should be visible in the test mount namespace.
    93  	links := []struct {
    94  		source string
    95  		target string
    96  	}{
    97  		{
    98  			source: "fd",
    99  			target: "/proc/self/fd",
   100  		},
   101  		{
   102  			source: "stdin",
   103  			target: "/proc/self/fd/0",
   104  		},
   105  		{
   106  			source: "stdout",
   107  			target: "/proc/self/fd/1",
   108  		},
   109  		{
   110  			source: "stderr",
   111  			target: "/proc/self/fd/2",
   112  		},
   113  		{
   114  			source: "ptmx",
   115  			target: "pts/ptmx",
   116  		},
   117  	}
   118  
   119  	for _, link := range links {
   120  		abspath := path.Join(devPath, link.source)
   121  		if gotTarget, err := vfsObj.ReadlinkAt(ctx, creds, &vfs.PathOperation{
   122  			Root:  root,
   123  			Start: root,
   124  			Path:  fspath.Parse(abspath),
   125  		}); err != nil || gotTarget != link.target {
   126  			t.Errorf("readlink(%q): got (%q, %v), wanted (%q, nil)", abspath, gotTarget, err, link.target)
   127  		}
   128  	}
   129  
   130  	dirs := []string{"shm", "pts"}
   131  	for _, dir := range dirs {
   132  		abspath := path.Join(devPath, dir)
   133  		statx, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{
   134  			Root:  root,
   135  			Start: root,
   136  			Path:  fspath.Parse(abspath),
   137  		}, &vfs.StatOptions{
   138  			Mask: linux.STATX_MODE,
   139  		})
   140  		if err != nil {
   141  			t.Errorf("stat(%q): got error %v ", abspath, err)
   142  			continue
   143  		}
   144  		if want := uint16(0755) | linux.S_IFDIR; statx.Mode != want {
   145  			t.Errorf("stat(%q): got mode %x, want %x", abspath, statx.Mode, want)
   146  		}
   147  	}
   148  }
   149  
   150  func TestCreateDeviceFile(t *testing.T) {
   151  	ctx, creds, vfsObj, root, cleanup := setupDevtmpfs(t)
   152  	defer cleanup()
   153  
   154  	a, err := NewAccessor(ctx, vfsObj, creds, "devtmpfs")
   155  	if err != nil {
   156  		t.Fatalf("failed to create devtmpfs.Accessor: %v", err)
   157  	}
   158  	defer a.Release(ctx)
   159  
   160  	devFiles := []struct {
   161  		path  string
   162  		kind  vfs.DeviceKind
   163  		major uint32
   164  		minor uint32
   165  		perms uint16
   166  	}{
   167  		{
   168  			path:  "dummy",
   169  			kind:  vfs.CharDevice,
   170  			major: 12,
   171  			minor: 34,
   172  			perms: 0600,
   173  		},
   174  		{
   175  			path:  "foo/bar",
   176  			kind:  vfs.BlockDevice,
   177  			major: 13,
   178  			minor: 35,
   179  			perms: 0660,
   180  		},
   181  		{
   182  			path:  "foo/baz",
   183  			kind:  vfs.CharDevice,
   184  			major: 12,
   185  			minor: 40,
   186  			perms: 0666,
   187  		},
   188  		{
   189  			path:  "a/b/c/d/e",
   190  			kind:  vfs.BlockDevice,
   191  			major: 12,
   192  			minor: 34,
   193  			perms: 0600,
   194  		},
   195  	}
   196  
   197  	for _, f := range devFiles {
   198  		if err := a.CreateDeviceFile(ctx, f.path, f.kind, f.major, f.minor, f.perms); err != nil {
   199  			t.Fatalf("failed to create device file: %v", err)
   200  		}
   201  		// The device special file should be visible in the test mount namespace.
   202  		abspath := path.Join(devPath, f.path)
   203  		stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{
   204  			Root:  root,
   205  			Start: root,
   206  			Path:  fspath.Parse(abspath),
   207  		}, &vfs.StatOptions{
   208  			Mask: linux.STATX_TYPE | linux.STATX_MODE,
   209  		})
   210  		if err != nil {
   211  			t.Fatalf("failed to stat device file at %q: %v", abspath, err)
   212  		}
   213  		if stat.RdevMajor != f.major {
   214  			t.Errorf("major device number: got %v, wanted %v", stat.RdevMajor, f.major)
   215  		}
   216  		if stat.RdevMinor != f.minor {
   217  			t.Errorf("minor device number: got %v, wanted %v", stat.RdevMinor, f.minor)
   218  		}
   219  		wantMode := f.perms
   220  		switch f.kind {
   221  		case vfs.CharDevice:
   222  			wantMode |= linux.S_IFCHR
   223  		case vfs.BlockDevice:
   224  			wantMode |= linux.S_IFBLK
   225  		}
   226  		if stat.Mode != wantMode {
   227  			t.Errorf("device file mode: got %v, wanted %v", stat.Mode, wantMode)
   228  		}
   229  	}
   230  }