From 0122593312679f193e8c2574abf9ae879173d185 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sat, 30 Aug 2025 13:19:15 +0900 Subject: [PATCH] system/acl: wrap libacl errors in PathError This helps determine which libacl function the errno came from. Signed-off-by: Ophestra --- system/acl/acl.go | 5 +-- system/acl/libacl-helper.c | 29 ++++++++++++--- system/acl/libacl-helper.go | 40 +++++++++++++++++++++ system/acl/libacl-helper_test.go | 60 ++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 system/acl/libacl-helper.go create mode 100644 system/acl/libacl-helper_test.go diff --git a/system/acl/acl.go b/system/acl/acl.go index bc590db..366ad53 100644 --- a/system/acl/acl.go +++ b/system/acl/acl.go @@ -29,8 +29,5 @@ func Update(name string, uid int, perms ...Perm) error { (*C.acl_perm_t)(p), C.size_t(len(perms)), ) - if r == 0 { - return nil - } - return err + return newAclPathError(name, int(r), err) } diff --git a/system/acl/libacl-helper.c b/system/acl/libacl-helper.c index 905cfa5..d1df816 100644 --- a/system/acl/libacl-helper.c +++ b/system/acl/libacl-helper.c @@ -6,7 +6,7 @@ int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid, acl_perm_t *perms, size_t plen) { - int ret = -1; + int ret; bool v; int i; acl_t acl; @@ -15,51 +15,70 @@ int hakurei_acl_update_file_by_uid(const char *path_p, uid_t uid, void *qualifier_p; acl_permset_t permset; + ret = -1; /* acl_get_file */ acl = acl_get_file(path_p, ACL_TYPE_ACCESS); if (acl == NULL) goto out; - // prune entries by 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 */ if (acl_get_tag_type(entry, &tag_type) != 0) - return -1; + goto out; if (tag_type != ACL_USER) continue; + ret = -3; /* acl_get_qualifier */ qualifier_p = acl_get_qualifier(entry); if (qualifier_p == NULL) - return -1; + goto out; v = *(uid_t *)qualifier_p == uid; acl_free(qualifier_p); if (!v) continue; - acl_delete_entry(acl, entry); + ret = -4; /* acl_delete_entry */ + if (acl_delete_entry(acl, entry) != 0) + goto out; } if (plen == 0) goto set; + ret = -5; /* acl_create_entry */ if (acl_create_entry(&acl, &entry) != 0) goto out; + + ret = -6; /* 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 = -8; /* acl_set_tag_type */ if (acl_set_tag_type(entry, ACL_USER) != 0) goto out; + + ret = -9; /* acl_set_qualifier */ if (acl_set_qualifier(entry, (void *)&uid) != 0) goto out; set: + ret = -10; /* acl_calc_mask */ if (acl_calc_mask(&acl) != 0) goto out; + + ret = -11; /* acl_valid */ if (acl_valid(acl) != 0) goto out; + + ret = -12; /* acl_set_file */ if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) == 0) ret = 0; diff --git a/system/acl/libacl-helper.go b/system/acl/libacl-helper.go new file mode 100644 index 0000000..2257529 --- /dev/null +++ b/system/acl/libacl-helper.go @@ -0,0 +1,40 @@ +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/system/acl/libacl-helper_test.go b/system/acl/libacl-helper_test.go new file mode 100644 index 0000000..78e5776 --- /dev/null +++ b/system/acl/libacl-helper_test.go @@ -0,0 +1,60 @@ +package acl + +import ( + "os" + "reflect" + "syscall" + "testing" + + "hakurei.app/container" +) + +func TestNewAclPathError(t *testing.T) { + testCases := []struct { + name string + path string + r int + err error + want error + }{ + {"nil", container.Nonexistent, 0, syscall.ENOTRECOVERABLE, nil}, + + {"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, + &os.PathError{Op: "acl_get_tag_type", Path: container.Nonexistent, Err: 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, + &os.PathError{Op: "acl_delete_entry", Path: container.Nonexistent, Err: 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, + &os.PathError{Op: "acl_get_permset", Path: container.Nonexistent, Err: 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, + &os.PathError{Op: "acl_set_tag_type", Path: container.Nonexistent, Err: 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, + &os.PathError{Op: "acl_calc_mask", Path: container.Nonexistent, Err: 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, + &os.PathError{Op: "acl_set_file", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}}, + + {"acl", container.Nonexistent, -13, syscall.ENOTRECOVERABLE, + &os.PathError{Op: "setfacl", Path: container.Nonexistent, Err: syscall.ENOTRECOVERABLE}}, + {"invalid", container.Nonexistent, -0xdeadbeef, 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) + if !reflect.DeepEqual(err, tc.want) { + t.Errorf("newAclPathError: %v, want %v", err, tc.want) + } + }) + } +}