mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Misc fixes to make stat_test pass (almost)
The only test failing now requires socket which is not available in VFS2 yet. Updates #1198 PiperOrigin-RevId: 302976572
This commit is contained in:
committed by
gVisor bot
parent
c7f5673529
commit
e541ebec2f
@@ -42,3 +42,11 @@ func Mask(is ...int) T {
|
||||
func MaskOf(i int) T {
|
||||
return T(1) << T(i)
|
||||
}
|
||||
|
||||
// IsPowerOfTwo returns true if v is power of 2.
|
||||
func IsPowerOfTwo(v T) bool {
|
||||
if v == 0 {
|
||||
return false
|
||||
}
|
||||
return v&(v-1) == 0
|
||||
}
|
||||
|
||||
@@ -114,3 +114,21 @@ func TestIsOn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPowerOfTwo(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
v uint64
|
||||
want bool
|
||||
}{
|
||||
{v: 0, want: false},
|
||||
{v: 1, want: true},
|
||||
{v: 2, want: true},
|
||||
{v: 3, want: false},
|
||||
{v: 4, want: true},
|
||||
{v: 5, want: false},
|
||||
} {
|
||||
if got := IsPowerOfTwo64(tc.v); got != tc.want {
|
||||
t.Errorf("IsPowerOfTwo(%d) = %t, want: %t", tc.v, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,6 +454,9 @@ func (fs *filesystem) unlinkAt(ctx context.Context, rp *vfs.ResolvingPath, dir b
|
||||
}
|
||||
if fs.opts.interop != InteropModeShared {
|
||||
parent.touchCMtime(ctx)
|
||||
if dir {
|
||||
parent.decLinks()
|
||||
}
|
||||
parent.cacheNegativeChildLocked(name)
|
||||
parent.dirents = nil
|
||||
}
|
||||
@@ -569,8 +572,13 @@ func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
|
||||
func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error {
|
||||
return fs.doCreateAt(ctx, rp, true /* dir */, func(parent *dentry, name string) error {
|
||||
creds := rp.Credentials()
|
||||
_, err := parent.file.mkdir(ctx, name, (p9.FileMode)(opts.Mode), (p9.UID)(creds.EffectiveKUID), (p9.GID)(creds.EffectiveKGID))
|
||||
return err
|
||||
if _, err := parent.file.mkdir(ctx, name, (p9.FileMode)(opts.Mode), (p9.UID)(creds.EffectiveKUID), (p9.GID)(creds.EffectiveKGID)); err != nil {
|
||||
return err
|
||||
}
|
||||
if fs.opts.interop != InteropModeShared {
|
||||
parent.incLinks()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -962,6 +970,10 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
|
||||
oldParent.dirents = nil
|
||||
delete(newParent.negativeChildren, newName)
|
||||
newParent.dirents = nil
|
||||
if renamed.isDir() {
|
||||
oldParent.decLinks()
|
||||
newParent.incLinks()
|
||||
}
|
||||
}
|
||||
vfsObj.CommitRenameReplaceDentry(&renamed.vfsd, &newParent.vfsd, newName, replacedVFSD)
|
||||
return nil
|
||||
|
||||
@@ -485,6 +485,11 @@ type dentry struct {
|
||||
// locked to mutate it).
|
||||
size uint64
|
||||
|
||||
// nlink counts the number of hard links to this dentry. It's updated and
|
||||
// accessed using atomic operations. It's not protected by metadataMu like the
|
||||
// other metadata fields.
|
||||
nlink uint32
|
||||
|
||||
mapsMu sync.Mutex
|
||||
|
||||
// If this dentry represents a regular file, mappings tracks mappings of
|
||||
@@ -604,6 +609,9 @@ func (fs *filesystem) newDentry(ctx context.Context, file p9file, qid p9.QID, ma
|
||||
if mask.BTime {
|
||||
d.btime = dentryTimestampFromP9(attr.BTimeSeconds, attr.BTimeNanoSeconds)
|
||||
}
|
||||
if mask.NLink {
|
||||
d.nlink = uint32(attr.NLink)
|
||||
}
|
||||
d.vfsd.Init(d)
|
||||
|
||||
fs.syncMu.Lock()
|
||||
@@ -645,6 +653,9 @@ func (d *dentry) updateFromP9Attrs(mask p9.AttrMask, attr *p9.Attr) {
|
||||
if mask.BTime {
|
||||
atomic.StoreInt64(&d.btime, dentryTimestampFromP9(attr.BTimeSeconds, attr.BTimeNanoSeconds))
|
||||
}
|
||||
if mask.NLink {
|
||||
atomic.StoreUint32(&d.nlink, uint32(attr.NLink))
|
||||
}
|
||||
if mask.Size {
|
||||
d.dataMu.Lock()
|
||||
atomic.StoreUint64(&d.size, attr.Size)
|
||||
@@ -687,10 +698,7 @@ func (d *dentry) fileType() uint32 {
|
||||
func (d *dentry) statTo(stat *linux.Statx) {
|
||||
stat.Mask = linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_NLINK | linux.STATX_UID | linux.STATX_GID | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME | linux.STATX_INO | linux.STATX_SIZE | linux.STATX_BLOCKS | linux.STATX_BTIME
|
||||
stat.Blksize = atomic.LoadUint32(&d.blockSize)
|
||||
stat.Nlink = 1
|
||||
if d.isDir() {
|
||||
stat.Nlink = 2
|
||||
}
|
||||
stat.Nlink = atomic.LoadUint32(&d.nlink)
|
||||
stat.UID = atomic.LoadUint32(&d.uid)
|
||||
stat.GID = atomic.LoadUint32(&d.gid)
|
||||
stat.Mode = uint16(atomic.LoadUint32(&d.mode))
|
||||
@@ -703,7 +711,7 @@ func (d *dentry) statTo(stat *linux.Statx) {
|
||||
stat.Btime = statxTimestampFromDentry(atomic.LoadInt64(&d.btime))
|
||||
stat.Ctime = statxTimestampFromDentry(atomic.LoadInt64(&d.ctime))
|
||||
stat.Mtime = statxTimestampFromDentry(atomic.LoadInt64(&d.mtime))
|
||||
// TODO(jamieliu): device number
|
||||
// TODO(gvisor.dev/issue/1198): device number
|
||||
}
|
||||
|
||||
func (d *dentry) setStat(ctx context.Context, creds *auth.Credentials, stat *linux.Statx, mnt *vfs.Mount) error {
|
||||
@@ -1094,6 +1102,26 @@ func (d *dentry) ensureSharedHandle(ctx context.Context, read, write, trunc bool
|
||||
return nil
|
||||
}
|
||||
|
||||
// incLinks increments link count.
|
||||
//
|
||||
// Preconditions: d.nlink != 0 && d.nlink < math.MaxUint32.
|
||||
func (d *dentry) incLinks() {
|
||||
v := atomic.AddUint32(&d.nlink, 1)
|
||||
if v < 2 {
|
||||
panic(fmt.Sprintf("dentry.nlink is invalid (was 0 or overflowed): %d", v))
|
||||
}
|
||||
}
|
||||
|
||||
// decLinks decrements link count.
|
||||
//
|
||||
// Preconditions: d.nlink > 1.
|
||||
func (d *dentry) decLinks() {
|
||||
v := atomic.AddUint32(&d.nlink, ^uint32(0))
|
||||
if v == 0 {
|
||||
panic(fmt.Sprintf("dentry.nlink must be greater than 0: %d", v))
|
||||
}
|
||||
}
|
||||
|
||||
// fileDescription is embedded by gofer implementations of
|
||||
// vfs.FileDescriptionImpl.
|
||||
type fileDescription struct {
|
||||
@@ -1112,7 +1140,8 @@ func (fd *fileDescription) dentry() *dentry {
|
||||
// Stat implements vfs.FileDescriptionImpl.Stat.
|
||||
func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
d := fd.dentry()
|
||||
if d.fs.opts.interop == InteropModeShared && opts.Mask&(linux.STATX_MODE|linux.STATX_UID|linux.STATX_GID|linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_CTIME|linux.STATX_SIZE|linux.STATX_BLOCKS|linux.STATX_BTIME) != 0 && opts.Sync != linux.AT_STATX_DONT_SYNC {
|
||||
const validMask = uint32(linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME | linux.STATX_SIZE | linux.STATX_BLOCKS | linux.STATX_BTIME)
|
||||
if d.fs.opts.interop == InteropModeShared && opts.Mask&(validMask) != 0 && opts.Sync != linux.AT_STATX_DONT_SYNC {
|
||||
// TODO(jamieliu): Use specialFileFD.handle.file for the getattr if
|
||||
// available?
|
||||
if err := d.updateFromGetattr(ctx); err != nil {
|
||||
|
||||
@@ -31,6 +31,7 @@ go_library(
|
||||
visibility = ["//:sandbox"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/bits",
|
||||
"//pkg/fspath",
|
||||
"//pkg/gohacks",
|
||||
"//pkg/sentry/arch",
|
||||
|
||||
@@ -250,7 +250,7 @@ func rmdirat(t *kernel.Task, dirfd int32, pathAddr usermem.Addr) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, followFinalSymlink)
|
||||
tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, nofollowFinalSymlink)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ func (cb *getdentsCallback) Handle(dirent vfs.Dirent) error {
|
||||
// char d_name[]; /* Filename (null-terminated) */
|
||||
// };
|
||||
size := 8 + 8 + 2 + 1 + 1 + len(dirent.Name)
|
||||
if size < cb.remaining {
|
||||
if size > cb.remaining {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
buf = cb.t.CopyScratchBuffer(size)
|
||||
@@ -125,7 +125,7 @@ func (cb *getdentsCallback) Handle(dirent vfs.Dirent) error {
|
||||
panic(fmt.Sprintf("unsupported sizeof(unsigned long): %d", cb.t.Arch().Width()))
|
||||
}
|
||||
size := 8 + 8 + 2 + 1 + 1 + 1 + len(dirent.Name)
|
||||
if size < cb.remaining {
|
||||
if size > cb.remaining {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
buf = cb.t.CopyScratchBuffer(size)
|
||||
|
||||
@@ -16,6 +16,7 @@ package vfs2
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/bits"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/gohacks"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
@@ -153,7 +154,11 @@ func Statx(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_NOFOLLOW|linux.AT_STATX_SYNC_TYPE) != 0 {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
// Make sure that only one sync type option is set.
|
||||
syncType := uint32(flags & linux.AT_STATX_SYNC_TYPE)
|
||||
if syncType != 0 && !bits.IsPowerOfTwo32(syncType) {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
if mask&linux.STATX__RESERVED != 0 {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
@@ -329,10 +329,22 @@ func (rp *ResolvingPath) ResolveComponent(d *Dentry) (*Dentry, error) {
|
||||
// component in pcs represents a symbolic link, the symbolic link should be
|
||||
// followed.
|
||||
//
|
||||
// If path is terminated with '/', the '/' is considered the last element and
|
||||
// any symlink before that is followed:
|
||||
// - For most non-creating walks, the last path component is handled by
|
||||
// fs/namei.c:lookup_last(), which sets LOOKUP_FOLLOW if the first byte
|
||||
// after the path component is non-NULL (which is only possible if it's '/')
|
||||
// and the path component is of type LAST_NORM.
|
||||
//
|
||||
// - For open/openat/openat2 without O_CREAT, the last path component is
|
||||
// handled by fs/namei.c:do_last(), which does the same, though without the
|
||||
// LAST_NORM check.
|
||||
//
|
||||
// Preconditions: !rp.Done().
|
||||
func (rp *ResolvingPath) ShouldFollowSymlink() bool {
|
||||
// Non-final symlinks are always followed.
|
||||
return rp.flags&rpflagsFollowFinalSymlink != 0 || !rp.Final()
|
||||
// Non-final symlinks are always followed. Paths terminated with '/' are also
|
||||
// always followed.
|
||||
return rp.flags&rpflagsFollowFinalSymlink != 0 || !rp.Final() || rp.MustBeDir()
|
||||
}
|
||||
|
||||
// HandleSymlink is called when the current path component is a symbolic link
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
#include "test/util/temp_path.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
#ifndef AT_STATX_FORCE_SYNC
|
||||
#define AT_STATX_FORCE_SYNC 0x2000
|
||||
#endif
|
||||
#ifndef AT_STATX_DONT_SYNC
|
||||
#define AT_STATX_DONT_SYNC 0x4000
|
||||
#endif
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
@@ -700,8 +707,10 @@ TEST_F(StatTest, StatxInvalidFlags) {
|
||||
struct kernel_statx stx;
|
||||
EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), 12345, 0, &stx),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
|
||||
// Sync flags are mutually exclusive.
|
||||
EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(),
|
||||
0x6000 /* AT_STATX_SYNC_TYPE */, 0, &stx),
|
||||
AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC, 0, &stx),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user