Implement POSIX file locking for FUSE mount (#8750)
* Add POSIX byte-range lock table for FUSE mount Implement PosixLockTable with per-inode range lock tracking supporting: - Shared (F_RDLCK) and exclusive (F_WRLCK) byte-range locks - Conflict detection across different lock owners - Lock coalescing for adjacent/overlapping same-owner same-type locks - Lock splitting on partial-range unlock - Blocking waiter support for SetLkw with cancellation - Owner-based cleanup for Release * Wire POSIX lock handlers into FUSE mount Implement GetLk, SetLk, SetLkw on WFS delegating to PosixLockTable. Add posixLocks field to WFS and initialize in constructor. Clean up locks on Release via ReleaseOwner using ReleaseIn.LockOwner. Remove ENOSYS stubs from weedfs_unsupported.go. * Enable POSIX and flock lock capabilities in FUSE mount Set EnableLocks: true in mount options to advertise CAP_POSIX_LOCKS and CAP_FLOCK_LOCKS during FUSE INIT. * Avoid thundering herd in lock waiter wake-up Replace broadcast-all wakeWaiters with selective wakeEligibleWaiters that checks each waiter's requested lock against remaining held locks. Only waiters whose request no longer conflicts are woken; others stay queued. Store the requested lockRange in each lockWaiter to enable this. * Fix uint64 overflow in adjacency check for lock coalescing Guard h.End+1 and lk.End+1 with < ^uint64(0) checks so that End == math.MaxUint64 (EOF) does not wrap to 0 and falsely merge non-adjacent locks. * Add test for non-adjacent ranges with gap not being coalesced
This commit is contained in:
51
weed/mount/weedfs_file_lock.go
Normal file
51
weed/mount/weedfs_file_lock.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
)
|
||||
|
||||
// GetLk queries for a conflicting lock on the file.
|
||||
// If a conflict exists, the conflicting lock is returned in out.
|
||||
// If no conflict, out.Lk.Typ is set to F_UNLCK.
|
||||
func (wfs *WFS) GetLk(cancel <-chan struct{}, in *fuse.LkIn, out *fuse.LkOut) fuse.Status {
|
||||
proposed := lockRange{
|
||||
Start: in.Lk.Start,
|
||||
End: in.Lk.End,
|
||||
Typ: in.Lk.Typ,
|
||||
Owner: in.Owner,
|
||||
Pid: in.Lk.Pid,
|
||||
}
|
||||
wfs.posixLocks.GetLk(in.NodeId, proposed, out)
|
||||
return fuse.OK
|
||||
}
|
||||
|
||||
// SetLk sets or clears a POSIX lock (non-blocking).
|
||||
// Returns EAGAIN if the lock conflicts with an existing lock from another owner.
|
||||
func (wfs *WFS) SetLk(cancel <-chan struct{}, in *fuse.LkIn) fuse.Status {
|
||||
lk := lockRange{
|
||||
Start: in.Lk.Start,
|
||||
End: in.Lk.End,
|
||||
Typ: in.Lk.Typ,
|
||||
Owner: in.Owner,
|
||||
Pid: in.Lk.Pid,
|
||||
}
|
||||
return wfs.posixLocks.SetLk(in.NodeId, lk)
|
||||
}
|
||||
|
||||
// SetLkw sets a POSIX lock (blocking).
|
||||
// Waits until the lock can be acquired or the request is cancelled.
|
||||
func (wfs *WFS) SetLkw(cancel <-chan struct{}, in *fuse.LkIn) fuse.Status {
|
||||
lk := lockRange{
|
||||
Start: in.Lk.Start,
|
||||
End: in.Lk.End,
|
||||
Typ: in.Lk.Typ,
|
||||
Owner: in.Owner,
|
||||
Pid: in.Lk.Pid,
|
||||
}
|
||||
if lk.Typ == syscall.F_UNLCK {
|
||||
return wfs.posixLocks.SetLk(in.NodeId, lk)
|
||||
}
|
||||
return wfs.posixLocks.SetLkw(in.NodeId, lk, cancel)
|
||||
}
|
||||
Reference in New Issue
Block a user