100 lines
1.7 KiB
Go
100 lines
1.7 KiB
Go
package acl
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
//#include <stdlib.h>
|
|
//#include <sys/acl.h>
|
|
//#include <acl/libacl.h>
|
|
//#cgo linux LDFLAGS: -lacl
|
|
import "C"
|
|
|
|
type acl struct {
|
|
val C.acl_t
|
|
freed bool
|
|
}
|
|
|
|
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))
|
|
|
|
if errors.Is(err, syscall.ENODATA) {
|
|
err = nil
|
|
}
|
|
return &acl{val: a, freed: false}, 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))
|
|
return err
|
|
}
|
|
|
|
func (a *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 {
|
|
return err
|
|
} else if r == 0 {
|
|
// return on acl with no entries
|
|
return nil
|
|
}
|
|
|
|
for {
|
|
if r, err := C.acl_get_entry(a.val, C.ACL_NEXT_ENTRY, &e); err != nil {
|
|
return err
|
|
} else if r == 0 {
|
|
// return on drained acl
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
q int
|
|
t C.acl_tag_t
|
|
)
|
|
|
|
// get current entry tag type
|
|
if _, err := C.acl_get_tag_type(e, &t); err != nil {
|
|
return err
|
|
}
|
|
|
|
// get current entry qualifier
|
|
if rq, err := C.acl_get_qualifier(e); err != nil {
|
|
// neither ACL_USER nor ACL_GROUP
|
|
if errors.Is(err, syscall.EINVAL) {
|
|
continue
|
|
}
|
|
|
|
return err
|
|
} else {
|
|
q = *(*int)(rq)
|
|
C.acl_free(rq)
|
|
}
|
|
|
|
// delete on match
|
|
if t == tt && q == tq {
|
|
_, err := C.acl_delete_entry(a.val, e)
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *acl) free() {
|
|
if a.freed {
|
|
panic("acl already freed")
|
|
}
|
|
C.acl_free(unsafe.Pointer(a.val))
|
|
a.freed = true
|
|
}
|