gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/tmpfs/stat_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 tmpfs 16 17 import ( 18 "fmt" 19 "testing" 20 21 "gvisor.dev/gvisor/pkg/abi/linux" 22 "gvisor.dev/gvisor/pkg/sentry/contexttest" 23 "gvisor.dev/gvisor/pkg/sentry/kernel/auth" 24 "gvisor.dev/gvisor/pkg/sentry/vfs" 25 ) 26 27 func TestStatAfterCreate(t *testing.T) { 28 ctx := contexttest.Context(t) 29 mode := linux.FileMode(0644) 30 31 // Run with different file types. 32 for _, typ := range []string{"file", "dir", "pipe"} { 33 t.Run(fmt.Sprintf("type=%q", typ), func(t *testing.T) { 34 var ( 35 fd *vfs.FileDescription 36 cleanup func() 37 err error 38 ) 39 switch typ { 40 case "file": 41 fd, cleanup, err = newFileFD(ctx, mode) 42 case "dir": 43 fd, cleanup, err = newDirFD(ctx, mode) 44 case "pipe": 45 fd, cleanup, err = newPipeFD(ctx, mode) 46 default: 47 panic(fmt.Sprintf("unknown typ %q", typ)) 48 } 49 if err != nil { 50 t.Fatal(err) 51 } 52 defer cleanup() 53 54 got, err := fd.Stat(ctx, vfs.StatOptions{}) 55 if err != nil { 56 t.Fatalf("Stat failed: %v", err) 57 } 58 59 // Atime, Ctime, Mtime should all be current time (non-zero). 60 atime, ctime, mtime := got.Atime.ToNsec(), got.Ctime.ToNsec(), got.Mtime.ToNsec() 61 if atime != ctime || ctime != mtime { 62 t.Errorf("got atime=%d ctime=%d mtime=%d, wanted equal values", atime, ctime, mtime) 63 } 64 if atime == 0 { 65 t.Errorf("got atime=%d, want non-zero", atime) 66 } 67 68 // Btime should be 0, as it is not set by tmpfs. 69 if btime := got.Btime.ToNsec(); btime != 0 { 70 t.Errorf("got btime %d, want 0", got.Btime.ToNsec()) 71 } 72 73 // Size should be 0 (except for directories, which make up a size 74 // of 20 per entry, including the "." and ".." entries present in 75 // otherwise-empty directories). 76 wantSize := uint64(0) 77 if typ == "dir" { 78 wantSize = 40 79 } 80 if got.Size != wantSize { 81 t.Errorf("got size %d, want %d", got.Size, wantSize) 82 } 83 84 // Nlink should be 1 for files, 2 for dirs. 85 wantNlink := uint32(1) 86 if typ == "dir" { 87 wantNlink = 2 88 } 89 if got.Nlink != wantNlink { 90 t.Errorf("got nlink %d, want %d", got.Nlink, wantNlink) 91 } 92 93 // UID and GID are set from context creds. 94 creds := auth.CredentialsFromContext(ctx) 95 if got.UID != uint32(creds.EffectiveKUID) { 96 t.Errorf("got uid %d, want %d", got.UID, uint32(creds.EffectiveKUID)) 97 } 98 if got.GID != uint32(creds.EffectiveKGID) { 99 t.Errorf("got gid %d, want %d", got.GID, uint32(creds.EffectiveKGID)) 100 } 101 102 // Mode. 103 wantMode := uint16(mode) 104 switch typ { 105 case "file": 106 wantMode |= linux.S_IFREG 107 case "dir": 108 wantMode |= linux.S_IFDIR 109 case "pipe": 110 wantMode |= linux.S_IFIFO 111 default: 112 panic(fmt.Sprintf("unknown typ %q", typ)) 113 } 114 115 if got.Mode != wantMode { 116 t.Errorf("got mode %x, want %x", got.Mode, wantMode) 117 } 118 119 // Ino. 120 if got.Ino == 0 { 121 t.Errorf("got ino %d, want not 0", got.Ino) 122 } 123 }) 124 } 125 } 126 127 func TestSetStatAtime(t *testing.T) { 128 ctx := contexttest.Context(t) 129 fd, cleanup, err := newFileFD(ctx, 0644) 130 if err != nil { 131 t.Fatal(err) 132 } 133 defer cleanup() 134 135 allStatOptions := vfs.StatOptions{Mask: linux.STATX_ALL} 136 137 // Get initial stat. 138 initialStat, err := fd.Stat(ctx, allStatOptions) 139 if err != nil { 140 t.Fatalf("Stat failed: %v", err) 141 } 142 143 // Set atime, but without the mask. 144 if err := fd.SetStat(ctx, vfs.SetStatOptions{Stat: linux.Statx{ 145 Mask: 0, 146 Atime: linux.NsecToStatxTimestamp(100), 147 }}); err != nil { 148 t.Errorf("SetStat atime without mask failed: %v", err) 149 } 150 // Atime should be unchanged. 151 if gotStat, err := fd.Stat(ctx, allStatOptions); err != nil { 152 t.Errorf("Stat got error: %v", err) 153 } else if gotStat.Atime != initialStat.Atime { 154 t.Errorf("Stat got atime %d, want %d", gotStat.Atime, initialStat.Atime) 155 } 156 157 // Set atime, this time included in the mask. 158 setStat := linux.Statx{ 159 Mask: linux.STATX_ATIME, 160 Atime: linux.NsecToStatxTimestamp(100), 161 } 162 if err := fd.SetStat(ctx, vfs.SetStatOptions{Stat: setStat}); err != nil { 163 t.Errorf("SetStat atime with mask failed: %v", err) 164 } 165 if gotStat, err := fd.Stat(ctx, allStatOptions); err != nil { 166 t.Errorf("Stat got error: %v", err) 167 } else if gotStat.Atime != setStat.Atime { 168 t.Errorf("Stat got atime %d, want %d", gotStat.Atime, setStat.Atime) 169 } 170 } 171 172 func TestSetStat(t *testing.T) { 173 ctx := contexttest.Context(t) 174 mode := linux.FileMode(0644) 175 176 // Run with different file types. 177 for _, typ := range []string{"file", "dir", "pipe"} { 178 t.Run(fmt.Sprintf("type=%q", typ), func(t *testing.T) { 179 var ( 180 fd *vfs.FileDescription 181 cleanup func() 182 err error 183 ) 184 switch typ { 185 case "file": 186 fd, cleanup, err = newFileFD(ctx, mode) 187 case "dir": 188 fd, cleanup, err = newDirFD(ctx, mode) 189 case "pipe": 190 fd, cleanup, err = newPipeFD(ctx, mode) 191 default: 192 panic(fmt.Sprintf("unknown typ %q", typ)) 193 } 194 if err != nil { 195 t.Fatal(err) 196 } 197 defer cleanup() 198 199 allStatOptions := vfs.StatOptions{Mask: linux.STATX_ALL} 200 201 // Get initial stat. 202 initialStat, err := fd.Stat(ctx, allStatOptions) 203 if err != nil { 204 t.Fatalf("Stat failed: %v", err) 205 } 206 207 // Set atime, but without the mask. 208 if err := fd.SetStat(ctx, vfs.SetStatOptions{Stat: linux.Statx{ 209 Mask: 0, 210 Atime: linux.NsecToStatxTimestamp(100), 211 }}); err != nil { 212 t.Errorf("SetStat atime without mask failed: %v", err) 213 } 214 // Atime should be unchanged. 215 if gotStat, err := fd.Stat(ctx, allStatOptions); err != nil { 216 t.Errorf("Stat got error: %v", err) 217 } else if gotStat.Atime != initialStat.Atime { 218 t.Errorf("Stat got atime %d, want %d", gotStat.Atime, initialStat.Atime) 219 } 220 221 // Set atime, this time included in the mask. 222 setStat := linux.Statx{ 223 Mask: linux.STATX_ATIME, 224 Atime: linux.NsecToStatxTimestamp(100), 225 } 226 if err := fd.SetStat(ctx, vfs.SetStatOptions{Stat: setStat}); err != nil { 227 t.Errorf("SetStat atime with mask failed: %v", err) 228 } 229 if gotStat, err := fd.Stat(ctx, allStatOptions); err != nil { 230 t.Errorf("Stat got error: %v", err) 231 } else if gotStat.Atime != setStat.Atime { 232 t.Errorf("Stat got atime %d, want %d", gotStat.Atime, setStat.Atime) 233 } 234 }) 235 } 236 }