diff --git a/pkg/abi/linux/file.go b/pkg/abi/linux/file.go index 540991b57..899c0ce52 100644 --- a/pkg/abi/linux/file.go +++ b/pkg/abi/linux/file.go @@ -271,6 +271,12 @@ type Statx struct { DevMinor uint32 } +// String implements fmt.Stringer.String. +func (s *Statx) String() string { + return fmt.Sprintf("Statx{Mask: %d, Blksize: %d, Attributes: %d, Nlink: %d, UID: %d, GID: %d, Mode: %d, Ino: %d, Size: %d, Blocks: %d, AttributesMask: %d, Atime: %d, Btime: %d, Ctime: %d, Mtime: %d, RdevMajor: %d, RdevMinor: %d, DevMajor: %d, DevMinor: %d}", + s.Mask, s.Blksize, s.Attributes, s.Nlink, s.UID, s.GID, s.Mode, s.Ino, s.Size, s.Blocks, s.AttributesMask, s.Atime, s.Btime, s.Ctime, s.Mtime, s.RdevMajor, s.RdevMinor, s.DevMajor, s.DevMinor) +} + // SizeOfStatx is the size of a Statx struct. var SizeOfStatx = (*Statx)(nil).SizeBytes() diff --git a/pkg/lisafs/channel.go b/pkg/lisafs/channel.go index 301212e51..f7ae2b7b4 100644 --- a/pkg/lisafs/channel.go +++ b/pkg/lisafs/channel.go @@ -15,6 +15,7 @@ package lisafs import ( + "fmt" "math" "runtime" @@ -80,6 +81,11 @@ func (ch *channel) SndRcvMessage(m MID, payloadLen uint32, wantFDs uint8) (MID, return ch.rcvMsg(rcvDataLen) } +// String implements fmt.Stringer.String. +func (ch *channel) String() string { + return fmt.Sprintf("channel %p", ch) +} + func (ch *channel) shutdown() { ch.data.Shutdown() } diff --git a/pkg/lisafs/client.go b/pkg/lisafs/client.go index 92997b930..bb741bdee 100644 --- a/pkg/lisafs/client.go +++ b/pkg/lisafs/client.go @@ -97,8 +97,11 @@ func NewClient(sock *unet.Socket) (*Client, Inode, error) { // Mount RPC below. c.supported = make([]bool, Mount+1) c.supported[Mount] = true - var mountResp MountResp - if err := c.SndRcvMessage(Mount, 0, NoopMarshal, mountResp.CheckedUnmarshal, nil); err != nil { + var ( + mountReq MountReq + mountResp MountResp + ) + if err := c.SndRcvMessage(Mount, uint32(mountReq.SizeBytes()), mountReq.MarshalBytes, mountResp.CheckedUnmarshal, nil, mountResp.String, mountResp.String); err != nil { return nil, Inode{}, err } @@ -218,9 +221,12 @@ func (c *Client) Close() { } func (c *Client) createChannel() (*channel, error) { - var chanResp ChannelResp + var ( + chanReq ChannelReq + chanResp ChannelResp + ) var fds [2]int - if err := c.SndRcvMessage(Channel, 0, NoopMarshal, chanResp.CheckedUnmarshal, fds[:]); err != nil { + if err := c.SndRcvMessage(Channel, uint32(chanReq.SizeBytes()), chanReq.MarshalBytes, chanResp.CheckedUnmarshal, fds[:], chanReq.String, chanResp.String); err != nil { return nil, err } if fds[0] < 0 || fds[1] < 0 { @@ -277,8 +283,9 @@ func (c *Client) CloseFDBatched(ctx context.Context, fd FDID) { c.fdsMu.Unlock() req := CloseReq{FDs: toClose} + var resp CloseResp ctx.UninterruptibleSleepStart(false) - err := c.SndRcvMessage(Close, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := c.SndRcvMessage(Close, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) if err != nil { log.Warningf("lisafs: batch closing FDs returned error: %v", err) @@ -291,8 +298,9 @@ func (c *Client) SyncFDs(ctx context.Context, fds []FDID) error { return nil } req := FsyncReq{FDs: fds} + var resp FsyncResp ctx.UninterruptibleSleepStart(false) - err := c.SndRcvMessage(FSync, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := c.SndRcvMessage(FSync, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -302,15 +310,11 @@ func (c *Client) SyncFDs(ctx context.Context, fds []FDID) error { // and invokes respUnmarshal with the response payload. respFDs is populated // with the received FDs, extra fields are set to -1. // -// Note that the function arguments intentionally accept marshal.Marshallable -// functions like Marshal{Bytes/Unsafe} and Unmarshal{Bytes/Unsafe} instead of -// directly accepting the marshal.Marshallable interface. Even though just -// accepting marshal.Marshallable is cleaner, it leads to a heap allocation -// (even if that interface variable itself does not escape). In other words, -// implicit conversion to an interface leads to an allocation. +// See messages.go to understand why function arguments are used instead of +// combining these functions into an interface type. // -// Precondition: reqMarshal and respUnmarshal must be non-nil. -func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []byte) []byte, respUnmarshal func(src []byte) ([]byte, bool), respFDs []int) error { +// Precondition: function arguments must be non-nil. +func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal marshalFunc, respUnmarshal unmarshalFunc, respFDs []int, reqString debugStringer, respString debugStringer) error { if !c.IsSupported(m) { return unix.EOPNOTSUPP } @@ -328,6 +332,8 @@ func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []b comm := c.acquireCommunicator() defer c.releaseCommunicator(comm) + debugf("send", comm, reqString) + // Marshal the request into comm's payload buffer and make the RPC. reqMarshal(comm.PayloadBuf(payloadLen)) respM, respPayloadLen, err := comm.SndRcvMessage(m, payloadLen, uint8(wantFDs)) @@ -363,6 +369,7 @@ func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []b closeFDs(respFDs) var resp ErrorResp resp.UnmarshalUnsafe(comm.PayloadBuf(respPayloadLen)) + debugf("recv", comm, resp.String) return unix.Errno(resp.errno) } if respM != m { @@ -376,9 +383,18 @@ func (c *Client) SndRcvMessage(m MID, payloadLen uint32, reqMarshal func(dst []b log.Warningf("server response unmarshalling for %d message failed", respM) return unix.EIO } + debugf("recv", comm, respString) return nil } +func debugf(action string, comm Communicator, debugMsg debugStringer) { + // Replicate the log.IsLogging(log.Debug) check to avoid having to call + // debugMsg() on the hot path. + if log.IsLogging(log.Debug) { + log.Debugf("%s [%s] %s", action, comm, debugMsg()) + } +} + // Postcondition: releaseCommunicator() must be called on the returned value. func (c *Client) acquireCommunicator() Communicator { // Prefer using channel over socket because: diff --git a/pkg/lisafs/client_file.go b/pkg/lisafs/client_file.go index 6ec2a657a..76ea68576 100644 --- a/pkg/lisafs/client_file.go +++ b/pkg/lisafs/client_file.go @@ -66,9 +66,10 @@ func (f *ClientFD) CloseBatched(ctx context.Context) { func (f *ClientFD) Close(ctx context.Context) error { fdArr := [1]FDID{f.fd} req := CloseReq{FDs: fdArr[:]} + var resp CloseResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(Close, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(Close, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -82,7 +83,7 @@ func (f *ClientFD) OpenAt(ctx context.Context, flags uint32) (FDID, int, error) var respFD [1]int var resp OpenAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(OpenAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, respFD[:]) + err := f.client.SndRcvMessage(OpenAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, respFD[:], req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.OpenFD, respFD[0], err } @@ -100,7 +101,7 @@ func (f *ClientFD) OpenCreateAt(ctx context.Context, name string, flags uint32, var respFD [1]int var resp OpenCreateAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(OpenCreateAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, respFD[:]) + err := f.client.SndRcvMessage(OpenCreateAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, respFD[:], req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Child, resp.NewFD, respFD[0], err } @@ -109,7 +110,7 @@ func (f *ClientFD) OpenCreateAt(ctx context.Context, name string, flags uint32, func (f *ClientFD) StatTo(ctx context.Context, stat *linux.Statx) error { req := StatReq{FD: f.fd} ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FStat, uint32(req.SizeBytes()), req.MarshalUnsafe, stat.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(FStat, uint32(req.SizeBytes()), req.MarshalUnsafe, stat.CheckedUnmarshal, nil, req.String, stat.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -117,8 +118,9 @@ func (f *ClientFD) StatTo(ctx context.Context, stat *linux.Statx) error { // Sync makes the Fsync RPC. func (f *ClientFD) Sync(ctx context.Context) error { req := FsyncReq{FDs: []FDID{f.fd}} + var resp FsyncResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FSync, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(FSync, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -181,7 +183,7 @@ func (f *ClientFD) Read(ctx context.Context, dst []byte, offset uint64) (uint64, // PReadResp.CheckedUnmarshal expects this to be set. resp.Buf = buf ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(PRead, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(PRead, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return uint64(resp.NumBytes), err }) @@ -205,7 +207,7 @@ func (f *ClientFD) Write(ctx context.Context, src []byte, offset uint64) (uint64 var resp PWriteResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(PWrite, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(PWrite, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Count, err }) @@ -222,7 +224,7 @@ func (f *ClientFD) MkdirAt(ctx context.Context, name string, mode linux.FileMode var resp MkdirAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(MkdirAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(MkdirAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.ChildDir, err } @@ -239,7 +241,7 @@ func (f *ClientFD) SymlinkAt(ctx context.Context, name, target string, uid UID, var resp SymlinkAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(SymlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(SymlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Symlink, err } @@ -254,7 +256,7 @@ func (f *ClientFD) LinkAt(ctx context.Context, targetFD FDID, name string) (Inod var resp LinkAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(LinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(LinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Link, err } @@ -272,7 +274,7 @@ func (f *ClientFD) MknodAt(ctx context.Context, name string, mode linux.FileMode var resp MknodAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(MknodAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(MknodAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Child, err } @@ -298,7 +300,7 @@ func (f *ClientFD) SetStat(ctx context.Context, stat *linux.Statx) (uint32, erro var resp SetStatResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(SetStat, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(SetStat, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.FailureMask, unix.Errno(resp.FailureErrNo), err } @@ -312,7 +314,7 @@ func (f *ClientFD) WalkMultiple(ctx context.Context, names []string) (WalkStatus var resp WalkResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Status, resp.Inodes, err } @@ -327,7 +329,7 @@ func (f *ClientFD) Walk(ctx context.Context, name string) (Inode, error) { var inode [1]Inode resp := WalkResp{Inodes: inode[:]} ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(Walk, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) if err != nil { return Inode{}, err @@ -363,7 +365,7 @@ func (f *ClientFD) WalkStat(ctx context.Context, names []string) ([]linux.Statx, var resp WalkStatResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(WalkStat, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(WalkStat, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Stats, err } @@ -372,7 +374,7 @@ func (f *ClientFD) WalkStat(ctx context.Context, names []string) ([]linux.Statx, func (f *ClientFD) StatFSTo(ctx context.Context, statFS *StatFS) error { req := FStatFSReq{FD: f.fd} ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FStatFS, uint32(req.SizeBytes()), req.MarshalUnsafe, statFS.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(FStatFS, uint32(req.SizeBytes()), req.MarshalUnsafe, statFS.CheckedUnmarshal, nil, req.String, statFS.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -385,8 +387,9 @@ func (f *ClientFD) Allocate(ctx context.Context, mode, offset, length uint64) er Offset: offset, Length: length, } + var resp FAllocateResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FAllocate, uint32(req.SizeBytes()), req.MarshalUnsafe, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(FAllocate, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -396,7 +399,7 @@ func (f *ClientFD) ReadLinkAt(ctx context.Context) (string, error) { req := ReadLinkAtReq{FD: f.fd} var resp ReadLinkAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(ReadLinkAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(ReadLinkAt, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return string(resp.Target), err } @@ -408,8 +411,9 @@ func (f *ClientFD) Flush(ctx context.Context) error { return nil } req := FlushReq{FD: f.fd} + var resp FlushResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(Flush, uint32(req.SizeBytes()), req.MarshalUnsafe, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(Flush, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -417,9 +421,10 @@ func (f *ClientFD) Flush(ctx context.Context) error { // Connect makes the Connect RPC. func (f *ClientFD) Connect(ctx context.Context, sockType linux.SockType) (int, error) { req := ConnectReq{FD: f.fd, SockType: uint32(sockType)} + var resp ConnectResp var sockFD [1]int ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(Connect, uint32(req.SizeBytes()), req.MarshalUnsafe, NoopUnmarshal, sockFD[:]) + err := f.client.SndRcvMessage(Connect, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, sockFD[:], req.String, resp.String) ctx.UninterruptibleSleepFinish(false) if err == nil && sockFD[0] < 0 { err = unix.EBADF @@ -434,9 +439,9 @@ func (f *ClientFD) UnlinkAt(ctx context.Context, name string, flags uint32) erro Name: SizedString(name), Flags: primitive.Uint32(flags), } - + var resp UnlinkAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(UnlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(UnlinkAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -450,9 +455,9 @@ func (f *ClientFD) RenameAt(ctx context.Context, oldName string, newDirFD FDID, NewDir: newDirFD, NewName: SizedString(newName), } - + var resp RenameAtResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(RenameAt, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(RenameAt, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -466,7 +471,7 @@ func (f *ClientFD) Getdents64(ctx context.Context, count int32) ([]Dirent64, err var resp Getdents64Resp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(Getdents64, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(Getdents64, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Dirents, err } @@ -480,7 +485,7 @@ func (f *ClientFD) ListXattr(ctx context.Context, size uint64) ([]string, error) var resp FListXattrResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FListXattr, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(FListXattr, uint32(req.SizeBytes()), req.MarshalUnsafe, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return resp.Xattrs, err } @@ -495,7 +500,7 @@ func (f *ClientFD) GetXattr(ctx context.Context, name string, size uint64) (stri var resp FGetXattrResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FGetXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil) + err := f.client.SndRcvMessage(FGetXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return string(resp.Value), err } @@ -508,9 +513,9 @@ func (f *ClientFD) SetXattr(ctx context.Context, name string, value string, flag Value: SizedString(value), Flags: primitive.Uint32(flags), } - + var resp FSetXattrResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FSetXattr, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(FSetXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } @@ -521,9 +526,9 @@ func (f *ClientFD) RemoveXattr(ctx context.Context, name string) error { FD: f.fd, Name: SizedString(name), } - + var resp FRemoveXattrResp ctx.UninterruptibleSleepStart(false) - err := f.client.SndRcvMessage(FRemoveXattr, uint32(req.SizeBytes()), req.MarshalBytes, NoopUnmarshal, nil) + err := f.client.SndRcvMessage(FRemoveXattr, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String) ctx.UninterruptibleSleepFinish(false) return err } diff --git a/pkg/lisafs/communicator.go b/pkg/lisafs/communicator.go index ec2035158..0946ec8bb 100644 --- a/pkg/lisafs/communicator.go +++ b/pkg/lisafs/communicator.go @@ -14,11 +14,17 @@ package lisafs -import "golang.org/x/sys/unix" +import ( + "fmt" + + "golang.org/x/sys/unix" +) // Communicator is a server side utility which represents exactly how the // server is communicating with the client. type Communicator interface { + fmt.Stringer + // PayloadBuf returns a slice to the payload section of its internal buffer // where the message can be marshalled. The handlers should use this to // populate the payload buffer with the message. diff --git a/pkg/lisafs/connection_test.go b/pkg/lisafs/connection_test.go index 616f40ef2..b15413702 100644 --- a/pkg/lisafs/connection_test.go +++ b/pkg/lisafs/connection_test.go @@ -118,8 +118,9 @@ func TestStartUp(t *testing.T) { func TestUnsupportedMessage(t *testing.T) { unsupportedM := lisafs.MID(len(handlers)) + var em lisafs.EmptyMessage runServerClient(t, func(c *lisafs.Client) { - if err := c.SndRcvMessage(unsupportedM, 0, lisafs.NoopMarshal, lisafs.NoopUnmarshal, nil); err != unix.EOPNOTSUPP { + if err := c.SndRcvMessage(unsupportedM, uint32(em.SizeBytes()), em.MarshalBytes, em.CheckedUnmarshal, nil, em.String, em.String); err != unix.EOPNOTSUPP { t.Errorf("expected EOPNOTSUPP but got err: %v", err) } }) @@ -154,7 +155,7 @@ func TestStress(t *testing.T) { req.Randomize(100) var resp lisafs.MsgDynamic - if err := c.SndRcvMessage(dynamicMsgID, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil); err != nil { + if err := c.SndRcvMessage(dynamicMsgID, uint32(req.SizeBytes()), req.MarshalBytes, resp.CheckedUnmarshal, nil, req.String, resp.String); err != nil { t.Errorf("SndRcvMessage: received unexpected error %v", err) return } @@ -198,7 +199,7 @@ func BenchmarkSendRecv(b *testing.B) { var recvV lisafs.P9Version runServerClient(b, func(c *lisafs.Client) { for i := 0; i < b.N; i++ { - if err := c.SndRcvMessage(versionMsgID, uint32(sendV.SizeBytes()), sendV.MarshalBytes, recvV.CheckedUnmarshal, nil); err != nil { + if err := c.SndRcvMessage(versionMsgID, uint32(sendV.SizeBytes()), sendV.MarshalBytes, recvV.CheckedUnmarshal, nil, sendV.String, recvV.String); err != nil { b.Fatalf("unexpected error occurred: %v", err) } } diff --git a/pkg/lisafs/message.go b/pkg/lisafs/message.go index d0370647c..a04f84a05 100644 --- a/pkg/lisafs/message.go +++ b/pkg/lisafs/message.go @@ -15,8 +15,10 @@ package lisafs import ( + "fmt" "math" "os" + "strings" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/hostarch" @@ -26,8 +28,35 @@ import ( // Messages have two parts: // * A transport header used to decipher received messages. // * A byte array referred to as "payload" which contains the actual message. -// // "dataLen" refers to the size of both combined. +// +// All messages must implement the following functions: +// * marshal.Marshallable.SizeBytes +// * marshal.Marshallable.Marshal{Unsafe/Bytes} +// * marshal.CheckedMarshallable.CheckedUnmarshal +// * fmt.Stringer.String +// +// There is no explicit interface definition for this because that definition +// will not be used anywhere. If a concrete type is passed into a function +// which receives it as an interface, the struct is moved to the heap. This +// erodes memory performance. Message structs are be short lived - they are +// initialized, marshalled into a buffer and not used after that. So heap +// allocating these message structs is wasteful. Don't define Message interface +// so it's not used. Instead use function arguments. See Client.SndRcvMessage. +// +// Unmarshalling code should use the Checked variant of the Unmarshal functions +// because a malicious encoder could have manipulated payload bytes to make the +// unchecked unmarshal variants panic due to the lack of bound checking. +// Marshalling code does not need additional bound checking because the caller +// itself intializes the struct being marshalled, so it is trusted. +// +// String() implementations must ensure that the message struct doesn't escape. +// For instance, directly passing the struct to fmt.Sprintf() escapes it +// because of the implicit conversion to interface{}. + +type marshalFunc func([]byte) []byte +type unmarshalFunc func([]byte) ([]byte, bool) +type debugStringer func() string // MID (message ID) is used to identify messages to parse from payload. // @@ -174,11 +203,24 @@ func (gid GID) Ok() bool { return gid != NoGID } -// NoopMarshal is a noop implementation of marshal.Marshallable.MarshalBytes. -func NoopMarshal(b []byte) []byte { return b } +// EmptyMessage is an empty message. +type EmptyMessage struct{} -// NoopUnmarshal is a noop implementation of marshal.Marshallable.UnmarshalBytes. -func NoopUnmarshal(b []byte) ([]byte, bool) { return b, true } +// String implements fmt.Stringer.String. +func (*EmptyMessage) String() string { + return "EmptyMessage{}" +} + +// SizeBytes implements marshal.Marshallable.SizeBytes. +func (*EmptyMessage) SizeBytes() int { + return 0 +} + +// MarshalBytes implements marshal.Marshallable.MarshalBytes. +func (*EmptyMessage) MarshalBytes(dst []byte) []byte { return dst } + +// CheckedUnmarshal implements marshal.CheckedMarshallable.CheckedUnmarshal. +func (*EmptyMessage) CheckedUnmarshal(src []byte) ([]byte, bool) { return src, true } // SizedString represents a string in memory. The marshalled string bytes are // preceded by a uint16 signifying the string length. @@ -213,6 +255,19 @@ func (s *SizedString) CheckedUnmarshal(src []byte) ([]byte, bool) { // array data is preceded by a uint16 signifying the array length. type StringArray []string +// String implements fmt.Stringer.String. This ensures that the string slice is +// not escaped so that callers that use a statically sized string array do not +// incur an unnecessary allocation. +func (s *StringArray) String() string { + var b strings.Builder + b.WriteString("[") + for _, str := range *s { + b.WriteString(fmt.Sprintf("%s, ", str)) + } + b.WriteString("]") + return b.String() +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (s *StringArray) SizeBytes() int { size := (*primitive.Uint16)(nil).SizeBytes() @@ -268,6 +323,14 @@ type Inode struct { Stat linux.Statx } +// MountReq is an empty requent to Mount on the connection. +type MountReq struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*MountReq) String() string { + return "MountReq{}" +} + // MountResp represents a Mount response. type MountResp struct { Root Inode @@ -278,6 +341,11 @@ type MountResp struct { SupportedMs []MID } +// String implements fmt.Stringer.String. +func (m *MountResp) String() string { + return fmt.Sprintf("MountResp{Root: %+v, MaxMessageSize: %d, SupportedMs: %+v}", m.Root, m.MaxMessageSize, m.SupportedMs) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (m *MountResp) SizeBytes() int { return m.Root.SizeBytes() + @@ -316,6 +384,14 @@ func (m *MountResp) CheckedUnmarshal(src []byte) ([]byte, bool) { return UnmarshalUnsafeMIDSlice(m.SupportedMs, srcRemain), true } +// ChannelReq is an empty requent to create a Channel. +type ChannelReq struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*ChannelReq) String() string { + return "ChannelReq{}" +} + // ChannelResp is the response to the create channel request. // // +marshal boundCheck @@ -324,6 +400,11 @@ type ChannelResp struct { dataLength uint64 } +// String implements fmt.Stringer.String. +func (c *ChannelResp) String() string { + return fmt.Sprintf("ChannelResp{dataOffset: %d, dataLength: %d}", c.dataOffset, c.dataLength) +} + // ErrorResp is returned to represent an error while handling a request. // // +marshal @@ -331,6 +412,11 @@ type ErrorResp struct { errno uint32 } +// String implements fmt.Stringer.String. +func (e *ErrorResp) String() string { + return fmt.Sprintf("ErrorResp{errno: %d}", e.errno) +} + // StatReq requests the stat results for the specified FD. // // +marshal boundCheck @@ -338,6 +424,11 @@ type StatReq struct { FD FDID } +// String implements fmt.Stringer.String. +func (s *StatReq) String() string { + return fmt.Sprintf("StatReq{FD: %d}", s.FD) +} + // SetStatReq is used to set attributeds on FDs. // // +marshal boundCheck @@ -353,6 +444,12 @@ type SetStatReq struct { Mtime linux.Timespec } +// String implements fmt.Stringer.String. +func (s *SetStatReq) String() string { + return fmt.Sprintf("SetStatReq{FD: %d, Mask: %d, Mode: %d, UID: %d, GID: %d, Size: %d, Atime: %+v, Mtime: %+v}", + s.FD, s.Mask, s.Mode, s.UID, s.GID, s.Size, s.Atime, s.Mtime) +} + // SetStatResp is used to communicate SetStat results. It contains a mask // representing the failed changes. It also contains the errno of the failed // set attribute operation. If multiple operations failed then any of those @@ -364,6 +461,11 @@ type SetStatResp struct { FailureErrNo uint32 } +// String implements fmt.Stringer.String. +func (s *SetStatResp) String() string { + return fmt.Sprintf("SetStatResp{FailureMask: %d, FailureErrNo: %d}", s.FailureMask, s.FailureErrNo) +} + // WalkReq is used to request to walk multiple path components at once. This // is used for both Walk and WalkStat. type WalkReq struct { @@ -371,6 +473,11 @@ type WalkReq struct { Path StringArray } +// String implements fmt.Stringer.String. +func (w *WalkReq) String() string { + return fmt.Sprintf("WalkReq{DirFD: %d, Path: %s}", w.DirFD, w.Path.String()) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (w *WalkReq) SizeBytes() int { return w.DirFD.SizeBytes() + w.Path.SizeBytes() @@ -423,6 +530,19 @@ type WalkResp struct { Inodes []Inode } +// String implements fmt.Stringer.String. This ensures that the Inode slice is +// not escaped so that callers that use a statically sized Inode array do not +// incur an unnecessary allocation. +func (w *WalkResp) String() string { + var arrB strings.Builder + arrB.WriteString("[") + for i := range w.Inodes { + arrB.WriteString(fmt.Sprintf("%+v, ", w.Inodes[i])) + } + arrB.WriteString("]") + return fmt.Sprintf("WalkResp{Status: %d, Inodes: %s}", w.Status, arrB.String()) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (w *WalkResp) SizeBytes() int { return w.Status.SizeBytes() + @@ -466,6 +586,11 @@ type WalkStatResp struct { Stats []linux.Statx } +// String implements fmt.Stringer.String. +func (w *WalkStatResp) String() string { + return fmt.Sprintf("WalkStatResp{Stats: %+v}", w.Stats) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (w *WalkStatResp) SizeBytes() int { return (*primitive.Uint16)(nil).SizeBytes() + (len(w.Stats) * linux.SizeOfStatx) @@ -507,6 +632,11 @@ type OpenAtReq struct { Flags uint32 } +// String implements fmt.Stringer.String. +func (o *OpenAtReq) String() string { + return fmt.Sprintf("OpenAtReq{FD: %d, Flags: %d}", o.FD, o.Flags) +} + // OpenAtResp is used to communicate the newly created FD. // // +marshal boundCheck @@ -514,6 +644,11 @@ type OpenAtResp struct { OpenFD FDID } +// String implements fmt.Stringer.String. +func (o *OpenAtResp) String() string { + return fmt.Sprintf("OpenAtResp{OpenFD: %d}", o.OpenFD) +} + // +marshal type createCommon struct { DirFD FDID @@ -530,6 +665,11 @@ type OpenCreateAtReq struct { Name SizedString } +// String implements fmt.Stringer.String. +func (o *OpenCreateAtReq) String() string { + return fmt.Sprintf("OpenCreateAtReq{DirFD: %d, Mode: %s, UID: %d, GID: %d, Flags: %d, Name: %s}", o.DirFD, o.Mode, o.UID, o.GID, o.Flags, o.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (o *OpenCreateAtReq) SizeBytes() int { return o.createCommon.SizeBytes() + o.Flags.SizeBytes() + o.Name.SizeBytes() @@ -565,11 +705,29 @@ type OpenCreateAtResp struct { _ uint32 // Need to make struct packed. } +// String implements fmt.Stringer.String. +func (o *OpenCreateAtResp) String() string { + return fmt.Sprintf("OpenCreateAtResp{Child: %+v, NewFD: %d}", o.Child, o.NewFD) +} + // FdArray is a utility struct which implements a marshallable type for // communicating an array of FDIDs. In memory, the array data is preceded by a // uint16 denoting the array length. type FdArray []FDID +// String implements fmt.Stringer.String. This ensures that the FDID slice is +// not escaped so that callers that use a statically sized FDID array do not +// incur an unnecessary allocation. +func (f *FdArray) String() string { + var b strings.Builder + b.WriteString("[") + for _, fd := range *f { + b.WriteString(fmt.Sprintf("%d, ", fd)) + } + b.WriteString("]") + return b.String() +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (f *FdArray) SizeBytes() int { return (*primitive.Uint16)(nil).SizeBytes() + (len(*f) * (*FDID)(nil).SizeBytes()) @@ -606,6 +764,11 @@ type CloseReq struct { FDs FdArray } +// String implements fmt.Stringer.String. +func (c *CloseReq) String() string { + return fmt.Sprintf("CloseReq{FDs: %s}", c.FDs.String()) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (c *CloseReq) SizeBytes() int { return c.FDs.SizeBytes() @@ -621,11 +784,24 @@ func (c *CloseReq) CheckedUnmarshal(src []byte) ([]byte, bool) { return c.FDs.CheckedUnmarshal(src) } +// CloseResp is an empty response to CloseReq. +type CloseResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*CloseResp) String() string { + return "CloseResp{}" +} + // FsyncReq is used to fsync(2) FDs. type FsyncReq struct { FDs FdArray } +// String implements fmt.Stringer.String. +func (f *FsyncReq) String() string { + return fmt.Sprintf("FsyncReq{FDs: %s}", f.FDs.String()) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (f *FsyncReq) SizeBytes() int { return f.FDs.SizeBytes() @@ -641,6 +817,14 @@ func (f *FsyncReq) CheckedUnmarshal(src []byte) ([]byte, bool) { return f.FDs.CheckedUnmarshal(src) } +// FsyncResp is an empty response to FsyncReq. +type FsyncResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*FsyncResp) String() string { + return "FsyncResp{}" +} + // PReadReq is used to pread(2) on an FD. // // +marshal boundCheck @@ -650,12 +834,22 @@ type PReadReq struct { Count uint32 } +// String implements fmt.Stringer.String. +func (r *PReadReq) String() string { + return fmt.Sprintf("PReadReq{Offset: %d, FD: %d, Count: %d}", r.Offset, r.FD, r.Count) +} + // PReadResp is used to return the result of pread(2). type PReadResp struct { NumBytes primitive.Uint64 Buf []byte } +// String implements fmt.Stringer.String. +func (r *PReadResp) String() string { + return fmt.Sprintf("PReadResp{NumBytes: %d, Buf: %v}", r.NumBytes, r.Buf) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (r *PReadResp) SizeBytes() int { return r.NumBytes.SizeBytes() + int(r.NumBytes) @@ -687,6 +881,11 @@ type PWriteReq struct { Buf []byte } +// String implements fmt.Stringer.String. +func (w *PWriteReq) String() string { + return fmt.Sprintf("PWriteReq{Offset: %d, FD: %d, NumBytes: %d, Buf: %v}", w.Offset, w.FD, w.NumBytes, w.Buf) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (w *PWriteReq) SizeBytes() int { return w.Offset.SizeBytes() + w.FD.SizeBytes() + w.NumBytes.SizeBytes() + int(w.NumBytes) @@ -726,12 +925,22 @@ type PWriteResp struct { Count uint64 } +// String implements fmt.Stringer.String. +func (w *PWriteResp) String() string { + return fmt.Sprintf("PWriteResp{Count: %d}", w.Count) +} + // MkdirAtReq is used to make MkdirAt requests. type MkdirAtReq struct { createCommon Name SizedString } +// String implements fmt.Stringer.String. +func (m *MkdirAtReq) String() string { + return fmt.Sprintf("MkdirAtReq{DirFD: %d, Mode: %s, UID: %d, GID: %d, Name: %s}", m.DirFD, m.Mode, m.UID, m.GID, m.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (m *MkdirAtReq) SizeBytes() int { return m.createCommon.SizeBytes() + m.Name.SizeBytes() @@ -763,6 +972,11 @@ type MkdirAtResp struct { ChildDir Inode } +// String implements fmt.Stringer.String. +func (m *MkdirAtResp) String() string { + return fmt.Sprintf("MkdirAtResp{ChildDir: %+v}", m.ChildDir) +} + // MknodAtReq is used to make MknodAt requests. type MknodAtReq struct { createCommon @@ -771,6 +985,11 @@ type MknodAtReq struct { Name SizedString } +// String implements fmt.Stringer.String. +func (m *MknodAtReq) String() string { + return fmt.Sprintf("MknodAtReq{DirFD: %d, Mode: %s, UID: %d, GID: %d, Minor: %d, Major: %d, Name: %s}", m.DirFD, m.Mode, m.UID, m.GID, m.Minor, m.Major, m.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (m *MknodAtReq) SizeBytes() int { return m.createCommon.SizeBytes() + m.Minor.SizeBytes() + m.Major.SizeBytes() + m.Name.SizeBytes() @@ -806,6 +1025,11 @@ type MknodAtResp struct { Child Inode } +// String implements fmt.Stringer.String. +func (m *MknodAtResp) String() string { + return fmt.Sprintf("MknodAtResp{Child: %+v}", m.Child) +} + // SymlinkAtReq is used to make SymlinkAt request. type SymlinkAtReq struct { DirFD FDID @@ -815,6 +1039,11 @@ type SymlinkAtReq struct { Target SizedString } +// String implements fmt.Stringer.String. +func (s *SymlinkAtReq) String() string { + return fmt.Sprintf("SymlinkAtReq{DirFD: %d, UID: %d, GID: %d, Name: %s, Target: %s}", s.DirFD, s.UID, s.GID, s.Name, s.Target) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (s *SymlinkAtReq) SizeBytes() int { return s.DirFD.SizeBytes() + s.UID.SizeBytes() + s.GID.SizeBytes() + s.Name.SizeBytes() + s.Target.SizeBytes() @@ -856,6 +1085,11 @@ type SymlinkAtResp struct { Symlink Inode } +// String implements fmt.Stringer.String. +func (s *SymlinkAtResp) String() string { + return fmt.Sprintf("SymlinkAtResp{Symlink: %+v}", s.Symlink) +} + // LinkAtReq is used to make LinkAt requests. type LinkAtReq struct { DirFD FDID @@ -863,6 +1097,11 @@ type LinkAtReq struct { Name SizedString } +// String implements fmt.Stringer.String. +func (l *LinkAtReq) String() string { + return fmt.Sprintf("LinkAtReq{DirFD: %d, Target: %d, Name: %s}", l.DirFD, l.Target, l.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (l *LinkAtReq) SizeBytes() int { return l.DirFD.SizeBytes() + l.Target.SizeBytes() + l.Name.SizeBytes() @@ -896,6 +1135,11 @@ type LinkAtResp struct { Link Inode } +// String implements fmt.Stringer.String. +func (l *LinkAtResp) String() string { + return fmt.Sprintf("LinkAtResp{Link: %+v}", l.Link) +} + // FStatFSReq is used to request StatFS results for the specified FD. // // +marshal boundCheck @@ -903,6 +1147,11 @@ type FStatFSReq struct { FD FDID } +// String implements fmt.Stringer.String. +func (s *FStatFSReq) String() string { + return fmt.Sprintf("FStatFSReq{FD: %d}", s.FD) +} + // StatFS is responded to a successful FStatFS request. // // +marshal boundCheck @@ -917,6 +1166,12 @@ type StatFS struct { NameLength uint64 } +// String implements fmt.Stringer.String. +func (s *StatFS) String() string { + return fmt.Sprintf("StatFS{Type: %d, BlockSize: %d, Blocks: %d, BlocksFree: %d, BlocksAvailable: %d, Files: %d, FilesFree: %d, NameLength: %d}", + s.Type, s.BlockSize, s.Blocks, s.BlocksFree, s.BlocksAvailable, s.Files, s.FilesFree, s.NameLength) +} + // FAllocateReq is used to request to fallocate(2) an FD. This has no response. // // +marshal boundCheck @@ -928,6 +1183,19 @@ type FAllocateReq struct { Length uint64 } +// String implements fmt.Stringer.String. +func (a *FAllocateReq) String() string { + return fmt.Sprintf("FAllocateReq{FD: %d, Mode: %d, Offset: %d, Length: %d}", a.FD, a.Mode, a.Offset, a.Length) +} + +// FAllocateResp is an empty response to FAllocateReq. +type FAllocateResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*FAllocateResp) String() string { + return "FAllocateResp{}" +} + // ReadLinkAtReq is used to readlinkat(2) at the specified FD. // // +marshal boundCheck @@ -935,11 +1203,21 @@ type ReadLinkAtReq struct { FD FDID } +// String implements fmt.Stringer.String. +func (r *ReadLinkAtReq) String() string { + return fmt.Sprintf("ReadLinkAtReq{FD: %d}", r.FD) +} + // ReadLinkAtResp is used to communicate ReadLinkAt results. type ReadLinkAtResp struct { Target SizedString } +// String implements fmt.Stringer.String. +func (r *ReadLinkAtResp) String() string { + return fmt.Sprintf("ReadLinkAtResp{Target: %s}", r.Target) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (r *ReadLinkAtResp) SizeBytes() int { return r.Target.SizeBytes() @@ -962,6 +1240,19 @@ type FlushReq struct { FD FDID } +// String implements fmt.Stringer.String. +func (f *FlushReq) String() string { + return fmt.Sprintf("FlushReq{FD: %d}", f.FD) +} + +// FlushResp is an empty response to FlushReq. +type FlushResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*FlushResp) String() string { + return "FlushResp{}" +} + // ConnectReq is used to make a Connect request. // // +marshal boundCheck @@ -973,6 +1264,19 @@ type ConnectReq struct { SockType uint32 } +// String implements fmt.Stringer.String. +func (c *ConnectReq) String() string { + return fmt.Sprintf("ConnectReq{FD: %d, SockType: %d}", c.FD, c.SockType) +} + +// ConnectResp is an empty response to ConnectReq. +type ConnectResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*ConnectResp) String() string { + return "ConnectResp{}" +} + // UnlinkAtReq is used to make UnlinkAt request. type UnlinkAtReq struct { DirFD FDID @@ -980,6 +1284,11 @@ type UnlinkAtReq struct { Name SizedString } +// String implements fmt.Stringer.String. +func (u *UnlinkAtReq) String() string { + return fmt.Sprintf("UnlinkAtReq{DirFD: %d, Flags: %d, Name: %s}", u.DirFD, u.Flags, u.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (u *UnlinkAtReq) SizeBytes() int { return u.DirFD.SizeBytes() + u.Flags.SizeBytes() + u.Name.SizeBytes() @@ -1006,6 +1315,14 @@ func (u *UnlinkAtReq) CheckedUnmarshal(src []byte) ([]byte, bool) { return src, false } +// UnlinkAtResp is an empty response to UnlinkAtReq. +type UnlinkAtResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*UnlinkAtResp) String() string { + return "UnlinkAtResp{}" +} + // RenameAtReq is used to make RenameAt requests. Note that the request takes in // the to-be-renamed file's FD instead of oldDir and oldName like renameat(2). type RenameAtReq struct { @@ -1015,6 +1332,11 @@ type RenameAtReq struct { NewName SizedString } +// String implements fmt.Stringer.String. +func (r *RenameAtReq) String() string { + return fmt.Sprintf("RenameAtReq{OldDir: %d, NewDir: %d, OldName: %s, NewName: %s}", r.OldDir, r.NewDir, r.OldName, r.NewName) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (r *RenameAtReq) SizeBytes() int { return r.OldDir.SizeBytes() + r.NewDir.SizeBytes() + r.OldName.SizeBytes() + r.NewName.SizeBytes() @@ -1047,6 +1369,14 @@ func (r *RenameAtReq) CheckedUnmarshal(src []byte) ([]byte, bool) { return srcRemain, true } +// RenameAtResp is an empty response to RenameAtReq. +type RenameAtResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*RenameAtResp) String() string { + return "RenameAtResp{}" +} + // Getdents64Req is used to make Getdents64 requests. // // +marshal boundCheck @@ -1059,6 +1389,11 @@ type Getdents64Req struct { Count int32 } +// String implements fmt.Stringer.String. +func (g *Getdents64Req) String() string { + return fmt.Sprintf("Getdents64Req{DirFD: %d, Count: %d}", g.DirFD, g.Count) +} + // Dirent64 is analogous to struct linux_dirent64. type Dirent64 struct { Ino primitive.Uint64 @@ -1069,6 +1404,11 @@ type Dirent64 struct { Name SizedString } +// String implements fmt.Stringer.String. +func (d *Dirent64) String() string { + return fmt.Sprintf("Dirent64{Ino: %d, DevMinor: %d, DevMajor: %d, Off: %d, Type: %d, Name: %s}", d.Ino, d.DevMinor, d.DevMajor, d.Off, d.Type, d.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (d *Dirent64) SizeBytes() int { return d.Ino.SizeBytes() + d.DevMinor.SizeBytes() + d.DevMajor.SizeBytes() + d.Off.SizeBytes() + d.Type.SizeBytes() + d.Name.SizeBytes() @@ -1107,6 +1447,11 @@ type Getdents64Resp struct { Dirents []Dirent64 } +// String implements fmt.Stringer.String. +func (g *Getdents64Resp) String() string { + return fmt.Sprintf("Getdents64Resp{Dirents: %+v}", g.Dirents) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (g *Getdents64Resp) SizeBytes() int { ret := (*primitive.Uint16)(nil).SizeBytes() @@ -1157,6 +1502,11 @@ type FGetXattrReq struct { Name SizedString } +// String implements fmt.Stringer.String. +func (g *FGetXattrReq) String() string { + return fmt.Sprintf("FGetXattrReq{FD: %d, BufSize: %d, Name: %s}", g.FD, g.BufSize, g.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (g *FGetXattrReq) SizeBytes() int { return g.FD.SizeBytes() + g.BufSize.SizeBytes() + g.Name.SizeBytes() @@ -1188,6 +1538,11 @@ type FGetXattrResp struct { Value SizedString } +// String implements fmt.Stringer.String. +func (g *FGetXattrResp) String() string { + return fmt.Sprintf("FGetXattrResp{Value: %s}", g.Value) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (g *FGetXattrResp) SizeBytes() int { return g.Value.SizeBytes() @@ -1211,6 +1566,11 @@ type FSetXattrReq struct { Value SizedString } +// String implements fmt.Stringer.String. +func (s *FSetXattrReq) String() string { + return fmt.Sprintf("FSetXattrReq{FD: %d, Flags: %d, Name: %s, Value: %s}", s.FD, s.Flags, s.Name, s.Value) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (s *FSetXattrReq) SizeBytes() int { return s.FD.SizeBytes() + s.Flags.SizeBytes() + s.Name.SizeBytes() + s.Value.SizeBytes() @@ -1243,12 +1603,25 @@ func (s *FSetXattrReq) CheckedUnmarshal(src []byte) ([]byte, bool) { return srcRemain, true } +// FSetXattrResp is an empty response to FSetXattrReq. +type FSetXattrResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*FSetXattrResp) String() string { + return "FSetXattrResp{}" +} + // FRemoveXattrReq is used to make FRemoveXattr requests. It has no response. type FRemoveXattrReq struct { FD FDID Name SizedString } +// String implements fmt.Stringer.String. +func (r *FRemoveXattrReq) String() string { + return fmt.Sprintf("FRemoveXattrReq{FD: %d, Name: %s}", r.FD, r.Name) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (r *FRemoveXattrReq) SizeBytes() int { return r.FD.SizeBytes() + r.Name.SizeBytes() @@ -1273,6 +1646,14 @@ func (r *FRemoveXattrReq) CheckedUnmarshal(src []byte) ([]byte, bool) { return src, false } +// FRemoveXattrResp is an empty response to FRemoveXattrReq. +type FRemoveXattrResp struct{ EmptyMessage } + +// String implements fmt.Stringer.String. +func (*FRemoveXattrResp) String() string { + return "FRemoveXattrResp{}" +} + // FListXattrReq is used to make FListXattr requests. // // +marshal boundCheck @@ -1282,11 +1663,21 @@ type FListXattrReq struct { Size uint64 } +// String implements fmt.Stringer.String. +func (l *FListXattrReq) String() string { + return fmt.Sprintf("FListXattrReq{FD: %d, Size: %d}", l.FD, l.Size) +} + // FListXattrResp is used to respond to FListXattr requests. type FListXattrResp struct { Xattrs StringArray } +// String implements fmt.Stringer.String. +func (l *FListXattrResp) String() string { + return fmt.Sprintf("FListXattrResp{Xattrs: %s}", l.Xattrs.String()) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (l *FListXattrResp) SizeBytes() int { return l.Xattrs.SizeBytes() diff --git a/pkg/lisafs/sample_message.go b/pkg/lisafs/sample_message.go index 25ee031cf..00919ed0d 100644 --- a/pkg/lisafs/sample_message.go +++ b/pkg/lisafs/sample_message.go @@ -15,6 +15,7 @@ package lisafs import ( + "fmt" "math/rand" "gvisor.dev/gvisor/pkg/marshal/primitive" @@ -46,6 +47,11 @@ type MsgDynamic struct { Arr []MsgSimple } +// String implements fmt.Stringer.String. +func (m *MsgDynamic) String() string { + return fmt.Sprintf("MsgDynamic{N: %d, Arr: %v}", m.N, m.Arr) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (m *MsgDynamic) SizeBytes() int { return m.N.SizeBytes() + @@ -98,6 +104,11 @@ type P9Version struct { Version string } +// String implements fmt.Stringer.String. +func (v *P9Version) String() string { + return fmt.Sprintf("P9Version{MSize: %d, Version: %s}", v.MSize, v.Version) +} + // SizeBytes implements marshal.Marshallable.SizeBytes. func (v *P9Version) SizeBytes() int { return (*primitive.Uint32)(nil).SizeBytes() + (*primitive.Uint16)(nil).SizeBytes() + len(v.Version) diff --git a/pkg/lisafs/sock.go b/pkg/lisafs/sock.go index 88210242f..8f554f9ed 100644 --- a/pkg/lisafs/sock.go +++ b/pkg/lisafs/sock.go @@ -15,6 +15,7 @@ package lisafs import ( + "fmt" "io" "golang.org/x/sys/unix" @@ -89,6 +90,11 @@ func (s *sockCommunicator) SndRcvMessage(m MID, payloadLen uint32, wantFDs uint8 return s.rcvMsg(wantFDs) } +// String implements fmt.Stringer.String. +func (s *sockCommunicator) String() string { + return fmt.Sprintf("sockComm %d", s.sock.FD()) +} + // sndPrepopulatedMsg assumes that s.buf has already been populated with // `payloadLen` bytes of data. func (s *sockCommunicator) sndPrepopulatedMsg(m MID, payloadLen uint32, fds []int) error { diff --git a/pkg/lisafs/sock_test.go b/pkg/lisafs/sock_test.go index 2819a72b0..387f4b7a8 100644 --- a/pkg/lisafs/sock_test.go +++ b/pkg/lisafs/sock_test.go @@ -195,7 +195,7 @@ func TestSndRcvMessageNoPayload(t *testing.T) { }) } -func checkMessageReceive(t *testing.T, comm *sockCommunicator, wantM MID, wantMsg interface{}) { +func checkMessageReceive(t *testing.T, comm *sockCommunicator, wantM MID, wantMsg marshal.Marshallable) { gotM, payloadLen, err := comm.rcvMsg(0) if err != nil { t.Fatalf("readMessageFrom failed: %v", err)