Files
seaweedfs/weed/mount/winfsp/errno_windows_test.go
Chris LuandGitHub a0e278f86f mount: forward extended attributes on windows (#10554)
weed/mount implements all four xattr operations and the filer stores the
values, but the Windows adapter overrode none of them, so cgofuse's
defaults answered every call with 'not implemented'. WinFsp advertises
extended attribute support either way, because cgofuse registers the
callbacks unconditionally, so applications were told the volume has them
and then refused on every use. Attributes written from Linux were
invisible from Windows.

Untested in CI: exercising Windows extended attributes needs the native
NtSetEaFile path rather than anything in os or PowerShell.
2026-08-03 21:55:59 -07:00

88 lines
2.9 KiB
Go

package winfsp
import (
"syscall"
"testing"
cgofuse "github.com/winfsp/cgofuse/fuse"
)
// The errno values are spelled out so the table stays portable and testable on
// any runner. This pins them to what cgofuse actually decodes, so a divergence
// is a failing test rather than an operation reporting an unrelated error.
func TestErrnoValuesMatchCgofuse(t *testing.T) {
for _, c := range []struct {
name string
ours int
want int
}{
{"EPERM", ePERM, cgofuse.EPERM},
{"ENOENT", eNOENT, cgofuse.ENOENT},
{"EINTR", eINTR, cgofuse.EINTR},
{"EIO", eIO, cgofuse.EIO},
{"ENXIO", eNXIO, cgofuse.ENXIO},
{"EBADF", eBADF, cgofuse.EBADF},
{"EAGAIN", eAGAIN, cgofuse.EAGAIN},
{"ENOMEM", eNOMEM, cgofuse.ENOMEM},
{"EACCES", eACCES, cgofuse.EACCES},
{"EBUSY", eBUSY, cgofuse.EBUSY},
{"EEXIST", eEXIST, cgofuse.EEXIST},
{"EXDEV", eXDEV, cgofuse.EXDEV},
{"ENODEV", eNODEV, cgofuse.ENODEV},
{"ENOTDIR", eNOTDIR, cgofuse.ENOTDIR},
{"EISDIR", eISDIR, cgofuse.EISDIR},
{"EINVAL", eINVAL, cgofuse.EINVAL},
{"ENFILE", eNFILE, cgofuse.ENFILE},
{"EMFILE", eMFILE, cgofuse.EMFILE},
{"EFBIG", eFBIG, cgofuse.EFBIG},
{"ENOSPC", eNOSPC, cgofuse.ENOSPC},
{"ESPIPE", eSPIPE, cgofuse.ESPIPE},
{"EROFS", eROFS, cgofuse.EROFS},
{"EMLINK", eMLINK, cgofuse.EMLINK},
{"EPIPE", ePIPE, cgofuse.EPIPE},
{"ERANGE", eRANGE, cgofuse.ERANGE},
{"ENAMETOOLONG", eNAMETOOLONG, cgofuse.ENAMETOOLONG},
{"ENOSYS", eNOSYS, cgofuse.ENOSYS},
{"ENOTEMPTY", eNOTEMPTY, cgofuse.ENOTEMPTY},
{"ELOOP", eLOOP, cgofuse.ELOOP},
{"ENODATA", eNODATA, cgofuse.ENODATA},
{"ENOTSUP", eNOTSUP, cgofuse.ENOTSUP},
} {
if c.ours != c.want {
t.Errorf("%s = %d, cgofuse uses %d", c.name, c.ours, c.want)
}
}
}
// translateOpenFlags relies on cgofuse using MSVC's numbering while the raw
// filesystem reads Go's. Pin that, because a swap between O_EXCL and O_TRUNC
// would turn "fail if it exists" into "truncate it".
func TestOpenFlagTranslation(t *testing.T) {
for _, c := range []struct {
name string
in int
want uint32
}{
{"O_EXCL", cgofuse.O_EXCL, uint32(syscall.O_EXCL)},
{"O_TRUNC", cgofuse.O_TRUNC, uint32(syscall.O_TRUNC)},
{"O_CREAT", cgofuse.O_CREAT, uint32(syscall.O_CREAT)},
{"O_APPEND", cgofuse.O_APPEND, uint32(syscall.O_APPEND)},
} {
if got := translateOpenFlags(c.in); got != c.want {
t.Errorf("translateOpenFlags(%s=%#x) = %#x, want %#x", c.name, c.in, got, c.want)
}
}
if got := translateOpenFlags(cgofuse.O_RDWR); got != uint32(syscall.O_RDWR) {
t.Errorf("access mode not preserved: %#x", got)
}
}
// Setxattr passes cgofuse's flags straight to the raw filesystem, which is
// only correct while the two number them identically. The other side is
// pinned by TestXattrFlagValues in weed/mount, so editing either alone fails.
func TestXattrFlagValues(t *testing.T) {
if cgofuse.XATTR_CREATE != 1 || cgofuse.XATTR_REPLACE != 2 {
t.Fatalf("cgofuse xattr flags moved: create=%d replace=%d", cgofuse.XATTR_CREATE, cgofuse.XATTR_REPLACE)
}
}