Files
seaweedfs/weed/filer/posixlock/codec.go
T
Chris Lu 475ae2b443 filer: serialize the POSIX lock set for entry metadata (phase 2) (#9661)
* filer: POSIX advisory lock set primitive (phase 1)

Pure per-inode conflict/coalesce/range-split logic for fcntl byte-range
and flock whole-file locks, extracted from the mount's PosixLockTable
without its wait queue or inode-map concurrency. Owner identity is
(Sid, Owner) so the same FUSE owner on different mounts never aliases,
and ReleaseSession reaps a dead mount's locks. The owner filer will hold
one Set per inode under the per-path lock; no concurrency control here.

* test: tolerate transient FUSE invisibility in ConcurrentReadWrite

A concurrent truncating overwrite leaves a short-lived dentry/cache window
where the file is momentarily ENOENT to another opener. Retry the reads and
writes a few times before failing, as ConcurrentDirectoryOperations does.

* filer: serialize the POSIX lock set for entry metadata

Versioned fixed-width binary encoding of a Set, so an inode's held locks
can ride in its entry metadata: a lock op materializes the Set from the
blob, applies under the per-path lock, and writes it back. Empty set
encodes to nil so a lock-free inode carries no blob.

* filer: encode the POSIX lock set as protobuf

Replace the hand-rolled fixed-width codec with a LockSetProto message, so the
metadata blob can gain fields without a format-version migration. proto.Unmarshal
already rejects a malformed blob, so the explicit version and length checks go
away. Marshal now returns an error to match.
2026-05-24 22:33:36 -07:00

59 lines
1.5 KiB
Go

package posixlock
import (
"fmt"
"google.golang.org/protobuf/proto"
)
// The held lock set rides in an inode's entry metadata, so the wire format must
// stay backward compatible across filer versions. It is a protobuf message
// (LockSetProto), so new fields can be added without breaking old readers.
// Marshal encodes the held locks for storage in the inode's entry metadata. A
// lock-free set encodes to nil, so an inode that holds no locks carries no blob.
func (s *Set) Marshal() ([]byte, error) {
if len(s.locks) == 0 {
return nil, nil
}
pb := &LockSetProto{Locks: make([]*LockProto, len(s.locks))}
for i, l := range s.locks {
pb.Locks[i] = &LockProto{
Start: l.Start,
End: l.End,
Type: l.Type,
Sid: l.Sid,
Owner: l.Owner,
Pid: l.Pid,
IsFlock: l.IsFlock,
}
}
return proto.Marshal(pb)
}
// Unmarshal decodes a Set produced by Marshal. Empty input is an empty set, so a
// missing metadata key reads back as "no locks held". A malformed blob is
// rejected rather than silently misread.
func Unmarshal(b []byte) (*Set, error) {
if len(b) == 0 {
return &Set{}, nil
}
pb := &LockSetProto{}
if err := proto.Unmarshal(b, pb); err != nil {
return nil, fmt.Errorf("posixlock: %w", err)
}
locks := make([]Range, len(pb.Locks))
for i, l := range pb.Locks {
locks[i] = Range{
Start: l.Start,
End: l.End,
Type: l.Type,
Sid: l.Sid,
Owner: l.Owner,
Pid: l.Pid,
IsFlock: l.IsFlock,
}
}
return &Set{locks: locks}, nil
}