blob: 2495ad0804772bb5701d5312dab298a4f7d238cf [file] [log] [blame]
// Copyright 2022 by Chenlin Fan. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || android
// +build linux android
package fslock
import (
"os"
"golang.org/x/sys/unix"
)
// flockLock is an implementation of lock using Linux flock().
//
// A process may only hold one type of lock (shared or exclusive) on a file.
// Subsequent flock() calls on an already locked file will convert an existing
// lock to the new lock mode. Attempts to acquire a file lock first check to
// see if there is already a global entity holding the lock (fail), then attempt
// to acquire the lock at a filesystem level.
//
// We can't use POSIX FcntlFlock because Linux has a bug in it's implementation
// which different from the documented behaviour. If the process calling execve
// is not the last thread of the process, the lock will be released even without
// FD_CLOEXEC flag.
// See also: crbug.com/1276120
func flockLock(l *L, fd *os.File) error {
// Use "flock()" to get a lock on the file.
//
// LOCK_EX: Exclusive lock
// LOCK_NB: Non-blocking.
flags := unix.LOCK_NB
if l.Shared {
flags |= unix.LOCK_SH
} else {
flags |= unix.LOCK_EX
}
if err := unix.Flock(int(fd.Fd()), flags); err != nil {
if errno, ok := err.(unix.Errno); ok {
switch errno {
case unix.EWOULDBLOCK:
// Someone else holds the lock on this file.
return ErrLockHeld
default:
return err
}
}
return err
}
return nil
}