flock: new module
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 3 Oct 2008 13:58:14 +0000 (14:58 +0100)
committerJim Meyering <meyering@redhat.com>
Tue, 7 Oct 2008 06:32:09 +0000 (08:32 +0200)
* MODULES.html.sh: Add to list of modules.
* lib/flock.c: flock implementation for Windows and Unix systems
which have fcntl.
* doc/glibc-functions/flock.texi: Update documentation.
* lib/sys_file.in.h: <sys/file.h> header file.
* m4/flock.m4: M4 macros.
* m4/sys_file_h.m4: M4 macros for replacement sys/file.h.
* modules/flock: flock module.
* modules/flock-tests: flock tests module.
* modules/sys_file: sys/file.h module.
* tests/test-flock.c: test suite for flock.

ChangeLog
MODULES.html.sh
doc/glibc-functions/flock.texi
lib/flock.c [new file with mode: 0644]
lib/sys_file.in.h [new file with mode: 0644]
m4/flock.m4 [new file with mode: 0644]
m4/sys_file_h.m4 [new file with mode: 0644]
modules/flock [new file with mode: 0644]
modules/flock-tests [new file with mode: 0644]
modules/sys_file [new file with mode: 0644]
tests/test-flock.c [new file with mode: 0644]

index 51ce3f6..f1c32e6 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2008-10-07  Richard W.M. Jones <rjones@redhat.com>
+
+       flock: new module
+       * MODULES.html.sh: Add to list of modules.
+       * lib/flock.c: flock implementation for Windows and Unix systems
+       which have fcntl.
+       * doc/glibc-functions/flock.texi: Update documentation.
+       * lib/sys_file.in.h: <sys/file.h> header file.
+       * m4/flock.m4: M4 macros.
+       * m4/sys_file_h.m4: M4 macros for replacement sys/file.h.
+       * modules/flock: flock module.
+       * modules/flock-tests: flock tests module.
+       * modules/sys_file: sys/file.h module.
+       * tests/test-flock.c: test suite for flock.
+
 2008-10-06  Jim Meyering  <meyering@redhat.com>
 
        bootstrap: check for LT_INIT more portably still ;-)
index 229fb53..96fe700 100755 (executable)
@@ -396,6 +396,7 @@ fgets
 fgetwc
 fgetws
 fileno
+flock
 flockfile
 floor
 floorf
@@ -2106,6 +2107,7 @@ func_all_modules ()
   func_module errno
   func_module fchdir
   func_module fcntl
+  func_module flock
   func_module fopen
   func_module fprintf-posix
   func_module freopen
index 29a2eb8..23878cf 100644 (file)
@@ -2,15 +2,18 @@
 @subsection @code{flock}
 @findex flock
 
-Gnulib module: ---
+Gnulib module: flock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+mingw
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
 @item
 This function is missing on some platforms:
-AIX 5.1, HP-UX 11, Solaris 10, mingw, BeOS.
+AIX 5.1, HP-UX 11, Solaris 10, BeOS.
 @end itemize
