common: Prepare for parsing mail sub-addresses.
[gnupg.git] / common / dotlock.c
index e3e9fa3..1bc31d8 100644 (file)
@@ -2,20 +2,67 @@
  * Copyright (C) 1998, 2000, 2001, 2003, 2004,
  *               2005, 2006, 2008, 2010, 2011 Free Software Foundation, Inc.
  *
- * This file is part of JNLIB, which is a subsystem of GnuPG.
+ * This file is part of GnuPG.
  *
- * JNLIB 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 3 of
- * the License, or (at your option) any later version.
+ * GnuPG is free software; you can redistribute and/or modify this
+ * part of GnuPG under the terms of either
  *
- * JNLIB is distributed in the hope that it will be useful, but
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - 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.
+ *
+ * or both in parallel, as here.
+ *
+ * GnuPG 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.
+ * General Public License for more details.
+ *
+ * You should have received a copies of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, see <https://www.gnu.org/licenses/>.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * ALTERNATIVELY, this file may be distributed under the terms of the
+ * following license, in which case the provisions of this license are
+ * required INSTEAD OF the GNU Lesser General License or the GNU
+ * General Public License. If you wish to allow use of your version of
+ * this file only under the terms of the GNU Lesser General License or
+ * the GNU General Public License, and not to allow others to use your
+ * version of this file under the terms of the following license,
+ * indicate your decision by deleting this paragraph and the license
+ * below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 /*
    At program initialization time, the module should be explicitly
    initialized:
 
-      dotlock_create (NULL);
+      dotlock_create (NULL, 0);
 
    This installs an atexit handler and may also initialize mutex etc.
    It is optional for non-threaded applications.  Only the first call
-   has an effect.
+   has an effect.  This needs to be done before any extra threads are
+   started.
 
    To create a lock file (which  prepares it but does not take the
    lock) you do:
 
      dotlock_t h
 
-     h = dotlock_create (fname);
+     h = dotlock_create (fname, 0);
      if (!h)
        error ("error creating lock file: %s\n", strerror (errno));
 
    It is important to handle the error.  For example on a read-only
    file system a lock can't be created (but is usually not needed).
    FNAME is the file you want to lock; the actual lockfile is that
-   name with the suffix ".lock" appended.  This call creates a unique
-   file temporary file (".#lk*") in the same directory as FNAME and
-   returns a handle for further operations.  The module keeps track of
-   theses unique files so that they will be unlinked using the atexit
-   handler.  If you don't need the lock file anymore, you may also
-   explicitly remove it with a call to:
+   name with the suffix ".lock" appended.  On success a handle to be
+   used with the other functions is returned or NULL on error.  Note
+   that the handle shall only be used by one thread at a time.  This
+   function creates a unique file temporary file (".#lk*") in the same
+   directory as FNAME and returns a handle for further operations.
+   The module keeps track of theses unique files so that they will be
+   unlinked using the atexit handler.  If you don't need the lock file
+   anymore, you may also explicitly remove it with a call to:
 
      dotlock_destroy (h);
 
    you pass (0) instead of (-1) the function does not wait in case the
    file is already locked but returns -1 and sets ERRNO to EACCES.
    Any other positive value for the second parameter is considered a
-   timeout valuie in milliseconds.
+   timeout value in milliseconds.
 
    To release the lock you call:
 
      if (dotlock_release (h))
        error ("error releasing lock: %s\n", strerror (errno));
 
-   or, if the lock file is not anymore needed, you may call
-   dotlock_destroy.
+   or, if the lock file is not anymore needed, you may just call
+   dotlock_destroy.  However dotlock_release does some extra checks
+   before releasing the lock and prints diagnostics to help detecting
+   bugs.
 
    If you want to explicitly destroy all lock files you may call
 
 
    before any locks are created.
 
+   There are two convenience functions to store an integer (e.g. a
+   file descriptor) value with the handle:
+
+     void dotlock_set_fd (dotlock_t h, int fd);
+     int  dotlock_get_fd (dotlock_t h);
+
+   If nothing has been stored dotlock_get_fd returns -1.
+
+
 
    How to build:
    =============
    to pass -DHAVE_CONFIG_H to the compiler.  Macros used by this
    module are:
 
+     DOTLOCK_USE_PTHREAD  - Define if POSIX threads are in use.
+
      DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions.
 
+     DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the
+                              string to which this macro evaluates.
+
      GNUPG_MAJOR_VERSION - Defined when used by GnuPG.
 
-     HAVE_DOSISH_SYSTEM   - Defined for Windows etc.  Will be
-                            automatically defined if a the target is
-                            Windows.
-     HAVE_POSIX_SYSTEM    - Internally defined to !HAVE_DOSISH_SYSTEM.
+     HAVE_DOSISH_SYSTEM  - Defined for Windows etc.  Will be
+                           automatically defined if a the target is
+                           Windows.
+
+     HAVE_POSIX_SYSTEM   - Internally defined to !HAVE_DOSISH_SYSTEM.
 
-     HAVE_SIGNAL_H        - Should be defined on Posix systems.  If config.h
-                            is not used defaults to defined.
+     HAVE_SIGNAL_H       - Should be defined on Posix systems.  If config.h
+                           is not used defaults to defined.
 
      DIRSEP_C            - Separation character for file name parts.
                            Usually not redefined.
-     EXTSEP_S "."        - Separation string for file name suffixes.
+
+     EXTSEP_S            - Separation string for file name suffixes.
                            Usually not redefined.
 
      HAVE_W32CE_SYSTEM   - Currently only used by GnuPG.
    it on the command line, remember to pass -D_FILE_OFFSET_BITS=64
 
 
+   Bugs:
+   =====
+
+   On Windows this module is not yet thread-safe.
+
+
    Miscellaneous notes:
    ====================
 
 #ifdef HAVE_SIGNAL_H
 # include <signal.h>
 #endif
+#ifdef DOTLOCK_USE_PTHREAD
+# include <pthread.h>
+#endif
 
 #ifdef DOTLOCK_GLIB_LOGGING
 # include <glib.h>
 #endif
 
 #ifdef GNUPG_MAJOR_VERSION
-# include "libjnlib-config.h"
+# include "util.h"
+# include "common-defs.h"
+# include "stringhelp.h"  /* For stpcpy and w32_strerror. */
 #endif
 #ifdef HAVE_W32CE_SYSTEM
 # include "utf8conv.h"  /* WindowsCE requires filename conversion.  */
 # endif
 #endif
 
