]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
flock: use O_RDWR as fallback if O_RDONLY returns EIO
authorKarel Zak <kzak@redhat.com>
Mon, 21 Nov 2011 16:12:33 +0000 (17:12 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 21 Nov 2011 16:26:41 +0000 (17:26 +0100)
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 <vapier@gentoo.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/flock.c

index b5289af08b4b09b88e52766f7fd7efd4da52a0f0..0d4a8fbf5e1409e6d76bf04d5b9126b4a15f59b6 100644 (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);
-
-               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)