From c8a90666c5a683df2259d15683086a3a8dfdde14 Mon Sep 17 00:00:00 2001 From: Ophestra Umiker Date: Mon, 16 Dec 2024 18:27:19 +0900 Subject: [PATCH] acl: refactor and clean up Move all C code to c.go, switch to pkg-config, set up finalizer for acl. Signed-off-by: Ophestra Umiker --- acl/acl.go | 19 ++++++ acl/c.go | 159 ++++++++++++++++++++++++++++++++++++++++---------- acl/export.go | 107 --------------------------------- 3 files changed, 147 insertions(+), 138 deletions(-) create mode 100644 acl/acl.go delete mode 100644 acl/export.go diff --git a/acl/acl.go b/acl/acl.go new file mode 100644 index 0000000..8b77568 --- /dev/null +++ b/acl/acl.go @@ -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) +} diff --git a/acl/c.go b/acl/c.go index 89bc046..4bbc549 100644 --- a/acl/c.go +++ b/acl/c.go @@ -1,50 +1,95 @@ package acl +import "C" import ( "errors" - "fmt" + "runtime" "syscall" "unsafe" ) -//#include -//#include -//#include -//#cgo linux LDFLAGS: -lacl -import "C" +/* +#cgo linux pkg-config: libacl -type acl struct { - val C.acl_t - freed bool +#include +#include +#include + +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) { - p := C.CString(path) - a, err := C.acl_get_file(p, t) - C.free(unsafe.Pointer(p)) +static int _go_acl_set_file(const char *path_p, acl_type_t type, acl_t acl) { + if (acl_valid(acl) != 0) { + return -1; + } + 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) { 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 { - if C.acl_valid(a.val) != 0 { - return fmt.Errorf("invalid acl") - } - - p := C.CString(path) - _, err := C.acl_set_file(p, t, a.val) - C.free(unsafe.Pointer(p)) +func (acl *ACL) setFile(name string, t C.acl_type_t) error { + _, err := C._go_acl_set_file(C.CString(name), t, acl.acl) 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 // 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 } else if r == 0 { // return on acl with no entries @@ -52,7 +97,7 @@ func (a *acl) removeEntry(tt C.acl_tag_t, tq int) error { } 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 } else if r == 0 { // return on drained acl @@ -84,16 +129,68 @@ func (a *acl) removeEntry(tt C.acl_tag_t, tq int) error { // delete on match if t == tt && q == tq { - _, err := C.acl_delete_entry(a.val, e) + _, err := C.acl_delete_entry(acl.acl, e) return err } } } -func (a *acl) free() { - if a.freed { - panic("acl already freed") +func UpdatePerm(name string, uid int, perms ...Perm) error { + // read acl from file + a, err := getFile(name, TypeAccess) + if err != nil { + return err } - C.acl_free(unsafe.Pointer(a.val)) - a.freed = true + // 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.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) } diff --git a/acl/export.go b/acl/export.go deleted file mode 100644 index 322ea1d..0000000 --- a/acl/export.go +++ /dev/null @@ -1,107 +0,0 @@ -// Package acl implements simple ACL manipulation via libacl. -package acl - -import "unsafe" - -//#include -//#include -//#include -//#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) -}