diff --git a/pkg/context/BUILD b/pkg/context/BUILD index e6f52309e..d38182689 100644 --- a/pkg/context/BUILD +++ b/pkg/context/BUILD @@ -12,6 +12,8 @@ go_library( ], visibility = ["//:sandbox"], deps = [ + "//pkg/abi/linux/errno", + "//pkg/errors", "//pkg/log", "//pkg/waiter", ], diff --git a/pkg/context/context.go b/pkg/context/context.go index 469e254ca..14776d7aa 100644 --- a/pkg/context/context.go +++ b/pkg/context/context.go @@ -27,6 +27,8 @@ import ( "sync" "time" + "gvisor.dev/gvisor/pkg/abi/linux/errno" + "gvisor.dev/gvisor/pkg/errors" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/waiter" ) @@ -49,6 +51,11 @@ type Blocker interface { // result of the requested event (versus an external interrupt). BlockOn(waiter.Waitable, waiter.EventMask) bool + // Block blocks until an event is received from C, or some external + // interrupt. It returns nil if an event is received from C and an err if t + // is interrupted. + Block(C <-chan struct{}) error + // BlockWithTimeoutOn blocks until either the conditions of Block are // satisfied, or the timeout is hit. Note that deadlines are not supported // since the notion of "with respect to what clock" is not resolved. @@ -88,6 +95,19 @@ func (nt *NoTask) Interrupted() bool { return nt.cancel != nil && len(nt.cancel) > 0 } +// Block implements Blocker.Block. +func (nt *NoTask) Block(C <-chan struct{}) error { + if nt.cancel == nil { + nt.cancel = make(chan struct{}, 1) + } + select { + case <-nt.cancel: + return errors.New(errno.EINTR, "interrupted system call") // Interrupted. + case <-C: + return nil + } +} + // BlockOn implements Blocker.BlockOn. func (nt *NoTask) BlockOn(w waiter.Waitable, mask waiter.EventMask) bool { if nt.cancel == nil { diff --git a/pkg/sentry/fsimpl/fuse/connection.go b/pkg/sentry/fsimpl/fuse/connection.go index 2d52d3847..3f5a2987d 100644 --- a/pkg/sentry/fsimpl/fuse/connection.go +++ b/pkg/sentry/fsimpl/fuse/connection.go @@ -22,7 +22,6 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/log" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/waiter" ) @@ -176,7 +175,7 @@ type connection struct { // Negotiated and only set in INIT. bigWrites bool - // dontMask if filestestem does not apply umask to creation modes. + // dontMask if filesystem does not apply umask to creation modes. // Negotiated in INIT. dontMask bool @@ -227,9 +226,9 @@ func newFUSEConnection(_ context.Context, fuseFD *DeviceFD, opts *filesystemOpti // CallAsync makes an async (aka background) request. // It's a simple wrapper around Call(). -func (conn *connection) CallAsync(t *kernel.Task, r *Request) error { +func (conn *connection) CallAsync(ctx context.Context, r *Request) error { r.async = true - _, err := conn.Call(t, r) + _, err := conn.Call(ctx, r) return err } @@ -251,10 +250,11 @@ func (conn *connection) CallAsync(t *kernel.Task, r *Request) error { // // The forget request does not have a reply, // as documented in include/uapi/linux/fuse.h:FUSE_FORGET. -func (conn *connection) Call(t *kernel.Task, r *Request) (*Response, error) { +func (conn *connection) Call(ctx context.Context, r *Request) (*Response, error) { + b := blockerFromContext(ctx) // Block requests sent before connection is initialized. if !conn.Initialized() && r.hdr.Opcode != linux.FUSE_INIT { - if err := t.Block(conn.initializedChan); err != nil { + if err := b.Block(conn.initializedChan); err != nil { return nil, err } } @@ -275,19 +275,19 @@ func (conn *connection) Call(t *kernel.Task, r *Request) (*Response, error) { return nil, linuxerr.ECONNREFUSED } - fut, err := conn.callFuture(t, r) + fut, err := conn.callFuture(b, r) conn.fd.mu.Unlock() if err != nil { return nil, err } - return fut.resolve(t) + return fut.resolve(b) } // callFuture makes a request to the server and returns a future response. // Call resolve() when the response needs to be fulfilled. // +checklocks:conn.fd.mu -func (conn *connection) callFuture(t *kernel.Task, r *Request) (*futureResponse, error) { +func (conn *connection) callFuture(b context.Blocker, r *Request) (*futureResponse, error) { // Is the queue full? // // We must busy wait here until the request can be queued. We don't @@ -303,19 +303,19 @@ func (conn *connection) callFuture(t *kernel.Task, r *Request) (*futureResponse, log.Infof("Blocking request %v from being queued. Too many active requests: %v", r.id, conn.fd.numActiveRequests) conn.fd.mu.Unlock() - err := t.Block(conn.fd.fullQueueCh) + err := b.Block(conn.fd.fullQueueCh) conn.fd.mu.Lock() if err != nil { return nil, err } } - return conn.callFutureLocked(t, r) + return conn.callFutureLocked(r) } // callFutureLocked makes a request to the server and returns a future response. // +checklocks:conn.fd.mu -func (conn *connection) callFutureLocked(t *kernel.Task, r *Request) (*futureResponse, error) { +func (conn *connection) callFutureLocked(r *Request) (*futureResponse, error) { // Check connected again holding conn.mu. conn.mu.Lock() if !conn.connected { diff --git a/pkg/sentry/fsimpl/fuse/connection_control.go b/pkg/sentry/fsimpl/fuse/connection_control.go index 60832ea3d..ae2cad8c6 100644 --- a/pkg/sentry/fsimpl/fuse/connection_control.go +++ b/pkg/sentry/fsimpl/fuse/connection_control.go @@ -88,13 +88,13 @@ func (conn *connection) InitSend(creds *auth.Credentials, pid uint32) error { req := conn.NewRequest(creds, pid, 0, linux.FUSE_INIT, &in) // Since there is no task to block on and FUSE_INIT is the request - // to unblock other requests, use nil. - return conn.CallAsync(nil, req) + // to unblock other requests, use context.Background(). + return conn.CallAsync(context.Background(), req) } // InitRecv receives a FUSE_INIT reply and process it. // -// Preconditions: conn.asyncMu must not be held if minor verion is newer than 13. +// Preconditions: conn.asyncMu must not be held if minor version is newer than 13. func (conn *connection) InitRecv(res *Response, hasSysAdminCap bool) error { if err := res.Error(); err != nil { return err @@ -115,7 +115,7 @@ func (conn *connection) InitRecv(res *Response, hasSysAdminCap bool) error { // It tries to acquire the conn.asyncMu lock if minor version is newer than 13. func (conn *connection) initProcessReply(out *linux.FUSEInitOut, hasSysAdminCap bool) error { conn.mu.Lock() - // No matter error or not, always set initialzied. + // No matter error or not, always set initialized. // to unblock the blocked requests. defer func() { conn.SetInitialized() diff --git a/pkg/sentry/fsimpl/fuse/connection_test.go b/pkg/sentry/fsimpl/fuse/connection_test.go index 848a5bbf5..51886f720 100644 --- a/pkg/sentry/fsimpl/fuse/connection_test.go +++ b/pkg/sentry/fsimpl/fuse/connection_test.go @@ -21,7 +21,6 @@ import ( "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/marshal/primitive" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ) @@ -33,9 +32,7 @@ func TestConnectionInitBlock(t *testing.T) { s := setup(t) defer s.Destroy() - k := kernel.KernelFromContext(s.Ctx) - - conn, _, err := newTestConnection(s, k, maxActiveRequestsDefault) + conn, _, err := newTestConnection(s, maxActiveRequestsDefault) if err != nil { t.Fatalf("newTestConnection: %v", err) } @@ -59,13 +56,11 @@ func TestConnectionAbort(t *testing.T) { s := setup(t) defer s.Destroy() - k := kernel.KernelFromContext(s.Ctx) creds := auth.CredentialsFromContext(s.Ctx) - task := kernel.TaskFromContext(s.Ctx) const numRequests uint64 = 256 - conn, _, err := newTestConnection(s, k, numRequests) + conn, _, err := newTestConnection(s, numRequests) if err != nil { t.Fatalf("newTestConnection: %v", err) } @@ -75,7 +70,7 @@ func TestConnectionAbort(t *testing.T) { for i := 0; i < int(numRequests); i++ { req := conn.NewRequest(creds, uint32(i), uint64(i), 0, &testObj) conn.fd.mu.Lock() - fut, err := conn.callFutureLocked(task, req) + fut, err := conn.callFutureLocked(req) conn.fd.mu.Unlock() if err != nil { t.Fatalf("callFutureLocked failed: %v", err) @@ -104,7 +99,7 @@ func TestConnectionAbort(t *testing.T) { // After abort, Call() should return directly with ENOTCONN. req := conn.NewRequest(creds, 0, 0, 0, &testObj) - _, err = conn.Call(task, req) + _, err = conn.Call(s.Ctx, req) if !linuxerr.Equals(linuxerr.ENOTCONN, err) { t.Fatalf("Incorrect error code received for Call() after connection aborted") } diff --git a/pkg/sentry/fsimpl/fuse/dev_test.go b/pkg/sentry/fsimpl/fuse/dev_test.go index 736ea8c4e..a8d62e196 100644 --- a/pkg/sentry/fsimpl/fuse/dev_test.go +++ b/pkg/sentry/fsimpl/fuse/dev_test.go @@ -90,7 +90,7 @@ func TestFUSECommunication(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.Name, func(t *testing.T) { - conn, fd, err := newTestConnection(s, k, testCase.MaxActiveRequests) + conn, fd, err := newTestConnection(s, testCase.MaxActiveRequests) if err != nil { t.Fatalf("newTestConnection: %v", err) } @@ -139,8 +139,7 @@ func TestFUSECommunication(t *testing.T) { func TestReuseFd(t *testing.T) { s := setup(t) defer s.Destroy() - k := kernel.KernelFromContext(s.Ctx) - _, fd, err := newTestConnection(s, k, maxActiveRequestsDefault) + _, fd, err := newTestConnection(s, maxActiveRequestsDefault) if err != nil { t.Fatalf("newTestConnection: %v", err) } @@ -174,7 +173,7 @@ func CallTest(conn *connection, t *kernel.Task, r *Request, i uint32) (*Response conn.fd.mu.Lock() } - fut, err := conn.callFutureLocked(t, r) // No task given. + fut, err := conn.callFutureLocked(r) // No task given. conn.fd.mu.Unlock() if err != nil { diff --git a/pkg/sentry/fsimpl/fuse/directory.go b/pkg/sentry/fsimpl/fuse/directory.go index 0cd9234de..3ea0f14af 100644 --- a/pkg/sentry/fsimpl/fuse/directory.go +++ b/pkg/sentry/fsimpl/fuse/directory.go @@ -18,7 +18,6 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/usermem" @@ -56,7 +55,6 @@ func (*directoryFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs. // IterDirents implements vfs.FileDescriptionImpl.IterDirents. func (dir *directoryFD) IterDirents(ctx context.Context, callback vfs.IterDirentsCallback) error { fusefs := dir.inode().fs - task, creds := kernel.TaskFromContext(ctx), auth.CredentialsFromContext(ctx) in := linux.FUSEReadIn{ Fh: dir.Fh, @@ -66,8 +64,8 @@ func (dir *directoryFD) IterDirents(ctx context.Context, callback vfs.IterDirent } // TODO(gVisor.dev/issue/3404): Support FUSE_READDIRPLUS. - req := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), dir.inode().nodeID, linux.FUSE_READDIR, &in) - res, err := fusefs.conn.Call(task, req) + req := fusefs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), dir.inode().nodeID, linux.FUSE_READDIR, &in) + res, err := fusefs.conn.Call(ctx, req) if err != nil { return err } diff --git a/pkg/sentry/fsimpl/fuse/file.go b/pkg/sentry/fsimpl/fuse/file.go index 502a24eea..333b00556 100644 --- a/pkg/sentry/fsimpl/fuse/file.go +++ b/pkg/sentry/fsimpl/fuse/file.go @@ -20,7 +20,6 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/usermem" @@ -87,11 +86,10 @@ func (fd *fileDescription) Release(ctx context.Context) { } else { opcode = linux.FUSE_RELEASE } - kernelTask := kernel.TaskFromContext(ctx) // Ignoring errors and FUSE server replies is analogous to Linux's behavior. - req := conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), inode.nodeID, opcode, &in) + req := conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), inode.nodeID, opcode, &in) // The reply will be ignored since no callback is defined in asyncCallBack(). - conn.CallAsync(kernelTask, req) + conn.CallAsync(ctx, req) } // PRead implements vfs.FileDescriptionImpl.PRead. @@ -149,15 +147,14 @@ func (fd *fileDescription) Sync(ctx context.Context) error { if conn.noOpen { return linuxerr.EINVAL } - kernelTask := kernel.TaskFromContext(ctx) in := linux.FUSEFsyncIn{ Fh: fd.Fh, FsyncFlags: fd.statusFlags(), } // Ignoring errors and FUSE server replies is analogous to Linux's behavior. - req := conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), inode.nodeID, linux.FUSE_FSYNC, &in) + req := conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), inode.nodeID, linux.FUSE_FSYNC, &in) // The reply will be ignored since no callback is defined in asyncCallBack(). - conn.CallAsync(kernelTask, req) + conn.CallAsync(ctx, req) return nil } diff --git a/pkg/sentry/fsimpl/fuse/inode.go b/pkg/sentry/fsimpl/fuse/inode.go index 7def4ad9a..ab8fdfc8d 100644 --- a/pkg/sentry/fsimpl/fuse/inode.go +++ b/pkg/sentry/fsimpl/fuse/inode.go @@ -24,7 +24,6 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/hostarch" - "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/marshal" "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" @@ -105,6 +104,31 @@ type inode struct { blockSize atomicbitops.Uint32 // 0 if unknown. } +func blockerFromContext(ctx context.Context) context.Blocker { + kernelTask := kernel.TaskFromContext(ctx) + if kernelTask == nil { + return ctx + } + return kernelTask +} + +func pidFromContext(ctx context.Context) uint32 { + kernelTask := kernel.TaskFromContext(ctx) + if kernelTask == nil { + return 0 + } + return uint32(kernelTask.ThreadID()) +} + +func umaskFromContext(ctx context.Context) uint32 { + kernelTask := kernel.TaskFromContext(ctx) + umask := uint32(0) + if kernelTask != nil { + umask = uint32(kernelTask.FSContext().Umask()) + } + return umask +} + func (i *inode) Mode() linux.FileMode { i.attrMu.Lock() defer i.attrMu.Unlock() @@ -212,14 +236,9 @@ func (i *inode) CheckPermissions(ctx context.Context, creds *auth.Credentials, a } return err } else if ats.MayRead() || ats.MayWrite() || ats.MayExec() { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.CheckPermissions: couldn't get kernel task from context") - return linuxerr.EINVAL - } in := linux.FUSEAccessIn{Mask: uint32(ats)} - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_ACCESS, &in) - res, err := i.fs.conn.Call(kernelTask, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_ACCESS, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return err } @@ -269,7 +288,7 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr } fd.LockFD.Init(&i.locks) - // FOPEN_KEEP_CACHE is the defualt flag for noOpen. + // FOPEN_KEEP_CACHE is the default flag for noOpen. fd.OpenFlag = linux.FOPEN_KEEP_CACHE if i.fh.new { @@ -279,20 +298,14 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr // Only send an open request when the FUSE server supports open or is // opening a directory. } else if !i.fs.conn.noOpen || i.filemode().IsDir() { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.Open: couldn't get kernel task from context") - return nil, linuxerr.EINVAL - } - in := linux.FUSEOpenIn{Flags: opts.Flags & ^uint32(linux.O_CREAT|linux.O_EXCL|linux.O_NOCTTY)} // Truncating with SETATTR instead of O_TRUNC, so clear the flag. if !i.fs.conn.atomicOTrunc { in.Flags &= ^uint32(linux.O_TRUNC) } - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, &in) - res, err := i.fs.conn.Call(kernelTask, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, opcode, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return nil, err } @@ -370,16 +383,11 @@ func (*inode) IterDirents(ctx context.Context, mnt *vfs.Mount, callback vfs.Iter func (i *inode) NewFile(ctx context.Context, name string, opts vfs.OpenOptions) (kernfs.Inode, error) { opts.Flags &= linux.O_ACCMODE | linux.O_CREAT | linux.O_EXCL | linux.O_TRUNC | linux.O_DIRECTORY | linux.O_NOFOLLOW | linux.O_NONBLOCK | linux.O_NOCTTY - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.NewFile: couldn't get kernel task from context", i.nodeID) - return nil, linuxerr.EINVAL - } in := linux.FUSECreateIn{ CreateMeta: linux.FUSECreateMeta{ Flags: opts.Flags, Mode: uint32(opts.Mode) | linux.S_IFREG, - Umask: uint32(kernelTask.FSContext().Umask()), + Umask: umaskFromContext(ctx), }, Name: linux.CString(name), } @@ -392,7 +400,7 @@ func (i *inode) NewNode(ctx context.Context, name string, opts vfs.MknodOptions) MknodMeta: linux.FUSEMknodMeta{ Mode: uint32(opts.Mode), Rdev: linux.MakeDeviceID(uint16(opts.DevMajor), opts.DevMinor), - Umask: uint32(kernel.TaskFromContext(ctx).FSContext().Umask()), + Umask: umaskFromContext(ctx), }, Name: linux.CString(name), } @@ -420,14 +428,9 @@ func (i *inode) NewLink(ctx context.Context, name string, target kernfs.Inode) ( // Unlink implements kernfs.Inode.Unlink. func (i *inode) Unlink(ctx context.Context, name string, child kernfs.Inode) error { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID) - return linuxerr.EINVAL - } in := linux.FUSEUnlinkIn{Name: linux.CString(name)} - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_UNLINK, &in) - res, err := i.fs.conn.Call(kernelTask, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_UNLINK, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return err } @@ -440,7 +443,7 @@ func (i *inode) NewDir(ctx context.Context, name string, opts vfs.MkdirOptions) in := linux.FUSEMkdirIn{ MkdirMeta: linux.FUSEMkdirMeta{ Mode: uint32(opts.Mode), - Umask: uint32(kernel.TaskFromContext(ctx).FSContext().Umask()), + Umask: umaskFromContext(ctx), }, Name: linux.CString(name), } @@ -449,12 +452,9 @@ func (i *inode) NewDir(ctx context.Context, name string, opts vfs.MkdirOptions) // RmDir implements kernfs.Inode.RmDir. func (i *inode) RmDir(ctx context.Context, name string, child kernfs.Inode) error { - fusefs := i.fs - task, creds := kernel.TaskFromContext(ctx), auth.CredentialsFromContext(ctx) - in := linux.FUSERmDirIn{Name: linux.CString(name)} - req := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_RMDIR, &in) - res, err := i.fs.conn.Call(task, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_RMDIR, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return err } @@ -463,20 +463,14 @@ func (i *inode) RmDir(ctx context.Context, name string, child kernfs.Inode) erro // Rename implements kernfs.Inode.Rename. func (i *inode) Rename(ctx context.Context, oldname, newname string, child, dstDir kernfs.Inode) error { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID) - return linuxerr.EINVAL - } - dstDirInode := dstDir.(*inode) in := linux.FUSERenameIn{ Newdir: primitive.Uint64(dstDirInode.nodeID), Oldname: linux.CString(oldname), Newname: linux.CString(newname), } - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_RENAME, &in) - res, err := i.fs.conn.Call(kernelTask, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_RENAME, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return err } @@ -487,13 +481,8 @@ func (i *inode) Rename(ctx context.Context, oldname, newname string, child, dstD // entry according to response. Shared by FUSE_MKNOD, FUSE_MKDIR, FUSE_SYMLINK, // FUSE_LINK and FUSE_LOOKUP. func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMode, opcode linux.FUSEOpcode, payload marshal.Marshallable) (kernfs.Inode, error) { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.nodeID) - return nil, linuxerr.EINVAL - } - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, opcode, payload) - res, err := i.fs.conn.Call(kernelTask, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, opcode, payload) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return nil, err } @@ -542,13 +531,8 @@ func (i *inode) Readlink(ctx context.Context, mnt *vfs.Mount) (string, error) { return "", linuxerr.EINVAL } if len(i.link) == 0 { - kernelTask := kernel.TaskFromContext(ctx) - if kernelTask == nil { - log.Warningf("fusefs.Inode.Readlink: couldn't get kernel task from context") - return "", linuxerr.EINVAL - } - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.nodeID, linux.FUSE_READLINK, &linux.FUSEEmptyIn{}) - res, err := i.fs.conn.Call(kernelTask, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_READLINK, &linux.FUSEEmptyIn{}) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return "", err } @@ -649,21 +633,14 @@ func (i *inode) getAttr(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOp // Currently we always send a request, // and we always set the metadata with the new result, // unless attributeVersion has changed. - - task := kernel.TaskFromContext(ctx) - if task == nil { - log.Warningf("couldn't get kernel task from context") - return linux.FUSEAttr{}, linuxerr.EINVAL - } - creds := auth.CredentialsFromContext(ctx) in := linux.FUSEGetAttrIn{ GetAttrFlags: flags, Fh: fh, } - req := i.fs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_GETATTR, &in) - res, err := i.fs.conn.Call(task, req) + req := i.fs.conn.NewRequest(creds, pidFromContext(ctx), i.nodeID, linux.FUSE_GETATTR, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return linux.FUSEAttr{}, err } @@ -719,16 +696,10 @@ func (i *inode) DecRef(ctx context.Context) { // StatFS implements kernfs.Inode.StatFS. func (i *inode) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, error) { - task := kernel.TaskFromContext(ctx) - if task == nil { - log.Warningf("couldn't get kernel task from context") - return linux.Statfs{}, linuxerr.EINVAL - } - - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(task.ThreadID()), i.nodeID, + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_STATFS, &linux.FUSEEmptyIn{}, ) - res, err := i.fs.conn.Call(task, req) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return linux.Statfs{}, err } @@ -795,12 +766,6 @@ type fhOptions struct { // +checklocks:i.attrMu func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions, fhOpts fhOptions) error { - task := kernel.TaskFromContext(ctx) - if task == nil { - log.Warningf("couldn't get kernel task from context") - return linuxerr.EINVAL - } - // We should retain the original file type when assigning a new mode. fattrMask := fattrMaskFromStats(opts.Stat.Mask) if fhOpts.useFh { @@ -826,8 +791,8 @@ func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre UID: opts.Stat.UID, GID: opts.Stat.GID, } - req := i.fs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_SETATTR, &in) - res, err := i.fs.conn.Call(task, req) + req := i.fs.conn.NewRequest(creds, pidFromContext(ctx), i.nodeID, linux.FUSE_SETATTR, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return err } diff --git a/pkg/sentry/fsimpl/fuse/read_write.go b/pkg/sentry/fsimpl/fuse/read_write.go index 9bd02ea28..389f675dc 100644 --- a/pkg/sentry/fsimpl/fuse/read_write.go +++ b/pkg/sentry/fsimpl/fuse/read_write.go @@ -21,8 +21,6 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/hostarch" - "gvisor.dev/gvisor/pkg/log" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/usermem" ) @@ -36,12 +34,6 @@ import ( func (fs *filesystem) ReadInPages(ctx context.Context, fd *regularFileFD, off uint64, size uint32) ([][]byte, uint32, error) { attributeVersion := fs.conn.attributeVersion.Load() - t := kernel.TaskFromContext(ctx) - if t == nil { - log.Warningf("fusefs.Read: couldn't get kernel task from context") - return nil, 0, linuxerr.EINVAL - } - // Round up to a multiple of page size. readSize, _ := hostarch.PageRoundUp(uint64(size)) @@ -80,9 +72,8 @@ func (fs *filesystem) ReadInPages(ctx context.Context, fd *regularFileFD, off ui in.Size = pagesCanRead << hostarch.PageShift // TODO(gvisor.dev/issue/3247): support async read. - - req := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(t.ThreadID()), fd.inode().nodeID, linux.FUSE_READ, &in) - res, err := fs.conn.Call(t, req) + req := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), fd.inode().nodeID, linux.FUSE_READ, &in) + res, err := fs.conn.Call(ctx, req) if err != nil { return nil, 0, err } @@ -148,12 +139,6 @@ func (fs *filesystem) ReadCallback(ctx context.Context, i *inode, off uint64, si // Write sends FUSE_WRITE requests and return the bytes written according to the // response. func (fs *filesystem) Write(ctx context.Context, fd *regularFileFD, offset int64, src usermem.IOSequence) (int64, int64, error) { - t := kernel.TaskFromContext(ctx) - if t == nil { - log.Warningf("fusefs.Write: couldn't get kernel task from context") - return 0, offset, linuxerr.EINVAL - } - // One request cannot exceed either maxWrite or maxPages. maxWrite := uint32(fs.conn.maxPages) << hostarch.PageShift if maxWrite > fs.conn.maxWrite { @@ -204,10 +189,9 @@ func (fs *filesystem) Write(ctx context.Context, fd *regularFileFD, offset int64 in.Header.Size = uint32(cp) in.Payload = data - req := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(t.ThreadID()), fd.inode().nodeID, linux.FUSE_WRITE, &in) - + req := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), fd.inode().nodeID, linux.FUSE_WRITE, &in) // TODO(gvisor.dev/issue/3247): support async write. - res, err := fs.conn.Call(t, req) + res, err := fs.conn.Call(ctx, req) if err != nil { return n, offset, err } diff --git a/pkg/sentry/fsimpl/fuse/regular_file.go b/pkg/sentry/fsimpl/fuse/regular_file.go index 90d409523..1f5090a94 100644 --- a/pkg/sentry/fsimpl/fuse/regular_file.go +++ b/pkg/sentry/fsimpl/fuse/regular_file.go @@ -23,9 +23,7 @@ import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/hostarch" - "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/fsutil" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/memmap" "gvisor.dev/gvisor/pkg/sentry/vfs" @@ -62,11 +60,6 @@ type regularFileFD struct { // Seek implements vfs.FileDescriptionImpl.Allocate. func (fd *regularFileFD) Allocate(ctx context.Context, mode, offset, length uint64) error { - task := kernel.TaskFromContext(ctx) - if task == nil { - log.Warningf("fusefs.regularFileFD.Allocate: couldn't get kernel task from context") - return linuxerr.EINVAL - } if mode & ^uint64(linux.FALLOC_FL_KEEP_SIZE|linux.FALLOC_FL_PUNCH_HOLE|linux.FALLOC_FL_ZERO_RANGE) != 0 { return linuxerr.EOPNOTSUPP } @@ -77,8 +70,8 @@ func (fd *regularFileFD) Allocate(ctx context.Context, mode, offset, length uint Mode: uint32(mode), } i := fd.inode() - req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(task.ThreadID()), i.nodeID, linux.FUSE_FALLOCATE, &in) - res, err := i.fs.conn.Call(task, req) + req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), pidFromContext(ctx), i.nodeID, linux.FUSE_FALLOCATE, &in) + res, err := i.fs.conn.Call(ctx, req) if err != nil { return err } diff --git a/pkg/sentry/fsimpl/fuse/request_response.go b/pkg/sentry/fsimpl/fuse/request_response.go index fc65a3edc..4c0be398d 100644 --- a/pkg/sentry/fsimpl/fuse/request_response.go +++ b/pkg/sentry/fsimpl/fuse/request_response.go @@ -17,11 +17,11 @@ package fuse import ( "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/marshal" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ) @@ -156,13 +156,13 @@ func newFutureResponse(req *Request) *futureResponse { // resolve blocks the task until the server responds to its corresponding request, // then returns a resolved response. -func (f *futureResponse) resolve(t *kernel.Task) (*Response, error) { +func (f *futureResponse) resolve(b context.Blocker) (*Response, error) { // Return directly for async requests. if f.async { return nil, nil } - if err := t.Block(f.ch); err != nil { + if err := b.Block(f.ch); err != nil { return nil, err } diff --git a/pkg/sentry/fsimpl/fuse/utils_test.go b/pkg/sentry/fsimpl/fuse/utils_test.go index 73c53e4bd..14f2422de 100644 --- a/pkg/sentry/fsimpl/fuse/utils_test.go +++ b/pkg/sentry/fsimpl/fuse/utils_test.go @@ -20,7 +20,6 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil" - "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" ) @@ -49,7 +48,7 @@ func setup(t *testing.T) *testutil.System { // newTestConnection creates a fuse connection that the sentry can communicate with // and the FD for the server to communicate with. -func newTestConnection(system *testutil.System, k *kernel.Kernel, maxActiveRequests uint64) (*connection, *vfs.FileDescription, error) { +func newTestConnection(system *testutil.System, maxActiveRequests uint64) (*connection, *vfs.FileDescription, error) { fuseDev := &DeviceFD{} vd := system.VFS.NewAnonVirtualDentry("fuse") diff --git a/pkg/sentry/kernel/task_block.go b/pkg/sentry/kernel/task_block.go index 7b2413140..04f5991cd 100644 --- a/pkg/sentry/kernel/task_block.go +++ b/pkg/sentry/kernel/task_block.go @@ -114,11 +114,7 @@ func (t *Task) BlockWithTimer(C <-chan struct{}, tchan <-chan struct{}) error { return t.block(C, tchan) } -// Block blocks t until an event is received from C or t is interrupted. It -// returns nil if an event is received from C and linuxerr.ErrInterrupted if t -// is interrupted. -// -// Preconditions: The caller must be running on the task goroutine. +// Block implements context.Context.Block func (t *Task) Block(C <-chan struct{}) error { return t.block(C, nil) } diff --git a/pkg/tcpip/BUILD b/pkg/tcpip/BUILD index 501d98c8c..74a848eab 100644 --- a/pkg/tcpip/BUILD +++ b/pkg/tcpip/BUILD @@ -70,6 +70,8 @@ deps_test( "//pkg/sync/locking", "//pkg/waiter", "//pkg/xdp", + "//pkg/abi/linux/errno", + "//pkg/errors", # Other deps. "@com_github_google_btree//:go_default_library",