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
This commit is contained in:
Dean Deng
2020-01-16 12:56:33 -08:00
committed by gVisor bot
parent 7b7c31820b
commit 07f2584979
20 changed files with 374 additions and 93 deletions
+23
View File
@@ -165,6 +165,29 @@ func (c *clientFile) SetAttr(valid SetAttrMask, attr SetAttr) error {
return c.client.sendRecv(&Tsetattr{FID: c.fid, Valid: valid, SetAttr: attr}, &Rsetattr{})
}
// GetXattr implements File.GetXattr.
func (c *clientFile) GetXattr(name string, size uint64) (string, error) {
if atomic.LoadUint32(&c.closed) != 0 {
return "", syscall.EBADF
}
rgetxattr := Rgetxattr{}
if err := c.client.sendRecv(&Tgetxattr{FID: c.fid, Name: name, Size: size}, &rgetxattr); err != nil {
return "", err
}
return rgetxattr.Value, nil
}
// SetXattr implements File.SetXattr.
func (c *clientFile) SetXattr(name, value string, flags uint32) error {
if atomic.LoadUint32(&c.closed) != 0 {
return syscall.EBADF
}
return c.client.sendRecv(&Tsetxattr{FID: c.fid, Name: name, Value: value, Flags: flags}, &Rsetxattr{})
}
// Allocate implements File.Allocate.
func (c *clientFile) Allocate(mode AllocateMode, offset, length uint64) error {
if atomic.LoadUint32(&c.closed) != 0 {
+16
View File
@@ -89,6 +89,22 @@ type File interface {
// On the server, SetAttr has a write concurrency guarantee.
SetAttr(valid SetAttrMask, attr SetAttr) error
// GetXattr returns extended attributes of this node.
//
// Size indicates the size of the buffer that has been allocated to hold the
// attribute value. If the value is 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/127675828): Determine concurrency guarantees once implemented.
GetXattr(name string, size uint64) (string, error)
// SetXattr sets extended attributes on this node.
//
// TODO(b/127675828): Determine concurrency guarantees once implemented.
SetXattr(name, value string, flags uint32) 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
+29
View File
@@ -912,6 +912,35 @@ func (t *Txattrcreate) handle(cs *connState) message {
return newErr(syscall.ENOSYS)
}
// handle implements handler.handle.
func (t *Tgetxattr) handle(cs *connState) message {
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
val, err := ref.file.GetXattr(t.Name, t.Size)
if err != nil {
return newErr(err)
}
return &Rgetxattr{Value: val}
}
// handle implements handler.handle.
func (t *Tsetxattr) handle(cs *connState) message {
ref, ok := cs.LookupFID(t.FID)
if !ok {
return newErr(syscall.EBADF)
}
defer ref.DecRef()
if err := ref.file.SetXattr(t.Name, t.Value, t.Flags); err != nil {
return newErr(err)
}
return &Rsetxattr{}
}
// handle implements handler.handle.
func (t *Treaddir) handle(cs *connState) message {
ref, ok := cs.LookupFID(t.Directory)
+129
View File
@@ -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{} })
+15
View File
@@ -194,6 +194,21 @@ func TestEncodeDecode(t *testing.T) {
Flags: 3,
},
&Rxattrcreate{},
&Tgetxattr{
FID: 1,
Name: "abc",
Size: 2,
},
&Rgetxattr{
Value: "xyz",
},
&Tsetxattr{
FID: 1,
Name: "abc",
Value: "xyz",
Flags: 2,
},
&Rsetxattr{},
&Treaddir{
Directory: 1,
Offset: 2,
+4
View File
@@ -339,6 +339,10 @@ const (
MsgRxattrwalk = 31
MsgTxattrcreate = 32
MsgRxattrcreate = 33
MsgTgetxattr = 34
MsgRgetxattr = 35
MsgTsetxattr = 36
MsgRsetxattr = 37
MsgTreaddir = 40
MsgRreaddir = 41
MsgTfsync = 50
+5 -4
View File
@@ -18,6 +18,7 @@ import (
"fmt"
"io"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/context"
"gvisor.dev/gvisor/pkg/sentry/memmap"
@@ -395,12 +396,12 @@ func copyContentsLocked(ctx context.Context, upper *Inode, lower *Inode, size in
// Size and permissions are set on upper when the file content is copied
// and when the file is created respectively.
func copyAttributesLocked(ctx context.Context, upper *Inode, lower *Inode) error {
// Extract attributes fro the lower filesystem.
// Extract attributes from the lower filesystem.
lowerAttr, err := lower.UnstableAttr(ctx)
if err != nil {
return err
}
lowerXattr, err := lower.Listxattr()
lowerXattr, err := lower.ListXattr(ctx)
if err != nil && err != syserror.EOPNOTSUPP {
return err
}
@@ -421,11 +422,11 @@ func copyAttributesLocked(ctx context.Context, upper *Inode, lower *Inode) error
if isXattrOverlay(name) {
continue
}
value, err := lower.Getxattr(name)
value, err := lower.GetXattr(ctx, name, linux.XATTR_SIZE_MAX)
if err != nil {
return err
}
if err := upper.InodeOperations.Setxattr(upper, name, value); err != nil {
if err := upper.InodeOperations.SetXattr(ctx, upper, name, value, 0 /* flags */); err != nil {
return err
}
}
+1 -1
View File
@@ -475,7 +475,7 @@ func readdirEntries(ctx context.Context, o *overlayEntry) (*SortedDentryMap, err
// Skip this name if it is a negative entry in the
// upper or there exists a whiteout for it.
if o.upper != nil {
if overlayHasWhiteout(o.upper, name) {
if overlayHasWhiteout(ctx, o.upper, name) {
continue
}
}
+27 -14
View File
@@ -15,6 +15,7 @@
package fsutil
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/context"
"gvisor.dev/gvisor/pkg/sentry/fs"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
@@ -202,7 +203,7 @@ func (i *InodeSimpleAttributes) NotifyModificationAndStatusChange(ctx context.Co
}
// InodeSimpleExtendedAttributes implements
// fs.InodeOperations.{Get,Set,List}xattr.
// fs.InodeOperations.{Get,Set,List}Xattr.
//
// +stateify savable
type InodeSimpleExtendedAttributes struct {
@@ -211,8 +212,8 @@ type InodeSimpleExtendedAttributes struct {
xattrs map[string]string
}
// Getxattr implements fs.InodeOperations.Getxattr.
func (i *InodeSimpleExtendedAttributes) Getxattr(_ *fs.Inode, name string) (string, error) {
// GetXattr implements fs.InodeOperations.GetXattr.
func (i *InodeSimpleExtendedAttributes) GetXattr(_ context.Context, _ *fs.Inode, name string, _ uint64) (string, error) {
i.mu.RLock()
value, ok := i.xattrs[name]
i.mu.RUnlock()
@@ -222,19 +223,31 @@ func (i *InodeSimpleExtendedAttributes) Getxattr(_ *fs.Inode, name string) (stri
return value, nil
}
// Setxattr implements fs.InodeOperations.Setxattr.
func (i *InodeSimpleExtendedAttributes) Setxattr(_ *fs.Inode, name, value string) error {
// SetXattr implements fs.InodeOperations.SetXattr.
func (i *InodeSimpleExtendedAttributes) SetXattr(_ context.Context, _ *fs.Inode, name, value string, flags uint32) error {
i.mu.Lock()
defer i.mu.Unlock()
if i.xattrs == nil {
if flags&linux.XATTR_REPLACE != 0 {
return syserror.ENODATA
}
i.xattrs = make(map[string]string)
}
_, ok := i.xattrs[name]
if ok && flags&linux.XATTR_CREATE != 0 {
return syserror.EEXIST
}
if !ok && flags&linux.XATTR_REPLACE != 0 {
return syserror.ENODATA
}
i.xattrs[name] = value
i.mu.Unlock()
return nil
}
// Listxattr implements fs.InodeOperations.Listxattr.
func (i *InodeSimpleExtendedAttributes) Listxattr(_ *fs.Inode) (map[string]struct{}, error) {
// ListXattr implements fs.InodeOperations.ListXattr.
func (i *InodeSimpleExtendedAttributes) ListXattr(context.Context, *fs.Inode) (map[string]struct{}, error) {
i.mu.RLock()
names := make(map[string]struct{}, len(i.xattrs))
for name := range i.xattrs {
@@ -436,18 +449,18 @@ func (InodeNotSymlink) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
// extended attributes.
type InodeNoExtendedAttributes struct{}
// Getxattr implements fs.InodeOperations.Getxattr.
func (InodeNoExtendedAttributes) Getxattr(*fs.Inode, string) (string, error) {
// GetXattr implements fs.InodeOperations.GetXattr.
func (InodeNoExtendedAttributes) GetXattr(context.Context, *fs.Inode, string, uint64) (string, error) {
return "", syserror.EOPNOTSUPP
}
// Setxattr implements fs.InodeOperations.Setxattr.
func (InodeNoExtendedAttributes) Setxattr(*fs.Inode, string, string) error {
// SetXattr implements fs.InodeOperations.SetXattr.
func (InodeNoExtendedAttributes) SetXattr(context.Context, *fs.Inode, string, string, uint32) error {
return syserror.EOPNOTSUPP
}
// Listxattr implements fs.InodeOperations.Listxattr.
func (InodeNoExtendedAttributes) Listxattr(*fs.Inode) (map[string]struct{}, error) {
// ListXattr implements fs.InodeOperations.ListXattr.
func (InodeNoExtendedAttributes) ListXattr(context.Context, *fs.Inode) (map[string]struct{}, error) {
return nil, syserror.EOPNOTSUPP
}
+14
View File
@@ -59,6 +59,20 @@ func (c *contextFile) setAttr(ctx context.Context, valid p9.SetAttrMask, attr p9
return err
}
func (c *contextFile) getXattr(ctx context.Context, name string, size uint64) (string, error) {
ctx.UninterruptibleSleepStart(false)
val, err := c.file.GetXattr(name, size)
ctx.UninterruptibleSleepFinish(false)
return val, err
}
func (c *contextFile) setXattr(ctx context.Context, name, value string, flags uint32) error {
ctx.UninterruptibleSleepStart(false)
err := c.file.SetXattr(name, value, flags)
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)
+16 -2
View File
@@ -38,8 +38,7 @@ import (
//
// +stateify savable
type inodeOperations struct {
fsutil.InodeNotVirtual `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNotVirtual `state:"nosave"`
// fileState implements fs.CachedFileObject. It exists
// to break a circular load dependency between inodeOperations
@@ -604,6 +603,21 @@ func (i *inodeOperations) Truncate(ctx context.Context, inode *fs.Inode, length
return i.fileState.file.setAttr(ctx, p9.SetAttrMask{Size: true}, p9.SetAttr{Size: uint64(length)})
}
// GetXattr implements fs.InodeOperations.GetXattr.
func (i *inodeOperations) GetXattr(ctx context.Context, inode *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 {
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
}
// Allocate implements fs.InodeOperations.Allocate.
func (i *inodeOperations) Allocate(ctx context.Context, inode *fs.Inode, offset, length int64) error {
// This can only be called for files anyway.
+12 -12
View File
@@ -261,28 +261,28 @@ func (i *Inode) UnstableAttr(ctx context.Context) (UnstableAttr, error) {
return i.InodeOperations.UnstableAttr(ctx, i)
}
// Getxattr calls i.InodeOperations.Getxattr with i as the Inode.
func (i *Inode) Getxattr(name string) (string, error) {
// GetXattr calls i.InodeOperations.GetXattr with i as the Inode.
func (i *Inode) GetXattr(ctx context.Context, name string, size uint64) (string, error) {
if i.overlay != nil {
return overlayGetxattr(i.overlay, name)
return overlayGetXattr(ctx, i.overlay, name, size)
}
return i.InodeOperations.Getxattr(i, name)
return i.InodeOperations.GetXattr(ctx, i, name, size)
}
// Setxattr calls i.InodeOperations.Setxattr with i as the Inode.
func (i *Inode) Setxattr(name, value string) error {
// SetXattr calls i.InodeOperations.SetXattr with i as the Inode.
func (i *Inode) SetXattr(ctx context.Context, name, value string, flags uint32) error {
if i.overlay != nil {
return overlaySetxattr(i.overlay, name, value)
return overlaySetxattr(ctx, i.overlay, name, value, flags)
}
return i.InodeOperations.Setxattr(i, name, value)
return i.InodeOperations.SetXattr(ctx, i, name, value, flags)
}
// Listxattr calls i.InodeOperations.Listxattr with i as the Inode.
func (i *Inode) Listxattr() (map[string]struct{}, error) {
// ListXattr calls i.InodeOperations.ListXattr with i as the Inode.
func (i *Inode) ListXattr(ctx context.Context) (map[string]struct{}, error) {
if i.overlay != nil {
return overlayListxattr(i.overlay)
return overlayListXattr(ctx, i.overlay)
}
return i.InodeOperations.Listxattr(i)
return i.InodeOperations.ListXattr(ctx, i)
}
// CheckPermission will check if the caller may access this file in the
+16 -9
View File
@@ -170,20 +170,27 @@ type InodeOperations interface {
// file system events.
UnstableAttr(ctx context.Context, inode *Inode) (UnstableAttr, error)
// Getxattr retrieves the value of extended attribute name. Inodes that
// do not support extended attributes return EOPNOTSUPP. Inodes that
// support extended attributes but don't have a value at name return
// GetXattr retrieves the value of extended attribute specified by name.
// Inodes that do not support extended attributes return EOPNOTSUPP. Inodes
// that support extended attributes but don't have a value at name return
// ENODATA.
Getxattr(inode *Inode, name string) (string, error)
//
// If this is called through the getxattr(2) syscall, size indicates the
// size of the buffer that the application has allocated to hold the
// attribute value. If the value is 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.
GetXattr(ctx context.Context, inode *Inode, name string, size uint64) (string, error)
// Setxattr sets the value of extended attribute name. Inodes that
// do not support extended attributes return EOPNOTSUPP.
Setxattr(inode *Inode, name, value string) error
// SetXattr sets the value of extended attribute specified by name. Inodes
// that do not support extended attributes return EOPNOTSUPP.
SetXattr(ctx context.Context, inode *Inode, name, value string, flags uint32) error
// Listxattr returns the set of all extended attributes names that
// ListXattr returns the set of all extended attributes names that
// have values. Inodes that do not support extended attributes return
// EOPNOTSUPP.
Listxattr(inode *Inode) (map[string]struct{}, error)
ListXattr(ctx context.Context, inode *Inode) (map[string]struct{}, error)
// Check determines whether an Inode can be accessed with the
// requested permission mask using the context (which gives access
+15 -15
View File
@@ -25,13 +25,13 @@ import (
"gvisor.dev/gvisor/pkg/syserror"
)
func overlayHasWhiteout(parent *Inode, name string) bool {
s, err := parent.Getxattr(XattrOverlayWhiteout(name))
func overlayHasWhiteout(ctx context.Context, parent *Inode, name string) bool {
s, err := parent.GetXattr(ctx, XattrOverlayWhiteout(name), 1)
return err == nil && s == "y"
}
func overlayCreateWhiteout(parent *Inode, name string) error {
return parent.InodeOperations.Setxattr(parent, XattrOverlayWhiteout(name), "y")
func overlayCreateWhiteout(ctx context.Context, parent *Inode, name string) error {
return parent.InodeOperations.SetXattr(ctx, parent, XattrOverlayWhiteout(name), "y", 0 /* flags */)
}
func overlayWriteOut(ctx context.Context, o *overlayEntry) error {
@@ -89,7 +89,7 @@ func overlayLookup(ctx context.Context, parent *overlayEntry, inode *Inode, name
}
// Are we done?
if overlayHasWhiteout(parent.upper, name) {
if overlayHasWhiteout(ctx, parent.upper, name) {
if upperInode == nil {
parent.copyMu.RUnlock()
if negativeUpperChild {
@@ -345,7 +345,7 @@ func overlayRemove(ctx context.Context, o *overlayEntry, parent *Dirent, child *
}
}
if child.Inode.overlay.lowerExists {
if err := overlayCreateWhiteout(o.upper, child.name); err != nil {
if err := overlayCreateWhiteout(ctx, o.upper, child.name); err != nil {
return err
}
}
@@ -426,7 +426,7 @@ func overlayRename(ctx context.Context, o *overlayEntry, oldParent *Dirent, rena
return err
}
if renamed.Inode.overlay.lowerExists {
if err := overlayCreateWhiteout(oldParent.Inode.overlay.upper, oldName); err != nil {
if err := overlayCreateWhiteout(ctx, oldParent.Inode.overlay.upper, oldName); err != nil {
return err
}
}
@@ -528,7 +528,7 @@ func overlayUnstableAttr(ctx context.Context, o *overlayEntry) (UnstableAttr, er
return attr, err
}
func overlayGetxattr(o *overlayEntry, name string) (string, error) {
func overlayGetXattr(ctx context.Context, o *overlayEntry, name string, size uint64) (string, error) {
// Hot path. This is how the overlay checks for whiteout files.
// Avoid defers.
var (
@@ -544,31 +544,31 @@ func overlayGetxattr(o *overlayEntry, name string) (string, error) {
o.copyMu.RLock()
if o.upper != nil {
s, err = o.upper.Getxattr(name)
s, err = o.upper.GetXattr(ctx, name, size)
} else {
s, err = o.lower.Getxattr(name)
s, err = o.lower.GetXattr(ctx, name, size)
}
o.copyMu.RUnlock()
return s, err
}
// TODO(b/146028302): Support setxattr for overlayfs.
func overlaySetxattr(o *overlayEntry, name, value string) error {
func overlaySetxattr(ctx context.Context, o *overlayEntry, name, value string, flags uint32) error {
return syserror.EOPNOTSUPP
}
func overlayListxattr(o *overlayEntry) (map[string]struct{}, error) {
func overlayListXattr(ctx context.Context, o *overlayEntry) (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()
names, err = o.upper.ListXattr(ctx)
} else {
names, err = o.lower.Listxattr()
names, err = o.lower.ListXattr(ctx)
}
for name := range names {
// Same as overlayGetxattr, we shouldn't forward along
// Same as overlayGetXattr, we shouldn't forward along
// overlay attributes.
if strings.HasPrefix(XattrOverlayPrefix, name) {
delete(names, name)
+2 -2
View File
@@ -382,8 +382,8 @@ type dir struct {
ReaddirCalled bool
}
// Getxattr implements InodeOperations.Getxattr.
func (d *dir) Getxattr(inode *fs.Inode, name string) (string, error) {
// GetXattr implements InodeOperations.GetXattr.
func (d *dir) GetXattr(_ context.Context, _ *fs.Inode, name string, _ uint64) (string, error) {
for _, n := range d.negative {
if name == fs.XattrOverlayWhiteout(n) {
return "y", nil
+9 -9
View File
@@ -148,19 +148,19 @@ func (d *Dir) CreateFifo(ctx context.Context, dir *fs.Inode, name string, perms
return d.ramfsDir.CreateFifo(ctx, dir, name, perms)
}
// Getxattr implements fs.InodeOperations.Getxattr.
func (d *Dir) Getxattr(i *fs.Inode, name string) (string, error) {
return d.ramfsDir.Getxattr(i, name)
// GetXattr implements fs.InodeOperations.GetXattr.
func (d *Dir) GetXattr(ctx context.Context, i *fs.Inode, name string, size uint64) (string, error) {
return d.ramfsDir.GetXattr(ctx, i, name, size)
}
// Setxattr implements fs.InodeOperations.Setxattr.
func (d *Dir) Setxattr(i *fs.Inode, name, value string) error {
return d.ramfsDir.Setxattr(i, name, value)
// SetXattr implements fs.InodeOperations.SetXattr.
func (d *Dir) SetXattr(ctx context.Context, i *fs.Inode, name, value string, flags uint32) error {
return d.ramfsDir.SetXattr(ctx, i, name, value, flags)
}
// Listxattr implements fs.InodeOperations.Listxattr.
func (d *Dir) Listxattr(i *fs.Inode) (map[string]struct{}, error) {
return d.ramfsDir.Listxattr(i)
// 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)
}
// Lookup implements fs.InodeOperations.Lookup.
+2 -2
View File
@@ -228,10 +228,10 @@ 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),
188: syscalls.PartiallySupported("setxattr", Setxattr, "Only supported for tmpfs.", nil),
188: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil),
189: syscalls.Error("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
190: syscalls.Error("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
191: syscalls.PartiallySupported("getxattr", Getxattr, "Only supported for tmpfs.", nil),
191: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil),
192: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
193: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
194: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
+2 -2
View File
@@ -41,10 +41,10 @@ var ARM64 = &kernel.SyscallTable{
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),
5: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil),
6: syscalls.Error("lsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
7: syscalls.Error("fsetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
8: syscalls.PartiallySupported("getxattr", Getxattr, "Only supported for tmpfs.", nil),
8: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil),
9: syscalls.ErrorWithEvent("lgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
10: syscalls.ErrorWithEvent("fgetxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
11: syscalls.ErrorWithEvent("listxattr", syserror.ENOTSUP, "Requires filesystem support.", nil),
+27 -21
View File
@@ -25,12 +25,12 @@ import (
"gvisor.dev/gvisor/pkg/syserror"
)
// Getxattr implements linux syscall getxattr(2).
func Getxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
// GetXattr implements linux syscall getxattr(2).
func GetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
pathAddr := args[0].Pointer()
nameAddr := args[1].Pointer()
valueAddr := args[2].Pointer()
size := args[3].SizeT()
size := uint64(args[3].SizeT())
path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
if err != nil {
@@ -39,22 +39,28 @@ func Getxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
valueLen := 0
err = fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
value, err := getxattr(t, d, dirPath, nameAddr)
// If getxattr(2) is called with size 0, the size of the value will be
// returned successfully even if it is nonzero. In that case, we need to
// retrieve the entire attribute value so we can return the correct size.
requestedSize := size
if size == 0 || size > linux.XATTR_SIZE_MAX {
requestedSize = linux.XATTR_SIZE_MAX
}
value, err := getXattr(t, d, dirPath, nameAddr, uint64(requestedSize))
if err != nil {
return err
}
valueLen = len(value)
if size == 0 {
return nil
}
if size > linux.XATTR_SIZE_MAX {
size = linux.XATTR_SIZE_MAX
}
if valueLen > int(size) {
if uint64(valueLen) > requestedSize {
return syserror.ERANGE
}
// Skip copying out the attribute value if size is 0.
if size == 0 {
return nil
}
_, err = t.CopyOutBytes(valueAddr, []byte(value))
return err
})
@@ -64,8 +70,8 @@ func Getxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
return uintptr(valueLen), nil, nil
}
// getxattr implements getxattr from the given *fs.Dirent.
func getxattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr usermem.Addr) (string, error) {
// getXattr implements getxattr(2) from the given *fs.Dirent.
func getXattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr usermem.Addr, size uint64) (string, error) {
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
return "", syserror.ENOTDIR
}
@@ -83,15 +89,15 @@ func getxattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr usermem.Addr)
return "", syserror.EOPNOTSUPP
}
return d.Inode.Getxattr(name)
return d.Inode.GetXattr(t, name, size)
}
// Setxattr implements linux syscall setxattr(2).
func Setxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
// SetXattr implements linux syscall setxattr(2).
func SetXattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
pathAddr := args[0].Pointer()
nameAddr := args[1].Pointer()
valueAddr := args[2].Pointer()
size := args[3].SizeT()
size := uint64(args[3].SizeT())
flags := args[4].Uint()
path, dirPath, err := copyInPath(t, pathAddr, false /* allowEmpty */)
@@ -104,12 +110,12 @@ func Setxattr(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
}
return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
return setxattr(t, d, dirPath, nameAddr, valueAddr, size, flags)
return setXattr(t, d, dirPath, nameAddr, valueAddr, uint64(size), flags)
})
}
// setxattr implements setxattr from the given *fs.Dirent.
func setxattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr, valueAddr usermem.Addr, size uint, flags uint32) error {
// setXattr implements setxattr(2) from the given *fs.Dirent.
func setXattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr, valueAddr usermem.Addr, size uint64, flags uint32) error {
if dirPath && !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -136,7 +142,7 @@ func setxattr(t *kernel.Task, d *fs.Dirent, dirPath bool, nameAddr, valueAddr us
return syserror.EOPNOTSUPP
}
return d.Inode.Setxattr(name, value)
return d.Inode.SetXattr(t, name, value, flags)
}
func copyInXattrName(t *kernel.Task, nameAddr usermem.Addr) (string, error) {
+10
View File
@@ -767,6 +767,16 @@ func (l *localFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
return err
}
// TODO(b/127675828): support getxattr.
func (l *localFile) GetXattr(name string, size uint64) (string, error) {
return "", syscall.EOPNOTSUPP
}
// TODO(b/127675828): support setxattr.
func (l *localFile) SetXattr(name, value string, flags uint32) error {
return syscall.EOPNOTSUPP
}
// Allocate implements p9.File.
func (l *localFile) Allocate(mode p9.AllocateMode, offset, length uint64) error {
if !l.isOpen() {