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

     1  // Copyright 2019 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 tmpfs
    16  
    17  import (
    18  	"bytes"
    19  	"testing"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    24  	"github.com/SagerNet/gvisor/pkg/fspath"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/contexttest"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    28  	"github.com/SagerNet/gvisor/pkg/syserror"
    29  	"github.com/SagerNet/gvisor/pkg/usermem"
    30  )
    31  
    32  const fileName = "mypipe"
    33  
    34  func TestSeparateFDs(t *testing.T) {
    35  	ctx, creds, vfsObj, root := setup(t)
    36  	defer root.DecRef(ctx)
    37  
    38  	// Open the read side. This is done in a concurrently because opening
    39  	// One end the pipe blocks until the other end is opened.
    40  	pop := vfs.PathOperation{
    41  		Root:               root,
    42  		Start:              root,
    43  		Path:               fspath.Parse(fileName),
    44  		FollowFinalSymlink: true,
    45  	}
    46  	rfdchan := make(chan *vfs.FileDescription)
    47  	go func() {
    48  		openOpts := vfs.OpenOptions{Flags: linux.O_RDONLY}
    49  		rfd, _ := vfsObj.OpenAt(ctx, creds, &pop, &openOpts)
    50  		rfdchan <- rfd
    51  	}()
    52  
    53  	// Open the write side.
    54  	openOpts := vfs.OpenOptions{Flags: linux.O_WRONLY}
    55  	wfd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts)
    56  	if err != nil {
    57  		t.Fatalf("failed to open pipe for writing %q: %v", fileName, err)
    58  	}
    59  	defer wfd.DecRef(ctx)
    60  
    61  	rfd, ok := <-rfdchan
    62  	if !ok {
    63  		t.Fatalf("failed to open pipe for reading %q", fileName)
    64  	}
    65  	defer rfd.DecRef(ctx)
    66  
    67  	const msg = "vamos azul"
    68  	checkEmpty(ctx, t, rfd)
    69  	checkWrite(ctx, t, wfd, msg)
    70  	checkRead(ctx, t, rfd, msg)
    71  }
    72  
    73  func TestNonblockingRead(t *testing.T) {
    74  	ctx, creds, vfsObj, root := setup(t)
    75  	defer root.DecRef(ctx)
    76  
    77  	// Open the read side as nonblocking.
    78  	pop := vfs.PathOperation{
    79  		Root:               root,
    80  		Start:              root,
    81  		Path:               fspath.Parse(fileName),
    82  		FollowFinalSymlink: true,
    83  	}
    84  	openOpts := vfs.OpenOptions{Flags: linux.O_RDONLY | linux.O_NONBLOCK}
    85  	rfd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts)
    86  	if err != nil {
    87  		t.Fatalf("failed to open pipe for reading %q: %v", fileName, err)
    88  	}
    89  	defer rfd.DecRef(ctx)
    90  
    91  	// Open the write side.
    92  	openOpts = vfs.OpenOptions{Flags: linux.O_WRONLY}
    93  	wfd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts)
    94  	if err != nil {
    95  		t.Fatalf("failed to open pipe for writing %q: %v", fileName, err)
    96  	}
    97  	defer wfd.DecRef(ctx)
    98  
    99  	const msg = "geh blau"
   100  	checkEmpty(ctx, t, rfd)
   101  	checkWrite(ctx, t, wfd, msg)
   102  	checkRead(ctx, t, rfd, msg)
   103  }
   104  
   105  func TestNonblockingWriteError(t *testing.T) {
   106  	ctx, creds, vfsObj, root := setup(t)
   107  	defer root.DecRef(ctx)
   108  
   109  	// Open the write side as nonblocking, which should return ENXIO.
   110  	pop := vfs.PathOperation{
   111  		Root:               root,
   112  		Start:              root,
   113  		Path:               fspath.Parse(fileName),
   114  		FollowFinalSymlink: true,
   115  	}
   116  	openOpts := vfs.OpenOptions{Flags: linux.O_WRONLY | linux.O_NONBLOCK}
   117  	_, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts)
   118  	if !linuxerr.Equals(linuxerr.ENXIO, err) {
   119  		t.Fatalf("expected ENXIO, but got error: %v", err)
   120  	}
   121  }
   122  
   123  func TestSingleFD(t *testing.T) {
   124  	ctx, creds, vfsObj, root := setup(t)
   125  	defer root.DecRef(ctx)
   126  
   127  	// Open the pipe as readable and writable.
   128  	pop := vfs.PathOperation{
   129  		Root:               root,
   130  		Start:              root,
   131  		Path:               fspath.Parse(fileName),
   132  		FollowFinalSymlink: true,
   133  	}
   134  	openOpts := vfs.OpenOptions{Flags: linux.O_RDWR}
   135  	fd, err := vfsObj.OpenAt(ctx, creds, &pop, &openOpts)
   136  	if err != nil {
   137  		t.Fatalf("failed to open pipe for writing %q: %v", fileName, err)
   138  	}
   139  	defer fd.DecRef(ctx)
   140  
   141  	const msg = "forza blu"
   142  	checkEmpty(ctx, t, fd)
   143  	checkWrite(ctx, t, fd, msg)
   144  	checkRead(ctx, t, fd, msg)
   145  }
   146  
   147  // setup creates a VFS with a pipe in the root directory at path fileName. The
   148  // returned VirtualDentry must be DecRef()'d be the caller. It calls t.Fatal
   149  // upon failure.
   150  func setup(t *testing.T) (context.Context, *auth.Credentials, *vfs.VirtualFilesystem, vfs.VirtualDentry) {
   151  	ctx := contexttest.Context(t)
   152  	creds := auth.CredentialsFromContext(ctx)
   153  
   154  	// Create VFS.
   155  	vfsObj := &vfs.VirtualFilesystem{}
   156  	if err := vfsObj.Init(ctx); err != nil {
   157  		t.Fatalf("VFS init: %v", err)
   158  	}
   159  	vfsObj.MustRegisterFilesystemType("tmpfs", FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
   160  		AllowUserMount: true,
   161  	})
   162  	mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{})
   163  	if err != nil {
   164  		t.Fatalf("failed to create tmpfs root mount: %v", err)
   165  	}
   166  
   167  	// Create the pipe.
   168  	root := mntns.Root()
   169  	root.IncRef()
   170  	pop := vfs.PathOperation{
   171  		Root:  root,
   172  		Start: root,
   173  		Path:  fspath.Parse(fileName),
   174  	}
   175  	mknodOpts := vfs.MknodOptions{Mode: linux.ModeNamedPipe | 0644}
   176  	if err := vfsObj.MknodAt(ctx, creds, &pop, &mknodOpts); err != nil {
   177  		t.Fatalf("failed to create file %q: %v", fileName, err)
   178  	}
   179  
   180  	// Sanity check: the file pipe exists and has the correct mode.
   181  	stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{
   182  		Root:               root,
   183  		Start:              root,
   184  		Path:               fspath.Parse(fileName),
   185  		FollowFinalSymlink: true,
   186  	}, &vfs.StatOptions{})
   187  	if err != nil {
   188  		t.Fatalf("stat(%q) failed: %v", fileName, err)
   189  	}
   190  	if stat.Mode&^linux.S_IFMT != 0644 {
   191  		t.Errorf("got wrong permissions (%0o)", stat.Mode)
   192  	}
   193  	if stat.Mode&linux.S_IFMT != linux.ModeNamedPipe {
   194  		t.Errorf("got wrong file type (%0o)", stat.Mode)
   195  	}
   196  
   197  	return ctx, creds, vfsObj, root
   198  }
   199  
   200  // checkEmpty calls t.Fatal if the pipe in fd is not empty.
   201  func checkEmpty(ctx context.Context, t *testing.T, fd *vfs.FileDescription) {
   202  	readData := make([]byte, 1)
   203  	dst := usermem.BytesIOSequence(readData)
   204  	bytesRead, err := fd.Read(ctx, dst, vfs.ReadOptions{})
   205  	if err != syserror.ErrWouldBlock {
   206  		t.Fatalf("expected ErrWouldBlock reading from empty pipe %q, but got: %v", fileName, err)
   207  	}
   208  	if bytesRead != 0 {
   209  		t.Fatalf("expected to read 0 bytes, but got %d", bytesRead)
   210  	}
   211  }
   212  
   213  // checkWrite calls t.Fatal if it fails to write all of msg to fd.
   214  func checkWrite(ctx context.Context, t *testing.T, fd *vfs.FileDescription, msg string) {
   215  	writeData := []byte(msg)
   216  	src := usermem.BytesIOSequence(writeData)
   217  	bytesWritten, err := fd.Write(ctx, src, vfs.WriteOptions{})
   218  	if err != nil {
   219  		t.Fatalf("error writing to pipe %q: %v", fileName, err)
   220  	}
   221  	if bytesWritten != int64(len(writeData)) {
   222  		t.Fatalf("expected to write %d bytes, but wrote %d", len(writeData), bytesWritten)
   223  	}
   224  }
   225  
   226  // checkRead calls t.Fatal if it fails to read msg from fd.
   227  func checkRead(ctx context.Context, t *testing.T, fd *vfs.FileDescription, msg string) {
   228  	readData := make([]byte, len(msg))
   229  	dst := usermem.BytesIOSequence(readData)
   230  	bytesRead, err := fd.Read(ctx, dst, vfs.ReadOptions{})
   231  	if err != nil {
   232  		t.Fatalf("error reading from pipe %q: %v", fileName, err)
   233  	}
   234  	if bytesRead != int64(len(msg)) {
   235  		t.Fatalf("expected to read %d bytes, but got %d", len(msg), bytesRead)
   236  	}
   237  	if !bytes.Equal(readData, []byte(msg)) {
   238  		t.Fatalf("expected to read %q from pipe, but got %q", msg, string(readData))
   239  	}
   240  }