-/* In GnuPG we use wrappers around the malloc fucntions.  If they are
+/* In GnuPG we use wrappers around the malloc functions.  If they are
    not defined we assume that this code is used outside of GnuPG and
    fall back to the regular malloc functions.  */
-#ifndef jnlib_malloc
-# define jnlib_malloc(a)     malloc ((a))
-# define jnlib_calloc(a,b)   calloc ((a), (b))
-# define jnlib_free(a)      free ((a))
+#ifndef xtrymalloc
+# define xtrymalloc(a)     malloc ((a))
+# define xtrycalloc(a,b)   calloc ((a), (b))
+# define xfree(a)         free ((a))
 #endif
 
-/* Wrapper to set ERRNO.  */
-#ifndef jnlib_set_errno
-# ifdef HAVE_W32CE_SYSTEM
-#  define jnlib_set_errno(e)  gpg_err_set_errno ((e))
-# else
-#  define jnlib_set_errno(e)  do { errno = (e); } while (0)
-# endif
+/* Wrapper to set ERRNO (required for W32CE).  */
+#ifdef GPG_ERROR_VERSION
+#  define my_set_errno(e)  gpg_err_set_errno ((e))
+#else
+#  define my_set_errno(e)  do { errno = (e); } while (0)
 #endif
 
 /* Gettext macro replacement.  */
 # define my_error_1(a,b)    log_error ((a), (b))
 # define my_error_2(a,b,c)  log_error ((a), (b), (c))
 # define my_debug_1(a,b)    log_debug ((a), (b))
