mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
@@ -21,6 +21,7 @@ const (
|
||||
F_SETFD = 2
|
||||
F_GETFL = 3
|
||||
F_SETFL = 4
|
||||
F_GETLK = 5
|
||||
F_SETLK = 6
|
||||
F_SETLKW = 7
|
||||
F_SETOWN = 8
|
||||
@@ -55,7 +56,7 @@ type Flock struct {
|
||||
_ [4]byte
|
||||
Start int64
|
||||
Len int64
|
||||
Pid int32
|
||||
PID int32
|
||||
_ [4]byte
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ go_library(
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/log",
|
||||
"//pkg/sync",
|
||||
"//pkg/waiter",
|
||||
|
||||
+131
-22
@@ -54,6 +54,8 @@ import (
|
||||
"math"
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
@@ -83,6 +85,17 @@ const (
|
||||
// offset 0 to LockEOF.
|
||||
const LockEOF = math.MaxUint64
|
||||
|
||||
// OwnerInfo describes the owner of a lock.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/5264): We may need to add other fields in the future
|
||||
// (e.g., Linux's file_lock.fl_flags to support open file-descriptor locks).
|
||||
//
|
||||
// +stateify savable
|
||||
type OwnerInfo struct {
|
||||
// PID is the process ID of the lock owner.
|
||||
PID int32
|
||||
}
|
||||
|
||||
// Lock is a regional file lock. It consists of either a single writer
|
||||
// or a set of readers.
|
||||
//
|
||||
@@ -92,14 +105,20 @@ const LockEOF = math.MaxUint64
|
||||
// A Lock may be downgraded from a write lock to a read lock only if
|
||||
// the write lock's uid is the same as the read lock.
|
||||
//
|
||||
// Accesses to Lock are synchronized through the Locks object to which it
|
||||
// belongs.
|
||||
//
|
||||
// +stateify savable
|
||||
type Lock struct {
|
||||
// Readers are the set of read lock holders identified by UniqueID.
|
||||
// If len(Readers) > 0 then HasWriter must be false.
|
||||
Readers map[UniqueID]bool
|
||||
// If len(Readers) > 0 then Writer must be nil.
|
||||
Readers map[UniqueID]OwnerInfo
|
||||
|
||||
// Writer holds the writer unique ID. It's nil if there are no writers.
|
||||
Writer UniqueID
|
||||
|
||||
// WriterInfo describes the writer. It is only meaningful if Writer != nil.
|
||||
WriterInfo OwnerInfo
|
||||
}
|
||||
|
||||
// Locks is a thread-safe wrapper around a LockSet.
|
||||
@@ -135,14 +154,14 @@ const (
|
||||
// acquiring the lock in a non-blocking mode or "interrupted" if in a blocking mode.
|
||||
// Blocker is the interface used to provide blocking behavior, passing a nil Blocker
|
||||
// will result in non-blocking behavior.
|
||||
func (l *Locks) LockRegion(uid UniqueID, t LockType, r LockRange, block Blocker) bool {
|
||||
func (l *Locks) LockRegion(uid UniqueID, ownerPID int32, t LockType, r LockRange, block Blocker) bool {
|
||||
for {
|
||||
l.mu.Lock()
|
||||
|
||||
// Blocking locks must run in a loop because we'll be woken up whenever an unlock event
|
||||
// happens for this lock. We will then attempt to take the lock again and if it fails
|
||||
// continue blocking.
|
||||
res := l.locks.lock(uid, t, r)
|
||||
res := l.locks.lock(uid, ownerPID, t, r)
|
||||
if !res && block != nil {
|
||||
e, ch := waiter.NewChannelEntry(nil)
|
||||
l.blockedQueue.EventRegister(&e, EventMaskAll)
|
||||
@@ -161,6 +180,14 @@ func (l *Locks) LockRegion(uid UniqueID, t LockType, r LockRange, block Blocker)
|
||||
}
|
||||
}
|
||||
|
||||
// LockRegionVFS1 is a wrapper around LockRegion for VFS1, which does not implement
|
||||
// F_GETLK (and does not care about storing PIDs as a result).
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1624): Delete.
|
||||
func (l *Locks) LockRegionVFS1(uid UniqueID, t LockType, r LockRange, block Blocker) bool {
|
||||
return l.LockRegion(uid, 0 /* ownerPID */, t, r, block)
|
||||
}
|
||||
|
||||
// UnlockRegion attempts to release a lock for the uid on a region of a file.
|
||||
// This operation is always successful, even if there did not exist a lock on
|
||||
// the requested region held by uid in the first place.
|
||||
@@ -175,13 +202,14 @@ func (l *Locks) UnlockRegion(uid UniqueID, r LockRange) {
|
||||
|
||||
// makeLock returns a new typed Lock that has either uid as its only reader
|
||||
// or uid as its only writer.
|
||||
func makeLock(uid UniqueID, t LockType) Lock {
|
||||
value := Lock{Readers: make(map[UniqueID]bool)}
|
||||
func makeLock(uid UniqueID, ownerPID int32, t LockType) Lock {
|
||||
value := Lock{Readers: make(map[UniqueID]OwnerInfo)}
|
||||
switch t {
|
||||
case ReadLock:
|
||||
value.Readers[uid] = true
|
||||
value.Readers[uid] = OwnerInfo{PID: ownerPID}
|
||||
case WriteLock:
|
||||
value.Writer = uid
|
||||
value.WriterInfo = OwnerInfo{PID: ownerPID}
|
||||
default:
|
||||
panic(fmt.Sprintf("makeLock: invalid lock type %d", t))
|
||||
}
|
||||
@@ -190,17 +218,20 @@ func makeLock(uid UniqueID, t LockType) Lock {
|
||||
|
||||
// isHeld returns true if uid is a holder of Lock.
|
||||
func (l Lock) isHeld(uid UniqueID) bool {
|
||||
return l.Writer == uid || l.Readers[uid]
|
||||
if _, ok := l.Readers[uid]; ok {
|
||||
return true
|
||||
}
|
||||
return l.Writer == uid
|
||||
}
|
||||
|
||||
// lock sets uid as a holder of a typed lock on Lock.
|
||||
//
|
||||
// Preconditions: canLock is true for the range containing this Lock.
|
||||
func (l *Lock) lock(uid UniqueID, t LockType) {
|
||||
func (l *Lock) lock(uid UniqueID, ownerPID int32, t LockType) {
|
||||
switch t {
|
||||
case ReadLock:
|
||||
// If we are already a reader, then this is a no-op.
|
||||
if l.Readers[uid] {
|
||||
if _, ok := l.Readers[uid]; ok {
|
||||
return
|
||||
}
|
||||
// We cannot downgrade a write lock to a read lock unless the
|
||||
@@ -210,11 +241,11 @@ func (l *Lock) lock(uid UniqueID, t LockType) {
|
||||
panic(fmt.Sprintf("lock: cannot downgrade write lock to read lock for uid %d, writer is %d", uid, l.Writer))
|
||||
}
|
||||
// Ensure that there is only one reader if upgrading.
|
||||
l.Readers = make(map[UniqueID]bool)
|
||||
l.Readers = make(map[UniqueID]OwnerInfo)
|
||||
// Ensure that there is no longer a writer.
|
||||
l.Writer = nil
|
||||
}
|
||||
l.Readers[uid] = true
|
||||
l.Readers[uid] = OwnerInfo{PID: ownerPID}
|
||||
return
|
||||
case WriteLock:
|
||||
// If we are already the writer, then this is a no-op.
|
||||
@@ -228,13 +259,14 @@ func (l *Lock) lock(uid UniqueID, t LockType) {
|
||||
if readers != 1 {
|
||||
panic(fmt.Sprintf("lock: cannot upgrade read lock to write lock for uid %d, too many readers %v", uid, l.Readers))
|
||||
}
|
||||
if !l.Readers[uid] {
|
||||
if _, ok := l.Readers[uid]; !ok {
|
||||
panic(fmt.Sprintf("lock: cannot upgrade read lock to write lock for uid %d, conflicting reader %v", uid, l.Readers))
|
||||
}
|
||||
}
|
||||
// Ensure that there is only a writer.
|
||||
l.Readers = make(map[UniqueID]bool)
|
||||
l.Readers = make(map[UniqueID]OwnerInfo)
|
||||
l.Writer = uid
|
||||
l.WriterInfo = OwnerInfo{PID: ownerPID}
|
||||
default:
|
||||
panic(fmt.Sprintf("lock: invalid lock type %d", t))
|
||||
}
|
||||
@@ -247,7 +279,7 @@ func (l LockSet) lockable(r LockRange, check func(value Lock) bool) bool {
|
||||
// Get our starting point.
|
||||
seg := l.LowerBoundSegment(r.Start)
|
||||
for seg.Ok() && seg.Start() < r.End {
|
||||
// Note that we don't care about overruning the end of the
|
||||
// Note that we don't care about overrunning the end of the
|
||||
// last segment because if everything checks out we'll just
|
||||
// split the last segment.
|
||||
if !check(seg.Value()) {
|
||||
@@ -281,7 +313,7 @@ func (l LockSet) canLock(uid UniqueID, t LockType, r LockRange) bool {
|
||||
if value.Writer == nil {
|
||||
// Then this uid can only take a write lock if this is a private
|
||||
// upgrade, meaning that the only reader is uid.
|
||||
return len(value.Readers) == 1 && value.Readers[uid]
|
||||
return value.isOnlyReader(uid)
|
||||
}
|
||||
// If the uid is already a writer on this region, then
|
||||
// adding a write lock would be a no-op.
|
||||
@@ -292,11 +324,19 @@ func (l LockSet) canLock(uid UniqueID, t LockType, r LockRange) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Lock) isOnlyReader(uid UniqueID) bool {
|
||||
if len(l.Readers) != 1 {
|
||||
return false
|
||||
}
|
||||
_, ok := l.Readers[uid]
|
||||
return ok
|
||||
}
|
||||
|
||||
// lock returns true if uid took a lock of type t on the entire range of
|
||||
// LockRange.
|
||||
//
|
||||
// Preconditions: r.Start <= r.End (will panic otherwise).
|
||||
func (l *LockSet) lock(uid UniqueID, t LockType, r LockRange) bool {
|
||||
func (l *LockSet) lock(uid UniqueID, ownerPID int32, t LockType, r LockRange) bool {
|
||||
if r.Start > r.End {
|
||||
panic(fmt.Sprintf("lock: r.Start %d > r.End %d", r.Start, r.End))
|
||||
}
|
||||
@@ -317,7 +357,7 @@ func (l *LockSet) lock(uid UniqueID, t LockType, r LockRange) bool {
|
||||
seg, gap := l.Find(r.Start)
|
||||
if gap.Ok() {
|
||||
// Fill in the gap and get the next segment to modify.
|
||||
seg = l.Insert(gap, gap.Range().Intersect(r), makeLock(uid, t)).NextSegment()
|
||||
seg = l.Insert(gap, gap.Range().Intersect(r), makeLock(uid, ownerPID, t)).NextSegment()
|
||||
} else if seg.Start() < r.Start {
|
||||
// Get our first segment to modify.
|
||||
_, seg = l.Split(seg, r.Start)
|
||||
@@ -331,12 +371,12 @@ func (l *LockSet) lock(uid UniqueID, t LockType, r LockRange) bool {
|
||||
// Set the lock on the segment. This is guaranteed to
|
||||
// always be safe, given canLock above.
|
||||
value := seg.ValuePtr()
|
||||
value.lock(uid, t)
|
||||
value.lock(uid, ownerPID, t)
|
||||
|
||||
// Fill subsequent gaps.
|
||||
gap = seg.NextGap()
|
||||
if gr := gap.Range().Intersect(r); gr.Length() > 0 {
|
||||
seg = l.Insert(gap, gr, makeLock(uid, t)).NextSegment()
|
||||
seg = l.Insert(gap, gr, makeLock(uid, ownerPID, t)).NextSegment()
|
||||
} else {
|
||||
seg = gap.NextSegment()
|
||||
}
|
||||
@@ -380,7 +420,7 @@ func (l *LockSet) unlock(uid UniqueID, r LockRange) {
|
||||
// only ever be one writer and no readers, then this
|
||||
// lock should always be removed from the set.
|
||||
remove = true
|
||||
} else if value.Readers[uid] {
|
||||
} else if _, ok := value.Readers[uid]; ok {
|
||||
// If uid is the last reader, then just remove the entire
|
||||
// segment.
|
||||
if len(value.Readers) == 1 {
|
||||
@@ -390,7 +430,7 @@ func (l *LockSet) unlock(uid UniqueID, r LockRange) {
|
||||
// affecting any other segment's readers. To do
|
||||
// this, we need to make a copy of the Readers map
|
||||
// and not add this uid.
|
||||
newValue := Lock{Readers: make(map[UniqueID]bool)}
|
||||
newValue := Lock{Readers: make(map[UniqueID]OwnerInfo)}
|
||||
for k, v := range value.Readers {
|
||||
if k != uid {
|
||||
newValue.Readers[k] = v
|
||||
@@ -451,3 +491,72 @@ func ComputeRange(start, length, offset int64) (LockRange, error) {
|
||||
// Offset is guaranteed to be positive at this point.
|
||||
return LockRange{Start: uint64(offset), End: end}, nil
|
||||
}
|
||||
|
||||
// TestRegion checks whether the lock holder identified by uid can hold a lock
|
||||
// of type t on range r. It returns a Flock struct representing this
|
||||
// information as the F_GETLK fcntl does.
|
||||
//
|
||||
// Note that the PID returned in the flock structure is relative to the root PID
|
||||
// namespace. It needs to be converted to the caller's PID namespace before
|
||||
// returning to userspace.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/5264): we don't support OFD locks through fcntl, which
|
||||
// would return a struct with pid = -1.
|
||||
func (l *Locks) TestRegion(ctx context.Context, uid UniqueID, t LockType, r LockRange) linux.Flock {
|
||||
f := linux.Flock{Type: linux.F_UNLCK}
|
||||
switch t {
|
||||
case ReadLock:
|
||||
l.testRegion(r, func(lock Lock, start, length uint64) bool {
|
||||
if lock.Writer == nil || lock.Writer == uid {
|
||||
return true
|
||||
}
|
||||
f.Type = linux.F_WRLCK
|
||||
f.PID = lock.WriterInfo.PID
|
||||
f.Start = int64(start)
|
||||
f.Len = int64(length)
|
||||
return false
|
||||
})
|
||||
case WriteLock:
|
||||
l.testRegion(r, func(lock Lock, start, length uint64) bool {
|
||||
if lock.Writer == nil {
|
||||
for k, v := range lock.Readers {
|
||||
if k != uid {
|
||||
// Stop at the first conflict detected.
|
||||
f.Type = linux.F_RDLCK
|
||||
f.PID = v.PID
|
||||
f.Start = int64(start)
|
||||
f.Len = int64(length)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if lock.Writer == uid {
|
||||
return true
|
||||
}
|
||||
f.Type = linux.F_WRLCK
|
||||
f.PID = lock.WriterInfo.PID
|
||||
f.Start = int64(start)
|
||||
f.Len = int64(length)
|
||||
return false
|
||||
})
|
||||
default:
|
||||
panic(fmt.Sprintf("TestRegion: invalid lock type %d", t))
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (l *Locks) testRegion(r LockRange, check func(lock Lock, start, length uint64) bool) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
seg := l.locks.LowerBoundSegment(r.Start)
|
||||
for seg.Ok() && seg.Start() < r.End {
|
||||
lock := seg.Value()
|
||||
if !check(lock, seg.Start(), seg.End()-seg.Start()) {
|
||||
// Stop at the first conflict detected.
|
||||
return
|
||||
}
|
||||
seg = seg.NextSegment()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (lockSetFunctions) Merge(r1 LockRange, val1 Lock, r2 LockRange, val2 Lock)
|
||||
return Lock{}, false
|
||||
}
|
||||
for k := range val1.Readers {
|
||||
if !val2.Readers[k] {
|
||||
if _, ok := val2.Readers[k]; !ok {
|
||||
return Lock{}, false
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,12 @@ func (lockSetFunctions) Merge(r1 LockRange, val1 Lock, r2 LockRange, val2 Lock)
|
||||
func (lockSetFunctions) Split(r LockRange, val Lock, split uint64) (Lock, Lock) {
|
||||
// Copy the segment so that split segments don't contain map references
|
||||
// to other segments.
|
||||
val0 := Lock{Readers: make(map[UniqueID]bool)}
|
||||
val0 := Lock{Readers: make(map[UniqueID]OwnerInfo)}
|
||||
for k, v := range val.Readers {
|
||||
val0.Readers[k] = v
|
||||
}
|
||||
val0.Writer = val.Writer
|
||||
val0.WriterInfo = val.WriterInfo
|
||||
|
||||
return val, val0
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ func equals(e0, e1 []entry) bool {
|
||||
}
|
||||
for i := range e0 {
|
||||
for k := range e0[i].Lock.Readers {
|
||||
if !e1[i].Lock.Readers[k] {
|
||||
if _, ok := e1[i].Lock.Readers[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k := range e1[i].Lock.Readers {
|
||||
if !e0[i].Lock.Readers[k] {
|
||||
if _, ok := e0[i].Lock.Readers[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -90,15 +90,15 @@ func TestCanLock(t *testing.T) {
|
||||
// 0 1024 2048 3072 4096
|
||||
l := fill([]entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, 2048},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true, 3: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}, 3: OwnerInfo{}}},
|
||||
LockRange: LockRange{2048, 3072},
|
||||
},
|
||||
{
|
||||
@@ -220,7 +220,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -266,7 +266,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 4096},
|
||||
},
|
||||
{
|
||||
@@ -283,7 +283,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -302,7 +302,7 @@ func TestSetLock(t *testing.T) {
|
||||
LockRange: LockRange{0, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -333,7 +333,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -351,7 +351,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -366,11 +366,11 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -383,7 +383,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -398,15 +398,15 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 4096 8192 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, 8192},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{8192, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -419,7 +419,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -434,7 +434,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -447,7 +447,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 4096
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 4096},
|
||||
},
|
||||
},
|
||||
@@ -467,11 +467,11 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -484,7 +484,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -499,15 +499,15 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -520,15 +520,15 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 2048 4096 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, 2048},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -543,7 +543,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
@@ -551,7 +551,7 @@ func TestSetLock(t *testing.T) {
|
||||
LockRange: LockRange{1024, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -564,15 +564,15 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 2048 4096 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, 2048},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -587,7 +587,7 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 3072 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
@@ -595,7 +595,7 @@ func TestSetLock(t *testing.T) {
|
||||
LockRange: LockRange{1024, 3072},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -608,11 +608,11 @@ func TestSetLock(t *testing.T) {
|
||||
// 0 1024 2048
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, 2048},
|
||||
},
|
||||
},
|
||||
@@ -634,15 +634,15 @@ func TestSetLock(t *testing.T) {
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, 2048},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{2048, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -676,7 +676,7 @@ func TestSetLock(t *testing.T) {
|
||||
l := fill(test.before)
|
||||
|
||||
r := LockRange{Start: test.start, End: test.end}
|
||||
success := l.lock(test.uid, test.lockType, r)
|
||||
success := l.lock(test.uid, 0 /* ownerPID */, test.lockType, r)
|
||||
var got []entry
|
||||
for seg := l.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
|
||||
got = append(got, entry{
|
||||
@@ -739,7 +739,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -752,7 +752,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -765,7 +765,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -797,7 +797,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -810,7 +810,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -849,7 +849,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -862,7 +862,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 4096
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 4096},
|
||||
},
|
||||
},
|
||||
@@ -901,7 +901,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 1024 4096 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
@@ -909,7 +909,7 @@ func TestUnlock(t *testing.T) {
|
||||
LockRange: LockRange{1024, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -922,11 +922,11 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 1024 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -939,7 +939,7 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 max uint64
|
||||
before: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -952,15 +952,15 @@ func TestUnlock(t *testing.T) {
|
||||
// 0 1024 4096 max uint64
|
||||
after: []entry{
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{1024, 4096},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}, 2: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -977,7 +977,7 @@ func TestUnlock(t *testing.T) {
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -994,7 +994,7 @@ func TestUnlock(t *testing.T) {
|
||||
LockRange: LockRange{0, 8},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -1011,7 +1011,7 @@ func TestUnlock(t *testing.T) {
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, LockEOF},
|
||||
},
|
||||
},
|
||||
@@ -1028,11 +1028,11 @@ func TestUnlock(t *testing.T) {
|
||||
LockRange: LockRange{0, 1024},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{1: OwnerInfo{}}},
|
||||
LockRange: LockRange{4096, 8192},
|
||||
},
|
||||
{
|
||||
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
||||
Lock: Lock{Readers: map[UniqueID]OwnerInfo{0: OwnerInfo{}, 1: OwnerInfo{}}},
|
||||
LockRange: LockRange{8192, LockEOF},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1944,19 +1944,19 @@ func (fd *fileDescription) RemoveXattr(ctx context.Context, name string) error {
|
||||
}
|
||||
|
||||
// LockBSD implements vfs.FileDescriptionImpl.LockBSD.
|
||||
func (fd *fileDescription) LockBSD(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, block fslock.Blocker) error {
|
||||
func (fd *fileDescription) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error {
|
||||
fd.lockLogging.Do(func() {
|
||||
log.Infof("File lock using gofer file handled internally.")
|
||||
})
|
||||
return fd.LockFD.LockBSD(ctx, uid, t, block)
|
||||
return fd.LockFD.LockBSD(ctx, uid, ownerPID, t, block)
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
func (fd *fileDescription) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
func (fd *fileDescription) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
fd.lockLogging.Do(func() {
|
||||
log.Infof("Range lock using gofer file handled internally.")
|
||||
})
|
||||
return fd.Locks().LockPOSIX(ctx, uid, t, r, block)
|
||||
return fd.Locks().LockPOSIX(ctx, uid, ownerPID, t, r, block)
|
||||
}
|
||||
|
||||
// UnlockPOSIX implements vfs.FileDescriptionImpl.UnlockPOSIX.
|
||||
|
||||
@@ -140,32 +140,32 @@ func TestLocks(t *testing.T) {
|
||||
|
||||
uid1 := 123
|
||||
uid2 := 456
|
||||
if err := fd.Impl().LockBSD(ctx, uid1, lock.ReadLock, nil); err != nil {
|
||||
if err := fd.Impl().LockBSD(ctx, uid1, 0 /* ownerPID */, lock.ReadLock, nil); err != nil {
|
||||
t.Fatalf("fd.Impl().LockBSD failed: err = %v", err)
|
||||
}
|
||||
if err := fd.Impl().LockBSD(ctx, uid2, lock.ReadLock, nil); err != nil {
|
||||
if err := fd.Impl().LockBSD(ctx, uid2, 0 /* ownerPID */, lock.ReadLock, nil); err != nil {
|
||||
t.Fatalf("fd.Impl().LockBSD failed: err = %v", err)
|
||||
}
|
||||
if got, want := fd.Impl().LockBSD(ctx, uid2, lock.WriteLock, nil), syserror.ErrWouldBlock; got != want {
|
||||
if got, want := fd.Impl().LockBSD(ctx, uid2, 0 /* ownerPID */, lock.WriteLock, nil), syserror.ErrWouldBlock; got != want {
|
||||
t.Fatalf("fd.Impl().LockBSD failed: got = %v, want = %v", got, want)
|
||||
}
|
||||
if err := fd.Impl().UnlockBSD(ctx, uid1); err != nil {
|
||||
t.Fatalf("fd.Impl().UnlockBSD failed: err = %v", err)
|
||||
}
|
||||
if err := fd.Impl().LockBSD(ctx, uid2, lock.WriteLock, nil); err != nil {
|
||||
if err := fd.Impl().LockBSD(ctx, uid2, 0 /* ownerPID */, lock.WriteLock, nil); err != nil {
|
||||
t.Fatalf("fd.Impl().LockBSD failed: err = %v", err)
|
||||
}
|
||||
|
||||
if err := fd.Impl().LockPOSIX(ctx, uid1, lock.ReadLock, lock.LockRange{Start: 0, End: 1}, nil); err != nil {
|
||||
if err := fd.Impl().LockPOSIX(ctx, uid1, 0 /* ownerPID */, lock.ReadLock, lock.LockRange{Start: 0, End: 1}, nil); err != nil {
|
||||
t.Fatalf("fd.Impl().LockPOSIX failed: err = %v", err)
|
||||
}
|
||||
if err := fd.Impl().LockPOSIX(ctx, uid2, lock.ReadLock, lock.LockRange{Start: 1, End: 2}, nil); err != nil {
|
||||
if err := fd.Impl().LockPOSIX(ctx, uid2, 0 /* ownerPID */, lock.ReadLock, lock.LockRange{Start: 1, End: 2}, nil); err != nil {
|
||||
t.Fatalf("fd.Impl().LockPOSIX failed: err = %v", err)
|
||||
}
|
||||
if err := fd.Impl().LockPOSIX(ctx, uid1, lock.WriteLock, lock.LockRange{Start: 0, End: 1}, nil); err != nil {
|
||||
if err := fd.Impl().LockPOSIX(ctx, uid1, 0 /* ownerPID */, lock.WriteLock, lock.LockRange{Start: 0, End: 1}, nil); err != nil {
|
||||
t.Fatalf("fd.Impl().LockPOSIX failed: err = %v", err)
|
||||
}
|
||||
if got, want := fd.Impl().LockPOSIX(ctx, uid2, lock.ReadLock, lock.LockRange{Start: 0, End: 1}, nil), syserror.ErrWouldBlock; got != want {
|
||||
if got, want := fd.Impl().LockPOSIX(ctx, uid2, 0 /* ownerPID */, lock.ReadLock, lock.LockRange{Start: 0, End: 1}, nil), syserror.ErrWouldBlock; got != want {
|
||||
t.Fatalf("fd.Impl().LockPOSIX failed: got = %v, want = %v", got, want)
|
||||
}
|
||||
if err := fd.Impl().UnlockPOSIX(ctx, uid1, lock.LockRange{Start: 0, End: 1}); err != nil {
|
||||
|
||||
@@ -1104,8 +1104,8 @@ func (fd *fileDescription) Write(ctx context.Context, src usermem.IOSequence, op
|
||||
}
|
||||
|
||||
// LockBSD implements vfs.FileDescriptionImpl.LockBSD.
|
||||
func (fd *fileDescription) LockBSD(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, block fslock.Blocker) error {
|
||||
return fd.lowerFD.LockBSD(ctx, t, block)
|
||||
func (fd *fileDescription) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error {
|
||||
return fd.lowerFD.LockBSD(ctx, ownerPID, t, block)
|
||||
}
|
||||
|
||||
// UnlockBSD implements vfs.FileDescriptionImpl.UnlockBSD.
|
||||
@@ -1114,8 +1114,8 @@ func (fd *fileDescription) UnlockBSD(ctx context.Context, uid fslock.UniqueID) e
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
func (fd *fileDescription) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
return fd.lowerFD.LockPOSIX(ctx, uid, t, r, block)
|
||||
func (fd *fileDescription) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
return fd.lowerFD.LockPOSIX(ctx, uid, ownerPID, t, r, block)
|
||||
}
|
||||
|
||||
// UnlockPOSIX implements vfs.FileDescriptionImpl.UnlockPOSIX.
|
||||
@@ -1123,6 +1123,11 @@ func (fd *fileDescription) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID,
|
||||
return fd.lowerFD.UnlockPOSIX(ctx, uid, r)
|
||||
}
|
||||
|
||||
// TestPOSIX implements vfs.FileDescriptionImpl.TestPOSIX.
|
||||
func (fd *fileDescription) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
|
||||
return fd.lowerFD.TestPOSIX(ctx, uid, t, r)
|
||||
}
|
||||
|
||||
// FileReadWriteSeeker is a helper struct to pass a vfs.FileDescription as
|
||||
// io.Reader/io.Writer/io.ReadSeeker/io.ReaderAt/io.WriterAt/etc.
|
||||
type FileReadWriteSeeker struct {
|
||||
|
||||
@@ -240,7 +240,6 @@ go_library(
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fs/timerfd",
|
||||
"//pkg/sentry/fsbridge",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/fsimpl/pipefs",
|
||||
"//pkg/sentry/fsimpl/sockfs",
|
||||
"//pkg/sentry/fsimpl/timerfd",
|
||||
|
||||
@@ -216,7 +216,7 @@ func (ns *PIDNamespace) TaskWithID(tid ThreadID) *Task {
|
||||
return t
|
||||
}
|
||||
|
||||
// ThreadGroupWithID returns the thread group lead by the task with thread ID
|
||||
// ThreadGroupWithID returns the thread group led by the task with thread ID
|
||||
// tid in PID namespace ns. If no task has that TID, or if the task with that
|
||||
// TID is not a thread group leader, ThreadGroupWithID returns nil.
|
||||
func (ns *PIDNamespace) ThreadGroupWithID(tid ThreadID) *ThreadGroup {
|
||||
@@ -292,6 +292,11 @@ func (ns *PIDNamespace) UserNamespace() *auth.UserNamespace {
|
||||
return ns.userns
|
||||
}
|
||||
|
||||
// Root returns the root PID namespace of ns.
|
||||
func (ns *PIDNamespace) Root() *PIDNamespace {
|
||||
return ns.owner.Root
|
||||
}
|
||||
|
||||
// A threadGroupNode defines the relationship between a thread group and the
|
||||
// rest of the system. Conceptually, threadGroupNode is data belonging to the
|
||||
// owning TaskSet, as if TaskSet contained a field `nodes
|
||||
@@ -485,3 +490,8 @@ func (t *Task) Parent() *Task {
|
||||
func (t *Task) ThreadID() ThreadID {
|
||||
return t.tg.pidns.IDOfTask(t)
|
||||
}
|
||||
|
||||
// TGIDInRoot returns t's TGID in the root PID namespace.
|
||||
func (t *Task) TGIDInRoot() ThreadID {
|
||||
return t.tg.pidns.owner.Root.IDOfThreadGroup(t.tg)
|
||||
}
|
||||
|
||||
@@ -1014,12 +1014,12 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
}
|
||||
if cmd == linux.F_SETLK {
|
||||
// Non-blocking lock, provide a nil lock.Blocker.
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegion(t.FDTable(), lock.ReadLock, rng, nil) {
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegionVFS1(t.FDTable(), lock.ReadLock, rng, nil) {
|
||||
return 0, nil, syserror.EAGAIN
|
||||
}
|
||||
} else {
|
||||
// Blocking lock, pass in the task to satisfy the lock.Blocker interface.
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegion(t.FDTable(), lock.ReadLock, rng, t) {
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegionVFS1(t.FDTable(), lock.ReadLock, rng, t) {
|
||||
return 0, nil, syserror.EINTR
|
||||
}
|
||||
}
|
||||
@@ -1030,12 +1030,12 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
}
|
||||
if cmd == linux.F_SETLK {
|
||||
// Non-blocking lock, provide a nil lock.Blocker.
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegion(t.FDTable(), lock.WriteLock, rng, nil) {
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegionVFS1(t.FDTable(), lock.WriteLock, rng, nil) {
|
||||
return 0, nil, syserror.EAGAIN
|
||||
}
|
||||
} else {
|
||||
// Blocking lock, pass in the task to satisfy the lock.Blocker interface.
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegion(t.FDTable(), lock.WriteLock, rng, t) {
|
||||
if !file.Dirent.Inode.LockCtx.Posix.LockRegionVFS1(t.FDTable(), lock.WriteLock, rng, t) {
|
||||
return 0, nil, syserror.EINTR
|
||||
}
|
||||
}
|
||||
@@ -2167,24 +2167,24 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
case linux.LOCK_EX:
|
||||
if nonblocking {
|
||||
// Since we're nonblocking we pass a nil lock.Blocker implementation.
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegion(file, lock.WriteLock, rng, nil) {
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegionVFS1(file, lock.WriteLock, rng, nil) {
|
||||
return 0, nil, syserror.EWOULDBLOCK
|
||||
}
|
||||
} else {
|
||||
// Because we're blocking we will pass the task to satisfy the lock.Blocker interface.
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegion(file, lock.WriteLock, rng, t) {
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegionVFS1(file, lock.WriteLock, rng, t) {
|
||||
return 0, nil, syserror.EINTR
|
||||
}
|
||||
}
|
||||
case linux.LOCK_SH:
|
||||
if nonblocking {
|
||||
// Since we're nonblocking we pass a nil lock.Blocker implementation.
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegion(file, lock.ReadLock, rng, nil) {
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegionVFS1(file, lock.ReadLock, rng, nil) {
|
||||
return 0, nil, syserror.EWOULDBLOCK
|
||||
}
|
||||
} else {
|
||||
// Because we're blocking we will pass the task to satisfy the lock.Blocker interface.
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegion(file, lock.ReadLock, rng, t) {
|
||||
if !file.Dirent.Inode.LockCtx.BSD.LockRegionVFS1(file, lock.ReadLock, rng, t) {
|
||||
return 0, nil, syserror.EINTR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +205,12 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
}
|
||||
err := tmpfs.AddSeals(file, args[2].Uint())
|
||||
return 0, nil, err
|
||||
case linux.F_SETLK, linux.F_SETLKW:
|
||||
return 0, nil, posixLock(t, args, file, cmd)
|
||||
case linux.F_SETLK:
|
||||
return 0, nil, posixLock(t, args, file, false /* blocking */)
|
||||
case linux.F_SETLKW:
|
||||
return 0, nil, posixLock(t, args, file, true /* blocking */)
|
||||
case linux.F_GETLK:
|
||||
return 0, nil, posixTestLock(t, args, file)
|
||||
case linux.F_GETSIG:
|
||||
a := file.AsyncHandler()
|
||||
if a == nil {
|
||||
@@ -292,7 +296,49 @@ func setAsyncOwner(t *kernel.Task, fd int, file *vfs.FileDescription, ownerType,
|
||||
}
|
||||
}
|
||||
|
||||
func posixLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescription, cmd int32) error {
|
||||
func posixTestLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescription) error {
|
||||
// Copy in the lock request.
|
||||
flockAddr := args[2].Pointer()
|
||||
var flock linux.Flock
|
||||
if _, err := flock.CopyIn(t, flockAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
var typ lock.LockType
|
||||
switch flock.Type {
|
||||
case linux.F_RDLCK:
|
||||
typ = lock.ReadLock
|
||||
case linux.F_WRLCK:
|
||||
typ = lock.WriteLock
|
||||
default:
|
||||
return syserror.EINVAL
|
||||
}
|
||||
r, err := file.ComputeLockRange(t, uint64(flock.Start), uint64(flock.Len), flock.Whence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newFlock, err := file.TestPOSIX(t, t.FDTable(), typ, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newFlock.PID = translatePID(t.PIDNamespace().Root(), t.PIDNamespace(), newFlock.PID)
|
||||
if _, err = newFlock.CopyOut(t, flockAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// translatePID translates a pid from one namespace to another. Note that this
|
||||
// may race with task termination/creation, in which case the original task
|
||||
// corresponding to pid may no longer exist. This is used to implement the
|
||||
// F_GETLK fcntl, which has the same potential race in Linux as well (i.e.,
|
||||
// there is no synchronization between retrieving the lock PID and translating
|
||||
// it). See fs/locks.c:posix_lock_to_flock.
|
||||
func translatePID(old, new *kernel.PIDNamespace, pid int32) int32 {
|
||||
return int32(new.IDOfTask(old.TaskWithID(kernel.ThreadID(pid))))
|
||||
}
|
||||
|
||||
func posixLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescription, blocking bool) error {
|
||||
// Copy in the lock request.
|
||||
flockAddr := args[2].Pointer()
|
||||
var flock linux.Flock
|
||||
@@ -301,7 +347,7 @@ func posixLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescrip
|
||||
}
|
||||
|
||||
var blocker lock.Blocker
|
||||
if cmd == linux.F_SETLKW {
|
||||
if blocking {
|
||||
blocker = t
|
||||
}
|
||||
|
||||
@@ -315,13 +361,13 @@ func posixLock(t *kernel.Task, args arch.SyscallArguments, file *vfs.FileDescrip
|
||||
if !file.IsReadable() {
|
||||
return syserror.EBADF
|
||||
}
|
||||
return file.LockPOSIX(t, t.FDTable(), lock.ReadLock, r, blocker)
|
||||
return file.LockPOSIX(t, t.FDTable(), int32(t.TGIDInRoot()), lock.ReadLock, r, blocker)
|
||||
|
||||
case linux.F_WRLCK:
|
||||
if !file.IsWritable() {
|
||||
return syserror.EBADF
|
||||
}
|
||||
return file.LockPOSIX(t, t.FDTable(), lock.WriteLock, r, blocker)
|
||||
return file.LockPOSIX(t, t.FDTable(), int32(t.TGIDInRoot()), lock.WriteLock, r, blocker)
|
||||
|
||||
case linux.F_UNLCK:
|
||||
return file.UnlockPOSIX(t, t.FDTable(), r)
|
||||
|
||||
@@ -44,11 +44,11 @@ func Flock(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
|
||||
switch operation {
|
||||
case linux.LOCK_EX:
|
||||
if err := file.LockBSD(t, lock.WriteLock, blocker); err != nil {
|
||||
if err := file.LockBSD(t, int32(t.TGIDInRoot()), lock.WriteLock, blocker); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
case linux.LOCK_SH:
|
||||
if err := file.LockBSD(t, lock.ReadLock, blocker); err != nil {
|
||||
if err := file.LockBSD(t, int32(t.TGIDInRoot()), lock.ReadLock, blocker); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
case linux.LOCK_UN:
|
||||
|
||||
@@ -448,16 +448,19 @@ type FileDescriptionImpl interface {
|
||||
RemoveXattr(ctx context.Context, name string) error
|
||||
|
||||
// LockBSD tries to acquire a BSD-style advisory file lock.
|
||||
LockBSD(ctx context.Context, uid lock.UniqueID, t lock.LockType, block lock.Blocker) error
|
||||
LockBSD(ctx context.Context, uid lock.UniqueID, ownerPID int32, t lock.LockType, block lock.Blocker) error
|
||||
|
||||
// UnlockBSD releases a BSD-style advisory file lock.
|
||||
UnlockBSD(ctx context.Context, uid lock.UniqueID) error
|
||||
|
||||
// LockPOSIX tries to acquire a POSIX-style advisory file lock.
|
||||
LockPOSIX(ctx context.Context, uid lock.UniqueID, t lock.LockType, r lock.LockRange, block lock.Blocker) error
|
||||
LockPOSIX(ctx context.Context, uid lock.UniqueID, ownerPID int32, t lock.LockType, r lock.LockRange, block lock.Blocker) error
|
||||
|
||||
// UnlockPOSIX releases a POSIX-style advisory file lock.
|
||||
UnlockPOSIX(ctx context.Context, uid lock.UniqueID, ComputeLockRange lock.LockRange) error
|
||||
|
||||
// TestPOSIX returns information about whether the specified lock can be held, in the style of the F_GETLK fcntl.
|
||||
TestPOSIX(ctx context.Context, uid lock.UniqueID, t lock.LockType, r lock.LockRange) (linux.Flock, error)
|
||||
}
|
||||
|
||||
// Dirent holds the information contained in struct linux_dirent64.
|
||||
@@ -791,9 +794,9 @@ func (fd *FileDescription) Msync(ctx context.Context, mr memmap.MappableRange) e
|
||||
}
|
||||
|
||||
// LockBSD tries to acquire a BSD-style advisory file lock.
|
||||
func (fd *FileDescription) LockBSD(ctx context.Context, lockType lock.LockType, blocker lock.Blocker) error {
|
||||
func (fd *FileDescription) LockBSD(ctx context.Context, ownerPID int32, lockType lock.LockType, blocker lock.Blocker) error {
|
||||
atomic.StoreUint32(&fd.usedLockBSD, 1)
|
||||
return fd.impl.LockBSD(ctx, fd, lockType, blocker)
|
||||
return fd.impl.LockBSD(ctx, fd, ownerPID, lockType, blocker)
|
||||
}
|
||||
|
||||
// UnlockBSD releases a BSD-style advisory file lock.
|
||||
@@ -802,8 +805,8 @@ func (fd *FileDescription) UnlockBSD(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// LockPOSIX locks a POSIX-style file range lock.
|
||||
func (fd *FileDescription) LockPOSIX(ctx context.Context, uid lock.UniqueID, t lock.LockType, r lock.LockRange, block lock.Blocker) error {
|
||||
return fd.impl.LockPOSIX(ctx, uid, t, r, block)
|
||||
func (fd *FileDescription) LockPOSIX(ctx context.Context, uid lock.UniqueID, ownerPID int32, t lock.LockType, r lock.LockRange, block lock.Blocker) error {
|
||||
return fd.impl.LockPOSIX(ctx, uid, ownerPID, t, r, block)
|
||||
}
|
||||
|
||||
// UnlockPOSIX unlocks a POSIX-style file range lock.
|
||||
@@ -811,6 +814,11 @@ func (fd *FileDescription) UnlockPOSIX(ctx context.Context, uid lock.UniqueID, r
|
||||
return fd.impl.UnlockPOSIX(ctx, uid, r)
|
||||
}
|
||||
|
||||
// TestPOSIX returns information about whether the specified lock can be held.
|
||||
func (fd *FileDescription) TestPOSIX(ctx context.Context, uid lock.UniqueID, t lock.LockType, r lock.LockRange) (linux.Flock, error) {
|
||||
return fd.impl.TestPOSIX(ctx, uid, t, r)
|
||||
}
|
||||
|
||||
// ComputeLockRange computes the range of a file lock based on the given values.
|
||||
func (fd *FileDescription) ComputeLockRange(ctx context.Context, start uint64, length uint64, whence int16) (lock.LockRange, error) {
|
||||
var off int64
|
||||
|
||||
@@ -419,8 +419,8 @@ func (fd *LockFD) Locks() *FileLocks {
|
||||
}
|
||||
|
||||
// LockBSD implements vfs.FileDescriptionImpl.LockBSD.
|
||||
func (fd *LockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, block fslock.Blocker) error {
|
||||
return fd.locks.LockBSD(uid, t, block)
|
||||
func (fd *LockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error {
|
||||
return fd.locks.LockBSD(ctx, uid, ownerPID, t, block)
|
||||
}
|
||||
|
||||
// UnlockBSD implements vfs.FileDescriptionImpl.UnlockBSD.
|
||||
@@ -430,8 +430,8 @@ func (fd *LockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error {
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
func (fd *LockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
return fd.locks.LockPOSIX(ctx, uid, t, r, block)
|
||||
func (fd *LockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
return fd.locks.LockPOSIX(ctx, uid, ownerPID, t, r, block)
|
||||
}
|
||||
|
||||
// UnlockPOSIX implements vfs.FileDescriptionImpl.UnlockPOSIX.
|
||||
@@ -439,6 +439,11 @@ func (fd *LockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock
|
||||
return fd.locks.UnlockPOSIX(ctx, uid, r)
|
||||
}
|
||||
|
||||
// TestPOSIX implements vfs.FileDescriptionImpl.TestPOSIX.
|
||||
func (fd *LockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
|
||||
return fd.locks.TestPOSIX(ctx, uid, t, r)
|
||||
}
|
||||
|
||||
// NoLockFD implements Lock*/Unlock* portion of FileDescriptionImpl interface
|
||||
// returning ENOLCK.
|
||||
//
|
||||
@@ -446,7 +451,7 @@ func (fd *LockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock
|
||||
type NoLockFD struct{}
|
||||
|
||||
// LockBSD implements vfs.FileDescriptionImpl.LockBSD.
|
||||
func (NoLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, block fslock.Blocker) error {
|
||||
func (NoLockFD) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, block fslock.Blocker) error {
|
||||
return syserror.ENOLCK
|
||||
}
|
||||
|
||||
@@ -456,7 +461,7 @@ func (NoLockFD) UnlockBSD(ctx context.Context, uid fslock.UniqueID) error {
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
func (NoLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
func (NoLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
return syserror.ENOLCK
|
||||
}
|
||||
|
||||
@@ -464,3 +469,8 @@ func (NoLockFD) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.Loc
|
||||
func (NoLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock.LockRange) error {
|
||||
return syserror.ENOLCK
|
||||
}
|
||||
|
||||
// TestPOSIX implements vfs.FileDescriptionImpl.TestPOSIX.
|
||||
func (NoLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
|
||||
return linux.Flock{}, syserror.ENOLCK
|
||||
}
|
||||
|
||||
+7
-28
@@ -39,8 +39,8 @@ type FileLocks struct {
|
||||
}
|
||||
|
||||
// LockBSD tries to acquire a BSD-style lock on the entire file.
|
||||
func (fl *FileLocks) LockBSD(uid fslock.UniqueID, t fslock.LockType, block fslock.Blocker) error {
|
||||
if fl.bsd.LockRegion(uid, t, fslock.LockRange{0, fslock.LockEOF}, block) {
|
||||
func (fl *FileLocks) LockBSD(ctx context.Context, uid fslock.UniqueID, ownerID int32, t fslock.LockType, block fslock.Blocker) error {
|
||||
if fl.bsd.LockRegion(uid, ownerID, t, fslock.LockRange{0, fslock.LockEOF}, block) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@ func (fl *FileLocks) UnlockBSD(uid fslock.UniqueID) {
|
||||
}
|
||||
|
||||
// LockPOSIX tries to acquire a POSIX-style lock on a file region.
|
||||
func (fl *FileLocks) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
if fl.posix.LockRegion(uid, t, r, block) {
|
||||
func (fl *FileLocks) LockPOSIX(ctx context.Context, uid fslock.UniqueID, ownerPID int32, t fslock.LockType, r fslock.LockRange, block fslock.Blocker) error {
|
||||
if fl.posix.LockRegion(uid, ownerPID, t, r, block) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -83,28 +83,7 @@ func (fl *FileLocks) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fsl
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeRange(ctx context.Context, fd *FileDescription, start uint64, length uint64, whence int16) (fslock.LockRange, error) {
|
||||
var off int64
|
||||
switch whence {
|
||||
case linux.SEEK_SET:
|
||||
off = 0
|
||||
case linux.SEEK_CUR:
|
||||
// Note that Linux does not hold any mutexes while retrieving the file
|
||||
// offset, see fs/locks.c:flock_to_posix_lock and fs/locks.c:fcntl_setlk.
|
||||
curOff, err := fd.Seek(ctx, 0, linux.SEEK_CUR)
|
||||
if err != nil {
|
||||
return fslock.LockRange{}, err
|
||||
}
|
||||
off = curOff
|
||||
case linux.SEEK_END:
|
||||
stat, err := fd.Stat(ctx, StatOptions{Mask: linux.STATX_SIZE})
|
||||
if err != nil {
|
||||
return fslock.LockRange{}, err
|
||||
}
|
||||
off = int64(stat.Size)
|
||||
default:
|
||||
return fslock.LockRange{}, syserror.EINVAL
|
||||
}
|
||||
|
||||
return fslock.ComputeRange(int64(start), int64(length), off)
|
||||
// TestPOSIX returns information about whether the specified lock can be held, in the style of the F_GETLK fcntl.
|
||||
func (fl *FileLocks) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) {
|
||||
return fl.posix.TestRegion(ctx, uid, t, r), nil
|
||||
}
|
||||
|
||||
@@ -797,6 +797,7 @@ cc_binary(
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
":socket_test_util",
|
||||
"//test/util:capability_util",
|
||||
"//test/util:cleanup",
|
||||
"//test/util:eventfd_util",
|
||||
"//test/util:file_descriptor",
|
||||
@@ -807,6 +808,7 @@ cc_binary(
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
gtest,
|
||||
"//test/util:memory_util",
|
||||
"//test/util:multiprocess_util",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:save_util",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <syscall.h>
|
||||
#include <unistd.h>
|
||||
@@ -35,10 +36,12 @@
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
#include "test/util/capability_util.h"
|
||||
#include "test/util/cleanup.h"
|
||||
#include "test/util/eventfd_util.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/memory_util.h"
|
||||
#include "test/util/multiprocess_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/save_util.h"
|
||||
@@ -1642,6 +1645,202 @@ TEST(FcntlTest, SetFlSetOwnSetSigDoNotRace) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(FcntlLockTest, GetLockOnNothing) {
|
||||
SKIP_IF(IsRunningWithVFS1());
|
||||
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
|
||||
FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666));
|
||||
|
||||
struct flock fl;
|
||||
fl.l_type = F_RDLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds());
|
||||
ASSERT_TRUE(fl.l_type == F_UNLCK);
|
||||
}
|
||||
|
||||
TEST_F(FcntlLockTest, GetLockOnLockSameProcess) {
|
||||
SKIP_IF(IsRunningWithVFS1());
|
||||
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
|
||||
FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666));
|
||||
|
||||
struct flock fl;
|
||||
fl.l_type = F_RDLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds());
|
||||
ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds());
|
||||
ASSERT_TRUE(fl.l_type == F_UNLCK);
|
||||
|
||||
fl.l_type = F_WRLCK;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds());
|
||||
ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds());
|
||||
ASSERT_TRUE(fl.l_type == F_UNLCK);
|
||||
}
|
||||
|
||||
TEST_F(FcntlLockTest, GetReadLockOnReadLock) {
|
||||
SKIP_IF(IsRunningWithVFS1());
|
||||
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
|
||||
FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666));
|
||||
|
||||
struct flock fl;
|
||||
fl.l_type = F_RDLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds());
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0);
|
||||
TEST_CHECK(fl.l_type == F_UNLCK);
|
||||
_exit(0);
|
||||
}
|
||||
int status;
|
||||
ASSERT_THAT(waitpid(child_pid, &status, 0),
|
||||
SyscallSucceedsWithValue(child_pid));
|
||||
ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
TEST_F(FcntlLockTest, GetReadLockOnWriteLock) {
|
||||
SKIP_IF(IsRunningWithVFS1());
|
||||
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
|
||||
FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666));
|
||||
|
||||
struct flock fl;
|
||||
fl.l_type = F_WRLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds());
|
||||
|
||||
fl.l_type = F_RDLCK;
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0);
|
||||
TEST_CHECK(fl.l_type == F_WRLCK);
|
||||
TEST_CHECK(fl.l_pid == getppid());
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int status;
|
||||
ASSERT_THAT(waitpid(child_pid, &status, 0),
|
||||
SyscallSucceedsWithValue(child_pid));
|
||||
ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
TEST_F(FcntlLockTest, GetWriteLockOnReadLock) {
|
||||
SKIP_IF(IsRunningWithVFS1());
|
||||
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
|
||||
FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666));
|
||||
|
||||
struct flock fl;
|
||||
fl.l_type = F_RDLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds());
|
||||
|
||||
fl.l_type = F_WRLCK;
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0);
|
||||
TEST_CHECK(fl.l_type == F_RDLCK);
|
||||
TEST_CHECK(fl.l_pid == getppid());
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int status;
|
||||
ASSERT_THAT(waitpid(child_pid, &status, 0),
|
||||
SyscallSucceedsWithValue(child_pid));
|
||||
ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
TEST_F(FcntlLockTest, GetWriteLockOnWriteLock) {
|
||||
SKIP_IF(IsRunningWithVFS1());
|
||||
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
|
||||
FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666));
|
||||
|
||||
struct flock fl;
|
||||
fl.l_type = F_WRLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds());
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0);
|
||||
TEST_CHECK(fl.l_type == F_WRLCK);
|
||||
TEST_CHECK(fl.l_pid == getppid());
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int status;
|
||||
ASSERT_THAT(waitpid(child_pid, &status, 0),
|
||||
SyscallSucceedsWithValue(child_pid));
|
||||
ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
// Tests that the pid returned from F_GETLK is relative to the caller's PID
|
||||
// namespace.
|
||||
TEST_F(FcntlLockTest, GetLockRespectsPIDNamespace) {
|
||||
SKIP_IF(IsRunningWithVFS1());
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
|
||||
auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
|
||||
std::string filename = file.path();
|
||||
const FileDescriptor fd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR, 0666));
|
||||
|
||||
// Lock in the parent process.
|
||||
struct flock fl;
|
||||
fl.l_type = F_WRLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds());
|
||||
|
||||
auto child_getlk = [](void* filename) {
|
||||
int fd = open((char*)filename, O_RDWR, 0666);
|
||||
TEST_CHECK(fd >= 0);
|
||||
|
||||
struct flock fl;
|
||||
fl.l_type = F_WRLCK;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_start = 0;
|
||||
fl.l_len = 40;
|
||||
TEST_CHECK(fcntl(fd, F_GETLK, &fl) >= 0);
|
||||
TEST_CHECK(fl.l_type == F_WRLCK);
|
||||
// Parent PID should be 0 in the child PID namespace.
|
||||
TEST_CHECK(fl.l_pid == 0);
|
||||
close(fd);
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Set up child process in a new PID namespace.
|
||||
constexpr int kStackSize = 4096;
|
||||
Mapping stack = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
Mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0));
|
||||
pid_t child_pid;
|
||||
ASSERT_THAT(
|
||||
child_pid = clone(child_getlk, (char*)stack.ptr() + stack.len(),
|
||||
CLONE_NEWPID | SIGCHLD, (void*)filename.c_str()),
|
||||
SyscallSucceeds());
|
||||
|
||||
int status;
|
||||
ASSERT_THAT(waitpid(child_pid, &status, 0),
|
||||
SyscallSucceedsWithValue(child_pid));
|
||||
ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
|
||||
Reference in New Issue
Block a user