mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-19 13:46:58 +00:00
mount: build the package on windows (#10535)
* mount: drop the unused go-fuse fs package dependency WFS embedded fs.Inode but never used any of its methods, and the only other reference was RENAME_EXCHANGE, a constant sitting next to three literals. Removing both drops fs and five internal packages from the mount build graph. * mount: build the package on windows Windows has no fcntl lock types, no O_ACCMODE and no x/sys/unix, so a handful of constants kept weed/mount pinned to unix even though the code using them is portable in-memory logic. Route them through per-OS shims and give setBlksize a windows no-op. The POSIX lock table now compiles on windows but stays unreachable: WinFsp resolves byte-range locks in its own kernel driver, so nothing will feed it there. go.mod points at a go-fuse branch commit and needs repinning to a release tag once that lands. * ci: cross-compile for windows Nothing caught the unix-only constants creeping into weed/mount until a release build failed. * mount: let readdir feed a sink instead of the kernel buffer doReadDirectory wrote directly into fuse.DirEntryList, which is the kernel's wire format. A front end that is not the kernel would have to pack entries only to parse them straight back out. Route it through DirEntrySink instead. ReadDir and ReadDirPlus pass the reply buffer, so nothing changes for the FUSE server. * mount: pin go-fuse v2.9.4 for the windows build
This commit is contained in:
@@ -75,6 +75,21 @@ jobs:
|
||||
- name: Build
|
||||
run: cd weed; go build -tags "elastic gocdk sqlite ydb tarantool tikv rclone" -v .
|
||||
|
||||
build-windows:
|
||||
name: Build windows
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v7
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
# weed/mount builds here but cannot mount yet. Without this the unix-only
|
||||
# syscall constants it needs creep back in unnoticed.
|
||||
- name: Build windows/amd64
|
||||
run: GOOS=windows GOARCH=amd64 go build ./weed/...
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -148,7 +148,7 @@ require (
|
||||
github.com/rdleal/intervalst v1.5.0
|
||||
github.com/redis/go-redis/v9 v9.21.0
|
||||
github.com/schollz/progressbar/v3 v3.19.1
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.3
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.4
|
||||
github.com/shirou/gopsutil/v4 v4.26.6
|
||||
github.com/tarantool/go-option v1.1.0
|
||||
github.com/tarantool/go-tarantool/v3 v3.0.0
|
||||
|
||||
@@ -1810,6 +1810,12 @@ github.com/seaweedfs/cockroachdb-parser v0.0.0-20260225204133-2f342c5ea564 h1:Tg
|
||||
github.com/seaweedfs/cockroachdb-parser v0.0.0-20260225204133-2f342c5ea564/go.mod h1:JSKCh6uCHBz91lQYFYHCyTrSVIPge4SUFVn28iwMNB0=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.3 h1:rJufGrHImTx7yoGUmetUi+To4LrmTQJROJnBHWt92ic=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.3/go.mod h1:zABdmWEa6A0bwaBeEOBUeUkGIZlxUhcdv+V1Dcc/U/I=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.4-0.20260803073934-cb95bfebced6 h1:Kn79NPV77uc3bEat4RWi6vpNYNacYfjXcWBLNv3RZ2A=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.4-0.20260803073934-cb95bfebced6/go.mod h1:zABdmWEa6A0bwaBeEOBUeUkGIZlxUhcdv+V1Dcc/U/I=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.4-0.20260803074752-bd46d45ccb3b h1:ECjScJHzIATk8CTdlP+yfPY4W5bLxeoy1Mma8rd0dRA=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.4-0.20260803074752-bd46d45ccb3b/go.mod h1:zABdmWEa6A0bwaBeEOBUeUkGIZlxUhcdv+V1Dcc/U/I=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.4 h1:ACyloiuopdhRSjdLLeSWbsVaemMPskORaRF01TY6GyM=
|
||||
github.com/seaweedfs/go-fuse/v2 v2.9.4/go.mod h1:zABdmWEa6A0bwaBeEOBUeUkGIZlxUhcdv+V1Dcc/U/I=
|
||||
github.com/seaweedfs/goexif v1.0.3 h1:ve/OjI7dxPW8X9YQsv3JuVMaxEyF9Rvfd04ouL+Bz30=
|
||||
github.com/seaweedfs/goexif v1.0.3/go.mod h1:Oni780Z236sXpIQzk1XoJlTwqrJ02smEin9zQeff7Fk=
|
||||
github.com/seaweedfs/raft v1.2.0 h1:Ez4Hw9ifBbTT7wg54DvGHBjw1vRlTb4roH0TKl0Oj9Y=
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
)
|
||||
|
||||
// DirEntrySink receives the entries a readdir produces. The FUSE server packs
|
||||
// them straight into the kernel's reply buffer; a front end that is not the
|
||||
// kernel reads them out instead of re-parsing that wire format.
|
||||
type DirEntrySink interface {
|
||||
// AddEntry reports one entry, returning false once the sink is full. A
|
||||
// full sink ends the batch; the client resumes from the entry's Off.
|
||||
AddEntry(entry fuse.DirEntry) bool
|
||||
|
||||
// AddEntryPlus is AddEntry for readdirplus, returning the attribute block
|
||||
// to fill in, or nil once the sink is full.
|
||||
AddEntryPlus(entry fuse.DirEntry) *fuse.EntryOut
|
||||
}
|
||||
|
||||
// fuseDirEntryList adapts the kernel reply buffer to DirEntrySink.
|
||||
type fuseDirEntryList struct {
|
||||
*fuse.DirEntryList
|
||||
}
|
||||
|
||||
func (l fuseDirEntryList) AddEntry(entry fuse.DirEntry) bool {
|
||||
return l.AddDirEntry(entry)
|
||||
}
|
||||
|
||||
func (l fuseDirEntryList) AddEntryPlus(entry fuse.DirEntry) *fuse.EntryOut {
|
||||
return l.AddDirLookupEntry(entry)
|
||||
}
|
||||
|
||||
// ReadDirectoryInto runs a readdir against sink. ReadDir and ReadDirPlus are
|
||||
// this with the kernel reply buffer as the sink.
|
||||
func (wfs *WFS) ReadDirectoryInto(input *fuse.ReadIn, sink DirEntrySink, isPlusMode bool) fuse.Status {
|
||||
return wfs.doReadDirectory(input, sink, isPlusMode)
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
)
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
type lockRange struct {
|
||||
Start uint64 // inclusive byte offset
|
||||
End uint64 // inclusive; math.MaxUint64 means "to EOF"
|
||||
Typ uint32 // syscall.F_RDLCK or syscall.F_WRLCK
|
||||
Typ uint32 // f_RDLCK or f_WRLCK
|
||||
Owner uint64 // FUSE lock owner (from LkIn.Owner)
|
||||
Pid uint32 // PID of lock holder (for GetLk reporting)
|
||||
// flock and fcntl locks have different ownership and close semantics.
|
||||
@@ -127,7 +126,7 @@ func findConflict(locks []lockRange, proposed lockRange) (lockRange, bool) {
|
||||
if !rangesOverlap(h.Start, h.End, proposed.Start, proposed.End) {
|
||||
continue
|
||||
}
|
||||
if h.Typ == syscall.F_RDLCK && proposed.Typ == syscall.F_RDLCK {
|
||||
if h.Typ == f_RDLCK && proposed.Typ == f_RDLCK {
|
||||
continue
|
||||
}
|
||||
return h, true
|
||||
@@ -304,7 +303,7 @@ func (plt *PosixLockTable) GetLk(inode uint64, proposed lockRange, out *fuse.LkO
|
||||
for {
|
||||
il := plt.getInodeLocks(inode)
|
||||
if il == nil {
|
||||
out.Lk.Typ = syscall.F_UNLCK
|
||||
out.Lk.Typ = f_UNLCK
|
||||
return
|
||||
}
|
||||
il.mu.Lock()
|
||||
@@ -324,7 +323,7 @@ func (plt *PosixLockTable) GetLk(inode uint64, proposed lockRange, out *fuse.LkO
|
||||
out.Lk.Typ = conflict.Typ
|
||||
out.Lk.Pid = conflict.Pid
|
||||
} else {
|
||||
out.Lk.Typ = syscall.F_UNLCK
|
||||
out.Lk.Typ = f_UNLCK
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -334,7 +333,7 @@ func (plt *PosixLockTable) GetLk(inode uint64, proposed lockRange, out *fuse.LkO
|
||||
// For unlock (F_UNLCK): removes locks in the given range for the owner.
|
||||
// For lock: returns fuse.EAGAIN if a conflict exists, fuse.OK on success.
|
||||
func (plt *PosixLockTable) SetLk(inode uint64, lk lockRange) fuse.Status {
|
||||
if lk.Typ == syscall.F_UNLCK {
|
||||
if lk.Typ == f_UNLCK {
|
||||
il := plt.getInodeLocks(inode)
|
||||
if il == nil {
|
||||
return fuse.OK
|
||||
@@ -376,7 +375,7 @@ func (plt *PosixLockTable) SetLk(inode uint64, lk lockRange) fuse.Status {
|
||||
// SetLkw attempts a blocking lock. It waits until the lock can be acquired
|
||||
// or the cancel channel is closed.
|
||||
func (plt *PosixLockTable) SetLkw(inode uint64, lk lockRange, cancel <-chan struct{}) fuse.Status {
|
||||
if lk.Typ == syscall.F_UNLCK {
|
||||
if lk.Typ == f_UNLCK {
|
||||
return plt.SetLk(inode, lk)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build !windows
|
||||
|
||||
package mount
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
f_RDLCK = syscall.F_RDLCK
|
||||
f_WRLCK = syscall.F_WRLCK
|
||||
f_UNLCK = syscall.F_UNLCK
|
||||
o_ACCMODE = syscall.O_ACCMODE
|
||||
|
||||
xattr_CREATE = sys.XATTR_CREATE
|
||||
xattr_REPLACE = sys.XATTR_REPLACE
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package mount
|
||||
|
||||
// Windows has no fcntl lock types and no access-mode mask. The Linux values
|
||||
// stand in: nothing on Windows feeds these, and the lock table only ever
|
||||
// compares them against each other.
|
||||
const (
|
||||
f_RDLCK = 0
|
||||
f_WRLCK = 1
|
||||
f_UNLCK = 2
|
||||
o_ACCMODE = 0x3
|
||||
|
||||
xattr_CREATE = 1
|
||||
xattr_REPLACE = 2
|
||||
)
|
||||
@@ -155,7 +155,7 @@ func checkStickyBit(dirMode, dirUid, targetUid, callerUid uint32) fuse.Status {
|
||||
|
||||
// openFlagsToAccessMask converts open(2) flags to an access permission mask.
|
||||
func openFlagsToAccessMask(flags uint32) uint32 {
|
||||
switch flags & uint32(syscall.O_ACCMODE) {
|
||||
switch flags & uint32(o_ACCMODE) {
|
||||
case syscall.O_WRONLY:
|
||||
return fuse.W_OK
|
||||
case syscall.O_RDWR:
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
)
|
||||
|
||||
func setBlksize(out *fuse.Attr, size uint32) {
|
||||
}
|
||||
@@ -139,14 +139,14 @@ func (wfs *WFS) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) (code fuse
|
||||
* '1'.
|
||||
*/
|
||||
func (wfs *WFS) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
|
||||
return wfs.doReadDirectory(input, out, false)
|
||||
return wfs.doReadDirectory(input, fuseDirEntryList{out}, false)
|
||||
}
|
||||
|
||||
func (wfs *WFS) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
|
||||
return wfs.doReadDirectory(input, out, true)
|
||||
return wfs.doReadDirectory(input, fuseDirEntryList{out}, true)
|
||||
}
|
||||
|
||||
func (wfs *WFS) doReadDirectory(input *fuse.ReadIn, out *fuse.DirEntryList, isPlusMode bool) fuse.Status {
|
||||
func (wfs *WFS) doReadDirectory(input *fuse.ReadIn, out DirEntrySink, isPlusMode bool) fuse.Status {
|
||||
// Get the directory handle and lock it for the duration of this operation.
|
||||
// This serializes concurrent readdir calls on the same handle, fixing the
|
||||
// race condition that caused hangs with NFS-Ganesha.
|
||||
@@ -182,11 +182,11 @@ func (wfs *WFS) doReadDirectory(input *fuse.ReadIn, out *fuse.DirEntryList, isPl
|
||||
dirEntry.Off = dh.entryStreamOffset + uint64(index) + 1
|
||||
|
||||
if !isPlusMode {
|
||||
if !out.AddDirEntry(dirEntry) {
|
||||
if !out.AddEntry(dirEntry) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
entryOut := out.AddDirLookupEntry(dirEntry)
|
||||
entryOut := out.AddEntryPlus(dirEntry)
|
||||
if entryOut == nil {
|
||||
return false
|
||||
}
|
||||
@@ -203,14 +203,14 @@ func (wfs *WFS) doReadDirectory(input *fuse.ReadIn, out *fuse.DirEntryList, isPl
|
||||
if input.Offset < directoryStreamBaseOffset {
|
||||
if !isPlusMode {
|
||||
if input.Offset == 0 {
|
||||
out.AddDirEntry(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".", Off: 1})
|
||||
out.AddEntry(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".", Off: 1})
|
||||
}
|
||||
out.AddDirEntry(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "..", Off: 2})
|
||||
out.AddEntry(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "..", Off: 2})
|
||||
} else {
|
||||
if input.Offset == 0 {
|
||||
out.AddDirLookupEntry(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".", Off: 1})
|
||||
out.AddEntryPlus(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".", Off: 1})
|
||||
}
|
||||
out.AddDirLookupEntry(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "..", Off: 2})
|
||||
out.AddEntryPlus(fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "..", Off: 2})
|
||||
}
|
||||
input.Offset = directoryStreamBaseOffset
|
||||
}
|
||||
@@ -296,7 +296,7 @@ func (wfs *WFS) doReadDirectory(input *fuse.ReadIn, out *fuse.DirEntryList, isPl
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
func (wfs *WFS) readDirectoryDirect(input *fuse.ReadIn, out *fuse.DirEntryList, dh *DirectoryHandle, dirPath util.FullPath, processEachEntryFn func(entry *filer.Entry, index int64) bool) fuse.Status {
|
||||
func (wfs *WFS) readDirectoryDirect(input *fuse.ReadIn, out DirEntrySink, dh *DirectoryHandle, dirPath util.FullPath, processEachEntryFn func(entry *filer.Entry, index int64) bool) fuse.Status {
|
||||
var lastEntryName string
|
||||
|
||||
if input.Offset >= dh.entryStreamOffset {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
)
|
||||
|
||||
@@ -56,7 +54,7 @@ func (wfs *WFS) SetLkw(cancel <-chan struct{}, in *fuse.LkIn) fuse.Status {
|
||||
Pid: in.Lk.Pid,
|
||||
IsFlock: in.LkFlags&fuse.FUSE_LK_FLOCK != 0,
|
||||
}
|
||||
if lk.Typ == syscall.F_UNLCK {
|
||||
if lk.Typ == f_UNLCK {
|
||||
return wfs.posixLocks.SetLk(in.NodeId, lk)
|
||||
}
|
||||
return wfs.posixLocks.SetLkw(in.NodeId, lk, cancel)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/hex"
|
||||
"math/rand/v2"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
@@ -103,9 +102,9 @@ func (wfs *WFS) posixLockKeyForInode(inode uint64) (string, bool) {
|
||||
|
||||
func posixLockTypeToWire(typ uint32) uint32 {
|
||||
switch typ {
|
||||
case syscall.F_RDLCK:
|
||||
case f_RDLCK:
|
||||
return posixlock.Read
|
||||
case syscall.F_WRLCK:
|
||||
case f_WRLCK:
|
||||
return posixlock.Write
|
||||
default:
|
||||
return posixlock.Unlock
|
||||
@@ -115,11 +114,11 @@ func posixLockTypeToWire(typ uint32) uint32 {
|
||||
func posixLockTypeFromWire(typ uint32) uint32 {
|
||||
switch typ {
|
||||
case posixlock.Read:
|
||||
return syscall.F_RDLCK
|
||||
return f_RDLCK
|
||||
case posixlock.Write:
|
||||
return syscall.F_WRLCK
|
||||
return f_WRLCK
|
||||
default:
|
||||
return syscall.F_UNLCK
|
||||
return f_UNLCK
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +185,7 @@ func (wfs *WFS) routedGetLk(cancel <-chan struct{}, in *fuse.LkIn, out *fuse.LkO
|
||||
out.Lk.Start, out.Lk.End, out.Lk.Pid = c.GetStart(), c.GetEnd(), c.GetPid()
|
||||
out.Lk.Typ = posixLockTypeFromWire(c.GetType())
|
||||
} else {
|
||||
out.Lk.Typ = syscall.F_UNLCK
|
||||
out.Lk.Typ = f_UNLCK
|
||||
}
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -123,11 +122,11 @@ func (wfs *WFS) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr st
|
||||
}
|
||||
_, exists := entry.Extended[XATTR_PREFIX+attr]
|
||||
switch input.Flags {
|
||||
case sys.XATTR_CREATE:
|
||||
case xattr_CREATE:
|
||||
if exists {
|
||||
return fuse.Status(syscall.EEXIST)
|
||||
}
|
||||
case sys.XATTR_REPLACE:
|
||||
case xattr_REPLACE:
|
||||
if !exists {
|
||||
return fuse.ENODATA
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user