+# define my_fatal_0(a)      log_fatal ((a))
 #elif defined (DOTLOCK_GLIB_LOGGING)
 # define my_info_0(a)       g_message ((a))
 # define my_info_1(a,b)     g_message ((a), (b))
 # define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d))
 # define my_error_0(a)      g_warning ((a))
 # define my_error_1(a,b)    g_warning ((a), (b))
-# define my_error_2(a,b,c   g_warning ((a), (b), (c))
+# define my_error_2(a,b,c)  g_warning ((a), (b), (c))
 # define my_debug_1(a,b)    g_debug ((a), (b))
+# define my_fatal_0(a)      g_error ((a))
 #else
 # define my_info_0(a)       fprintf (stderr, (a))
 # define my_info_1(a,b)     fprintf (stderr, (a), (b))
 # define my_error_1(a,b)    fprintf (stderr, (a), (b))
 # define my_error_2(a,b,c)  fprintf (stderr, (a), (b), (c))
 # define my_debug_1(a,b)    fprintf (stderr, (a), (b))
+# define my_fatal_0(a)      do { fprintf (stderr,(a)); fflush (stderr); \
+                                 abort (); } while (0)
 #endif
 
 
@@ -318,6 +399,8 @@ struct dotlock_handle
   unsigned int disable:1;    /* If true, locking is disabled.         */
   unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking.        */
 
+  int extra_fd;              /* A place for the caller to store an FD.  */
+
 #ifdef HAVE_DOSISH_SYSTEM
   HANDLE lockhd;       /* The W32 handle of the lock file.      */
 #else /*!HAVE_DOSISH_SYSTEM */
@@ -328,14 +411,66 @@ struct dotlock_handle
 };
 
 
-/* A list of of all lock handles.  The volatile attribute might help
-   if used in an atexit handler.  */
+/* A list of all lock handles.  The volatile attribute might help
+   if used in an atexit handler.  Note that [UN]LOCK_all_lockfiles
+   must not change ERRNO. */
 static volatile dotlock_t all_lockfiles;
+#ifdef DOTLOCK_USE_PTHREAD
+static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK_all_lockfiles() do {                               \
+        if (pthread_mutex_lock (&all_lockfiles_mutex))           \
+          my_fatal_0 ("locking all_lockfiles_mutex failed\n");   \
+      } while (0)
+# define UNLOCK_all_lockfiles() do {                             \
+        if (pthread_mutex_unlock (&all_lockfiles_mutex))         \
+          my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \
+      } while (0)
+#else  /*!DOTLOCK_USE_PTHREAD*/
+# define LOCK_all_lockfiles()   do { } while (0)
+# define UNLOCK_all_lockfiles() do { } while (0)
+#endif /*!DOTLOCK_USE_PTHREAD*/
 
 /* If this has the value true all locking is disabled.  */
 static int never_lock;
 
 
+
+\f
+#ifdef HAVE_DOSISH_SYSTEM
+static int
+map_w32_to_errno (DWORD w32_err)
+{
+  switch (w32_err)
+    {
+    case 0:
+      return 0;
+
+    case ERROR_FILE_NOT_FOUND:
+      return ENOENT;
+
+    case ERROR_PATH_NOT_FOUND:
+      return ENOENT;
+
+    case ERROR_ACCESS_DENIED:
+      return EPERM;
+
+    case ERROR_INVALID_HANDLE:
+    case ERROR_INVALID_BLOCK:
+      return EINVAL;
+
+    case ERROR_NOT_ENOUGH_MEMORY:
+      return ENOMEM;
+
+    case ERROR_NO_DATA:
+    case ERROR_BROKEN_PIPE:
+      return EPIPE;
+
+    default:
+      return EIO;
+    }
+}
+#endif /*HAVE_DOSISH_SYSTEM*/
+
 \f
 /* Entirely disable all locking.  This function should be called
    before any locking is done.  It may be called right at startup of
@@ -352,13 +487,19 @@ static int
 maybe_deadlock (dotlock_t h)
 {
   dotlock_t r;
+  int res = 0;
 
-  for ( r=all_lockfiles; r; r = r->next )
+  LOCK_all_lockfiles ();
+  for (r=all_lockfiles; r; r = r->next)
     {
       if ( r != h && r->locked )
-        return 1;
+        {
+          res = 1;
+          break;
+        }
     }
-  return 0;
+  UNLOCK_all_lockfiles ();
+  return res;
 }
 #endif /*HAVE_POSIX_SYSTEM*/
 
