58 lines
1.3 KiB
Go
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])
|
|
}
|