diff options
Diffstat (limited to 'internal/acl')
| -rw-r--r-- | internal/acl/acl.go | 84 | ||||
| -rw-r--r-- | internal/acl/acl_test.go | 136 | ||||
| -rw-r--r-- | internal/acl/libacl-helper.c | 87 | ||||
| -rw-r--r-- | internal/acl/libacl-helper.go | 40 | ||||
| -rw-r--r-- | internal/acl/libacl-helper.h | 24 | ||||
| -rw-r--r-- | internal/acl/libacl-helper_test.go | 47 | ||||
| -rw-r--r-- | internal/acl/perms.go | 26 | ||||
| -rw-r--r-- | internal/acl/perms_test.go | 20 |
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) { |
