.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / internal / renameio / renameio.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package renameio writes files atomically by renaming temporary files.
6 package renameio
7
8 import (
9         "bytes"
10         "io"
11         "math/rand"
12         "os"
13         "path/filepath"
14         "strconv"
15
16         "honnef.co/go/tools/internal/robustio"
17 )
18
19 const patternSuffix = ".tmp"
20
21 // Pattern returns a glob pattern that matches the unrenamed temporary files
22 // created when writing to filename.
23 func Pattern(filename string) string {
24         return filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix)
25 }
26
27 // WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary
28 // file in the same directory as filename, then renames it atomically to the
29 // final name.
30 //
31 // That ensures that the final location, if it exists, is always a complete file.
32 func WriteFile(filename string, data []byte, perm os.FileMode) (err error) {
33         return WriteToFile(filename, bytes.NewReader(data), perm)
34 }
35
36 // WriteToFile is a variant of WriteFile that accepts the data as an io.Reader
37 // instead of a slice.
38 func WriteToFile(filename string, data io.Reader, perm os.FileMode) (err error) {
39         f, err := tempFile(filepath.Dir(filename), filepath.Base(filename), perm)
40         if err != nil {
41                 return err
42         }
43         defer func() {
44                 // Only call os.Remove on f.Name() if we failed to rename it: otherwise,
45                 // some other process may have created a new file with the same name after
46                 // that.
47                 if err != nil {
48                         f.Close()
49                         os.Remove(f.Name())
50                 }
51         }()
52
53         if _, err := io.Copy(f, data); err != nil {
54                 return err
55         }
56         // Sync the file before renaming it: otherwise, after a crash the reader may
57         // observe a 0-length file instead of the actual contents.
58         // See https://golang.org/issue/22397#issuecomment-380831736.
59         if err := f.Sync(); err != nil {
60                 return err
61         }
62         if err := f.Close(); err != nil {
63                 return err
64         }
65
66         return robustio.Rename(f.Name(), filename)
67 }
68
69 // ReadFile is like ioutil.ReadFile, but on Windows retries spurious errors that
70 // may occur if the file is concurrently replaced.
71 //
72 // Errors are classified heuristically and retries are bounded, so even this
73 // function may occasionally return a spurious error on Windows.
74 // If so, the error will likely wrap one of:
75 //      - syscall.ERROR_ACCESS_DENIED
76 //      - syscall.ERROR_FILE_NOT_FOUND
77 //      - internal/syscall/windows.ERROR_SHARING_VIOLATION
78 func ReadFile(filename string) ([]byte, error) {
79         return robustio.ReadFile(filename)
80 }
81
82 // tempFile creates a new temporary file with given permission bits.
83 func tempFile(dir, prefix string, perm os.FileMode) (f *os.File, err error) {
84         for i := 0; i < 10000; i++ {
85                 name := filepath.Join(dir, prefix+strconv.Itoa(rand.Intn(1000000000))+patternSuffix)
86                 f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm)
87                 if os.IsExist(err) {
88                         continue
89                 }
90                 break
91         }
92         return
93 }