diff --git a/lib/flock.c b/lib/flock.c
new file mode 100644 (file)
index 0000000..5aa7690
--- /dev/null
@@ -0,0 +1,219 @@
+/* Emulate flock on platforms that lack it, primarily Windows and MinGW.
+
+   This is derived from sqlite3 sources.
+   http://www.sqlite.org/cvstrac/rlog?f=sqlite/src/os_win.c
+   http://www.sqlite.org/copyright.html
+
+   Written by Richard W.M. Jones <rjones.at.redhat.com>
+
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include <sys/file.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+/* _get_osfhandle */
+#include <io.h>
+
+/* LockFileEx */
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <errno.h>
+
+/* Determine the current size of a file.  Because the other braindead
+ * APIs we'll call need lower/upper 32 bit pairs, keep the file size
+ * like that too.
+ */
+static BOOL
+file_size (HANDLE h, DWORD * lower, DWORD * upper)
+{
+  *lower = GetFileSize (h, upper);
+  return 1;
+}
+
+/* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */
+#ifndef LOCKFILE_FAIL_IMMEDIATELY
+# define LOCKFILE_FAIL_IMMEDIATELY 1
+#endif
+
+/* Acquire a lock. */
+static BOOL
+do_lock (HANDLE h, int non_blocking, int exclusive)
+{
+  BOOL res;
+  DWORD size_lower, size_upper;
+  OVERLAPPED ovlp;
+  int flags = 0;
+
+  /* We're going to lock the whole file, so get the file size. */
+  res = file_size (h, &size_lower, &size_upper);
+  if (!res)
+    return 0;
+
+  /* Start offset is 0, and also zero the remaining members of this struct. */
+  memset (&ovlp, 0, sizeof ovlp);
+
+  if (non_blocking)
+    flags |= LOCKFILE_FAIL_IMMEDIATELY;
+  if (exclusive)
+    flags |= LOCKFILE_EXCLUSIVE_LOCK;
+
+  return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp);
+}
+
+/* Unlock reader or exclusive lock. */
+static BOOL
+do_unlock (HANDLE h)
+{
+  int res;
+  DWORD size_lower, size_upper;
+
+  res = file_size (h, &size_lower, &size_upper);
+  if (!res)
+    return 0;
+
+  return UnlockFile (h, 0, 0, size_lower, size_upper);
+}
+
+/* Now our BSD-like flock operation. */
+int
+flock (int fd, int operation)
+{
+  HANDLE h = (HANDLE) _get_osfhandle (fd);
+  DWORD res;
+  int non_blocking;
+
+  if (h == INVALID_HANDLE_VALUE)
+    {
+      errno = EBADF;
+      return -1;
+    }
+
+  non_blocking = operation & LOCK_NB;
+  operation &= ~LOCK_NB;
+
+  switch (operation)
+    {
+    case LOCK_SH:
+      res = do_lock (h, non_blocking, 0);
+      break;
+    case LOCK_EX:
+      res = do_lock (h, non_blocking, 1);
+      break;
+    case LOCK_UN:
+      res = do_unlock (h);
+      break;
+    default:
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Map Windows errors into Unix errnos.  As usual MSDN fails to
+   * document the permissible error codes.
+   */
+  if (!res)
+    {
+      DWORD err = GetLastError ();
+      switch (err)
+       {
+         /* This means someone else is holding a lock. */
+       case ERROR_LOCK_VIOLATION:
+         errno = EAGAIN;
+         break;
+
+         /* Out of memory. */
+       case ERROR_NOT_ENOUGH_MEMORY:
+         errno = ENOMEM;
+         break;
+
+       case ERROR_BAD_COMMAND:
+         errno = EINVAL;
+         break;
+
+         /* Unlikely to be other errors, but at least don't lose the
+          * error code.
+          */
+       default:
+         errno = err;
+       }
+
+      return -1;
+    }
+
+  return 0;
+}
+
+#else /* !Windows */
+
+#ifdef HAVE_FLOCK_L_TYPE
+/* We know how to implement flock in terms of fcntl. */
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+int
+flock (int fd, int operation)
+{
+  int cmd, r;
+  struct flock fl;
+
+  if (operation & LOCK_NB)
+    cmd = F_SETLK;
+  else
+    cmd = F_SETLKW;
+  operation &= ~LOCK_NB;
+
+  memset (&fl, 0, sizeof fl);
+  fl.l_whence = SEEK_SET;
+  /* l_start & l_len are 0, which as a special case means "whole file". */
+
+  switch (operation)
+    {
+    case LOCK_SH:
+      fl.l_type = F_RDLCK;
+      break;
+    case LOCK_EX:
+      fl.l_type = F_WRLCK;
+      break;
+    case LOCK_UN:
+      fl.l_type = F_UNLCK;
+      break;
+    default:
+      errno = EINVAL;
+      return -1;
+    }
+
+  r = fcntl (fd, cmd, &fl);
+  if (r == -1 && errno == EACCES)
+    errno = EAGAIN;
+
+  return r;
+}
+
+#else /* !HAVE_FLOCK_L_TYPE */
+
+#error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
+
+#endif /* !HAVE_FLOCK_L_TYPE */
+
+#endif /* !Windows */
diff --git a/lib/sys_file.in.h b/lib/sys_file.in.h
new file mode 100644 (file)
index 0000000..c1d2315
--- /dev/null
@@ -0,0 +1,58 @@
+/* Provide a more complete sys/file.h.
+
+   Copyright (C) 2007-2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Richard W.M. Jones.  */
+#ifndef _GL_SYS_FILE_H
+
+@PRAGMA_SYSTEM_HEADER@
+
+/* The include_next requires a split double-inclusion guard.  */
+# if @HAVE_SYS_FILE_H@
+#  @INCLUDE_NEXT@ @NEXT_SYS_FILE_H@
+# endif
+
+#ifndef _GL_SYS_FILE_H
+#define _GL_SYS_FILE_H
+
+
+#if @GNULIB_FLOCK@
+/* Apply or remove advisory locks on an open file.
+   Return 0 if successful, otherwise -1 and errno set.  */
+# if !@HAVE_FLOCK@
+extern int flock (int fd, int operation);
+
+/* Operations for the 'flock' call (same as Linux kernel constants).  */
+#define LOCK_SH 1       /* Shared lock.  */
+#define LOCK_EX 2       /* Exclusive lock.  */
+#define LOCK_UN 8       /* Unlock.  */
+
+/* Can be OR'd in to one of the above.  */
+#define LOCK_NB 4       /* Don't block when locking.  */
+
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef flock
+# define flock(fd,op)                         \
+    (GL_LINK_WARNING ("flock is unportable - " \
+                      "use gnulib module flock for portability"), \
+     flock ((fd), (op)))
+#endif
+
+
+#endif /* _GL_SYS_FILE_H */
+#endif /* _GL_SYS_FILE_H */
diff --git a/m4/flock.m4 b/m4/flock.m4
new file mode 100644 (file)
index 0000000..96475fc
--- /dev/null
@@ -0,0 +1,26 @@
+# flock.m4 serial 1
+dnl Copyright (C) 2008 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_FLOCK],
+[
+  AC_REQUIRE([gl_HEADER_SYS_FILE_H_DEFAULTS])
+  AC_CHECK_FUNCS_ONCE([flock])
+  if test $ac_cv_func_flock = no; then
+    HAVE_FLOCK=0
+    AC_LIBOBJ([flock])
+    gl_PREREQ_FLOCK
+  fi
+])
+
+dnl Prerequisites of lib/flock.c.
+AC_DEFUN([gl_PREREQ_FLOCK],
+[
+  AC_CHECK_FUNCS_ONCE([fcntl])
+  AC_CHECK_HEADERS_ONCE([unistd.h fcntl.h])
+
+  dnl Do we have a POSIX fcntl lock implementation?
+  AC_CHECK_MEMBERS([struct flock.l_type],[],[],[[#include <fcntl.h>]])
+])
diff --git a/m4/sys_file_h.m4 b/m4/sys_file_h.m4
new file mode 100644 (file)
index 0000000..436c6fe
--- /dev/null
@@ -0,0 +1,41 @@
+# Configure a replacement for <sys/file.h>.
+
+# Copyright (C) 2008 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Written by Richard W.M. Jones.
+
+AC_DEFUN([gl_HEADER_SYS_FILE_H],
+[
+  AC_REQUIRE([gl_HEADER_SYS_FILE_H_DEFAULTS])
+
+  dnl Only flock is defined in a working <sys/file.h>.  If that
+  dnl function is already there, we don't want to do any substitution.
+  AC_CHECK_FUNCS_ONCE([flock])
+
+  gl_CHECK_NEXT_HEADERS([sys/file.h])
+  SYS_FILE_H='sys/file.h'
+  AC_SUBST([SYS_FILE_H])
+
+  AC_CHECK_HEADERS_ONCE([sys/file.h])
+  if test $ac_cv_header_sys_file_h = yes; then
+    HAVE_SYS_FILE_H=1
+  else
+    HAVE_SYS_FILE_H=0
+  fi
+  AC_SUBST([HAVE_SYS_FILE_H])
+])
+
+AC_DEFUN([gl_HEADER_SYS_FILE_MODULE_INDICATOR],
+[
+  AC_REQUIRE([gl_HEADER_SYS_FILE_H_DEFAULTS])
+  GNULIB_[]m4_translit([$1],[abcdefghijklmnopqrstuvwxyz./-],[ABCDEFGHIJKLMNOPQRSTUVWXYZ___])=1
+])
+
+AC_DEFUN([gl_HEADER_SYS_FILE_H_DEFAULTS],
+[
+  GNULIB_FLOCK=0;        AC_SUBST([GNULIB_FLOCK])
+  HAVE_FLOCK=1;          AC_SUBST([HAVE_FLOCK])
+])
diff --git a/modules/flock b/modules/flock
new file mode 100644 (file)
index 0000000..9134d51
--- /dev/null
@@ -0,0 +1,24 @@
+Description:
+flock(2) function: advisory file locks.
+
+Files:
+lib/flock.c
+m4/flock.m4
+
+Depends-on:
+sys_file
+
+configure.ac:
+gl_FUNC_FLOCK
+gl_HEADER_SYS_FILE_MODULE_INDICATOR([flock])
+
+Makefile.am:
+
+Include:
+<sys/file.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+Richard W.M. Jones, Jim Meyering
diff --git a/modules/flock-tests b/modules/flock-tests
new file mode 100644 (file)
index 0000000..b546594
--- /dev/null
@@ -0,0 +1,11 @@
+Files:
+tests/test-flock.c
+
+Depends-on:
+unistd
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-flock
+check_PROGRAMS += test-flock
diff --git a/modules/sys_file b/modules/sys_file
new file mode 100644 (file)
index 0000000..2c1f8f8
--- /dev/null
@@ -0,0 +1,44 @@
+Description:
+<sys/file.h> for systems with an incomplete header.
+
+Files:
+lib/sys_file.in.h
+m4/sys_file_h.m4
+
+Depends-on:
+include_next
+link-warning
+
+configure.ac:
+gl_HEADER_SYS_FILE_H
+AC_PROG_MKDIR_P
+
+Makefile.am:
+BUILT_SOURCES += $(SYS_FILE_H)
+
+# We need the following in order to create <sys/file.h> when the system
+# has one that is incomplete.
+sys/file.h: sys_file.in.h
+       @MKDIR_P@ sys
+       rm -f $@-t $@
+       { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+         sed -e 's/@''HAVE_SYS_FILE_H''@/$(HAVE_SYS_FILE_H)/g' \
+             -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+             -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+             -e 's|@''NEXT_SYS_FILE_H''@|$(NEXT_SYS_FILE_H)|g' \
+             -e 's/@''HAVE_FLOCK''@/$(HAVE_FLOCK)/g' \
+             -e 's/@''GNULIB_FLOCK''@/$(GNULIB_FLOCK)/g' \
+             < $(srcdir)/sys_file.in.h; \
+       } > $@-t
+       mv $@-t $@
+MOSTLYCLEANFILES += sys/file.h sys/file.h-t
+MOSTLYCLEANDIRS += sys
+
+Include:
+#include <sys/file.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/tests/test-flock.c b/tests/test-flock.c
new file mode 100644 (file)
index 0000000..a1872cd
--- /dev/null
@@ -0,0 +1,104 @@
+/* Test of flock() function.
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/file.h>
+
+#define ASSERT(expr) \
+  do                                                                   \
+    {                                                                  \
+      if (!(expr))                                                     \
+        {                                                              \
+         fprintf (stderr, "%s:%d: assertion failed, errno = %d\n",     \
+                  __FILE__, __LINE__, errno);                          \
+          fflush (stderr);                                             \
+          abort ();                                                    \
+        }                                                              \
+    }                                                                  \
+  while (0)
+
+static void
+test_shared (const char *file, int fd)
+{
+  /* Should be able to acquire several shared locks on a file, through
+   * different file table entries.
+   */
+  int fd2, r;
+
+  ASSERT (flock (fd, LOCK_SH) == 0);
+
+  fd2 = open (file, O_RDWR, 0644);
+  ASSERT (fd2 >= 0);
+
+  r = flock (fd2, LOCK_SH | LOCK_NB);
+  ASSERT (r == 0);             /* Was able to acquire a second shared lock. */
+
+  ASSERT (flock (fd, LOCK_UN) == 0);
+  ASSERT (close (fd2) == 0);
+}
+
+static void
+test_exclusive (const char *file, int fd)
+{
+  /* Should not be able to acquire more than one exclusive lock on a file. */
+  int fd2, r;
+
+  ASSERT (flock (fd, LOCK_EX) == 0);
+
+  fd2 = open (file, O_RDWR, 0644);
+  ASSERT (fd2 >= 0);
+
+  r = flock (fd2, LOCK_SH | LOCK_NB);
+  ASSERT (r == -1);            /* Was unable to acquire a second exclusive lock. */
+
+  ASSERT (flock (fd, LOCK_UN) == 0);
+  ASSERT (close (fd2) == 0);
+}
+
+int
+main (int argc, char *argv[])
+{
+  int fd;
+  const char *file = "test-flock.txt";
+
+  /* Open a non-empty file for testing. */
+  fd = open (file, O_RDWR | O_CREAT | O_TRUNC, 0644);
+  ASSERT (fd >= 0);
+  ASSERT (write (fd, "hello", 5) == 5);
+
+  /* Some impossible operation codes which should never be accepted. */
+  ASSERT (flock (fd, LOCK_SH | LOCK_EX) == -1);
+  ASSERT (errno == EINVAL);
+  ASSERT (flock (fd, LOCK_SH | LOCK_UN) == -1);
+  ASSERT (errno == EINVAL);
+  ASSERT (flock (fd, LOCK_EX | LOCK_UN) == -1);
+  ASSERT (errno == EINVAL);
+  ASSERT (flock (fd, 0) == -1);
+  ASSERT (errno == EINVAL);
+
+  test_shared (file, fd);
+  test_exclusive (file, fd);
+
+  /* Close and remove the test file. */
+  ASSERT (close (fd) == 0);
+  ASSERT (unlink (file) == 0);
+
+  return 0;
+}