rename_windows.go

 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}