sysfs: cleanup windows rename (#1584)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -70,7 +70,7 @@ func (d *dirFS) Chmod(path string, perm fs.FileMode) syscall.Errno {
|
|||||||
// Rename implements the same method as documented on fsapi.FS
|
// Rename implements the same method as documented on fsapi.FS
|
||||||
func (d *dirFS) Rename(from, to string) syscall.Errno {
|
func (d *dirFS) Rename(from, to string) syscall.Errno {
|
||||||
from, to = d.join(from), d.join(to)
|
from, to = d.join(from), d.join(to)
|
||||||
return Rename(from, to)
|
return rename(from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Readlink implements the same method as documented on fsapi.FS
|
// Readlink implements the same method as documented on fsapi.FS
|
||||||
@@ -92,13 +92,17 @@ func (d *dirFS) Link(oldName, newName string) syscall.Errno {
|
|||||||
|
|
||||||
// Rmdir implements the same method as documented on fsapi.FS
|
// Rmdir implements the same method as documented on fsapi.FS
|
||||||
func (d *dirFS) Rmdir(path string) syscall.Errno {
|
func (d *dirFS) Rmdir(path string) syscall.Errno {
|
||||||
err := syscall.Rmdir(d.join(path))
|
return rmdir(d.join(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
func rmdir(path string) syscall.Errno {
|
||||||
|
err := syscall.Rmdir(path)
|
||||||
return platform.UnwrapOSError(err)
|
return platform.UnwrapOSError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlink implements the same method as documented on fsapi.FS
|
// Unlink implements the same method as documented on fsapi.FS
|
||||||
func (d *dirFS) Unlink(path string) (err syscall.Errno) {
|
func (d *dirFS) Unlink(path string) (err syscall.Errno) {
|
||||||
return Unlink(d.join(path))
|
return unlink(d.join(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Symlink implements the same method as documented on fsapi.FS
|
// Symlink implements the same method as documented on fsapi.FS
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
const NonBlockingFileIoSupported = true
|
const nonBlockingFileIoSupported = true
|
||||||
|
|
||||||
// readFd exposes syscall.Read.
|
// readFd exposes syscall.Read.
|
||||||
func readFd(fd uintptr, buf []byte) (int, syscall.Errno) {
|
func readFd(fd uintptr, buf []byte) (int, syscall.Errno) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ package sysfs
|
|||||||
|
|
||||||
import "syscall"
|
import "syscall"
|
||||||
|
|
||||||
const NonBlockingFileIoSupported = false
|
const nonBlockingFileIoSupported = false
|
||||||
|
|
||||||
// readFd returns ENOSYS on unsupported platforms.
|
// readFd returns ENOSYS on unsupported platforms.
|
||||||
func readFd(fd uintptr, buf []byte) (int, syscall.Errno) {
|
func readFd(fd uintptr, buf []byte) (int, syscall.Errno) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
const NonBlockingFileIoSupported = true
|
const nonBlockingFileIoSupported = true
|
||||||
|
|
||||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ func (f *osFile) Read(buf []byte) (n int, errno syscall.Errno) {
|
|||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return 0, 0 // Short-circuit 0-len reads.
|
return 0, 0 // Short-circuit 0-len reads.
|
||||||
}
|
}
|
||||||
if NonBlockingFileIoSupported && f.IsNonblock() {
|
if nonBlockingFileIoSupported && f.IsNonblock() {
|
||||||
n, errno = readFd(f.fd, buf)
|
n, errno = readFd(f.fd, buf)
|
||||||
} else {
|
} else {
|
||||||
n, errno = read(f.file, buf)
|
n, errno = read(f.file, buf)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Rename(from, to string) syscall.Errno {
|
func rename(from, to string) syscall.Errno {
|
||||||
if from == to {
|
if from == to {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestRename(t *testing.T) {
|
|||||||
err := os.WriteFile(file1Path, []byte{1}, 0o600)
|
err := os.WriteFile(file1Path, []byte{1}, 0o600)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = Rename(path.Join(tmpDir, "non-exist"), file1Path)
|
err = rename(path.Join(tmpDir, "non-exist"), file1Path)
|
||||||
require.EqualErrno(t, syscall.ENOENT, err)
|
require.EqualErrno(t, syscall.ENOENT, err)
|
||||||
})
|
})
|
||||||
t.Run("file to non-exist", func(t *testing.T) {
|
t.Run("file to non-exist", func(t *testing.T) {
|
||||||
@@ -31,7 +31,7 @@ func TestRename(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
file2Path := path.Join(tmpDir, "file2")
|
file2Path := path.Join(tmpDir, "file2")
|
||||||
errno := Rename(file1Path, file2Path)
|
errno := rename(file1Path, file2Path)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
// Show the prior path no longer exists
|
// Show the prior path no longer exists
|
||||||
@@ -49,7 +49,7 @@ func TestRename(t *testing.T) {
|
|||||||
require.NoError(t, os.Mkdir(dir1Path, 0o700))
|
require.NoError(t, os.Mkdir(dir1Path, 0o700))
|
||||||
|
|
||||||
dir2Path := path.Join(tmpDir, "dir2")
|
dir2Path := path.Join(tmpDir, "dir2")
|
||||||
errno := Rename(dir1Path, dir2Path)
|
errno := rename(dir1Path, dir2Path)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
// Show the prior path no longer exists
|
// Show the prior path no longer exists
|
||||||
@@ -72,7 +72,7 @@ func TestRename(t *testing.T) {
|
|||||||
err := os.WriteFile(dir2Path, []byte{2}, 0o600)
|
err := os.WriteFile(dir2Path, []byte{2}, 0o600)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = Rename(dir1Path, dir2Path)
|
err = rename(dir1Path, dir2Path)
|
||||||
require.EqualErrno(t, syscall.ENOTDIR, err)
|
require.EqualErrno(t, syscall.ENOTDIR, err)
|
||||||
})
|
})
|
||||||
t.Run("file to dir", func(t *testing.T) {
|
t.Run("file to dir", func(t *testing.T) {
|
||||||
@@ -86,7 +86,7 @@ func TestRename(t *testing.T) {
|
|||||||
dir1Path := path.Join(tmpDir, "dir1")
|
dir1Path := path.Join(tmpDir, "dir1")
|
||||||
require.NoError(t, os.Mkdir(dir1Path, 0o700))
|
require.NoError(t, os.Mkdir(dir1Path, 0o700))
|
||||||
|
|
||||||
err = Rename(file1Path, dir1Path)
|
err = rename(file1Path, dir1Path)
|
||||||
require.EqualErrno(t, syscall.EISDIR, err)
|
require.EqualErrno(t, syscall.EISDIR, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ func TestRename(t *testing.T) {
|
|||||||
dir2Path := path.Join(tmpDir, "dir2")
|
dir2Path := path.Join(tmpDir, "dir2")
|
||||||
require.NoError(t, os.Mkdir(dir2Path, 0o700))
|
require.NoError(t, os.Mkdir(dir2Path, 0o700))
|
||||||
|
|
||||||
errno := Rename(dir1Path, dir2Path)
|
errno := rename(dir1Path, dir2Path)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
// Show the prior path no longer exists
|
// Show the prior path no longer exists
|
||||||
@@ -142,7 +142,7 @@ func TestRename(t *testing.T) {
|
|||||||
err = os.WriteFile(path.Join(dir2Path, "existing.txt"), []byte("any thing"), 0o600)
|
err = os.WriteFile(path.Join(dir2Path, "existing.txt"), []byte("any thing"), 0o600)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = Rename(dir1Path, dir2Path)
|
err = rename(dir1Path, dir2Path)
|
||||||
require.EqualErrno(t, syscall.ENOTEMPTY, err)
|
require.EqualErrno(t, syscall.ENOTEMPTY, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ func TestRename(t *testing.T) {
|
|||||||
err = os.WriteFile(file2Path, file2Contents, 0o600)
|
err = os.WriteFile(file2Path, file2Contents, 0o600)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
errno := Rename(file1Path, file2Path)
|
errno := rename(file1Path, file2Path)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
// Show the prior path no longer exists
|
// Show the prior path no longer exists
|
||||||
@@ -177,7 +177,7 @@ func TestRename(t *testing.T) {
|
|||||||
dir1Path := path.Join(tmpDir, "dir1")
|
dir1Path := path.Join(tmpDir, "dir1")
|
||||||
require.NoError(t, os.Mkdir(dir1Path, 0o700))
|
require.NoError(t, os.Mkdir(dir1Path, 0o700))
|
||||||
|
|
||||||
errno := Rename(dir1Path, dir1Path)
|
errno := rename(dir1Path, dir1Path)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
s, err := os.Stat(dir1Path)
|
s, err := os.Stat(dir1Path)
|
||||||
@@ -192,7 +192,7 @@ func TestRename(t *testing.T) {
|
|||||||
err := os.WriteFile(file1Path, file1Contents, 0o600)
|
err := os.WriteFile(file1Path, file1Contents, 0o600)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
errno := Rename(file1Path, file1Path)
|
errno := rename(file1Path, file1Path)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
b, err := os.ReadFile(file1Path)
|
b, err := os.ReadFile(file1Path)
|
||||||
|
|||||||
@@ -1,47 +1,55 @@
|
|||||||
package sysfs
|
package sysfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Rename(from, to string) syscall.Errno {
|
func rename(from, to string) syscall.Errno {
|
||||||
if from == to {
|
if from == to {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fromStat, err := os.Stat(from)
|
var fromIsDir, toIsDir bool
|
||||||
if err != nil {
|
if fromStat, errno := stat(from); errno != 0 {
|
||||||
return syscall.ENOENT
|
return errno // failed to stat from
|
||||||
|
} else {
|
||||||
|
fromIsDir = fromStat.Mode.IsDir()
|
||||||
|
}
|
||||||
|
if toStat, errno := stat(to); errno == syscall.ENOENT {
|
||||||
|
return syscallRename(from, to) // file or dir to not-exist is ok
|
||||||
|
} else if errno != 0 {
|
||||||
|
return errno // failed to stat to
|
||||||
|
} else {
|
||||||
|
toIsDir = toStat.Mode.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
if toStat, err := os.Stat(to); err == nil {
|
// Now, handle known cases
|
||||||
fromIsDir, toIsDir := fromStat.IsDir(), toStat.IsDir()
|
switch {
|
||||||
if fromIsDir && !toIsDir { // dir to file
|
case !fromIsDir && toIsDir: // file to dir
|
||||||
return syscall.ENOTDIR
|
return syscall.EISDIR
|
||||||
} else if !fromIsDir && toIsDir { // file to dir
|
case !fromIsDir && !toIsDir: // file to file
|
||||||
return syscall.EISDIR
|
// Use os.Rename instead of syscall.Rename to overwrite a file.
|
||||||
} else if !fromIsDir && !toIsDir { // file to file
|
// This uses MoveFileEx instead of MoveFile (used by syscall.Rename).
|
||||||
// Use os.Rename instead of syscall.Rename in order to allow the overrides of the existing file.
|
return platform.UnwrapOSError(os.Rename(from, to))
|
||||||
// Underneath os.Rename, it uses MoveFileEx instead of MoveFile (used by syscall.Rename).
|
case fromIsDir && !toIsDir: // dir to file
|
||||||
return platform.UnwrapOSError(os.Rename(from, to))
|
return syscall.ENOTDIR
|
||||||
} else { // dir to dir
|
default: // dir to dir
|
||||||
if dirs, _ := os.ReadDir(to); len(dirs) == 0 {
|
|
||||||
// On Windows, renaming to the empty dir will be rejected,
|
// We can't tell if a directory is empty or not, via stat information.
|
||||||
// so first we remove the empty dir, and then rename to it.
|
// Reading the directory is expensive, as it can buffer large amounts
|
||||||
if err := os.Remove(to); err != nil {
|
// of data on fail. Instead, speculatively try to remove the directory.
|
||||||
return platform.UnwrapOSError(err)
|
// This is only one syscall and won't buffer anything.
|
||||||
}
|
if errno := rmdir(to); errno == 0 || errno == syscall.ENOENT {
|
||||||
return platform.UnwrapOSError(syscall.Rename(from, to))
|
return syscallRename(from, to)
|
||||||
}
|
} else {
|
||||||
return syscall.ENOTEMPTY
|
return errno
|
||||||
}
|
}
|
||||||
} else if !errors.Is(err, syscall.ENOENT) { // Failed to stat the destination.
|
|
||||||
return platform.UnwrapOSError(err)
|
|
||||||
} else { // Destination not-exist.
|
|
||||||
return platform.UnwrapOSError(syscall.Rename(from, to))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func syscallRename(from string, to string) syscall.Errno {
|
||||||
|
return platform.UnwrapOSError(syscall.Rename(from, to))
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// dirNlinkIncludesDot is true because even though os.File filters out dot
|
||||||
|
// entries, the underlying syscall.Stat includes them.
|
||||||
|
//
|
||||||
|
// Note: this is only used in tests
|
||||||
|
const dirNlinkIncludesDot = true
|
||||||
|
|
||||||
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
||||||
if info, err := os.Lstat(path); err != nil {
|
if info, err := os.Lstat(path); err != nil {
|
||||||
return sys.Stat_t{}, platform.UnwrapOSError(err)
|
return sys.Stat_t{}, platform.UnwrapOSError(err)
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// dirNlinkIncludesDot is true because even though os.File filters out dot
|
||||||
|
// entries, the underlying syscall.Stat includes them.
|
||||||
|
//
|
||||||
|
// Note: this is only used in tests
|
||||||
|
const dirNlinkIncludesDot = true
|
||||||
|
|
||||||
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
||||||
if info, err := os.Lstat(path); err != nil {
|
if info, err := os.Lstat(path); err != nil {
|
||||||
return sys.Stat_t{}, platform.UnwrapOSError(err)
|
return sys.Stat_t{}, platform.UnwrapOSError(err)
|
||||||
|
|||||||
@@ -23,14 +23,50 @@ func TestStat(t *testing.T) {
|
|||||||
|
|
||||||
var st sys.Stat_t
|
var st sys.Stat_t
|
||||||
|
|
||||||
t.Run("dir", func(t *testing.T) {
|
t.Run("empty dir", func(t *testing.T) {
|
||||||
st, errno = stat(tmpDir)
|
st, errno = stat(tmpDir)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
require.True(t, st.Mode.IsDir())
|
require.True(t, st.Mode.IsDir())
|
||||||
require.NotEqual(t, uint64(0), st.Ino)
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
|
|
||||||
|
// We expect one link: the directory itself
|
||||||
|
expectedNlink := uint64(1)
|
||||||
|
if dirNlinkIncludesDot {
|
||||||
|
expectedNlink++
|
||||||
|
}
|
||||||
|
require.Equal(t, expectedNlink, st.Nlink, runtime.GOOS)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
subdir := path.Join(tmpDir, "sub")
|
||||||
|
var stSubdir sys.Stat_t
|
||||||
|
t.Run("subdir", func(t *testing.T) {
|
||||||
|
require.NoError(t, os.Mkdir(subdir, 0o500))
|
||||||
|
|
||||||
|
stSubdir, errno = stat(subdir)
|
||||||
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
|
require.True(t, stSubdir.Mode.IsDir())
|
||||||
|
require.NotEqual(t, uint64(0), st.Ino)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not empty dir", func(t *testing.T) {
|
||||||
|
st, errno = stat(tmpDir)
|
||||||
|
require.EqualErrno(t, 0, errno)
|
||||||
|
|
||||||
|
// We expect two links: the directory itself and the subdir
|
||||||
|
expectedNlink := uint64(2)
|
||||||
|
if dirNlinkIncludesDot {
|
||||||
|
expectedNlink++
|
||||||
|
} else if runtime.GOOS == "windows" {
|
||||||
|
expectedNlink = 1 // directory count is not returned.
|
||||||
|
}
|
||||||
|
require.Equal(t, expectedNlink, st.Nlink, runtime.GOOS)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: Investigate why Nlink increases on BSD when a file is added, but
|
||||||
|
// not Linux.
|
||||||
|
|
||||||
file := path.Join(tmpDir, "file")
|
file := path.Join(tmpDir, "file")
|
||||||
var stFile sys.Stat_t
|
var stFile sys.Stat_t
|
||||||
|
|
||||||
@@ -54,18 +90,6 @@ func TestStat(t *testing.T) {
|
|||||||
require.Equal(t, stFile, stLink) // resolves to the file
|
require.Equal(t, stFile, stLink) // resolves to the file
|
||||||
})
|
})
|
||||||
|
|
||||||
subdir := path.Join(tmpDir, "sub")
|
|
||||||
var stSubdir sys.Stat_t
|
|
||||||
t.Run("subdir", func(t *testing.T) {
|
|
||||||
require.NoError(t, os.Mkdir(subdir, 0o500))
|
|
||||||
|
|
||||||
stSubdir, errno = stat(subdir)
|
|
||||||
require.EqualErrno(t, 0, errno)
|
|
||||||
|
|
||||||
require.True(t, stSubdir.Mode.IsDir())
|
|
||||||
require.NotEqual(t, uint64(0), st.Ino)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("link to dir", func(t *testing.T) {
|
t.Run("link to dir", func(t *testing.T) {
|
||||||
link := path.Join(tmpDir, "dir-link")
|
link := path.Join(tmpDir, "dir-link")
|
||||||
require.NoError(t, os.Symlink(subdir, link))
|
require.NoError(t, os.Symlink(subdir, link))
|
||||||
@@ -249,7 +273,7 @@ func TestStatFile_dev_inode(t *testing.T) {
|
|||||||
require.EqualErrno(t, 0, l2.Close())
|
require.EqualErrno(t, 0, l2.Close())
|
||||||
|
|
||||||
// Renaming a file shouldn't change its inodes.
|
// Renaming a file shouldn't change its inodes.
|
||||||
require.EqualErrno(t, 0, Rename(path1, path2))
|
require.EqualErrno(t, 0, rename(path1, path2))
|
||||||
f1 = requireOpenFile(t, path2, os.O_RDONLY, 0)
|
f1 = requireOpenFile(t, path2, os.O_RDONLY, 0)
|
||||||
defer f1.Close()
|
defer f1.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ import (
|
|||||||
// Note: go:build constraints must be the same as /sys.stat_unsupported.go for
|
// Note: go:build constraints must be the same as /sys.stat_unsupported.go for
|
||||||
// the same reasons.
|
// the same reasons.
|
||||||
|
|
||||||
|
// dirNlinkIncludesDot might be true for some operating systems, which can have
|
||||||
|
// new stat_XX.go files as necessary.
|
||||||
|
//
|
||||||
|
// Note: this is only used in tests
|
||||||
|
const dirNlinkIncludesDot = false
|
||||||
|
|
||||||
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
||||||
if info, err := os.Lstat(path); err != nil {
|
if info, err := os.Lstat(path); err != nil {
|
||||||
return sys.Stat_t{}, platform.UnwrapOSError(err)
|
return sys.Stat_t{}, platform.UnwrapOSError(err)
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/sys"
|
"github.com/tetratelabs/wazero/sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// dirNlinkIncludesDot is false because Windows does not return dot entries.
|
||||||
|
//
|
||||||
|
// Note: this is only used in tests
|
||||||
|
const dirNlinkIncludesDot = false
|
||||||
|
|
||||||
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
func lstat(path string) (sys.Stat_t, syscall.Errno) {
|
||||||
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||||
// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
|
// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Unlink(name string) (errno syscall.Errno) {
|
func unlink(name string) (errno syscall.Errno) {
|
||||||
err := syscall.Unlink(name)
|
err := syscall.Unlink(name)
|
||||||
if errno = platform.UnwrapOSError(err); errno == syscall.EPERM {
|
if errno = platform.UnwrapOSError(err); errno == syscall.EPERM {
|
||||||
errno = syscall.EISDIR
|
errno = syscall.EISDIR
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
func TestUnlink(t *testing.T) {
|
func TestUnlink(t *testing.T) {
|
||||||
t.Run("doesn't exist", func(t *testing.T) {
|
t.Run("doesn't exist", func(t *testing.T) {
|
||||||
name := "non-existent"
|
name := "non-existent"
|
||||||
errno := Unlink(name)
|
errno := unlink(name)
|
||||||
require.EqualErrno(t, syscall.ENOENT, errno)
|
require.EqualErrno(t, syscall.ENOENT, errno)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ func TestUnlink(t *testing.T) {
|
|||||||
dir := path.Join(tmpDir, "dir")
|
dir := path.Join(tmpDir, "dir")
|
||||||
require.NoError(t, os.Mkdir(dir, 0o700))
|
require.NoError(t, os.Mkdir(dir, 0o700))
|
||||||
|
|
||||||
errno := Unlink(dir)
|
errno := unlink(dir)
|
||||||
require.EqualErrno(t, syscall.EISDIR, errno)
|
require.EqualErrno(t, syscall.EISDIR, errno)
|
||||||
|
|
||||||
require.NoError(t, os.Remove(dir))
|
require.NoError(t, os.Remove(dir))
|
||||||
@@ -40,7 +40,7 @@ func TestUnlink(t *testing.T) {
|
|||||||
require.NoError(t, os.Symlink("subdir", symlinkName))
|
require.NoError(t, os.Symlink("subdir", symlinkName))
|
||||||
|
|
||||||
// Unlinking the symlink should suceed.
|
// Unlinking the symlink should suceed.
|
||||||
errno := Unlink(symlinkName)
|
errno := unlink(symlinkName)
|
||||||
require.EqualErrno(t, 0, errno)
|
require.EqualErrno(t, 0, errno)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ func TestUnlink(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, os.WriteFile(name, []byte{}, 0o600))
|
require.NoError(t, os.WriteFile(name, []byte{}, 0o600))
|
||||||
|
|
||||||
require.EqualErrno(t, 0, Unlink(name))
|
require.EqualErrno(t, 0, unlink(name))
|
||||||
_, err := os.Stat(name)
|
_, err := os.Stat(name)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Unlink(name string) syscall.Errno {
|
func unlink(name string) syscall.Errno {
|
||||||
err := syscall.Unlink(name)
|
err := syscall.Unlink(name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -52,7 +52,11 @@ type Stat_t struct {
|
|||||||
// type of the file (fs.ModeType) and its permissions (fs.ModePerm).
|
// type of the file (fs.ModeType) and its permissions (fs.ModePerm).
|
||||||
Mode fs.FileMode
|
Mode fs.FileMode
|
||||||
|
|
||||||
/// Nlink is the number of hard links to the file.
|
// Nlink is the number of hard links to the file.
|
||||||
|
//
|
||||||
|
// Note: This value is platform-specific and often at least one. Linux will
|
||||||
|
// return 1+N for a directory, where BSD (like Darwin) return 2+N, which
|
||||||
|
// includes the dot entry.
|
||||||
Nlink uint64
|
Nlink uint64
|
||||||
|
|
||||||
// Size is the length in bytes for regular files. For symbolic links, this
|
// Size is the length in bytes for regular files. For symbolic links, this
|
||||||
|
|||||||
Reference in New Issue
Block a user