Implement access/faccessat for VFS2.
Note that the raw faccessat system call does not actually take a flags argument; according to faccessat(2), the glibc wrapper implements the flags by using fstatat(2). Remove the flag argument that we try to extract from vfs1, which would just be a garbage value. Updates #1965 Fixes #2101 PiperOrigin-RevId: 300796067
This commit is contained in:
parent
f458a325e9
commit
2e38408f20
|
@ -22,6 +22,7 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/ext/disklayout"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
|
@ -255,6 +256,15 @@ func (fs *filesystem) statTo(stat *linux.Statfs) {
|
|||
// TODO(b/134676337): Set Statfs.Flags and Statfs.FSID.
|
||||
}
|
||||
|
||||
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
||||
func (fs *filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
_, inode, err := fs.walk(rp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return inode.checkPermissions(rp.Credentials(), ats)
|
||||
}
|
||||
|
||||
// GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
|
||||
func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
|
||||
vfsd, inode, err := fs.walk(rp, false)
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
@ -499,6 +500,18 @@ func (fs *filesystem) renameMuUnlockAndCheckCaching(ds **[]*dentry) {
|
|||
putDentrySlice(*ds)
|
||||
}
|
||||
|
||||
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
||||
func (fs *filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckCaching(&ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.checkPermissions(creds, ats, d.isDir())
|
||||
}
|
||||
|
||||
// GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
|
||||
func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
|
||||
var ds *[]*dentry
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
@ -229,6 +230,19 @@ func (fs *Filesystem) Sync(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
||||
func (fs *Filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
defer fs.processDeferredDecRefs()
|
||||
|
||||
_, inode, err := fs.walkExistingLocked(ctx, rp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return inode.CheckPermissions(ctx, creds, ats)
|
||||
}
|
||||
|
||||
// GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
|
||||
func (fs *Filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
|
||||
fs.mu.RLock()
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
@ -154,6 +155,17 @@ func (fs *filesystem) doCreateAt(rp *vfs.ResolvingPath, dir bool, create func(pa
|
|||
return create(parent, name)
|
||||
}
|
||||
|
||||
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
||||
func (fs *filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
d, err := resolveLocked(rp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.inode.checkPermissions(creds, ats, d.inode.isDir())
|
||||
}
|
||||
|
||||
// GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
|
||||
func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
|
||||
fs.mu.RLock()
|
||||
|
|
|
@ -514,7 +514,7 @@ func (ac accessContext) Value(key interface{}) interface{} {
|
|||
}
|
||||
}
|
||||
|
||||
func accessAt(t *kernel.Task, dirFD int32, addr usermem.Addr, resolve bool, mode uint) error {
|
||||
func accessAt(t *kernel.Task, dirFD int32, addr usermem.Addr, mode uint) error {
|
||||
const rOK = 4
|
||||
const wOK = 2
|
||||
const xOK = 1
|
||||
|
@ -529,7 +529,7 @@ func accessAt(t *kernel.Task, dirFD int32, addr usermem.Addr, resolve bool, mode
|
|||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
return fileOpOn(t, dirFD, path, resolve, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
return fileOpOn(t, dirFD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
// access(2) and faccessat(2) check permissions using real
|
||||
// UID/GID, not effective UID/GID.
|
||||
//
|
||||
|
@ -564,17 +564,23 @@ func Access(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
|||
addr := args[0].Pointer()
|
||||
mode := args[1].ModeT()
|
||||
|
||||
return 0, nil, accessAt(t, linux.AT_FDCWD, addr, true, mode)
|
||||
return 0, nil, accessAt(t, linux.AT_FDCWD, addr, mode)
|
||||
}
|
||||
|
||||
// Faccessat implements linux syscall faccessat(2).
|
||||
//
|
||||
// Note that the faccessat() system call does not take a flags argument:
|
||||
// "The raw faccessat() system call takes only the first three arguments. The
|
||||
// AT_EACCESS and AT_SYMLINK_NOFOLLOW flags are actually implemented within
|
||||
// the glibc wrapper function for faccessat(). If either of these flags is
|
||||
// specified, then the wrapper function employs fstatat(2) to determine access
|
||||
// permissions." - faccessat(2)
|
||||
func Faccessat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
dirFD := args[0].Int()
|
||||
addr := args[1].Pointer()
|
||||
mode := args[2].ModeT()
|
||||
flags := args[3].Int()
|
||||
|
||||
return 0, nil, accessAt(t, dirFD, addr, flags&linux.AT_SYMLINK_NOFOLLOW == 0, mode)
|
||||
return 0, nil, accessAt(t, dirFD, addr, mode)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/filesystem.go)
|
||||
|
|
|
@ -228,14 +228,64 @@ func Readlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
|||
|
||||
// Access implements Linux syscall access(2).
|
||||
func Access(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
// FIXME(jamieliu): actually implement
|
||||
return 0, nil, nil
|
||||
addr := args[0].Pointer()
|
||||
mode := args[1].ModeT()
|
||||
|
||||
return 0, nil, accessAt(t, linux.AT_FDCWD, addr, mode)
|
||||
}
|
||||
|
||||
// Faccessat implements Linux syscall access(2).
|
||||
// Faccessat implements Linux syscall faccessat(2).
|
||||
//
|
||||
// Note that the faccessat() system call does not take a flags argument:
|
||||
// "The raw faccessat() system call takes only the first three arguments. The
|
||||
// AT_EACCESS and AT_SYMLINK_NOFOLLOW flags are actually implemented within
|
||||
// the glibc wrapper function for faccessat(). If either of these flags is
|
||||
// specified, then the wrapper function employs fstatat(2) to determine access
|
||||
// permissions." - faccessat(2)
|
||||
func Faccessat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
// FIXME(jamieliu): actually implement
|
||||
return 0, nil, nil
|
||||
dirfd := args[0].Int()
|
||||
addr := args[1].Pointer()
|
||||
mode := args[2].ModeT()
|
||||
|
||||
return 0, nil, accessAt(t, dirfd, addr, mode)
|
||||
}
|
||||
|
||||
func accessAt(t *kernel.Task, dirfd int32, pathAddr usermem.Addr, mode uint) error {
|
||||
const rOK = 4
|
||||
const wOK = 2
|
||||
const xOK = 1
|
||||
|
||||
// Sanity check the mode.
|
||||
if mode&^(rOK|wOK|xOK) != 0 {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
path, err := copyInPath(t, pathAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, followFinalSymlink)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// access(2) and faccessat(2) check permissions using real
|
||||
// UID/GID, not effective UID/GID.
|
||||
//
|
||||
// "access() needs to use the real uid/gid, not the effective
|
||||
// uid/gid. We do this by temporarily clearing all FS-related
|
||||
// capabilities and switching the fsuid/fsgid around to the
|
||||
// real ones." -fs/open.c:faccessat
|
||||
creds := t.Credentials().Fork()
|
||||
creds.EffectiveKUID = creds.RealKUID
|
||||
creds.EffectiveKGID = creds.RealKGID
|
||||
if creds.EffectiveKUID.In(creds.UserNamespace) == auth.RootUID {
|
||||
creds.EffectiveCaps = creds.PermittedCaps
|
||||
} else {
|
||||
creds.EffectiveCaps = 0
|
||||
}
|
||||
|
||||
return t.Kernel().VFS().AccessAt(t, creds, vfs.AccessTypes(mode), &tpop.pop)
|
||||
}
|
||||
|
||||
// Readlinkat implements Linux syscall mknodat(2).
|
||||
|
|
|
@ -41,7 +41,14 @@ func (vfs *VirtualFilesystem) NewAnonVirtualDentry(name string) VirtualDentry {
|
|||
}
|
||||
}
|
||||
|
||||
const anonfsBlockSize = usermem.PageSize // via fs/libfs.c:pseudo_fs_fill_super()
|
||||
const (
|
||||
anonfsBlockSize = usermem.PageSize // via fs/libfs.c:pseudo_fs_fill_super()
|
||||
|
||||
// Mode, UID, and GID for a generic anonfs file.
|
||||
anonFileMode = 0600 // no type is correct
|
||||
anonFileUID = auth.RootKUID
|
||||
anonFileGID = auth.RootKGID
|
||||
)
|
||||
|
||||
// anonFilesystem is the implementation of FilesystemImpl that backs
|
||||
// VirtualDentries returned by VirtualFilesystem.NewAnonVirtualDentry().
|
||||
|
@ -69,6 +76,16 @@ func (fs *anonFilesystem) Sync(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1965): Implement access permissions.
|
||||
func (fs *anonFilesystem) AccessAt(ctx context.Context, rp *ResolvingPath, creds *auth.Credentials, ats AccessTypes) error {
|
||||
if !rp.Done() {
|
||||
return syserror.ENOTDIR
|
||||
}
|
||||
return GenericCheckPermissions(creds, ats, false /* isDir */, anonFileMode, anonFileUID, anonFileGID)
|
||||
}
|
||||
|
||||
// GetDentryAt implements FilesystemImpl.GetDentryAt.
|
||||
func (fs *anonFilesystem) GetDentryAt(ctx context.Context, rp *ResolvingPath, opts GetDentryOptions) (*Dentry, error) {
|
||||
if !rp.Done() {
|
||||
|
@ -167,9 +184,9 @@ func (fs *anonFilesystem) StatAt(ctx context.Context, rp *ResolvingPath, opts St
|
|||
Mask: linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_NLINK | linux.STATX_UID | linux.STATX_GID | linux.STATX_INO | linux.STATX_SIZE | linux.STATX_BLOCKS,
|
||||
Blksize: anonfsBlockSize,
|
||||
Nlink: 1,
|
||||
UID: uint32(auth.RootKUID),
|
||||
GID: uint32(auth.RootKGID),
|
||||
Mode: 0600, // no type is correct
|
||||
UID: uint32(anonFileUID),
|
||||
GID: uint32(anonFileGID),
|
||||
Mode: anonFileMode,
|
||||
Ino: 1,
|
||||
Size: 0,
|
||||
Blocks: 0,
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
)
|
||||
|
||||
// A Filesystem is a tree of nodes represented by Dentries, which forms part of
|
||||
|
@ -144,6 +145,9 @@ type FilesystemImpl interface {
|
|||
// file data to be written to the underlying [filesystem]", as by syncfs(2).
|
||||
Sync(ctx context.Context) error
|
||||
|
||||
// AccessAt checks whether a user with creds can access the file at rp.
|
||||
AccessAt(ctx context.Context, rp *ResolvingPath, creds *auth.Credentials, ats AccessTypes) error
|
||||
|
||||
// GetDentryAt returns a Dentry representing the file at rp. A reference is
|
||||
// taken on the returned Dentry.
|
||||
//
|
||||
|
|
|
@ -174,6 +174,23 @@ type PathOperation struct {
|
|||
FollowFinalSymlink bool
|
||||
}
|
||||
|
||||
// AccessAt checks whether a user with creds has access to the file at
|
||||
// the given path.
|
||||
func (vfs *VirtualFilesystem) AccessAt(ctx context.Context, creds *auth.Credentials, ats AccessTypes, pop *PathOperation) error {
|
||||
rp := vfs.getResolvingPath(creds, pop)
|
||||
for {
|
||||
err := rp.mount.fs.impl.AccessAt(ctx, rp, creds, ats)
|
||||
if err == nil {
|
||||
vfs.putResolvingPath(rp)
|
||||
return nil
|
||||
}
|
||||
if !rp.handleError(err) {
|
||||
vfs.putResolvingPath(rp)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetDentryAt returns a VirtualDentry representing the given path, at which a
|
||||
// file must exist. A reference is taken on the returned VirtualDentry.
|
||||
func (vfs *VirtualFilesystem) GetDentryAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, opts *GetDentryOptions) (VirtualDentry, error) {
|
||||
|
|
Loading…
Reference in New Issue