Implement F_GETLK fcntl.

Fixes #5113.

PiperOrigin-RevId: 353313374
This commit is contained in:
Dean Deng
2021-01-22 13:58:16 -08:00
committed by gVisor bot
parent 6c0e1d9cfe
commit f52f0101bb
18 changed files with 532 additions and 161 deletions
+2 -1
View File
@@ -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
}
+2
View File
@@ -41,6 +41,8 @@ go_library(
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/context",
"//pkg/log",
"//pkg/sync",
"//pkg/waiter",
+131 -22
View File
@@ -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()
}
}
+3 -2
View File
@@ -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
}
+62 -62
View File
@@ -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},
},
},
+4 -4
View File
@@ -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.
+8 -8
View File
@@ -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 {
+9 -4
View File
@@ -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 {
-1
View File
@@ -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",
+11 -1
View File
@@ -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)
}
+8 -8
View File
@@ -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
}
}
+52 -6
View File
@@ -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)
+2 -2
View File
@@ -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:
+14 -6
View File
@@ -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
+16 -6
View File
@@ -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
View File
@@ -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
}
+2
View File
@@ -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",
+199
View File
@@ -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