Files
URBM/internal/platform/browser.go
T
2026-06-14 12:08:37 +02:00

76 lines
2.0 KiB
Go

package platform
import (
"errors"
"os"
"path/filepath"
"sort"
"strings"
)
type DirectoryEntry struct {
Name string `json:"name"`
Path string `json:"path"`
}
var browseRoots = []string{"/mnt/user", "/mnt/disks", "/mnt/remotes", "/boot"}
func BrowseDirectories(path string) ([]DirectoryEntry, error) {
clean, err := validateBrowsePath(path)
if err != nil {
return nil, err
}
entries, err := os.ReadDir(clean)
if err != nil {
return nil, err
}
result := make([]DirectoryEntry, 0, len(entries))
for _, entry := range entries {
if entry.IsDir() && entry.Type()&os.ModeSymlink == 0 {
result = append(result, DirectoryEntry{Name: entry.Name(), Path: filepath.Join(clean, entry.Name())})
}
}
sort.Slice(result, func(i, j int) bool { return strings.ToLower(result[i].Name) < strings.ToLower(result[j].Name) })
return result, nil
}
func BrowseRoots() []DirectoryEntry {
result := make([]DirectoryEntry, 0, len(browseRoots))
for _, root := range browseRoots {
if info, err := os.Stat(root); err == nil && info.IsDir() {
result = append(result, DirectoryEntry{Name: root, Path: root})
}
}
return result
}
func validateBrowsePath(path string) (string, error) {
if path == "" || path == "/" {
return "", errors.New("validation: select an allowed browse root")
}
clean := filepath.Clean(path)
if !filepath.IsAbs(clean) {
return "", errors.New("validation: browse path must be absolute")
}
allowed := false
for _, root := range browseRoots {
if clean == root || strings.HasPrefix(clean, root+string(os.PathSeparator)) {
allowed = true
break
}
}
if !allowed {
return "", errors.New("validation: browse path is outside allowed Unraid storage roots")
}
resolved, err := filepath.EvalSymlinks(clean)
if err != nil {
return "", err
}
for _, root := range browseRoots {
if resolved == root || strings.HasPrefix(resolved, root+string(os.PathSeparator)) {
return resolved, nil
}
}
return "", errors.New("validation: browse path resolves outside allowed Unraid storage roots")
}