Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"encoding/json"
"io"
"path/filepath"
"strconv"
"strings"

Expand All @@ -19,6 +20,7 @@ import (

"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/internal/ufs"
"github.com/pterodactyl/wings/server/filesystem"
)

// The file parsing options that are available for a server configuration file.
Expand Down Expand Up @@ -591,3 +593,51 @@ func (f *ConfigurationFile) parsePropertiesFile(file ufs.File) error {
}
return nil
}

// MatchPattern takes a filesystem, root path, and pattern to match files against,
// returning a list of matched file paths. It supports wildcard patterns using
// standard filepath matching rules.
func MatchPattern(fs *filesystem.Filesystem, rootPath, pattern string) ([]string, error) {
var results []string

// Separate the pattern into parts
parts := strings.Split(pattern, string(filepath.Separator))

// If the pattern has multiple parts (e.g., logs/*.txt), process recursively
if len(parts) > 1 {
currentDir := rootPath
for i, part := range parts {
if i == len(parts)-1 {
// Last part is the file pattern
entries, err := fs.ListDirectory(currentDir)
// If directory don't exist, just ignore.
if err != nil {
return nil, nil
}

for _, entry := range entries {
if matched, _ := filepath.Match(part, entry.Name()); matched {
results = append(results, filepath.Join(currentDir, entry.Name()))
}
}
} else {
// Intermediate parts: enter the directory if it exists
currentDir = filepath.Join(currentDir, part)
}
}
} else {
// Simple pattern (e.g., *.txt) - match in the rootPath
entries, err := fs.ListDirectory(rootPath)
if err != nil {
return nil, err
}

for _, entry := range entries {
if matched, _ := filepath.Match(pattern, entry.Name()); matched {
results = append(results, filepath.Join(rootPath, entry.Name()))
}
}
}

return results, nil
}
34 changes: 22 additions & 12 deletions router/router_server_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/internal/models"
"github.com/pterodactyl/wings/parser"
"github.com/pterodactyl/wings/router/downloader"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/tokens"
Expand Down Expand Up @@ -202,21 +203,30 @@ func postServerDeleteFiles(c *gin.Context) {
return
}

g, ctx := errgroup.WithContext(context.Background())
// Expand any patterns provided in the files array.
var expandedFiles []string
for _, pattern := range data.Files {
matches, err := parser.MatchPattern(s.Filesystem(), data.Root, pattern)
if err != nil {
middleware.CaptureAndAbort(c, err)
return
}
expandedFiles = append(expandedFiles, matches...)
}

// Loop over the array of files passed in and delete them. If any of the file deletions
// fail just abort the process entirely.
for _, p := range data.Files {
pi := path.Join(data.Root, p)
g, ctx := errgroup.WithContext(context.Background())

g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return s.Filesystem().Delete(pi)
for _, filePath := range expandedFiles {
g.Go(func(fp string) func() error {
return func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return s.Filesystem().Delete(fp)
}
}
})
}(filePath))
}

if err := g.Wait(); err != nil {
Expand Down