Files
URBM/internal/platform/storage.go
T

87 lines
2.3 KiB
Go

package platform
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"git.casaderoll.de/michael/urbm/internal/model"
)
var arrayDiskName = regexp.MustCompile(`^disk[1-9][0-9]*$`)
func isArrayDiskPath(path string) bool {
parts := strings.Split(strings.TrimPrefix(filepath.Clean(path), "/"), "/")
return len(parts) >= 2 && parts[0] == "mnt" && arrayDiskName.MatchString(parts[1])
}
func mountedPaths() ([]string, error) {
data, err := os.ReadFile("/proc/self/mountinfo")
if err != nil {
return nil, err
}
return parseMountPaths(string(data)), nil
}
func parseMountPaths(data string) []string {
var paths []string
unescape := strings.NewReplacer(`\040`, " ", `\011`, "\t", `\012`, "\n", `\134`, `\`)
for _, line := range strings.Split(data, "\n") {
fields := strings.Fields(line)
if len(fields) >= 10 {
paths = append(paths, unescape.Replace(fields[4]))
}
}
return paths
}
func storageBrowseRoots(mounts []string) []string {
roots := append([]string{}, browseRoots...)
seen := make(map[string]bool)
for _, mount := range mounts {
if filepath.Dir(mount) == "/mnt" && isArrayDiskPath(mount) && !seen[mount] {
roots = append(roots, mount)
seen[mount] = true
}
}
return roots
}
func pathUnder(path, root string) bool {
return path == root || strings.HasPrefix(path, root+string(os.PathSeparator))
}
// Require an actual mount for removable disks and array sources. An empty
// directory left behind by an unmount must never become a backup destination.
func validateRsyncMounts(job model.Job, mounts []string) error {
paths := []string{job.Rsync.Target}
for _, source := range job.Sources {
paths = append(paths, source.Path)
}
for _, path := range paths {
clean := filepath.Clean(path)
if !isArrayDiskPath(clean) && !pathUnder(clean, "/mnt/disks") {
continue
}
resolved, err := filepath.EvalSymlinks(clean)
if err != nil {
return fmt.Errorf("environment: rsync disk unavailable: %s: %w", clean, err)
}
if !diskPathMounted(resolved, mounts) {
return fmt.Errorf("environment: rsync disk is not mounted: %s", clean)
}
}
return nil
}
func diskPathMounted(resolved string, mounts []string) bool {
for _, mount := range mounts {
if ((isArrayDiskPath(mount) && filepath.Dir(mount) == "/mnt") || strings.HasPrefix(mount, "/mnt/disks/")) && pathUnder(resolved, mount) {
return true
}
}
return false
}