]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/config/permissions.go
b84b521d1c615289aeaf17e704bd2ec1dbe79927
[safcm/safcm.git] / cmd / safcm / config / permissions.go
1 // Config: parse permissions.yaml
2
3 // Copyright (C) 2021  Simon Ruderich
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 package config
19
20 import (
21         "fmt"
22         "io/fs"
23         "os"
24         "path/filepath"
25         "strconv"
26         "strings"
27
28         "gopkg.in/yaml.v2"
29
30         "ruderich.org/simon/safcm"
31 )
32
33 func LoadPermissions(group string, files map[string]*safcm.File) error {
34         path := filepath.Join(group, "permissions.yaml")
35
36         var cfg map[string]string
37         x, err := os.ReadFile(path)
38         if err != nil {
39                 if os.IsNotExist(err) {
40                         return nil
41                 }
42                 return err
43         }
44         err = yaml.UnmarshalStrict(x, &cfg)
45         if err != nil {
46                 return fmt.Errorf("%s: failed to load: %v", path, err)
47         }
48
49         for p, x := range cfg {
50                 _, ok := files[p]
51                 if !ok {
52                         return fmt.Errorf("%s: %q does not exist in files/",
53                                 path, p)
54                 }
55
56                 xs := strings.Fields(x)
57                 if len(xs) != 1 && len(xs) != 3 {
58                         return fmt.Errorf("%s: invalid line %q "+
59                                 "(expected <perm> [<user> <group>])",
60                                 path, x)
61                 }
62                 perm, err := strconv.ParseInt(xs[0], 8, 32)
63                 if err != nil {
64                         return fmt.Errorf("%s: invalid permission %q "+
65                                 "(expected e.g. %q or %q)",
66                                 path, xs[0], "0644", "01777")
67                 }
68                 if perm > 07777 {
69                         return fmt.Errorf("%s: invalid permission %#o "+
70                                 "(expected e.g. %#o or %#o)",
71                                 path, perm, 0644, 01777)
72                 }
73
74                 file := files[p]
75                 // Sanity check
76                 if file.Mode.Perm()&0111 != 0 && perm&0111 == 0 {
77                         return fmt.Errorf(
78                                 "%s: %q: trying to remove +x from file, "+
79                                         "manually chmod -x in files/",
80                                 path, p)
81                 }
82                 file.Mode = file.Mode.Type() | FullPermToFileMode(int(perm))
83                 if len(xs) == 3 {
84                         file.User = xs[1]
85                         file.Group = xs[2]
86                 }
87         }
88
89         return nil
90 }
91
92 func FileModeToFullPerm(mode fs.FileMode) int {
93         perm := mode.Perm()
94         if mode&fs.ModeSticky != 0 {
95                 perm |= 01000
96         }
97         if mode&fs.ModeSetgid != 0 {
98                 perm |= 02000
99         }
100         if mode&fs.ModeSetuid != 0 {
101                 perm |= 04000
102         }
103         return int(perm)
104 }
105
106 func FullPermToFileMode(perm int) fs.FileMode {
107         mode := fs.FileMode(perm & 0777)
108         if perm&01000 != 0 {
109                 mode |= fs.ModeSticky
110         }
111         if perm&02000 != 0 {
112                 mode |= fs.ModeSetgid
113         }
114         if perm&04000 != 0 {
115                 mode |= fs.ModeSetuid
116         }
117         return mode
118 }