Installing packages and starting/enabling services is not yet supported.
There are minor limitations when handling symlinks (see README.adoc).
--- /dev/null
+image: freebsd/latest
+packages:
+ - go
+ - gmake
+tasks:
+ - all: |
+ sudo ln -sf /usr/local/bin/gmake /usr/bin/make
+ cd safcm
+ ./ci/run
result is similar -- but not identical -- to quoted strings in regular shell
scripts which can be confusing.
+- Permissions of symlinks are ignored on BSD systems. They are always shown to
+ have `0777` as permissions even though the current umask controls the actual
+ permissions when creating new symlinks. Existing symlinks with different
+ permissions are not updated. Most BSDs ignore the permissions when following
+ symlinks which should reduce the impact of this limitation.
+
== Requirements
* Supported operating system:
** GNU/Linux with common commands (`uname`, `id`, `stat`, `sha512sum`,
`cat`, `mktemp`, `rm`, `ln`, `chmod`)
+ ** FreeBSD (same commands, but uses `sha512`)
* SSH server
* to install packages:
** `apt-get` (Debian or derivative)
"io/fs"
"os"
"path/filepath"
+ "runtime"
"syscall"
"testing"
}
_, uid, _, gid := ft.CurrentUserAndGroup()
+ symlinkExists := "open file: too many levels of symbolic links"
+ if runtime.GOOS == "freebsd" {
+ // EMLINK instead of ELOOP
+ symlinkExists = "open file: too many links"
+ }
+
tests := []struct {
name string
path string
},
},
nil,
- fmt.Errorf("open file: too many levels of symbolic links"),
+ fmt.Errorf(symlinkExists),
},
{
"exists: fifo",
mkdir -p "$dest"
+build freebsd amd64
build linux amd64
build_arm linux arm 7
# TODO: support more operating systems and architectures
oldFh, err := OpenFileNoFollow(file.Path)
if err != nil {
err := err.(*fs.PathError)
- if err.Err == syscall.ELOOP {
+ if err.Err == syscall.ELOOP || err.Err == syscall.EMLINK {
// Check if ELOOP was caused not by O_NOFOLLOW but by
// too many nested symlinks before the final path
// component.
if !change.Created {
// Compare permissions
change.Old.Mode = oldStat.Mode()
+ if change.Old.Mode.Type() == fs.ModeSymlink {
+ // Some BSD systems permit changing permissions of
+ // symlinks but ignore them on traversal. To keep it
+ // simple we don't support that and always use 0777 for
+ // symlink permissions (the value on GNU/Linux).
+ //
+ // TODO: Add proper support for symlinks on BSD
+ change.Old.Mode |= 0777
+ }
if change.Old.Mode != file.Mode {
if change.Old.Mode.Type() != file.Mode.Type() {
changeType = true
// don't want to modify the running system. Use this
// test (and the one below for triggers) as a basic
// check that absolute paths work.
+ //
+ // Use numeric IDs as not all systems use root/root;
+ // for example BSDs use root/wheel.
"absolute paths: no change",
safcm.MsgSyncReq{
Files: map[string]*safcm.File{
"/": {
Path: "/",
Mode: fs.ModeDir | 0755,
- User: "root",
- Uid: -1,
- Group: "root",
- Gid: -1,
+ Uid: 0,
+ Gid: 0,
OrigGroup: "group",
},
"/etc": {
Path: "/etc",
Mode: fs.ModeDir | 0755,
- User: "root",
- Uid: -1,
- Group: "root",
- Gid: -1,
+ Uid: 0,
+ Gid: 0,
OrigGroup: "group",
},
"/tmp": {
Path: "/tmp",
Mode: fs.ModeDir | 0777 | fs.ModeSticky,
- User: "root",
- Uid: -1,
- Group: "root",
- Gid: -1,
+ Uid: 0,
+ Gid: 0,
OrigGroup: "group",
},
"/var/tmp": {
Path: "/var/tmp",
Mode: fs.ModeDir | 0777 | fs.ModeSticky,
- User: "root",
- Uid: -1,
- Group: "root",
- Gid: -1,
+ Uid: 0,
+ Gid: 0,
OrigGroup: "group",
},
},
"/": {
Path: "/",
Mode: fs.ModeDir | 0755,
- User: "root",
- Uid: -1,
- Group: "root",
- Gid: -1,
+ Uid: 0,
+ Gid: 0,
OrigGroup: "group",
TriggerCommands: []string{
"echo trigger /",
"/tmp": {
Path: "/tmp",
Mode: fs.ModeDir | 0777 | fs.ModeSticky,
- User: "root",
- Uid: -1,
- Group: "root",
- Gid: -1,
+ Uid: 0,
+ Gid: 0,
OrigGroup: "group",
TriggerCommands: []string{
"echo trigger /tmp",
return err
}
f.Data = []byte(x)
+ f.Mode |= 0777 // see sync/files.go
}
res = append(res, f)
return nil
return err
}
data = []byte(x)
+ perm |= 0777 // see cmd/safcm-remote/sync/files.go
} else {
return fmt.Errorf("%q: file type not supported", path)
}
"fmt"
"io/fs"
"os"
+ "runtime"
"syscall"
"testing"
t.Fatal(err)
}
+ // Regular users cannot create sticky files
+ skipInvalidSticky := runtime.GOOS == "freebsd"
+
chmod("files-invalid-perm-dir/files", 0500)
defer chmod("files-invalid-perm-dir/files", 0700)
chmod("files-invalid-perm-dir/files/etc/", 0755)
chmod("files-invalid-perm-file-executable/files", 0755)
chmod("files-invalid-perm-file-executable/files/etc", 0755)
chmod("files-invalid-perm-file-executable/files/etc/rc.local", 0750)
- chmod("files-invalid-perm-file-sticky/files", 0755)
- chmod("files-invalid-perm-file-sticky/files/etc", 0755)
- chmod("files-invalid-perm-file-sticky/files/etc/resolv.conf", 01644)
+ if !skipInvalidSticky {
+ chmod("files-invalid-perm-file-sticky/files", 0755)
+ chmod("files-invalid-perm-file-sticky/files/etc", 0755)
+ chmod("files-invalid-perm-file-sticky/files/etc/resolv.conf", 01644)
+ }
err = syscall.Mkfifo("files-invalid-type/files/invalid", 0644)
if err != nil {
tests := []struct {
group string
+ skip bool
exp map[string]*safcm.File
expErr error
}{
{
"empty",
+ false,
nil,
nil,
},
{
"group",
+ false,
map[string]*safcm.File{
"/": {
Path: "/",
{
"files-invalid-type",
+ false,
nil,
fmt.Errorf("files-invalid-type: \"files-invalid-type/files/invalid\": file type not supported"),
},
{
"files-invalid-perm-dir",
+ false,
nil,
fmt.Errorf("files-invalid-perm-dir: \"files-invalid-perm-dir/files\": invalid permissions 0500" + errMsg),
},
{
"files-invalid-perm-dir-setgid",
+ false,
nil,
fmt.Errorf("files-invalid-perm-dir-setgid: \"files-invalid-perm-dir-setgid/files/etc\": invalid permissions 02755" + errMsg),
},
{
"files-invalid-perm-file",
+ false,
nil,
fmt.Errorf("files-invalid-perm-file: \"files-invalid-perm-file/files/etc/resolv.conf\": invalid permissions 0600" + errMsg),
},
{
"files-invalid-perm-file-executable",
+ false,
nil,
fmt.Errorf("files-invalid-perm-file-executable: \"files-invalid-perm-file-executable/files/etc/rc.local\": invalid permissions 0750" + errMsg),
},
{
"files-invalid-perm-file-sticky",
+ skipInvalidSticky,
nil,
fmt.Errorf("files-invalid-perm-file-sticky: \"files-invalid-perm-file-sticky/files/etc/resolv.conf\": invalid permissions 01644" + errMsg),
},
for _, tc := range tests {
t.Run(tc.group, func(t *testing.T) {
+ if tc.skip {
+ t.SkipNow()
+ }
+
res, err := LoadFiles(tc.group)
testutil.AssertEqual(t, "res", res, tc.exp)
testutil.AssertErrorEqual(t, "err", err, tc.expErr)
compat_sha512sum() {
sha512sum "$1"
}
+`
+ case "freebsd":
+ compat = `
+dir_stat='41777 0 0'
+file_stat="100700 $(id -u) $(id -g)"
+compat_stat() {
+ stat -f '%p %u %g' "$1"
+}
+compat_sha512sum() {
+ sha512 -q "$1"
+}
`
default:
return fmt.Errorf("internal error: no support for %q", goos)
switch x {
case "GNU/Linux":
goos = "linux"
+ case "FreeBSD":
+ goos = "freebsd"
default:
return "", fmt.Errorf("unsupported OS %q (`uname -o`)", x)
}
// NOTE: Adapt cmd/safcm-remote/build.sh when adding new architectures
var goarch string
switch x {
- case "x86_64":
+ case "x86_64", "amd64":
goarch = "amd64"
case "armv7l":
goarch = "armv7l"