Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add os.Rename wrapper for Plan 9 #87

Merged
merged 1 commit into from
Jul 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func Move(oldPath string, newPath string, out io.Writer) error {
// else we found something unexpected, so to be safe just move it
log.Warnw("found unexpected file in datastore directory, moving anyways", "file", fn)
newPath := filepath.Join(newDS.path, fn)
err := os.Rename(oldPath, newPath)
err := rename(oldPath, newPath)
if err != nil {
return err
}
Expand All @@ -177,7 +177,7 @@ func moveKey(oldDS *Datastore, newDS *Datastore, key datastore.Key) error {
if err != nil && !os.IsExist(err) {
return err
}
err = os.Rename(oldPath, newPath)
err = rename(oldPath, newPath)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions flatfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ func (fs *Datastore) renameAndUpdateDiskUsage(tmpPath, path string) error {
// it will either a) Re-add the size of an existing file, which
// was sustracted before b) Add 0 if there is no existing file.
for i := 0; i < RetryAttempts; i++ {
err = os.Rename(tmpPath, path)
err = rename(tmpPath, path)
// if there's no error, or the source file doesn't exist, abort.
if err == nil || os.IsNotExist(err) {
break
Expand Down Expand Up @@ -1060,7 +1060,7 @@ func (fs *Datastore) writeDiskUsageFile(du int64, doSync bool) {
}
closed = true

if err := os.Rename(tmp.Name(), filepath.Join(fs.path, DiskUsageFile)); err != nil {
if err := rename(tmp.Name(), filepath.Join(fs.path, DiskUsageFile)); err != nil {
log.Warnw("cound not write disk usage", "error", err)
return
}
Expand Down
7 changes: 7 additions & 0 deletions rename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// +build !plan9

package flatfs

import "os"

var rename = os.Rename
77 changes: 77 additions & 0 deletions rename_plan9.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package flatfs

import (
"io"
"os"
"path/filepath"
"syscall"
)

// rename behaves like os.Rename but can rename files across directories.
func rename(oldpath, newpath string) error {
err := os.Rename(oldpath, newpath)
if le, ok := err.(*os.LinkError); !ok || le.Err != os.ErrInvalid {
return err
}
if filepath.Dir(oldpath) == filepath.Dir(newpath) {
// We should not get here, but just in case
// os.ErrInvalid is used for something else in the future.
return err
}

src, err := os.Open(oldpath)
if err != nil {
return &os.LinkError{"rename", oldpath, newpath, err}
}
defer src.Close()

fi, err := src.Stat()
if err != nil {
return &os.LinkError{"rename", oldpath, newpath, err}
}
if fi.Mode().IsDir() {
return &os.LinkError{"rename", oldpath, newpath, syscall.EISDIR}
}

dst, err := os.OpenFile(newpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
if err != nil {
return &os.LinkError{"rename", oldpath, newpath, err}
}

if _, err := io.Copy(dst, src); err != nil {
dst.Close()
os.Remove(newpath)
return &os.LinkError{"rename", oldpath, newpath, err}
}
if err := dst.Close(); err != nil {
os.Remove(newpath)
return &os.LinkError{"rename", oldpath, newpath, err}
}

// Copy mtime and mode from original file.
// We need only one syscall if we avoid os.Chmod and os.Chtimes.
dir := fi.Sys().(*syscall.Dir)
var d syscall.Dir
d.Null()
d.Mtime = dir.Mtime
d.Mode = dir.Mode
_ = dirwstat(newpath, &d) // ignore error, as per mv(1)

if err := os.Remove(oldpath); err != nil {
return &os.LinkError{"rename", oldpath, newpath, err}
}
return nil
}

func dirwstat(name string, d *syscall.Dir) error {
var buf [syscall.STATFIXLEN]byte

n, err := d.Marshal(buf[:])
if err != nil {
return &os.PathError{"dirwstat", name, err}
}
if err = syscall.Wstat(name, buf[:n]); err != nil {
return &os.PathError{"dirwstat", name, err}
}
return nil
}