OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package local | 5 package local |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 "io/ioutil" | |
9 "os" | 10 "os" |
10 "path/filepath" | 11 "path/filepath" |
11 "strings" | 12 "strings" |
12 "sync" | 13 "sync" |
13 "time" | 14 "time" |
14 | 15 |
15 "github.com/luci/luci-go/common/logging" | 16 "github.com/luci/luci-go/common/logging" |
16 ) | 17 ) |
17 | 18 |
18 // FileSystem is a high-level interface for operations that touch single file | 19 // FileSystem is a high-level interface for operations that touch single file |
(...skipping 17 matching lines...) Expand all Loading... | |
36 | 37 |
37 // EnsureDirectory creates a directory at given native path if it doesn' t | 38 // EnsureDirectory creates a directory at given native path if it doesn' t |
38 // exist yet. It takes an absolute path or a path relative to the curren t | 39 // exist yet. It takes an absolute path or a path relative to the curren t |
39 // working directory and always returns absolute path. | 40 // working directory and always returns absolute path. |
40 EnsureDirectory(path string) (string, error) | 41 EnsureDirectory(path string) (string, error) |
41 | 42 |
42 // EnsureSymlink creates a symlink pointing to a target. It will create full | 43 // EnsureSymlink creates a symlink pointing to a target. It will create full |
43 // directory path to the symlink if necessary. | 44 // directory path to the symlink if necessary. |
44 EnsureSymlink(path string, target string) error | 45 EnsureSymlink(path string, target string) error |
45 | 46 |
47 // EnsureFile creates a file with given content. If will create full dir ectory | |
48 // path to the file if necessary. | |
49 EnsureFile(path string, body []byte, perm os.FileMode) error | |
50 | |
46 // EnsureFileGone removes a file, logging the errors (if any). Missing f ile is | 51 // EnsureFileGone removes a file, logging the errors (if any). Missing f ile is |
47 // not an error. | 52 // not an error. |
48 EnsureFileGone(path string) error | 53 EnsureFileGone(path string) error |
49 | 54 |
50 // EnsureDirectoryGone recursively removes a directory. | 55 // EnsureDirectoryGone recursively removes a directory. |
51 EnsureDirectoryGone(path string) error | 56 EnsureDirectoryGone(path string) error |
52 | 57 |
53 // Renames oldpath to newpath. If newpath already exists (be it a file o r a | 58 // Renames oldpath to newpath. If newpath already exists (be it a file o r a |
54 // directory), removes it first. If oldpath is a symlink, it's moved as is | 59 // directory), removes it first. If oldpath is a symlink, it's moved as is |
55 // (e.g. as a symlink). | 60 // (e.g. as a symlink). |
(...skipping 15 matching lines...) Expand all Loading... | |
71 return &fsImpl{abs, logger} | 76 return &fsImpl{abs, logger} |
72 } | 77 } |
73 | 78 |
74 // fsImplErr implements FileSystem by returning given error from all methods. | 79 // fsImplErr implements FileSystem by returning given error from all methods. |
75 type fsImplErr struct { | 80 type fsImplErr struct { |
76 err error | 81 err error |
77 } | 82 } |
78 | 83 |
79 // Root returns absolute path to a directory FileSystem operates in. All FS | 84 // Root returns absolute path to a directory FileSystem operates in. All FS |
80 // actions are restricted to this directory. | 85 // actions are restricted to this directory. |
81 func (f *fsImplErr) Root() string { return "" } | 86 func (f *fsImplErr) Root() string { return "" } |
82 func (f *fsImplErr) CwdRelToAbs(path string) (string, error) { return "", f.err } | 87 func (f *fsImplErr) CwdRelToAbs(path string) (string, error) { return "", f.err } |
83 func (f *fsImplErr) RootRelToAbs(path string) (string, error) { return "", f.err } | 88 func (f *fsImplErr) RootRelToAbs(path string) (string, error) { return "", f.err } |
84 func (f *fsImplErr) EnsureDirectory(path string) (string, error) { return "", f.err } | 89 func (f *fsImplErr) EnsureDirectory(path string) (string, error) { return "", f.err } |
85 func (f *fsImplErr) EnsureSymlink(path string, target string) error { return f.e rr } | 90 func (f *fsImplErr) EnsureSymlink(path string, target string) error { return f.err } |
86 func (f *fsImplErr) EnsureFileGone(path string) error { return f.e rr } | 91 func (f *fsImplErr) EnsureFile(path string, body []byte, perm os.FileMode) error { return f.err } |
87 func (f *fsImplErr) EnsureDirectoryGone(path string) error { return f.e rr } | 92 func (f *fsImplErr) EnsureFileGone(path string) error { return f.err } |
88 func (f *fsImplErr) Replace(oldpath, newpath string) error { return f.e rr } | 93 func (f *fsImplErr) EnsureDirectoryGone(path string) error { return f.err } |
94 func (f *fsImplErr) Replace(oldpath, newpath string) error { return f.err } | |
89 | 95 |
90 /// Implementation. | 96 /// Implementation. |
91 | 97 |
92 type fsImpl struct { | 98 type fsImpl struct { |
93 root string | 99 root string |
94 logger logging.Logger | 100 logger logging.Logger |
95 } | 101 } |
96 | 102 |
97 func (f *fsImpl) Root() string { | 103 func (f *fsImpl) Root() string { |
98 return f.root | 104 return f.root |
(...skipping 24 matching lines...) Expand all Loading... | |
123 | 129 |
124 func (f *fsImpl) EnsureDirectory(path string) (string, error) { | 130 func (f *fsImpl) EnsureDirectory(path string) (string, error) { |
125 path, err := f.CwdRelToAbs(path) | 131 path, err := f.CwdRelToAbs(path) |
126 if err != nil { | 132 if err != nil { |
127 return "", err | 133 return "", err |
128 } | 134 } |
129 // MkdirAll returns nil if path already exists. | 135 // MkdirAll returns nil if path already exists. |
130 if err = os.MkdirAll(path, 0777); err != nil { | 136 if err = os.MkdirAll(path, 0777); err != nil { |
131 return "", err | 137 return "", err |
132 } | 138 } |
139 // TODO(vadimsh): Do not fail if path already exists and is a regular fi le? | |
nodir
2015/07/29 20:43:47
probably not, because this func must ensure that a
Vadim Sh.
2015/07/29 21:04:28
I mean if a/b/c exists and is a regular file, Ensu
nodir
2015/07/29 21:08:59
Acknowledged.
| |
133 return path, nil | 140 return path, nil |
134 } | 141 } |
135 | 142 |
143 func (f *fsImpl) EnsureFile(path string, body []byte, perm os.FileMode) error { | |
144 path, err := f.CwdRelToAbs(path) | |
145 if err != nil { | |
146 return err | |
147 } | |
148 if _, err := f.EnsureDirectory(filepath.Dir(path)); err != nil { | |
149 return err | |
150 } | |
151 | |
152 // Create a temp file with new content. | |
153 temp := fmt.Sprintf("%s_%s", path, pseudoRand()) | |
154 if err := ioutil.WriteFile(temp, body, perm); err != nil { | |
155 return err | |
156 } | |
157 | |
158 // Replace the current file (if there's one) with a new one. Use nuclear | |
159 // version (f.Replace) instead of simple atomicReplace to handle various edge | |
160 // cases handled by the nuclear version (e.g replacing a non-empty direc tory). | |
161 if err := f.Replace(temp, path); err != nil { | |
162 if err2 := os.Remove(temp); err2 != nil && !os.IsNotExist(err2) { | |
163 f.logger.Warningf("fs: failed to remove %s - %s", temp, err2) | |
164 } | |
165 return err | |
166 } | |
167 | |
168 return nil | |
169 } | |
170 | |
136 func (f *fsImpl) EnsureSymlink(path string, target string) error { | 171 func (f *fsImpl) EnsureSymlink(path string, target string) error { |
137 path, err := f.CwdRelToAbs(path) | 172 path, err := f.CwdRelToAbs(path) |
138 if err != nil { | 173 if err != nil { |
139 return err | 174 return err |
140 } | 175 } |
141 if existing, _ := os.Readlink(path); existing == target { | 176 if existing, _ := os.Readlink(path); existing == target { |
142 return nil | 177 return nil |
143 } | 178 } |
144 if _, err := f.EnsureDirectory(filepath.Dir(path)); err != nil { | 179 if _, err := f.EnsureDirectory(filepath.Dir(path)); err != nil { |
145 return err | 180 return err |
146 } | 181 } |
147 | 182 |
148 // Create a new symlink file, can't modify existing one in place. | 183 // Create a new symlink file, can't modify existing one in place. |
149 temp := fmt.Sprintf("%s_%s", path, pseudoRand()) | 184 temp := fmt.Sprintf("%s_%s", path, pseudoRand()) |
150 if err := os.Symlink(target, temp); err != nil { | 185 if err := os.Symlink(target, temp); err != nil { |
151 return err | 186 return err |
152 } | 187 } |
153 | 188 |
154 » // Atomically replace the current symlink with a new one. | 189 » // Replace the current symlink with a new one. Use nuclear version (f.Re place) |
155 » if err := os.Rename(temp, path); err != nil { | 190 » // instead of simple atomicReplace to handle various edge cases handled by |
156 » » err2 := os.Remove(temp) | 191 » // the nuclear version (e.g replacing a non-empty directory). |
Vadim Sh.
2015/07/28 01:20:18
it's a prep work for "replace locked files". The c
| |
157 » » if err2 != nil { | 192 » if err := f.Replace(temp, path); err != nil { |
193 » » if err2 := os.Remove(temp); err2 != nil && !os.IsNotExist(err2) { | |
158 f.logger.Warningf("fs: failed to remove %s - %s", temp, err2) | 194 f.logger.Warningf("fs: failed to remove %s - %s", temp, err2) |
159 } | 195 } |
160 return err | 196 return err |
161 } | 197 } |
162 | 198 |
163 return nil | 199 return nil |
164 } | 200 } |
165 | 201 |
166 func (f *fsImpl) EnsureFileGone(path string) error { | 202 func (f *fsImpl) EnsureFileGone(path string) error { |
167 path, err := f.CwdRelToAbs(path) | 203 path, err := f.CwdRelToAbs(path) |
168 if err != nil { | 204 if err != nil { |
169 return err | 205 return err |
170 } | 206 } |
171 » err = os.Remove(path) | 207 » if err = os.Remove(path); err != nil && !os.IsNotExist(err) { |
172 » if err != nil && !os.IsNotExist(err) { | |
173 f.logger.Warningf("fs: failed to remove %s - %s", path, err) | 208 f.logger.Warningf("fs: failed to remove %s - %s", path, err) |
174 return err | 209 return err |
175 } | 210 } |
176 return nil | 211 return nil |
177 } | 212 } |
178 | 213 |
179 func (f *fsImpl) EnsureDirectoryGone(path string) error { | 214 func (f *fsImpl) EnsureDirectoryGone(path string) error { |
180 path, err := f.CwdRelToAbs(path) | 215 path, err := f.CwdRelToAbs(path) |
181 if err != nil { | 216 if err != nil { |
182 return err | 217 return err |
183 } | 218 } |
184 // Make directory "disappear" instantly by renaming it first. | 219 // Make directory "disappear" instantly by renaming it first. |
185 temp := fmt.Sprintf("%s_%v", path, pseudoRand()) | 220 temp := fmt.Sprintf("%s_%v", path, pseudoRand()) |
186 » if err = os.Rename(path, temp); err != nil { | 221 » if err = atomicRename(path, temp); err != nil { |
187 if os.IsNotExist(err) { | 222 if os.IsNotExist(err) { |
188 return nil | 223 return nil |
189 } | 224 } |
190 f.logger.Warningf("fs: failed to rename directory %s - %s", path , err) | 225 f.logger.Warningf("fs: failed to rename directory %s - %s", path , err) |
191 return err | 226 return err |
192 } | 227 } |
193 if err = os.RemoveAll(temp); err != nil { | 228 if err = os.RemoveAll(temp); err != nil { |
194 f.logger.Warningf("fs: failed to remove directory %s - %s", temp , err) | 229 f.logger.Warningf("fs: failed to remove directory %s - %s", temp , err) |
195 return err | 230 return err |
196 } | 231 } |
(...skipping 17 matching lines...) Expand all Loading... | |
214 if _, err = os.Lstat(oldpath); err != nil { | 249 if _, err = os.Lstat(oldpath); err != nil { |
215 return err | 250 return err |
216 } | 251 } |
217 | 252 |
218 // Make parent directory of newpath. | 253 // Make parent directory of newpath. |
219 if _, err = f.EnsureDirectory(filepath.Dir(newpath)); err != nil { | 254 if _, err = f.EnsureDirectory(filepath.Dir(newpath)); err != nil { |
220 return err | 255 return err |
221 } | 256 } |
222 | 257 |
223 // Try a regular move first. Replaces files and empty directories. | 258 // Try a regular move first. Replaces files and empty directories. |
224 » if err = os.Rename(oldpath, newpath); err == nil { | 259 » if err = atomicRename(oldpath, newpath); err == nil { |
225 return nil | 260 return nil |
226 } | 261 } |
227 | 262 |
228 // Move existing path away, if it is there. | 263 // Move existing path away, if it is there. |
229 temp := fmt.Sprintf("%s_%s", newpath, pseudoRand()) | 264 temp := fmt.Sprintf("%s_%s", newpath, pseudoRand()) |
230 » if err = os.Rename(newpath, temp); err != nil { | 265 » if err = atomicRename(newpath, temp); err != nil { |
231 if !os.IsNotExist(err) { | 266 if !os.IsNotExist(err) { |
232 f.logger.Warningf("fs: failed to rename(%v, %v) - %s", n ewpath, temp, err) | 267 f.logger.Warningf("fs: failed to rename(%v, %v) - %s", n ewpath, temp, err) |
233 return err | 268 return err |
234 } | 269 } |
235 temp = "" | 270 temp = "" |
236 } | 271 } |
237 | 272 |
238 // 'newpath' now should be available. | 273 // 'newpath' now should be available. |
239 » if err := os.Rename(oldpath, newpath); err != nil { | 274 » if err := atomicRename(oldpath, newpath); err != nil { |
240 f.logger.Warningf("fs: failed to rename(%v, %v) - %s", oldpath, newpath, err) | 275 f.logger.Warningf("fs: failed to rename(%v, %v) - %s", oldpath, newpath, err) |
241 // Try to return the path back... May be too late already. | 276 // Try to return the path back... May be too late already. |
242 if temp != "" { | 277 if temp != "" { |
243 » » » if err := os.Rename(temp, newpath); err != nil { | 278 » » » if err := atomicRename(temp, newpath); err != nil { |
244 f.logger.Errorf("fs: failed to rename(%v, %v) af ter unsuccessful move - %s", temp, newpath, err) | 279 f.logger.Errorf("fs: failed to rename(%v, %v) af ter unsuccessful move - %s", temp, newpath, err) |
245 } | 280 } |
246 } | 281 } |
247 return err | 282 return err |
248 } | 283 } |
249 | 284 |
250 // Cleanup the garbage left. Not a error if fails. | 285 // Cleanup the garbage left. Not a error if fails. |
251 if temp != "" { | 286 if temp != "" { |
252 if err := f.EnsureDirectoryGone(temp); err != nil { | 287 if err := f.EnsureDirectoryGone(temp); err != nil { |
253 f.logger.Warningf("fs: failed to cleanup garbage after f ile replace - %s", err) | 288 f.logger.Warningf("fs: failed to cleanup garbage after f ile replace - %s", err) |
(...skipping 14 matching lines...) Expand all Loading... | |
268 func pseudoRand() string { | 303 func pseudoRand() string { |
269 ts := time.Now().UnixNano() | 304 ts := time.Now().UnixNano() |
270 lastUsedTimeLock.Lock() | 305 lastUsedTimeLock.Lock() |
271 if ts <= lastUsedTime { | 306 if ts <= lastUsedTime { |
272 ts = lastUsedTime + 1 | 307 ts = lastUsedTime + 1 |
273 } | 308 } |
274 lastUsedTime = ts | 309 lastUsedTime = ts |
275 lastUsedTimeLock.Unlock() | 310 lastUsedTimeLock.Unlock() |
276 return fmt.Sprintf("%v_%v", os.Getpid(), ts) | 311 return fmt.Sprintf("%v_%v", os.Getpid(), ts) |
277 } | 312 } |
OLD | NEW |