flock: use O_RDWR as fallback if O_RDONLY returns EIO

The commit 75aaee08f0 introduces
regression:

     $ echo '#!/bin/sh' > test.sh
     $ chmod a+rx test.sh
     $ flock -eon ./test.sh ./test.sh
     flock: ./test.sh: Text file busy

The lock file cannot be opened in read-write mode by default, because
then we cannot use flock(1) to lock executable files.

The read-write mode for lock files is necessary on NFSv4 where
flock(2) is emulated by by fcntl() -- this situation is possible to
detect by flock(2) EIO error.

This patch reverts the default to O_RDONLY and use O_RDWR only if EIO
error is detected.

Reported-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2011-11-21 17:12:33 +01:00
parent bc3ae4c6fc
commit eb742a1f66
1 changed files with 47 additions and 19 deletions

View File

@ -100,13 +100,42 @@ static void cancel_timer(struct itimerval *old_timer, struct sigaction *old_sa)
sigaction(SIGALRM, old_sa, NULL);
}
static int open_file(const char *filename, int *flags)
{
int fd;
int fl = *flags == 0 ? O_RDONLY : *flags;
errno = 0;
fl |= O_NOCTTY | O_CREAT;
fd = open(filename, fl, 0666);
/* Linux doesn't like O_CREAT on a directory, even though it
* should be a no-op; POSIX doesn't allow O_RDWR or O_WRONLY
*/
if (fd < 0 && errno == EISDIR) {
fl = O_RDONLY | O_NOCTTY;
fd = open(filename, fl);
}
if (fd < 0) {
warn(_("cannot open lock file %s"), filename);
if (errno == ENOMEM || errno == EMFILE || errno == ENFILE)
exit(EX_OSERR);
if (errno == EROFS || errno == ENOSPC)
exit(EX_CANTCREAT);
exit(EX_NOINPUT);
}
*flags = fl;
return fd;
}
int main(int argc, char *argv[])
{
struct itimerval timeout, old_timer;
int have_timeout = 0;
int type = LOCK_EX;
int block = 0;
int open_accmode;
int open_flags = 0;
int fd = -1;
int opt, ix;
int do_close = 0;
@ -195,25 +224,8 @@ int main(int argc, char *argv[])
}
filename = argv[optind];
open_accmode =
((type == LOCK_SH
|| access(filename,
R_OK | W_OK) < 0) ? O_RDONLY : O_RDWR);
fd = open(filename, open_accmode | O_NOCTTY | O_CREAT, 0666);
/* Linux doesn't like O_CREAT on a directory, even though it
* should be a no-op; POSIX doesn't allow O_RDWR or O_WRONLY
*/
if (fd < 0 && errno == EISDIR)
fd = open(filename, O_RDONLY | O_NOCTTY);
fd = open_file(filename, &open_flags);
if (fd < 0) {
warn(_("cannot open lock file %s"), argv[optind]);
if (errno == ENOMEM || errno == EMFILE || errno == ENFILE)
exit(EX_OSERR);
if (errno == EROFS || errno == ENOSPC)
exit(EX_CANTCREAT);
exit(EX_NOINPUT);
}
} else if (optind < argc) {
/* Use provided file descriptor */
fd = (int)strtol_or_err(argv[optind], "bad number");
@ -252,6 +264,22 @@ int main(int argc, char *argv[])
exit(1);
/* otherwise try again */
continue;
case EIO:
/* Probably NFSv4 where flock() is emulated by fcntl().
* Let's try to reopen in read-write mode.
*/
if (!(open_flags & O_RDWR) &&
type != LOCK_SH &&
access(filename, R_OK | W_OK) == 0) {
close(fd);
open_flags = O_RDWR;
fd = open_file(filename, &open_flags);
if (open_flags & O_RDWR)
break;
}
/* go through */
default:
/* Other errors */
if (filename)