use atomicfile for safer writes
for now, allow daemon and client to both hit config
Brian Tiger Chow committed
Jan 13, 2015 at 06:27 UTC
9700d2f94bf3f2ae6e0e031d282fc7ad6068a367
8 files changed
+227
-1
Godeps/Godeps.json
+4
@@ -68,6 +68,10 @@
68
"ImportPath": "github.com/dustin/go-humanize",
69
"Rev": "b198514c204f20799b91c93b6ffd8b26be04c2c9"
70
},
71
+ {
72
+ "ImportPath": "github.com/facebookgo/atomicfile",
73
+ "Rev": "6f117f2e7f224fb03eb5e5fba370eade6e2b90c8"
74
+ },
75
{
76
"ImportPath": "github.com/facebookgo/stack",
77
"Rev": "4da6d991fc3c389efa512151354d643eb5fae4e2"
Godeps/_workspace/src/github.com/facebookgo/atomicfile/.travis.yml
new
+24
@@ -0,0 +1,24 @@
1
+language: go
2
+
3
+go:
4
+ - 1.2
5
+ - 1.3
6
+
7
+matrix:
8
+ fast_finish: true
9
+
10
+before_install:
11
+ - go get -v code.google.com/p/go.tools/cmd/vet
12
+ - go get -v github.com/golang/lint/golint
13
+ - go get -v code.google.com/p/go.tools/cmd/cover
14
+
15
+install:
16
+ - go install -race -v std
17
+ - go get -race -t -v ./...
18
+ - go install -race -v ./...
19
+
20
+script:
21
+ - go vet ./...
22
+ - $HOME/gopath/bin/golint .
23
+ - go test -cpu=2 -race -v ./...
24
+ - go test -cpu=2 -covermode=atomic ./...
Godeps/_workspace/src/github.com/facebookgo/atomicfile/atomicfile.go
new
+54
@@ -0,0 +1,54 @@
1
+// Package atomicfile provides the ability to write a file with an eventual
2
+// rename on Close. This allows for a file to always be in a consistent state
3
+// and never represent an in-progress write.
4
+package atomicfile
5
+
6
+import (
7
+ "io/ioutil"
8
+ "os"
9
+ "path/filepath"
10
+)
11
+
12
+// File behaves like os.File, but does an atomic rename operation at Close.
13
+type File struct {
14
+ *os.File
15
+ path string
16
+}
17
+
18
+// New creates a new temporary file that will replace the file at the given
19
+// path when Closed.
20
+func New(path string, mode os.FileMode) (*File, error) {
21
+ f, err := ioutil.TempFile(filepath.Dir(path), filepath.Base(path))
22
+ if err != nil {
23
+ return nil, err
24
+ }
25
+ if err := os.Chmod(f.Name(), mode); err != nil {
26
+ os.Remove(f.Name())
27
+ return nil, err
28
+ }
29
+ return &File{File: f, path: path}, nil
30
+}
31
+
32
+// Close the file replacing the configured file.
33
+func (f *File) Close() error {
34
+ if err := f.File.Close(); err != nil {
35
+ return err
36
+ }
37
+ if err := os.Rename(f.Name(), f.path); err != nil {
38
+ return err
39
+ }
40
+ return nil
41
+}
42
+
43
+// Abort closes the file and removes it instead of replacing the configured
44
+// file. This is useful if after starting to write to the file you decide you
45
+// don't want it anymore.
46
+func (f *File) Abort() error {
47
+ if err := f.File.Close(); err != nil {
48
+ return err
49
+ }
50
+ if err := os.Remove(f.Name()); err != nil {
51
+ return err
52
+ }
53
+ return nil
54
+}
Godeps/_workspace/src/github.com/facebookgo/atomicfile/atomicfile_test.go
new
+86
@@ -0,0 +1,86 @@
1
+package atomicfile_test
2
+
3
+import (
4
+ "bytes"
5
+ "io/ioutil"
6
+ "os"
7
+ "testing"
8
+
9
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/facebookgo/atomicfile"
10
+)
11
+
12
+func test(t *testing.T, dir, prefix string) {
13
+ t.Parallel()
14
+
15
+ tmpfile, err := ioutil.TempFile(dir, prefix)
16
+ if err != nil {
17
+ t.Fatal(err)
18
+ }
19
+ name := tmpfile.Name()
20
+
21
+ if err := os.Remove(name); err != nil {
22
+ t.Fatal(err)
23
+ }
24
+
25
+ defer os.Remove(name)
26
+ f, err := atomicfile.New(name, os.FileMode(0666))
27
+ if err != nil {
28
+ t.Fatal(err)
29
+ }
30
+ f.Write([]byte("foo"))
31
+ if _, err := os.Stat(name); !os.IsNotExist(err) {
32
+ t.Fatal("did not expect file to exist")
33
+ }
34
+ if err := f.Close(); err != nil {
35
+ t.Fatal(err)
36
+ }
37
+ if _, err := os.Stat(name); err != nil {
38
+ t.Fatalf("expected file to exist: %s", err)
39
+ }
40
+}
41
+
42
+func TestCurrentDir(t *testing.T) {
43
+ cwd, _ := os.Getwd()
44
+ test(t, cwd, "atomicfile-current-dir-")
45
+}
46
+
47
+func TestRootTmpDir(t *testing.T) {
48
+ test(t, "/tmp", "atomicfile-root-tmp-dir-")
49
+}
50
+
51
+func TestDefaultTmpDir(t *testing.T) {
52
+ test(t, "", "atomicfile-default-tmp-dir-")
53
+}
54
+
55
+func TestAbort(t *testing.T) {
56
+ contents := []byte("the answer is 42")
57
+ t.Parallel()
58
+ tmpfile, err := ioutil.TempFile("", "atomicfile-abort-")
59
+ if err != nil {
60
+ t.Fatal(err)
61
+ }
62
+ name := tmpfile.Name()
63
+ if _, err := tmpfile.Write(contents); err != nil {
64
+ t.Fatal(err)
65
+ }
66
+ defer os.Remove(name)
67
+
68
+ f, err := atomicfile.New(name, os.FileMode(0666))
69
+ if err != nil {
70
+ t.Fatal(err)
71
+ }
72
+ f.Write([]byte("foo"))
73
+ if err := f.Abort(); err != nil {
74
+ t.Fatal(err)
75
+ }
76
+ if _, err := os.Stat(name); err != nil {
77
+ t.Fatalf("expected file to exist: %s", err)
78
+ }
79
+ actual, err := ioutil.ReadFile(name)
80
+ if err != nil {
81
+ t.Fatal(err)
82
+ }
83
+ if !bytes.Equal(contents, actual) {
84
+ t.Fatalf(`did not find expected "%s" instead found "%s"`, contents, actual)
85
+ }
86
+}
Godeps/_workspace/src/github.com/facebookgo/atomicfile/license
new
+30
@@ -0,0 +1,30 @@
1
+BSD License
2
+
3
+For atomicfile software
4
+
5
+Copyright (c) 2014, Facebook, Inc. All rights reserved.
6
+
7
+Redistribution and use in source and binary forms, with or without modification,
8
+are permitted provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright notice, this
11
+ list of conditions and the following disclaimer.
12
+
13
+ * Redistributions in binary form must reproduce the above copyright notice,
14
+ this list of conditions and the following disclaimer in the documentation
15
+ and/or other materials provided with the distribution.
16
+
17
+ * Neither the name Facebook nor the names of its contributors may be used to
18
+ endorse or promote products derived from this software without specific
19
+ prior written permission.
20
+
21
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Godeps/_workspace/src/github.com/facebookgo/atomicfile/patents
new
+23
@@ -0,0 +1,23 @@
1
+Additional Grant of Patent Rights
2
+
3
+"Software" means the atomicfile software distributed by Facebook, Inc.
4
+
5
+Facebook hereby grants you a perpetual, worldwide, royalty-free, non-exclusive,
6
+irrevocable (subject to the termination provision below) license under any
7
+rights in any patent claims owned by Facebook, to make, have made, use, sell,
8
+offer to sell, import, and otherwise transfer the Software. For avoidance of
9
+doubt, no license is granted under Facebook’s rights in any patent claims that
10
+are infringed by (i) modifications to the Software made by you or a third party,
11
+or (ii) the Software in combination with any software or other technology
12
+provided by you or a third party.
13
+
14
+The license granted hereunder will terminate, automatically and without notice,
15
+for anyone that makes any claim (including by filing any lawsuit, assertion or
16
+other action) alleging (a) direct, indirect, or contributory infringement or
17
+inducement to infringe any patent: (i) by Facebook or any of its subsidiaries or
18
+affiliates, whether or not such claim is related to the Software, (ii) by any
19
+party if such claim arises in whole or in part from any software, product or
20
+service of Facebook or any of its subsidiaries or affiliates, whether or not
21
+such claim is related to the Software, or (iii) by any party relating to the
22
+Software; or (b) that any right in any patent claim of Facebook is invalid or
23
+unenforceable.
Godeps/_workspace/src/github.com/facebookgo/atomicfile/readme.md
new
+4
@@ -0,0 +1,4 @@
1
+atomicfile [](http://travis-ci.org/facebookgo/atomicfile)
2
+==========
3
+
4
+Documentation: http://godoc.org/github.com/facebookgo/atomicfile
repo/fsrepo/serialize.go
+2
-1
@@ -7,6 +7,7 @@ import (
7
"os"
8
"path/filepath"
9
10
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/facebookgo/atomicfile"
11
"github.com/jbenet/go-ipfs/repo/config"
12
"github.com/jbenet/go-ipfs/util"
13
"github.com/jbenet/go-ipfs/util/debugerror"
@@ -34,7 +35,7 @@ func writeConfigFile(filename string, cfg interface{}) error {
35
return err
36
}
37
37
- f, err := os.Create(filename)
38
+ f, err := atomicfile.New(filename, 0775)
39
if err != nil {
40
return err
41
}