--- /dev/null
+// Config: parse permissions.yaml
+
+// Copyright (C) 2021 Simon Ruderich
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package config
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "gopkg.in/yaml.v2"
+
+ "ruderich.org/simon/safcm"
+)
+
+func LoadPermissions(group string, files map[string]*safcm.File) error {
+ path := filepath.Join(group, "permissions.yaml")
+
+ var cfg map[string]string
+ x, err := os.ReadFile(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+ err = yaml.UnmarshalStrict(x, &cfg)
+ if err != nil {
+ return fmt.Errorf("%s: failed to load: %v", path, err)
+ }
+
+ for p, x := range cfg {
+ _, ok := files[p]
+ if !ok {
+ return fmt.Errorf("%s: %q does not exist in files/",
+ path, p)
+ }
+
+ xs := strings.Fields(x)
+ if len(xs) != 1 && len(xs) != 3 {
+ return fmt.Errorf("%s: invalid line %q "+
+ "(expected <perm> [<user> <group>])",
+ path, x)
+ }
+ perm, err := strconv.ParseInt(xs[0], 8, 32)
+ if err != nil {
+ return fmt.Errorf("%s: invalid permission %q "+
+ "(expected e.g. %q or %q)",
+ path, xs[0], "0644", "01777")
+ }
+ if perm > 07777 {
+ return fmt.Errorf("%s: invalid permission %#o "+
+ "(expected e.g. %#o or %#o)",
+ path, perm, 0644, 01777)
+ }
+
+ file := files[p]
+ // Sanity check
+ if file.Mode.Perm()&0111 != 0 && perm&0111 == 0 {
+ return fmt.Errorf(
+ "%s: %q: trying to remove +x from file, "+
+ "manually chmod -x in files/",
+ path, p)
+ }
+ file.Mode = file.Mode.Type() | FullPermToFileMode(int(perm))
+ if len(xs) == 3 {
+ file.User = xs[1]
+ file.Group = xs[2]
+ }
+ }
+
+ return nil
+}
+
+func FileModeToFullPerm(mode fs.FileMode) int {
+ perm := mode.Perm()
+ if mode&fs.ModeSticky != 0 {
+ perm |= 01000
+ }
+ if mode&fs.ModeSetgid != 0 {
+ perm |= 02000
+ }
+ if mode&fs.ModeSetuid != 0 {
+ perm |= 04000
+ }
+ return int(perm)
+}
+
+func FullPermToFileMode(perm int) fs.FileMode {
+ mode := fs.FileMode(perm & 0777)
+ if perm&01000 != 0 {
+ mode |= fs.ModeSticky
+ }
+ if perm&02000 != 0 {
+ mode |= fs.ModeSetgid
+ }
+ if perm&04000 != 0 {
+ mode |= fs.ModeSetuid
+ }
+ return mode
+}