Set permission on atomic file write

Perform chmod before rename with the atomic file writer.
Ensure writeErr is set on short write and file is removed on write error.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
Derek McGowan 2016-06-29 13:09:13 -07:00
parent 754ae20ec5
commit 003449018b
2 changed files with 19 additions and 4 deletions

View file

@ -15,13 +15,15 @@ func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, err
if err != nil { if err != nil {
return nil, err return nil, err
} }
abspath, err := filepath.Abs(filename) abspath, err := filepath.Abs(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &atomicFileWriter{ return &atomicFileWriter{
f: f, f: f,
fn: abspath, fn: abspath,
perm: perm,
}, nil }, nil
} }
@ -34,6 +36,7 @@ func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
n, err := f.Write(data) n, err := f.Write(data)
if err == nil && n < len(data) { if err == nil && n < len(data) {
err = io.ErrShortWrite err = io.ErrShortWrite
f.(*atomicFileWriter).writeErr = err
} }
if err1 := f.Close(); err == nil { if err1 := f.Close(); err == nil {
err = err1 err = err1
@ -45,6 +48,7 @@ type atomicFileWriter struct {
f *os.File f *os.File
fn string fn string
writeErr error writeErr error
perm os.FileMode
} }
func (w *atomicFileWriter) Write(dt []byte) (int, error) { func (w *atomicFileWriter) Write(dt []byte) (int, error) {
@ -57,7 +61,7 @@ func (w *atomicFileWriter) Write(dt []byte) (int, error) {
func (w *atomicFileWriter) Close() (retErr error) { func (w *atomicFileWriter) Close() (retErr error) {
defer func() { defer func() {
if retErr != nil { if retErr != nil || w.writeErr != nil {
os.Remove(w.f.Name()) os.Remove(w.f.Name())
} }
}() }()
@ -68,6 +72,9 @@ func (w *atomicFileWriter) Close() (retErr error) {
if err := w.f.Close(); err != nil { if err := w.f.Close(); err != nil {
return err return err
} }
if err := os.Chmod(w.f.Name(), w.perm); err != nil {
return err
}
if w.writeErr == nil { if w.writeErr == nil {
return os.Rename(w.f.Name(), w.fn) return os.Rename(w.f.Name(), w.fn)
} }

View file

@ -16,7 +16,7 @@ func TestAtomicWriteToFile(t *testing.T) {
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
expected := []byte("barbaz") expected := []byte("barbaz")
if err := AtomicWriteFile(filepath.Join(tmpDir, "foo"), expected, 0600); err != nil { if err := AtomicWriteFile(filepath.Join(tmpDir, "foo"), expected, 0666); err != nil {
t.Fatalf("Error writing to file: %v", err) t.Fatalf("Error writing to file: %v", err)
} }
@ -28,4 +28,12 @@ func TestAtomicWriteToFile(t *testing.T) {
if bytes.Compare(actual, expected) != 0 { if bytes.Compare(actual, expected) != 0 {
t.Fatalf("Data mismatch, expected %q, got %q", expected, actual) t.Fatalf("Data mismatch, expected %q, got %q", expected, actual)
} }
st, err := os.Stat(filepath.Join(tmpDir, "foo"))
if err != nil {
t.Fatalf("Error statting file: %v", err)
}
if expected := os.FileMode(0666); st.Mode() != expected {
t.Fatalf("Mode mismatched, expected %o, got %o", expected, st.Mode())
}
} }