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  }