github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/copy_up_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 "bytes" 19 "crypto/rand" 20 "fmt" 21 "io" 22 "testing" 23 24 "github.com/SagerNet/gvisor/pkg/sentry/fs" 25 _ "github.com/SagerNet/gvisor/pkg/sentry/fs/tmpfs" 26 "github.com/SagerNet/gvisor/pkg/sentry/kernel/contexttest" 27 "github.com/SagerNet/gvisor/pkg/sync" 28 "github.com/SagerNet/gvisor/pkg/usermem" 29 ) 30 31 const ( 32 // origFileSize is the original file size. This many bytes should be 33 // copied up before the test file is modified. 34 origFileSize = 4096 35 36 // truncatedFileSize is the size to truncate all test files. 37 truncateFileSize = 10 38 ) 39 40 // TestConcurrentCopyUp is a copy up stress test for an overlay. 41 // 42 // It creates a 64-level deep directory tree in the lower filesystem and 43 // populates the last subdirectory with 64 files containing random content: 44 // 45 // /lower 46 // /sudir0/.../subdir63/ 47 // /file0 48 // ... 49 // /file63 50 // 51 // The files are truncated concurrently by 4 goroutines per file. 52 // These goroutines contend with copying up all parent 64 subdirectories 53 // as well as the final file content. 54 // 55 // At the end of the test, we assert that the files respect the new truncated 56 // size and contain the content we expect. 57 func TestConcurrentCopyUp(t *testing.T) { 58 ctx := contexttest.Context(t) 59 files := makeOverlayTestFiles(t) 60 61 var wg sync.WaitGroup 62 for _, file := range files { 63 for i := 0; i < 4; i++ { 64 wg.Add(1) 65 go func(o *overlayTestFile) { 66 if err := o.File.Dirent.Inode.Truncate(ctx, o.File.Dirent, truncateFileSize); err != nil { 67 t.Errorf("failed to copy up: %v", err) 68 } 69 wg.Done() 70 }(file) 71 } 72 } 73 wg.Wait() 74 75 for _, file := range files { 76 got := make([]byte, origFileSize) 77 n, err := file.File.Readv(ctx, usermem.BytesIOSequence(got)) 78 if int(n) != truncateFileSize { 79 t.Fatalf("read %d bytes from file, want %d", n, truncateFileSize) 80 } 81 if err != nil && err != io.EOF { 82 t.Fatalf("read got error %v, want nil", err) 83 } 84 if !bytes.Equal(got[:n], file.content[:truncateFileSize]) { 85 t.Fatalf("file content is %v, want %v", got[:n], file.content[:truncateFileSize]) 86 } 87 } 88 } 89 90 type overlayTestFile struct { 91 File *fs.File 92 name string 93 content []byte 94 } 95 96 func makeOverlayTestFiles(t *testing.T) []*overlayTestFile { 97 ctx := contexttest.Context(t) 98 99 // Create a lower tmpfs mount. 100 fsys, _ := fs.FindFilesystem("tmpfs") 101 lower, err := fsys.Mount(contexttest.Context(t), "", fs.MountSourceFlags{}, "", nil) 102 if err != nil { 103 t.Fatalf("failed to mount tmpfs: %v", err) 104 } 105 lowerRoot := fs.NewDirent(ctx, lower, "") 106 107 // Make a deep set of subdirectories that everyone shares. 108 next := lowerRoot 109 for i := 0; i < 64; i++ { 110 name := fmt.Sprintf("subdir%d", i) 111 err := next.CreateDirectory(ctx, lowerRoot, name, fs.FilePermsFromMode(0777)) 112 if err != nil { 113 t.Fatalf("failed to create dir %q: %v", name, err) 114 } 115 next, err = next.Walk(ctx, lowerRoot, name) 116 if err != nil { 117 t.Fatalf("failed to walk to %q: %v", name, err) 118 } 119 } 120 121 // Make a bunch of files in the last directory. 122 var files []*overlayTestFile 123 for i := 0; i < 64; i++ { 124 name := fmt.Sprintf("file%d", i) 125 f, err := next.Create(ctx, next, name, fs.FileFlags{Read: true, Write: true}, fs.FilePermsFromMode(0666)) 126 if err != nil { 127 t.Fatalf("failed to create file %q: %v", name, err) 128 } 129 defer f.DecRef(ctx) 130 131 relname, _ := f.Dirent.FullName(lowerRoot) 132 133 o := &overlayTestFile{ 134 name: relname, 135 content: make([]byte, origFileSize), 136 } 137 138 if _, err := rand.Read(o.content); err != nil { 139 t.Fatalf("failed to read from /dev/urandom: %v", err) 140 } 141 142 if _, err := f.Writev(ctx, usermem.BytesIOSequence(o.content)); err != nil { 143 t.Fatalf("failed to write content to file %q: %v", name, err) 144 } 145 146 files = append(files, o) 147 } 148 149 // Create an empty upper tmpfs mount which we will copy up into. 150 upper, err := fsys.Mount(ctx, "", fs.MountSourceFlags{}, "", nil) 151 if err != nil { 152 t.Fatalf("failed to mount tmpfs: %v", err) 153 } 154 155 // Construct an overlay root. 156 overlay, err := fs.NewOverlayRoot(ctx, upper, lower, fs.MountSourceFlags{}) 157 if err != nil { 158 t.Fatalf("failed to construct overlay root: %v", err) 159 } 160 161 // Create a MountNamespace to traverse the file system. 162 mns, err := fs.NewMountNamespace(ctx, overlay) 163 if err != nil { 164 t.Fatalf("failed to construct mount manager: %v", err) 165 } 166 167 // Walk to all of the files in the overlay, open them readable. 168 for _, f := range files { 169 maxTraversals := uint(0) 170 d, err := mns.FindInode(ctx, mns.Root(), mns.Root(), f.name, &maxTraversals) 171 if err != nil { 172 t.Fatalf("failed to find %q: %v", f.name, err) 173 } 174 defer d.DecRef(ctx) 175 176 f.File, err = d.Inode.GetFile(ctx, d, fs.FileFlags{Read: true}) 177 if err != nil { 178 t.Fatalf("failed to open file %q readable: %v", f.name, err) 179 } 180 } 181 182 return files 183 }