Add -w option to lockf(1).

By default, lockf(1) opens its lock file O_RDONLY|O_EXLOCK.  On NFS, if the
file already exists, this is split into opening the file read-only and then
requesting an exclusive lock -- and the second step fails because NFS does
not permit exclusive locking on files which are opened read-only.

The new -w option changes the open flags to O_WRONLY|O_EXLOCK, allowing it
to work on NFS -- at the cost of not working if the file cannot be opened
for writing.

(Whether the traditional BSD behaviour of allowing exclusive locks to be
obtained on a file which cannot be opened for writing is a good idea is
perhaps questionable since it may allow less-privileged users to perform
a local denial of service; however this behaviour has been present for a
long time and changing it now seems like it would cause problems.)

Reviewed by:	rmacklem
Differential Revision:	https://reviews.freebsd.org/D26005
This commit is contained in:
Colin Percival 2020-08-26 19:26:48 +00:00
parent 507cf10ad5
commit 437bab4828
2 changed files with 17 additions and 6 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 18, 2020
.Dd August 26, 2020
.Dt LOCKF 1
.Os
.Sh NAME
@ -32,7 +32,7 @@
.Nd execute a command while holding a file lock
.Sh SYNOPSIS
.Nm
.Op Fl kns
.Op Fl knsw
.Op Fl t Ar seconds
.Ar file
.Ar command
@ -121,6 +121,14 @@ When a lock times out,
is
.Em not
executed.
.It Fl w
Causes
.Nm
to open
.Ar file
for writing rather than reading.
This is necessary on filesystems (including NFSv4) where a file which
has been opened read-only cannot be exclusively locked.
.El
.Pp
In no event will

View File

@ -62,9 +62,9 @@ main(int argc, char **argv)
pid_t child;
silent = keep = 0;
flags = O_CREAT;
flags = O_CREAT | O_RDONLY;
waitsec = -1; /* Infinite. */
while ((ch = getopt(argc, argv, "sknt:")) != -1) {
while ((ch = getopt(argc, argv, "sknt:w")) != -1) {
switch (ch) {
case 'k':
keep = 1;
@ -84,6 +84,9 @@ main(int argc, char **argv)
"invalid timeout \"%s\"", optarg);
}
break;
case 'w':
flags = (flags & ~O_RDONLY) | O_WRONLY;
break;
default:
usage();
}
@ -171,7 +174,7 @@ acquire_lock(const char *name, int flags)
{
int fd;
if ((fd = open(name, O_RDONLY|O_EXLOCK|flags, 0666)) == -1) {
if ((fd = open(name, O_EXLOCK|flags, 0666)) == -1) {
if (errno == EAGAIN || errno == EINTR)
return (-1);
else if (errno == ENOENT && (flags & O_CREAT) == 0)