-
-
Save var23rav/23ae5d0d4d830aff886c3c970b8f6c6b to your computer and use it in GitHub Desktop.
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 | |
} |
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.
Better copy-based implementation that retains quick renames and file mode where possible: