aboutsummaryrefslogtreecommitdiffstats
path: root/modules/storage/local.go
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2021-03-05 13:19:17 +0000
committerGitHub <noreply@github.com>2021-03-05 14:19:17 +0100
commit144cfe5720d28da4a3b9a0697234740d88d3b4c3 (patch)
tree6eacc04f6d77b0fba5d0b5c9bb83a5e305156023 /modules/storage/local.go
parent7525450232337a5c7144565ba92e2a7c18082e1d (diff)
downloadgitea-144cfe5720d28da4a3b9a0697234740d88d3b4c3.tar.gz
gitea-144cfe5720d28da4a3b9a0697234740d88d3b4c3.zip
Fix race in local storage (#14888)
LocalStorage should only put completed files in position Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'modules/storage/local.go')
-rw-r--r--modules/storage/local.go48
1 files changed, 38 insertions, 10 deletions
diff --git a/modules/storage/local.go b/modules/storage/local.go
index 93af13ee33..982d2b88c6 100644
--- a/modules/storage/local.go
+++ b/modules/storage/local.go
@@ -7,6 +7,7 @@ package storage
import (
"context"
"io"
+ "io/ioutil"
"net/url"
"os"
"path/filepath"
@@ -24,13 +25,15 @@ const LocalStorageType Type = "local"
// LocalStorageConfig represents the configuration for a local storage
type LocalStorageConfig struct {
- Path string `ini:"PATH"`
+ Path string `ini:"PATH"`
+ TemporaryPath string `ini:"TEMPORARY_PATH"`
}
// LocalStorage represents a local files storage
type LocalStorage struct {
- ctx context.Context
- dir string
+ ctx context.Context
+ dir string
+ tmpdir string
}
// NewLocalStorage returns a local files
@@ -46,9 +49,14 @@ func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
return nil, err
}
+ if config.TemporaryPath == "" {
+ config.TemporaryPath = config.Path + "/tmp"
+ }
+
return &LocalStorage{
- ctx: ctx,
- dir: config.Path,
+ ctx: ctx,
+ dir: config.Path,
+ tmpdir: config.TemporaryPath,
}, nil
}
@@ -64,17 +72,37 @@ func (l *LocalStorage) Save(path string, r io.Reader) (int64, error) {
return 0, err
}
- // always override
- if err := util.Remove(p); err != nil {
+ // Create a temporary file to save to
+ if err := os.MkdirAll(l.tmpdir, os.ModePerm); err != nil {
return 0, err
}
+ tmp, err := ioutil.TempFile(l.tmpdir, "upload-*")
+ if err != nil {
+ return 0, err
+ }
+ tmpRemoved := false
+ defer func() {
+ if !tmpRemoved {
+ _ = util.Remove(tmp.Name())
+ }
+ }()
- f, err := os.Create(p)
+ n, err := io.Copy(tmp, r)
if err != nil {
return 0, err
}
- defer f.Close()
- return io.Copy(f, r)
+
+ 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