feat: support explicit instance data deletion

This commit is contained in:
QiuSW
2026-07-27 23:16:11 +08:00
parent e400172d04
commit cab7e6ac7b
12 changed files with 670 additions and 28 deletions
@@ -0,0 +1,77 @@
package files
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
)
var (
ErrUnsafeInstanceDataDirectory = errors.New("unsafe instance data directory")
ErrInstanceDataNotDirectory = errors.New("instance data path is not a directory")
)
// InstanceDataRemover deletes one explicitly authorized browser User Data Dir.
// Callers must check browser occupancy before invoking it.
type InstanceDataRemover interface {
RemoveInstanceData(context.Context, string) error
}
type instanceDataRemover struct {
lstat func(string) (os.FileInfo, error)
removeAll func(string) error
}
func NewInstanceDataRemover() InstanceDataRemover {
return newInstanceDataRemover(os.Lstat, os.RemoveAll)
}
func newInstanceDataRemover(lstat func(string) (os.FileInfo, error), removeAll func(string) error) instanceDataRemover {
return instanceDataRemover{lstat: lstat, removeAll: removeAll}
}
func (r instanceDataRemover) RemoveInstanceData(ctx context.Context, userDataDir string) error {
if err := ctx.Err(); err != nil {
return err
}
path, err := normalizeRemovableInstanceDataDir(userDataDir)
if err != nil {
return err
}
info, err := r.lstat(path)
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return fmt.Errorf("inspect instance data directory: %w", err)
}
if info.Mode()&(os.ModeSymlink|os.ModeIrregular) != 0 || isReparsePoint(info) {
return fmt.Errorf("%w: links and reparse points are not removable", ErrUnsafeInstanceDataDirectory)
}
if !info.IsDir() {
return ErrInstanceDataNotDirectory
}
if err := ctx.Err(); err != nil {
return err
}
if err := r.removeAll(path); err != nil {
return fmt.Errorf("remove instance data directory: %w", err)
}
return nil
}
func normalizeRemovableInstanceDataDir(value string) (string, error) {
value = strings.TrimSpace(value)
if value == "" || !filepath.IsAbs(value) {
return "", fmt.Errorf("%w: path must be absolute", ErrUnsafeInstanceDataDirectory)
}
path := filepath.Clean(value)
volumeRoot := filepath.Clean(filepath.VolumeName(path) + string(filepath.Separator))
if strings.EqualFold(path, volumeRoot) {
return "", fmt.Errorf("%w: volume root is not removable", ErrUnsafeInstanceDataDirectory)
}
return path, nil
}