forked from TrueCloudLab/frostfs-node
Evgenii Stratonikov
b36a453238
Unless tested, generic version can start gaining bugs. With a separate build tag we can have the best of both worlds: 1. Use optimized implementation for linux by default. 2. Run tests or benchmarks for both. Note that they are not actually run automatically now, but this is at leas possible. Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
89 lines
2 KiB
Go
89 lines
2 KiB
Go
//go:build linux && !fstree_generic
|
|
|
|
package fstree
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"strconv"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
type linuxWriter struct {
|
|
root string
|
|
perm uint32
|
|
flags int
|
|
|
|
counter FileCounter
|
|
}
|
|
|
|
func newSpecificWriteData(c FileCounter, root string, perm fs.FileMode, noSync bool) writer {
|
|
flags := unix.O_WRONLY | unix.O_TMPFILE | unix.O_CLOEXEC
|
|
if !noSync {
|
|
flags |= unix.O_DSYNC
|
|
}
|
|
fd, err := unix.Open(root, flags, uint32(perm))
|
|
if err != nil {
|
|
// Which means that OS-specific writeData can't be created
|
|
// and FSTree should use the generic one.
|
|
return nil
|
|
}
|
|
_ = unix.Close(fd) // Don't care about error.
|
|
w := &linuxWriter{
|
|
root: root,
|
|
perm: uint32(perm),
|
|
flags: flags,
|
|
counter: c,
|
|
}
|
|
return w
|
|
}
|
|
|
|
func (w *linuxWriter) writeData(p string, data []byte) error {
|
|
err := w.writeFile(p, data)
|
|
if errors.Is(err, unix.ENOSPC) {
|
|
return common.ErrNoSpace
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (w *linuxWriter) writeFile(p string, data []byte) error {
|
|
fd, err := unix.Open(w.root, w.flags, w.perm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmpPath := "/proc/self/fd/" + strconv.FormatUint(uint64(fd), 10)
|
|
n, err := unix.Write(fd, data)
|
|
if err == nil {
|
|
if n == len(data) {
|
|
err = unix.Linkat(unix.AT_FDCWD, tmpPath, unix.AT_FDCWD, p, unix.AT_SYMLINK_FOLLOW)
|
|
if err == nil {
|
|
w.counter.Inc()
|
|
}
|
|
if errors.Is(err, unix.EEXIST) {
|
|
err = nil
|
|
}
|
|
} else {
|
|
err = errors.New("incomplete write")
|
|
}
|
|
}
|
|
errClose := unix.Close(fd)
|
|
if err != nil {
|
|
return err // Close() error is ignored, we have a better one.
|
|
}
|
|
return errClose
|
|
}
|
|
|
|
func (w *linuxWriter) removeFile(p string) error {
|
|
err := unix.Unlink(p)
|
|
if err != nil && err == unix.ENOENT {
|
|
return logicerr.Wrap(new(apistatus.ObjectNotFound))
|
|
}
|
|
if err == nil {
|
|
w.counter.Dec()
|
|
}
|
|
return err
|
|
}
|