// Config: parse permissions.yaml // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2021-2024 Simon Ruderich 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 < 0 || 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 }