@@ -382,7 +523,7 @@ read_lockfile (dotlock_t h, int *same_node )
   expected_len = 10 + 1 + h->nodename_len + 1;
   if ( expected_len >= sizeof buffer_space)
     {
-      buffer = jnlib_malloc (expected_len);
+      buffer = xtrymalloc (expected_len);
       if (!buffer)
         return -1;
     }
@@ -392,11 +533,11 @@ read_lockfile (dotlock_t h, int *same_node )
   if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
     {
       int e = errno;
-      my_info_2 ("error opening lockfile `%s': %s\n",
+      my_info_2 ("error opening lockfile '%s': %s\n",
                  h->lockname, strerror(errno) );
       if (buffer != buffer_space)
-        jnlib_free (buffer);
-      jnlib_set_errno (e); /* Need to return ERRNO here. */
+        xfree (buffer);
+      my_set_errno (e); /* Need to return ERRNO here. */
       return -1;
     }
 
@@ -409,11 +550,12 @@ read_lockfile (dotlock_t h, int *same_node )
         continue;
       if (res < 0)
         {
-          my_info_1 ("error reading lockfile `%s'\n", h->lockname );
+          int e = errno;
+          my_info_1 ("error reading lockfile '%s'\n", h->lockname );
           close (fd);
           if (buffer != buffer_space)
-            jnlib_free (buffer);
-          jnlib_set_errno (0); /* Do not return an inappropriate ERRNO. */
+            xfree (buffer);
+          my_set_errno (e);
           return -1;
         }
       p += res;
@@ -424,10 +566,10 @@ read_lockfile (dotlock_t h, int *same_node )
 
   if (nread < 11)
     {
-      my_info_1 ("invalid size of lockfile `%s'\n", h->lockname);
+      my_info_1 ("invalid size of lockfile '%s'\n", h->lockname);
       if (buffer != buffer_space)
-        jnlib_free (buffer);
-      jnlib_set_errno (0); /* Better don't return an inappropriate ERRNO. */
+        xfree (buffer);
+      my_set_errno (EINVAL);
       return -1;
     }
 
@@ -435,10 +577,10 @@ read_lockfile (dotlock_t h, int *same_node )
       || (buffer[10] = 0, pid = atoi (buffer)) == -1
       || !pid )
     {
-      my_error_2 ("invalid pid %d in lockfile `%s'\n", pid, h->lockname);
+      my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname);
       if (buffer != buffer_space)
-        jnlib_free (buffer);
-      jnlib_set_errno (0);
+        xfree (buffer);
+      my_set_errno (EINVAL);
       return -1;
     }
 
@@ -448,7 +590,7 @@ read_lockfile (dotlock_t h, int *same_node )
     *same_node = 1;
 
   if (buffer != buffer_space)
-    jnlib_free (buffer);
+    xfree (buffer);
   return pid;
 }
 #endif /*HAVE_POSIX_SYSTEM */
@@ -472,13 +614,14 @@ use_hardlinks_p (const char *tname)
     return -1;
   nlink = (unsigned int)sb.st_nlink;
 
-  lname = jnlib_malloc (strlen (tname) + 1 + 1);
+  lname = xtrymalloc (strlen (tname) + 1 + 1);
   if (!lname)
     return -1;
   strcpy (lname, tname);
   strcat (lname, "x");
 
-  link (tname, lname);
+  /* We ignore the return value of link() because it is unreliable.  */
+  (void) link (tname, lname);
 
   if (stat (tname, &sb))
     res = -1;  /* Ooops.  */
@@ -488,7 +631,7 @@ use_hardlinks_p (const char *tname)
     res = 1;   /* No hardlink support.  */
 
   unlink (lname);
