Chromium Code Reviews| 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 |