...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package atomicfile
21
22 import (
23 "errors"
24 "io"
25 "io/ioutil"
26 "os"
27 "path/filepath"
28 "runtime"
29 )
30
31
32
33
34 type AtomicFile interface {
35 io.Reader
36 io.ReaderAt
37 io.Writer
38 io.WriterAt
39 io.Seeker
40 Truncate(size int64) error
41
42
43
44 io.Closer
45
46 GetFile() *os.File
47
48 Commit() error
49 }
50
51 type atomicFile struct {
52 *os.File
53 name string
54 }
55
56
57
58 func New(name string) (AtomicFile, error) {
59 tempfile, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".tmp")
60 if err != nil {
61 return nil, err
62 }
63 f := &atomicFile{tempfile, name}
64 runtime.SetFinalizer(f, (*atomicFile).Close)
65 return f, nil
66 }
67
68 func (f *atomicFile) GetFile() *os.File {
69 return f.File
70 }
71
72 func (f *atomicFile) Close() error {
73 if f.File == nil {
74 return nil
75 }
76 f.File.Close()
77 os.Remove(f.File.Name())
78 f.File = nil
79 runtime.SetFinalizer(f, nil)
80 return nil
81 }
82
83 func (f *atomicFile) Commit() error {
84 if f.File == nil {
85 return errors.New("file is closed")
86 }
87 f.File.Chmod(0644)
88 f.File.Close()
89
90 if err := os.Remove(f.name); err != nil && !os.IsNotExist(err) {
91 return err
92 }
93 if err := os.Rename(f.File.Name(), f.name); err != nil {
94 return err
95 }
96 f.File = nil
97 runtime.SetFinalizer(f, nil)
98 return nil
99 }
100
View as plain text