From ce34b7b21a967879d6e0e76fdfd8c6c1ab05316a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 12 Mar 2025 12:15:23 +0200 Subject: [PATCH] Improve filesystem mounting --- cmd/enit/go.mod | 4 +- cmd/enit/go.sum | 6 +- cmd/enit/main.go | 40 +++++++------ cmd/enit/mount.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 26 deletions(-) create mode 100644 cmd/enit/mount.go diff --git a/cmd/enit/go.mod b/cmd/enit/go.mod index 89a00dc..fa25cb7 100644 --- a/cmd/enit/go.mod +++ b/cmd/enit/go.mod @@ -1,3 +1,5 @@ module enit -go 1.23.4 \ No newline at end of file +go 1.23.4 + +require golang.org/x/sys v0.31.0 diff --git a/cmd/enit/go.sum b/cmd/enit/go.sum index a62c313..c55261f 100644 --- a/cmd/enit/go.sum +++ b/cmd/enit/go.sum @@ -1,4 +1,2 @@ -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/cmd/enit/main.go b/cmd/enit/main.go index 1184a91..4dc5181 100644 --- a/cmd/enit/main.go +++ b/cmd/enit/main.go @@ -1,7 +1,6 @@ package main import ( - "errors" "flag" "fmt" "log" @@ -75,43 +74,45 @@ func setProcessName() error { func mountVirtualFilesystems() { fmt.Print("Mounting virtual filesystems... ") - commonFlags := uintptr(0 | syscall.MS_NOSUID | syscall.MS_RELATIME) + commonOptions := "rw,nosuid,relatime" + // Mount /proc - if err := syscall.Mount("proc", "/proc", "proc", commonFlags|syscall.MS_NODEV|syscall.MS_NOEXEC|syscall.MS_REMOUNT, ""); err != nil { + if err := mount("proc", "/proc", "proc", commonOptions+",nodev,noexec", false); err != nil { panic(err) } + // Mount /sys - if err := syscall.Mount("sys", "/sys", "sysfs", commonFlags|syscall.MS_NODEV|syscall.MS_NOEXEC|syscall.MS_REMOUNT, ""); err != nil { + if err := mount("sys", "/sys", "sysfs", commonOptions+",nodev,noexec", false); err != nil { panic(err) } + // Mount /dev - if err := syscall.Mount("dev", "/dev", "devtmpfs", commonFlags|syscall.MS_REMOUNT, "mode=755,inode64"); err != nil { + if err := mount("dev", "/dev", "devtmpfs", commonOptions+",mode=755,inode64", false); err != nil { panic(err) } + // Mount /run - if err := syscall.Mount("run", "/run", "tmpfs", commonFlags|syscall.MS_NODEV|syscall.MS_REMOUNT, "mode=755,inode64"); err != nil { + if err := mount("run", "/run", "tmpfs", commonOptions+",nodev,mode=755,inode64", false); err != nil { panic(err) } + // Mount /dev/pts - if err := os.Mkdir("/dev/pts", 0755); err != nil && !errors.Is(err, os.ErrExist) { - panic(err) - } - if err := syscall.Mount("devpts", "/dev/pts", "devpts", commonFlags, "gid=5,mode=620,ptmxmode=000"); err != nil { + if err := mount("devpts", "/dev/pts", "devpts", commonOptions+",gid=5,mode=620,ptmxmode=000", true); err != nil { panic(err) } + // Mount /dev/shm - if err := os.Mkdir("/dev/shm", 0755); err != nil && !errors.Is(err, os.ErrExist) { - panic(err) - } - if err := syscall.Mount("shm", "/dev/shm", "tmpfs", commonFlags|syscall.MS_NODEV, "inode64"); err != nil { + if err := mount("shm", "/dev/shm", "tmpfs", commonOptions+",nodev,inode64", true); err != nil { panic(err) } + // Mount securityfs - if err := syscall.Mount("securityfs", "/sys/kernel/security", "securityfs", commonFlags, ""); err != nil { + if err := mount("securityfs", "/sys/kernel/security", "securityfs", commonOptions, false); err != nil { panic(err) } + // Mount cgroups v2 - if err := syscall.Mount("cgroup2", "/sys/fs/cgroup", "cgroup2", commonFlags|syscall.MS_NOEXEC, ""); err != nil { + if err := mount("cgroup2", "/sys/fs/cgroup", "cgroup2", commonOptions+",noexec,nsdelegate,memory_recursiveprot", false); err != nil { panic(err) } @@ -119,12 +120,9 @@ func mountVirtualFilesystems() { } func mountFilesystems() { - fmt.Print("Mounting filesystems... ") + fmt.Print("Mounting fstab entries... ") - cmd := exec.Command("/bin/mount", "-a") - err := cmd.Run() - - if err != nil { + if err := mountFstabEntries(); err != nil { log.Println("Could not mount fstab entries!") panic(err) } diff --git a/cmd/enit/mount.go b/cmd/enit/mount.go new file mode 100644 index 0000000..55a5aac --- /dev/null +++ b/cmd/enit/mount.go @@ -0,0 +1,141 @@ +package main + +import ( + "golang.org/x/sys/unix" + "os" + "slices" + "strings" +) + +var flagsEquivalence = map[string]uintptr{ + "dirsync": unix.MS_DIRSYNC, + "lazytime": unix.MS_LAZYTIME, + "noatime": unix.MS_NOATIME, + "nodev": unix.MS_NODEV, + "nodiratime": unix.MS_NODIRATIME, + "noexec": unix.MS_NOEXEC, + "nosuid": unix.MS_NOSUID, + "ro": unix.MS_RDONLY, + "rw": 0, + "relatime": unix.MS_RELATIME, + "silent": unix.MS_SILENT, + "strictatime": unix.MS_STRICTATIME, + "sync": unix.MS_SYNCHRONOUS, + "defaults": 0, +} + +// Split string flags to mount flags and mount data +func convertMountOptions(options string) (flags []uintptr, data string) { + for _, flag := range strings.Split(options, ",") { + if unixFlag, ok := flagsEquivalence[flag]; ok { + flags = append(flags, unixFlag) + } else { + if data == "" { + data = flag + } else { + data += "," + flag + } + } + } + + return flags, data +} + +// Combine a unix flag slice or array into a single uintptr +func combineUnixFlags(flagsSlice []uintptr) (flags uintptr) { + flags = 0 + for _, flag := range flagsSlice { + flags |= flag + } + + return flags +} + +// Check whether a certain path is a mountpoint +func isMountpoint(mountpoint string) bool { + if mountpoint != "/" { + mountpoint = strings.TrimRight(mountpoint, "/") + } + + if _, err := os.Stat("/proc/mounts"); err != nil { + return false + } + + bytes, err := os.ReadFile("/proc/mounts") + if err != nil { + return false + } + + for _, line := range strings.Split(string(bytes), "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + + if strings.Split(line, " ")[1] == mountpoint { + return true + } + } + + return false +} + +func mount(source, target, fstype string, options string, mkdir bool) error { + flags, data := convertMountOptions(options) + + if isMountpoint(target) && !slices.Contains(flags, unix.MS_REMOUNT) { + flags = append(flags, unix.MS_REMOUNT) + } + + if mkdir { + err := os.MkdirAll(target, 0755) + if err != nil { + return err + } + } + + if err := unix.Mount(source, target, fstype, combineUnixFlags(flags), data); err != nil { + return err + } + + return nil +} + +func mountFstabEntries() error { + if _, err := os.Stat("/etc/fstab"); err != nil { + return err + } + + bytes, err := os.ReadFile("/etc/fstab") + if err != nil { + return err + } + + for _, line := range strings.Split(string(bytes), "\n") { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "#") || line == "" { + continue + } + + source := strings.Split(line, " ")[0] + target := strings.Split(line, " ")[1] + fstype := strings.Split(line, " ")[2] + options := strings.Split(line, " ")[3] + + flags, data := convertMountOptions(options) + + if slices.Contains(strings.Split(data, ","), "noauto") { + continue + } + + if isMountpoint(target) && !slices.Contains(flags, unix.MS_REMOUNT) { + flags = append(flags, unix.MS_REMOUNT) + } + + if err := unix.Mount(source, target, fstype, combineUnixFlags(flags), data); err != nil { + return err + } + } + + return nil +}