gvisor/pkg/p9/handlers.go

706 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package p9
import (
"io"
"os"
"sync/atomic"
"syscall"
"gvisor.googlesource.com/gvisor/pkg/log"
)
// newErr returns a new error message from an error.
func newErr(err error) *Rlerror {
switch e := err.(type) {
case syscall.Errno:
return &Rlerror{Error: uint32(e)}
case *os.PathError:
return newErr(e.Err)
case *os.SyscallError:
return newErr(e.Err)
default:
log.Warningf("unknown error: %v", err)
return &Rlerror{Error: uint32(syscall.EIO)}
}
}
// handler is implemented for server-handled messages.
//
// See server.go for call information.
type handler interface {
// Handle handles the given message.
//
// This may modify the server state. The handle function must return a
// message which will be sent back to the client. It may be useful to
// use newErr to automatically extract an error message.
handle(cs *connState) message
}
// handle implements handler.handle.
func (t *Tversion) handle(cs *connState) message {
if t.MSize == 0 {
return newErr(syscall.EINVAL)
}
if t.MSize > maximumLength {
return newErr(syscall.EINVAL)
}
atomic.StoreUint32(&cs.messageSize, t.MSize)
requested, ok := parseVersion(t.Version)
if !ok {
return newErr(syscall.EINVAL)
}
// The server cannot support newer versions that it doesn't know about. In this
// case we return EAGAIN to tell the client to try again with a lower version.
if requested > highestSupportedVersion {
return newErr(syscall.EAGAIN)
}
// From Tversion(9P): "The server may respond with the clients version
// string, or a version string identifying an earlier defined protocol version".
atomic.StoreUint32(&cs.version, requested)
return &Rversion{
MSize: t.MSize,
Version: t.Version,
}
}
// handle implements handler.handle.
func (t *Tflush) handle(cs *connState) message {
cs.WaitTag(t.OldTag)
return &Rflush{}
}
// handle implements handler.handle.
func (t *Twalk) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Has it been opened already?
if _, opened := ref.OpenFlags(); opened {
return newErr(syscall.EBUSY)
}
// Do the walk.
qids, sf, err := ref.file.Walk(t.Names)
if err != nil {
return newErr(err)
}
// Install the new FID.
cs.InsertFID(t.NewFID, &fidRef{file: sf})
return &Rwalk{QIDs: qids}
}
// handle implements handler.handle.
func (t *Tclunk) handle(cs *connState) message {
if !cs.DeleteFID(t.FID) {
return newErr(syscall.EBADF)
}
return &Rclunk{}
}
// handle implements handler.handle.
func (t *Tremove) handle(cs *connState) message {
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// "The remove request asks the file server both to remove the file
// represented by fid and to clunk the fid, even if the remove fails."
//
// "It is correct to consider remove to be a clunk with the side effect
// of removing the file if permissions allow."
// https://swtch.com/plan9port/man/man9/remove.html
err := ref.file.Remove()
// Clunk the FID regardless of Remove error.
if !cs.DeleteFID(t.FID) {
return newErr(syscall.EBADF)
}
if err != nil {
return newErr(err)
}
return &Rremove{}
}
// handle implements handler.handle.
//
// We don't support authentication, so this just returns ENOSYS.
func (t *Tauth) handle(cs *connState) message {
return newErr(syscall.ENOSYS)
}
// handle implements handler.handle.
func (t *Tattach) handle(cs *connState) message {
// Ensure no authentication FID is provided.
if t.Auth.AuthenticationFID != NoFID {
return newErr(syscall.EINVAL)
}
// Do the attach.
sf, err := cs.server.attacher.Attach(t.Auth.AttachName)
if err != nil {
return newErr(err)
}
cs.InsertFID(t.FID, &fidRef{file: sf})
// Return an empty QID.
return &Rattach{}
}
// handle implements handler.handle.
func (t *Tlopen) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
ref.openedMu.Lock()
defer ref.openedMu.Unlock()
// Has it been opened already?
if ref.opened {
return newErr(syscall.EINVAL)
}
// Do the open.
osFile, qid, ioUnit, err := ref.file.Open(t.Flags)
if err != nil {
return newErr(err)
}
// Mark file as opened and set open mode.
ref.opened = true
ref.openFlags = t.Flags
return &Rlopen{QID: qid, IoUnit: ioUnit, File: osFile}
}
func (t *Tlcreate) do(cs *connState, uid UID) (*Rlcreate, error) {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return nil, syscall.EBADF
}
defer ref.DecRef()
// Do the create.
osFile, nsf, qid, ioUnit, err := ref.file.Create(t.Name, t.OpenFlags, t.Permissions, uid, t.GID)
if err != nil {
return nil, err
}
// Replace the FID reference.
//
// The new file will be opened already.
cs.InsertFID(t.FID, &fidRef{file: nsf, opened: true, openFlags: t.OpenFlags})
return &Rlcreate{Rlopen: Rlopen{QID: qid, IoUnit: ioUnit, File: osFile}}, nil
}
// handle implements handler.handle.
func (t *Tlcreate) handle(cs *connState) message {
rlcreate, err := t.do(cs, NoUID)
if err != nil {
return newErr(err)
}
return rlcreate
}
// handle implements handler.handle.
func (t *Tsymlink) handle(cs *connState) message {
rsymlink, err := t.do(cs, NoUID)
if err != nil {
return newErr(err)
}
return rsymlink
}
func (t *Tsymlink) do(cs *connState, uid UID) (*Rsymlink, error) {
// Lookup the FID.
ref, ok := cs.LookupFID(t.Directory)
if !ok {
return nil, syscall.EBADF
}
defer ref.DecRef()
// Do the symlink.
qid, err := ref.file.Symlink(t.Target, t.Name, uid, t.GID)
if err != nil {
return nil, err
}
return &Rsymlink{QID: qid}, nil
}
// handle implements handler.handle.
func (t *Tlink) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.Directory)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Lookup the other FID.
refTarget, ok := cs.LookupFID(t.Target)
if !ok {
return newErr(syscall.EBADF)
}
defer refTarget.DecRef()
// Do the link.
if err := ref.file.Link(refTarget.file, t.Name); err != nil {
return newErr(err)
}
return &Rlink{}
}
// handle implements handler.handle.
func (t *Trenameat) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.OldDirectory)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Lookup the other FID.
refTarget, ok := cs.LookupFID(t.NewDirectory)
if !ok {
return newErr(syscall.EBADF)
}
defer refTarget.DecRef()
// Do the rename.
if err := ref.file.RenameAt(t.OldName, refTarget.file, t.NewName); err != nil {
return newErr(err)
}
return &Rrenameat{}
}
// handle implements handler.handle.
func (t *Tunlinkat) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.Directory)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Do the unlink.
if err := ref.file.UnlinkAt(t.Name, t.Flags); err != nil {
return newErr(err)
}
return &Runlinkat{}
}
// handle implements handler.handle.
func (t *Trename) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Lookup the target.
refTarget, ok := cs.LookupFID(t.Directory)
if !ok {
return newErr(syscall.EBADF)
}
defer refTarget.DecRef()
// Call the rename method.
if err := ref.file.Rename(refTarget.file, t.Name); err != nil {
return newErr(err)
}
return &Rrename{}
}
// handle implements handler.handle.
func (t *Treadlink) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Do the read.
target, err := ref.file.Readlink()
if err != nil {
return newErr(err)
}
return &Rreadlink{target}
}
// handle implements handler.handle.
func (t *Tread) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Has it been opened already?
openFlags, opened := ref.OpenFlags()
if !opened {
return newErr(syscall.EINVAL)
}
// Can it be read? Check permissions.
if openFlags&OpenFlagsModeMask == WriteOnly {
return newErr(syscall.EPERM)
}
// Constrain the size of the read buffer.
if int(t.Count) > int(maximumLength) {
return newErr(syscall.ENOBUFS)
}
// Do the read.
data := make([]byte, t.Count)
n, err := ref.file.ReadAt(data, t.Offset)
if err != nil && err != io.EOF {
return newErr(err)
}
return &Rread{Data: data[:n]}
}
// handle implements handler.handle.
func (t *Twrite) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Has it been opened already?
openFlags, opened := ref.OpenFlags()
if !opened {
return newErr(syscall.EINVAL)
}
// Can it be write? Check permissions.
if openFlags&OpenFlagsModeMask == ReadOnly {
return newErr(syscall.EPERM)
}
// Do the write.
n, err := ref.file.WriteAt(t.Data, t.Offset)
if err != nil {
return newErr(err)
}
return &Rwrite{Count: uint32(n)}
}
// handle implements handler.handle.
func (t *Tmknod) handle(cs *connState) message {
rmknod, err := t.do(cs, NoUID)
if err != nil {
return newErr(err)
}
return rmknod
}
func (t *Tmknod) do(cs *connState, uid UID) (*Rmknod, error) {
// Lookup the FID.
ref, ok := cs.LookupFID(t.Directory)
if !ok {
return nil, syscall.EBADF
}
defer ref.DecRef()
// Do the mknod.
qid, err := ref.file.Mknod(t.Name, t.Permissions, t.Major, t.Minor, uid, t.GID)
if err != nil {
return nil, err
}
return &Rmknod{QID: qid}, nil
}
// handle implements handler.handle.
func (t *Tmkdir) handle(cs *connState) message {
rmkdir, err := t.do(cs, NoUID)
if err != nil {
return newErr(err)
}
return rmkdir
}
func (t *Tmkdir) do(cs *connState, uid UID) (*Rmkdir, error) {
// Lookup the FID.
ref, ok := cs.LookupFID(t.Directory)
if !ok {
return nil, syscall.EBADF
}
defer ref.DecRef()
// Do the mkdir.
qid, err := ref.file.Mkdir(t.Name, t.Permissions, uid, t.GID)
if err != nil {
return nil, err
}
return &Rmkdir{QID: qid}, nil
}
// handle implements handler.handle.
func (t *Tgetattr) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Get attributes.
qid, valid, attr, err := ref.file.GetAttr(t.AttrMask)
if err != nil {
return newErr(err)
}
return &Rgetattr{QID: qid, Valid: valid, Attr: attr}
}
// handle implements handler.handle.
func (t *Tsetattr) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Set attributes.
if err := ref.file.SetAttr(t.Valid, t.SetAttr); err != nil {
return newErr(err)
}
return &Rsetattr{}
}
// handle implements handler.handle.
func (t *Txattrwalk) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// We don't support extended attributes.
return newErr(syscall.ENODATA)
}
// handle implements handler.handle.
func (t *Txattrcreate) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// We don't support extended attributes.
return newErr(syscall.ENOSYS)
}
// handle implements handler.handle.
func (t *Treaddir) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.Directory)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Has it been opened already?
if _, opened := ref.OpenFlags(); !opened {
return newErr(syscall.EINVAL)
}
// Read the entries.
entries, err := ref.file.Readdir(t.Offset, t.Count)
if err != nil && err != io.EOF {
return newErr(err)
}
return &Rreaddir{Count: t.Count, Entries: entries}
}
// handle implements handler.handle.
func (t *Tfsync) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Has it been opened already?
if _, opened := ref.OpenFlags(); !opened {
return newErr(syscall.EINVAL)
}
err := ref.file.FSync()
if err != nil {
return newErr(err)
}
return &Rfsync{}
}
// handle implements handler.handle.
func (t *Tstatfs) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
st, err := ref.file.StatFS()
if err != nil {
return newErr(err)
}
return &Rstatfs{st}
}
// handle implements handler.handle.
func (t *Tflushf) handle(cs *connState) message {
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
if err := ref.file.Flush(); err != nil {
return newErr(err)
}
return &Rflushf{}
}
// handle implements handler.handle.
func (t *Twalkgetattr) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Has it been opened already?
if _, opened := ref.OpenFlags(); opened {
return newErr(syscall.EBUSY)
}
// Do the walk.
qids, sf, valid, attr, err := ref.file.WalkGetAttr(t.Names)
if err == syscall.ENOSYS {
qids, sf, err = ref.file.Walk(t.Names)
if err != nil {
return newErr(err)
}
_, valid, attr, err = sf.GetAttr(AttrMaskAll())
}
if err != nil {
return newErr(err)
}
// Install the new FID.
cs.InsertFID(t.NewFID, &fidRef{file: sf})
return &Rwalkgetattr{QIDs: qids, Valid: valid, Attr: attr}
}
// handle implements handler.handle.
func (t *Tucreate) handle(cs *connState) message {
rlcreate, err := t.Tlcreate.do(cs, t.UID)
if err != nil {
return newErr(err)
}
return &Rucreate{*rlcreate}
}
// handle implements handler.handle.
func (t *Tumkdir) handle(cs *connState) message {
rmkdir, err := t.Tmkdir.do(cs, t.UID)
if err != nil {
return newErr(err)
}
return &Rumkdir{*rmkdir}
}
// handle implements handler.handle.
func (t *Tusymlink) handle(cs *connState) message {
rsymlink, err := t.Tsymlink.do(cs, t.UID)
if err != nil {
return newErr(err)
}
return &Rusymlink{*rsymlink}
}
// handle implements handler.handle.
func (t *Tumknod) handle(cs *connState) message {
rmknod, err := t.Tmknod.do(cs, t.UID)
if err != nil {
return newErr(err)
}
return &Rumknod{*rmknod}
}
// handle implements handler.handle.
func (t *Tlconnect) handle(cs *connState) message {
// Lookup the FID.
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
// Do the connect.
osFile, err := ref.file.Connect(t.Flags)
if err != nil {
return newErr(err)
}
return &Rlconnect{File: osFile}
}