Handle path resolution errors better in the file walker

This commit is contained in:
Dane Everitt 2020-07-18 11:57:50 -07:00
parent 4f1b0c67d6
commit 085a02726b
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
2 changed files with 47 additions and 7 deletions

View File

@ -26,7 +26,18 @@ import (
) )
// Error returned when there is a bad path provided to one of the FS calls. // Error returned when there is a bad path provided to one of the FS calls.
var InvalidPathResolution = errors.New("invalid path resolution") type PathResolutionError struct{}
// Returns the error response in a string form that can be more easily consumed.
func (pre PathResolutionError) Error() string {
return "invalid path resolution"
}
func IsPathResolutionError(err error) bool {
_, ok := err.(PathResolutionError)
return ok
}
type Filesystem struct { type Filesystem struct {
Server *Server Server *Server
@ -87,7 +98,7 @@ func (fs *Filesystem) SafePath(p string) (string, error) {
// attempt going on, and we should NOT resolve this path for them. // attempt going on, and we should NOT resolve this path for them.
if nonExistentPathResolution != "" { if nonExistentPathResolution != "" {
if !strings.HasPrefix(nonExistentPathResolution, fs.Path()) { if !strings.HasPrefix(nonExistentPathResolution, fs.Path()) {
return "", InvalidPathResolution return "", PathResolutionError{}
} }
// If the nonExistentPathResolution variable is not empty then the initial path requested // If the nonExistentPathResolution variable is not empty then the initial path requested
@ -104,7 +115,7 @@ func (fs *Filesystem) SafePath(p string) (string, error) {
return p, nil return p, nil
} }
return "", InvalidPathResolution return "", PathResolutionError{}
} }
// Helper function to keep some of the codebase a little cleaner. Returns a "safe" version of the path // Helper function to keep some of the codebase a little cleaner. Returns a "safe" version of the path
@ -246,7 +257,7 @@ func (fs *Filesystem) DirectorySize(dir string) (int64, error) {
var size int64 var size int64
err := fs.Walk(dir, func(_ string, f os.FileInfo, err error) error { err := fs.Walk(dir, func(_ string, f os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return fs.handleWalkerError(err, f)
} }
if !f.IsDir() { if !f.IsDir() {
@ -707,7 +718,7 @@ func (fs *Filesystem) GetIncludedFiles(dir string, ignored []string) (*backup.In
if err := fs.Walk(cleaned, func(p string, f os.FileInfo, err error) error { if err := fs.Walk(cleaned, func(p string, f os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return fs.handleWalkerError(err, f)
} }
// Avoid unnecessary parsing if there are no ignored files, nothing will match anyways // Avoid unnecessary parsing if there are no ignored files, nothing will match anyways
@ -764,7 +775,7 @@ func (fs *Filesystem) CompressFiles(dir string, paths []string) (os.FileInfo, er
if f.IsDir() { if f.IsDir() {
err := fs.Walk(p, func(s string, info os.FileInfo, err error) error { err := fs.Walk(p, func(s string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return fs.handleWalkerError(err, info)
} }
if !info.IsDir() { if !info.IsDir() {
@ -788,3 +799,23 @@ func (fs *Filesystem) CompressFiles(dir string, paths []string) (os.FileInfo, er
return a.Create(d, context.Background()) return a.Create(d, context.Background())
} }
// Handle errors encountered when walking through directories.
//
// If there is a path resolution error just skip the item entirely. Only return this for a
// directory, otherwise return nil. Returning this error for a file will stop the walking
// for the remainder of the directory. This is assuming an os.FileInfo struct was even returned.
func (fs *Filesystem) handleWalkerError(err error, f os.FileInfo) error {
fmt.Println("handling walker error")
if !IsPathResolutionError(err) {
fmt.Println("not a path res error")
fmt.Println(err)
return err
}
if f != nil && f.IsDir() {
return filepath.SkipDir
}
return nil
}

View File

@ -62,9 +62,18 @@ func (w *PooledFileWalker) process(path string) error {
for _, f := range files { for _, f := range files {
sp, err := w.Filesystem.SafeJoin(p, f) sp, err := w.Filesystem.SafeJoin(p, f)
if err != nil { if err != nil {
// Let the callback function handle what to do if there is a path resolution error because a
// dangerous path was resolved. If there is an error returned, return from this entire process
// otherwise just skip over this specific file. We don't care if its a file or a directory at
// this point since either way we're skipping it, however, still check for the SkipDir since that
// would be thrown otherwise.
if err = w.callback(sp, f, err); err != nil && err != filepath.SkipDir {
return err return err
} }
continue
}
i, err := os.Stat(sp) i, err := os.Stat(sp)
// You might end up getting an error about a file or folder not existing if the given path // You might end up getting an error about a file or folder not existing if the given path
// if it is an invalid symlink. We can safely just skip over these files I believe. // if it is an invalid symlink. We can safely just skip over these files I believe.