aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/acl/acl.go
blob: 8b862ba2b55db6353a270abfcddde4976371fe8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Package acl implements simple ACL manipulation via libacl.
package acl

/*
#cgo linux pkg-config: --static libacl

#include "libacl-helper.h"
*/
import "C"

import (
	"os"

	"hakurei.app/container"
)

// Perm represents acl_perm_t.
type Perm C.acl_perm_t

const (
	// 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
)

// 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()
}

// Update replaces ACL_USER entry with qualifier uid.
func Update(name string, uid int, perms Perm) error {
	f, _err := os.OpenFile(name, container.O_PATH, 0)
	if _err != nil {
		return _err
	}

	r, err := C.hakurei_update_acl(
		C.int(f.Fd()),
		C.uid_t(uid),
		C.acl_perm_t(perms),
	)

	if _err = f.Close(); _err != nil {
		return _err
	}

	if r == C.HAKUREI_ACL_OK {
		return nil
	}
	return &Error{name, r, err}
}