Files
dsh-web/internal/api/instance_data.go
T

58 lines
1.3 KiB
Go

package api
import (
"errors"
"os"
"strings"
"git.misaka.ren/m1saka/dsh-web/internal/store"
)
// Call only with the instance lock held, after Docker resources are removed.
func (a *API) removeInstanceData(i store.Instance) error {
for _, id := range []string{i.UID, i.ID} {
if id == "" || id == "." || id == ".." || strings.ContainsAny(id, "/\\") {
return errors.New("invalid instance storage path")
}
}
for _, parts := range [][]string{{"workspaces", i.UID, "instances", i.ID}, {"backups", i.UID, i.ID}} {
if err := a.removeDataDirectory(parts); err != nil {
return err
}
}
return nil
}
// Walk parents without following symlinks; RemoveAll unlinks child symlinks.
func (a *API) removeDataDirectory(parts []string) error {
root, err := os.OpenRoot(a.root)
if err != nil {
return err
}
defer func() { root.Close() }()
for _, part := range parts[:len(parts)-1] {
st, err := root.Lstat(part)
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return err
}
if !st.IsDir() {
return errors.New("unsafe storage path")
}
next, err := root.OpenRoot(part)
if err != nil {
return err
}
actual, err := next.Stat(".")
if err != nil || !os.SameFile(st, actual) {
next.Close()
return errors.New("storage path changed")
}
root.Close()
root = next
}
return root.RemoveAll(parts[len(parts)-1])
}