From ff597672db24f85a8ed46b38c86a55e88780047b Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Sun, 20 Sep 2020 12:51:12 -0600 Subject: [PATCH] Fix #2380 --- api/sftp_endpoints.go | 4 ++-- events/pool.go | 2 +- router/router_server_files.go | 10 ++++++++++ router/websocket/websocket.go | 4 ++-- server/filesystem.go | 14 +++++++++++--- server/filesystem_unarchive.go | 25 ++++++++++++++++++++++--- server/listeners.go | 2 +- system/bool.go | 2 +- 8 files changed, 50 insertions(+), 13 deletions(-) diff --git a/api/sftp_endpoints.go b/api/sftp_endpoints.go index 10025c5..ce88552 100644 --- a/api/sftp_endpoints.go +++ b/api/sftp_endpoints.go @@ -70,8 +70,8 @@ func (r *PanelRequest) ValidateSftpCredentials(request SftpAuthRequest) (*SftpAu if r.HttpResponseCode() >= 400 && r.HttpResponseCode() < 500 { log.WithFields(log.Fields{ "subsystem": "sftp", - "username": request.User, - "ip": request.IP, + "username": request.User, + "ip": request.IP, }).Warn(r.Error().String()) return nil, new(sftpInvalidCredentialsError) diff --git a/events/pool.go b/events/pool.go index 881d3c7..0636eea 100644 --- a/events/pool.go +++ b/events/pool.go @@ -46,4 +46,4 @@ func (cp *CallbackPool) index(v reflect.Value) int { } return -1 -} \ No newline at end of file +} diff --git a/router/router_server_files.go b/router/router_server_files.go index ffe5f35..52ec18c 100644 --- a/router/router_server_files.go +++ b/router/router_server_files.go @@ -327,6 +327,16 @@ func postServerDecompressFiles(c *gin.Context) { hasSpace, err := s.Filesystem.SpaceAvailableForDecompression(data.RootPath, data.File) if err != nil { + // Handle an unknown format error. + if errors.Is(err, server.ErrUnknownArchiveFormat) { + s.Log().WithField("error", err).Warn("failed to decompress file due to unknown format") + + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ + "error": "unknown archive format", + }) + return + } + TrackedServerError(err, s).AbortWithServerError(c) return } diff --git a/router/websocket/websocket.go b/router/websocket/websocket.go index 4b893fe..4d5036b 100644 --- a/router/websocket/websocket.go +++ b/router/websocket/websocket.go @@ -38,9 +38,9 @@ type Handler struct { } var ( - ErrJwtNotPresent = errors.New("jwt: no jwt present") + ErrJwtNotPresent = errors.New("jwt: no jwt present") ErrJwtNoConnectPerm = errors.New("jwt: missing connect permission") - ErrJwtUuidMismatch = errors.New("jwt: server uuid mismatch") + ErrJwtUuidMismatch = errors.New("jwt: server uuid mismatch") ) func IsJwtError(err error) bool { diff --git a/server/filesystem.go b/server/filesystem.go index 0642970..77de583 100644 --- a/server/filesystem.go +++ b/server/filesystem.go @@ -599,7 +599,15 @@ func (fs *Filesystem) Copy(p string) error { base := filepath.Base(cleaned) relative := strings.TrimSuffix(strings.TrimPrefix(cleaned, fs.Path()), base) extension := filepath.Ext(base) - name := strings.TrimSuffix(base, filepath.Ext(base)) + name := strings.TrimSuffix(base, extension) + + // Ensure that ".tar" is also counted as apart of the file extension. + // There might be a better way to handle this for other double file extensions, + // but this is a good workaround for now. + if strings.HasSuffix(name, ".tar") { + extension = ".tar" + extension + name = strings.TrimSuffix(name, ".tar") + } // Begin looping up to 50 times to try and create a unique copy file name. This will take // an input of "file.txt" and generate "file copy.txt". If that name is already taken, it will @@ -942,7 +950,7 @@ func (fs *Filesystem) handleWalkerError(err error, f os.FileInfo) error { } type fileOpener struct { - busy uint + busy uint } // Attempts to open a given file up to "attempts" number of times, using a backoff. If the file @@ -965,4 +973,4 @@ func (fo *fileOpener) open(path string, flags int, perm os.FileMode) (*os.File, return f, err } -} \ No newline at end of file +} diff --git a/server/filesystem_unarchive.go b/server/filesystem_unarchive.go index c96daf9..6173e19 100644 --- a/server/filesystem_unarchive.go +++ b/server/filesystem_unarchive.go @@ -10,9 +10,12 @@ import ( "os" "path/filepath" "reflect" + "strings" "sync/atomic" ) +var ErrUnknownArchiveFormat = errors.New("filesystem: unknown archive format") + // Look through a given archive and determine if decompressing it would put the server over // its allocated disk space limit. func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) (bool, error) { @@ -35,14 +38,21 @@ func (fs *Filesystem) SpaceAvailableForDecompression(dir string, file string) (b var max = fs.Server.DiskSpace() * 1000.0 * 1000.0 // 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 > max { + if atomic.AddInt64(&size, f.Size())+dirSize > max { return errors.WithStack(ErrNotEnoughDiskSpace) } return nil }) + if err != nil { + if strings.HasPrefix(err.Error(), "format ") { + return false, errors.WithStack(ErrUnknownArchiveFormat) + } - return err == nil, errors.WithStack(err) + return false, errors.WithStack(err) + } + + return true, errors.WithStack(err) } // Decompress a file in a given directory by using the archiver tool to infer the file @@ -63,7 +73,7 @@ func (fs *Filesystem) DecompressFile(dir string, file string) error { // Walk over all of the files spinning up an additional go-routine for each file we've encountered // and then extract that file from the archive and write it to the disk. If any part of this process // encounters an error the entire process will be stopped. - return archiver.Walk(source, func(f archiver.File) error { + err = archiver.Walk(source, func(f archiver.File) error { // Don't waste time with directories, we don't need to create them if they have no contents, and // we will ensure the directory exists when opening the file for writing anyways. if f.IsDir() { @@ -90,4 +100,13 @@ func (fs *Filesystem) DecompressFile(dir string, file string) error { return errors.Wrap(fs.Writefile(p, f), "could not extract file from archive") }) + if err != nil { + if strings.HasPrefix(err.Error(), "format ") { + return errors.WithStack(ErrUnknownArchiveFormat) + } + + return errors.WithStack(err) + } + + return nil } diff --git a/server/listeners.go b/server/listeners.go index 4029de4..3b1cbbb 100644 --- a/server/listeners.go +++ b/server/listeners.go @@ -23,7 +23,7 @@ var dockerEvents = []string{ func (s *Server) StartEventListeners() { console := func(e events.Event) { t := s.Throttler() - err := t.Increment(func () { + err := t.Increment(func() { s.PublishConsoleOutputFromDaemon("Your server is outputting too much data and is being throttled.") }) diff --git a/system/bool.go b/system/bool.go index f3cd08e..5f30367 100644 --- a/system/bool.go +++ b/system/bool.go @@ -17,4 +17,4 @@ func (ab *AtomicBool) Set(v bool) { func (ab *AtomicBool) Get() bool { return atomic.LoadUint32(&ab.flag) == 1 -} \ No newline at end of file +}