From a368f9987532a68a3d676566141654a81aa8100b Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 8 Nov 2018 14:30:25 +0100 Subject: [PATCH] add WriteFile (#7) --- writefile.go | 38 +++++++++++++++++++++++++++++++ writefile_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 writefile.go create mode 100644 writefile_test.go diff --git a/writefile.go b/writefile.go new file mode 100644 index 0000000..187d2d4 --- /dev/null +++ b/writefile.go @@ -0,0 +1,38 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package renameio + +import "os" + +// WriteFile mirrors ioutil.WriteFile, replacing an existing file with the same +// name atomically. +func WriteFile(filename string, data []byte, perm os.FileMode) error { + t, err := TempFile("", filename) + if err != nil { + return err + } + defer t.Cleanup() + + // Set permissions before writing data, in case the data is sensitive. + if err := t.Chmod(perm); err != nil { + return err + } + + if _, err := t.Write(data); err != nil { + return err + } + + return t.CloseAtomicallyReplace() +} diff --git a/writefile_test.go b/writefile_test.go new file mode 100644 index 0000000..8cdafef --- /dev/null +++ b/writefile_test.go @@ -0,0 +1,57 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows + +package renameio + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestWriteFile(t *testing.T) { + d, err := ioutil.TempDir("", "tempdirtest") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(d) + + filename := filepath.Join(d, "hello.sh") + + wantData := []byte("#!/bin/sh\necho \"Hello World\"\n") + wantPerm := os.FileMode(0755) + if err := WriteFile(filename, wantData, wantPerm); err != nil { + t.Fatal(err) + } + + gotData, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(gotData, wantData) { + t.Errorf("got data %v, want data %v", gotData, wantData) + } + + fi, err := os.Stat(filename) + if err != nil { + t.Fatal(err) + } + if gotPerm := fi.Mode() & os.ModePerm; gotPerm != wantPerm { + t.Errorf("got permissions 0%o, want permissions 0%o", gotPerm, wantPerm) + } +}