-  jnlib_free (lname);
+  xfree (lname);
   return res;
 }
 #endif /*HAVE_POSIX_SYSTEM */
@@ -528,18 +671,17 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
       dirpart = file_to_lock;
     }
 
-#ifdef _REENTRANT
-    /* fixme: aquire mutex on all_lockfiles */
-#endif
+  LOCK_all_lockfiles ();
   h->next = all_lockfiles;
   all_lockfiles = h;
 
   tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1;
-  h->tname = jnlib_malloc (tnamelen + 1);
+  h->tname = xtrymalloc (tnamelen + 1);
   if (!h->tname)
     {
       all_lockfiles = h->next;
-      jnlib_free (h);
+      UNLOCK_all_lockfiles ();
+      xfree (h);
       return NULL;
     }
   h->nodename_len = strlen (nodename);
@@ -551,7 +693,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
 
   do
     {
-      jnlib_set_errno (0);
+      my_set_errno (0);
       fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
                  S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
     }
@@ -559,11 +701,14 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
 
   if ( fd == -1 )
     {
+      int saveerrno = errno;
       all_lockfiles = h->next;
-      my_error_2 (_("failed to create temporary file `%s': %s\n"),
-                  h->tname, strerror(errno));
-      jnlib_free (h->tname);
-      jnlib_free (h);
+      UNLOCK_all_lockfiles ();
+      my_error_2 (_("failed to create temporary file '%s': %s\n"),
+                  h->tname, strerror (errno));
+      xfree (h->tname);
+      xfree (h);
+      my_set_errno (saveerrno);
       return NULL;
     }
   if ( write (fd, pidstr, 11 ) != 11 )
@@ -573,7 +718,12 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
   if ( write (fd, "\n", 1 ) != 1 )
     goto write_failed;
   if ( close (fd) )
-    goto write_failed;
+    {
+      if ( errno == EINTR )
+        fd = -1;
+      goto write_failed;
+    }
+  fd = -1;
 
   /* Check whether we support hard links.  */
   switch (use_hardlinks_p (h->tname))
@@ -585,39 +735,47 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
       h->use_o_excl = 1;
       break;
     default:
-      my_error_2 ("can't check whether hardlinks are supported for `%s': %s\n",
-                  h->tname, strerror(errno));
+      {
+        int saveerrno = errno;
+        my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n"
+                    , h->tname, strerror (saveerrno));
+        my_set_errno (saveerrno);
+      }
       goto write_failed;
     }
 
-# ifdef _REENTRANT
-  /* release mutex */
-# endif
-  h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 );
+  h->lockname = xtrymalloc (strlen (file_to_lock) + 6 );
   if (!h->lockname)
     {
+      int saveerrno = errno;
       all_lockfiles = h->next;
+      UNLOCK_all_lockfiles ();
       unlink (h->tname);
-      jnlib_free (h->tname);
-      jnlib_free (h);
+      xfree (h->tname);
+      xfree (h);
+      my_set_errno (saveerrno);
       return NULL;
     }
   strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
+  UNLOCK_all_lockfiles ();
   if (h->use_o_excl)
-    my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname);
+    my_debug_1 ("locking for '%s' done via O_EXCL\n", h->lockname);
 
   return h;
 
  write_failed:
-  all_lockfiles = h->next;
-# ifdef _REENTRANT
-  /* fixme: release mutex */
-# endif
-  my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno));
-  close (fd);
-  unlink (h->tname);
-  jnlib_free (h->tname);
-  jnlib_free (h);
+  {
+    int saveerrno = errno;
+    all_lockfiles = h->next;
+    UNLOCK_all_lockfiles ();
+    my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno));
+    if ( fd != -1 )
+      close (fd);
+    unlink (h->tname);
+    xfree (h->tname);
+    xfree (h);
+    my_set_errno (saveerrno);
+  }
   return NULL;
 }
 #endif /*HAVE_POSIX_SYSTEM*/
