Improve dotlocking.
authorWerner Koch <wk@gnupg.org>
Tue, 5 May 2009 15:32:16 +0000 (15:32 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 5 May 2009 15:32:16 +0000 (15:32 +0000)
Implement locking for W32.

NEWS
g10/ChangeLog
util/ChangeLog
util/dotlock.c

diff --git a/NEWS b/NEWS
index befd148..e76bcdb 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ Noteworthy changes in version 1.4.10 (unreleased)
     * The algorithm to compute the SIG_ID status has been changed to
       match the one from 2.0.10.
 
+    * Improved file locking.  Implemented it for W32.
+
 
 Noteworthy changes in version 1.4.9 (2008-03-26)
 ------------------------------------------------
index 52696c4..7b5b546 100644 (file)
@@ -1,12 +1,12 @@
 2009-05-05  Werner Koch  <wk@g10code.com>
 
-       * keygen.c (output_control_s): s/create/creation/.
+       * keygen.c (read_parameter_file): Add keyword "Creation-Date".
+       (output_control_s): s/create/creation/.
        (enum para_name): Add pCREATIONDATE, pKEYCREATIONDATE.  Remove
        pCREATETIME.
        (generate_keypair): Do not set old pCREATETIME.
        (parse_creation_string): New.
        (proc_parameter_file): Set pCREATIONDATE.
-       (read_parameter_file): Add keyword "Creation-Date".
        (do_generate_keypair): Remove arg TIMESTAMP. Set it using
        pKEYCREATIONDATE.
        (get_parameter_u32): Set a default pKEYCREATIONDATE.
index f99f0a4..85d5a49 100644 (file)
@@ -1,5 +1,8 @@
 2009-05-05  Werner Koch  <wk@g10code.com>
 
+       * dotlock.c: Merged changes from GnuPG-2.  Better detection of
+       stale lockfiles and actual locking support on W32.  Fixes bug#1028.
+
        * miscutil.c (isotime2seconds): New.
 
 2009-04-05  David Shaw  <dshaw@jabberwocky.com>
index 8369af7..ccbb6e8 100644 (file)
@@ -1,6 +1,6 @@
 /* dotlock.c - dotfile locking
  * Copyright (C) 1998, 1999, 2000, 2001, 2004,
- *               2005 Free Software Foundation, Inc.
+ *               2005, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <ctype.h>
 #include <errno.h>
 #include <unistd.h>
-#if !defined (HAVE_DOSISH_SYSTEM)
-#include <sys/utsname.h>
+#ifdef  HAVE_DOSISH_SYSTEM
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#else
+# include <sys/utsname.h>
 #endif
 #include <sys/types.h>
 #ifndef _WIN32
 #include "types.h"
 #include "util.h"
 #include "memory.h"
+#include "i18n.h"
 
+
+/* The object describing a lock.  */
 struct dotlock_handle {
     struct dotlock_handle *next;
     char *tname;    /* name of lockfile template */
     char *lockname; /* name of the real lockfile */
     int locked;     /* lock status */
     int disable;    /* locking */
+#ifdef HAVE_DOSISH_SYSTEM
+    HANDLE lockhd;  /* The W32 handle of the lock file.  */
+#else
+    size_t nodename_off; /* Offset in TNAME to the nodename part. */
+    size_t nodename_len; /* Length of the nodename part.  */
+#endif
 };
 
 
+/* A list of of all lock handles. */
 static volatile DOTLOCK all_lockfiles;
+
+/* If this has the value true all locking is disabled.  */
 static int never_lock;
 
+
+#ifdef _REENTRANT
+ /* fixme: acquire mutex on all_lockfiles */
+# define lock_all_lockfiles(h)  do {  } while (0) 
+# define unlock_all_lockfiles(h) do {  } while (0)
+#else
+# define lock_all_lockfiles(h)  do {  } while (0) 
+# define unlock_all_lockfiles(h) do {  } while (0)
+#endif
+
+
+
 void
 disable_dotlock(void)
 {
-    never_lock = 1;
+  never_lock = 1;
 }
 
+
 /****************
  * Create a lockfile with the given name and return an object of
  * type DOTLOCK which may be used later to actually do the lock.
@@ -78,14 +106,16 @@ create_dotlock( const char *file_to_lock )
 {
     static int initialized;
     DOTLOCK h;
-#if !defined (HAVE_DOSISH_SYSTEM)
+#ifndef HAVE_DOSISH_SYSTEM
     int fd = -1;
     char pidstr[16];
     struct utsname utsbuf;
     const char *nodename;
     const char *dirpart;
     int dirpartlen;
-#endif
+    size_t tnamelen;
+    int n;
+#endif /*!HAVE_DOSISH_SYSTEM*/
 
     if( !initialized ) {
        atexit( remove_lockfiles );
@@ -97,18 +127,15 @@ create_dotlock( const char *file_to_lock )
     h = xmalloc_clear( sizeof *h );
     if( never_lock ) {
        h->disable = 1;
-#ifdef _REENTRANT
-       /* fixme: aquire mutex on all_lockfiles */
-#endif
+        lock_all_lockfiles (h);
        h->next = all_lockfiles;
        all_lockfiles = h;
        return h;
     }
 
 
-#if !defined (HAVE_DOSISH_SYSTEM)
-    sprintf( pidstr, "%10d\n", (int)getpid() );
-    /* fixme: add the hostname to the second line (FQDN or IP addr?) */
+#ifndef HAVE_DOSISH_SYSTEM
+    snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() );
 
     /* create a temporary file */
     if( uname( &utsbuf ) )
@@ -116,14 +143,14 @@ create_dotlock( const char *file_to_lock )
     else
        nodename = utsbuf.nodename;
 
-#ifdef __riscos__
+# ifdef __riscos__
     {
         char *iter = (char *) nodename;
         for (; iter[0]; iter++)
             if (iter[0] == '.')
                 iter[0] = '/';
     }
-#endif /* __riscos__ */
+# endif /* __riscos__ */
 
     if( !(dirpart = strrchr( file_to_lock, DIRSEP_C )) ) {
        dirpart = EXTSEP_S;
@@ -134,25 +161,27 @@ create_dotlock( const char *file_to_lock )
        dirpart = file_to_lock;
     }
 
-#ifdef _REENTRANT
-    /* fixme: aquire mutex on all_lockfiles */
-#endif
+    lock_all_lockfiles (h);
     h->next = all_lockfiles;
     all_lockfiles = h;
 
-    h->tname = xmalloc( dirpartlen + 6+30+ strlen(nodename) + 11 );
-#ifndef __riscos__
-    sprintf( h->tname, "%.*s/.#lk%p.%s.%d",
-            dirpartlen, dirpart, (void *)h, nodename, (int)getpid() );
-#else /* __riscos__ */
-    sprintf( h->tname, "%.*s.lk%p/%s/%d",
-            dirpartlen, dirpart, (void *)h, nodename, (int)getpid() );
-#endif /* __riscos__ */
+    tnamelen = dirpartlen + 6+30+ strlen(nodename) + 10;
+    h->tname = xmalloc (tnamelen + 1);
+    h->nodename_len = strlen (nodename);
+
+# ifndef __riscos__
+    snprintf (h->tname, tnamelen, "%.*s/.#lk%p.%n%s.%d",
+              dirpartlen, dirpart, (void *)h, &n, nodename, (int)getpid ());
+# else /* __riscos__ */
+    snprintf (h->tname, tnamelen, "%.*s.lk%p/%n%s/%d",
+              dirpartlen, dirpart, (void *)h, &n, nodename, (int)getpid ());
+# endif /* __riscos__ */
+    h->nodename_off = n;
 
     do {
        errno = 0;
-       fd = openh->tname, O_WRONLY|O_CREAT|O_EXCL,
-                         S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
+       fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
+                   S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR);
     } while( fd == -1 && errno == EINTR );
     if( fd == -1 ) {
        all_lockfiles = h->next;
@@ -162,78 +191,110 @@ create_dotlock( const char *file_to_lock )
        xfree(h);
        return NULL;
     }
-    if( write(fd, pidstr, 11 ) != 11 ) {
-       all_lockfiles = h->next;
-#ifdef _REENTRANT
-       /* release mutex */
-#endif
-       log_fatal( "error writing to `%s': %s\n", h->tname, strerror(errno) );
-       close(fd);
-       unlink(h->tname);
-       xfree(h->tname);
-       xfree(h);
-       return NULL;
-    }
-    if( close(fd) ) {
-       all_lockfiles = h->next;
-#ifdef _REENTRANT
-       /* release mutex */
-#endif
-       log_error( "error closing `%s': %s\n", h->tname, strerror(errno));
-       unlink(h->tname);
-       xfree(h->tname);
-       xfree(h);
-       return NULL;
-    }
-
-#ifdef _REENTRANT
-    /* release mutex */
-#endif
-#endif
+    if (write (fd, pidstr, 11) != 11) 
+      goto write_failed;
+    if (write (fd, nodename, strlen (nodename)) != strlen (nodename))
+      goto write_failed;
+    if (write (fd, "\n", 1 ) != 1)
+      goto write_failed;
+    if (close (fd))
+      goto write_failed;
+
+    unlock_all_lockfiles (h);
     h->lockname = xmalloc( strlen(file_to_lock) + 6 );
     strcpy(stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
     return h;
+
+ write_failed:
+    all_lockfiles = h->next;
+    unlock_all_lockfiles (h);
+    log_error ("error writing to `%s': %s\n", h->tname, strerror(errno));
+    close (fd);
+    unlink (h->tname);
+    xfree (h->tname);
+    xfree (h);
+    return NULL;
+
+#else /* HAVE_DOSISH_SYSTEM */
+
+  /* The Windows version does not need a temporary file but uses the
+     plain lock file along with record locking.  We create this file
+     here so that we later do only need to do the file locking.  For
+     error reporting it is useful to keep the name of the file in the
+     handle.  */
+  h->next = all_lockfiles;
+  all_lockfiles = h;
+
+  h->lockname = xmalloc ( strlen (file_to_lock) + 6 );
+  strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
+
+  /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE
+     along with FILE_SHARE_DELETE but that does not work due to a race
+     condition: Despite the OPEN_ALWAYS flag CreateFile may return an
+     error and we can't reliable create/open the lock file unless we
+     would wait here until it works - however there are other valid
+     reasons why a lock file can't be created and thus the process
+     would not stop as expected but spin until Windows crashes.  Our
+     solution is to keep the lock file open; that does not harm. */ 
+  h->lockhd = CreateFile (h->lockname,
+                          GENERIC_READ|GENERIC_WRITE,
+                          FILE_SHARE_READ|FILE_SHARE_WRITE,
+                          NULL, OPEN_ALWAYS, 0, NULL);
+  if (h->lockhd == INVALID_HANDLE_VALUE)
+    {
+      log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
+      all_lockfiles = h->next;
+      xfree (h->lockname);
+      xfree (h);
+      return NULL;
+    }
+  return h;
+
+#endif /* HAVE_DOSISH_SYSTEM */
 }
 
 
 void
 destroy_dotlock ( DOTLOCK h )
 {
-#if !defined (HAVE_DOSISH_SYSTEM)
-    if ( h )
-      {
-        DOTLOCK hprev, htmp;
+  DOTLOCK hprev, htmp;
 
-        /* First remove the handle from our global list of all locks. */
-        for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
-          if (htmp == h)
-            {
-              if (hprev)
-                hprev->next = htmp->next;
-              else
-                all_lockfiles = htmp->next;
-              h->next = NULL;
-              break;
-            }
-
-        /* Second destroy the lock. */
-       if (!h->disable)
-          {
-           if (h->locked && h->lockname)
-              unlink (h->lockname);
-            if (h->tname)
-              unlink (h->tname);
-           xfree (h->tname);
-           xfree (h->lockname);
-          }
-       xfree(h);
+  if (!h)
+    return;
 
+  /* First remove the handle from our global list of all locks. */
+  for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
+    if (htmp == h)
+      {
+        if (hprev)
+          hprev->next = htmp->next;
+        else
+          all_lockfiles = htmp->next;
+        h->next = NULL;
+        break;
       }
+
+  /* Second destroy the lock. */
+  if (!h->disable)
+    {
+#ifdef HAVE_DOSISH_SYSTEM
+      if (h->locked)
+        UnlockFile (h->lockhd, 0, 0, 1, 0);
+      CloseHandle (h->lockhd);
+#else
+      if (h->locked && h->lockname)
+        unlink (h->lockname);
+      if (h->tname)
+        unlink (h->tname);
+      xfree (h->tname);
 #endif
+      xfree (h->lockname);
+    }
+  xfree(h);
 }
 
-#ifndef HAVE_DOSISH_SYSTEM
 
+#ifndef HAVE_DOSISH_SYSTEM
 static int
 maybe_deadlock( DOTLOCK h )
 {
@@ -245,44 +306,101 @@ maybe_deadlock( DOTLOCK h )
     }
     return 0;
 }
