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 {
|
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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -400,9 +400,10 @@ func (fs *Filesystem) Writefile(p string, r io.Reader) error {
|
||||||
currentSize = stat.Size()
|
currentSize = stat.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o := &fileOpener{}
|
||||||
// This will either create the file if it does not already exist, or open and
|
// This will either create the file if it does not already exist, or open and
|
||||||
// truncate the existing file.
|
// 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 {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -937,3 +938,29 @@ func (fs *Filesystem) handleWalkerError(err error, f os.FileInfo) error {
|
||||||
|
|
||||||
return nil
|
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