mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
This patch adds a missing docstrings for exported identifiers, and adjust some of the existing ones to match the standard style. In some cases, the identifiers were un-exported after noticing they had no external users. Besides improving documentation, it also reduces the linter noise significantly.
84 lines
2.0 KiB
Go
84 lines
2.0 KiB
Go
// Package safeio implements convenient I/O routines that provide additional
|
|
// levels of safety in the presence of unexpected failures.
|
|
package safeio
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"syscall"
|
|
)
|
|
|
|
// FileOp represents an operation on a file (passed by its name).
|
|
type FileOp func(fname string) error
|
|
|
|
// WriteFile writes data to a file named by filename, atomically.
|
|
//
|
|
// It's a wrapper to ioutil.WriteFile, but provides atomicity (and increased
|
|
// safety) by writing to a temporary file and renaming it at the end.
|
|
//
|
|
// Before the final rename, the given ops (if any) are called. They can be
|
|
// used to manipulate the file before it is atomically renamed.
|
|
// If any operation fails, the file is removed and the error is returned.
|
|
//
|
|
// Note this relies on same-directory Rename being atomic, which holds in most
|
|
// reasonably modern filesystems.
|
|
func WriteFile(filename string, data []byte, perm os.FileMode, ops ...FileOp) error {
|
|
// Note we create the temporary file in the same directory, otherwise we
|
|
// would have no expectation of Rename being atomic.
|
|
// We make the file names start with "." so there's no confusion with the
|
|
// originals.
|
|
tmpf, err := ioutil.TempFile(path.Dir(filename), "."+path.Base(filename))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = tmpf.Chmod(perm); err != nil {
|
|
tmpf.Close()
|
|
os.Remove(tmpf.Name())
|
|
return err
|
|
}
|
|
|
|
if uid, gid := getOwner(filename); uid >= 0 {
|
|
if err = tmpf.Chown(uid, gid); err != nil {
|
|
tmpf.Close()
|
|
os.Remove(tmpf.Name())
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, err = tmpf.Write(data); err != nil {
|
|
tmpf.Close()
|
|
os.Remove(tmpf.Name())
|
|
return err
|
|
}
|
|
|
|
if err = tmpf.Close(); err != nil {
|
|
os.Remove(tmpf.Name())
|
|
return err
|
|
}
|
|
|
|
for _, op := range ops {
|
|
if err = op(tmpf.Name()); err != nil {
|
|
os.Remove(tmpf.Name())
|
|
return err
|
|
}
|
|
}
|
|
|
|
return os.Rename(tmpf.Name(), filename)
|
|
}
|
|
|
|
func getOwner(fname string) (uid, gid int) {
|
|
uid = -1
|
|
gid = -1
|
|
stat, err := os.Stat(fname)
|
|
if err == nil {
|
|
if sysstat, ok := stat.Sys().(*syscall.Stat_t); ok {
|
|
uid = int(sysstat.Uid)
|
|
gid = int(sysstat.Gid)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|