From eb742a1f66d5e3a7c5b43efce741c113f51bef3b Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 21 Nov 2011 17:12:33 +0100 Subject: flock: use O_RDWR as fallback if O_RDONLY returns EIO The commit 75aaee08f06b92d119ed827c53d1af5474eb16ff 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 Signed-off-by: Karel Zak --- sys-utils/flock.c | 68 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 20 deletions(-) (limited to 'sys-utils/flock.c') diff --git a/sys-utils/flock.c b/sys-utils/flock.c index b5289af08..0d4a8fbf5 100644 --- a/sys-utils/flock.c +++ b/sys-utils/flock.c @@ -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); - - 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); - } + fd = open_file(filename, &open_flags); + } 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) -- cgit v1.2.3-55-g7522