@@ -632,14 +790,16 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
 static dotlock_t
 dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
 {
+  LOCK_all_lockfiles ();
   h->next = all_lockfiles;
   all_lockfiles = h;
 
-  h->lockname = jnlib_malloc ( strlen (file_to_lock) + 6 );
+  h->lockname = xtrymalloc ( strlen (file_to_lock) + 6 );
   if (!h->lockname)
     {
       all_lockfiles = h->next;
-      jnlib_free (h);
+      UNLOCK_all_lockfiles ();
+      xfree (h);
       return NULL;
     }
   strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
@@ -656,25 +816,30 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
 #ifdef HAVE_W32CE_SYSTEM
     wchar_t *wname = utf8_to_wchar (h->lockname);
 
-    h->lockhd = INVALID_HANDLE_VALUE;
     if (wname)
       h->lockhd = CreateFile (wname,
+                              GENERIC_READ|GENERIC_WRITE,
+                              FILE_SHARE_READ|FILE_SHARE_WRITE,
+                              NULL, OPEN_ALWAYS, 0, NULL);
+    else
+      h->lockhd = INVALID_HANDLE_VALUE;
+    xfree (wname);
 #else
     h->lockhd = CreateFile (h->lockname,
-#endif
                             GENERIC_READ|GENERIC_WRITE,
                             FILE_SHARE_READ|FILE_SHARE_WRITE,
                             NULL, OPEN_ALWAYS, 0, NULL);
-#ifdef HAVE_W32CE_SYSTEM
-    jnlib_free (wname);
 #endif
   }
   if (h->lockhd == INVALID_HANDLE_VALUE)
     {
-      my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
+      int saveerrno = map_w32_to_errno (GetLastError ());
       all_lockfiles = h->next;
-      jnlib_free (h->lockname);
-      jnlib_free (h);
+      UNLOCK_all_lockfiles ();
+      my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1));
+      xfree (h->lockname);
+      xfree (h);
+      my_set_errno (saveerrno);
       return NULL;
     }
   return h;
@@ -696,12 +861,15 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
    POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is
    used.
 
+   FLAGS must be 0.
+
    The function returns an new handle which needs to be released using
    destroy_dotlock but gets also released at the termination of the
    process.  On error NULL is returned.
  */
+
 dotlock_t
-dotlock_create (const char *file_to_lock)
+dotlock_create (const char *file_to_lock, unsigned int flags)
 {
   static int initialized;
   dotlock_t h;
@@ -715,18 +883,24 @@ dotlock_create (const char *file_to_lock)
   if ( !file_to_lock )
     return NULL;  /* Only initialization was requested.  */
 
-  h = jnlib_calloc (1, sizeof *h);
+  if (flags)
+    {
+      my_set_errno (EINVAL);
+      return NULL;
+    }
+
+  h = xtrycalloc (1, sizeof *h);
   if (!h)
     return NULL;
+  h->extra_fd = -1;
 
   if (never_lock)
     {
       h->disable = 1;
-#ifdef _REENTRANT
-      /* fixme: aquire mutex on all_lockfiles */
-#endif
+      LOCK_all_lockfiles ();
       h->next = all_lockfiles;
       all_lockfiles = h;
+      UNLOCK_all_lockfiles ();
       return h;
     }
 
@@ -739,6 +913,24 @@ dotlock_create (const char *file_to_lock)
 
 
 \f
+/* Convenience function to store a file descriptor (or any other
+   integer value) in the context of handle H.  */
+void
+dotlock_set_fd (dotlock_t h, int fd)
+{
+  h->extra_fd = fd;
+}
+
+/* Convenience function to retrieve a file descriptor (or any other
+   integer value) stored in the context of handle H.  */
+int
+dotlock_get_fd (dotlock_t h)
+{
+  return h->extra_fd;
+}
+
+
+\f
 #ifdef HAVE_POSIX_SYSTEM
 /* Unix specific code of destroy_dotlock.  */
 static void
@@ -748,7 +940,7 @@ dotlock_destroy_unix (dotlock_t h)
     unlink (h->lockname);
   if (h->tname && !h->use_o_excl)
     unlink (h->tname);
-  jnlib_free (h->tname);
+  xfree (h->tname);
 }
 #endif /*HAVE_POSIX_SYSTEM*/
 
