github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/file_overlay_test.go (about)

     1  // Copyright 2018 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 fs_test
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/fs/ramfs"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/contexttest"
    26  )
    27  
    28  func TestReaddir(t *testing.T) {
    29  	ctx := contexttest.Context(t)
    30  	ctx = &rootContext{
    31  		Context: ctx,
    32  		root:    fs.NewDirent(ctx, newTestRamfsDir(ctx, nil, nil), "root"),
    33  	}
    34  	for _, test := range []struct {
    35  		// Test description.
    36  		desc string
    37  
    38  		// Lookup parameters.
    39  		dir *fs.Inode
    40  
    41  		// Want from lookup.
    42  		err   error
    43  		names []string
    44  	}{
    45  		{
    46  			desc: "no upper, lower has entries",
    47  			dir: fs.NewTestOverlayDir(ctx,
    48  				nil, /* upper */
    49  				newTestRamfsDir(ctx, []dirContent{
    50  					{name: "a"},
    51  					{name: "b"},
    52  				}, nil), /* lower */
    53  				false /* revalidate */),
    54  			names: []string{".", "..", "a", "b"},
    55  		},
    56  		{
    57  			desc: "upper has entries, no lower",
    58  			dir: fs.NewTestOverlayDir(ctx,
    59  				newTestRamfsDir(ctx, []dirContent{
    60  					{name: "a"},
    61  					{name: "b"},
    62  				}, nil), /* upper */
    63  				nil, /* lower */
    64  				false /* revalidate */),
    65  			names: []string{".", "..", "a", "b"},
    66  		},
    67  		{
    68  			desc: "upper and lower, entries combine",
    69  			dir: fs.NewTestOverlayDir(ctx,
    70  				newTestRamfsDir(ctx, []dirContent{
    71  					{name: "a"},
    72  				}, nil), /* upper */
    73  				newTestRamfsDir(ctx, []dirContent{
    74  					{name: "b"},
    75  				}, nil), /* lower */
    76  				false /* revalidate */),
    77  			names: []string{".", "..", "a", "b"},
    78  		},
    79  		{
    80  			desc: "upper and lower, entries combine, none are masked",
    81  			dir: fs.NewTestOverlayDir(ctx,
    82  				newTestRamfsDir(ctx, []dirContent{
    83  					{name: "a"},
    84  				}, []string{"b"}), /* upper */
    85  				newTestRamfsDir(ctx, []dirContent{
    86  					{name: "c"},
    87  				}, nil), /* lower */
    88  				false /* revalidate */),
    89  			names: []string{".", "..", "a", "c"},
    90  		},
    91  		{
    92  			desc: "upper and lower, entries combine, upper masks some of lower",
    93  			dir: fs.NewTestOverlayDir(ctx,
    94  				newTestRamfsDir(ctx, []dirContent{
    95  					{name: "a"},
    96  				}, []string{"b"}), /* upper */
    97  				newTestRamfsDir(ctx, []dirContent{
    98  					{name: "b"}, /* will be masked */
    99  					{name: "c"},
   100  				}, nil), /* lower */
   101  				false /* revalidate */),
   102  			names: []string{".", "..", "a", "c"},
   103  		},
   104  	} {
   105  		t.Run(test.desc, func(t *testing.T) {
   106  			openDir, err := test.dir.GetFile(ctx, fs.NewDirent(ctx, test.dir, "stub"), fs.FileFlags{Read: true})
   107  			if err != nil {
   108  				t.Fatalf("GetFile got error %v, want nil", err)
   109  			}
   110  			stubSerializer := &fs.CollectEntriesSerializer{}
   111  			err = openDir.Readdir(ctx, stubSerializer)
   112  			if err != test.err {
   113  				t.Fatalf("Readdir got error %v, want nil", err)
   114  			}
   115  			if err != nil {
   116  				return
   117  			}
   118  			if !reflect.DeepEqual(stubSerializer.Order, test.names) {
   119  				t.Errorf("Readdir got names %v, want %v", stubSerializer.Order, test.names)
   120  			}
   121  		})
   122  	}
   123  }
   124  
   125  func TestReaddirRevalidation(t *testing.T) {
   126  	ctx := contexttest.Context(t)
   127  	ctx = &rootContext{
   128  		Context: ctx,
   129  		root:    fs.NewDirent(ctx, newTestRamfsDir(ctx, nil, nil), "root"),
   130  	}
   131  
   132  	// Create an overlay with two directories, each with one file.
   133  	upper := newTestRamfsDir(ctx, []dirContent{{name: "a"}}, nil)
   134  	lower := newTestRamfsDir(ctx, []dirContent{{name: "b"}}, nil)
   135  	overlay := fs.NewTestOverlayDir(ctx, upper, lower, true /* revalidate */)
   136  
   137  	// Get a handle to the dirent in the upper filesystem so that we can
   138  	// modify it without going through the dirent.
   139  	upperDir := upper.InodeOperations.(*dir).InodeOperations.(*ramfs.Dir)
   140  
   141  	// Check that overlay returns the files from both upper and lower.
   142  	openDir, err := overlay.GetFile(ctx, fs.NewDirent(ctx, overlay, "stub"), fs.FileFlags{Read: true})
   143  	if err != nil {
   144  		t.Fatalf("GetFile got error %v, want nil", err)
   145  	}
   146  	ser := &fs.CollectEntriesSerializer{}
   147  	if err := openDir.Readdir(ctx, ser); err != nil {
   148  		t.Fatalf("Readdir got error %v, want nil", err)
   149  	}
   150  	got, want := ser.Order, []string{".", "..", "a", "b"}
   151  	if !reflect.DeepEqual(got, want) {
   152  		t.Errorf("Readdir got names %v, want %v", got, want)
   153  	}
   154  
   155  	// Remove "a" from the upper and add "c".
   156  	if err := upperDir.Remove(ctx, upper, "a"); err != nil {
   157  		t.Fatalf("error removing child: %v", err)
   158  	}
   159  	upperDir.AddChild(ctx, "c", fs.NewInode(ctx, fsutil.NewSimpleFileInode(ctx, fs.RootOwner, fs.FilePermissions{}, 0),
   160  		upper.MountSource, fs.StableAttr{Type: fs.RegularFile}))
   161  
   162  	// Seek to beginning of the directory and do the readdir again.
   163  	if _, err := openDir.Seek(ctx, fs.SeekSet, 0); err != nil {
   164  		t.Fatalf("error seeking to beginning of dir: %v", err)
   165  	}
   166  	ser = &fs.CollectEntriesSerializer{}
   167  	if err := openDir.Readdir(ctx, ser); err != nil {
   168  		t.Fatalf("Readdir got error %v, want nil", err)
   169  	}
   170  
   171  	// Readdir should return the updated children.
   172  	got, want = ser.Order, []string{".", "..", "b", "c"}
   173  	if !reflect.DeepEqual(got, want) {
   174  		t.Errorf("Readdir got names %v, want %v", got, want)
   175  	}
   176  }
   177  
   178  type rootContext struct {
   179  	context.Context
   180  	root *fs.Dirent
   181  }
   182  
   183  // Value implements context.Context.
   184  func (r *rootContext) Value(key interface{}) interface{} {
   185  	switch key {
   186  	case fs.CtxRoot:
   187  		r.root.IncRef()
   188  		return r.root
   189  	default:
   190  		return r.Context.Value(key)
   191  	}
   192  }