]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/config/files.go
safcm: add experimental support to sync from Windows hosts
[safcm/safcm.git] / cmd / safcm / config / files.go
1 // Config: load files/ directory tree
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         slashpath "path"
25         "path/filepath"
26         "runtime"
27
28         "ruderich.org/simon/safcm"
29 )
30
31 func LoadFiles(group string) (map[string]*safcm.File, error) {
32         basePath := filepath.Join(group, "files")
33
34         const errMsg = `
35
36 The actual permissions and user/group of files and directories are not used
37 (except for +x on files). 0644/0755 and current remote user/group is used per
38 default. Apply different file permissions via permissions.yaml. To prevent
39 confusion files must be manually chmodded 0644/0755 and directories 0755 or
40 via "safcm fixperms".
41 `
42
43         // No permission checks on windows which doesn't track them.
44         windows := runtime.GOOS == "windows"
45
46         files := make(map[string]*safcm.File)
47         err := filepath.WalkDir(basePath, func(path string, d fs.DirEntry,
48                 err error) error {
49
50                 if err != nil {
51                         return err
52                 }
53
54                 info, err := d.Info()
55                 if err != nil {
56                         return err
57                 }
58                 typ := info.Mode().Type()
59                 perm := FileModeToFullPerm(info.Mode())
60
61                 var data []byte
62                 // See errMsg above. If a user stores a file with stricter
63                 // permissions they could assume that these permissions are
64                 // respected. This is not the case.
65                 if typ == 0 /* regular file */ {
66                         if windows {
67                                 perm = 0644
68                                 // 0755 must be set via permissions.yaml if
69                                 // windows is used
70                         }
71                         if perm != 0644 && perm != 0755 {
72                                 return fmt.Errorf(
73                                         "%q: invalid permissions %#o%s",
74                                         path, perm, errMsg)
75                         }
76                         data, err = os.ReadFile(path)
77                         if err != nil {
78                                 return err
79                         }
80                 } else if typ == fs.ModeDir {
81                         if windows {
82                                 perm = 0755
83                         }
84                         if perm != 0755 {
85                                 return fmt.Errorf(
86                                         "%q: invalid permissions %#o%s",
87                                         path, perm, errMsg)
88                         }
89                 } else if typ == fs.ModeSymlink {
90                         x, err := os.Readlink(path)
91                         if err != nil {
92                                 return err
93                         }
94                         data = []byte(x)
95                         perm |= 0777 // see cmd/safcm-remote/sync/files.go
96                 } else {
97                         return fmt.Errorf("%q: file type not supported", path)
98                 }
99
100                 // Convert to absolute and slash-separated path as used on the
101                 // target host
102                 x, err := filepath.Rel(basePath, path)
103                 if err != nil {
104                         return err
105                 }
106                 x = slashpath.Join("/", filepath.ToSlash(x))
107
108                 files[x] = &safcm.File{
109                         Path: x,
110                         Mode: typ | (fs.FileMode(perm) & fs.ModePerm),
111                         Uid:  -1,
112                         Gid:  -1,
113                         Data: data,
114                 }
115                 return nil
116         })
117         if err != nil {
118                 if os.IsNotExist(err) {
119                         return nil, nil
120                 }
121                 return nil, fmt.Errorf("%s: %v", group, err)
122         }
123         return files, nil
124 }