@@ -770,7 +962,7 @@ dotlock_destroy_w32 (dotlock_t h)
 #endif /*HAVE_DOSISH_SYSTEM*/
 
 
-/* Destroy the locck handle H and release the lock.  */
+/* Destroy the lock handle H and release the lock.  */
 void
 dotlock_destroy (dotlock_t h)
 {
@@ -780,6 +972,7 @@ dotlock_destroy (dotlock_t h)
     return;
 
   /* First remove the handle from our global list of all locks. */
+  LOCK_all_lockfiles ();
   for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
     if (htmp == h)
       {
@@ -790,6 +983,7 @@ dotlock_destroy (dotlock_t h)
         h->next = NULL;
         break;
       }
+  UNLOCK_all_lockfiles ();
 
   /* Then destroy the lock. */
   if (!h->disable)
@@ -799,9 +993,9 @@ dotlock_destroy (dotlock_t h)
 #else /* !HAVE_DOSISH_SYSTEM */
       dotlock_destroy_unix (h);
 #endif /* HAVE_DOSISH_SYSTEM */
-      jnlib_free (h->lockname);
+      xfree (h->lockname);
     }
-  jnlib_free(h);
+  xfree(h);
 }
 
 
@@ -819,6 +1013,7 @@ dotlock_take_unix (dotlock_t h, long timeout)
   int ownerchanged;
   const char *maybe_dead="";
   int same_node;
+  int saveerrno;
 
  again:
   if (h->use_o_excl)
@@ -828,7 +1023,7 @@ dotlock_take_unix (dotlock_t h, long timeout)
 
       do
         {
-          jnlib_set_errno (0);
+          my_set_errno (0);
           fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL,
                      S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
         }
@@ -838,8 +1033,10 @@ dotlock_take_unix (dotlock_t h, long timeout)
         ; /* Lock held by another process.  */
       else if (fd == -1)
         {
-          my_error_2 ("lock not made: open(O_EXCL) of `%s' failed: %s\n",
-                      h->lockname, strerror (errno));
+          saveerrno = errno;
+          my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n",
+                      h->lockname, strerror (saveerrno));
+          my_set_errno (saveerrno);
           return -1;
         }
       else
@@ -857,10 +1054,12 @@ dotlock_take_unix (dotlock_t h, long timeout)
               return 0;
             }
           /* Write error.  */
-          my_error_2 ("lock not made: writing to `%s' failed: %s\n",
+          saveerrno = errno;
+          my_error_2 ("lock not made: writing to '%s' failed: %s\n",
                       h->lockname, strerror (errno));
           close (fd);
           unlink (h->lockname);
+          my_set_errno (saveerrno);
           return -1;
         }
     }
@@ -868,15 +1067,18 @@ dotlock_take_unix (dotlock_t h, long timeout)
     {
       struct stat sb;
 
-      link (h->tname, h->lockname);
+      /* We ignore the return value of link() because it is unreliable.  */
+      (void) link (h->tname, h->lockname);
 
       if (stat (h->tname, &sb))
         {
+          saveerrno = errno;
           my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n",
                       strerror (errno));
           /* In theory this might be a severe error: It is possible
              that link succeeded but stat failed due to changed
              permissions.  We can't do anything about it, though.  */
+          my_set_errno (saveerrno);
           return -1;
         }
 
@@ -892,7 +1094,9 @@ dotlock_take_unix (dotlock_t h, long timeout)
     {
       if ( errno != ENOENT )
         {
+          saveerrno = errno;
           my_info_0 ("cannot read lockfile\n");
+          my_set_errno (saveerrno);
           return -1;
         }
       my_info_0 ("lockfile disappeared\n");
@@ -956,7 +1160,7 @@ dotlock_take_unix (dotlock_t h, long timeout)
       goto again;
     }
 
-  jnlib_set_errno (EACCES);
+  my_set_errno (EACCES);
   return -1;
 }
 #endif /*HAVE_POSIX_SYSTEM*/
