Skip to content

Instantly share code, notes, and snippets.

@var23rav
Last active October 21, 2023 15:34
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save var23rav/23ae5d0d4d830aff886c3c970b8f6c6b to your computer and use it in GitHub Desktop.
Save var23rav/23ae5d0d4d830aff886c3c970b8f6c6b to your computer and use it in GitHub Desktop.
GoLang: os.Rename() give error "invalid cross-device link" for Docker container with Volumes. MoveFile(source, destination) will work moving file between folders
import (
"fmt"
"io"
"os"
)
/*
GoLang: os.Rename() give error "invalid cross-device link" for Docker container with Volumes.
MoveFile(source, destination) will work moving file between folders
*/
func MoveFile(sourcePath, destPath string) error {
inputFile, err := os.Open(sourcePath)
if err != nil {
return fmt.Errorf("Couldn't open source file: %s", err)
}
outputFile, err := os.Create(destPath)
if err != nil {
inputFile.Close()
return fmt.Errorf("Couldn't open dest file: %s", err)
}
defer outputFile.Close()
_, err = io.Copy(outputFile, inputFile)
inputFile.Close()
if err != nil {
return fmt.Errorf("Writing to output file failed: %s", err)
}
// The copy was successful, so now delete the original file
err = os.Remove(sourcePath)
if err != nil {
return fmt.Errorf("Failed removing original file: %s", err)
}
return nil
}
@nishanths
Copy link

nishanths commented Jan 10, 2023

A few more notes.

Error comparison:

os.Rename() give error "invalid cross-device link" for Docker container with Volumes.
strings.Contains(err.Error(), "invalid cross-device link")

If one is only targeting a Unix-like system, the check for this error may be better achieved by checking whether the syscall.Errno is 18. If one were to use the strings.Contain check as written below, in go1.19, the check will be a false negative e.g. on netbsd and darwin, where the error string is "cross-device link" (note, missing "invalid" at start).

File types/symbolic links: On Unix-like systems, when the source file is a symbolic link, the rename(2) and renameat(2) syscalls, used by os.Rename, will rename the symbolic link itself. In others words, the system calls, and consequently os.Rename, do not follow symbolic links. But in func moveCrossDevice, using os.Open will always follow symbolic links. Given this, one may likely want to do the following:

  • If the source file is a symbolic link, use os.Symlink, then remove the source file.
  • If the source file is a regular file, use os.Open, os.Create, io.Copy, then remove the source file.
  • For other file types, ???

Additionally, one may want to change os.Stat(source), which follows symbolic links, to os.Lstat(source), which doesn't follow symbolic links.

Error handling: For the (*os.File).Close and os.Remove calls in func moveCrossDevice, one may want to handle returned errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment