aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/acl
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-13 18:55:52 +0900
committerOphestra <cat@gensokyo.uk>2026-10-13 18:55:52 +0900
commit0abf367b1334b76f5e8f989cf0c07b5d00b25868 (patch)
tree9d78aae80b86770762e5d86358fd1d89c18c3f35 /internal/acl
parent11e395a1a7453b93c695e849bd93d8c2c0af6aef (diff)
internal/acl: skip superfluous acl_set_file
This slightly reduces startup latency. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/acl')
-rw-r--r--internal/acl/acl.go84
-rw-r--r--internal/acl/acl_test.go136
-rw-r--r--internal/acl/libacl-helper.c87
-rw-r--r--internal/acl/libacl-helper.go40
-rw-r--r--internal/acl/libacl-helper.h24
-rw-r--r--internal/acl/libacl-helper_test.go47
-rw-r--r--internal/acl/perms.go26
-rw-r--r--internal/acl/perms_test.go20
8 files changed, 302 insertions, 162 deletions
diff --git a/internal/acl/acl.go b/internal/acl/acl.go
index 366ad533..7718ba3c 100644
--- a/internal/acl/acl.go
+++ b/internal/acl/acl.go
@@ -7,27 +7,89 @@ package acl
#include "libacl-helper.h"
*/
import "C"
+import "os"
+// Perm represents acl_perm_t.
type Perm C.acl_perm_t
const (
- Read Perm = C.ACL_READ
- Write Perm = C.ACL_WRITE
+ // Read represents ACL_READ.
+ Read Perm = C.ACL_READ
+ // Write represents ACL_WRITE.
+ Write Perm = C.ACL_WRITE
+ // Execute represents ACL_EXECUTE.
Execute Perm = C.ACL_EXECUTE
)
-// Update replaces ACL_USER entry with qualifier uid.
-func Update(name string, uid int, perms ...Perm) error {
- var p *Perm
- if len(perms) > 0 {
- p = &perms[0]
+// Status is the outcome of an ACL update operation.
+type Status = C.enum_hakurei_acl_status
+
+// An Error describes an error encountered during [Update].
+type Error struct {
+ // Pathname of the file to update ACL for.
+ Path string
+ // Function that returned the error.
+ Op Status
+ // Underlying errno.
+ Err error
+}
+
+// Unwrap returns an [os.PathError] describing e.
+func (e Error) Unwrap() error {
+ v := os.PathError{Path: e.Path, Err: e.Err}
+ switch e.Op {
+ case C.HAKUREI_ACL_OK:
+ return os.ErrInvalid
+
+ case C.HAKUREI_ACL_GET_FILE:
+ v.Op = "acl_get_file"
+ case C.HAKUREI_ACL_GET_TAG_TYPE:
+ v.Op = "acl_get_tag_type"
+ case C.HAKUREI_ACL_GET_QUALIFIER:
+ v.Op = "acl_get_qualifier"
+ case C.HAKUREI_ACL_DELETE_ENTRY:
+ v.Op = "acl_delete_entry"
+ case C.HAKUREI_ACL_CREATE_ENTRY:
+ v.Op = "acl_create_entry"
+ case C.HAKUREI_ACL_GET_PERMSET:
+ v.Op = "acl_get_permset"
+ case C.HAKUREI_ACL_ADD_PERM:
+ v.Op = "acl_add_perm"
+ case C.HAKUREI_ACL_SET_TAG_TYPE:
+ v.Op = "acl_set_tag_type"
+ case C.HAKUREI_ACL_SET_QUALIFIER:
+ v.Op = "acl_set_qualifier"
+ case C.HAKUREI_ACL_CALC_MASK:
+ v.Op = "acl_calc_mask"
+ case C.HAKUREI_ACL_VALID:
+ v.Op = "acl_valid"
+ case C.HAKUREI_ACL_SET_FILE:
+ v.Op = "acl_set_file"
+ case C.HAKUREI_ACL_GET_PERM:
+ v.Op = "acl_get_perm"
+ case C.HAKUREI_ACL_DUP:
+ v.Op = "acl_dup"
+
+ default: // unreachable
+ v.Op = "setfacl"
}
+ return &v
+}
+
+// Error returns the message of the resulting [os.PathError].
+func (e Error) Error() string {
+ return e.Unwrap().Error()
+}
- r, err := C.hakurei_acl_update_file_by_uid(
+// Update replaces ACL_USER entry with qualifier uid.
+func Update(name string, uid int, perms Perm) error {
+ r, err := C.hakurei_update_acl(
C.CString(name),
C.uid_t(uid),
- (*C.acl_perm_t)(p),
- C.size_t(len(perms)),
+ C.acl_perm_t(perms),
)
- return newAclPathError(name, int(r), err)
+ if r == C.HAKUREI_ACL_OK {
+ return nil
+ }
+ return &Error{name, r, err}
}
diff --git a/internal/acl/acl_test.go b/internal/acl/acl_test.go
index 60c4b702..6e84e832 100644
--- a/internal/acl/acl_test.go
+++ b/internal/acl/acl_test.go
@@ -22,7 +22,7 @@ const testFileName = "acl.test"
var (
uid = os.Geteuid()
- cred = int32(os.Geteuid())
+ cred = int32(uid)
)
func TestUpdate(t *testing.T) {
@@ -31,7 +31,7 @@ func TestUpdate(t *testing.T) {
if err := os.WriteFile(name, nil, 0); err != nil {
t.Fatal(err)
}
- if err := acl.Update(name, os.Geteuid()); errors.Is(err, syscall.ENOTSUP) {
+ if err := acl.Update(name, uid, 0); errors.Is(err, syscall.ENOTSUP) {
t.Skip(err)
}
}
@@ -60,7 +60,7 @@ func TestUpdate(t *testing.T) {
})
t.Run("default clear mask", func(t *testing.T) {
- if err := acl.Update(testFilePath, uid); err != nil {
+ if err := acl.Update(testFilePath, uid, 0); err != nil {
t.Fatalf("Update: error = %v", err)
}
if cur = getfacl(t, testFilePath); len(cur) != 4 {
@@ -69,7 +69,7 @@ func TestUpdate(t *testing.T) {
})
t.Run("default clear consistency", func(t *testing.T) {
- if err := acl.Update(testFilePath, uid); err != nil {
+ if err := acl.Update(testFilePath, uid, 0); err != nil {
t.Fatalf("Update: error = %v", err)
}
if val := getfacl(t, testFilePath); !reflect.DeepEqual(val, cur) {
@@ -77,34 +77,78 @@ func TestUpdate(t *testing.T) {
}
})
- testUpdate(t, testFilePath, "r--", cur, fAclPermRead, acl.Read)
- testUpdate(t, testFilePath, "-w-", cur, fAclPermWrite, acl.Write)
- testUpdate(t, testFilePath, "--x", cur, fAclPermExecute, acl.Execute)
- testUpdate(t, testFilePath, "-wx", cur, fAclPermWrite|fAclPermExecute, acl.Write, acl.Execute)
- testUpdate(t, testFilePath, "r-x", cur, fAclPermRead|fAclPermExecute, acl.Read, acl.Execute)
- testUpdate(t, testFilePath, "rw-", cur, fAclPermRead|fAclPermWrite, acl.Read, acl.Write)
- testUpdate(t, testFilePath, "rwx", cur, fAclPermRead|fAclPermWrite|fAclPermExecute, acl.Read, acl.Write, acl.Execute)
+ testUpdate(t, testFilePath, "r--", "", cur, acl.Read, true)
+ testUpdate(t, testFilePath, "-w-", "", cur, acl.Write, true)
+ testUpdate(t, testFilePath, "--x", "", cur, acl.Execute, true)
+
+ testUpdate(
+ t, testFilePath, "-wx", "", cur,
+ acl.Write|acl.Execute,
+ true,
+ )
+ testUpdate(
+ t, testFilePath, "r-x", "", cur,
+ acl.Read|acl.Execute,
+ true,
+ )
+ testUpdate(
+ t, testFilePath, "rw-", "", cur,
+ acl.Read|acl.Write,
+ true,
+ )
+ testUpdate(
+ t, testFilePath, "rwx", "", cur,
+ acl.Read|acl.Write|acl.Execute,
+ true,
+ )
+
+ for i := range 16 {
+ testUpdate(
+ t, testFilePath, "r-x", " "+strconv.Itoa(i), cur,
+ acl.Read|acl.Execute,
+ false,
+ )
+ }
+
+ testUpdate(
+ t, testFilePath, "rwx", " cleanup", cur,
+ acl.Read|acl.Write|acl.Execute,
+ true,
+ )
}
-func testUpdate(t *testing.T, testFilePath, name string, cur []*getFAclResp, val fAclPerm, perms ...acl.Perm) {
- t.Run(name, func(t *testing.T) {
- t.Cleanup(func() {
- if err := acl.Update(testFilePath, uid); err != nil {
- t.Fatalf("Update: error = %v", err)
- }
- if v := getfacl(t, testFilePath); !reflect.DeepEqual(v, cur) {
- t.Fatalf("Update: %v, want %v", v, cur)
- }
- })
+func testUpdate(
+ t *testing.T,
+ testFilePath, name, suffix string,
+ cur []*getFACLResp,
+ perms acl.Perm,
+ cleanup bool,
+) {
+ t.Run(name+suffix, func(t *testing.T) {
+ if cleanup {
+ t.Cleanup(func() {
+ if err := acl.Update(testFilePath, uid, 0); err != nil {
+ t.Fatalf("Update: error = %v", err)
+ }
+ if v := getfacl(t, testFilePath); !reflect.DeepEqual(v, cur) {
+ t.Fatalf("Update: %v, want %v", v, cur)
+ }
+ })
+ }
- if err := acl.Update(testFilePath, uid, perms...); err != nil {
+ if err := acl.Update(testFilePath, uid, perms); err != nil {
t.Fatalf("Update: error = %v", err)
}
r := respByCred(getfacl(t, testFilePath), fAclTypeUser, cred)
if r == nil {
t.Fatalf("Update did not add an ACL entry")
}
- if !r.equals(fAclTypeUser, cred, val) {
+
+ if !r.equals(&getFACLResp{
+ typ: fAclTypeUser,
+ cred: cred,
+ val: perms,
+ }) {
t.Fatalf("Update(%s) = %s", name, r)
}
})
@@ -113,31 +157,24 @@ func testUpdate(t *testing.T, testFilePath, name string, cur []*getFAclResp, val
type (
getFAclInvocation struct {
cmd *exec.Cmd
- val []*getFAclResp
+ val []*getFACLResp
pe []error
}
- getFAclResp struct {
+ getFACLResp struct {
typ fAclType
cred int32
- val fAclPerm
+ val acl.Perm
raw []byte
}
- fAclPerm uintptr
fAclType uint8
)
const fAclBufSize = 16
const (
- fAclPermRead fAclPerm = 1 << iota
- fAclPermWrite
- fAclPermExecute
-)
-
-const (
fAclTypeUser fAclType = iota
fAclTypeGroup
fAclTypeMask
@@ -149,7 +186,13 @@ func (c *getFAclInvocation) run(name string) error {
panic("attempted to run twice")
}
- c.cmd = exec.Command("getfacl", "--omit-header", "--absolute-names", "--numeric", name)
+ c.cmd = exec.Command(
+ "getfacl",
+ "--omit-header",
+ "--absolute-names",
+ "--numeric",
+ name,
+ )
c.cmd.Stderr = os.Stderr
scanErr := make(chan error, 1)
@@ -166,18 +209,17 @@ func (c *getFAclInvocation) run(name string) error {
return errors.Join(<-scanErr, c.cmd.Wait())
}
-func (c *getFAclInvocation) parse(pipe io.Reader, scanErr chan error) {
- c.val = make([]*getFAclResp, 0, 4+fAclBufSize)
+func (c *getFAclInvocation) parse(r io.Reader, scanErr chan error) {
+ c.val = make([]*getFACLResp, 0, 4+fAclBufSize)
- s := bufio.NewScanner(pipe)
+ s := bufio.NewScanner(r)
for s.Scan() {
fields := bytes.SplitN(s.Bytes(), []byte{':'}, 3)
if len(fields) != 3 {
continue
}
- resp := getFAclResp{}
-
+ var resp getFACLResp
switch string(fields[0]) {
case "user":
resp.typ = fAclTypeUser
@@ -213,7 +255,7 @@ func (c *getFAclInvocation) parse(pipe io.Reader, scanErr chan error) {
} else {
switch fields[2][0] {
case 'r':
- resp.val |= fAclPermRead
+ resp.val |= acl.Read
case '-':
default:
c.pe = append(c.pe, fmt.Errorf("invalid perm %v", fields[2][0]))
@@ -221,7 +263,7 @@ func (c *getFAclInvocation) parse(pipe io.Reader, scanErr chan error) {
}
switch fields[2][1] {
case 'w':
- resp.val |= fAclPermWrite
+ resp.val |= acl.Write
case '-':
default:
c.pe = append(c.pe, fmt.Errorf("invalid perm %v", fields[2][1]))
@@ -229,7 +271,7 @@ func (c *getFAclInvocation) parse(pipe io.Reader, scanErr chan error) {
}
switch fields[2][2] {
case 'x':
- resp.val |= fAclPermExecute
+ resp.val |= acl.Execute
case '-':
default:
c.pe = append(c.pe, fmt.Errorf("invalid perm %v", fields[2][2]))
@@ -244,7 +286,7 @@ func (c *getFAclInvocation) parse(pipe io.Reader, scanErr chan error) {
scanErr <- s.Err()
}
-func (r *getFAclResp) String() string {
+func (r *getFACLResp) String() string {
if r.raw != nil && len(r.raw) > 0 {
return string(r.raw)
}
@@ -252,11 +294,11 @@ func (r *getFAclResp) String() string {
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
+func (r *getFACLResp) equals(v *getFACLResp) bool {
+ return r.typ == v.typ && r.cred == v.cred && r.val == v.val
}
-func getfacl(t *testing.T, name string) []*getFAclResp {
+func getfacl(t *testing.T, name string) []*getFACLResp {
c := new(getFAclInvocation)
if err := c.run(name); err != nil {
t.Fatalf("getfacl: error = %v", err)
@@ -267,7 +309,7 @@ func getfacl(t *testing.T, name string) []*getFAclResp {
return c.val
}
-func respByCred(v []*getFAclResp, typ fAclType, cred int32) *getFAclResp {
+func respByCred(v []*getFACLResp, typ fAclType, cred int32) *getFACLResp {
j := -1
for i, r := range v {
if r.typ == typ && r.cred == cred {
diff --git a/internal/acl/libacl-helper.c b/internal/acl/libacl-helper.c
index 259dfb2b..065c1928 100644
--- a/internal/acl/libacl-helper.c
+++ b/internal/acl/libacl-helper.c
@@ -4,18 +4,22 @@
#include <stdlib.h>
#include <sys/acl.h>
-int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
- acl_perm_t *perms, size_t plen) {
- int ret;
+enum hakurei_acl_status hakurei_update_acl(
+ const char *path_p,
+ uid_t uid,
+ acl_perm_t perms) {
+ enum hakurei_acl_status ret;
+
bool v;
int i;
- acl_t acl;
+ acl_t acl, acl_d = NULL;
acl_entry_t entry;
acl_tag_t tag_type;
void *qualifier_p;
acl_permset_t permset;
+ acl_perm_t has_perms;
- ret = -1; /* acl_get_file */
+ ret = HAKUREI_ACL_GET_FILE;
acl = acl_get_file(path_p, ACL_TYPE_ACCESS);
if (acl == NULL)
goto out;
@@ -23,13 +27,13 @@ int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
/* prune entries by uid */
for (i = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); i == 1;
i = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) {
- ret = -2; /* acl_get_tag_type */
+ ret = HAKUREI_ACL_GET_TAG_TYPE;
if (acl_get_tag_type(entry, &tag_type) != 0)
goto out;
if (tag_type != ACL_USER)
continue;
- ret = -3; /* acl_get_qualifier */
+ ret = HAKUREI_ACL_GET_QUALIFIER;
qualifier_p = acl_get_qualifier(entry);
if (qualifier_p == NULL)
goto out;
@@ -39,52 +43,91 @@ int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
if (!v)
continue;
- ret = -4; /* acl_delete_entry */
+ ret = HAKUREI_ACL_GET_PERMSET;
+ if (acl_get_permset(entry, &permset) != 0)
+ goto out;
+
+ if (perms == 0)
+ goto delete;
+
+ /* this is quite ugly, but the standard makes it impossible to directly
+ * obtain these bits */
+ has_perms = 0;
+#define COPY_PERM_BIT(perm) \
+ do { \
+ ret = HAKUREI_ACL_GET_PERM; \
+ i = acl_get_perm(permset, perm); \
+ if (i < 0) \
+ goto out; \
+ if (i > 0) \
+ has_perms |= perm; \
+ } while (0)
+ COPY_PERM_BIT(ACL_READ);
+ COPY_PERM_BIT(ACL_WRITE);
+ COPY_PERM_BIT(ACL_EXECUTE);
+
+ if (perms == has_perms) {
+ ret = HAKUREI_ACL_DUP;
+ acl_d = acl_dup(acl);
+ if (acl_d == NULL)
+ goto out;
+
+ ret = HAKUREI_ACL_CALC_MASK;
+ if (acl_calc_mask(&acl_d) != 0)
+ goto out;
+
+ ret = HAKUREI_ACL_OK;
+ if (acl_cmp(acl, acl_d) == 0)
+ goto out;
+ }
+
+ delete:
+ ret = HAKUREI_ACL_DELETE_ENTRY;
if (acl_delete_entry(acl, entry) != 0)
goto out;
}
- if (plen == 0)
+ if (perms == 0)
goto set;
- ret = -5; /* acl_create_entry */
+ ret = HAKUREI_ACL_CREATE_ENTRY;
if (acl_create_entry(&acl, &entry) != 0)
goto out;
- ret = -6; /* acl_get_permset */
+ ret = HAKUREI_ACL_GET_PERMSET;
if (acl_get_permset(entry, &permset) != 0)
goto out;
- ret = -7; /* acl_add_perm */
- for (i = 0; i < plen; i++) {
- if (acl_add_perm(permset, perms[i]) != 0)
- goto out;
- }
+ ret = HAKUREI_ACL_ADD_PERM;
+ if (acl_add_perm(permset, perms) != 0)
+ goto out;
- ret = -8; /* acl_set_tag_type */
+ ret = HAKUREI_ACL_SET_TAG_TYPE;
if (acl_set_tag_type(entry, ACL_USER) != 0)
goto out;
- ret = -9; /* acl_set_qualifier */
+ ret = HAKUREI_ACL_SET_QUALIFIER;
if (acl_set_qualifier(entry, (void *)&uid) != 0)
goto out;
set:
- ret = -10; /* acl_calc_mask */
+ ret = HAKUREI_ACL_CALC_MASK;
if (acl_calc_mask(&acl) != 0)
goto out;
- ret = -11; /* acl_valid */
+ ret = HAKUREI_ACL_VALID;
if (acl_valid(acl) != 0)
goto out;
- ret = -12; /* acl_set_file */
+ ret = HAKUREI_ACL_SET_FILE;
if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) == 0)
- ret = 0;
+ ret = HAKUREI_ACL_OK;
out:
free((void *)path_p);
if (acl != NULL)
acl_free((void *)acl);
+ if (acl_d != NULL)
+ acl_free((void *)acl_d);
return ret;
}
diff --git a/internal/acl/libacl-helper.go b/internal/acl/libacl-helper.go
deleted file mode 100644
index 22575296..00000000
--- a/internal/acl/libacl-helper.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package acl
-
-import "os"
-
-func newAclPathError(name string, r int, err error) error {
- pathError := &os.PathError{Path: name, Err: err}
- switch r {
- case 0:
- return nil
-
- case -1:
- pathError.Op = "acl_get_file"
- case -2:
- pathError.Op = "acl_get_tag_type"
- case -3:
- pathError.Op = "acl_get_qualifier"
- case -4:
- pathError.Op = "acl_delete_entry"
- case -5:
- pathError.Op = "acl_create_entry"
- case -6:
- pathError.Op = "acl_get_permset"
- case -7:
- pathError.Op = "acl_add_perm"
- case -8:
- pathError.Op = "acl_set_tag_type"
- case -9:
- pathError.Op = "acl_set_qualifier"
- case -10:
- pathError.Op = "acl_calc_mask"
- case -11:
- pathError.Op = "acl_valid"
- case -12:
- pathError.Op = "acl_set_file"
-
- default: // unreachable
- pathError.Op = "setfacl"
- }
- return pathError
-}
diff --git a/internal/acl/libacl-helper.h b/internal/acl/libacl-helper.h
index b86eb170..753e1109 100644
--- a/internal/acl/libacl-helper.h
+++ b/internal/acl/libacl-helper.h
@@ -1,4 +1,24 @@
#include <sys/acl.h>
-int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid,
- acl_perm_t *perms, size_t plen);
+enum hakurei_acl_status {
+ HAKUREI_ACL_OK,
+ HAKUREI_ACL_GET_FILE,
+ HAKUREI_ACL_GET_TAG_TYPE,
+ HAKUREI_ACL_GET_QUALIFIER,
+ HAKUREI_ACL_DELETE_ENTRY,
+ HAKUREI_ACL_CREATE_ENTRY,
+ HAKUREI_ACL_GET_PERMSET,
+ HAKUREI_ACL_ADD_PERM,
+ HAKUREI_ACL_SET_TAG_TYPE,
+ HAKUREI_ACL_SET_QUALIFIER,
+ HAKUREI_ACL_CALC_MASK,
+ HAKUREI_ACL_VALID,
+ HAKUREI_ACL_SET_FILE,
+ HAKUREI_ACL_GET_PERM,
+ HAKUREI_ACL_DUP,
+};
+
+enum hakurei_acl_status hakurei_update_acl(
+ const char *path_p,
+ uid_t uid,
+ acl_perm_t perms);
diff --git a/internal/acl/libacl-helper_test.go b/internal/acl/libacl-helper_test.go
index 13d23a3c..05c43639 100644
--- a/internal/acl/libacl-helper_test.go
+++ b/internal/acl/libacl-helper_test.go
@@ -1,4 +1,4 @@
-package acl
+package acl_test
import (
"os"
@@ -7,9 +7,12 @@ import (
"testing"
"hakurei.app/container"
+ "hakurei.app/internal/acl"
)
-func TestNewAclPathError(t *testing.T) {
+func TestError(t *testing.T) {
+ t.Parallel()
+
testCases := []struct {
name string
path string
@@ -17,43 +20,51 @@ func TestNewAclPathError(t *testing.T) {
err error
want error
}{
- {"nil", container.Nonexistent, 0, syscall.ENOTRECOVERABLE, nil},
+ {"nil", container.Nonexistent, 0, syscall.ENOTRECOVERABLE, os.ErrInvalid},
- {"acl_get_file", container.Nonexistent, -1, syscall.ENOTRECOVERABLE,
+ {"acl_get_file", container.Nonexistent, 1, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_get_file", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_get_tag_type", container.Nonexistent, -2, syscall.ENOTRECOVERABLE,
+ {"acl_get_tag_type", container.Nonexistent, 2, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_get_tag_type", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_get_qualifier", container.Nonexistent, -3, syscall.ENOTRECOVERABLE,
+ {"acl_get_qualifier", container.Nonexistent, 3, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_get_qualifier", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_delete_entry", container.Nonexistent, -4, syscall.ENOTRECOVERABLE,
+ {"acl_delete_entry", container.Nonexistent, 4, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_delete_entry", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_create_entry", container.Nonexistent, -5, syscall.ENOTRECOVERABLE,
+ {"acl_create_entry", container.Nonexistent, 5, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_create_entry", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_get_permset", container.Nonexistent, -6, syscall.ENOTRECOVERABLE,
+ {"acl_get_permset", container.Nonexistent, 6, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_get_permset", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_add_perm", container.Nonexistent, -7, syscall.ENOTRECOVERABLE,
+ {"acl_add_perm", container.Nonexistent, 7, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_add_perm", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_set_tag_type", container.Nonexistent, -8, syscall.ENOTRECOVERABLE,
+ {"acl_set_tag_type", container.Nonexistent, 8, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_set_tag_type", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_set_qualifier", container.Nonexistent, -9, syscall.ENOTRECOVERABLE,
+ {"acl_set_qualifier", container.Nonexistent, 9, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_set_qualifier", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_calc_mask", container.Nonexistent, -10, syscall.ENOTRECOVERABLE,
+ {"acl_calc_mask", container.Nonexistent, 10, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_calc_mask", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_valid", container.Nonexistent, -11, syscall.ENOTRECOVERABLE,
+ {"acl_valid", container.Nonexistent, 11, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_valid", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl_set_file", container.Nonexistent, -12, syscall.ENOTRECOVERABLE,
+ {"acl_set_file", container.Nonexistent, 12, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "acl_set_file", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
+ {"acl_get_perm", container.Nonexistent, 13, syscall.ENOTRECOVERABLE,
+ &os.PathError{Op: "acl_get_perm", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
+ {"acl_dup", container.Nonexistent, 14, syscall.ENOTRECOVERABLE,
+ &os.PathError{Op: "acl_dup", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
- {"acl", container.Nonexistent, -13, syscall.ENOTRECOVERABLE,
+ {"acl", container.Nonexistent, 15, syscall.ENOTRECOVERABLE,
&os.PathError{Op: "setfacl", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}},
{"invalid", container.Nonexistent, -0xdead, nil,
&os.PathError{Op: "setfacl", Path: container.Nonexistent}},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- err := newAclPathError(tc.path, tc.r, tc.err)
+ err := acl.Error{
+ Path: tc.path,
+ Op: acl.Status(tc.r),
+ Err: tc.err,
+ }.Unwrap()
if !reflect.DeepEqual(err, tc.want) {
- t.Errorf("newAclPathError: %v, want %v", err, tc.want)
+ t.Errorf("Unwrap: %v, want %v", err, tc.want)
}
})
}
diff --git a/internal/acl/perms.go b/internal/acl/perms.go
index fbc99e82..829b134f 100644
--- a/internal/acl/perms.go
+++ b/internal/acl/perms.go
@@ -1,18 +1,18 @@
package acl
-type Perms []Perm
+import "unsafe"
-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'
- }
+// String returns a text representation of perms.
+func (perms Perm) String() string {
+ buf := []byte{'-', '-', '-'}
+ if perms&Read != 0 {
+ buf[0] = 'r'
}
- return string(s)
+ if perms&Write != 0 {
+ buf[1] = 'w'
+ }
+ if perms&Execute != 0 {
+ buf[2] = 'x'
+ }
+ return unsafe.String(unsafe.SliceData(buf), len(buf))
}
diff --git a/internal/acl/perms_test.go b/internal/acl/perms_test.go
index 3920046d..9fa24dc8 100644
--- a/internal/acl/perms_test.go
+++ b/internal/acl/perms_test.go
@@ -7,18 +7,20 @@ import (
)
func TestPerms(t *testing.T) {
+ t.Parallel()
+
testCases := []struct {
name string
- perms acl.Perms
+ perms acl.Perm
}{
- {"---", acl.Perms{}},
- {"r--", acl.Perms{acl.Read}},
- {"-w-", acl.Perms{acl.Write}},
- {"--x", acl.Perms{acl.Execute}},
- {"rw-", acl.Perms{acl.Read, acl.Read, acl.Write}},
- {"r-x", acl.Perms{acl.Read, acl.Execute, acl.Execute}},
- {"-wx", acl.Perms{acl.Write, acl.Write, acl.Execute, acl.Execute}},
- {"rwx", acl.Perms{acl.Read, acl.Write, acl.Execute}},
+ {"---", 0},
+ {"r--", acl.Read},
+ {"-w-", acl.Write},
+ {"--x", acl.Execute},
+ {"rw-", acl.Read | acl.Write},
+ {"r-x", acl.Read | acl.Execute},
+ {"-wx", acl.Write | acl.Execute},
+ {"rwx", acl.Read | acl.Write | acl.Execute},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {