Skip to content

Commit

Permalink
Add AccessFSSet lookup by shortened file system operation names.
Browse files Browse the repository at this point in the history
Fixes #14.

This helper will return an error when invoked with an invalid or
duplicate name. The supported names correspond to the Landlock file
system operations and are notated in lowercase with underscores, for
example "execute", "write_file", "read_dir", "make_fifo".
  • Loading branch information
gnoack committed Sep 5, 2021
1 parent fbe3b00 commit d1bd348
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
40 changes: 40 additions & 0 deletions landlock/accessfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"
)

// Flag names by bit index, e.g. index n corresponds to AccessFSSet flag 1<<n.
var flagNames = []string{
"execute",
"write_file",
Expand All @@ -21,6 +22,15 @@ var flagNames = []string{
"make_sym",
}

var setByFlagName map[string]AccessFSSet

func init() {
setByFlagName = make(map[string]AccessFSSet)
for i, fn := range flagNames {
setByFlagName[fn] = 1<<i
}
}

// AccessFSSet is a set of Landlockable file system access operations.
type AccessFSSet uint64

Expand Down Expand Up @@ -66,3 +76,33 @@ func (a AccessFSSet) isEmpty() bool {
func (a AccessFSSet) valid() bool {
return a.isSubset(supportedAccessFS)
}

// NewAccessFSSetFromNames returns an AccessFSSet constructed from
// "short names" for the Landlock file system operations.
//
// The supported names are the same as in the Landlock C headers, or
// as exposed in the landlock/syscall package, but only the last part
// of the name, in lowercase with underscores. For example,
// "write_file" refers to LANDLOCK_ACCESS_FS_WRITE_FILE from the C
// header.
//
// This function returns an empty AccessFSSet and an error if an
// invalid name was passed, or if the same name was passed twice.
//
// Calling NewAccessFSSetFromNames with multiple valid names is
// equivalent to calling it once for each of them and combining the
// resulting sets with a bitwise OR.
func NewAccessFSSetFromNames(name ...string) (AccessFSSet, error) {
var res AccessFSSet
for _, n := range name {
a, ok := setByFlagName[n]
if !ok {
return 0, fmt.Errorf("invalid argument: %q", n)
}
if a.isSubset(res) {
return 0, fmt.Errorf("duplicate argument: %q", n)
}
res |= a
}
return res, nil
}
36 changes: 36 additions & 0 deletions landlock/accessfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,39 @@ func TestPrettyPrint(t *testing.T) {
}
}
}

func TestNewAccessFSSetFromNames(t *testing.T) {
for _, tc := range []struct{
a AccessFSSet
ns []string
} {
{a: 0b1111111111111, ns: []string{"execute", "write_file", "read_file", "read_dir", "remove_dir", "remove_file", "make_char", "make_dir", "make_reg", "make_sock", "make_fifo", "make_block", "make_sym"}},
{a: 0b1111100000000, ns: []string{"make_reg", "make_sock", "make_fifo", "make_block", "make_sym"}},
{a: 0b0000011111111, ns: []string{"execute", "write_file", "read_file", "read_dir", "remove_dir", "remove_file", "make_char", "make_dir"}},
{a: ll.AccessFSExecute, ns: []string{"execute"}},
{a: ll.AccessFSWriteFile, ns: []string{"write_file"}},
{a: ll.AccessFSReadFile, ns: []string{"read_file"}},
{a: 0, ns: []string{}},
}{
got, err := NewAccessFSSetFromNames(tc.ns...)
if err != nil {
t.Errorf("NewAccessFSSetFromNames(%v): unexpected error: %v", tc.ns, err)
}
want := tc.a
if got != want {
t.Errorf("NewAccessFSSetFromNames(%v): got %v, want %v", tc.ns, uint64(got), uint64(want))
}
}
}

func TestNewAccessFSSetFromNamesError(t*testing.T) {
for _, names := range [][]string{
{"frobnicate"}, // does not exist
{"execute", "write_file", "read_file", "execute"}, // duplicate "execute"
}{
_, err := NewAccessFSSetFromNames(names...)
if err == nil {
t.Errorf("NewAccessFSSetFromNames(%q): want error, got success", names)
}
}
}

0 comments on commit d1bd348

Please sign in to comment.