//
// FAI: https://fai-project.org
-// Copyright (C) 2021 Simon Ruderich
+// Copyright (C) 2021-2023 Simon Ruderich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
"io"
"io/fs"
"os"
- "path/filepath"
"strings"
"syscall"
+ "golang.org/x/sys/unix"
+
"ruderich.org/simon/safcm/remote/sync"
)
optionCreate := flag.Bool("create", false,
"create the path if it does not exist")
- flag.CommandLine.Parse(args[2:])
+ flag.CommandLine.Parse(args[2:]) //nolint:errcheck
if flag.NArg() != 2 {
flag.Usage()
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(
gid = int(x.Gid)
mode = stat.Mode()
}
- stat = nil // prevent accidental use
+ stat = nil //nolint:wastedassign // prevent accidental use
// Check if the expected line is present
var found bool
}
// 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) //nolint:errcheck
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