mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-07 05:30:11 +02:00
Merge pull request #8914 from z017/master
fn: add synchronous write file
This commit is contained in:
commit
729cd22bf6
45
fn/io.go
Normal file
45
fn/io.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package fn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteFile synchronously writes data to the named file.
|
||||||
|
// If the file does not exist, WriteFile creates it with permissions perm
|
||||||
|
// (before umask); otherwise WriteFile truncates it before writing, without
|
||||||
|
// changing permissions.
|
||||||
|
// By opening the file with O_SYNC, it ensures the data is written to disk.
|
||||||
|
// If an error occurs, it does not remove the file.
|
||||||
|
func WriteFile(name string, data []byte, perm os.FileMode) error {
|
||||||
|
f, err := os.OpenFile(
|
||||||
|
name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC|os.O_SYNC, perm,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Write(data)
|
||||||
|
|
||||||
|
// Prioritize the error on Write but make sure to call Close regardless
|
||||||
|
// to avoid leaking a file handle.
|
||||||
|
if err1 := f.Close(); err1 != nil && err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFileRemove synchronously writes data to the named file.
|
||||||
|
// If the file does not exist, WriteFileRemove creates it with permissions perm
|
||||||
|
// (before umask); otherwise WriteFileRemove truncates it before writing,
|
||||||
|
// without changing permissions.
|
||||||
|
// By opening the file with O_SYNC, it ensures the data is written to disk.
|
||||||
|
// If an error occurs, it removes the file.
|
||||||
|
func WriteFileRemove(name string, data []byte, perm os.FileMode) error {
|
||||||
|
err := WriteFile(name, data, perm)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.Remove(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
96
fn/io_test.go
Normal file
96
fn/io_test.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package fn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
data1 = "The network is robust in its unstructured simplicity."
|
||||||
|
data2 = "Nodes work all at once with little coordination."
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestWriteFile uses same scenario of ioutil asserting the content created and
|
||||||
|
// stored on new file with the original one.
|
||||||
|
func TestWriteFile(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f, deferred := ensureTempfile(t)
|
||||||
|
filename := f.Name()
|
||||||
|
defer deferred()
|
||||||
|
|
||||||
|
// Write file.
|
||||||
|
err := WriteFile(filename, []byte(data2), 0644)
|
||||||
|
require.NoError(t, err, "couldn't write file")
|
||||||
|
ensureFileContents(t, filename, data2)
|
||||||
|
|
||||||
|
// Overwrite file.
|
||||||
|
err = WriteFile(filename, []byte(data1), 0644)
|
||||||
|
require.NoError(t, err, "couldn't overwrite file")
|
||||||
|
ensureFileContents(t, filename, data1)
|
||||||
|
|
||||||
|
// Change file permission to read-only.
|
||||||
|
err = os.Chmod(filename, 0444)
|
||||||
|
require.NoError(t, err, "couldn't change file to read-only")
|
||||||
|
|
||||||
|
// Write must fail and keep the file.
|
||||||
|
err = WriteFile(filename, []byte(data2), 0644)
|
||||||
|
require.Error(t, err, "shouldn't write a read-only file")
|
||||||
|
|
||||||
|
_, err = os.Stat(filename)
|
||||||
|
require.NoError(t, err, "shouldn't remove file on error")
|
||||||
|
ensureFileContents(t, filename, data1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteFileRemove(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f, deferred := ensureTempfile(t)
|
||||||
|
filename := f.Name()
|
||||||
|
defer deferred()
|
||||||
|
|
||||||
|
// Write file.
|
||||||
|
err := WriteFileRemove(filename, []byte(data2), 0644)
|
||||||
|
require.NoError(t, err, "couldn't write file")
|
||||||
|
ensureFileContents(t, filename, data2)
|
||||||
|
|
||||||
|
// Overwrite file.
|
||||||
|
err = WriteFileRemove(filename, []byte(data1), 0644)
|
||||||
|
require.NoError(t, err, "couldn't overwrite file")
|
||||||
|
ensureFileContents(t, filename, data1)
|
||||||
|
|
||||||
|
// Change file permission to read-only.
|
||||||
|
err = os.Chmod(filename, 0444)
|
||||||
|
require.NoError(t, err, "couldn't change file to read-only")
|
||||||
|
|
||||||
|
// Write must fail and remove the file.
|
||||||
|
err = WriteFileRemove(filename, []byte(data2), 0644)
|
||||||
|
require.Error(t, err, "shouldn't write a read-only file")
|
||||||
|
|
||||||
|
_, err = os.Stat(filename)
|
||||||
|
require.ErrorContains(
|
||||||
|
t, err, "no such file or directory",
|
||||||
|
"should remove file on error",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureTempfile(t *testing.T) (*os.File, func()) {
|
||||||
|
t.Helper()
|
||||||
|
f, err := os.CreateTemp("", "fn-io-test-TestWriteFile")
|
||||||
|
require.NoError(t, err, "couldn't create temporary file")
|
||||||
|
|
||||||
|
return f, func() {
|
||||||
|
f.Close()
|
||||||
|
os.Remove(f.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureFileContents(t *testing.T, filename string, data string) {
|
||||||
|
t.Helper()
|
||||||
|
contents, err := os.ReadFile(filename)
|
||||||
|
require.NoError(t, err, "couldn't read file")
|
||||||
|
|
||||||
|
require.Equal(t, data, string(contents))
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user