//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 }