acl: refactor and clean up
All checks were successful
test / test (push) Successful in 37s

Move all C code to c.go, switch to pkg-config, set up finalizer for acl.

Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
Ophestra 2024-12-16 18:27:19 +09:00
parent ee41b37606
commit c8a90666c5
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
3 changed files with 147 additions and 138 deletions

19
acl/acl.go Normal file
View File

@ -0,0 +1,19 @@
// Package acl implements simple ACL manipulation via libacl.
package acl
type Perms []Perm
func (ps Perms) String() string {
var s = []byte("---")
for _, p := range ps {
switch p {
case Read:
s[0] = 'r'
case Write:
s[1] = 'w'
case Execute:
s[2] = 'x'
}
}
return string(s)
}

159
acl/c.go
View File

@ -1,50 +1,95 @@
package acl package acl
import "C"
import ( import (
"errors" "errors"
"fmt" "runtime"
"syscall" "syscall"
"unsafe" "unsafe"
) )
//#include <stdlib.h> /*
//#include <sys/acl.h> #cgo linux pkg-config: libacl
//#include <acl/libacl.h>
//#cgo linux LDFLAGS: -lacl
import "C"
type acl struct { #include <stdlib.h>
val C.acl_t #include <sys/acl.h>
freed bool #include <acl/libacl.h>
static acl_t _go_acl_get_file(const char *path_p, acl_type_t type) {
acl_t acl = acl_get_file(path_p, type);
free((void *)path_p);
return acl;
} }
func aclGetFile(path string, t C.acl_type_t) (*acl, error) { static int _go_acl_set_file(const char *path_p, acl_type_t type, acl_t acl) {
p := C.CString(path) if (acl_valid(acl) != 0) {
a, err := C.acl_get_file(p, t) return -1;
C.free(unsafe.Pointer(p)) }
int ret = acl_set_file(path_p, type, acl);
free((void *)path_p);
return ret;
}
*/
import "C"
func getFile(name string, t C.acl_type_t) (*ACL, error) {
a, err := C._go_acl_get_file(C.CString(name), t)
if errors.Is(err, syscall.ENODATA) { if errors.Is(err, syscall.ENODATA) {
err = nil err = nil
} }
return &acl{val: a, freed: false}, err
return newACL(a), err
} }
func (a *acl) setFile(path string, t C.acl_type_t) error { func (acl *ACL) setFile(name string, t C.acl_type_t) error {
if C.acl_valid(a.val) != 0 { _, err := C._go_acl_set_file(C.CString(name), t, acl.acl)
return fmt.Errorf("invalid acl")
}
p := C.CString(path)
_, err := C.acl_set_file(p, t, a.val)
C.free(unsafe.Pointer(p))
return err return err
} }
func (a *acl) removeEntry(tt C.acl_tag_t, tq int) error { func newACL(a C.acl_t) *ACL {
acl := &ACL{a}
runtime.SetFinalizer(acl, (*ACL).free)
return acl
}
type ACL struct {
acl C.acl_t
}
func (acl *ACL) free() {
C.acl_free(unsafe.Pointer(acl.acl))
// no need for a finalizer anymore
runtime.SetFinalizer(acl, nil)
}
const (
Read = C.ACL_READ
Write = C.ACL_WRITE
Execute = C.ACL_EXECUTE
TypeDefault = C.ACL_TYPE_DEFAULT
TypeAccess = C.ACL_TYPE_ACCESS
UndefinedTag = C.ACL_UNDEFINED_TAG
UserObj = C.ACL_USER_OBJ
User = C.ACL_USER
GroupObj = C.ACL_GROUP_OBJ
Group = C.ACL_GROUP
Mask = C.ACL_MASK
Other = C.ACL_OTHER
)
type (
Perm C.acl_perm_t
)
func (acl *ACL) removeEntry(tt C.acl_tag_t, tq int) error {
var e C.acl_entry_t var e C.acl_entry_t
// get first entry // get first entry
if r, err := C.acl_get_entry(a.val, C.ACL_FIRST_ENTRY, &e); err != nil { if r, err := C.acl_get_entry(acl.acl, C.ACL_FIRST_ENTRY, &e); err != nil {
return err return err
} else if r == 0 { } else if r == 0 {
// return on acl with no entries // return on acl with no entries
@ -52,7 +97,7 @@ func (a *acl) removeEntry(tt C.acl_tag_t, tq int) error {
} }
for { for {
if r, err := C.acl_get_entry(a.val, C.ACL_NEXT_ENTRY, &e); err != nil { if r, err := C.acl_get_entry(acl.acl, C.ACL_NEXT_ENTRY, &e); err != nil {
return err return err
} else if r == 0 { } else if r == 0 {
// return on drained acl // return on drained acl
@ -84,16 +129,68 @@ func (a *acl) removeEntry(tt C.acl_tag_t, tq int) error {
// delete on match // delete on match
if t == tt && q == tq { if t == tt && q == tq {
_, err := C.acl_delete_entry(a.val, e) _, err := C.acl_delete_entry(acl.acl, e)
return err return err
} }
} }
} }
func (a *acl) free() { func UpdatePerm(name string, uid int, perms ...Perm) error {
if a.freed { // read acl from file
panic("acl already freed") a, err := getFile(name, TypeAccess)
if err != nil {
return err
} }
C.acl_free(unsafe.Pointer(a.val)) // free acl on return if get is successful
a.freed = true defer a.free()
// remove existing entry
if err = a.removeEntry(User, uid); err != nil {
return err
}
// create new entry if perms are passed
if len(perms) > 0 {
// create new acl entry
var e C.acl_entry_t
if _, err = C.acl_create_entry(&a.acl, &e); err != nil {
return err
}
// get perm set of new entry
var p C.acl_permset_t
if _, err = C.acl_get_permset(e, &p); err != nil {
return err
}
// add target perms
for _, perm := range perms {
if _, err = C.acl_add_perm(p, C.acl_perm_t(perm)); err != nil {
return err
}
}
// set perm set to new entry
if _, err = C.acl_set_permset(e, p); err != nil {
return err
}
// set user tag to new entry
if _, err = C.acl_set_tag_type(e, User); err != nil {
return err
}
// set qualifier (uid) to new entry
if _, err = C.acl_set_qualifier(e, unsafe.Pointer(&uid)); err != nil {
return err
}
}
// calculate mask after update
if _, err = C.acl_calc_mask(&a.acl); err != nil {
return err
}
// write acl to file
return a.setFile(name, TypeAccess)
} }

View File

@ -1,107 +0,0 @@
// Package acl implements simple ACL manipulation via libacl.
package acl
import "unsafe"
//#include <stdlib.h>
//#include <sys/acl.h>
//#include <acl/libacl.h>
//#cgo linux LDFLAGS: -lacl
import "C"
const (
Read = C.ACL_READ
Write = C.ACL_WRITE
Execute = C.ACL_EXECUTE
TypeDefault = C.ACL_TYPE_DEFAULT
TypeAccess = C.ACL_TYPE_ACCESS
UndefinedTag = C.ACL_UNDEFINED_TAG
UserObj = C.ACL_USER_OBJ
User = C.ACL_USER
GroupObj = C.ACL_GROUP_OBJ
Group = C.ACL_GROUP
Mask = C.ACL_MASK
Other = C.ACL_OTHER
)
type (
Perm C.acl_perm_t
Perms []Perm
)
func (ps Perms) String() string {
var s = []byte("---")
for _, p := range ps {
switch p {
case Read:
s[0] = 'r'
case Write:
s[1] = 'w'
case Execute:
s[2] = 'x'
}
}
return string(s)
}
func UpdatePerm(path string, uid int, perms ...Perm) error {
// read acl from file
a, err := aclGetFile(path, TypeAccess)
if err != nil {
return err
}
// free acl on return if get is successful
defer a.free()
// remove existing entry
if err = a.removeEntry(User, uid); err != nil {
return err
}
// create new entry if perms are passed
if len(perms) > 0 {
// create new acl entry
var e C.acl_entry_t
if _, err = C.acl_create_entry(&a.val, &e); err != nil {
return err
}
// get perm set of new entry
var p C.acl_permset_t
if _, err = C.acl_get_permset(e, &p); err != nil {
return err
}
// add target perms
for _, perm := range perms {
if _, err = C.acl_add_perm(p, C.acl_perm_t(perm)); err != nil {
return err
}
}
// set perm set to new entry
if _, err = C.acl_set_permset(e, p); err != nil {
return err
}
// set user tag to new entry
if _, err = C.acl_set_tag_type(e, User); err != nil {
return err
}
// set qualifier (uid) to new entry
if _, err = C.acl_set_qualifier(e, unsafe.Pointer(&uid)); err != nil {
return err
}
}
// calculate mask after update
if _, err = C.acl_calc_mask(&a.val); err != nil {
return err
}
// write acl to file
return a.setFile(path, TypeAccess)
}