aboutsummaryrefslogtreecommitdiffhomepage
path: root/system/acl/acl_getfacl_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'system/acl/acl_getfacl_test.go')
-rw-r--r--system/acl/acl_getfacl_test.go156
1 files changed, 156 insertions, 0 deletions
diff --git a/system/acl/acl_getfacl_test.go b/system/acl/acl_getfacl_test.go
new file mode 100644
index 00000000..c20fade7
--- /dev/null
+++ b/system/acl/acl_getfacl_test.go
@@ -0,0 +1,156 @@
+package acl_test
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "os/exec"
+ "strconv"
+)
+
+type (
+ getFAclInvocation struct {
+ cmd *exec.Cmd
+ val []*getFAclResp
+ pe []error
+ }
+
+ getFAclResp struct {
+ typ fAclType
+ cred int32
+ val fAclPerm
+
+ raw []byte
+ }
+
+ fAclPerm uintptr
+ fAclType uint8
+)
+
+const fAclBufSize = 16
+
+const (
+ fAclPermRead fAclPerm = 1 << iota
+ fAclPermWrite
+ fAclPermExecute
+)
+
+const (
+ fAclTypeUser fAclType = iota
+ fAclTypeGroup
+ fAclTypeMask
+ fAclTypeOther
+)
+
+func (c *getFAclInvocation) run(name string) error {
+ if c.cmd != nil {
+ panic("attempted to run twice")
+ }
+
+ c.cmd = exec.Command("getfacl", "--omit-header", "--absolute-names", "--numeric", name)
+
+ scanErr := make(chan error, 1)
+ if p, err := c.cmd.StdoutPipe(); err != nil {
+ return err
+ } else {
+ go c.parse(p, scanErr)
+ }
+
+ if err := c.cmd.Start(); err != nil {
+ return err
+ }
+
+ return errors.Join(<-scanErr, c.cmd.Wait())
+}
+
+func (c *getFAclInvocation) parse(pipe io.Reader, scanErr chan error) {
+ c.val = make([]*getFAclResp, 0, 4+fAclBufSize)
+
+ s := bufio.NewScanner(pipe)
+ for s.Scan() {
+ fields := bytes.SplitN(s.Bytes(), []byte{':'}, 3)
+ if len(fields) != 3 {
+ continue
+ }
+
+ resp := getFAclResp{}
+
+ switch string(fields[0]) {
+ case "user":
+ resp.typ = fAclTypeUser
+ case "group":
+ resp.typ = fAclTypeGroup
+ case "mask":
+ resp.typ = fAclTypeMask
+ case "other":
+ resp.typ = fAclTypeOther
+ default:
+ c.pe = append(c.pe, fmt.Errorf("unknown type %s", string(fields[0])))
+ continue
+ }
+
+ if len(fields[1]) == 0 {
+ resp.cred = -1
+ } else {
+ if cred, err := strconv.Atoi(string(fields[1])); err != nil {
+ c.pe = append(c.pe, err)
+ continue
+ } else {
+ resp.cred = int32(cred)
+ if resp.cred < 0 {
+ c.pe = append(c.pe, fmt.Errorf("credential %d out of range", resp.cred))
+ continue
+ }
+ }
+ }
+
+ if len(fields[2]) != 3 {
+ c.pe = append(c.pe, fmt.Errorf("invalid perm length %d", len(fields[2])))
+ continue
+ } else {
+ switch fields[2][0] {
+ case 'r':
+ resp.val |= fAclPermRead
+ case '-':
+ default:
+ c.pe = append(c.pe, fmt.Errorf("invalid perm %v", fields[2][0]))
+ continue
+ }
+ switch fields[2][1] {
+ case 'w':
+ resp.val |= fAclPermWrite
+ case '-':
+ default:
+ c.pe = append(c.pe, fmt.Errorf("invalid perm %v", fields[2][1]))
+ continue
+ }
+ switch fields[2][2] {
+ case 'x':
+ resp.val |= fAclPermExecute
+ case '-':
+ default:
+ c.pe = append(c.pe, fmt.Errorf("invalid perm %v", fields[2][2]))
+ continue
+ }
+ }
+
+ resp.raw = make([]byte, len(s.Bytes()))
+ copy(resp.raw, s.Bytes())
+ c.val = append(c.val, &resp)
+ }
+ scanErr <- s.Err()
+}
+
+func (r *getFAclResp) String() string {
+ if r.raw != nil && len(r.raw) > 0 {
+ return string(r.raw)
+ }
+
+ return "(user-initialised resp value)"
+}
+
+func (r *getFAclResp) equals(typ fAclType, cred int32, val fAclPerm) bool {
+ return r.typ == typ && r.cred == cred && r.val == val
+}