A Go (golang) implementation of libcap: import "libcap/cap".
The API for this "libcap/cap" package is very similar to libcap.
I've included a substantial interoperability test that validate
libcap(c) and libcap/cap(go) have import/export text and binary
format compatibility.
My motivation for implementing a standalone Go package was for a
cross-compilation issue I ran into (Go is much more friendly for
cross-compilation by default, unless you need to use cgo).
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
diff --git a/cap/text.go b/cap/text.go
new file mode 100644
index 0000000..0af6290
--- /dev/null
+++ b/cap/text.go
@@ -0,0 +1,182 @@
+package cap
+
+import (
+ "bufio"
+ "errors"
+ "strconv"
+ "strings"
+)
+
+// String converts a capability Value into its canonical text
+// representation.
+func (v Value) String() string {
+ name, ok := names[v]
+ if ok {
+ return name
+ }
+ // Un-named capabilities are referred to numerically (in decimal).
+ return strconv.Itoa(int(v))
+}
+
+// FromName converts a named capability Value to its binary
+// representation.
+func FromName(name string) (Value, error) {
+ startUp.Do(cInit)
+ v, ok := bits[name]
+ if ok {
+ if v >= Value(words*32) {
+ return 0, ErrBadValue
+ }
+ return v, nil
+ }
+ i, err := strconv.Atoi(name)
+ if err != nil {
+ return 0, err
+ }
+ if i >= 0 && i < int(words*32) {
+ return Value(i), nil
+ }
+ return 0, ErrBadValue
+}
+
+const (
+ eBin uint = (1 << Effective)
+ pBin = (1 << Permitted)
+ iBin = (1 << Inheritable)
+)
+
+var combos = []string{"", "e", "p", "ep", "i", "ei", "pi", "epi"}
+
+// histo generates a histogram of flag state combinations.
+func (c *Set) histo(m uint, bins []int, patterns []uint, from, limit Value) uint {
+ for v := from; v < limit; v++ {
+ b := uint(v & 31)
+ u, mask, err := bitOf(0, v)
+ if err != nil {
+ break
+ }
+ x := uint((c.flat[u][Effective]&mask)>>b) * eBin
+ x |= uint((c.flat[u][Permitted]&mask)>>b) * pBin
+ x |= uint((c.flat[u][Inheritable]&mask)>>b) * iBin
+ bins[x]++
+ patterns[uint(v)] = x
+ if bins[m] <= bins[x] {
+ m = x
+ }
+ }
+ return m
+}
+
+// String converts a full capability Set into it canonical readable
+// string representation (which may contain spaces).
+func (c *Set) String() string {
+ if c == nil {
+ return "<invalid>"
+ }
+ bins := make([]int, 8)
+ patterns := make([]uint, 32*words)
+
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+
+ // Note, in order to have a *Set pointer, startUp.Do(cInit)
+ // must have been called which sets maxValues.
+ m := c.histo(0, bins, patterns, Value(maxValues), 32*Value(words))
+ // Background state is the most popular of the unnamed bits,
+ // this has the effect of tending to not list numerical values
+ // for unnamed capabilities in the generated text.
+ vs := []string{"=" + combos[m]}
+
+ // Extend the histogram with the remaining Value occurrences.
+ c.histo(m, bins, patterns, 0, Value(maxValues))
+ for i := uint(8); i > 0; {
+ i--
+ if i == m || bins[i] == 0 {
+ continue
+ }
+ var list []string
+ for j, p := range patterns {
+ if p != i {
+ continue
+ }
+ list = append(list, Value(j).String())
+ }
+ if cf := i & ^m; cf != 0 {
+ vs = append(vs, strings.Join(list, ",")+"+"+combos[cf])
+ }
+ if cf := m & ^i; cf != 0 {
+ vs = append(vs, strings.Join(list, ",")+"-"+combos[cf])
+ }
+ }
+ return strings.Join(vs, " ")
+}
+
+// ErrBadText is returned if the text for a capability set cannot be parsed.
+var ErrBadText = errors.New("bad text")
+
+// FromText converts the canonical text representation for a Set into
+// a freshly allocated Set.
+func FromText(text string) (*Set, error) {
+ c := NewSet()
+ scanner := bufio.NewScanner(strings.NewReader(text))
+ scanner.Split(bufio.ScanWords)
+ chunks := 0
+ for scanner.Scan() {
+ chunks++
+ // Parsing for xxx[-+=][eip]+
+ t := scanner.Text()
+ i := strings.IndexAny(t, "=+-")
+ if i < 0 {
+ return nil, ErrBadText
+ }
+ var vs []Value
+ sep := t[i]
+ if vals := t[:i]; vals != "all" && vals != "" {
+ for _, name := range strings.Split(vals, ",") {
+ v, err := FromName(name)
+ if err != nil {
+ return nil, ErrBadText
+ }
+ vs = append(vs, v)
+ }
+ } else if sep != '=' && vals == "" {
+ return nil, ErrBadText // Only "=" supports ""=="all".
+ }
+ sets := t[i+1:]
+ var fE, fP, fI bool
+ for j := 0; j < len(sets); j++ {
+ switch sets[j] {
+ case 'e':
+ fE = true
+ case 'p':
+ fP = true
+ case 'i':
+ fI = true
+ default:
+ return nil, ErrBadText
+ }
+ }
+ if sep == '=' {
+ keep := len(vs) == 0 // '=' means default to off except setting individual values.
+ c.forceFlag(Effective, fE && keep)
+ c.forceFlag(Permitted, fP && keep)
+ c.forceFlag(Inheritable, fI && keep)
+ if keep {
+ continue
+ }
+ }
+ if fE {
+ c.SetFlag(Effective, sep != '-', vs...)
+ }
+ if fP {
+ c.SetFlag(Permitted, sep != '-', vs...)
+ }
+ if fI {
+ c.SetFlag(Inheritable, sep == '+', vs...)
+ }
+ }
+ if chunks == 0 {
+ return nil, ErrBadText
+ }
+ return c, nil
+}