mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Use host-defined file owner and mode, when possible, for imported fds.
Using the host-defined file owner matches VFS1. It is more correct to use the host-defined mode, since the cached value may become out of date. However, kernfs.Inode.Mode() does not return an error--other filesystems on kernfs are in-memory so retrieving mode should not fail. Therefore, if the host syscall fails, we rely on a cached value instead. Updates #1672. PiperOrigin-RevId: 303220864
This commit is contained in:
@@ -224,8 +224,6 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
|
||||
}
|
||||
}
|
||||
|
||||
mounter := fs.FileOwnerFromContext(ctx)
|
||||
|
||||
// TODO(gvisor.dev/issue/1623): Use host FD when supported in VFS2.
|
||||
var ttyFile *fs.File
|
||||
for appFD, hostFile := range args.FilePayload.Files {
|
||||
@@ -235,7 +233,7 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
|
||||
// Import the file as a host TTY file.
|
||||
if ttyFile == nil {
|
||||
var err error
|
||||
appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), mounter, true /* isTTY */)
|
||||
appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), true /* isTTY */)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
@@ -254,7 +252,7 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
|
||||
} else {
|
||||
// Import the file as a regular host file.
|
||||
var err error
|
||||
appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), mounter, false /* isTTY */)
|
||||
appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), false /* isTTY */)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ go_test(
|
||||
"//pkg/fd",
|
||||
"//pkg/fdnotifier",
|
||||
"//pkg/sentry/contexttest",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/socket",
|
||||
"//pkg/sentry/socket/unix/transport",
|
||||
|
||||
@@ -78,7 +78,7 @@ func fdsToFiles(ctx context.Context, fds []int) []*fs.File {
|
||||
}
|
||||
|
||||
// Create the file backed by hostFD.
|
||||
file, err := NewFile(ctx, fd, fs.FileOwnerFromContext(ctx))
|
||||
file, err := NewFile(ctx, fd)
|
||||
if err != nil {
|
||||
ctx.Warningf("Error creating file from host FD: %v", err)
|
||||
break
|
||||
|
||||
@@ -60,8 +60,8 @@ var _ fs.FileOperations = (*fileOperations)(nil)
|
||||
// The returned File cannot be saved, since there is no guarantee that the same
|
||||
// FD will exist or represent the same file at time of restore. If such a
|
||||
// guarantee does exist, use ImportFile instead.
|
||||
func NewFile(ctx context.Context, fd int, mounter fs.FileOwner) (*fs.File, error) {
|
||||
return newFileFromDonatedFD(ctx, fd, mounter, false, false)
|
||||
func NewFile(ctx context.Context, fd int) (*fs.File, error) {
|
||||
return newFileFromDonatedFD(ctx, fd, false, false)
|
||||
}
|
||||
|
||||
// ImportFile creates a new File backed by the provided host file descriptor.
|
||||
@@ -71,13 +71,13 @@ func NewFile(ctx context.Context, fd int, mounter fs.FileOwner) (*fs.File, error
|
||||
// If the returned file is saved, it will be restored by re-importing the FD
|
||||
// originally passed to ImportFile. It is the restorer's responsibility to
|
||||
// ensure that the FD represents the same file.
|
||||
func ImportFile(ctx context.Context, fd int, mounter fs.FileOwner, isTTY bool) (*fs.File, error) {
|
||||
return newFileFromDonatedFD(ctx, fd, mounter, true, isTTY)
|
||||
func ImportFile(ctx context.Context, fd int, isTTY bool) (*fs.File, error) {
|
||||
return newFileFromDonatedFD(ctx, fd, true, isTTY)
|
||||
}
|
||||
|
||||
// newFileFromDonatedFD returns an fs.File from a donated FD. If the FD is
|
||||
// saveable, then saveable is true.
|
||||
func newFileFromDonatedFD(ctx context.Context, donated int, mounter fs.FileOwner, saveable, isTTY bool) (*fs.File, error) {
|
||||
func newFileFromDonatedFD(ctx context.Context, donated int, saveable, isTTY bool) (*fs.File, error) {
|
||||
var s syscall.Stat_t
|
||||
if err := syscall.Fstat(donated, &s); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/contexttest"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
)
|
||||
|
||||
// TestCloseFD verifies fds will be closed.
|
||||
@@ -33,7 +32,7 @@ func TestCloseFD(t *testing.T) {
|
||||
|
||||
// Use the write-end because we will detect if it's closed on the read end.
|
||||
ctx := contexttest.Context(t)
|
||||
file, err := NewFile(ctx, p[1], fs.RootOwner)
|
||||
file, err := NewFile(ctx, p[1])
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create File: %v", err)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/contexttest"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
@@ -34,7 +33,7 @@ func TestWait(t *testing.T) {
|
||||
defer syscall.Close(fds[1])
|
||||
|
||||
ctx := contexttest.Context(t)
|
||||
file, err := NewFile(ctx, fds[0], fs.RootOwner)
|
||||
file, err := NewFile(ctx, fds[0])
|
||||
if err != nil {
|
||||
syscall.Close(fds[0])
|
||||
t.Fatalf("NewFile failed: %v", err)
|
||||
|
||||
@@ -54,7 +54,7 @@ func NewMount(vfsObj *vfs.VirtualFilesystem) (*vfs.Mount, error) {
|
||||
}
|
||||
|
||||
// ImportFD sets up and returns a vfs.FileDescription from a donated fd.
|
||||
func ImportFD(mnt *vfs.Mount, hostFD int, ownerUID auth.KUID, ownerGID auth.KGID, isTTY bool) (*vfs.FileDescription, error) {
|
||||
func ImportFD(mnt *vfs.Mount, hostFD int, isTTY bool) (*vfs.FileDescription, error) {
|
||||
fs, ok := mnt.Filesystem().Impl().(*kernfs.Filesystem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("can't import host FDs into filesystems of type %T", mnt.Filesystem().Impl())
|
||||
@@ -78,8 +78,6 @@ func ImportFD(mnt *vfs.Mount, hostFD int, ownerUID auth.KUID, ownerGID auth.KGID
|
||||
canMap: canMap(uint32(fileType)),
|
||||
ino: fs.NextIno(),
|
||||
mode: fileMode,
|
||||
uid: ownerUID,
|
||||
gid: ownerGID,
|
||||
// For simplicity, set offset to 0. Technically, we should
|
||||
// only set to 0 on files that are not seekable (sockets, pipes, etc.),
|
||||
// and use the offset from the host fd otherwise.
|
||||
@@ -135,17 +133,20 @@ type inode struct {
|
||||
// This field is initialized at creation time and is immutable.
|
||||
ino uint64
|
||||
|
||||
// TODO(gvisor.dev/issue/1672): protect mode, uid, and gid with mutex.
|
||||
// modeMu protects mode.
|
||||
modeMu sync.Mutex
|
||||
|
||||
// mode is the file mode of this inode. Note that this value may become out
|
||||
// of date if the mode is changed on the host, e.g. with chmod.
|
||||
// mode is a cached version of the file mode on the host. Note that it may
|
||||
// become out of date if the mode is changed on the host, e.g. with chmod.
|
||||
//
|
||||
// Generally, it is better to retrieve the mode from the host through an
|
||||
// fstat syscall. We only use this value in inode.Mode(), which cannot
|
||||
// return an error, if the syscall to host fails.
|
||||
//
|
||||
// FIXME(b/152294168): Plumb error into Inode.Mode() return value so we
|
||||
// can get rid of this.
|
||||
mode linux.FileMode
|
||||
|
||||
// uid and gid of the file owner. Note that these refer to the owner of the
|
||||
// file created on import, not the fd on the host.
|
||||
uid auth.KUID
|
||||
gid auth.KGID
|
||||
|
||||
// offsetMu protects offset.
|
||||
offsetMu sync.Mutex
|
||||
|
||||
@@ -168,12 +169,35 @@ func fileFlagsFromHostFD(fd int) (int, error) {
|
||||
|
||||
// CheckPermissions implements kernfs.Inode.
|
||||
func (i *inode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
return vfs.GenericCheckPermissions(creds, ats, i.mode, i.uid, i.gid)
|
||||
mode, uid, gid, err := i.getPermissions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return vfs.GenericCheckPermissions(creds, ats, mode, uid, gid)
|
||||
}
|
||||
|
||||
// Mode implements kernfs.Inode.
|
||||
func (i *inode) Mode() linux.FileMode {
|
||||
return i.mode
|
||||
mode, _, _, err := i.getPermissions()
|
||||
if err != nil {
|
||||
return i.mode
|
||||
}
|
||||
|
||||
return linux.FileMode(mode)
|
||||
}
|
||||
|
||||
func (i *inode) getPermissions() (linux.FileMode, auth.KUID, auth.KGID, error) {
|
||||
// Retrieve metadata.
|
||||
var s syscall.Stat_t
|
||||
if err := syscall.Fstat(i.hostFD, &s); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
// Update cached mode.
|
||||
i.modeMu.Lock()
|
||||
i.mode = linux.FileMode(s.Mode)
|
||||
i.modeMu.Unlock()
|
||||
return linux.FileMode(s.Mode), auth.KUID(s.Uid), auth.KGID(s.Gid), nil
|
||||
}
|
||||
|
||||
// Stat implements kernfs.Inode.
|
||||
@@ -213,45 +237,51 @@ func (i *inode) Stat(_ *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, erro
|
||||
ls.Attributes = s.Attributes
|
||||
ls.AttributesMask = s.Attributes_mask
|
||||
|
||||
if mask|linux.STATX_TYPE != 0 {
|
||||
if mask&linux.STATX_TYPE != 0 {
|
||||
ls.Mode |= s.Mode & linux.S_IFMT
|
||||
}
|
||||
if mask|linux.STATX_MODE != 0 {
|
||||
if mask&linux.STATX_MODE != 0 {
|
||||
ls.Mode |= s.Mode &^ linux.S_IFMT
|
||||
}
|
||||
if mask|linux.STATX_NLINK != 0 {
|
||||
if mask&linux.STATX_NLINK != 0 {
|
||||
ls.Nlink = s.Nlink
|
||||
}
|
||||
if mask|linux.STATX_ATIME != 0 {
|
||||
if mask&linux.STATX_UID != 0 {
|
||||
ls.UID = s.Uid
|
||||
}
|
||||
if mask&linux.STATX_GID != 0 {
|
||||
ls.GID = s.Gid
|
||||
}
|
||||
if mask&linux.STATX_ATIME != 0 {
|
||||
ls.Atime = unixToLinuxStatxTimestamp(s.Atime)
|
||||
}
|
||||
if mask|linux.STATX_BTIME != 0 {
|
||||
if mask&linux.STATX_BTIME != 0 {
|
||||
ls.Btime = unixToLinuxStatxTimestamp(s.Btime)
|
||||
}
|
||||
if mask|linux.STATX_CTIME != 0 {
|
||||
if mask&linux.STATX_CTIME != 0 {
|
||||
ls.Ctime = unixToLinuxStatxTimestamp(s.Ctime)
|
||||
}
|
||||
if mask|linux.STATX_MTIME != 0 {
|
||||
if mask&linux.STATX_MTIME != 0 {
|
||||
ls.Mtime = unixToLinuxStatxTimestamp(s.Mtime)
|
||||
}
|
||||
if mask|linux.STATX_SIZE != 0 {
|
||||
if mask&linux.STATX_SIZE != 0 {
|
||||
ls.Size = s.Size
|
||||
}
|
||||
if mask|linux.STATX_BLOCKS != 0 {
|
||||
if mask&linux.STATX_BLOCKS != 0 {
|
||||
ls.Blocks = s.Blocks
|
||||
}
|
||||
|
||||
// Use our own internal inode number and file owner.
|
||||
if mask|linux.STATX_INO != 0 {
|
||||
// Use our own internal inode number.
|
||||
if mask&linux.STATX_INO != 0 {
|
||||
ls.Ino = i.ino
|
||||
}
|
||||
if mask|linux.STATX_UID != 0 {
|
||||
ls.UID = uint32(i.uid)
|
||||
}
|
||||
if mask|linux.STATX_GID != 0 {
|
||||
ls.GID = uint32(i.gid)
|
||||
}
|
||||
|
||||
// Update cached mode.
|
||||
if (mask&linux.STATX_TYPE != 0) && (mask&linux.STATX_MODE != 0) {
|
||||
i.modeMu.Lock()
|
||||
i.mode = linux.FileMode(s.Mode)
|
||||
i.modeMu.Unlock()
|
||||
}
|
||||
return ls, nil
|
||||
}
|
||||
|
||||
@@ -274,6 +304,8 @@ func (i *inode) fstat(opts vfs.StatOptions) (linux.Statx, error) {
|
||||
Mask: linux.STATX_BASIC_STATS,
|
||||
Blksize: uint32(s.Blksize),
|
||||
Nlink: uint32(s.Nlink),
|
||||
UID: s.Uid,
|
||||
GID: s.Gid,
|
||||
Mode: uint16(s.Mode),
|
||||
Size: uint64(s.Size),
|
||||
Blocks: uint64(s.Blocks),
|
||||
@@ -282,15 +314,13 @@ func (i *inode) fstat(opts vfs.StatOptions) (linux.Statx, error) {
|
||||
Mtime: timespecToStatxTimestamp(s.Mtim),
|
||||
}
|
||||
|
||||
// Use our own internal inode number and file owner.
|
||||
// Use our own internal inode number.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1672): Use a kernfs-specific device number as well.
|
||||
// If we use the device number from the host, it may collide with another
|
||||
// sentry-internal device number. We handle device/inode numbers without
|
||||
// relying on the host to prevent collisions.
|
||||
ls.Ino = i.ino
|
||||
ls.UID = uint32(i.uid)
|
||||
ls.GID = uint32(i.gid)
|
||||
|
||||
return ls, nil
|
||||
}
|
||||
@@ -306,7 +336,11 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
|
||||
if m&^(linux.STATX_MODE|linux.STATX_SIZE|linux.STATX_ATIME|linux.STATX_MTIME) != 0 {
|
||||
return syserror.EPERM
|
||||
}
|
||||
if err := vfs.CheckSetStat(ctx, creds, &s, i.Mode(), i.uid, i.gid); err != nil {
|
||||
mode, uid, gid, err := i.getPermissions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := vfs.CheckSetStat(ctx, creds, &s, mode.Permissions(), uid, gid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -314,7 +348,9 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
|
||||
if err := syscall.Fchmod(i.hostFD, uint32(s.Mode)); err != nil {
|
||||
return err
|
||||
}
|
||||
i.modeMu.Lock()
|
||||
i.mode = linux.FileMode(s.Mode)
|
||||
i.modeMu.Unlock()
|
||||
}
|
||||
if m&linux.STATX_SIZE != 0 {
|
||||
if err := syscall.Ftruncate(i.hostFD, int64(s.Size)); err != nil {
|
||||
@@ -351,7 +387,11 @@ func (i *inode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptio
|
||||
}
|
||||
|
||||
func (i *inode) open(d *vfs.Dentry, mnt *vfs.Mount) (*vfs.FileDescription, error) {
|
||||
fileType := i.mode.FileType()
|
||||
mode, _, _, err := i.getPermissions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileType := mode.FileType()
|
||||
if fileType == syscall.S_IFSOCK {
|
||||
if i.isTTY {
|
||||
return nil, errors.New("cannot use host socket as TTY")
|
||||
|
||||
+2
-3
@@ -34,7 +34,6 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []int) (*kernel.F
|
||||
k := kernel.KernelFromContext(ctx)
|
||||
fdTable := k.NewFDTable()
|
||||
defer fdTable.DecRef()
|
||||
mounter := fs.FileOwnerFromContext(ctx)
|
||||
|
||||
var ttyFile *fs.File
|
||||
for appFD, hostFD := range stdioFDs {
|
||||
@@ -44,7 +43,7 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []int) (*kernel.F
|
||||
// Import the file as a host TTY file.
|
||||
if ttyFile == nil {
|
||||
var err error
|
||||
appFile, err = host.ImportFile(ctx, hostFD, mounter, true /* isTTY */)
|
||||
appFile, err = host.ImportFile(ctx, hostFD, true /* isTTY */)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -63,7 +62,7 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []int) (*kernel.F
|
||||
} else {
|
||||
// Import the file as a regular host file.
|
||||
var err error
|
||||
appFile, err = host.ImportFile(ctx, hostFD, mounter, false /* isTTY */)
|
||||
appFile, err = host.ImportFile(ctx, hostFD, false /* isTTY */)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user