diff --git a/internal/ufs/walk_unix.go b/internal/ufs/walk_unix.go index e3945b1..8472edf 100644 --- a/internal/ufs/walk_unix.go +++ b/internal/ufs/walk_unix.go @@ -66,6 +66,13 @@ func (fs *UnixFS) walkDir(b []byte, parentfd int, name, relative string, d DirEn } for _, d1 := range dirs { + // TODO: the path.Join on this line may actually be partially incorrect. + // If we are not walking starting at the root, relative will contain the + // name of the directory we are starting the walk from, which will be + // relative to the root of the filesystem instead of from where the walk + // was initiated from. + // + // ref; https://github.com/pterodactyl/panel/issues/5030 if err := fs.walkDir(b, dirfd, d1.Name(), path.Join(relative, d1.Name()), d1, walkDirFn); err != nil { if err == SkipDir { break diff --git a/server/filesystem/archive.go b/server/filesystem/archive.go index dae5cc6..16ae7f9 100644 --- a/server/filesystem/archive.go +++ b/server/filesystem/archive.go @@ -113,6 +113,10 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error { return errors.New("filesystem: archive.Filesystem is unset") } + // The base directory may come with a prefixed `/`, strip it to prevent + // problems. + a.BaseDirectory = strings.TrimPrefix(a.BaseDirectory, "/") + if filesLen := len(a.Files); filesLen > 0 { files := make([]string, filesLen) for i, f := range a.Files { @@ -167,11 +171,14 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error { callback = a.callback() } + // Open the base directory we were provided. dirfd, name, closeFd, err := fs.SafePath(a.BaseDirectory) defer closeFd() if err != nil { return err } + + // Recursively walk the base directory. return fs.WalkDirat(dirfd, name, func(dirfd int, name, relative string, d ufs.DirEntry, err error) error { if err != nil { return err @@ -188,12 +195,28 @@ func (a *Archive) Stream(ctx context.Context, w io.Writer) error { // Callback function used to determine if a given file should be included in the archive // being generated. func (a *Archive) callback(opts ...walkFunc) walkFunc { + // Get the base directory we need to strip when walking. + // + // This is important as when we are walking, the last part of the base directory + // is present on all the paths we walk. + var base string + if a.BaseDirectory != "" { + base = filepath.Base(a.BaseDirectory) + "/" + } return func(dirfd int, name, relative string, d ufs.DirEntry) error { // Skip directories because we are walking them recursively. if d.IsDir() { return nil } + // If base isn't empty, strip it from the relative path. This fixes an + // issue when creating an archive starting from a nested directory. + // + // See https://github.com/pterodactyl/panel/issues/5030 for more details. + if base != "" { + relative = strings.TrimPrefix(relative, base) + } + // Call the additional options passed to this callback function. If any of them return // a non-nil error we will exit immediately. for _, opt := range opts {