+#endif /* !HAVE_DOSISH_SYSTEM */
 
-/****************
- * Read the lock file and return the pid, returns -1 on error.
- */
+
+/* Read the lock file and return the pid, returns -1 on error.  True
+   will be stored in the integer at address SAME_NODE if the lock file
+   has been created on the same node. */
+#ifndef HAVE_DOSISH_SYSTEM
 static int
-read_lockfile( const char *name )
+read_lockfile (DOTLOCK h, int *same_node )
 {
-    int fd, pid;
-    char pidstr[16];
+  char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node
+                                   names are usually shorter.  */
+  int fd;
+  int pid = -1;
+  char *buffer, *p;
+  size_t expected_len;
+  int res, nread;
+  
+  *same_node = 0;
+  expected_len = 10 + 1 + h->nodename_len + 1;
+  if ( expected_len >= sizeof buffer_space)
+    buffer = xmalloc (expected_len);
+  else
+    buffer = buffer_space;
+
+  if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
+    {
+      int e = errno;
+      log_info ("error opening lockfile `%s': %s\n",
+                h->lockname, strerror(errno) );
+      if (buffer != buffer_space)
+        xfree (buffer);
+      errno = e; /* Need to return ERRNO here. */
+      return -1;
+    }
 
-    if( (fd = open(name, O_RDONLY)) == -1 ) {
-       int e = errno;
-       log_debug("error opening lockfile `%s': %s\n", name, strerror(errno) );
-       errno = e;
-       return -1;
+  p = buffer;
+  nread = 0;
+  do
+    {
+      res = read (fd, p, expected_len - nread);
+      if (res == -1 && errno == EINTR)
+        continue;
+      if (res < 0)
+        {
+          log_info ("error reading lockfile `%s'", h->lockname );
+          close (fd); 
+          if (buffer != buffer_space)
+            xfree (buffer);
+          errno = 0; /* Do not return an inappropriate ERRNO. */
+          return -1;
+        }
+      p += res;
+      nread += res;
     }
-    if( read(fd, pidstr, 10 ) != 10 ) {  /* Read 10 digits w/o newline */
-       log_debug("error reading lockfile `%s'", name );
-       close(fd);
-       errno = 0;
-       return -1;
+  while (res && nread != expected_len);
+  close (fd);
+
+  if (nread < 11)
+    {
+      log_info ("invalid size of lockfile `%s'", h->lockname );
+      if (buffer != buffer_space)
+        xfree (buffer);
+      errno = 0; /* Better don't return an inappropriate ERRNO. */
+      return -1;
     }
-    pidstr[10] = 0;  /* terminate pid string */
-    close(fd);
-    pid = atoi(pidstr);
+
+  if (buffer[10] != '\n'
+      || (buffer[10] = 0, pid = atoi (buffer)) == -1
 #ifndef __riscos__
-    if( !pid || pid == -1 ) {
+      || !pid 
 #else /* __riscos__ */
-    if( (!pid && riscos_getpid()) || pid == -1 ) {
+      || (!pid && riscos_getpid())
 #endif /* __riscos__ */
-       log_error("invalid pid %d in lockfile `%s'", pid, name );
-       errno = 0;
-       return -1;
+      )
+    {
+      log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname );
+      if (buffer != buffer_space)
+        xfree (buffer);
+      errno = 0;
+      return -1;
     }
-    return pid;
+
+  if (nread == expected_len
+      && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) 
+      && buffer[11+h->nodename_len] == '\n')
+    *same_node = 1;
+
+  if (buffer != buffer_space)
+    xfree (buffer);
+  return pid;
 }
 #endif /* !HAVE_DOSISH_SYSTEM */
 
+
 /****************
  * Do a lock on H. A TIMEOUT of 0 returns immediately,
  * -1 waits forever (hopefully not), other
@@ -292,90 +410,129 @@ read_lockfile( const char *name )
 int
 make_dotlock( DOTLOCK h, long timeout )
 {
-#if defined (HAVE_DOSISH_SYSTEM)
+  int backoff=0;
+#ifndef HAVE_DOSISH_SYSTEM
+  int  pid;
+  const char *maybe_dead="";
+  int same_node;
+#endif  
+  if (h->disable)
     return 0;
-#else
-    int  pid;
-    const char *maybe_dead="";
-    int backoff=0;
-
-    if( h->disable ) {
-       return 0;
-    }
 
-    if( h->locked ) {
+  if (h->locked)
+    {
 #ifndef __riscos__
-       log_debug("oops, `%s' is already locked\n", h->lockname );
+      log_debug ("oops, `%s' is already locked\n", h->lockname);
 #endif /* !__riscos__ */
-       return 0;
+      return 0;
     }
-
-    for(;;) {
-#ifndef __riscos__
-       if( !link(h->tname, h->lockname) ) {
-           /* fixme: better use stat to check the link count */
-           h->locked = 1;
-           return 0; /* okay */
+  
+  for (;;)
+    {
+#ifndef HAVE_DOSISH_SYSTEM
+# ifndef __riscos__
+      if( !link(h->tname, h->lockname) )
+        {
+          /* fixme: better use stat to check the link count */
+          h->locked = 1;
+          return 0; /* okay */
        }
-       if( errno != EEXIST ) {
-           log_error( "lock not made: link() failed: %s\n", strerror(errno) );
-           return -1;
+      if (errno != EEXIST)
+        {
+          log_error ("lock not made: link() failed: %s\n", strerror(errno));
+          return -1;
        }
-#else /* __riscos__ */
-        if( !riscos_renamefile(h->tname, h->lockname) ) {
-            h->locked = 1;
-            return 0; /* okay */
+# else /* __riscos__ */
+      if ( !riscos_renamefile(h->tname, h->lockname) )
+        {
+          h->locked = 1;
+          return 0; /* okay */
         }
-        if( errno != EEXIST ) {
-           log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
-           return -1;
+      if (errno != EEXIST)
+        {
+          log_error ("lock not made: rename() failed: %s\n", strerror(errno));
+          return -1;
         }
-#endif /* __riscos__ */
-       if( (pid = read_lockfile(h->lockname)) == -1 ) {
-           if( errno != ENOENT ) {
-               log_info("cannot read lockfile\n");
-               return -1;
+# endif /* __riscos__ */
+
+      if ((pid = read_lockfile (h, &same_node)) == -1 )
+        {
+          if (errno != ENOENT)
+            {
+              log_info ("cannot read lockfile\n");
+              return -1;
            }
-           log_info( "lockfile disappeared\n");
-           continue;
+          log_info ("lockfile disappeared\n");
+          continue;
        }
-       else if( pid == getpid() ) {
-           log_info( "Oops: lock already held by us\n");
-           h->locked = 1;
-           return 0; /* okay */
+      else if (pid == getpid ())
+        {
+          log_info ("Oops: lock already held by us\n");
+          h->locked = 1;
+          return 0; /* okay */
        }
-       else if( kill(pid, 0) && errno == ESRCH ) {
-#ifndef __riscos__
-           maybe_dead = " - probably dead";
-#if 0 /* we should not do this without checking the permissions */
-              /* and the hostname */
-           log_info( "removing stale lockfile (created by %d)", pid );
-#endif
-#else /* __riscos__ */
-            /* we are *pretty* sure that the other task is dead and therefore
-               we remove the other lock file */
-            maybe_dead = " - probably dead - removing lock";
-            unlink(h->lockname);
-#endif /* __riscos__ */
+      else if (same_node && kill(pid, 0) && errno == ESRCH)
+        {
+# ifndef __riscos__
+          log_info (_("removing stale lockfile (created by %d)\n"), pid );
+          unlink (h->lockname);
+          continue;
+# else /* __riscos__ */
+          /* We are *pretty* sure that the other task is dead and
+             therefore we remove the other lock file. */
+          maybe_dead = " - probably dead - removing lock";
+          unlink (h->lockname);
+# endif /* __riscos__ */
        }
-       if( timeout == -1 ) {
-           struct timeval tv;
-           log_info( "waiting for lock (held by %d%s) %s...\n",
-                     pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
+
+      if (timeout == -1)
+        {
+          struct timeval tv;
+
+          log_info ("waiting for lock (held by %d%s) %s...\n",
+                    pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
 
 
-           /* can't use sleep, cause signals may be blocked */
+           /* We can't use sleep, because signals may be blocked.  */
            tv.tv_sec = 1 + backoff;
            tv.tv_usec = 0;
-           select(0, NULL, NULL, NULL, &tv);
-           if( backoff < 10 )
-               backoff++ ;
+           select (0, NULL, NULL, NULL, &tv);
+           if (backoff < 10)
+              backoff++ ;
        }
-       else
-           return -1;
+      else
+        return -1;
+
+#else /*HAVE_DOSISH_SYSTEM*/
+      int w32err;
+      
+      if (LockFile (h->lockhd, 0, 0, 1, 0))
+        {
+          h->locked = 1;
+          return 0; /* okay */
+        }
+      w32err = GetLastError ();
+      if (w32err != ERROR_LOCK_VIOLATION)
+        {
+          log_error (_("lock `%s' not made: %s\n"),
+                     h->lockname, w32_strerror (w32err));
+          return -1;
+        }
+      
+      if (timeout == -1) 
+        {
+          /* Wait until lock has been released. */
+          log_info (_("waiting for lock %s...\n"), h->lockname);
+          Sleep ((1 + backoff)*1000);
+          if ( backoff < 10 )
+            backoff++ ;
+       }
+      else
+        return -1;
+#endif /*HAVE_DOSISH_SYSTEM*/
     }
-    /*not reached */
-#endif
+  /*NOTREACHED */
 }
 
 
@@ -384,56 +541,71 @@ make_dotlock( DOTLOCK h, long timeout )
  * Returns: 0 := success
  */
 int
-release_dotlock( DOTLOCK h )
+release_dotlock (DOTLOCK h)
 {
-#if defined (HAVE_DOSISH_SYSTEM)
-    return 0;
-#else
-    int pid;
+#ifndef HAVE_DOSISH_SYSTEM
+  int pid, same_node;
+#endif
 
-    /* To avoid atexit race conditions we first check whether there
-       are 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)
-        return 0;
+  /* To avoid atexit race conditions we first check whether there are
+     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)
+    return 0;
 
-    if( h->disable 
-       return 0;
+  if (h->disable
+    return 0;
 
-    if( !h->locked ) {
-       log_debug("oops, `%s' is not locked\n", h->lockname );
-       return 0;
+  if (!h->locked)
+    {
+      log_debug("oops, `%s' is not locked\n", h->lockname );
+      return 0;
     }
 
-    pid = read_lockfile( h->lockname );
-    if( pid == -1 ) {
-       log_error( "release_dotlock: lockfile error\n");
-       return -1;
+#ifndef HAVE_DOSISH_SYSTEM
+  pid = read_lockfile (h, &same_node);
+  if (pid == -1)
+    {
+      log_error ("release_dotlock: lockfile error\n");
+      return -1;
     }
-    if( pid != getpid() ) {
-       log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
-       return -1;
+  if (pid != getpid () || !same_node)
+    {
+      log_error ("release_dotlock: not our lock (pid=%d)\n", pid);
+      return -1;
     }
-#ifndef __riscos__
-    if( unlink( h->lockname ) ) {
-       log_error( "release_dotlock: error removing lockfile `%s'",
-                                                       h->lockname);
-       return -1;
+# ifndef __riscos__
+  if (unlink (h->lockname))
+    {
+      log_error ("release_dotlock: error removing lockfile `%s'",
+                 h->lockname);
+      return -1;
     }
-#else /* __riscos__ */
-    if( riscos_renamefile(h->lockname, h->tname) ) {
-       log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
-                                                       h->lockname, h->tname);
-       return -1;
+# else /* __riscos__ */
+  if( riscos_renamefile(h->lockname, h->tname) ) 
+    {
+      log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
+                 h->lockname, h->tname);
+      return -1;
     }
-#endif /* __riscos__ */
-    /* fixme: check that the link count is now 1 */
-    h->locked = 0;
-    return 0;
-#endif
+# endif /* __riscos__ */
+
+#else /*HAVE_DOSISH_SYSTEM*/
+
+  if (!UnlockFile (h->lockhd, 0, 0, 1, 0))
+    {
+      log_error ("release_dotlock: error removing lockfile `%s': %s\n",
+                 h->lockname, w32_strerror (-1));
+      return -1;
+    }
+#endif /*HAVE_DOSISH_SYSTEM*/
+
+  h->locked = 0;
+  return 0;
 }
 
+
 void
 remove_lockfiles()
 {