diff --git a/cephfs/userperm.go b/cephfs/userperm.go new file mode 100644 index 0000000..fe816ad --- /dev/null +++ b/cephfs/userperm.go @@ -0,0 +1,72 @@ +package cephfs + +/* +#cgo LDFLAGS: -lcephfs +#cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64 +#include +*/ +import "C" + +import ( + "runtime" + "unsafe" +) + +// UserPerm types may be used to get or change the credentials used by the +// connection or some operations. +type UserPerm struct { + userPerm *C.UserPerm + + // cache create-time params + managed bool // if set, the userPerm was created by go-ceph + uid C.uid_t + gid C.gid_t + gidList []C.gid_t +} + +// NewUserPerm creates a UserPerm pointer and the underlying ceph resources. +// +// Implements: +// UserPerm *ceph_userperm_new(uid_t uid, gid_t gid, int ngids, gid_t *gidlist); +func NewUserPerm(uid, gid int, gidlist []int) *UserPerm { + // the C code does not copy the content of the gid list so we keep the + // inputs stashed in the go type. For completeness we stash everything. + p := &UserPerm{ + managed: true, + uid: C.uid_t(uid), + gid: C.gid_t(gid), + gidList: make([]C.gid_t, len(gidlist)), + } + var cgids *C.gid_t + if len(p.gidList) > 0 { + for i, gid := range gidlist { + p.gidList[i] = C.gid_t(gid) + } + cgids = (*C.gid_t)(unsafe.Pointer(&p.gidList[0])) + } + p.userPerm = C.ceph_userperm_new( + p.uid, p.gid, C.int(len(p.gidList)), cgids) + // if the go object is unreachable, we would like to free the c-memory + // since this has no other resources than memory associated with it. + // This is only valid for UserPerm objects created by new, and thus have + // the managed var set. + runtime.SetFinalizer(p, destroyUserPerm) + return p +} + +// Destroy will explicitly free ceph resources associated with the UserPerm. +// +// Implements: +// void ceph_userperm_destroy(UserPerm *perm); +func (p *UserPerm) Destroy() { + if p.userPerm == nil || !p.managed { + return + } + C.ceph_userperm_destroy(p.userPerm) + p.userPerm = nil + p.gidList = nil +} + +func destroyUserPerm(p *UserPerm) { + p.Destroy() +} diff --git a/cephfs/userperm_test.go b/cephfs/userperm_test.go new file mode 100644 index 0000000..d006c8d --- /dev/null +++ b/cephfs/userperm_test.go @@ -0,0 +1,43 @@ +package cephfs + +import ( + "runtime" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUserPerm(t *testing.T) { + t.Run("newAndDestroy", func(t *testing.T) { + uperm := NewUserPerm(0, 0, nil) + assert.NotNil(t, uperm) + assert.Equal(t, 0, len(uperm.gidList)) + assert.True(t, uperm.managed) + + // Destroy should be idempotent in our go library + uperm.Destroy() + uperm.Destroy() + }) + t.Run("notManagedDestroy", func(t *testing.T) { + uperm := &UserPerm{} + assert.False(t, uperm.managed) + // Calling destroy shouldn't do much but is safe to call (many times) + uperm.Destroy() + uperm.Destroy() + }) + t.Run("tryForceGc", func(t *testing.T) { + func() { + uperm := NewUserPerm(0, 0, nil) + _ = uperm + }() + runtime.GC() + }) + t.Run("gidList", func(t *testing.T) { + uperm := NewUserPerm(1000, 1000, []int{1028, 1192, 2112}) + defer uperm.Destroy() + assert.Equal(t, 3, len(uperm.gidList)) + assert.EqualValues(t, 1028, uperm.gidList[0]) + assert.EqualValues(t, 1192, uperm.gidList[1]) + assert.EqualValues(t, 2112, uperm.gidList[2]) + }) +}