mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{} })
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user