Add better error handling for filesystem
This commit is contained in:
parent
a0ae5fd131
commit
0676a82a21
|
@ -2,13 +2,13 @@ package backup
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/pterodactyl/wings/server/filesystem"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pterodactyl/wings/server/filesystem"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/pterodactyl/wings/remote"
|
||||
"github.com/pterodactyl/wings/system"
|
||||
)
|
||||
|
||||
type LocalBackup struct {
|
||||
|
@ -78,10 +78,6 @@ func (b *LocalBackup) Restore(_ io.Reader, callback RestoreCallback) error {
|
|||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
name, err := system.ExtractArchiveSourceName(f, "/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callback(name, f)
|
||||
return callback(f.Name(), f)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/mholt/archiver/v3"
|
||||
"github.com/pterodactyl/wings/system"
|
||||
)
|
||||
|
||||
// CompressFiles compresses all of the files matching the given paths in the
|
||||
|
@ -86,13 +86,13 @@ func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) er
|
|||
// Walk over the archive and figure out just how large the final output would be from unarchiving it.
|
||||
err = archiver.Walk(source, func(f archiver.File) error {
|
||||
if atomic.AddInt64(&size, f.Size())+dirSize > fs.MaxDisk() {
|
||||
return &Error{code: ErrCodeDiskSpace}
|
||||
return newFilesystemError(ErrCodeDiskSpace, nil)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "format ") {
|
||||
return &Error{code: ErrCodeUnknownArchive}
|
||||
if IsUnknownArchiveFormatError(err) {
|
||||
return newFilesystemError(ErrCodeUnknownArchive, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ func (fs *Filesystem) DecompressFile(dir string, file string) error {
|
|||
}
|
||||
// Ensure that the source archive actually exists on the system.
|
||||
if _, err := os.Stat(source); err != nil {
|
||||
return err
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// Walk all of the files in the archiver file and write them to the disk. If any
|
||||
|
@ -127,13 +127,13 @@ func (fs *Filesystem) DecompressFile(dir string, file string) error {
|
|||
return nil
|
||||
}
|
||||
if err := fs.Writefile(p, f); err != nil {
|
||||
return &Error{code: ErrCodeUnknownError, err: err, resolved: source}
|
||||
return wrapError(err, source)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "format ") {
|
||||
return &Error{code: ErrCodeUnknownArchive}
|
||||
if IsUnknownArchiveFormatError(err) {
|
||||
return newFilesystemError(ErrCodeUnknownArchive, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package filesystem
|
||||
|
||||
import (
|
||||
"emperror.dev/errors"
|
||||
"github.com/apex/log"
|
||||
"github.com/karrick/godirwalk"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/apex/log"
|
||||
"github.com/karrick/godirwalk"
|
||||
)
|
||||
|
||||
type SpaceCheckingOpts struct {
|
||||
|
@ -48,7 +49,7 @@ func (fs *Filesystem) SetDiskLimit(i int64) {
|
|||
// no space, rather than a boolean value.
|
||||
func (fs *Filesystem) HasSpaceErr(allowStaleValue bool) error {
|
||||
if !fs.HasSpaceAvailable(allowStaleValue) {
|
||||
return &Error{code: ErrCodeDiskSpace}
|
||||
return newFilesystemError(ErrCodeDiskSpace, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -200,16 +201,13 @@ func (fs *Filesystem) HasSpaceFor(size int64) error {
|
|||
if fs.MaxDisk() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
s, err := fs.DiskUsage(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if (s + size) > fs.MaxDisk() {
|
||||
return &Error{code: ErrCodeDiskSpace}
|
||||
return newFilesystemError(ErrCodeDiskSpace, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/apex/log"
|
||||
|
@ -34,6 +35,14 @@ type Error struct {
|
|||
path string
|
||||
}
|
||||
|
||||
// newFilesystemError returns a new error instance with a stack trace associated.
|
||||
func newFilesystemError(code ErrorCode, err error) error {
|
||||
if err != nil {
|
||||
return errors.WithStackDepth(&Error{code: code, err: err}, 1)
|
||||
}
|
||||
return errors.WithStackDepth(&Error{code: code}, 1)
|
||||
}
|
||||
|
||||
// Code returns the ErrorCode for this specific error instance.
|
||||
func (e *Error) Code() ErrorCode {
|
||||
return e.code
|
||||
|
@ -63,13 +72,13 @@ func (e *Error) Error() string {
|
|||
case ErrCodeUnknownError:
|
||||
fallthrough
|
||||
default:
|
||||
return fmt.Sprintf("filesystem: an error occurred: %s", e.Cause())
|
||||
return fmt.Sprintf("filesystem: an error occurred: %s", e.Unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of this filesystem error. In some causes
|
||||
// Unwrap returns the underlying cause of this filesystem error. In some causes
|
||||
// there may not be a cause present, in which case nil will be returned.
|
||||
func (e *Error) Cause() error {
|
||||
func (e *Error) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
|
@ -113,20 +122,26 @@ func IsErrorCode(err error, code ErrorCode) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// NewBadPathResolution returns a new BadPathResolution error.
|
||||
func NewBadPathResolution(path string, resolved string) *Error {
|
||||
return &Error{code: ErrCodePathResolution, path: path, resolved: resolved}
|
||||
// IsUnknownArchiveFormatError checks if the error is due to the archive being
|
||||
// in an unexpected file format.
|
||||
func IsUnknownArchiveFormatError(err error) bool {
|
||||
if err != nil && strings.HasPrefix(err.Error(), "format ") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WrapError wraps the provided error as a Filesystem error and attaches the
|
||||
// NewBadPathResolution returns a new BadPathResolution error.
|
||||
func NewBadPathResolution(path string, resolved string) error {
|
||||
return errors.WithStackDepth(&Error{code: ErrCodePathResolution, path: path, resolved: resolved}, 1)
|
||||
}
|
||||
|
||||
// wrapError wraps the provided error as a Filesystem error and attaches the
|
||||
// provided resolved source to it. If the error is already a Filesystem error
|
||||
// no action is taken.
|
||||
func WrapError(err error, resolved string) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
func wrapError(err error, resolved string) error {
|
||||
if err == nil || IsFilesystemError(err) {
|
||||
return err
|
||||
}
|
||||
if IsFilesystemError(err) {
|
||||
return err.(*Error)
|
||||
}
|
||||
return &Error{code: ErrCodeUnknownError, err: err, resolved: resolved}
|
||||
return errors.WithStackDepth(&Error{code: ErrCodeUnknownError, err: err, resolved: resolved}, 1)
|
||||
}
|
|
@ -1,13 +1,45 @@
|
|||
package filesystem
|
||||
|
||||
import (
|
||||
. "github.com/franela/goblin"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"emperror.dev/errors"
|
||||
. "github.com/franela/goblin"
|
||||
)
|
||||
|
||||
type stackTracer interface {
|
||||
StackTrace() errors.StackTrace
|
||||
}
|
||||
|
||||
func TestFilesystem_PathResolutionError(t *testing.T) {
|
||||
g := Goblin(t)
|
||||
|
||||
g.Describe("NewFilesystemError", func() {
|
||||
g.It("includes a stack trace for the error", func() {
|
||||
err := newFilesystemError(ErrCodeUnknownError, nil)
|
||||
|
||||
_, ok := err.(stackTracer)
|
||||
g.Assert(ok).IsTrue()
|
||||
})
|
||||
|
||||
g.It("properly wraps the underlying error cause", func() {
|
||||
underlying := io.EOF
|
||||
err := newFilesystemError(ErrCodeUnknownError, underlying)
|
||||
|
||||
_, ok := err.(stackTracer)
|
||||
g.Assert(ok).IsTrue()
|
||||
|
||||
_, ok = err.(*Error)
|
||||
g.Assert(ok).IsFalse()
|
||||
|
||||
fserr, ok := errors.Unwrap(err).(*Error)
|
||||
g.Assert(ok).IsTrue()
|
||||
g.Assert(fserr.Unwrap()).IsNotNil()
|
||||
g.Assert(fserr.Unwrap()).Equal(underlying)
|
||||
})
|
||||
})
|
||||
|
||||
g.Describe("NewBadPathResolutionError", func() {
|
||||
g.It("is can detect itself as an error correctly", func() {
|
||||
err := NewBadPathResolution("foo", "bar")
|
||||
|
@ -18,6 +50,7 @@ func TestFilesystem_PathResolutionError(t *testing.T) {
|
|||
|
||||
g.It("returns <empty> if no destination path is provided", func() {
|
||||
err := NewBadPathResolution("foo", "")
|
||||
g.Assert(err).IsNotNil()
|
||||
g.Assert(err.Error()).Equal("filesystem: server path [foo] resolves to a location outside the server root: <empty>")
|
||||
})
|
||||
})
|
||||
|
|
|
@ -67,7 +67,7 @@ func (fs *Filesystem) File(p string) (*os.File, Stat, error) {
|
|||
return nil, Stat{}, err
|
||||
}
|
||||
if st.IsDir() {
|
||||
return nil, Stat{}, &Error{code: ErrCodeIsDirectory}
|
||||
return nil, Stat{}, newFilesystemError(ErrCodeIsDirectory, nil)
|
||||
}
|
||||
f, err := os.Open(cleaned)
|
||||
if err != nil {
|
||||
|
@ -144,7 +144,7 @@ func (fs *Filesystem) Writefile(p string, r io.Reader) error {
|
|||
return errors.Wrap(err, "server/filesystem: writefile: failed to stat file")
|
||||
} else if err == nil {
|
||||
if stat.IsDir() {
|
||||
return &Error{code: ErrCodeIsDirectory, resolved: cleaned}
|
||||
return errors.WithStack(&Error{code: ErrCodeIsDirectory, resolved: cleaned})
|
||||
}
|
||||
currentSize = stat.Size()
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ func (fs *Filesystem) IsIgnored(paths ...string) error {
|
|||
return err
|
||||
}
|
||||
if fs.denylist.MatchesPath(sp) {
|
||||
return &Error{code: ErrCodeDenylistFile, path: p, resolved: sp}
|
||||
return errors.WithStack(&Error{code: ErrCodeDenylistFile, path: p, resolved: sp})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -2,11 +2,12 @@ package filesystem
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"emperror.dev/errors"
|
||||
. "github.com/franela/goblin"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"emperror.dev/errors"
|
||||
. "github.com/franela/goblin"
|
||||
)
|
||||
|
||||
func TestFilesystem_Path(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user