frostfs-node/pkg/local_object_storage/blobstor/fstree/fstree_write_linux.go

90 lines
2.0 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
}