Unverified Commit d5fa746e authored by Fazlul Shahriar's avatar Fazlul Shahriar Committed by GitHub

Add os.Rename wrapper for Plan 9 (#87)

os.Rename documentation says: "OS-specific restrictions may apply when
oldpath and newpath are in different directories." On Unix, this means
we can't rename across devices. On Plan 9 however, the functionality is
even more limited: cross-directory renames are not allowed at all.

Add a wrapper around os.Rename for Plan 9, which will copy the file if
we're renaming across directory. All tests seems to pass.

(Aside: I also had to write this wrapper to get go-git working on Plan 9:
https://github.com/go-git/go-billy/blob/v5.0.0/osfs/os_plan9.go#L27
but I notice few issues with that one.)

Fixes #86
parent 5aadd5ca
......@@ -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
}
......@@ -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
}
......
......@@ -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
......@@ -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
}
......
// +build !plan9
package flatfs
import "os"
var rename = os.Rename
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
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment