From 07f258497932e53f4651b80a086117ffda843fe3 Mon Sep 17 00:00:00 2001 From: Dean Deng Date: Thu, 16 Jan 2020 12:33:07 -0800 Subject: Plumb getting/setting xattrs through InodeOperations and 9p gofer interfaces. There was a very bare get/setxattr in the InodeOperations interface. Add context.Context to both, size to getxattr, and flags to setxattr. Note that extended attributes are passed around as strings in this implementation, so size is automatically encoded into the value. Size is added in getxattr so that implementations can return ERANGE if a value is larger than can fit in the user-allocated buffer. This prevents us from unnecessarily passing around an arbitrarily large xattr when the user buffer is actually too small. Don't use the existing xattrwalk and xattrcreate messages and define our own, mainly for the sake of simplicity. Extended attributes will be implemented in future commits. PiperOrigin-RevId: 290121300 --- pkg/p9/messages.go | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) (limited to 'pkg/p9/messages.go') diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go index ffdd7e8c6..ceb723d86 100644 --- a/pkg/p9/messages.go +++ b/pkg/p9/messages.go @@ -1611,6 +1611,131 @@ func (r *Rxattrcreate) String() string { return fmt.Sprintf("Rxattrcreate{}") } +// Tgetxattr is a getxattr request. +type Tgetxattr struct { + // FID refers to the file for which to get xattrs. + FID FID + + // Name is the xattr to get. + Name string + + // Size is the buffer size for the xattr to get. + Size uint64 +} + +// Decode implements encoder.Decode. +func (t *Tgetxattr) Decode(b *buffer) { + t.FID = b.ReadFID() + t.Name = b.ReadString() + t.Size = b.Read64() +} + +// Encode implements encoder.Encode. +func (t *Tgetxattr) Encode(b *buffer) { + b.WriteFID(t.FID) + b.WriteString(t.Name) + b.Write64(t.Size) +} + +// Type implements message.Type. +func (*Tgetxattr) Type() MsgType { + return MsgTgetxattr +} + +// String implements fmt.Stringer. +func (t *Tgetxattr) String() string { + return fmt.Sprintf("Tgetxattr{FID: %d, Name: %s, Size: %d}", t.FID, t.Name, t.Size) +} + +// Rgetxattr is a getxattr response. +type Rgetxattr struct { + // Value is the extended attribute value. + Value string +} + +// Decode implements encoder.Decode. +func (r *Rgetxattr) Decode(b *buffer) { + r.Value = b.ReadString() +} + +// Encode implements encoder.Encode. +func (r *Rgetxattr) Encode(b *buffer) { + b.WriteString(r.Value) +} + +// Type implements message.Type. +func (*Rgetxattr) Type() MsgType { + return MsgRgetxattr +} + +// String implements fmt.Stringer. +func (r *Rgetxattr) String() string { + return fmt.Sprintf("Rgetxattr{Value: %s}", r.Value) +} + +// Tsetxattr sets extended attributes. +type Tsetxattr struct { + // FID refers to the file on which to set xattrs. + FID FID + + // Name is the attribute name. + Name string + + // Value is the attribute value. + Value string + + // Linux setxattr(2) flags. + Flags uint32 +} + +// Decode implements encoder.Decode. +func (t *Tsetxattr) Decode(b *buffer) { + t.FID = b.ReadFID() + t.Name = b.ReadString() + t.Value = b.ReadString() + t.Flags = b.Read32() +} + +// Encode implements encoder.Encode. +func (t *Tsetxattr) Encode(b *buffer) { + b.WriteFID(t.FID) + b.WriteString(t.Name) + b.WriteString(t.Value) + b.Write32(t.Flags) +} + +// Type implements message.Type. +func (*Tsetxattr) Type() MsgType { + return MsgTsetxattr +} + +// String implements fmt.Stringer. +func (t *Tsetxattr) String() string { + return fmt.Sprintf("Tsetxattr{FID: %d, Name: %s, Value: %s, Flags: %d}", t.FID, t.Name, t.Value, t.Flags) +} + +// Rsetxattr is a setxattr response. +type Rsetxattr struct { +} + +// Decode implements encoder.Decode. +func (r *Rsetxattr) Decode(b *buffer) { +} + +// Encode implements encoder.Encode. +func (r *Rsetxattr) Encode(b *buffer) { +} + +// Type implements message.Type. +func (*Rsetxattr) Type() MsgType { + return MsgRsetxattr +} + +// String implements fmt.Stringer. +func (r *Rsetxattr) String() string { + return fmt.Sprintf("Rsetxattr{}") +} + // Treaddir is a readdir request. type Treaddir struct { // Directory is the directory FID to read. @@ -2363,6 +2488,10 @@ func init() { msgRegistry.register(MsgRxattrwalk, func() message { return &Rxattrwalk{} }) msgRegistry.register(MsgTxattrcreate, func() message { return &Txattrcreate{} }) msgRegistry.register(MsgRxattrcreate, func() message { return &Rxattrcreate{} }) + msgRegistry.register(MsgTgetxattr, func() message { return &Tgetxattr{} }) + msgRegistry.register(MsgRgetxattr, func() message { return &Rgetxattr{} }) + msgRegistry.register(MsgTsetxattr, func() message { return &Tsetxattr{} }) + msgRegistry.register(MsgRsetxattr, func() message { return &Rsetxattr{} }) msgRegistry.register(MsgTreaddir, func() message { return &Treaddir{} }) msgRegistry.register(MsgRreaddir, func() message { return &Rreaddir{} }) msgRegistry.register(MsgTfsync, func() message { return &Tfsync{} }) -- cgit v1.2.3 From 17b9f5e66238bde1e4ed3bd9e5fb67342c8b58ec Mon Sep 17 00:00:00 2001 From: Dean Deng Date: Fri, 7 Feb 2020 14:46:24 -0800 Subject: Support listxattr and removexattr syscalls. Note that these are only implemented for tmpfs, and other impls will still return EOPNOTSUPP. PiperOrigin-RevId: 293899385 --- pkg/p9/client_file.go | 33 ++++ pkg/p9/file.go | 16 ++ pkg/p9/handlers.go | 33 ++++ pkg/p9/messages.go | 199 +++++++++++++++---- pkg/p9/p9.go | 4 + pkg/p9/version.go | 8 +- pkg/sentry/fs/copy_up.go | 2 +- pkg/sentry/fs/fsutil/inode.go | 20 +- pkg/sentry/fs/gofer/context_file.go | 14 ++ pkg/sentry/fs/gofer/inode.go | 13 +- pkg/sentry/fs/inode.go | 14 +- pkg/sentry/fs/inode_operations.go | 13 +- pkg/sentry/fs/inode_overlay.go | 18 +- pkg/sentry/fs/tmpfs/tmpfs.go | 9 +- pkg/sentry/syscalls/linux/linux64_amd64.go | 27 ++- pkg/sentry/syscalls/linux/linux64_arm64.go | 37 ++-- pkg/sentry/syscalls/linux/sys_xattr.go | 200 +++++++++++++++++++- runsc/fsgofer/fsgofer.go | 16 +- test/syscalls/linux/BUILD | 1 + test/syscalls/linux/xattr.cc | 294 ++++++++++++++++------------- 20 files changed, 733 insertions(+), 238 deletions(-) (limited to 'pkg/p9/messages.go') diff --git a/pkg/p9/client_file.go b/pkg/p9/client_file.go index 0254e4ccc..2ee07b664 100644 --- a/pkg/p9/client_file.go +++ b/pkg/p9/client_file.go @@ -194,6 +194,39 @@ func (c *clientFile) SetXattr(name, value string, flags uint32) error { return c.client.sendRecv(&Tsetxattr{FID: c.fid, Name: name, Value: value, Flags: flags}, &Rsetxattr{}) } +// ListXattr implements File.ListXattr. +func (c *clientFile) ListXattr(size uint64) (map[string]struct{}, error) { + if atomic.LoadUint32(&c.closed) != 0 { + return nil, syscall.EBADF + } + if !versionSupportsListRemoveXattr(c.client.version) { + return nil, syscall.EOPNOTSUPP + } + + rlistxattr := Rlistxattr{} + if err := c.client.sendRecv(&Tlistxattr{FID: c.fid, Size: size}, &rlistxattr); err != nil { + return nil, err + } + + xattrs := make(map[string]struct{}, len(rlistxattr.Xattrs)) + for _, x := range rlistxattr.Xattrs { + xattrs[x] = struct{}{} + } + return xattrs, nil +} + +// RemoveXattr implements File.RemoveXattr. +func (c *clientFile) RemoveXattr(name string) error { + if atomic.LoadUint32(&c.closed) != 0 { + return syscall.EBADF + } + if !versionSupportsListRemoveXattr(c.client.version) { + return syscall.EOPNOTSUPP + } + + return c.client.sendRecv(&Tremovexattr{FID: c.fid, Name: name}, &Rremovexattr{}) +} + // Allocate implements File.Allocate. func (c *clientFile) Allocate(mode AllocateMode, offset, length uint64) error { if atomic.LoadUint32(&c.closed) != 0 { diff --git a/pkg/p9/file.go b/pkg/p9/file.go index 4607cfcdf..d4ffbc8e3 100644 --- a/pkg/p9/file.go +++ b/pkg/p9/file.go @@ -105,6 +105,22 @@ type File interface { // TODO(b/127675828): Determine concurrency guarantees once implemented. SetXattr(name, value string, flags uint32) error + // ListXattr lists the names of the extended attributes on this node. + // + // Size indicates the size of the buffer that has been allocated to hold the + // attribute list. If the list would be larger than size, implementations may + // return ERANGE to indicate that the buffer is too small, but they are also + // free to ignore the hint entirely (i.e. the value returned may be larger + // than size). All size checking is done independently at the syscall layer. + // + // TODO(b/148303075): Determine concurrency guarantees once implemented. + ListXattr(size uint64) (map[string]struct{}, error) + + // RemoveXattr removes extended attributes on this node. + // + // TODO(b/148303075): Determine concurrency guarantees once implemented. + RemoveXattr(name string) error + // Allocate allows the caller to directly manipulate the allocated disk space // for the file. See fallocate(2) for more details. Allocate(mode AllocateMode, offset, length uint64) error diff --git a/pkg/p9/handlers.go b/pkg/p9/handlers.go index 7d6653a07..2ac45eb80 100644 --- a/pkg/p9/handlers.go +++ b/pkg/p9/handlers.go @@ -941,6 +941,39 @@ func (t *Tsetxattr) handle(cs *connState) message { return &Rsetxattr{} } +// handle implements handler.handle. +func (t *Tlistxattr) handle(cs *connState) message { + ref, ok := cs.LookupFID(t.FID) + if !ok { + return newErr(syscall.EBADF) + } + defer ref.DecRef() + + xattrs, err := ref.file.ListXattr(t.Size) + if err != nil { + return newErr(err) + } + xattrList := make([]string, 0, len(xattrs)) + for x := range xattrs { + xattrList = append(xattrList, x) + } + return &Rlistxattr{Xattrs: xattrList} +} + +// handle implements handler.handle. +func (t *Tremovexattr) handle(cs *connState) message { + ref, ok := cs.LookupFID(t.FID) + if !ok { + return newErr(syscall.EBADF) + } + defer ref.DecRef() + + if err := ref.file.RemoveXattr(t.Name); err != nil { + return newErr(err) + } + return &Rremovexattr{} +} + // handle implements handler.handle. func (t *Treaddir) handle(cs *connState) message { ref, ok := cs.LookupFID(t.Directory) diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go index ceb723d86..b1cede5f5 100644 --- a/pkg/p9/messages.go +++ b/pkg/p9/messages.go @@ -174,11 +174,11 @@ type Rflush struct { } // Decode implements encoder.Decode. -func (*Rflush) Decode(b *buffer) { +func (*Rflush) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rflush) Encode(b *buffer) { +func (*Rflush) Encode(*buffer) { } // Type implements message.Type. @@ -188,7 +188,7 @@ func (*Rflush) Type() MsgType { // String implements fmt.Stringer. func (r *Rflush) String() string { - return fmt.Sprintf("RFlush{}") + return "RFlush{}" } // Twalk is a walk request. @@ -300,11 +300,11 @@ type Rclunk struct { } // Decode implements encoder.Decode. -func (*Rclunk) Decode(b *buffer) { +func (*Rclunk) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rclunk) Encode(b *buffer) { +func (*Rclunk) Encode(*buffer) { } // Type implements message.Type. @@ -314,7 +314,7 @@ func (*Rclunk) Type() MsgType { // String implements fmt.Stringer. func (r *Rclunk) String() string { - return fmt.Sprintf("Rclunk{}") + return "Rclunk{}" } // Tremove is a remove request. @@ -350,11 +350,11 @@ type Rremove struct { } // Decode implements encoder.Decode. -func (*Rremove) Decode(b *buffer) { +func (*Rremove) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rremove) Encode(b *buffer) { +func (*Rremove) Encode(*buffer) { } // Type implements message.Type. @@ -364,7 +364,7 @@ func (*Rremove) Type() MsgType { // String implements fmt.Stringer. func (r *Rremove) String() string { - return fmt.Sprintf("Rremove{}") + return "Rremove{}" } // Rlerror is an error response. @@ -745,16 +745,16 @@ func (*Rlink) Type() MsgType { } // Decode implements encoder.Decode. -func (*Rlink) Decode(b *buffer) { +func (*Rlink) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rlink) Encode(b *buffer) { +func (*Rlink) Encode(*buffer) { } // String implements fmt.Stringer. func (r *Rlink) String() string { - return fmt.Sprintf("Rlink{}") + return "Rlink{}" } // Trenameat is a rename request. @@ -803,11 +803,11 @@ type Rrenameat struct { } // Decode implements encoder.Decode. -func (*Rrenameat) Decode(b *buffer) { +func (*Rrenameat) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rrenameat) Encode(b *buffer) { +func (*Rrenameat) Encode(*buffer) { } // Type implements message.Type. @@ -817,7 +817,7 @@ func (*Rrenameat) Type() MsgType { // String implements fmt.Stringer. func (r *Rrenameat) String() string { - return fmt.Sprintf("Rrenameat{}") + return "Rrenameat{}" } // Tunlinkat is an unlink request. @@ -861,11 +861,11 @@ type Runlinkat struct { } // Decode implements encoder.Decode. -func (*Runlinkat) Decode(b *buffer) { +func (*Runlinkat) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Runlinkat) Encode(b *buffer) { +func (*Runlinkat) Encode(*buffer) { } // Type implements message.Type. @@ -875,7 +875,7 @@ func (*Runlinkat) Type() MsgType { // String implements fmt.Stringer. func (r *Runlinkat) String() string { - return fmt.Sprintf("Runlinkat{}") + return "Runlinkat{}" } // Trename is a rename request. @@ -922,11 +922,11 @@ type Rrename struct { } // Decode implements encoder.Decode. -func (*Rrename) Decode(b *buffer) { +func (*Rrename) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rrename) Encode(b *buffer) { +func (*Rrename) Encode(*buffer) { } // Type implements message.Type. @@ -936,7 +936,7 @@ func (*Rrename) Type() MsgType { // String implements fmt.Stringer. func (r *Rrename) String() string { - return fmt.Sprintf("Rrename{}") + return "Rrename{}" } // Treadlink is a readlink request. @@ -1409,11 +1409,11 @@ type Rsetattr struct { } // Decode implements encoder.Decode. -func (*Rsetattr) Decode(b *buffer) { +func (*Rsetattr) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rsetattr) Encode(b *buffer) { +func (*Rsetattr) Encode(*buffer) { } // Type implements message.Type. @@ -1423,7 +1423,7 @@ func (*Rsetattr) Type() MsgType { // String implements fmt.Stringer. func (r *Rsetattr) String() string { - return fmt.Sprintf("Rsetattr{}") + return "Rsetattr{}" } // Tallocate is an allocate request. This is an extension to 9P protocol, not @@ -1466,11 +1466,11 @@ type Rallocate struct { } // Decode implements encoder.Decode. -func (*Rallocate) Decode(b *buffer) { +func (*Rallocate) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rallocate) Encode(b *buffer) { +func (*Rallocate) Encode(*buffer) { } // Type implements message.Type. @@ -1480,7 +1480,71 @@ func (*Rallocate) Type() MsgType { // String implements fmt.Stringer. func (r *Rallocate) String() string { - return fmt.Sprintf("Rallocate{}") + return "Rallocate{}" +} + +// Tlistxattr is a listxattr request. +type Tlistxattr struct { + // FID refers to the file on which to list xattrs. + FID FID + + // Size is the buffer size for the xattr list. + Size uint64 +} + +// Decode implements encoder.Decode. +func (t *Tlistxattr) Decode(b *buffer) { + t.FID = b.ReadFID() + t.Size = b.Read64() +} + +// Encode implements encoder.Encode. +func (t *Tlistxattr) Encode(b *buffer) { + b.WriteFID(t.FID) + b.Write64(t.Size) +} + +// Type implements message.Type. +func (*Tlistxattr) Type() MsgType { + return MsgTlistxattr +} + +// String implements fmt.Stringer. +func (t *Tlistxattr) String() string { + return fmt.Sprintf("Tlistxattr{FID: %d, Size: %d}", t.FID, t.Size) +} + +// Rlistxattr is a listxattr response. +type Rlistxattr struct { + // Xattrs is a list of extended attribute names. + Xattrs []string +} + +// Decode implements encoder.Decode. +func (r *Rlistxattr) Decode(b *buffer) { + n := b.Read16() + r.Xattrs = r.Xattrs[:0] + for i := 0; i < int(n); i++ { + r.Xattrs = append(r.Xattrs, b.ReadString()) + } +} + +// Encode implements encoder.Encode. +func (r *Rlistxattr) Encode(b *buffer) { + b.Write16(uint16(len(r.Xattrs))) + for _, x := range r.Xattrs { + b.WriteString(x) + } +} + +// Type implements message.Type. +func (*Rlistxattr) Type() MsgType { + return MsgRlistxattr +} + +// String implements fmt.Stringer. +func (r *Rlistxattr) String() string { + return fmt.Sprintf("Rlistxattr{Xattrs: %v}", r.Xattrs) } // Txattrwalk walks extended attributes. @@ -1594,11 +1658,11 @@ type Rxattrcreate struct { } // Decode implements encoder.Decode. -func (r *Rxattrcreate) Decode(b *buffer) { +func (r *Rxattrcreate) Decode(*buffer) { } // Encode implements encoder.Encode. -func (r *Rxattrcreate) Encode(b *buffer) { +func (r *Rxattrcreate) Encode(*buffer) { } // Type implements message.Type. @@ -1608,7 +1672,7 @@ func (*Rxattrcreate) Type() MsgType { // String implements fmt.Stringer. func (r *Rxattrcreate) String() string { - return fmt.Sprintf("Rxattrcreate{}") + return "Rxattrcreate{}" } // Tgetxattr is a getxattr request. @@ -1719,11 +1783,11 @@ type Rsetxattr struct { } // Decode implements encoder.Decode. -func (r *Rsetxattr) Decode(b *buffer) { +func (r *Rsetxattr) Decode(*buffer) { } // Encode implements encoder.Encode. -func (r *Rsetxattr) Encode(b *buffer) { +func (r *Rsetxattr) Encode(*buffer) { } // Type implements message.Type. @@ -1733,7 +1797,60 @@ func (*Rsetxattr) Type() MsgType { // String implements fmt.Stringer. func (r *Rsetxattr) String() string { - return fmt.Sprintf("Rsetxattr{}") + return "Rsetxattr{}" +} + +// Tremovexattr is a removexattr request. +type Tremovexattr struct { + // FID refers to the file on which to set xattrs. + FID FID + + // Name is the attribute name. + Name string +} + +// Decode implements encoder.Decode. +func (t *Tremovexattr) Decode(b *buffer) { + t.FID = b.ReadFID() + t.Name = b.ReadString() +} + +// Encode implements encoder.Encode. +func (t *Tremovexattr) Encode(b *buffer) { + b.WriteFID(t.FID) + b.WriteString(t.Name) +} + +// Type implements message.Type. +func (*Tremovexattr) Type() MsgType { + return MsgTremovexattr +} + +// String implements fmt.Stringer. +func (t *Tremovexattr) String() string { + return fmt.Sprintf("Tremovexattr{FID: %d, Name: %s}", t.FID, t.Name) +} + +// Rremovexattr is a removexattr response. +type Rremovexattr struct { +} + +// Decode implements encoder.Decode. +func (r *Rremovexattr) Decode(*buffer) { +} + +// Encode implements encoder.Encode. +func (r *Rremovexattr) Encode(*buffer) { +} + +// Type implements message.Type. +func (*Rremovexattr) Type() MsgType { + return MsgRremovexattr +} + +// String implements fmt.Stringer. +func (r *Rremovexattr) String() string { + return "Rremovexattr{}" } // Treaddir is a readdir request. @@ -1880,11 +1997,11 @@ type Rfsync struct { } // Decode implements encoder.Decode. -func (*Rfsync) Decode(b *buffer) { +func (*Rfsync) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rfsync) Encode(b *buffer) { +func (*Rfsync) Encode(*buffer) { } // Type implements message.Type. @@ -1894,7 +2011,7 @@ func (*Rfsync) Type() MsgType { // String implements fmt.Stringer. func (r *Rfsync) String() string { - return fmt.Sprintf("Rfsync{}") + return "Rfsync{}" } // Tstatfs is a stat request. @@ -1980,11 +2097,11 @@ type Rflushf struct { } // Decode implements encoder.Decode. -func (*Rflushf) Decode(b *buffer) { +func (*Rflushf) Decode(*buffer) { } // Encode implements encoder.Encode. -func (*Rflushf) Encode(b *buffer) { +func (*Rflushf) Encode(*buffer) { } // Type implements message.Type. @@ -1994,7 +2111,7 @@ func (*Rflushf) Type() MsgType { // String implements fmt.Stringer. func (*Rflushf) String() string { - return fmt.Sprintf("Rflushf{}") + return "Rflushf{}" } // Twalkgetattr is a walk request. @@ -2484,6 +2601,8 @@ func init() { msgRegistry.register(MsgRgetattr, func() message { return &Rgetattr{} }) msgRegistry.register(MsgTsetattr, func() message { return &Tsetattr{} }) msgRegistry.register(MsgRsetattr, func() message { return &Rsetattr{} }) + msgRegistry.register(MsgTlistxattr, func() message { return &Tlistxattr{} }) + msgRegistry.register(MsgRlistxattr, func() message { return &Rlistxattr{} }) msgRegistry.register(MsgTxattrwalk, func() message { return &Txattrwalk{} }) msgRegistry.register(MsgRxattrwalk, func() message { return &Rxattrwalk{} }) msgRegistry.register(MsgTxattrcreate, func() message { return &Txattrcreate{} }) @@ -2492,6 +2611,8 @@ func init() { msgRegistry.register(MsgRgetxattr, func() message { return &Rgetxattr{} }) msgRegistry.register(MsgTsetxattr, func() message { return &Tsetxattr{} }) msgRegistry.register(MsgRsetxattr, func() message { return &Rsetxattr{} }) + msgRegistry.register(MsgTremovexattr, func() message { return &Tremovexattr{} }) + msgRegistry.register(MsgRremovexattr, func() message { return &Rremovexattr{} }) msgRegistry.register(MsgTreaddir, func() message { return &Treaddir{} }) msgRegistry.register(MsgRreaddir, func() message { return &Rreaddir{} }) msgRegistry.register(MsgTfsync, func() message { return &Tfsync{} }) diff --git a/pkg/p9/p9.go b/pkg/p9/p9.go index 5ab00d625..20ab31f7a 100644 --- a/pkg/p9/p9.go +++ b/pkg/p9/p9.go @@ -335,6 +335,8 @@ const ( MsgRgetattr = 25 MsgTsetattr = 26 MsgRsetattr = 27 + MsgTlistxattr = 28 + MsgRlistxattr = 29 MsgTxattrwalk = 30 MsgRxattrwalk = 31 MsgTxattrcreate = 32 @@ -343,6 +345,8 @@ const ( MsgRgetxattr = 35 MsgTsetxattr = 36 MsgRsetxattr = 37 + MsgTremovexattr = 38 + MsgRremovexattr = 39 MsgTreaddir = 40 MsgRreaddir = 41 MsgTfsync = 50 diff --git a/pkg/p9/version.go b/pkg/p9/version.go index 34a15eb55..09cde9f5a 100644 --- a/pkg/p9/version.go +++ b/pkg/p9/version.go @@ -26,7 +26,7 @@ const ( // // Clients are expected to start requesting this version number and // to continuously decrement it until a Tversion request succeeds. - highestSupportedVersion uint32 = 10 + highestSupportedVersion uint32 = 11 // lowestSupportedVersion is the lowest supported version X in a // version string of the format 9P2000.L.Google.X. @@ -167,3 +167,9 @@ func VersionSupportsOpenTruncateFlag(v uint32) bool { func versionSupportsGetSetXattr(v uint32) bool { return v >= 10 } + +// versionSupportsListRemoveXattr returns true if version v supports +// the Tlistxattr and Tremovexattr messages. +func versionSupportsListRemoveXattr(v uint32) bool { + return v >= 11 +} diff --git a/pkg/sentry/fs/copy_up.go b/pkg/sentry/fs/copy_up.go index f6c79e51b..b060a12ff 100644 --- a/pkg/sentry/fs/copy_up.go +++ b/pkg/sentry/fs/copy_up.go @@ -401,7 +401,7 @@ func copyAttributesLocked(ctx context.Context, upper *Inode, lower *Inode) error if err != nil { return err } - lowerXattr, err := lower.ListXattr(ctx) + lowerXattr, err := lower.ListXattr(ctx, linux.XATTR_SIZE_MAX) if err != nil && err != syserror.EOPNOTSUPP { return err } diff --git a/pkg/sentry/fs/fsutil/inode.go b/pkg/sentry/fs/fsutil/inode.go index 252830572..daecc4ffe 100644 --- a/pkg/sentry/fs/fsutil/inode.go +++ b/pkg/sentry/fs/fsutil/inode.go @@ -247,7 +247,7 @@ func (i *InodeSimpleExtendedAttributes) SetXattr(_ context.Context, _ *fs.Inode, } // ListXattr implements fs.InodeOperations.ListXattr. -func (i *InodeSimpleExtendedAttributes) ListXattr(context.Context, *fs.Inode) (map[string]struct{}, error) { +func (i *InodeSimpleExtendedAttributes) ListXattr(context.Context, *fs.Inode, uint64) (map[string]struct{}, error) { i.mu.RLock() names := make(map[string]struct{}, len(i.xattrs)) for name := range i.xattrs { @@ -257,6 +257,17 @@ func (i *InodeSimpleExtendedAttributes) ListXattr(context.Context, *fs.Inode) (m return names, nil } +// RemoveXattr implements fs.InodeOperations.RemoveXattr. +func (i *InodeSimpleExtendedAttributes) RemoveXattr(_ context.Context, _ *fs.Inode, name string) error { + i.mu.RLock() + defer i.mu.RUnlock() + if _, ok := i.xattrs[name]; ok { + delete(i.xattrs, name) + return nil + } + return syserror.ENOATTR +} + // staticFile is a file with static contents. It is returned by // InodeStaticFileGetter.GetFile. // @@ -460,10 +471,15 @@ func (InodeNoExtendedAttributes) SetXattr(context.Context, *fs.Inode, string, st } // ListXattr implements fs.InodeOperations.ListXattr. -func (InodeNoExtendedAttributes) ListXattr(context.Context, *fs.Inode) (map[string]struct{}, error) { +func (InodeNoExtendedAttributes) ListXattr(context.Context, *fs.Inode, uint64) (map[string]struct{}, error) { return nil, syserror.EOPNOTSUPP } +// RemoveXattr implements fs.InodeOperations.RemoveXattr. +func (InodeNoExtendedAttributes) RemoveXattr(context.Context, *fs.Inode, string) error { + return syserror.EOPNOTSUPP +} + // InodeNoopRelease implements fs.InodeOperations.Release as a noop. type InodeNoopRelease struct{} diff --git a/pkg/sentry/fs/gofer/context_file.go b/pkg/sentry/fs/gofer/context_file.go index 3da818aed..125907d70 100644 --- a/pkg/sentry/fs/gofer/context_file.go +++ b/pkg/sentry/fs/gofer/context_file.go @@ -73,6 +73,20 @@ func (c *contextFile) setXattr(ctx context.Context, name, value string, flags ui return err } +func (c *contextFile) listXattr(ctx context.Context, size uint64) (map[string]struct{}, error) { + ctx.UninterruptibleSleepStart(false) + xattrs, err := c.file.ListXattr(size) + ctx.UninterruptibleSleepFinish(false) + return xattrs, err +} + +func (c *contextFile) removeXattr(ctx context.Context, name string) error { + ctx.UninterruptibleSleepStart(false) + err := c.file.RemoveXattr(name) + ctx.UninterruptibleSleepFinish(false) + return err +} + func (c *contextFile) allocate(ctx context.Context, mode p9.AllocateMode, offset, length uint64) error { ctx.UninterruptibleSleepStart(false) err := c.file.Allocate(mode, offset, length) diff --git a/pkg/sentry/fs/gofer/inode.go b/pkg/sentry/fs/gofer/inode.go index ac28174d2..1c934981b 100644 --- a/pkg/sentry/fs/gofer/inode.go +++ b/pkg/sentry/fs/gofer/inode.go @@ -604,18 +604,23 @@ func (i *inodeOperations) Truncate(ctx context.Context, inode *fs.Inode, length } // GetXattr implements fs.InodeOperations.GetXattr. -func (i *inodeOperations) GetXattr(ctx context.Context, inode *fs.Inode, name string, size uint64) (string, error) { +func (i *inodeOperations) GetXattr(ctx context.Context, _ *fs.Inode, name string, size uint64) (string, error) { return i.fileState.file.getXattr(ctx, name, size) } // SetXattr implements fs.InodeOperations.SetXattr. -func (i *inodeOperations) SetXattr(ctx context.Context, inode *fs.Inode, name string, value string, flags uint32) error { +func (i *inodeOperations) SetXattr(ctx context.Context, _ *fs.Inode, name string, value string, flags uint32) error { return i.fileState.file.setXattr(ctx, name, value, flags) } // ListXattr implements fs.InodeOperations.ListXattr. -func (i *inodeOperations) ListXattr(context.Context, *fs.Inode) (map[string]struct{}, error) { - return nil, syscall.EOPNOTSUPP +func (i *inodeOperations) ListXattr(ctx context.Context, _ *fs.Inode, size uint64) (map[string]struct{}, error) { + return i.fileState.file.listXattr(ctx, size) +} + +// RemoveXattr implements fs.InodeOperations.RemoveXattr. +func (i *inodeOperations) RemoveXattr(ctx context.Context, _ *fs.Inode, name string) error { + return i.fileState.file.removeXattr(ctx, name) } // Allocate implements fs.InodeOperations.Allocate. diff --git a/pkg/sentry/fs/inode.go b/pkg/sentry/fs/inode.go index b66c091ab..55fb71c16 100644 --- a/pkg/sentry/fs/inode.go +++ b/pkg/sentry/fs/inode.go @@ -278,11 +278,19 @@ func (i *Inode) SetXattr(ctx context.Context, d *Dirent, name, value string, fla } // ListXattr calls i.InodeOperations.ListXattr with i as the Inode. -func (i *Inode) ListXattr(ctx context.Context) (map[string]struct{}, error) { +func (i *Inode) ListXattr(ctx context.Context, size uint64) (map[string]struct{}, error) { if i.overlay != nil { - return overlayListXattr(ctx, i.overlay) + return overlayListXattr(ctx, i.overlay, size) } - return i.InodeOperations.ListXattr(ctx, i) + return i.InodeOperations.ListXattr(ctx, i, size) +} + +// RemoveXattr calls i.InodeOperations.RemoveXattr with i as the Inode. +func (i *Inode) RemoveXattr(ctx context.Context, d *Dirent, name string) error { + if i.overlay != nil { + return overlayRemoveXattr(ctx, i.overlay, d, name) + } + return i.InodeOperations.RemoveXattr(ctx, i, name) } // CheckPermission will check if the caller may access this file in the diff --git a/pkg/sentry/fs/inode_operations.go b/pkg/sentry/fs/inode_operations.go index 70f2eae96..2bbfb72ef 100644 --- a/pkg/sentry/fs/inode_operations.go +++ b/pkg/sentry/fs/inode_operations.go @@ -190,7 +190,18 @@ type InodeOperations interface { // ListXattr returns the set of all extended attributes names that // have values. Inodes that do not support extended attributes return // EOPNOTSUPP. - ListXattr(ctx context.Context, inode *Inode) (map[string]struct{}, error) + // + // If this is called through the listxattr(2) syscall, size indicates the + // size of the buffer that the application has allocated to hold the + // attribute list. If the list would be larger than size, implementations may + // return ERANGE to indicate that the buffer is too small, but they are also + // free to ignore the hint entirely. All size checking is done independently + // at the syscall layer. + ListXattr(ctx context.Context, inode *Inode, size uint64) (map[string]struct{}, error) + + // RemoveXattr removes an extended attribute specified by name. Inodes that + // do not support extended attributes return EOPNOTSUPP. + RemoveXattr(ctx context.Context, inode *Inode, name string) error // Check determines whether an Inode can be accessed with the // requested permission mask using the context (which gives access diff --git a/pkg/sentry/fs/inode_overlay.go b/pkg/sentry/fs/inode_overlay.go index 4729b4aac..5ada33a32 100644 --- a/pkg/sentry/fs/inode_overlay.go +++ b/pkg/sentry/fs/inode_overlay.go @@ -564,15 +564,15 @@ func overlaySetxattr(ctx context.Context, o *overlayEntry, d *Dirent, name, valu return o.upper.SetXattr(ctx, d, name, value, flags) } -func overlayListXattr(ctx context.Context, o *overlayEntry) (map[string]struct{}, error) { +func overlayListXattr(ctx context.Context, o *overlayEntry, size uint64) (map[string]struct{}, error) { o.copyMu.RLock() defer o.copyMu.RUnlock() var names map[string]struct{} var err error if o.upper != nil { - names, err = o.upper.ListXattr(ctx) + names, err = o.upper.ListXattr(ctx, size) } else { - names, err = o.lower.ListXattr(ctx) + names, err = o.lower.ListXattr(ctx, size) } for name := range names { // Same as overlayGetXattr, we shouldn't forward along @@ -584,6 +584,18 @@ func overlayListXattr(ctx context.Context, o *overlayEntry) (map[string]struct{} return names, err } +func overlayRemoveXattr(ctx context.Context, o *overlayEntry, d *Dirent, name string) error { + // Don't allow changes to overlay xattrs through a removexattr syscall. + if strings.HasPrefix(XattrOverlayPrefix, name) { + return syserror.EPERM + } + + if err := copyUp(ctx, d); err != nil { + return err + } + return o.upper.RemoveXattr(ctx, d, name) +} + func overlayCheck(ctx context.Context, o *overlayEntry, p PermMask) error { o.copyMu.RLock() // Hot path. Avoid defers. diff --git a/pkg/sentry/fs/tmpfs/tmpfs.go b/pkg/sentry/fs/tmpfs/tmpfs.go index c00cef0a5..3c2b583ae 100644 --- a/pkg/sentry/fs/tmpfs/tmpfs.go +++ b/pkg/sentry/fs/tmpfs/tmpfs.go @@ -159,8 +159,13 @@ func (d *Dir) SetXattr(ctx context.Context, i *fs.Inode, name, value string, fla } // ListXattr implements fs.InodeOperations.ListXattr. -func (d *Dir) ListXattr(ctx context.Context, i *fs.Inode) (map[string]struct{}, error) { - return d.ramfsDir.ListXattr(ctx, i) +func (d *Dir) ListXattr(ctx context.Context, i *fs.Inode, size uint64) (map[string]struct{}, error) { + return d.ramfsDir.ListXattr(ctx, i, size) +} + +// RemoveXattr implements fs.InodeOperations.RemoveXattr. +func (d *Dir) RemoveXattr(ctx context.Context, i *fs.Inode, name string) error { + return d.ramfsDir.RemoveXattr(ctx, i, name) } // Lookup implements fs.InodeOperations.Lookup. diff --git a/pkg/sentry/syscalls/linux/linux64_amd64.go b/pkg/sentry/syscalls/linux/linux64_amd64.go index 588f8b087..79066ad2a 100644 --- a/pkg/sentry/syscalls/linux/linux64_amd64.go +++ b/pkg/sentry/syscalls/linux/linux64_amd64.go @@ -228,21 +228,18 @@ var AMD64 = &kernel.SyscallTable{ 185: syscalls.Error("security", syserror.ENOSYS, "Not implemented in Linux.", nil), 186: syscalls.Supported("gettid", Gettid), 187: syscalls.Supported("readahead", Readahead), - // TODO(b/148303075): Enable set/getxattr (in their various - // forms) once we also have list and removexattr. The JVM - // assumes that if get/set exist, then list and remove do too. - 188: syscalls.ErrorWithEvent("setxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 189: syscalls.ErrorWithEvent("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 190: syscalls.ErrorWithEvent("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 191: syscalls.ErrorWithEvent("getxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 192: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 193: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 194: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 195: syscalls.ErrorWithEvent("llistxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 196: syscalls.ErrorWithEvent("flistxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 197: syscalls.ErrorWithEvent("removexattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 198: syscalls.ErrorWithEvent("lremovexattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 199: syscalls.ErrorWithEvent("fremovexattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), + 188: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil), + 189: syscalls.PartiallySupported("lsetxattr", LSetXattr, "Only supported for tmpfs.", nil), + 190: syscalls.PartiallySupported("fsetxattr", FSetXattr, "Only supported for tmpfs.", nil), + 191: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil), + 192: syscalls.PartiallySupported("lgetxattr", LGetXattr, "Only supported for tmpfs.", nil), + 193: syscalls.PartiallySupported("fgetxattr", FGetXattr, "Only supported for tmpfs.", nil), + 194: syscalls.PartiallySupported("listxattr", ListXattr, "Only supported for tmpfs", nil), + 195: syscalls.PartiallySupported("llistxattr", LListXattr, "Only supported for tmpfs", nil), + 196: syscalls.PartiallySupported("flistxattr", FListXattr, "Only supported for tmpfs", nil), + 197: syscalls.PartiallySupported("removexattr", RemoveXattr, "Only supported for tmpfs", nil), + 198: syscalls.PartiallySupported("lremovexattr", LRemoveXattr, "Only supported for tmpfs", nil), + 199: syscalls.PartiallySupported("fremovexattr", FRemoveXattr, "Only supported for tmpfs", nil), 200: syscalls.Supported("tkill", Tkill), 201: syscalls.Supported("time", Time), 202: syscalls.PartiallySupported("futex", Futex, "Robust futexes not supported.", nil), diff --git a/pkg/sentry/syscalls/linux/linux64_arm64.go b/pkg/sentry/syscalls/linux/linux64_arm64.go index 06e5ee401..7421619de 100644 --- a/pkg/sentry/syscalls/linux/linux64_arm64.go +++ b/pkg/sentry/syscalls/linux/linux64_arm64.go @@ -36,26 +36,23 @@ var ARM64 = &kernel.SyscallTable{ }, AuditNumber: linux.AUDIT_ARCH_AARCH64, Table: map[uintptr]kernel.Syscall{ - 0: syscalls.PartiallySupported("io_setup", IoSetup, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), - 1: syscalls.PartiallySupported("io_destroy", IoDestroy, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), - 2: syscalls.PartiallySupported("io_submit", IoSubmit, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), - 3: syscalls.PartiallySupported("io_cancel", IoCancel, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), - 4: syscalls.PartiallySupported("io_getevents", IoGetevents, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), - // TODO(b/148303075): Enable set/getxattr (in their various - // forms) once we also have list and removexattr. The JVM - // assumes that if get/set exist, then list and remove do too. - 5: syscalls.ErrorWithEvent("setxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 6: syscalls.ErrorWithEvent("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 7: syscalls.ErrorWithEvent("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 8: syscalls.ErrorWithEvent("getxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 9: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 10: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 11: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 13: syscalls.ErrorWithEvent("llistxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 13: syscalls.ErrorWithEvent("flistxattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 14: syscalls.ErrorWithEvent("removexattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 15: syscalls.ErrorWithEvent("lremovexattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), - 16: syscalls.ErrorWithEvent("fremovexattr", syserror.ENOTSUP, "Requires filesystem support.", []string{"gvisor.dev/issue/1636"}), + 0: syscalls.PartiallySupported("io_setup", IoSetup, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), + 1: syscalls.PartiallySupported("io_destroy", IoDestroy, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), + 2: syscalls.PartiallySupported("io_submit", IoSubmit, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), + 3: syscalls.PartiallySupported("io_cancel", IoCancel, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), + 4: syscalls.PartiallySupported("io_getevents", IoGetevents, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}), + 5: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil), + 6: syscalls.PartiallySupported("lsetxattr", LSetXattr, "Only supported for tmpfs.", nil), + 7: syscalls.PartiallySupported("fsetxattr", FSetXattr, "Only supported for tmpfs.", nil), + 8: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil), + 9: syscalls.PartiallySupported("lgetxattr", LGetXattr, "Only supported for tmpfs.", nil), + 10: syscalls.PartiallySupported("fgetxattr", FGetXattr, "Only supported for tmpfs.", nil), + 11: syscalls.PartiallySupported("listxattr", ListXattr, "Only supported for tmpfs", nil), + 12: syscalls.PartiallySupported("llistxattr", LListXattr, "Only supported for tmpfs", nil), + 13: syscalls.PartiallySupported("flistxattr", FListXattr, "Only supported for tmpfs", nil), + 14: syscalls.PartiallySupported("removexattr", RemoveXattr, "Only supported for tmpfs", nil), + 15: syscalls.PartiallySupported("lremovexattr", LRemoveXattr, "Only supported for tmpfs", nil), + 16: syscalls.PartiallySupported("fremovexattr", FRemoveXattr, "Only supported for tmpfs", nil), 17: syscalls.Supported("getcwd", Getcwd), 18: syscalls.CapError("lookup_dcookie", linux.CAP_SYS_ADMIN, "", nil), 19: syscalls.Supported("eventfd2", Eventfd2), diff --git a/pkg/sentry/syscalls/linux/sys_xattr.go b/pkg/sentry/syscalls/linux/sys_xattr.go index efb95555c..342337726 100644 --- a/pkg/sentry/syscalls/linux/sys_xattr.go +++ b/pkg/sentry/syscalls/linux/sys_xattr.go @@ -72,7 +72,7 @@ func getXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink } valueLen := 0 - err = fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(root *fs.Dirent, d *fs.Dirent, _ uint) error { + err = fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error { if dirPath && !fs.IsDir(d.Inode.StableAttr) { return syserror.ENOTDIR } @@ -172,7 +172,7 @@ func setXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink return 0, nil, err } - return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(root *fs.Dirent, d *fs.Dirent, _ uint) error { + return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error { if dirPath && !fs.IsDir(d.Inode.StableAttr) { return syserror.ENOTDIR } @@ -187,12 +187,12 @@ func setXattr(t *kernel.Task, d *fs.Dirent, nameAddr, valueAddr usermem.Addr, si return syserror.EINVAL } - if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Write: true}); err != nil { + name, err := copyInXattrName(t, nameAddr) + if err != nil { return err } - name, err := copyInXattrName(t, nameAddr) - if err != nil { + if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Write: true}); err != nil { return err } @@ -226,12 +226,18 @@ func copyInXattrName(t *kernel.Task, nameAddr usermem.Addr) (string, error) { return name, nil } +// Restrict xattrs to regular files and directories. +// +// TODO(b/148380782): In Linux, this restriction technically only applies to +// xattrs in the "user.*" namespace. Make file type checks specific to the +// namespace once we allow other xattr prefixes. +func xattrFileTypeOk(i *fs.Inode) bool { + return fs.IsRegular(i.StableAttr) || fs.IsDir(i.StableAttr) +} + func checkXattrPermissions(t *kernel.Task, i *fs.Inode, perms fs.PermMask) error { // Restrict xattrs to regular files and directories. - // - // In Linux, this restriction technically only applies to xattrs in the - // "user.*" namespace, but we don't allow any other xattr prefixes anyway. - if !fs.IsRegular(i.StableAttr) && !fs.IsDir(i.StableAttr) { + if !xattrFileTypeOk(i) { if perms.Write { return syserror.EPERM } @@ -240,3 +246,179 @@ func checkXattrPermissions(t *kernel.Task, i *fs.Inode, perms fs.PermMask) error return i.CheckPermission(t, perms) } + +// ListXattr implements linux syscall listxattr(2). +func ListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + return listXattrFromPath(t, args, true) +} + +// LListXattr implements linux syscall llistxattr(2). +func LListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + return listXattrFromPath(t, args, false) +} + +// FListXattr implements linux syscall flistxattr(2). +func FListXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + fd := args[0].Int() + listAddr := args[1].Pointer() + size := uint64(args[2].SizeT()) + + // TODO(b/113957122): Return EBADF if the fd was opened with O_PATH. + f := t.GetFile(fd) + if f == nil { + return 0, nil, syserror.EBADF + } + defer f.DecRef() + + n, err := listXattr(t, f.Dirent, listAddr, size) + if err != nil { + return 0, nil, err + } + + return uintptr(n), nil, nil +} + +func listXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) { + pathAddr := args[0].Pointer() + listAddr := args[1].Pointer() + size := uint64(args[2].SizeT()) + + path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */) + if err != nil { + return 0, nil, err + } + + n := 0 + err = fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error { + if dirPath && !fs.IsDir(d.Inode.StableAttr) { + return syserror.ENOTDIR + } + + n, err = listXattr(t, d, listAddr, size) + return err + }) + if err != nil { + return 0, nil, err + } + + return uintptr(n), nil, nil +} + +func listXattr(t *kernel.Task, d *fs.Dirent, addr usermem.Addr, size uint64) (int, error) { + if !xattrFileTypeOk(d.Inode) { + return 0, nil + } + + // If listxattr(2) is called with size 0, the buffer size needed to contain + // the xattr list will be returned successfully even if it is nonzero. In + // that case, we need to retrieve the entire list so we can compute and + // return the correct size. + requestedSize := size + if size == 0 || size > linux.XATTR_SIZE_MAX { + requestedSize = linux.XATTR_SIZE_MAX + } + xattrs, err := d.Inode.ListXattr(t, requestedSize) + if err != nil { + return 0, err + } + + // TODO(b/148380782): support namespaces other than "user". + for x := range xattrs { + if !strings.HasPrefix(x, linux.XATTR_USER_PREFIX) { + delete(xattrs, x) + } + } + + listSize := xattrListSize(xattrs) + if listSize > linux.XATTR_SIZE_MAX { + return 0, syserror.E2BIG + } + if uint64(listSize) > requestedSize { + return 0, syserror.ERANGE + } + + // Don't copy out the attributes if size is 0. + if size == 0 { + return listSize, nil + } + + buf := make([]byte, 0, listSize) + for x := range xattrs { + buf = append(buf, []byte(x)...) + buf = append(buf, 0) + } + if _, err := t.CopyOutBytes(addr, buf); err != nil { + return 0, err + } + + return len(buf), nil +} + +func xattrListSize(xattrs map[string]struct{}) int { + size := 0 + for x := range xattrs { + size += len(x) + 1 + } + return size +} + +// RemoveXattr implements linux syscall removexattr(2). +func RemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + return removeXattrFromPath(t, args, true) +} + +// LRemoveXattr implements linux syscall lremovexattr(2). +func LRemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + return removeXattrFromPath(t, args, false) +} + +// FRemoveXattr implements linux syscall fremovexattr(2). +func FRemoveXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + fd := args[0].Int() + nameAddr := args[1].Pointer() + + // TODO(b/113957122): Return EBADF if the fd was opened with O_PATH. + f := t.GetFile(fd) + if f == nil { + return 0, nil, syserror.EBADF + } + defer f.DecRef() + + return 0, nil, removeXattr(t, f.Dirent, nameAddr) +} + +func removeXattrFromPath(t *kernel.Task, args arch.SyscallArguments, resolveSymlink bool) (uintptr, *kernel.SyscallControl, error) { + pathAddr := args[0].Pointer() + nameAddr := args[1].Pointer() + + path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */) + if err != nil { + return 0, nil, err + } + + return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, resolveSymlink, func(_ *fs.Dirent, d *fs.Dirent, _ uint) error { + if dirPath && !fs.IsDir(d.Inode.StableAttr) { + return syserror.ENOTDIR + } + + return removeXattr(t, d, nameAddr) + }) +} + +// removeXattr implements removexattr(2) from the given *fs.Dirent. +func removeXattr(t *kernel.Task, d *fs.Dirent, nameAddr usermem.Addr) error { + name, err := copyInXattrName(t, nameAddr) + if err != nil { + return err + } + + if err := checkXattrPermissions(t, d.Inode, fs.PermMask{Write: true}); err != nil { + return err + } + + if !strings.HasPrefix(name, linux.XATTR_USER_PREFIX) { + return syserror.EOPNOTSUPP + } + + return d.Inode.RemoveXattr(t, d, name) +} diff --git a/runsc/fsgofer/fsgofer.go b/runsc/fsgofer/fsgofer.go index 4d84ad999..cadd83273 100644 --- a/runsc/fsgofer/fsgofer.go +++ b/runsc/fsgofer/fsgofer.go @@ -768,12 +768,22 @@ func (l *localFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error { } // TODO(b/127675828): support getxattr. -func (l *localFile) GetXattr(name string, size uint64) (string, error) { +func (*localFile) GetXattr(string, uint64) (string, error) { return "", syscall.EOPNOTSUPP } // TODO(b/127675828): support setxattr. -func (l *localFile) SetXattr(name, value string, flags uint32) error { +func (*localFile) SetXattr(string, string, uint32) error { + return syscall.EOPNOTSUPP +} + +// TODO(b/148303075): support listxattr. +func (*localFile) ListXattr(uint64) (map[string]struct{}, error) { + return nil, syscall.EOPNOTSUPP +} + +// TODO(b/148303075): support removexattr. +func (*localFile) RemoveXattr(string) error { return syscall.EOPNOTSUPP } @@ -790,7 +800,7 @@ func (l *localFile) Allocate(mode p9.AllocateMode, offset, length uint64) error } // Rename implements p9.File; this should never be called. -func (l *localFile) Rename(p9.File, string) error { +func (*localFile) Rename(p9.File, string) error { panic("rename called directly") } diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 12d389c3e..ca1af209a 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -3782,6 +3782,7 @@ cc_binary( "//test/util:capability_util", "//test/util:file_descriptor", "//test/util:fs_util", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", gtest, "//test/util:posix_error", diff --git a/test/syscalls/linux/xattr.cc b/test/syscalls/linux/xattr.cc index 85eb31847..8b00ef44c 100644 --- a/test/syscalls/linux/xattr.cc +++ b/test/syscalls/linux/xattr.cc @@ -24,6 +24,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/container/flat_hash_set.h" #include "test/syscalls/linux/file_base.h" #include "test/util/capability_util.h" #include "test/util/file_descriptor.h" @@ -38,36 +39,36 @@ namespace { class XattrTest : public FileTest {}; -TEST_F(XattrTest, XattrNullName) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); +TEST_F(XattrTest, XattrNonexistentFile) { + const char* path = "/does/not/exist"; + EXPECT_THAT(setxattr(path, nullptr, nullptr, 0, /*flags=*/0), + SyscallFailsWithErrno(ENOENT)); + EXPECT_THAT(getxattr(path, nullptr, nullptr, 0), + SyscallFailsWithErrno(ENOENT)); + EXPECT_THAT(listxattr(path, nullptr, 0), SyscallFailsWithErrno(ENOENT)); + EXPECT_THAT(removexattr(path, nullptr), SyscallFailsWithErrno(ENOENT)); +} +TEST_F(XattrTest, XattrNullName) { const char* path = test_file_name_.c_str(); EXPECT_THAT(setxattr(path, nullptr, nullptr, 0, /*flags=*/0), SyscallFailsWithErrno(EFAULT)); EXPECT_THAT(getxattr(path, nullptr, nullptr, 0), SyscallFailsWithErrno(EFAULT)); + EXPECT_THAT(removexattr(path, nullptr), SyscallFailsWithErrno(EFAULT)); } TEST_F(XattrTest, XattrEmptyName) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); EXPECT_THAT(setxattr(path, "", nullptr, 0, /*flags=*/0), SyscallFailsWithErrno(ERANGE)); EXPECT_THAT(getxattr(path, "", nullptr, 0), SyscallFailsWithErrno(ERANGE)); + EXPECT_THAT(removexattr(path, ""), SyscallFailsWithErrno(ERANGE)); } TEST_F(XattrTest, XattrLargeName) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); std::string name = "user."; name += std::string(XATTR_NAME_MAX - name.length(), 'a'); @@ -86,28 +87,23 @@ TEST_F(XattrTest, XattrLargeName) { SyscallFailsWithErrno(ERANGE)); EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0), SyscallFailsWithErrno(ERANGE)); + EXPECT_THAT(removexattr(path, name.c_str()), SyscallFailsWithErrno(ERANGE)); } TEST_F(XattrTest, XattrInvalidPrefix) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); std::string name(XATTR_NAME_MAX, 'a'); EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0), SyscallFailsWithErrno(EOPNOTSUPP)); EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0), SyscallFailsWithErrno(EOPNOTSUPP)); + EXPECT_THAT(removexattr(path, name.c_str()), + SyscallFailsWithErrno(EOPNOTSUPP)); } // Do not allow save/restore cycles after making the test file read-only, as // the restore will fail to open it with r/w permissions. TEST_F(XattrTest, XattrReadOnly_NoRandomSave) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - // Drop capabilities that allow us to override file and directory permissions. ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); @@ -124,19 +120,21 @@ TEST_F(XattrTest, XattrReadOnly_NoRandomSave) { EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallFailsWithErrno(EACCES)); + EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(EACCES)); char buf = '-'; EXPECT_THAT(getxattr(path, name, &buf, size), SyscallSucceedsWithValue(size)); EXPECT_EQ(buf, val); + + char list[sizeof(name)]; + EXPECT_THAT(listxattr(path, list, sizeof(list)), + SyscallSucceedsWithValue(sizeof(name))); + EXPECT_STREQ(list, name); } // Do not allow save/restore cycles after making the test file write-only, as // the restore will fail to open it with r/w permissions. TEST_F(XattrTest, XattrWriteOnly_NoRandomSave) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - // Drop capabilities that allow us to override file and directory permissions. ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); @@ -152,6 +150,14 @@ TEST_F(XattrTest, XattrWriteOnly_NoRandomSave) { EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(EACCES)); + + // listxattr will succeed even without read permissions. + char list[sizeof(name)]; + EXPECT_THAT(listxattr(path, list, sizeof(list)), + SyscallSucceedsWithValue(sizeof(name))); + EXPECT_STREQ(list, name); + + EXPECT_THAT(removexattr(path, name), SyscallSucceeds()); } TEST_F(XattrTest, XattrTrustedWithNonadmin) { @@ -163,64 +169,66 @@ TEST_F(XattrTest, XattrTrustedWithNonadmin) { const char name[] = "trusted.abc"; EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallFailsWithErrno(EPERM)); + EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(EPERM)); EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); } TEST_F(XattrTest, XattrOnDirectory) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); const char name[] = "user.test"; - EXPECT_THAT(setxattr(dir.path().c_str(), name, NULL, 0, /*flags=*/0), + EXPECT_THAT(setxattr(dir.path().c_str(), name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - EXPECT_THAT(getxattr(dir.path().c_str(), name, NULL, 0), + EXPECT_THAT(getxattr(dir.path().c_str(), name, nullptr, 0), SyscallSucceedsWithValue(0)); + + char list[sizeof(name)]; + EXPECT_THAT(listxattr(dir.path().c_str(), list, sizeof(list)), + SyscallSucceedsWithValue(sizeof(name))); + EXPECT_STREQ(list, name); + + EXPECT_THAT(removexattr(dir.path().c_str(), name), SyscallSucceeds()); } TEST_F(XattrTest, XattrOnSymlink) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); TempPath link = ASSERT_NO_ERRNO_AND_VALUE( TempPath::CreateSymlinkTo(dir.path(), test_file_name_)); const char name[] = "user.test"; - EXPECT_THAT(setxattr(link.path().c_str(), name, NULL, 0, /*flags=*/0), + EXPECT_THAT(setxattr(link.path().c_str(), name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - EXPECT_THAT(getxattr(link.path().c_str(), name, NULL, 0), + EXPECT_THAT(getxattr(link.path().c_str(), name, nullptr, 0), SyscallSucceedsWithValue(0)); + + char list[sizeof(name)]; + EXPECT_THAT(listxattr(link.path().c_str(), list, sizeof(list)), + SyscallSucceedsWithValue(sizeof(name))); + EXPECT_STREQ(list, name); + + EXPECT_THAT(removexattr(link.path().c_str(), name), SyscallSucceeds()); } TEST_F(XattrTest, XattrOnInvalidFileTypes) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char name[] = "user.test"; char char_device[] = "/dev/zero"; - EXPECT_THAT(setxattr(char_device, name, NULL, 0, /*flags=*/0), + EXPECT_THAT(setxattr(char_device, name, nullptr, 0, /*flags=*/0), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(getxattr(char_device, name, NULL, 0), + EXPECT_THAT(getxattr(char_device, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); + EXPECT_THAT(listxattr(char_device, nullptr, 0), SyscallSucceedsWithValue(0)); // Use tmpfs, where creation of named pipes is supported. const std::string fifo = NewTempAbsPathInDir("/dev/shm"); const char* path = fifo.c_str(); EXPECT_THAT(mknod(path, S_IFIFO | S_IRUSR | S_IWUSR, 0), SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name, NULL, 0, /*flags=*/0), + EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(getxattr(path, name, NULL, 0), SyscallFailsWithErrno(ENODATA)); + EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); + EXPECT_THAT(listxattr(path, nullptr, 0), SyscallSucceedsWithValue(0)); + EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(EPERM)); } TEST_F(XattrTest, SetxattrSizeSmallerThanValue) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; std::vector val = {'a', 'a'}; @@ -236,10 +244,6 @@ TEST_F(XattrTest, SetxattrSizeSmallerThanValue) { } TEST_F(XattrTest, SetxattrZeroSize) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; char val = 'a'; @@ -252,10 +256,6 @@ TEST_F(XattrTest, SetxattrZeroSize) { } TEST_F(XattrTest, SetxattrSizeTooLarge) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; @@ -271,10 +271,6 @@ TEST_F(XattrTest, SetxattrSizeTooLarge) { } TEST_F(XattrTest, SetxattrNullValueAndNonzeroSize) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; EXPECT_THAT(setxattr(path, name, nullptr, 1, /*flags=*/0), @@ -284,10 +280,6 @@ TEST_F(XattrTest, SetxattrNullValueAndNonzeroSize) { } TEST_F(XattrTest, SetxattrNullValueAndZeroSize) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); @@ -296,10 +288,6 @@ TEST_F(XattrTest, SetxattrNullValueAndZeroSize) { } TEST_F(XattrTest, SetxattrValueTooLargeButOKSize) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; std::vector val(XATTR_SIZE_MAX + 1); @@ -316,10 +304,6 @@ TEST_F(XattrTest, SetxattrValueTooLargeButOKSize) { } TEST_F(XattrTest, SetxattrReplaceWithSmaller) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; std::vector val = {'a', 'a'}; @@ -335,10 +319,6 @@ TEST_F(XattrTest, SetxattrReplaceWithSmaller) { } TEST_F(XattrTest, SetxattrReplaceWithLarger) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; std::vector val = {'a', 'a'}; @@ -353,10 +333,6 @@ TEST_F(XattrTest, SetxattrReplaceWithLarger) { } TEST_F(XattrTest, SetxattrCreateFlag) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_CREATE), @@ -368,10 +344,6 @@ TEST_F(XattrTest, SetxattrCreateFlag) { } TEST_F(XattrTest, SetxattrReplaceFlag) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_REPLACE), @@ -384,10 +356,6 @@ TEST_F(XattrTest, SetxattrReplaceFlag) { } TEST_F(XattrTest, SetxattrInvalidFlags) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); int invalid_flags = 0xff; EXPECT_THAT(setxattr(path, nullptr, nullptr, 0, invalid_flags), @@ -395,10 +363,6 @@ TEST_F(XattrTest, SetxattrInvalidFlags) { } TEST_F(XattrTest, Getxattr) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; int val = 1234; @@ -411,10 +375,6 @@ TEST_F(XattrTest, Getxattr) { } TEST_F(XattrTest, GetxattrSizeSmallerThanValue) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; std::vector val = {'a', 'a'}; @@ -427,10 +387,6 @@ TEST_F(XattrTest, GetxattrSizeSmallerThanValue) { } TEST_F(XattrTest, GetxattrSizeLargerThanValue) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; char val = 'a'; @@ -446,10 +402,6 @@ TEST_F(XattrTest, GetxattrSizeLargerThanValue) { } TEST_F(XattrTest, GetxattrZeroSize) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; char val = 'a'; @@ -463,10 +415,6 @@ TEST_F(XattrTest, GetxattrZeroSize) { } TEST_F(XattrTest, GetxattrSizeTooLarge) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; char val = 'a'; @@ -483,10 +431,6 @@ TEST_F(XattrTest, GetxattrSizeTooLarge) { } TEST_F(XattrTest, GetxattrNullValue) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; char val = 'a'; @@ -498,10 +442,6 @@ TEST_F(XattrTest, GetxattrNullValue) { } TEST_F(XattrTest, GetxattrNullValueAndZeroSize) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - const char* path = test_file_name_.c_str(); const char name[] = "user.test"; char val = 'a'; @@ -518,35 +458,109 @@ TEST_F(XattrTest, GetxattrNullValueAndZeroSize) { } TEST_F(XattrTest, GetxattrNonexistentName) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); + const char* path = test_file_name_.c_str(); + const char name[] = "user.test"; + EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); +} + +TEST_F(XattrTest, Listxattr) { + const char* path = test_file_name_.c_str(); + const std::string name = "user.test"; + const std::string name2 = "user.test2"; + const std::string name3 = "user.test3"; + EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0), + SyscallSucceeds()); + EXPECT_THAT(setxattr(path, name2.c_str(), nullptr, 0, /*flags=*/0), + SyscallSucceeds()); + EXPECT_THAT(setxattr(path, name3.c_str(), nullptr, 0, /*flags=*/0), + SyscallSucceeds()); + std::vector list(name.size() + 1 + name2.size() + 1 + name3.size() + 1); + char* buf = list.data(); + EXPECT_THAT(listxattr(path, buf, XATTR_SIZE_MAX), + SyscallSucceedsWithValue(list.size())); + + absl::flat_hash_set got = {}; + for (char* p = buf; p < buf + list.size(); p += strlen(p) + 1) { + got.insert(std::string{p}); + } + + absl::flat_hash_set expected = {name, name2, name3}; + EXPECT_EQ(got, expected); +} + +TEST_F(XattrTest, ListxattrNoXattrs) { + const char* path = test_file_name_.c_str(); + + std::vector list, expected; + EXPECT_THAT(listxattr(path, list.data(), sizeof(list)), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(list, expected); + + // Listxattr should succeed if there are no attributes, even if the buffer + // passed in is a nullptr. + EXPECT_THAT(listxattr(path, nullptr, sizeof(list)), + SyscallSucceedsWithValue(0)); +} + +TEST_F(XattrTest, ListxattrNullBuffer) { const char* path = test_file_name_.c_str(); const char name[] = "user.test"; + EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); + + EXPECT_THAT(listxattr(path, nullptr, sizeof(name)), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(XattrTest, ListxattrSizeTooSmall) { + const char* path = test_file_name_.c_str(); + const char name[] = "user.test"; + EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); + + char list[sizeof(name) - 1]; + EXPECT_THAT(listxattr(path, list, sizeof(list)), + SyscallFailsWithErrno(ERANGE)); +} + +TEST_F(XattrTest, ListxattrZeroSize) { + const char* path = test_file_name_.c_str(); + const char name[] = "user.test"; + EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); + EXPECT_THAT(listxattr(path, nullptr, 0), + SyscallSucceedsWithValue(sizeof(name))); +} + +TEST_F(XattrTest, RemoveXattr) { + const char* path = test_file_name_.c_str(); + const char name[] = "user.test"; + EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); + EXPECT_THAT(removexattr(path, name), SyscallSucceeds()); EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); } -TEST_F(XattrTest, LGetSetxattrOnSymlink) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); +TEST_F(XattrTest, RemoveXattrNonexistentName) { + const char* path = test_file_name_.c_str(); + const char name[] = "user.test"; + EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(ENODATA)); +} +TEST_F(XattrTest, LXattrOnSymlink) { + const char name[] = "user.test"; TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); TempPath link = ASSERT_NO_ERRNO_AND_VALUE( TempPath::CreateSymlinkTo(dir.path(), test_file_name_)); - EXPECT_THAT(lsetxattr(link.path().c_str(), nullptr, nullptr, 0, 0), + EXPECT_THAT(lsetxattr(link.path().c_str(), name, nullptr, 0, 0), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(lgetxattr(link.path().c_str(), nullptr, nullptr, 0), + EXPECT_THAT(lgetxattr(link.path().c_str(), name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); + EXPECT_THAT(llistxattr(link.path().c_str(), nullptr, 0), + SyscallSucceedsWithValue(0)); + EXPECT_THAT(lremovexattr(link.path().c_str(), name), + SyscallFailsWithErrno(EPERM)); } -TEST_F(XattrTest, LGetSetxattrOnNonsymlink) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); - +TEST_F(XattrTest, LXattrOnNonsymlink) { const char* path = test_file_name_.c_str(); const char name[] = "user.test"; int val = 1234; @@ -558,13 +572,16 @@ TEST_F(XattrTest, LGetSetxattrOnNonsymlink) { EXPECT_THAT(lgetxattr(path, name, &buf, size), SyscallSucceedsWithValue(size)); EXPECT_EQ(buf, val); -} -TEST_F(XattrTest, FGetSetxattr) { - // TODO(gvisor.dev/issue/1636): Re-enable once list/remove xattr are - // supported, and get/set have been added pack to the syscall table. - SKIP_IF(IsRunningOnGvisor()); + char list[sizeof(name)]; + EXPECT_THAT(llistxattr(path, list, sizeof(list)), + SyscallSucceedsWithValue(sizeof(name))); + EXPECT_STREQ(list, name); + EXPECT_THAT(lremovexattr(path, name), SyscallSucceeds()); +} + +TEST_F(XattrTest, XattrWithFD) { const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_.c_str(), 0)); const char name[] = "user.test"; @@ -577,6 +594,13 @@ TEST_F(XattrTest, FGetSetxattr) { EXPECT_THAT(fgetxattr(fd.get(), name, &buf, size), SyscallSucceedsWithValue(size)); EXPECT_EQ(buf, val); + + char list[sizeof(name)]; + EXPECT_THAT(flistxattr(fd.get(), list, sizeof(list)), + SyscallSucceedsWithValue(sizeof(name))); + EXPECT_STREQ(list, name); + + EXPECT_THAT(fremovexattr(fd.get(), name), SyscallSucceeds()); } } // namespace -- cgit v1.2.3 From 50c493193b72997a6b09f353fd9217349941c494 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Fri, 14 Feb 2020 12:22:25 -0800 Subject: Un-export p9 message encode/decode functions. These are not used outside of the p9 package. PiperOrigin-RevId: 295200052 --- pkg/p9/buffer.go | 10 +- pkg/p9/messages.go | 672 +++++++++++++++++++++---------------------- pkg/p9/messages_test.go | 4 +- pkg/p9/p9.go | 68 ++--- pkg/p9/transport.go | 4 +- pkg/p9/transport_flipcall.go | 4 +- pkg/p9/transport_test.go | 8 +- 7 files changed, 385 insertions(+), 385 deletions(-) (limited to 'pkg/p9/messages.go') diff --git a/pkg/p9/buffer.go b/pkg/p9/buffer.go index 249536d8a..6a4951821 100644 --- a/pkg/p9/buffer.go +++ b/pkg/p9/buffer.go @@ -20,16 +20,16 @@ import ( // encoder is used for messages and 9P primitives. type encoder interface { - // Decode decodes from the given buffer. Decode may be called more than once + // decode decodes from the given buffer. decode may be called more than once // to reuse the instance. It must clear any previous state. // // This may not fail, exhaustion will be recorded in the buffer. - Decode(b *buffer) + decode(b *buffer) - // Encode encodes to the given buffer. + // encode encodes to the given buffer. // // This may not fail. - Encode(b *buffer) + encode(b *buffer) } // order is the byte order used for encoding. @@ -39,7 +39,7 @@ var order = binary.LittleEndian // // This is passed to the encoder methods. type buffer struct { - // data is the underlying data. This may grow during Encode. + // data is the underlying data. This may grow during encode. data []byte // overflow indicates whether an overflow has occurred. diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go index b1cede5f5..3863ad1f5 100644 --- a/pkg/p9/messages.go +++ b/pkg/p9/messages.go @@ -51,7 +51,7 @@ type payloader interface { // SetPayload returns the decoded message. // // This is going to be total message size - FixedSize. But this should - // be validated during Decode, which will be called after SetPayload. + // be validated during decode, which will be called after SetPayload. SetPayload([]byte) } @@ -90,14 +90,14 @@ type Tversion struct { Version string } -// Decode implements encoder.Decode. -func (t *Tversion) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tversion) decode(b *buffer) { t.MSize = b.Read32() t.Version = b.ReadString() } -// Encode implements encoder.Encode. -func (t *Tversion) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tversion) encode(b *buffer) { b.Write32(t.MSize) b.WriteString(t.Version) } @@ -121,14 +121,14 @@ type Rversion struct { Version string } -// Decode implements encoder.Decode. -func (r *Rversion) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rversion) decode(b *buffer) { r.MSize = b.Read32() r.Version = b.ReadString() } -// Encode implements encoder.Encode. -func (r *Rversion) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rversion) encode(b *buffer) { b.Write32(r.MSize) b.WriteString(r.Version) } @@ -149,13 +149,13 @@ type Tflush struct { OldTag Tag } -// Decode implements encoder.Decode. -func (t *Tflush) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tflush) decode(b *buffer) { t.OldTag = b.ReadTag() } -// Encode implements encoder.Encode. -func (t *Tflush) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tflush) encode(b *buffer) { b.WriteTag(t.OldTag) } @@ -173,12 +173,12 @@ func (t *Tflush) String() string { type Rflush struct { } -// Decode implements encoder.Decode. -func (*Rflush) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rflush) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rflush) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rflush) encode(*buffer) { } // Type implements message.Type. @@ -203,8 +203,8 @@ type Twalk struct { Names []string } -// Decode implements encoder.Decode. -func (t *Twalk) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Twalk) decode(b *buffer) { t.FID = b.ReadFID() t.NewFID = b.ReadFID() n := b.Read16() @@ -214,8 +214,8 @@ func (t *Twalk) Decode(b *buffer) { } } -// Encode implements encoder.Encode. -func (t *Twalk) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Twalk) encode(b *buffer) { b.WriteFID(t.FID) b.WriteFID(t.NewFID) b.Write16(uint16(len(t.Names))) @@ -240,22 +240,22 @@ type Rwalk struct { QIDs []QID } -// Decode implements encoder.Decode. -func (r *Rwalk) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rwalk) decode(b *buffer) { n := b.Read16() r.QIDs = r.QIDs[:0] for i := 0; i < int(n); i++ { var q QID - q.Decode(b) + q.decode(b) r.QIDs = append(r.QIDs, q) } } -// Encode implements encoder.Encode. -func (r *Rwalk) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rwalk) encode(b *buffer) { b.Write16(uint16(len(r.QIDs))) for _, q := range r.QIDs { - q.Encode(b) + q.encode(b) } } @@ -275,13 +275,13 @@ type Tclunk struct { FID FID } -// Decode implements encoder.Decode. -func (t *Tclunk) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tclunk) decode(b *buffer) { t.FID = b.ReadFID() } -// Encode implements encoder.Encode. -func (t *Tclunk) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tclunk) encode(b *buffer) { b.WriteFID(t.FID) } @@ -299,12 +299,12 @@ func (t *Tclunk) String() string { type Rclunk struct { } -// Decode implements encoder.Decode. -func (*Rclunk) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rclunk) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rclunk) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rclunk) encode(*buffer) { } // Type implements message.Type. @@ -325,13 +325,13 @@ type Tremove struct { FID FID } -// Decode implements encoder.Decode. -func (t *Tremove) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tremove) decode(b *buffer) { t.FID = b.ReadFID() } -// Encode implements encoder.Encode. -func (t *Tremove) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tremove) encode(b *buffer) { b.WriteFID(t.FID) } @@ -349,12 +349,12 @@ func (t *Tremove) String() string { type Rremove struct { } -// Decode implements encoder.Decode. -func (*Rremove) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rremove) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rremove) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rremove) encode(*buffer) { } // Type implements message.Type. @@ -374,13 +374,13 @@ type Rlerror struct { Error uint32 } -// Decode implements encoder.Decode. -func (r *Rlerror) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rlerror) decode(b *buffer) { r.Error = b.Read32() } -// Encode implements encoder.Encode. -func (r *Rlerror) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rlerror) encode(b *buffer) { b.Write32(r.Error) } @@ -409,16 +409,16 @@ type Tauth struct { UID UID } -// Decode implements encoder.Decode. -func (t *Tauth) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tauth) decode(b *buffer) { t.AuthenticationFID = b.ReadFID() t.UserName = b.ReadString() t.AttachName = b.ReadString() t.UID = b.ReadUID() } -// Encode implements encoder.Encode. -func (t *Tauth) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tauth) encode(b *buffer) { b.WriteFID(t.AuthenticationFID) b.WriteString(t.UserName) b.WriteString(t.AttachName) @@ -437,7 +437,7 @@ func (t *Tauth) String() string { // Rauth is an authentication response. // -// Encode, Decode and Length are inherited directly from QID. +// encode and decode are inherited directly from QID. type Rauth struct { QID } @@ -463,16 +463,16 @@ type Tattach struct { Auth Tauth } -// Decode implements encoder.Decode. -func (t *Tattach) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tattach) decode(b *buffer) { t.FID = b.ReadFID() - t.Auth.Decode(b) + t.Auth.decode(b) } -// Encode implements encoder.Encode. -func (t *Tattach) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tattach) encode(b *buffer) { b.WriteFID(t.FID) - t.Auth.Encode(b) + t.Auth.encode(b) } // Type implements message.Type. @@ -509,14 +509,14 @@ type Tlopen struct { Flags OpenFlags } -// Decode implements encoder.Decode. -func (t *Tlopen) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tlopen) decode(b *buffer) { t.FID = b.ReadFID() t.Flags = b.ReadOpenFlags() } -// Encode implements encoder.Encode. -func (t *Tlopen) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tlopen) encode(b *buffer) { b.WriteFID(t.FID) b.WriteOpenFlags(t.Flags) } @@ -542,15 +542,15 @@ type Rlopen struct { filePayload } -// Decode implements encoder.Decode. -func (r *Rlopen) Decode(b *buffer) { - r.QID.Decode(b) +// decode implements encoder.decode. +func (r *Rlopen) decode(b *buffer) { + r.QID.decode(b) r.IoUnit = b.Read32() } -// Encode implements encoder.Encode. -func (r *Rlopen) Encode(b *buffer) { - r.QID.Encode(b) +// encode implements encoder.encode. +func (r *Rlopen) encode(b *buffer) { + r.QID.encode(b) b.Write32(r.IoUnit) } @@ -587,8 +587,8 @@ type Tlcreate struct { GID GID } -// Decode implements encoder.Decode. -func (t *Tlcreate) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tlcreate) decode(b *buffer) { t.FID = b.ReadFID() t.Name = b.ReadString() t.OpenFlags = b.ReadOpenFlags() @@ -596,8 +596,8 @@ func (t *Tlcreate) Decode(b *buffer) { t.GID = b.ReadGID() } -// Encode implements encoder.Encode. -func (t *Tlcreate) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tlcreate) encode(b *buffer) { b.WriteFID(t.FID) b.WriteString(t.Name) b.WriteOpenFlags(t.OpenFlags) @@ -617,7 +617,7 @@ func (t *Tlcreate) String() string { // Rlcreate is a create response. // -// The Encode, Decode, etc. methods are inherited from Rlopen. +// The encode, decode, etc. methods are inherited from Rlopen. type Rlcreate struct { Rlopen } @@ -647,16 +647,16 @@ type Tsymlink struct { GID GID } -// Decode implements encoder.Decode. -func (t *Tsymlink) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tsymlink) decode(b *buffer) { t.Directory = b.ReadFID() t.Name = b.ReadString() t.Target = b.ReadString() t.GID = b.ReadGID() } -// Encode implements encoder.Encode. -func (t *Tsymlink) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tsymlink) encode(b *buffer) { b.WriteFID(t.Directory) b.WriteString(t.Name) b.WriteString(t.Target) @@ -679,14 +679,14 @@ type Rsymlink struct { QID QID } -// Decode implements encoder.Decode. -func (r *Rsymlink) Decode(b *buffer) { - r.QID.Decode(b) +// decode implements encoder.decode. +func (r *Rsymlink) decode(b *buffer) { + r.QID.decode(b) } -// Encode implements encoder.Encode. -func (r *Rsymlink) Encode(b *buffer) { - r.QID.Encode(b) +// encode implements encoder.encode. +func (r *Rsymlink) encode(b *buffer) { + r.QID.encode(b) } // Type implements message.Type. @@ -711,15 +711,15 @@ type Tlink struct { Name string } -// Decode implements encoder.Decode. -func (t *Tlink) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tlink) decode(b *buffer) { t.Directory = b.ReadFID() t.Target = b.ReadFID() t.Name = b.ReadString() } -// Encode implements encoder.Encode. -func (t *Tlink) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tlink) encode(b *buffer) { b.WriteFID(t.Directory) b.WriteFID(t.Target) b.WriteString(t.Name) @@ -744,12 +744,12 @@ func (*Rlink) Type() MsgType { return MsgRlink } -// Decode implements encoder.Decode. -func (*Rlink) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rlink) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rlink) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rlink) encode(*buffer) { } // String implements fmt.Stringer. @@ -772,16 +772,16 @@ type Trenameat struct { NewName string } -// Decode implements encoder.Decode. -func (t *Trenameat) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Trenameat) decode(b *buffer) { t.OldDirectory = b.ReadFID() t.OldName = b.ReadString() t.NewDirectory = b.ReadFID() t.NewName = b.ReadString() } -// Encode implements encoder.Encode. -func (t *Trenameat) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Trenameat) encode(b *buffer) { b.WriteFID(t.OldDirectory) b.WriteString(t.OldName) b.WriteFID(t.NewDirectory) @@ -802,12 +802,12 @@ func (t *Trenameat) String() string { type Rrenameat struct { } -// Decode implements encoder.Decode. -func (*Rrenameat) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rrenameat) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rrenameat) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rrenameat) encode(*buffer) { } // Type implements message.Type. @@ -832,15 +832,15 @@ type Tunlinkat struct { Flags uint32 } -// Decode implements encoder.Decode. -func (t *Tunlinkat) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tunlinkat) decode(b *buffer) { t.Directory = b.ReadFID() t.Name = b.ReadString() t.Flags = b.Read32() } -// Encode implements encoder.Encode. -func (t *Tunlinkat) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tunlinkat) encode(b *buffer) { b.WriteFID(t.Directory) b.WriteString(t.Name) b.Write32(t.Flags) @@ -860,12 +860,12 @@ func (t *Tunlinkat) String() string { type Runlinkat struct { } -// Decode implements encoder.Decode. -func (*Runlinkat) Decode(*buffer) { +// decode implements encoder.decode. +func (*Runlinkat) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Runlinkat) Encode(*buffer) { +// encode implements encoder.encode. +func (*Runlinkat) encode(*buffer) { } // Type implements message.Type. @@ -893,15 +893,15 @@ type Trename struct { Name string } -// Decode implements encoder.Decode. -func (t *Trename) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Trename) decode(b *buffer) { t.FID = b.ReadFID() t.Directory = b.ReadFID() t.Name = b.ReadString() } -// Encode implements encoder.Encode. -func (t *Trename) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Trename) encode(b *buffer) { b.WriteFID(t.FID) b.WriteFID(t.Directory) b.WriteString(t.Name) @@ -921,12 +921,12 @@ func (t *Trename) String() string { type Rrename struct { } -// Decode implements encoder.Decode. -func (*Rrename) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rrename) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rrename) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rrename) encode(*buffer) { } // Type implements message.Type. @@ -945,13 +945,13 @@ type Treadlink struct { FID FID } -// Decode implements encoder.Decode. -func (t *Treadlink) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Treadlink) decode(b *buffer) { t.FID = b.ReadFID() } -// Encode implements encoder.Encode. -func (t *Treadlink) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Treadlink) encode(b *buffer) { b.WriteFID(t.FID) } @@ -971,13 +971,13 @@ type Rreadlink struct { Target string } -// Decode implements encoder.Decode. -func (r *Rreadlink) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rreadlink) decode(b *buffer) { r.Target = b.ReadString() } -// Encode implements encoder.Encode. -func (r *Rreadlink) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rreadlink) encode(b *buffer) { b.WriteString(r.Target) } @@ -1003,15 +1003,15 @@ type Tread struct { Count uint32 } -// Decode implements encoder.Decode. -func (t *Tread) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tread) decode(b *buffer) { t.FID = b.ReadFID() t.Offset = b.Read64() t.Count = b.Read32() } -// Encode implements encoder.Encode. -func (t *Tread) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tread) encode(b *buffer) { b.WriteFID(t.FID) b.Write64(t.Offset) b.Write32(t.Count) @@ -1033,20 +1033,20 @@ type Rread struct { Data []byte } -// Decode implements encoder.Decode. +// decode implements encoder.decode. // // Data is automatically decoded via Payload. -func (r *Rread) Decode(b *buffer) { +func (r *Rread) decode(b *buffer) { count := b.Read32() if count != uint32(len(r.Data)) { b.markOverrun() } } -// Encode implements encoder.Encode. +// encode implements encoder.encode. // // Data is automatically encoded via Payload. -func (r *Rread) Encode(b *buffer) { +func (r *Rread) encode(b *buffer) { b.Write32(uint32(len(r.Data))) } @@ -1087,8 +1087,8 @@ type Twrite struct { Data []byte } -// Decode implements encoder.Decode. -func (t *Twrite) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Twrite) decode(b *buffer) { t.FID = b.ReadFID() t.Offset = b.Read64() count := b.Read32() @@ -1097,10 +1097,10 @@ func (t *Twrite) Decode(b *buffer) { } } -// Encode implements encoder.Encode. +// encode implements encoder.encode. // // This uses the buffer payload to avoid a copy. -func (t *Twrite) Encode(b *buffer) { +func (t *Twrite) encode(b *buffer) { b.WriteFID(t.FID) b.Write64(t.Offset) b.Write32(uint32(len(t.Data))) @@ -1137,13 +1137,13 @@ type Rwrite struct { Count uint32 } -// Decode implements encoder.Decode. -func (r *Rwrite) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rwrite) decode(b *buffer) { r.Count = b.Read32() } -// Encode implements encoder.Encode. -func (r *Rwrite) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rwrite) encode(b *buffer) { b.Write32(r.Count) } @@ -1178,8 +1178,8 @@ type Tmknod struct { GID GID } -// Decode implements encoder.Decode. -func (t *Tmknod) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tmknod) decode(b *buffer) { t.Directory = b.ReadFID() t.Name = b.ReadString() t.Mode = b.ReadFileMode() @@ -1188,8 +1188,8 @@ func (t *Tmknod) Decode(b *buffer) { t.GID = b.ReadGID() } -// Encode implements encoder.Encode. -func (t *Tmknod) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tmknod) encode(b *buffer) { b.WriteFID(t.Directory) b.WriteString(t.Name) b.WriteFileMode(t.Mode) @@ -1214,14 +1214,14 @@ type Rmknod struct { QID QID } -// Decode implements encoder.Decode. -func (r *Rmknod) Decode(b *buffer) { - r.QID.Decode(b) +// decode implements encoder.decode. +func (r *Rmknod) decode(b *buffer) { + r.QID.decode(b) } -// Encode implements encoder.Encode. -func (r *Rmknod) Encode(b *buffer) { - r.QID.Encode(b) +// encode implements encoder.encode. +func (r *Rmknod) encode(b *buffer) { + r.QID.encode(b) } // Type implements message.Type. @@ -1249,16 +1249,16 @@ type Tmkdir struct { GID GID } -// Decode implements encoder.Decode. -func (t *Tmkdir) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tmkdir) decode(b *buffer) { t.Directory = b.ReadFID() t.Name = b.ReadString() t.Permissions = b.ReadPermissions() t.GID = b.ReadGID() } -// Encode implements encoder.Encode. -func (t *Tmkdir) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tmkdir) encode(b *buffer) { b.WriteFID(t.Directory) b.WriteString(t.Name) b.WritePermissions(t.Permissions) @@ -1281,14 +1281,14 @@ type Rmkdir struct { QID QID } -// Decode implements encoder.Decode. -func (r *Rmkdir) Decode(b *buffer) { - r.QID.Decode(b) +// decode implements encoder.decode. +func (r *Rmkdir) decode(b *buffer) { + r.QID.decode(b) } -// Encode implements encoder.Encode. -func (r *Rmkdir) Encode(b *buffer) { - r.QID.Encode(b) +// encode implements encoder.encode. +func (r *Rmkdir) encode(b *buffer) { + r.QID.encode(b) } // Type implements message.Type. @@ -1310,16 +1310,16 @@ type Tgetattr struct { AttrMask AttrMask } -// Decode implements encoder.Decode. -func (t *Tgetattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tgetattr) decode(b *buffer) { t.FID = b.ReadFID() - t.AttrMask.Decode(b) + t.AttrMask.decode(b) } -// Encode implements encoder.Encode. -func (t *Tgetattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tgetattr) encode(b *buffer) { b.WriteFID(t.FID) - t.AttrMask.Encode(b) + t.AttrMask.encode(b) } // Type implements message.Type. @@ -1344,18 +1344,18 @@ type Rgetattr struct { Attr Attr } -// Decode implements encoder.Decode. -func (r *Rgetattr) Decode(b *buffer) { - r.Valid.Decode(b) - r.QID.Decode(b) - r.Attr.Decode(b) +// decode implements encoder.decode. +func (r *Rgetattr) decode(b *buffer) { + r.Valid.decode(b) + r.QID.decode(b) + r.Attr.decode(b) } -// Encode implements encoder.Encode. -func (r *Rgetattr) Encode(b *buffer) { - r.Valid.Encode(b) - r.QID.Encode(b) - r.Attr.Encode(b) +// encode implements encoder.encode. +func (r *Rgetattr) encode(b *buffer) { + r.Valid.encode(b) + r.QID.encode(b) + r.Attr.encode(b) } // Type implements message.Type. @@ -1380,18 +1380,18 @@ type Tsetattr struct { SetAttr SetAttr } -// Decode implements encoder.Decode. -func (t *Tsetattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tsetattr) decode(b *buffer) { t.FID = b.ReadFID() - t.Valid.Decode(b) - t.SetAttr.Decode(b) + t.Valid.decode(b) + t.SetAttr.decode(b) } -// Encode implements encoder.Encode. -func (t *Tsetattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tsetattr) encode(b *buffer) { b.WriteFID(t.FID) - t.Valid.Encode(b) - t.SetAttr.Encode(b) + t.Valid.encode(b) + t.SetAttr.encode(b) } // Type implements message.Type. @@ -1408,12 +1408,12 @@ func (t *Tsetattr) String() string { type Rsetattr struct { } -// Decode implements encoder.Decode. -func (*Rsetattr) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rsetattr) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rsetattr) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rsetattr) encode(*buffer) { } // Type implements message.Type. @@ -1435,18 +1435,18 @@ type Tallocate struct { Length uint64 } -// Decode implements encoder.Decode. -func (t *Tallocate) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tallocate) decode(b *buffer) { t.FID = b.ReadFID() - t.Mode.Decode(b) + t.Mode.decode(b) t.Offset = b.Read64() t.Length = b.Read64() } -// Encode implements encoder.Encode. -func (t *Tallocate) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tallocate) encode(b *buffer) { b.WriteFID(t.FID) - t.Mode.Encode(b) + t.Mode.encode(b) b.Write64(t.Offset) b.Write64(t.Length) } @@ -1465,12 +1465,12 @@ func (t *Tallocate) String() string { type Rallocate struct { } -// Decode implements encoder.Decode. -func (*Rallocate) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rallocate) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rallocate) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rallocate) encode(*buffer) { } // Type implements message.Type. @@ -1492,14 +1492,14 @@ type Tlistxattr struct { Size uint64 } -// Decode implements encoder.Decode. -func (t *Tlistxattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tlistxattr) decode(b *buffer) { t.FID = b.ReadFID() t.Size = b.Read64() } -// Encode implements encoder.Encode. -func (t *Tlistxattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tlistxattr) encode(b *buffer) { b.WriteFID(t.FID) b.Write64(t.Size) } @@ -1520,8 +1520,8 @@ type Rlistxattr struct { Xattrs []string } -// Decode implements encoder.Decode. -func (r *Rlistxattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rlistxattr) decode(b *buffer) { n := b.Read16() r.Xattrs = r.Xattrs[:0] for i := 0; i < int(n); i++ { @@ -1529,8 +1529,8 @@ func (r *Rlistxattr) Decode(b *buffer) { } } -// Encode implements encoder.Encode. -func (r *Rlistxattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rlistxattr) encode(b *buffer) { b.Write16(uint16(len(r.Xattrs))) for _, x := range r.Xattrs { b.WriteString(x) @@ -1559,15 +1559,15 @@ type Txattrwalk struct { Name string } -// Decode implements encoder.Decode. -func (t *Txattrwalk) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Txattrwalk) decode(b *buffer) { t.FID = b.ReadFID() t.NewFID = b.ReadFID() t.Name = b.ReadString() } -// Encode implements encoder.Encode. -func (t *Txattrwalk) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Txattrwalk) encode(b *buffer) { b.WriteFID(t.FID) b.WriteFID(t.NewFID) b.WriteString(t.Name) @@ -1589,13 +1589,13 @@ type Rxattrwalk struct { Size uint64 } -// Decode implements encoder.Decode. -func (r *Rxattrwalk) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rxattrwalk) decode(b *buffer) { r.Size = b.Read64() } -// Encode implements encoder.Encode. -func (r *Rxattrwalk) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rxattrwalk) encode(b *buffer) { b.Write64(r.Size) } @@ -1627,16 +1627,16 @@ type Txattrcreate struct { Flags uint32 } -// Decode implements encoder.Decode. -func (t *Txattrcreate) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Txattrcreate) decode(b *buffer) { t.FID = b.ReadFID() t.Name = b.ReadString() t.AttrSize = b.Read64() t.Flags = b.Read32() } -// Encode implements encoder.Encode. -func (t *Txattrcreate) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Txattrcreate) encode(b *buffer) { b.WriteFID(t.FID) b.WriteString(t.Name) b.Write64(t.AttrSize) @@ -1657,12 +1657,12 @@ func (t *Txattrcreate) String() string { type Rxattrcreate struct { } -// Decode implements encoder.Decode. -func (r *Rxattrcreate) Decode(*buffer) { +// decode implements encoder.decode. +func (r *Rxattrcreate) decode(*buffer) { } -// Encode implements encoder.Encode. -func (r *Rxattrcreate) Encode(*buffer) { +// encode implements encoder.encode. +func (r *Rxattrcreate) encode(*buffer) { } // Type implements message.Type. @@ -1687,15 +1687,15 @@ type Tgetxattr struct { Size uint64 } -// Decode implements encoder.Decode. -func (t *Tgetxattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tgetxattr) decode(b *buffer) { t.FID = b.ReadFID() t.Name = b.ReadString() t.Size = b.Read64() } -// Encode implements encoder.Encode. -func (t *Tgetxattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tgetxattr) encode(b *buffer) { b.WriteFID(t.FID) b.WriteString(t.Name) b.Write64(t.Size) @@ -1717,13 +1717,13 @@ type Rgetxattr struct { Value string } -// Decode implements encoder.Decode. -func (r *Rgetxattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rgetxattr) decode(b *buffer) { r.Value = b.ReadString() } -// Encode implements encoder.Encode. -func (r *Rgetxattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rgetxattr) encode(b *buffer) { b.WriteString(r.Value) } @@ -1752,16 +1752,16 @@ type Tsetxattr struct { Flags uint32 } -// Decode implements encoder.Decode. -func (t *Tsetxattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tsetxattr) decode(b *buffer) { t.FID = b.ReadFID() t.Name = b.ReadString() t.Value = b.ReadString() t.Flags = b.Read32() } -// Encode implements encoder.Encode. -func (t *Tsetxattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tsetxattr) encode(b *buffer) { b.WriteFID(t.FID) b.WriteString(t.Name) b.WriteString(t.Value) @@ -1782,12 +1782,12 @@ func (t *Tsetxattr) String() string { type Rsetxattr struct { } -// Decode implements encoder.Decode. -func (r *Rsetxattr) Decode(*buffer) { +// decode implements encoder.decode. +func (r *Rsetxattr) decode(*buffer) { } -// Encode implements encoder.Encode. -func (r *Rsetxattr) Encode(*buffer) { +// encode implements encoder.encode. +func (r *Rsetxattr) encode(*buffer) { } // Type implements message.Type. @@ -1809,14 +1809,14 @@ type Tremovexattr struct { Name string } -// Decode implements encoder.Decode. -func (t *Tremovexattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tremovexattr) decode(b *buffer) { t.FID = b.ReadFID() t.Name = b.ReadString() } -// Encode implements encoder.Encode. -func (t *Tremovexattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tremovexattr) encode(b *buffer) { b.WriteFID(t.FID) b.WriteString(t.Name) } @@ -1835,12 +1835,12 @@ func (t *Tremovexattr) String() string { type Rremovexattr struct { } -// Decode implements encoder.Decode. -func (r *Rremovexattr) Decode(*buffer) { +// decode implements encoder.decode. +func (r *Rremovexattr) decode(*buffer) { } -// Encode implements encoder.Encode. -func (r *Rremovexattr) Encode(*buffer) { +// encode implements encoder.encode. +func (r *Rremovexattr) encode(*buffer) { } // Type implements message.Type. @@ -1865,15 +1865,15 @@ type Treaddir struct { Count uint32 } -// Decode implements encoder.Decode. -func (t *Treaddir) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Treaddir) decode(b *buffer) { t.Directory = b.ReadFID() t.Offset = b.Read64() t.Count = b.Read32() } -// Encode implements encoder.Encode. -func (t *Treaddir) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Treaddir) encode(b *buffer) { b.WriteFID(t.Directory) b.Write64(t.Offset) b.Write32(t.Count) @@ -1907,14 +1907,14 @@ type Rreaddir struct { payload []byte } -// Decode implements encoder.Decode. -func (r *Rreaddir) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rreaddir) decode(b *buffer) { r.Count = b.Read32() entriesBuf := buffer{data: r.payload} r.Entries = r.Entries[:0] for { var d Dirent - d.Decode(&entriesBuf) + d.decode(&entriesBuf) if entriesBuf.isOverrun() { // Couldn't decode a complete entry. break @@ -1923,11 +1923,11 @@ func (r *Rreaddir) Decode(b *buffer) { } } -// Encode implements encoder.Encode. -func (r *Rreaddir) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rreaddir) encode(b *buffer) { entriesBuf := buffer{} for _, d := range r.Entries { - d.Encode(&entriesBuf) + d.encode(&entriesBuf) if len(entriesBuf.data) >= int(r.Count) { break } @@ -1972,13 +1972,13 @@ type Tfsync struct { FID FID } -// Decode implements encoder.Decode. -func (t *Tfsync) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tfsync) decode(b *buffer) { t.FID = b.ReadFID() } -// Encode implements encoder.Encode. -func (t *Tfsync) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tfsync) encode(b *buffer) { b.WriteFID(t.FID) } @@ -1996,12 +1996,12 @@ func (t *Tfsync) String() string { type Rfsync struct { } -// Decode implements encoder.Decode. -func (*Rfsync) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rfsync) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rfsync) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rfsync) encode(*buffer) { } // Type implements message.Type. @@ -2020,13 +2020,13 @@ type Tstatfs struct { FID FID } -// Decode implements encoder.Decode. -func (t *Tstatfs) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tstatfs) decode(b *buffer) { t.FID = b.ReadFID() } -// Encode implements encoder.Encode. -func (t *Tstatfs) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tstatfs) encode(b *buffer) { b.WriteFID(t.FID) } @@ -2046,14 +2046,14 @@ type Rstatfs struct { FSStat FSStat } -// Decode implements encoder.Decode. -func (r *Rstatfs) Decode(b *buffer) { - r.FSStat.Decode(b) +// decode implements encoder.decode. +func (r *Rstatfs) decode(b *buffer) { + r.FSStat.decode(b) } -// Encode implements encoder.Encode. -func (r *Rstatfs) Encode(b *buffer) { - r.FSStat.Encode(b) +// encode implements encoder.encode. +func (r *Rstatfs) encode(b *buffer) { + r.FSStat.encode(b) } // Type implements message.Type. @@ -2072,13 +2072,13 @@ type Tflushf struct { FID FID } -// Decode implements encoder.Decode. -func (t *Tflushf) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tflushf) decode(b *buffer) { t.FID = b.ReadFID() } -// Encode implements encoder.Encode. -func (t *Tflushf) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tflushf) encode(b *buffer) { b.WriteFID(t.FID) } @@ -2096,12 +2096,12 @@ func (t *Tflushf) String() string { type Rflushf struct { } -// Decode implements encoder.Decode. -func (*Rflushf) Decode(*buffer) { +// decode implements encoder.decode. +func (*Rflushf) decode(*buffer) { } -// Encode implements encoder.Encode. -func (*Rflushf) Encode(*buffer) { +// encode implements encoder.encode. +func (*Rflushf) encode(*buffer) { } // Type implements message.Type. @@ -2126,8 +2126,8 @@ type Twalkgetattr struct { Names []string } -// Decode implements encoder.Decode. -func (t *Twalkgetattr) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Twalkgetattr) decode(b *buffer) { t.FID = b.ReadFID() t.NewFID = b.ReadFID() n := b.Read16() @@ -2137,8 +2137,8 @@ func (t *Twalkgetattr) Decode(b *buffer) { } } -// Encode implements encoder.Encode. -func (t *Twalkgetattr) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Twalkgetattr) encode(b *buffer) { b.WriteFID(t.FID) b.WriteFID(t.NewFID) b.Write16(uint16(len(t.Names))) @@ -2169,26 +2169,26 @@ type Rwalkgetattr struct { QIDs []QID } -// Decode implements encoder.Decode. -func (r *Rwalkgetattr) Decode(b *buffer) { - r.Valid.Decode(b) - r.Attr.Decode(b) +// decode implements encoder.decode. +func (r *Rwalkgetattr) decode(b *buffer) { + r.Valid.decode(b) + r.Attr.decode(b) n := b.Read16() r.QIDs = r.QIDs[:0] for i := 0; i < int(n); i++ { var q QID - q.Decode(b) + q.decode(b) r.QIDs = append(r.QIDs, q) } } -// Encode implements encoder.Encode. -func (r *Rwalkgetattr) Encode(b *buffer) { - r.Valid.Encode(b) - r.Attr.Encode(b) +// encode implements encoder.encode. +func (r *Rwalkgetattr) encode(b *buffer) { + r.Valid.encode(b) + r.Attr.encode(b) b.Write16(uint16(len(r.QIDs))) for _, q := range r.QIDs { - q.Encode(b) + q.encode(b) } } @@ -2210,15 +2210,15 @@ type Tucreate struct { UID UID } -// Decode implements encoder.Decode. -func (t *Tucreate) Decode(b *buffer) { - t.Tlcreate.Decode(b) +// decode implements encoder.decode. +func (t *Tucreate) decode(b *buffer) { + t.Tlcreate.decode(b) t.UID = b.ReadUID() } -// Encode implements encoder.Encode. -func (t *Tucreate) Encode(b *buffer) { - t.Tlcreate.Encode(b) +// encode implements encoder.encode. +func (t *Tucreate) encode(b *buffer) { + t.Tlcreate.encode(b) b.WriteUID(t.UID) } @@ -2255,15 +2255,15 @@ type Tumkdir struct { UID UID } -// Decode implements encoder.Decode. -func (t *Tumkdir) Decode(b *buffer) { - t.Tmkdir.Decode(b) +// decode implements encoder.decode. +func (t *Tumkdir) decode(b *buffer) { + t.Tmkdir.decode(b) t.UID = b.ReadUID() } -// Encode implements encoder.Encode. -func (t *Tumkdir) Encode(b *buffer) { - t.Tmkdir.Encode(b) +// encode implements encoder.encode. +func (t *Tumkdir) encode(b *buffer) { + t.Tmkdir.encode(b) b.WriteUID(t.UID) } @@ -2300,15 +2300,15 @@ type Tumknod struct { UID UID } -// Decode implements encoder.Decode. -func (t *Tumknod) Decode(b *buffer) { - t.Tmknod.Decode(b) +// decode implements encoder.decode. +func (t *Tumknod) decode(b *buffer) { + t.Tmknod.decode(b) t.UID = b.ReadUID() } -// Encode implements encoder.Encode. -func (t *Tumknod) Encode(b *buffer) { - t.Tmknod.Encode(b) +// encode implements encoder.encode. +func (t *Tumknod) encode(b *buffer) { + t.Tmknod.encode(b) b.WriteUID(t.UID) } @@ -2345,15 +2345,15 @@ type Tusymlink struct { UID UID } -// Decode implements encoder.Decode. -func (t *Tusymlink) Decode(b *buffer) { - t.Tsymlink.Decode(b) +// decode implements encoder.decode. +func (t *Tusymlink) decode(b *buffer) { + t.Tsymlink.decode(b) t.UID = b.ReadUID() } -// Encode implements encoder.Encode. -func (t *Tusymlink) Encode(b *buffer) { - t.Tsymlink.Encode(b) +// encode implements encoder.encode. +func (t *Tusymlink) encode(b *buffer) { + t.Tsymlink.encode(b) b.WriteUID(t.UID) } @@ -2391,14 +2391,14 @@ type Tlconnect struct { Flags ConnectFlags } -// Decode implements encoder.Decode. -func (t *Tlconnect) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tlconnect) decode(b *buffer) { t.FID = b.ReadFID() t.Flags = b.ReadConnectFlags() } -// Encode implements encoder.Encode. -func (t *Tlconnect) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tlconnect) encode(b *buffer) { b.WriteFID(t.FID) b.WriteConnectFlags(t.Flags) } @@ -2418,11 +2418,11 @@ type Rlconnect struct { filePayload } -// Decode implements encoder.Decode. -func (r *Rlconnect) Decode(*buffer) {} +// decode implements encoder.decode. +func (r *Rlconnect) decode(*buffer) {} -// Encode implements encoder.Encode. -func (r *Rlconnect) Encode(*buffer) {} +// encode implements encoder.encode. +func (r *Rlconnect) encode(*buffer) {} // Type implements message.Type. func (*Rlconnect) Type() MsgType { @@ -2445,14 +2445,14 @@ type Tchannel struct { Control uint32 } -// Decode implements encoder.Decode. -func (t *Tchannel) Decode(b *buffer) { +// decode implements encoder.decode. +func (t *Tchannel) decode(b *buffer) { t.ID = b.Read32() t.Control = b.Read32() } -// Encode implements encoder.Encode. -func (t *Tchannel) Encode(b *buffer) { +// encode implements encoder.encode. +func (t *Tchannel) encode(b *buffer) { b.Write32(t.ID) b.Write32(t.Control) } @@ -2474,14 +2474,14 @@ type Rchannel struct { filePayload } -// Decode implements encoder.Decode. -func (r *Rchannel) Decode(b *buffer) { +// decode implements encoder.decode. +func (r *Rchannel) decode(b *buffer) { r.Offset = b.Read64() r.Length = b.Read64() } -// Encode implements encoder.Encode. -func (r *Rchannel) Encode(b *buffer) { +// encode implements encoder.encode. +func (r *Rchannel) encode(b *buffer) { b.Write64(r.Offset) b.Write64(r.Length) } @@ -2577,7 +2577,7 @@ func calculateSize(m message) uint32 { return p.FixedSize() } var dataBuf buffer - m.Encode(&dataBuf) + m.encode(&dataBuf) return uint32(len(dataBuf.data)) } diff --git a/pkg/p9/messages_test.go b/pkg/p9/messages_test.go index 825c939da..c20324404 100644 --- a/pkg/p9/messages_test.go +++ b/pkg/p9/messages_test.go @@ -382,7 +382,7 @@ func TestEncodeDecode(t *testing.T) { // Encode the original. data := make([]byte, initialBufferLength) buf := buffer{data: data[:0]} - enc.Encode(&buf) + enc.encode(&buf) // Create a new object, same as the first. enc2 := reflect.New(reflect.ValueOf(enc).Elem().Type()).Interface().(encoder) @@ -399,7 +399,7 @@ func TestEncodeDecode(t *testing.T) { } // Mark sure it was okay. - enc2.Decode(&buf2) + enc2.decode(&buf2) if buf2.isOverrun() { t.Errorf("object %#v->%#v got overrun on decode", enc, enc2) continue diff --git a/pkg/p9/p9.go b/pkg/p9/p9.go index 20ab31f7a..28d851ff5 100644 --- a/pkg/p9/p9.go +++ b/pkg/p9/p9.go @@ -450,15 +450,15 @@ func (q QID) String() string { return fmt.Sprintf("QID{Type: %d, Version: %d, Path: %d}", q.Type, q.Version, q.Path) } -// Decode implements encoder.Decode. -func (q *QID) Decode(b *buffer) { +// decode implements encoder.decode. +func (q *QID) decode(b *buffer) { q.Type = b.ReadQIDType() q.Version = b.Read32() q.Path = b.Read64() } -// Encode implements encoder.Encode. -func (q *QID) Encode(b *buffer) { +// encode implements encoder.encode. +func (q *QID) encode(b *buffer) { b.WriteQIDType(q.Type) b.Write32(q.Version) b.Write64(q.Path) @@ -515,8 +515,8 @@ type FSStat struct { NameLength uint32 } -// Decode implements encoder.Decode. -func (f *FSStat) Decode(b *buffer) { +// decode implements encoder.decode. +func (f *FSStat) decode(b *buffer) { f.Type = b.Read32() f.BlockSize = b.Read32() f.Blocks = b.Read64() @@ -528,8 +528,8 @@ func (f *FSStat) Decode(b *buffer) { f.NameLength = b.Read32() } -// Encode implements encoder.Encode. -func (f *FSStat) Encode(b *buffer) { +// encode implements encoder.encode. +func (f *FSStat) encode(b *buffer) { b.Write32(f.Type) b.Write32(f.BlockSize) b.Write64(f.Blocks) @@ -679,8 +679,8 @@ func (a AttrMask) String() string { return fmt.Sprintf("AttrMask{with: %s}", strings.Join(masks, " ")) } -// Decode implements encoder.Decode. -func (a *AttrMask) Decode(b *buffer) { +// decode implements encoder.decode. +func (a *AttrMask) decode(b *buffer) { mask := b.Read64() a.Mode = mask&0x00000001 != 0 a.NLink = mask&0x00000002 != 0 @@ -698,8 +698,8 @@ func (a *AttrMask) Decode(b *buffer) { a.DataVersion = mask&0x00002000 != 0 } -// Encode implements encoder.Encode. -func (a *AttrMask) Encode(b *buffer) { +// encode implements encoder.encode. +func (a *AttrMask) encode(b *buffer) { var mask uint64 if a.Mode { mask |= 0x00000001 @@ -774,8 +774,8 @@ func (a Attr) String() string { a.Mode, a.UID, a.GID, a.NLink, a.RDev, a.Size, a.BlockSize, a.Blocks, a.ATimeSeconds, a.ATimeNanoSeconds, a.MTimeSeconds, a.MTimeNanoSeconds, a.CTimeSeconds, a.CTimeNanoSeconds, a.BTimeSeconds, a.BTimeNanoSeconds, a.Gen, a.DataVersion) } -// Encode implements encoder.Encode. -func (a *Attr) Encode(b *buffer) { +// encode implements encoder.encode. +func (a *Attr) encode(b *buffer) { b.WriteFileMode(a.Mode) b.WriteUID(a.UID) b.WriteGID(a.GID) @@ -796,8 +796,8 @@ func (a *Attr) Encode(b *buffer) { b.Write64(a.DataVersion) } -// Decode implements encoder.Decode. -func (a *Attr) Decode(b *buffer) { +// decode implements encoder.decode. +func (a *Attr) decode(b *buffer) { a.Mode = b.ReadFileMode() a.UID = b.ReadUID() a.GID = b.ReadGID() @@ -926,8 +926,8 @@ func (s SetAttrMask) Empty() bool { return !s.Permissions && !s.UID && !s.GID && !s.Size && !s.ATime && !s.MTime && !s.CTime && !s.ATimeNotSystemTime && !s.MTimeNotSystemTime } -// Decode implements encoder.Decode. -func (s *SetAttrMask) Decode(b *buffer) { +// decode implements encoder.decode. +func (s *SetAttrMask) decode(b *buffer) { mask := b.Read32() s.Permissions = mask&0x00000001 != 0 s.UID = mask&0x00000002 != 0 @@ -972,8 +972,8 @@ func (s SetAttrMask) bitmask() uint32 { return mask } -// Encode implements encoder.Encode. -func (s *SetAttrMask) Encode(b *buffer) { +// encode implements encoder.encode. +func (s *SetAttrMask) encode(b *buffer) { b.Write32(s.bitmask()) } @@ -994,8 +994,8 @@ func (s SetAttr) String() string { return fmt.Sprintf("SetAttr{Permissions: 0o%o, UID: %d, GID: %d, Size: %d, ATime: {Sec: %d, NanoSec: %d}, MTime: {Sec: %d, NanoSec: %d}}", s.Permissions, s.UID, s.GID, s.Size, s.ATimeSeconds, s.ATimeNanoSeconds, s.MTimeSeconds, s.MTimeNanoSeconds) } -// Decode implements encoder.Decode. -func (s *SetAttr) Decode(b *buffer) { +// decode implements encoder.decode. +func (s *SetAttr) decode(b *buffer) { s.Permissions = b.ReadPermissions() s.UID = b.ReadUID() s.GID = b.ReadGID() @@ -1006,8 +1006,8 @@ func (s *SetAttr) Decode(b *buffer) { s.MTimeNanoSeconds = b.Read64() } -// Encode implements encoder.Encode. -func (s *SetAttr) Encode(b *buffer) { +// encode implements encoder.encode. +func (s *SetAttr) encode(b *buffer) { b.WritePermissions(s.Permissions) b.WriteUID(s.UID) b.WriteGID(s.GID) @@ -1064,17 +1064,17 @@ func (d Dirent) String() string { return fmt.Sprintf("Dirent{QID: %d, Offset: %d, Type: 0x%X, Name: %s}", d.QID, d.Offset, d.Type, d.Name) } -// Decode implements encoder.Decode. -func (d *Dirent) Decode(b *buffer) { - d.QID.Decode(b) +// decode implements encoder.decode. +func (d *Dirent) decode(b *buffer) { + d.QID.decode(b) d.Offset = b.Read64() d.Type = b.ReadQIDType() d.Name = b.ReadString() } -// Encode implements encoder.Encode. -func (d *Dirent) Encode(b *buffer) { - d.QID.Encode(b) +// encode implements encoder.encode. +func (d *Dirent) encode(b *buffer) { + d.QID.encode(b) b.Write64(d.Offset) b.WriteQIDType(d.Type) b.WriteString(d.Name) @@ -1118,8 +1118,8 @@ func (a *AllocateMode) ToLinux() uint32 { return rv } -// Decode implements encoder.Decode. -func (a *AllocateMode) Decode(b *buffer) { +// decode implements encoder.decode. +func (a *AllocateMode) decode(b *buffer) { mask := b.Read32() a.KeepSize = mask&0x01 != 0 a.PunchHole = mask&0x02 != 0 @@ -1130,8 +1130,8 @@ func (a *AllocateMode) Decode(b *buffer) { a.Unshare = mask&0x40 != 0 } -// Encode implements encoder.Encode. -func (a *AllocateMode) Encode(b *buffer) { +// encode implements encoder.encode. +func (a *AllocateMode) encode(b *buffer) { mask := uint32(0) if a.KeepSize { mask |= 0x01 diff --git a/pkg/p9/transport.go b/pkg/p9/transport.go index 9c11e28ce..7cec0e86d 100644 --- a/pkg/p9/transport.go +++ b/pkg/p9/transport.go @@ -80,7 +80,7 @@ func send(s *unet.Socket, tag Tag, m message) error { } // Encode the message. The buffer will grow automatically. - m.Encode(&dataBuf) + m.encode(&dataBuf) // Get our vectors to send. var hdr [headerLength]byte @@ -316,7 +316,7 @@ func recv(s *unet.Socket, msize uint32, lookup lookupTagAndType) (Tag, message, } // Decode the message data. - m.Decode(&dataBuf) + m.decode(&dataBuf) if dataBuf.isOverrun() { // No need to drain the socket. return NoTag, nil, ErrNoValidMessage diff --git a/pkg/p9/transport_flipcall.go b/pkg/p9/transport_flipcall.go index 233f825e3..a0d274f3b 100644 --- a/pkg/p9/transport_flipcall.go +++ b/pkg/p9/transport_flipcall.go @@ -151,7 +151,7 @@ func (ch *channel) send(m message) (uint32, error) { } else { ch.buf.Write8(0) // No incoming FD. } - m.Encode(&ch.buf) + m.encode(&ch.buf) ssz := uint32(len(ch.buf.data)) // Updated below. // Is there a payload? @@ -205,7 +205,7 @@ func (ch *channel) recv(r message, rsz uint32) (message, error) { ch.buf.data = ch.buf.data[:fs] } - r.Decode(&ch.buf) + r.decode(&ch.buf) if ch.buf.isOverrun() { // Nothing valid was available. log.Debugf("recv [got %d bytes, needed more]", rsz) diff --git a/pkg/p9/transport_test.go b/pkg/p9/transport_test.go index 2f50ff3ea..3668fcad7 100644 --- a/pkg/p9/transport_test.go +++ b/pkg/p9/transport_test.go @@ -56,8 +56,8 @@ func TestSendRecv(t *testing.T) { // badDecode overruns on decode. type badDecode struct{} -func (*badDecode) Decode(b *buffer) { b.markOverrun() } -func (*badDecode) Encode(b *buffer) {} +func (*badDecode) decode(b *buffer) { b.markOverrun() } +func (*badDecode) encode(b *buffer) {} func (*badDecode) Type() MsgType { return MsgTypeBadDecode } func (*badDecode) String() string { return "badDecode{}" } @@ -81,8 +81,8 @@ func TestRecvOverrun(t *testing.T) { // unregistered is not registered on decode. type unregistered struct{} -func (*unregistered) Decode(b *buffer) {} -func (*unregistered) Encode(b *buffer) {} +func (*unregistered) decode(b *buffer) {} +func (*unregistered) encode(b *buffer) {} func (*unregistered) Type() MsgType { return MsgTypeUnregistered } func (*unregistered) String() string { return "unregistered{}" } -- cgit v1.2.3 From d151693530db68db43188ce0fbc9f81aa5f27e2e Mon Sep 17 00:00:00 2001 From: Uros Prestor Date: Thu, 2 Apr 2020 22:01:57 -0700 Subject: Avoid sending a partial dirent when the Rreaddir response exceeds message limit. PiperOrigin-RevId: 304542967 --- pkg/p9/messages.go | 14 ++++++-------- pkg/p9/messages_test.go | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'pkg/p9/messages.go') diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go index 3863ad1f5..57b89ad7d 100644 --- a/pkg/p9/messages.go +++ b/pkg/p9/messages.go @@ -1926,19 +1926,17 @@ func (r *Rreaddir) decode(b *buffer) { // encode implements encoder.encode. func (r *Rreaddir) encode(b *buffer) { entriesBuf := buffer{} + payloadSize := 0 for _, d := range r.Entries { d.encode(&entriesBuf) - if len(entriesBuf.data) >= int(r.Count) { + if len(entriesBuf.data) > int(r.Count) { break } + payloadSize = len(entriesBuf.data) } - if len(entriesBuf.data) < int(r.Count) { - r.Count = uint32(len(entriesBuf.data)) - r.payload = entriesBuf.data - } else { - r.payload = entriesBuf.data[:r.Count] - } - b.Write32(uint32(r.Count)) + r.Count = uint32(payloadSize) + r.payload = entriesBuf.data[:payloadSize] + b.Write32(r.Count) } // Type implements message.Type. diff --git a/pkg/p9/messages_test.go b/pkg/p9/messages_test.go index c20324404..7facc9f5e 100644 --- a/pkg/p9/messages_test.go +++ b/pkg/p9/messages_test.go @@ -216,7 +216,7 @@ func TestEncodeDecode(t *testing.T) { }, &Rreaddir{ // Count must be sufficient to encode a dirent. - Count: 0x18, + Count: 0x1a, Entries: []Dirent{{QID: QID{Type: 2}}}, }, &Tfsync{ -- cgit v1.2.3 From 033fe6d70aa2c6bb64b33668c07eb5db522f9957 Mon Sep 17 00:00:00 2001 From: Sevki Date: Tue, 21 Jul 2020 19:42:04 +0100 Subject: p9: fix `registry.get` ob1 bug --- pkg/p9/messages.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pkg/p9/messages.go') diff --git a/pkg/p9/messages.go b/pkg/p9/messages.go index 57b89ad7d..2cb59f934 100644 --- a/pkg/p9/messages.go +++ b/pkg/p9/messages.go @@ -2506,7 +2506,7 @@ type msgFactory struct { var msgRegistry registry type registry struct { - factories [math.MaxUint8]msgFactory + factories [math.MaxUint8 + 1]msgFactory // largestFixedSize is computed so that given some message size M, you can // compute the maximum payload size (e.g. for Twrite, Rread) with -- cgit v1.2.3