diff --git a/cephfs/file_xattr.go b/cephfs/file_xattr.go index 0ef5e08..0fe7c5a 100644 --- a/cephfs/file_xattr.go +++ b/cephfs/file_xattr.go @@ -12,6 +12,7 @@ package cephfs import "C" import ( + "bytes" "unsafe" "github.com/ceph/go-ceph/internal/retry" @@ -97,3 +98,43 @@ func (f *File) GetXattr(name string) ([]byte, error) { } return buf[:ret], nil } + +// ListXattr returns a slice containing strings for the name of each xattr set +// on the fie. +// +// Implements: +// int ceph_flistxattr(struct ceph_mount_info *cmount, int fd, char *list, size_t size); +func (f *File) ListXattr() ([]string, error) { + if err := f.validate(); err != nil { + return nil, err + } + + var ( + ret C.int + err error + buf []byte + ) + // range from 1k to 64KiB + retry.WithSizes(1024, 1<<16, func(size int) retry.Hint { + buf = make([]byte, size) + ret = C.ceph_flistxattr( + f.mount.mount, + f.fd, + (*C.char)(unsafe.Pointer(&buf[0])), + C.size_t(size)) + err = getErrorIfNegative(ret) + return retry.DoubleSize.If(err == errRange) + }) + if err != nil { + return nil, err + } + + names := make([]string, 0) + for _, s := range bytes.Split(buf[:ret], []byte{0}) { + if len(s) > 0 { + name := C.GoString((*C.char)(unsafe.Pointer(&s[0]))) + names = append(names, name) + } + } + return names, nil +} diff --git a/cephfs/file_xattr_test.go b/cephfs/file_xattr_test.go index b7f5c8e..9a8eeee 100644 --- a/cephfs/file_xattr_test.go +++ b/cephfs/file_xattr_test.go @@ -74,5 +74,47 @@ func TestGetSetXattr(t *testing.T) { _, err = f1.GetXattr(samples[0].name) assert.Error(t, err) }) - +} + +func TestListXattr(t *testing.T) { + mount := fsConnect(t) + defer fsDisconnect(t, mount) + fname := "TestListXattr.txt" + + f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) + require.NoError(t, err) + defer func() { + assert.NoError(t, f.Close()) + assert.NoError(t, mount.Unlink(fname)) + }() + + t.Run("listXattrs1", func(t *testing.T) { + for _, s := range samples[:1] { + err := f.SetXattr(s.name, s.value, XattrDefault) + assert.NoError(t, err) + } + xl, err := f.ListXattr() + assert.NoError(t, err) + assert.Len(t, xl, 1) + assert.Contains(t, xl, samples[0].name) + }) + + t.Run("listXattrs2", func(t *testing.T) { + for _, s := range samples[:3] { + err := f.SetXattr(s.name, s.value, XattrDefault) + assert.NoError(t, err) + } + xl, err := f.ListXattr() + assert.NoError(t, err) + assert.Len(t, xl, 3) + assert.Contains(t, xl, samples[0].name) + assert.Contains(t, xl, samples[1].name) + assert.Contains(t, xl, samples[2].name) + }) + + t.Run("invalidFile", func(t *testing.T) { + f1 := &File{} + _, err := f1.ListXattr() + assert.Error(t, err) + }) }