@@ -985,8 +1189,9 @@ dotlock_take_w32 (dotlock_t h, long timeout)
   w32err = GetLastError ();
   if (w32err != ERROR_LOCK_VIOLATION)
     {
-      my_error_2 (_("lock `%s' not made: %s\n"),
+      my_error_2 (_("lock '%s' not made: %s\n"),
                   h->lockname, w32_strerror (w32err));
+      my_set_errno (map_w32_to_errno (w32err));
       return -1;
     }
 
@@ -1017,6 +1222,7 @@ dotlock_take_w32 (dotlock_t h, long timeout)
       goto again;
     }
 
+  my_set_errno (EACCES);
   return -1;
 }
 #endif /*HAVE_DOSISH_SYSTEM*/
@@ -1035,7 +1241,7 @@ dotlock_take (dotlock_t h, long timeout)
 
   if ( h->locked )
     {
-      my_debug_1 ("Oops, `%s' is already locked\n", h->lockname);
+      my_debug_1 ("Oops, '%s' is already locked\n", h->lockname);
       return 0;
     }
 
@@ -1056,23 +1262,29 @@ static int
 dotlock_release_unix (dotlock_t h)
 {
   int pid, same_node;
+  int saveerrno;
 
   pid = read_lockfile (h, &same_node);
   if ( pid == -1 )
     {
+      saveerrno = errno;
       my_error_0 ("release_dotlock: lockfile error\n");
+      my_set_errno (saveerrno);
       return -1;
     }
   if ( pid != getpid() || !same_node )
     {
       my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid);
+      my_set_errno (EACCES);
       return -1;
     }
 
   if ( unlink( h->lockname ) )
     {
-      my_error_1 ("release_dotlock: error removing lockfile `%s'\n",
+      saveerrno = errno;
+      my_error_1 ("release_dotlock: error removing lockfile '%s'\n",
                   h->lockname);
+      my_set_errno (saveerrno);
       return -1;
     }
   /* Fixme: As an extra check we could check whether the link count is
@@ -1092,8 +1304,10 @@ dotlock_release_w32 (dotlock_t h)
   memset (&ovl, 0, sizeof ovl);
   if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl))
     {
-      my_error_2 ("release_dotlock: error removing lockfile `%s': %s\n",
+      int saveerrno = map_w32_to_errno (GetLastError ());
+      my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n",
                   h->lockname, w32_strerror (-1));
+      my_set_errno (saveerrno);
       return -1;
     }
 
@@ -1112,7 +1326,10 @@ dotlock_release (dotlock_t h)
      any locks left.  It might happen that another atexit handler
      tries to release the lock while the atexit handler of this module
      already ran and thus H is undefined.  */
-  if (!all_lockfiles)
+  LOCK_all_lockfiles ();
+  ret = !all_lockfiles;
+  UNLOCK_all_lockfiles ();
+  if (ret)
     return 0;
 
   if ( h->disable )
@@ -1120,7 +1337,7 @@ dotlock_release (dotlock_t h)
 
   if ( !h->locked )
     {
-      my_debug_1 ("Oops, `%s' is not locked\n", h->lockname);
+      my_debug_1 ("Oops, '%s' is not locked\n", h->lockname);
       return 0;
     }
 
@@ -1137,7 +1354,7 @@ dotlock_release (dotlock_t h)
 
 
 \f
-/* Remove all lockfiles.  This is usually called by the atexit handler
+/* Remove all lockfiles.  This is called by the atexit handler
    installed by this module but may also be called by other
    termination handlers.  */
 void
@@ -1145,8 +1362,13 @@ dotlock_remove_lockfiles (void)
 {
   dotlock_t h, h2;
 
+  /* First set the lockfiles list to NULL so that for example
+     dotlock_release is aware that this function is currently
+     running.  */
+  LOCK_all_lockfiles ();
   h = all_lockfiles;
   all_lockfiles = NULL;
+  UNLOCK_all_lockfiles ();
 
   while ( h )
     {