--- /dev/null
+// Config: load files/ directory tree
+
+// 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"
+
+ "ruderich.org/simon/safcm"
+)
+
+func LoadFiles(group string) (map[string]*safcm.File, error) {
+ basePath := filepath.Join(group, "files")
+
+ const errMsg = `
+The actual permissions and user/group of files and directories are not used
+(except for +x on files). 0644/0755 and current remote user/group is used per
+default. Apply different file permissions via permissions.yaml. To prevent
+confusion files must be manually chmodded 0644/0755 and directories 0755 or
+via "safcm fixperms".
+`
+
+ files := make(map[string]*safcm.File)
+ err := filepath.WalkDir(basePath, func(path string, d fs.DirEntry,
+ err error) error {
+
+ if err != nil {
+ return err
+ }
+
+ info, err := d.Info()
+ if err != nil {
+ return err
+ }
+ typ := info.Mode().Type()
+ perm := FileModeToFullPerm(info.Mode())
+
+ var data []byte
+ // See errMsg above. If a user stores a file with stricter
+ // permissions they could assume that these permissions are
+ // respected. This is not the case.
+ if typ == 0 /* regular file */ {
+ if perm != 0644 && perm != 0755 {
+ return fmt.Errorf(
+ "%q: invalid permissions %#o%s",
+ path, perm, errMsg)
+ }
+ data, err = os.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ } else if typ == fs.ModeDir {
+ if perm != 0755 {
+ return fmt.Errorf(
+ "%q: invalid permissions %#o%s",
+ path, perm, errMsg)
+ }
+ } else if typ == fs.ModeSymlink {
+ x, err := os.Readlink(path)
+ if err != nil {
+ return err
+ }
+ data = []byte(x)
+ } else {
+ return fmt.Errorf("%q: file type not supported", path)
+ }
+
+ // Convert to absolute path as used on the target host
+ x, err := filepath.Rel(basePath, path)
+ if err != nil {
+ return err
+ }
+ x = filepath.Join("/", x)
+
+ files[x] = &safcm.File{
+ Path: x,
+ Mode: typ | (fs.FileMode(perm) & fs.ModePerm),
+ Uid: -1,
+ Gid: -1,
+ Data: data,
+ }
+ return nil
+ })
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("%s: %v", group, err)
+ }
+ return files, nil
+}