Tighten disk safety checks
This commit is contained in:
@@ -5,12 +5,17 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"jukebox_maker/internal/disk"
|
||||
)
|
||||
|
||||
type OverwriteMode string
|
||||
type FileSelectMode string
|
||||
|
||||
const (
|
||||
DefaultDestFolder = "media"
|
||||
|
||||
OverwriteSkip OverwriteMode = "skip"
|
||||
OverwriteDelete OverwriteMode = "delete"
|
||||
|
||||
@@ -30,12 +35,14 @@ type Config struct {
|
||||
OverwriteMode OverwriteMode `json:"overwrite_mode"`
|
||||
FileSelectMode FileSelectMode `json:"file_select_mode"`
|
||||
AutoCopy bool `json:"auto_copy"`
|
||||
FileReplicaCounts map[string]int `json:"file_replica_counts,omitempty"`
|
||||
DiskReplicaFiles map[string][]string `json:"disk_replica_files,omitempty"`
|
||||
}
|
||||
|
||||
func defaults() Config {
|
||||
return Config{
|
||||
ReserveFreeGB: 2.0,
|
||||
DestFolder: "media",
|
||||
DestFolder: DefaultDestFolder,
|
||||
OverwriteMode: OverwriteSkip,
|
||||
FileSelectMode: SelectNew,
|
||||
AutoCopy: false,
|
||||
@@ -55,6 +62,11 @@ func Load(path string) (*Config, error) {
|
||||
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if destFolder, err := NormalizeDestFolder(cfg.DestFolder); err == nil {
|
||||
cfg.DestFolder = destFolder
|
||||
} else {
|
||||
cfg.DestFolder = defaults().DestFolder
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
@@ -77,6 +89,9 @@ func (c *Config) Validate() error {
|
||||
if c.ReserveFreeGB < 0 {
|
||||
return errors.New("reserve_free_gb must be >= 0")
|
||||
}
|
||||
if _, err := NormalizeDestFolder(c.DestFolder); err != nil {
|
||||
return err
|
||||
}
|
||||
switch c.OverwriteMode {
|
||||
case OverwriteSkip, OverwriteDelete:
|
||||
default:
|
||||
@@ -89,3 +104,26 @@ func (c *Config) Validate() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NormalizeDestFolder(value string) (string, error) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return DefaultDestFolder, nil
|
||||
}
|
||||
|
||||
clean := filepath.ToSlash(filepath.Clean(value))
|
||||
clean = strings.TrimPrefix(clean, "./")
|
||||
clean = strings.TrimPrefix(clean, "/")
|
||||
|
||||
switch clean {
|
||||
case "", ".", "..":
|
||||
return "", errors.New("dest_folder must be a subfolder on disk, not the disk root")
|
||||
}
|
||||
if strings.HasPrefix(clean, "../") {
|
||||
return "", errors.New("dest_folder must stay inside the disk")
|
||||
}
|
||||
if clean == disk.MarkerDir || strings.HasPrefix(clean, disk.MarkerDir+"/") {
|
||||
return "", errors.New("dest_folder conflicts with internal disk metadata")
|
||||
}
|
||||
return clean, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user