mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-16 04:06:44 +00:00
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.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
package mount
|
||||
|
||||
import "testing"
|
||||
|
||||
// The Windows adapter forwards cgofuse's xattr flags to SetXAttr untouched,
|
||||
// which only holds while both sides number them the same. cgofuse's side is
|
||||
// pinned in weed/mount/winfsp; this pins ours, so editing either alone fails
|
||||
// rather than silently turning a create into a replace.
|
||||
func TestXattrFlagValues(t *testing.T) {
|
||||
if xattr_CREATE != 1 {
|
||||
t.Errorf("xattr_CREATE = %d, want 1", xattr_CREATE)
|
||||
}
|
||||
if xattr_REPLACE != 2 {
|
||||
t.Errorf("xattr_REPLACE = %d, want 2", xattr_REPLACE)
|
||||
}
|
||||
}
|
||||
@@ -76,3 +76,12 @@ func TestOpenFlagTranslation(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package winfsp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
)
|
||||
|
||||
// xattrBufferSize bounds one attribute value and one name listing. The mount
|
||||
// stores extended attributes in the entry itself, so they are small by
|
||||
// construction; this is the ceiling WinFsp will accept in a single reply.
|
||||
const xattrBufferSize = 64 * 1024
|
||||
|
||||
func (w *WinFS) Getxattr(path string, name string) (int, []byte) {
|
||||
inode, ref, status := w.resolve(path)
|
||||
if status != fuse.OK {
|
||||
return toErrno(status), nil
|
||||
}
|
||||
defer ref.release()
|
||||
|
||||
dest := make([]byte, xattrBufferSize)
|
||||
size, status := w.wfs.GetXAttr(never, ptr(w.caller(inode)), name, dest)
|
||||
if status != fuse.OK {
|
||||
return toErrno(status), nil
|
||||
}
|
||||
return 0, dest[:size]
|
||||
}
|
||||
|
||||
func (w *WinFS) Setxattr(path string, name string, value []byte, flags int) int {
|
||||
if w.denied() {
|
||||
return -eROFS
|
||||
}
|
||||
inode, ref, status := w.resolve(path)
|
||||
if status != fuse.OK {
|
||||
return toErrno(status)
|
||||
}
|
||||
defer ref.release()
|
||||
|
||||
// cgofuse and the raw filesystem number XATTR_CREATE and XATTR_REPLACE the
|
||||
// same, so the flags pass straight through; TestXattrFlagValues pins that.
|
||||
in := &fuse.SetXAttrIn{InHeader: w.caller(inode)}
|
||||
in.Flags = uint32(flags)
|
||||
in.Size = uint32(len(value))
|
||||
return toErrno(w.wfs.SetXAttr(never, in, name, value))
|
||||
}
|
||||
|
||||
func (w *WinFS) Removexattr(path string, name string) int {
|
||||
if w.denied() {
|
||||
return -eROFS
|
||||
}
|
||||
inode, ref, status := w.resolve(path)
|
||||
if status != fuse.OK {
|
||||
return toErrno(status)
|
||||
}
|
||||
defer ref.release()
|
||||
return toErrno(w.wfs.RemoveXAttr(never, ptr(w.caller(inode)), name))
|
||||
}
|
||||
|
||||
func (w *WinFS) Listxattr(path string, fill func(name string) bool) int {
|
||||
inode, ref, status := w.resolve(path)
|
||||
if status != fuse.OK {
|
||||
return toErrno(status)
|
||||
}
|
||||
defer ref.release()
|
||||
|
||||
dest := make([]byte, xattrBufferSize)
|
||||
size, status := w.wfs.ListXAttr(never, ptr(w.caller(inode)), dest)
|
||||
if status != fuse.OK {
|
||||
return toErrno(status)
|
||||
}
|
||||
for _, name := range splitXattrNames(dest[:size]) {
|
||||
if !fill(name) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// splitXattrNames unpacks the NUL-separated listing the raw filesystem
|
||||
// produces. A trailing NUL terminates the last name rather than starting an
|
||||
// empty one.
|
||||
func splitXattrNames(packed []byte) []string {
|
||||
var names []string
|
||||
for _, name := range bytes.Split(packed, []byte{0}) {
|
||||
if len(name) > 0 {
|
||||
names = append(names, string(name))
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
Reference in New Issue
Block a user