github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/dirent_refs_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
    16  
    17  import (
    18  	"testing"
    19  
    20  	"golang.org/x/sys/unix"
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/contexttest"
    23  )
    24  
    25  func newMockDirInode(ctx context.Context, cache *DirentCache) *Inode {
    26  	return NewMockInode(ctx, NewMockMountSource(cache), StableAttr{Type: Directory})
    27  }
    28  
    29  func TestWalkPositive(t *testing.T) {
    30  	// refs == 0 -> one reference.
    31  	// refs == -1 -> has been destroyed.
    32  
    33  	ctx := contexttest.Context(t)
    34  	root := NewDirent(ctx, newMockDirInode(ctx, nil), "root")
    35  
    36  	if got := root.ReadRefs(); got != 1 {
    37  		t.Fatalf("root has a ref count of %d, want %d", got, 1)
    38  	}
    39  
    40  	name := "d"
    41  	d, err := root.walk(ctx, root, name, false)
    42  	if err != nil {
    43  		t.Fatalf("root.walk(root, %q) got %v, want nil", name, err)
    44  	}
    45  
    46  	if got := root.ReadRefs(); got != 2 {
    47  		t.Fatalf("root has a ref count of %d, want %d", got, 2)
    48  	}
    49  
    50  	if got := d.ReadRefs(); got != 1 {
    51  		t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 1)
    52  	}
    53  
    54  	d.DecRef(ctx)
    55  
    56  	if got := root.ReadRefs(); got != 1 {
    57  		t.Fatalf("root has a ref count of %d, want %d", got, 1)
    58  	}
    59  
    60  	if got := d.ReadRefs(); got != 0 {
    61  		t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 0)
    62  	}
    63  
    64  	root.flush(ctx)
    65  
    66  	if got := len(root.children); got != 0 {
    67  		t.Fatalf("root has %d children, want %d", got, 0)
    68  	}
    69  }
    70  
    71  func TestWalkNegative(t *testing.T) {
    72  	// refs == 0 -> one reference.
    73  	// refs == -1 -> has been destroyed.
    74  
    75  	ctx := contexttest.Context(t)
    76  	root := NewDirent(ctx, NewEmptyDir(ctx, nil), "root")
    77  	mn := root.Inode.InodeOperations.(*mockInodeOperationsLookupNegative)
    78  
    79  	if got := root.ReadRefs(); got != 1 {
    80  		t.Fatalf("root has a ref count of %d, want %d", got, 1)
    81  	}
    82  
    83  	name := "d"
    84  	for i := 0; i < 100; i++ {
    85  		_, err := root.walk(ctx, root, name, false)
    86  		if err != unix.ENOENT {
    87  			t.Fatalf("root.walk(root, %q) got %v, want %v", name, err, unix.ENOENT)
    88  		}
    89  	}
    90  
    91  	if got := root.ReadRefs(); got != 1 {
    92  		t.Fatalf("root has a ref count of %d, want %d", got, 1)
    93  	}
    94  
    95  	if got := len(root.children); got != 1 {
    96  		t.Fatalf("root has %d children, want %d", got, 1)
    97  	}
    98  
    99  	w, ok := root.children[name]
   100  	if !ok {
   101  		t.Fatalf("root wants child at %q", name)
   102  	}
   103  
   104  	child := w.Get()
   105  	if child == nil {
   106  		t.Fatalf("root wants to resolve weak reference")
   107  	}
   108  
   109  	if !child.(*Dirent).IsNegative() {
   110  		t.Fatalf("root found positive child at %q, want negative", name)
   111  	}
   112  
   113  	if got := child.(*Dirent).ReadRefs(); got != 2 {
   114  		t.Fatalf("child has a ref count of %d, want %d", got, 2)
   115  	}
   116  
   117  	child.DecRef(ctx)
   118  
   119  	if got := child.(*Dirent).ReadRefs(); got != 1 {
   120  		t.Fatalf("child has a ref count of %d, want %d", got, 1)
   121  	}
   122  
   123  	if got := len(root.children); got != 1 {
   124  		t.Fatalf("root has %d children, want %d", got, 1)
   125  	}
   126  
   127  	root.DecRef(ctx)
   128  
   129  	if got := root.ReadRefs(); got != 0 {
   130  		t.Fatalf("root has a ref count of %d, want %d", got, 0)
   131  	}
   132  
   133  	AsyncBarrier()
   134  
   135  	if got := mn.releaseCalled; got != true {
   136  		t.Fatalf("root.Close was called %v, want true", got)
   137  	}
   138  }
   139  
   140  type mockInodeOperationsLookupNegative struct {
   141  	*MockInodeOperations
   142  	releaseCalled bool
   143  }
   144  
   145  func NewEmptyDir(ctx context.Context, cache *DirentCache) *Inode {
   146  	m := NewMockMountSource(cache)
   147  	return NewInode(ctx, &mockInodeOperationsLookupNegative{
   148  		MockInodeOperations: NewMockInodeOperations(ctx),
   149  	}, m, StableAttr{Type: Directory})
   150  }
   151  
   152  func (m *mockInodeOperationsLookupNegative) Lookup(ctx context.Context, dir *Inode, p string) (*Dirent, error) {
   153  	return NewNegativeDirent(p), nil
   154  }
   155  
   156  func (m *mockInodeOperationsLookupNegative) Release(context.Context) {
   157  	m.releaseCalled = true
   158  }
   159  
   160  func TestHashNegativeToPositive(t *testing.T) {
   161  	// refs == 0 -> one reference.
   162  	// refs == -1 -> has been destroyed.
   163  
   164  	ctx := contexttest.Context(t)
   165  	root := NewDirent(ctx, NewEmptyDir(ctx, nil), "root")
   166  
   167  	name := "d"
   168  	_, err := root.walk(ctx, root, name, false)
   169  	if err != unix.ENOENT {
   170  		t.Fatalf("root.walk(root, %q) got %v, want %v", name, err, unix.ENOENT)
   171  	}
   172  
   173  	if got := root.exists(ctx, root, name); got != false {
   174  		t.Fatalf("got %q exists, want does not exist", name)
   175  	}
   176  
   177  	f, err := root.Create(ctx, root, name, FileFlags{}, FilePermissions{})
   178  	if err != nil {
   179  		t.Fatalf("root.Create(%q, _), got error %v, want nil", name, err)
   180  	}
   181  	d := f.Dirent
   182  
   183  	if d.IsNegative() {
   184  		t.Fatalf("got negative Dirent, want positive")
   185  	}
   186  
   187  	if got := d.ReadRefs(); got != 1 {
   188  		t.Fatalf("child %q has a ref count of %d, want %d", name, got, 1)
   189  	}
   190  
   191  	if got := root.ReadRefs(); got != 2 {
   192  		t.Fatalf("root has a ref count of %d, want %d", got, 2)
   193  	}
   194  
   195  	if got := len(root.children); got != 1 {
   196  		t.Fatalf("got %d children, want %d", got, 1)
   197  	}
   198  
   199  	w, ok := root.children[name]
   200  	if !ok {
   201  		t.Fatalf("failed to find weak reference to %q", name)
   202  	}
   203  
   204  	child := w.Get()
   205  	if child == nil {
   206  		t.Fatalf("want to resolve weak reference")
   207  	}
   208  
   209  	if child.(*Dirent) != d {
   210  		t.Fatalf("got foreign child")
   211  	}
   212  }
   213  
   214  func TestRevalidate(t *testing.T) {
   215  	// refs == 0 -> one reference.
   216  	// refs == -1 -> has been destroyed.
   217  
   218  	for _, test := range []struct {
   219  		// desc is the test's description.
   220  		desc string
   221  
   222  		// Whether to make negative Dirents.
   223  		makeNegative bool
   224  	}{
   225  		{
   226  			desc:         "Revalidate negative Dirent",
   227  			makeNegative: true,
   228  		},
   229  		{
   230  			desc:         "Revalidate positive Dirent",
   231  			makeNegative: false,
   232  		},
   233  	} {
   234  		t.Run(test.desc, func(t *testing.T) {
   235  			ctx := contexttest.Context(t)
   236  			root := NewDirent(ctx, NewMockInodeRevalidate(ctx, test.makeNegative), "root")
   237  
   238  			name := "d"
   239  			d1, err := root.walk(ctx, root, name, false)
   240  			if !test.makeNegative && err != nil {
   241  				t.Fatalf("root.walk(root, %q) got %v, want nil", name, err)
   242  			}
   243  			d2, err := root.walk(ctx, root, name, false)
   244  			if !test.makeNegative && err != nil {
   245  				t.Fatalf("root.walk(root, %q) got %v, want nil", name, err)
   246  			}
   247  			if !test.makeNegative && d1 == d2 {
   248  				t.Fatalf("revalidating walk got same *Dirent, want different")
   249  			}
   250  			if got := len(root.children); got != 1 {
   251  				t.Errorf("revalidating walk got %d children, want %d", got, 1)
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  type MockInodeOperationsRevalidate struct {
   258  	*MockInodeOperations
   259  	makeNegative bool
   260  }
   261  
   262  func NewMockInodeRevalidate(ctx context.Context, makeNegative bool) *Inode {
   263  	mn := NewMockInodeOperations(ctx)
   264  	m := NewMockMountSource(nil)
   265  	m.MountSourceOperations.(*MockMountSourceOps).revalidate = true
   266  	return NewInode(ctx, &MockInodeOperationsRevalidate{MockInodeOperations: mn, makeNegative: makeNegative}, m, StableAttr{Type: Directory})
   267  }
   268  
   269  func (m *MockInodeOperationsRevalidate) Lookup(ctx context.Context, dir *Inode, p string) (*Dirent, error) {
   270  	if !m.makeNegative {
   271  		return m.MockInodeOperations.Lookup(ctx, dir, p)
   272  	}
   273  	return NewNegativeDirent(p), nil
   274  }
   275  
   276  func TestCreateExtraRefs(t *testing.T) {
   277  	// refs == 0 -> one reference.
   278  	// refs == -1 -> has been destroyed.
   279  
   280  	ctx := contexttest.Context(t)
   281  	for _, test := range []struct {
   282  		// desc is the test's description.
   283  		desc string
   284  
   285  		// root is the Dirent to create from.
   286  		root *Dirent
   287  
   288  		// expected references on walked Dirent.
   289  		refs int64
   290  	}{
   291  		{
   292  			desc: "Create caching",
   293  			root: NewDirent(ctx, NewEmptyDir(ctx, NewDirentCache(1)), "root"),
   294  			refs: 2,
   295  		},
   296  		{
   297  			desc: "Create not caching",
   298  			root: NewDirent(ctx, NewEmptyDir(ctx, nil), "root"),
   299  			refs: 1,
   300  		},
   301  	} {
   302  		t.Run(test.desc, func(t *testing.T) {
   303  			name := "d"
   304  			f, err := test.root.Create(ctx, test.root, name, FileFlags{}, FilePermissions{})
   305  			if err != nil {
   306  				t.Fatalf("root.Create(root, %q) failed: %v", name, err)
   307  			}
   308  			d := f.Dirent
   309  
   310  			if got := d.ReadRefs(); got != test.refs {
   311  				t.Errorf("dirent has a ref count of %d, want %d", got, test.refs)
   312  			}
   313  		})
   314  	}
   315  }
   316  
   317  func TestRemoveExtraRefs(t *testing.T) {
   318  	// refs == 0 -> one reference.
   319  	// refs == -1 -> has been destroyed.
   320  
   321  	ctx := contexttest.Context(t)
   322  	for _, test := range []struct {
   323  		// desc is the test's description.
   324  		desc string
   325  
   326  		// root is the Dirent to make and remove from.
   327  		root *Dirent
   328  	}{
   329  		{
   330  			desc: "Remove caching",
   331  			root: NewDirent(ctx, NewEmptyDir(ctx, NewDirentCache(1)), "root"),
   332  		},
   333  		{
   334  			desc: "Remove not caching",
   335  			root: NewDirent(ctx, NewEmptyDir(ctx, nil), "root"),
   336  		},
   337  	} {
   338  		t.Run(test.desc, func(t *testing.T) {
   339  			name := "d"
   340  			f, err := test.root.Create(ctx, test.root, name, FileFlags{}, FilePermissions{})
   341  			if err != nil {
   342  				t.Fatalf("root.Create(%q, _) failed: %v", name, err)
   343  			}
   344  			d := f.Dirent
   345  
   346  			if err := test.root.Remove(contexttest.Context(t), test.root, name, false /* dirPath */); err != nil {
   347  				t.Fatalf("root.Remove(root, %q) failed: %v", name, err)
   348  			}
   349  
   350  			if got := d.ReadRefs(); got != 1 {
   351  				t.Fatalf("dirent has a ref count of %d, want %d", got, 1)
   352  			}
   353  
   354  			d.DecRef(ctx)
   355  
   356  			test.root.flush(ctx)
   357  
   358  			if got := len(test.root.children); got != 0 {
   359  				t.Errorf("root has %d children, want %d", got, 0)
   360  			}
   361  		})
   362  	}
   363  }
   364  
   365  func TestRenameExtraRefs(t *testing.T) {
   366  	// refs == 0 -> one reference.
   367  	// refs == -1 -> has been destroyed.
   368  
   369  	for _, test := range []struct {
   370  		// desc is the test's description.
   371  		desc string
   372  
   373  		// cache of extra Dirent references, may be nil.
   374  		cache *DirentCache
   375  	}{
   376  		{
   377  			desc:  "Rename no caching",
   378  			cache: nil,
   379  		},
   380  		{
   381  			desc:  "Rename caching",
   382  			cache: NewDirentCache(5),
   383  		},
   384  	} {
   385  		t.Run(test.desc, func(t *testing.T) {
   386  			ctx := contexttest.Context(t)
   387  
   388  			dirAttr := StableAttr{Type: Directory}
   389  
   390  			oldParent := NewDirent(ctx, NewMockInode(ctx, NewMockMountSource(test.cache), dirAttr), "old_parent")
   391  			newParent := NewDirent(ctx, NewMockInode(ctx, NewMockMountSource(test.cache), dirAttr), "new_parent")
   392  
   393  			renamed, err := oldParent.Walk(ctx, oldParent, "old_child")
   394  			if err != nil {
   395  				t.Fatalf("Walk(oldParent, %q) got error %v, want nil", "old_child", err)
   396  			}
   397  			replaced, err := newParent.Walk(ctx, oldParent, "new_child")
   398  			if err != nil {
   399  				t.Fatalf("Walk(newParent, %q) got error %v, want nil", "new_child", err)
   400  			}
   401  
   402  			if err := Rename(contexttest.RootContext(t), oldParent /*root */, oldParent, "old_child", newParent, "new_child"); err != nil {
   403  				t.Fatalf("Rename got error %v, want nil", err)
   404  			}
   405  
   406  			oldParent.flush(ctx)
   407  			newParent.flush(ctx)
   408  
   409  			// Expect to have only active references.
   410  			if got := renamed.ReadRefs(); got != 1 {
   411  				t.Errorf("renamed has ref count %d, want only active references %d", got, 1)
   412  			}
   413  			if got := replaced.ReadRefs(); got != 1 {
   414  				t.Errorf("replaced has ref count %d, want only active references %d", got, 1)
   415  			}
   416  		})
   417  	}
   418  }