1package sysfs
2
3import (
4 "os"
5 "syscall"
6
7 "github.com/tetratelabs/wazero/experimental/sys"
8)
9
10func rename(from, to string) sys.Errno {
11 if from == to {
12 return 0
13 }
14
15 var fromIsDir, toIsDir bool
16 if fromStat, errno := stat(from); errno != 0 {
17 return errno // failed to stat from
18 } else {
19 fromIsDir = fromStat.Mode.IsDir()
20 }
21 if toStat, errno := stat(to); errno == sys.ENOENT {
22 return syscallRename(from, to) // file or dir to not-exist is ok
23 } else if errno != 0 {
24 return errno // failed to stat to
25 } else {
26 toIsDir = toStat.Mode.IsDir()
27 }
28
29 // Now, handle known cases
30 switch {
31 case !fromIsDir && toIsDir: // file to dir
32 return sys.EISDIR
33 case !fromIsDir && !toIsDir: // file to file
34 // Use os.Rename instead of syscall.Rename to overwrite a file.
35 // This uses MoveFileEx instead of MoveFile (used by syscall.Rename).
36 return sys.UnwrapOSError(os.Rename(from, to))
37 case fromIsDir && !toIsDir: // dir to file
38 return sys.ENOTDIR
39 default: // dir to dir
40
41 // We can't tell if a directory is empty or not, via stat information.
42 // Reading the directory is expensive, as it can buffer large amounts
43 // of data on fail. Instead, speculatively try to remove the directory.
44 // This is only one syscall and won't buffer anything.
45 if errno := rmdir(to); errno == 0 || errno == sys.ENOENT {
46 return syscallRename(from, to)
47 } else {
48 return errno
49 }
50 }
51}
52
53func syscallRename(from string, to string) sys.Errno {
54 return sys.UnwrapOSError(syscall.Rename(from, to))
55}