// Package acl implements simple ACL manipulation via libacl. package acl import ( "errors" "runtime" "syscall" "unsafe" ) /* #cgo linux pkg-config: --static libacl #include "acl-update.h" */ import "C" func getFile(name string, t C.acl_type_t) (*ACL, error) { a, err := C.f_acl_get_file(C.CString(name), t) if errors.Is(err, syscall.ENODATA) { err = nil } return newACL(a), err } func (acl *ACL) setFile(name string, t C.acl_type_t) error { _, err := C.f_acl_set_file(C.CString(name), t, acl.acl) return err } 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(uid int) error { _, err := C.f_acl_delete_by_uid(acl.acl, C.uid_t(uid)) return err } // Update replaces ACL_USER entry with qualifier uid. func Update(name string, uid int, perms ...Perm) error { // read acl from file a, err := getFile(name, TypeAccess) if err != nil { return err } // free acl on return if get is successful defer a.free() // remove existing entry if err = a.removeEntry(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) }