package fileops import ( "errors" "fmt" "os" "path/filepath" "strings" ) // RemoveAllUnderRoot removes target and its contents beneath root. target must // name a proper descendant of root; removal is confined by the opened root. func RemoveAllUnderRoot(rootPath, target string) error { root, targetName, err := openCleanupRoot(rootPath, target) if err != nil { return err } defer func() { _ = root.Close() }() if err := root.RemoveAll(targetName); err != nil { return fmt.Errorf("remove cleanup target %q: %w", targetName, err) } return nil } // RemoveFileUnderRoot removes an exact regular-file target below root without // following the leaf. A missing target is successful; directories, symlinks, // and other non-regular entries are rejected. func RemoveFileUnderRoot(rootPath, target string) error { root, targetName, err := openCleanupRoot(rootPath, target) if err != nil { return err } defer func() { _ = root.Close() }() info, err := root.Lstat(targetName) if errors.Is(err, os.ErrNotExist) { return nil } if err != nil { return fmt.Errorf("inspect cleanup file %q: %w", targetName, err) } if !info.Mode().IsRegular() { return fmt.Errorf("refusing to delete non-regular file path %q", targetName) } if err := root.Remove(targetName); err != nil { return fmt.Errorf("remove cleanup file %q: %w", targetName, err) } return nil } func openCleanupRoot(rootPath, target string) (*os.Root, string, error) { if strings.TrimSpace(rootPath) == "" { return nil, "", fmt.Errorf("cleanup root is required") } if strings.TrimSpace(target) == "" { return nil, "", fmt.Errorf("cleanup target is required") } rootAbs, err := filepath.Abs(rootPath) if err != nil { return nil, "", fmt.Errorf("resolve cleanup root: %w", err) } targetAbs, err := filepath.Abs(target) if err != nil { return nil, "", fmt.Errorf("resolve cleanup target: %w", err) } relative, err := filepath.Rel(rootAbs, targetAbs) if err != nil { return nil, "", fmt.Errorf("resolve cleanup target below root: %w", err) } if relative == "." || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) { return nil, "", fmt.Errorf("cleanup target %q must be below root %q", targetAbs, rootAbs) } name := filepath.Clean(relative) if filepath.IsAbs(name) || name == "." || name == ".." || strings.HasPrefix(name, ".."+string(filepath.Separator)) { return nil, "", fmt.Errorf("cleanup target %q must be below root %q", targetAbs, rootAbs) } root, err := os.OpenRoot(rootAbs) if err != nil { return nil, "", fmt.Errorf("open cleanup root %q: %w", rootAbs, err) } return root, name, nil }