diff --git a/cephfs/file.go b/cephfs/file.go index 7ae2145..17dd2de 100644 --- a/cephfs/file.go +++ b/cephfs/file.go @@ -199,3 +199,27 @@ func (f *File) Fchown(user uint32, group uint32) error { ret := C.ceph_fchown(f.mount.mount, f.fd, C.int(user), C.int(group)) return getError(ret) } + +// Fstatx returns information about an open file. +// +// Implements: +// int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx, +// unsigned int want, unsigned int flags); +func (f *File) Fstatx(want StatxMask, flags AtFlags) (*CephStatx, error) { + if err := f.validate(); err != nil { + return nil, err + } + + var stx C.struct_ceph_statx + ret := C.ceph_fstatx( + f.mount.mount, + f.fd, + &stx, + C.uint(want), + C.uint(flags), + ) + if err := getError(ret); err != nil { + return nil, err + } + return cStructToCephStatx(stx), nil +} diff --git a/cephfs/file_test.go b/cephfs/file_test.go index ae00f4f..9bc54ca 100644 --- a/cephfs/file_test.go +++ b/cephfs/file_test.go @@ -415,3 +415,63 @@ func TestFchown(t *testing.T) { err = f2.Fchown(bob, bob) assert.Error(t, err) } + +func TestFstatx(t *testing.T) { + fname := "test_fstatx.txt" + + mount := fsConnect(t) + defer fsDisconnect(t, mount) + + f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE, 0600) + assert.NoError(t, err) + assert.NotNil(t, f) + assert.NoError(t, f.Close()) + defer func() { assert.NoError(t, mount.Unlink(fname)) }() + + t.Run("emptyFile", func(t *testing.T) { + f, err := mount.Open(fname, os.O_RDWR, 0600) + assert.NoError(t, err) + assert.NotNil(t, f) + + st, err := f.Fstatx(StatxBasicStats, 0) + assert.NoError(t, err) + assert.NotNil(t, f) + + assert.Equal(t, uint16(0600), st.Mode&0600) + assert.Equal(t, uint64(0), st.Size) + }) + + t.Run("populateFile", func(t *testing.T) { + f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE, 0600) + assert.NoError(t, err) + assert.NotNil(t, f) + defer func() { assert.NoError(t, f.Close()) }() + + _, err = f.Write([]byte("See spot run.\nSee spot jump.\n")) + assert.NoError(t, err) + + st, err := f.Fstatx(StatxBasicStats, 0) + assert.NoError(t, err) + assert.NotNil(t, f) + + assert.Equal(t, uint16(0600), st.Mode&0600) + assert.Equal(t, uint64(29), st.Size) + }) + + t.Run("closedFile", func(t *testing.T) { + f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE, 0600) + assert.NoError(t, err) + assert.NotNil(t, f) + assert.NoError(t, f.Close()) + + st, err := f.Fstatx(StatxBasicStats, 0) + assert.Error(t, err) + assert.Nil(t, st) + }) + + t.Run("invalidFile", func(t *testing.T) { + f := &File{} + _, err := f.Fstatx(StatxBasicStats, 0) + assert.Error(t, err) + }) +}