better error handling for busy files; closes pterodactyl/panel#2332
This commit is contained in:
parent
6ba15e9884
commit
ce76b9339e
|
@ -339,10 +339,22 @@ func postServerDecompressFiles(c *gin.Context) {
|
|||
}
|
||||
|
||||
if err := s.Filesystem.DecompressFile(data.RootPath, data.File); err != nil {
|
||||
// Check if the file does not exist.
|
||||
// NOTE: os.IsNotExist() does not work if the error is wrapped.
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
c.Status(http.StatusNotFound)
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
|
||||
"error": "The requested archive was not found.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// If the file is busy for some reason just return a nicer error to the user since there is not
|
||||
// much we specifically can do. They'll need to stop the running server process in order to overwrite
|
||||
// a file like this.
|
||||
if strings.Contains(err.Error(), "text file busy") {
|
||||
s.Log().WithField("error", err).Warn("failed to decompress file due to busy text file")
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"error": "One or more files this archive is attempting to overwrite are currently in use by another process. Please try again.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -400,9 +400,10 @@ func (fs *Filesystem) Writefile(p string, r io.Reader) error {
|
|||
currentSize = stat.Size()
|
||||
}
|
||||
|
||||
o := &fileOpener{}
|
||||
// This will either create the file if it does not already exist, or open and
|
||||
// truncate the existing file.
|
||||
file, err := os.OpenFile(cleaned, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
file, err := o.open(cleaned, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
@ -937,3 +938,29 @@ func (fs *Filesystem) handleWalkerError(err error, f os.FileInfo) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
type fileOpener struct {
|
||||
busy uint
|
||||
}
|
||||
|
||||
// Attempts to open a given file up to "attempts" number of times, using a backoff. If the file
|
||||
// cannot be opened because of a "text file busy" error, we will attempt until the number of attempts
|
||||
// has been exhaused, at which point we will abort with an error.
|
||||
func (fo *fileOpener) open(path string, flags int, perm os.FileMode) (*os.File, error) {
|
||||
for {
|
||||
f, err := os.OpenFile(path, flags, perm)
|
||||
|
||||
// If there is an error because the text file is busy, go ahead and sleep for a few
|
||||
// hundred milliseconds and then try again up to three times before just returning the
|
||||
// error back to the caller.
|
||||
//
|
||||
// Based on code from: https://github.com/golang/go/issues/22220#issuecomment-336458122
|
||||
if err != nil && fo.busy < 3 && strings.Contains(err.Error(), "text file busy") {
|
||||
time.Sleep(100 * time.Millisecond << fo.busy)
|
||||
fo.busy++
|
||||
continue
|
||||
}
|
||||
|
||||
return f, err
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user