// 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 .
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 [ ])",
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
}