fstree: Use O_TMPFILE for temporary files #970
2 changed files with 74 additions and 63 deletions
|
@ -360,69 +360,6 @@ func (t *FSTree) Put(ctx context.Context, prm common.PutPrm) (common.PutRes, err
|
||||||
return common.PutRes{StorageID: []byte{}}, t.writeData(p, prm.RawData)
|
return common.PutRes{StorageID: []byte{}}, t.writeData(p, prm.RawData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FSTree) writeData(p string, data []byte) error {
|
|
||||||
tmpPath := p + "#" + strconv.FormatUint(t.suffix.Add(1), 10)
|
|
||||||
return t.writeAndRename(tmpPath, p, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeAndRename opens tmpPath exclusively, writes data to it and renames it to p.
|
|
||||||
func (t *FSTree) writeAndRename(tmpPath, p string, data []byte) error {
|
|
||||||
if t.fileCounterEnabled {
|
|
||||||
t.fileGuard.Lock(p)
|
|
||||||
defer t.fileGuard.Unlock(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := t.writeFile(tmpPath, data)
|
|
||||||
if err != nil {
|
|
||||||
var pe *fs.PathError
|
|
||||||
if errors.As(err, &pe) {
|
|
||||||
switch pe.Err {
|
|
||||||
case syscall.ENOSPC:
|
|
||||||
err = common.ErrNoSpace
|
|
||||||
_ = os.RemoveAll(tmpPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.fileCounterEnabled {
|
|
||||||
t.fileCounter.Inc()
|
|
||||||
var targetFileExists bool
|
|
||||||
if _, e := os.Stat(p); e == nil {
|
|
||||||
targetFileExists = true
|
|
||||||
}
|
|
||||||
err = os.Rename(tmpPath, p)
|
|
||||||
if err == nil && targetFileExists {
|
|
||||||
t.fileCounter.Dec()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = os.Rename(tmpPath, p)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *FSTree) writeFlags() int {
|
|
||||||
flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC | os.O_EXCL
|
|
||||||
if t.noSync {
|
|
||||||
return flags
|
|
||||||
}
|
|
||||||
return flags | os.O_SYNC
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeFile writes data to a file with path p.
|
|
||||||
// The code is copied from `os.WriteFile` with minor corrections for flags.
|
|
||||||
func (t *FSTree) writeFile(p string, data []byte) error {
|
|
||||||
f, err := os.OpenFile(p, t.writeFlags(), t.Permissions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = f.Write(data)
|
|
||||||
if err1 := f.Close(); err1 != nil && err == nil {
|
|
||||||
err = err1
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns an object from the storage by address.
|
// Get returns an object from the storage by address.
|
||||||
func (t *FSTree) Get(ctx context.Context, prm common.GetPrm) (common.GetRes, error) {
|
func (t *FSTree) Get(ctx context.Context, prm common.GetPrm) (common.GetRes, error) {
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package fstree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *FSTree) writeData(p string, data []byte) error {
|
||||||
|
tmpPath := p + "#" + strconv.FormatUint(t.suffix.Add(1), 10)
|
||||||
|
return t.writeAndRename(tmpPath, p, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeAndRename opens tmpPath exclusively, writes data to it and renames it to p.
|
||||||
|
func (t *FSTree) writeAndRename(tmpPath, p string, data []byte) error {
|
||||||
|
if t.fileCounterEnabled {
|
||||||
|
t.fileGuard.Lock(p)
|
||||||
|
defer t.fileGuard.Unlock(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := t.writeFile(tmpPath, data)
|
||||||
|
if err != nil {
|
||||||
|
var pe *fs.PathError
|
||||||
|
if errors.As(err, &pe) {
|
||||||
|
switch pe.Err {
|
||||||
|
case syscall.ENOSPC:
|
||||||
|
err = common.ErrNoSpace
|
||||||
|
_ = os.RemoveAll(tmpPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.fileCounterEnabled {
|
||||||
|
t.fileCounter.Inc()
|
||||||
|
var targetFileExists bool
|
||||||
|
if _, e := os.Stat(p); e == nil {
|
||||||
|
targetFileExists = true
|
||||||
|
}
|
||||||
|
err = os.Rename(tmpPath, p)
|
||||||
|
if err == nil && targetFileExists {
|
||||||
|
t.fileCounter.Dec()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = os.Rename(tmpPath, p)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *FSTree) writeFlags() int {
|
||||||
|
flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC | os.O_EXCL
|
||||||
|
if t.noSync {
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
return flags | os.O_SYNC
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeFile writes data to a file with path p.
|
||||||
|
// The code is copied from `os.WriteFile` with minor corrections for flags.
|
||||||
|
func (t *FSTree) writeFile(p string, data []byte) error {
|
||||||
|
f, err := os.OpenFile(p, t.writeFlags(), t.Permissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.Write(data)
|
||||||
|
if err1 := f.Close(); err1 != nil && err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
Loading…
Reference in a new issue