Fix race in local storage (#14888) (#14901)

LocalStorage should only put completed files in position

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
6543 2021-03-06 05:07:03 +01:00 committed by GitHub
parent 74dc22358b
commit da80e90ac8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -7,6 +7,7 @@ package storage
import ( import (
"context" "context"
"io" "io"
"io/ioutil"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
@ -25,12 +26,14 @@ const LocalStorageType Type = "local"
// LocalStorageConfig represents the configuration for a local storage // LocalStorageConfig represents the configuration for a local storage
type LocalStorageConfig struct { type LocalStorageConfig struct {
Path string `ini:"PATH"` Path string `ini:"PATH"`
TemporaryPath string `ini:"TEMPORARY_PATH"`
} }
// LocalStorage represents a local files storage // LocalStorage represents a local files storage
type LocalStorage struct { type LocalStorage struct {
ctx context.Context ctx context.Context
dir string dir string
tmpdir string
} }
// NewLocalStorage returns a local files // NewLocalStorage returns a local files
@ -45,9 +48,14 @@ func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
return nil, err return nil, err
} }
if config.TemporaryPath == "" {
config.TemporaryPath = config.Path + "/tmp"
}
return &LocalStorage{ return &LocalStorage{
ctx: ctx, ctx: ctx,
dir: config.Path, dir: config.Path,
tmpdir: config.TemporaryPath,
}, nil }, nil
} }
@ -63,17 +71,37 @@ func (l *LocalStorage) Save(path string, r io.Reader) (int64, error) {
return 0, err return 0, err
} }
// always override // Create a temporary file to save to
if err := util.Remove(p); err != nil { if err := os.MkdirAll(l.tmpdir, os.ModePerm); err != nil {
return 0, err return 0, err
} }
tmp, err := ioutil.TempFile(l.tmpdir, "upload-*")
f, err := os.Create(p)
if err != nil { if err != nil {
return 0, err return 0, err
} }
defer f.Close() tmpRemoved := false
return io.Copy(f, r) defer func() {
if !tmpRemoved {
_ = util.Remove(tmp.Name())
}
}()
n, err := io.Copy(tmp, r)
if err != nil {
return 0, err
}
if err := tmp.Close(); err != nil {
return 0, err
}
if err := os.Rename(tmp.Name(), p); err != nil {
return 0, err
}
tmpRemoved = true
return n, nil
} }
// Stat returns the info of the file // Stat returns the info of the file