"io"
"io/fs"
"os"
- "path/filepath"
"strings"
"syscall"
+ "golang.org/x/sys/unix"
+
"ruderich.org/simon/safcm/remote/sync"
)
line)
}
+ parentFd, baseName, err := sync.OpenParentDirectoryNoSymlinks(path)
+ if err != nil {
+ return nil, err
+ }
+ defer unix.Close(parentFd)
+
var changes []string
var uid, gid int
var mode fs.FileMode
- data, stat, err := readFileNoFollow(path)
+ data, stat, err := readFileAtNoFollow(parentFd, baseName)
if err != nil {
if !os.IsNotExist(err) {
- return nil, err
+ return nil, fmt.Errorf("%q: %v", path, err)
}
if !create {
return nil, fmt.Errorf(
}
// Write via temporary file and rename
- dir := filepath.Dir(path)
- base := filepath.Base(path)
- tmpPath, err := sync.WriteTemp(dir, "."+base, data, uid, gid, mode)
+ tmpBase, err := sync.WriteTempAt(parentFd, "."+baseName,
+ data, uid, gid, mode)
if err != nil {
return nil, err
}
- err = os.Rename(tmpPath, path)
+ err = unix.Renameat(parentFd, tmpBase, parentFd, baseName)
if err != nil {
- os.Remove(tmpPath)
+ unix.Unlinkat(parentFd, tmpBase, 0)
return nil, err
}
- err = sync.SyncPath(dir)
+ err = unix.Fsync(parentFd)
if err != nil {
return nil, err
}
return changes, nil
}
-func readFileNoFollow(path string) ([]byte, fs.FileInfo, error) {
- fh, err := sync.OpenFileNoFollow(path)
+func readFileAtNoFollow(dirfd int, base string) ([]byte, fs.FileInfo, error) {
+ fh, err := sync.OpenAtNoFollow(dirfd, base)
if err != nil {
return nil, nil, err
}
return nil, nil, err
}
if stat.Mode().Type() != 0 /* regular file */ {
- return nil, nil, fmt.Errorf("%q is not a regular file but %s",
- path, stat.Mode().Type())
+ return nil, nil, fmt.Errorf("not a regular file but %s",
+ stat.Mode().Type())
}
x, err := io.ReadAll(fh)
if err != nil {
- return nil, nil, fmt.Errorf("%q: %v", path, err)
+ return nil, nil, err
}
return x, stat, nil