mirror of https://github.com/ceph/go-ceph
cephfs: add path based Statx function implmenting ceph_statx
Add a Statx wrapper for ceph_statx. Add a type wrapping the statx status info that exposes the various fields from the C-struct. Add a type wrapping struct timespec, based on golang's x/sys, for the time fields in the struct. Note that the ceph struct is not the same as the linux statx struct. Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
parent
27a1824ca0
commit
d4079e3949
|
@ -45,7 +45,7 @@ const (
|
||||||
// Private errors:
|
// Private errors:
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
errInvalid = CephFSError(-C.EINVAL)
|
||||||
errNameTooLong = CephFSError(-C.ENAMETOOLONG)
|
errNameTooLong = CephFSError(-C.ENAMETOOLONG)
|
||||||
|
errNoEntry = CephFSError(-C.ENOENT)
|
||||||
errInvalid = CephFSError(-C.EINVAL)
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -130,3 +130,29 @@ func (mount *MountInfo) Readlink(path string) (string, error) {
|
||||||
|
|
||||||
return string(buf[:ret]), nil
|
return string(buf[:ret]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Statx returns information about a file/directory.
|
||||||
|
//
|
||||||
|
// Implements:
|
||||||
|
// int ceph_statx(struct ceph_mount_info *cmount, const char *path, struct ceph_statx *stx,
|
||||||
|
// unsigned int want, unsigned int flags);
|
||||||
|
func (mount *MountInfo) Statx(path string, want StatxMask, flags AtFlags) (*CephStatx, error) {
|
||||||
|
if err := mount.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cPath := C.CString(path)
|
||||||
|
defer C.free(unsafe.Pointer(cPath))
|
||||||
|
|
||||||
|
var stx C.struct_ceph_statx
|
||||||
|
ret := C.ceph_statx(
|
||||||
|
mount.mount,
|
||||||
|
cPath,
|
||||||
|
&stx,
|
||||||
|
C.uint(want),
|
||||||
|
C.uint(flags),
|
||||||
|
)
|
||||||
|
if err := getError(ret); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cStructToCephStatx(stx), nil
|
||||||
|
}
|
||||||
|
|
|
@ -357,3 +357,23 @@ func TestReadlink(t *testing.T) {
|
||||||
assert.Equal(t, buf, "")
|
assert.Equal(t, buf, "")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStatx(t *testing.T) {
|
||||||
|
mount := fsConnect(t)
|
||||||
|
defer fsDisconnect(t, mount)
|
||||||
|
|
||||||
|
dirname := "statme"
|
||||||
|
assert.NoError(t, mount.MakeDir(dirname, 0755))
|
||||||
|
|
||||||
|
st, err := mount.Statx(dirname, StatxBasicStats, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, st)
|
||||||
|
assert.Equal(t, uint16(0755), st.Mode&0777)
|
||||||
|
|
||||||
|
assert.NoError(t, mount.RemoveDir(dirname))
|
||||||
|
|
||||||
|
st, err = mount.Statx(dirname, StatxBasicStats, 0)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, st)
|
||||||
|
assert.Equal(t, errNoEntry, err)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
package cephfs
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -lcephfs
|
||||||
|
#cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64
|
||||||
|
#include <cephfs/libcephfs.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// StatxMask values contain bit-flags indicating what data should be
|
||||||
|
// populated by a statx-type call.
|
||||||
|
type StatxMask uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StatxMode requests the mode value be filled in.
|
||||||
|
StatxMode = StatxMask(C.CEPH_STATX_MODE)
|
||||||
|
// StatxNlink requests the nlink value be filled in.
|
||||||
|
StatxNlink = StatxMask(C.CEPH_STATX_NLINK)
|
||||||
|
// StatxUid requests the uid value be filled in.
|
||||||
|
StatxUid = StatxMask(C.CEPH_STATX_UID)
|
||||||
|
// StatxRdev requests the rdev value be filled in.
|
||||||
|
StatxRdev = StatxMask(C.CEPH_STATX_RDEV)
|
||||||
|
// StatxAtime requests the access-time value be filled in.
|
||||||
|
StatxAtime = StatxMask(C.CEPH_STATX_ATIME)
|
||||||
|
// StatxMtime requests the modified-time value be filled in.
|
||||||
|
StatxMtime = StatxMask(C.CEPH_STATX_MTIME)
|
||||||
|
// StatxIno requests the inode be filled in.
|
||||||
|
StatxIno = StatxMask(C.CEPH_STATX_INO)
|
||||||
|
// StatxSize requests the size value be filled in.
|
||||||
|
StatxSize = StatxMask(C.CEPH_STATX_SIZE)
|
||||||
|
// StatxBlocks requests the blocks value be filled in.
|
||||||
|
StatxBlocks = StatxMask(C.CEPH_STATX_BLOCKS)
|
||||||
|
// StatxBasicStats requests all the fields that are part of a
|
||||||
|
// traditional stat call.
|
||||||
|
StatxBasicStats = StatxMask(C.CEPH_STATX_BASIC_STATS)
|
||||||
|
// StatxBtime requests the birth-time value be filled in.
|
||||||
|
StatxBtime = StatxMask(C.CEPH_STATX_BTIME)
|
||||||
|
// StatxVersion requests the version value be filled in.
|
||||||
|
StatxVersion = StatxMask(C.CEPH_STATX_VERSION)
|
||||||
|
// StatxAllStats requests all known stat values be filled in.
|
||||||
|
StatxAllStats = StatxMask(C.CEPH_STATX_ALL_STATS)
|
||||||
|
)
|
||||||
|
|
||||||
|
// AtFlags represent flags to be passed to calls that control how files
|
||||||
|
// are used or referenced. For example, not following symlinks.
|
||||||
|
type AtFlags uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AtNoAttrSync requests that the stat call only fetch locally-cached
|
||||||
|
// values if possible, avoiding round trips to a back-end server.
|
||||||
|
AtNoAttrSync = AtFlags(C.AT_NO_ATTR_SYNC)
|
||||||
|
// AtSymlinkNofollow indicates the call should not follow symlinks
|
||||||
|
// but operate on the symlink itself.
|
||||||
|
AtSymlinkNofollow = AtFlags(C.AT_SYMLINK_NOFOLLOW)
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: CephStatx fields are meant to be settable by the callers.
|
||||||
|
// This is the primary reason we use public fields and not accessors
|
||||||
|
// for the CephStatx type.
|
||||||
|
|
||||||
|
// CephStatx instances are returned by extended stat (statx) calls.
|
||||||
|
// Note that CephStatx results are similar to but not identical
|
||||||
|
// to (Linux) system statx results.
|
||||||
|
type CephStatx struct {
|
||||||
|
// Mask is a bitmask indicating what fields have been set.
|
||||||
|
Mask StatxMask
|
||||||
|
// Blksize represents the file system's block size.
|
||||||
|
Blksize uint32
|
||||||
|
// Nlink is the number of links for the file.
|
||||||
|
Nlink uint32
|
||||||
|
// Uid (user id) value for the file.
|
||||||
|
Uid uint32
|
||||||
|
// Gid (group id) value for the file.
|
||||||
|
Gid uint32
|
||||||
|
// Mode is the file's type and mode value.
|
||||||
|
Mode uint16
|
||||||
|
// Inode value for the file.
|
||||||
|
Inode Inode
|
||||||
|
// Size of the file in bytes.
|
||||||
|
Size uint64
|
||||||
|
// Blocks indicates the number of blocks allocated to the file.
|
||||||
|
Blocks uint64
|
||||||
|
// Dev describes the device containing this file system.
|
||||||
|
Dev uint64
|
||||||
|
// Rdev describes the device of this file, if the file is a device.
|
||||||
|
Rdev uint64
|
||||||
|
// Atime is the access time of this file.
|
||||||
|
Atime Timespec
|
||||||
|
// Ctime is the status change time of this file.
|
||||||
|
Ctime Timespec
|
||||||
|
// Mtime is the modification time of this file.
|
||||||
|
Mtime Timespec
|
||||||
|
// Btime is the creation (birth) time of this file.
|
||||||
|
Btime Timespec
|
||||||
|
// Version value for the file.
|
||||||
|
Version uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func cStructToCephStatx(s C.struct_ceph_statx) *CephStatx {
|
||||||
|
return &CephStatx{
|
||||||
|
Mask: StatxMask(s.stx_mask),
|
||||||
|
Blksize: uint32(s.stx_blksize),
|
||||||
|
Nlink: uint32(s.stx_nlink),
|
||||||
|
Uid: uint32(s.stx_uid),
|
||||||
|
Gid: uint32(s.stx_gid),
|
||||||
|
Mode: uint16(s.stx_mode),
|
||||||
|
Inode: Inode(s.stx_ino),
|
||||||
|
Size: uint64(s.stx_size),
|
||||||
|
Blocks: uint64(s.stx_blocks),
|
||||||
|
Dev: uint64(s.stx_dev),
|
||||||
|
Rdev: uint64(s.stx_rdev),
|
||||||
|
Atime: cStructToTimespec(s.stx_atime),
|
||||||
|
Ctime: cStructToTimespec(s.stx_ctime),
|
||||||
|
Mtime: cStructToTimespec(s.stx_mtime),
|
||||||
|
Btime: cStructToTimespec(s.stx_btime),
|
||||||
|
Version: uint64(s.stx_version),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
- enable later when we can test round -trips
|
||||||
|
- add time fields
|
||||||
|
|
||||||
|
func (c *CephStatx) toCStruct() C.struct_ceph_statx {
|
||||||
|
var s C.struct_ceph_statx
|
||||||
|
s.stx_mask = C.uint32_t(c.Mask)
|
||||||
|
s.stx_blksize = C.uint32_t(c.Blksize)
|
||||||
|
s.stx_nlink = C.uint32_t(c.Nlink)
|
||||||
|
s.stx_uid = C.uint32_t(c.Uid)
|
||||||
|
s.stx_gid = C.uint32_t(c.Gid)
|
||||||
|
s.stx_mode = C.uint16_t(c.Mode)
|
||||||
|
s.stx_ino = C.uint64_t(c.Inode)
|
||||||
|
s.stx_size = C.uint64_t(c.Size)
|
||||||
|
s.stx_blocks = C.uint64_t(c.Blocks)
|
||||||
|
s.stx_dev = C.uint64_t(c.Dev)
|
||||||
|
s.stx_rdev = C.uint64_t(c.Rdev)
|
||||||
|
s.stx_version = C.uint64_t(c.Version)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,30 @@
|
||||||
|
package cephfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestStatxFieldsRootDir does not assert much about every field
|
||||||
|
// as these can vary between runs. We exercise the getters but
|
||||||
|
// can only make "lightweight" assertions here.
|
||||||
|
func TestStatxFieldsRootDir(t *testing.T) {
|
||||||
|
mount := fsConnect(t)
|
||||||
|
defer fsDisconnect(t, mount)
|
||||||
|
|
||||||
|
st, err := mount.Statx("/", StatxBasicStats, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, st)
|
||||||
|
|
||||||
|
assert.Equal(t, StatxBasicStats, st.Mask&StatxBasicStats)
|
||||||
|
assert.Equal(t, uint32(2), st.Nlink)
|
||||||
|
assert.Equal(t, uint32(0), st.Uid)
|
||||||
|
assert.Equal(t, uint32(0), st.Gid)
|
||||||
|
assert.NotEqual(t, uint16(0), st.Mode)
|
||||||
|
assert.Equal(t, uint16(0040000), st.Mode&0040000) // is dir?
|
||||||
|
assert.NotEqual(t, Inode(0), st.Inode)
|
||||||
|
assert.NotEqual(t, uint64(0), st.Dev)
|
||||||
|
assert.Equal(t, uint64(0), st.Rdev)
|
||||||
|
assert.Greater(t, st.Ctime.Sec, int64(1588711788))
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package cephfs
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <time.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Timespec behaves similarly to C's struct timespec.
|
||||||
|
// Timespec is used to retain fidelity to the C based file systems
|
||||||
|
// apis that could be lossy with the use of Go time types.
|
||||||
|
type Timespec unix.Timespec
|
||||||
|
|
||||||
|
func cStructToTimespec(t C.struct_timespec) Timespec {
|
||||||
|
return Timespec{
|
||||||
|
Sec: int64(t.tv_sec),
|
||||||
|
Nsec: int64(t.tv_nsec),
|
||||||
|
}
|
||||||
|
}
|
1
go.mod
1
go.mod
|
@ -5,4 +5,5 @@ go 1.12
|
||||||
require (
|
require (
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible
|
github.com/gofrs/uuid v3.2.0+incompatible
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -7,6 +7,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
|
||||||
|
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
|
Loading…
Reference in New Issue