Chromium Code Reviews| Index: go/src/infra/tools/cipd/local/fs.go |
| diff --git a/go/src/infra/tools/cipd/local/fs.go b/go/src/infra/tools/cipd/local/fs.go |
| index 69c1bc2660e4c37e47cba96c294b4fc8e3d51b54..803dab86560ef849d4ac0f1db67b456986eba7d8 100644 |
| --- a/go/src/infra/tools/cipd/local/fs.go |
| +++ b/go/src/infra/tools/cipd/local/fs.go |
| @@ -6,6 +6,7 @@ package local |
| import ( |
| "fmt" |
| + "io/ioutil" |
| "os" |
| "path/filepath" |
| "strings" |
| @@ -43,6 +44,10 @@ type FileSystem interface { |
| // directory path to the symlink if necessary. |
| EnsureSymlink(path string, target string) error |
| + // EnsureFile creates a file with given content. If will create full directory |
| + // path to the file if necessary. |
| + EnsureFile(path string, body []byte, perm os.FileMode) error |
| + |
| // EnsureFileGone removes a file, logging the errors (if any). Missing file is |
| // not an error. |
| EnsureFileGone(path string) error |
| @@ -78,14 +83,15 @@ type fsImplErr struct { |
| // Root returns absolute path to a directory FileSystem operates in. All FS |
| // actions are restricted to this directory. |
| -func (f *fsImplErr) Root() string { return "" } |
| -func (f *fsImplErr) CwdRelToAbs(path string) (string, error) { return "", f.err } |
| -func (f *fsImplErr) RootRelToAbs(path string) (string, error) { return "", f.err } |
| -func (f *fsImplErr) EnsureDirectory(path string) (string, error) { return "", f.err } |
| -func (f *fsImplErr) EnsureSymlink(path string, target string) error { return f.err } |
| -func (f *fsImplErr) EnsureFileGone(path string) error { return f.err } |
| -func (f *fsImplErr) EnsureDirectoryGone(path string) error { return f.err } |
| -func (f *fsImplErr) Replace(oldpath, newpath string) error { return f.err } |
| +func (f *fsImplErr) Root() string { return "" } |
| +func (f *fsImplErr) CwdRelToAbs(path string) (string, error) { return "", f.err } |
| +func (f *fsImplErr) RootRelToAbs(path string) (string, error) { return "", f.err } |
| +func (f *fsImplErr) EnsureDirectory(path string) (string, error) { return "", f.err } |
| +func (f *fsImplErr) EnsureSymlink(path string, target string) error { return f.err } |
| +func (f *fsImplErr) EnsureFile(path string, body []byte, perm os.FileMode) error { return f.err } |
| +func (f *fsImplErr) EnsureFileGone(path string) error { return f.err } |
| +func (f *fsImplErr) EnsureDirectoryGone(path string) error { return f.err } |
| +func (f *fsImplErr) Replace(oldpath, newpath string) error { return f.err } |
| /// Implementation. |
| @@ -130,9 +136,38 @@ func (f *fsImpl) EnsureDirectory(path string) (string, error) { |
| if err = os.MkdirAll(path, 0777); err != nil { |
| return "", err |
| } |
| + // TODO(vadimsh): Do not fail if path already exists and is a regular file? |
|
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.
|
| return path, nil |
| } |
| +func (f *fsImpl) EnsureFile(path string, body []byte, perm os.FileMode) error { |
| + path, err := f.CwdRelToAbs(path) |
| + if err != nil { |
| + return err |
| + } |
| + if _, err := f.EnsureDirectory(filepath.Dir(path)); err != nil { |
| + return err |
| + } |
| + |
| + // Create a temp file with new content. |
| + temp := fmt.Sprintf("%s_%s", path, pseudoRand()) |
| + if err := ioutil.WriteFile(temp, body, perm); err != nil { |
| + return err |
| + } |
| + |
| + // Replace the current file (if there's one) with a new one. Use nuclear |
| + // version (f.Replace) instead of simple atomicReplace to handle various edge |
| + // cases handled by the nuclear version (e.g replacing a non-empty directory). |
| + if err := f.Replace(temp, path); err != nil { |
| + if err2 := os.Remove(temp); err2 != nil && !os.IsNotExist(err2) { |
| + f.logger.Warningf("fs: failed to remove %s - %s", temp, err2) |
| + } |
| + return err |
| + } |
| + |
| + return nil |
| +} |
| + |
| func (f *fsImpl) EnsureSymlink(path string, target string) error { |
| path, err := f.CwdRelToAbs(path) |
| if err != nil { |
| @@ -151,10 +186,11 @@ func (f *fsImpl) EnsureSymlink(path string, target string) error { |
| return err |
| } |
| - // Atomically replace the current symlink with a new one. |
| - if err := os.Rename(temp, path); err != nil { |
| - err2 := os.Remove(temp) |
| - if err2 != nil { |
| + // Replace the current symlink with a new one. Use nuclear version (f.Replace) |
| + // instead of simple atomicReplace to handle various edge cases handled by |
| + // 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
|
| + if err := f.Replace(temp, path); err != nil { |
| + if err2 := os.Remove(temp); err2 != nil && !os.IsNotExist(err2) { |
| f.logger.Warningf("fs: failed to remove %s - %s", temp, err2) |
| } |
| return err |
| @@ -168,8 +204,7 @@ func (f *fsImpl) EnsureFileGone(path string) error { |
| if err != nil { |
| return err |
| } |
| - err = os.Remove(path) |
| - if err != nil && !os.IsNotExist(err) { |
| + if err = os.Remove(path); err != nil && !os.IsNotExist(err) { |
| f.logger.Warningf("fs: failed to remove %s - %s", path, err) |
| return err |
| } |
| @@ -183,7 +218,7 @@ func (f *fsImpl) EnsureDirectoryGone(path string) error { |
| } |
| // Make directory "disappear" instantly by renaming it first. |
| temp := fmt.Sprintf("%s_%v", path, pseudoRand()) |
| - if err = os.Rename(path, temp); err != nil { |
| + if err = atomicRename(path, temp); err != nil { |
| if os.IsNotExist(err) { |
| return nil |
| } |
| @@ -221,13 +256,13 @@ func (f *fsImpl) Replace(oldpath, newpath string) error { |
| } |
| // Try a regular move first. Replaces files and empty directories. |
| - if err = os.Rename(oldpath, newpath); err == nil { |
| + if err = atomicRename(oldpath, newpath); err == nil { |
| return nil |
| } |
| // Move existing path away, if it is there. |
| temp := fmt.Sprintf("%s_%s", newpath, pseudoRand()) |
| - if err = os.Rename(newpath, temp); err != nil { |
| + if err = atomicRename(newpath, temp); err != nil { |
| if !os.IsNotExist(err) { |
| f.logger.Warningf("fs: failed to rename(%v, %v) - %s", newpath, temp, err) |
| return err |
| @@ -236,11 +271,11 @@ func (f *fsImpl) Replace(oldpath, newpath string) error { |
| } |
| // 'newpath' now should be available. |
| - if err := os.Rename(oldpath, newpath); err != nil { |
| + if err := atomicRename(oldpath, newpath); err != nil { |
| f.logger.Warningf("fs: failed to rename(%v, %v) - %s", oldpath, newpath, err) |
| // Try to return the path back... May be too late already. |
| if temp != "" { |
| - if err := os.Rename(temp, newpath); err != nil { |
| + if err := atomicRename(temp, newpath); err != nil { |
| f.logger.Errorf("fs: failed to rename(%v, %v) after unsuccessful move - %s", temp, newpath, err) |
| } |
| } |