OSDN Git Service

* dcrt0.cc (dll_crt0_1): Move internal locale setting prior to potential
[pf3gnuchains/pf3gnuchains3x.git] / winsup / cygwin / flock.cc
index 1367060..e31add8 100644 (file)
@@ -1,6 +1,6 @@
 /* flock.cc.  NT specific implementation of advisory file locking.
 
-   Copyright 2003, 2008 Red Hat, Inc.
+   Copyright 2003, 2008, 2009 Red Hat, Inc.
 
    This file is part of Cygwin.
 
@@ -13,8 +13,8 @@
    The essential code of the lf_XXX functions has been taken from the
    module src/sys/kern/kern_lockf.c.  It has been adapted to use NT
    global namespace subdirs and event objects for synchronization
-   purposes. 
-   
+   purposes.
+
    So, the following copyright applies to most of the code in the lf_XXX
    functions.
 
@@ -49,7 +49,7 @@
  * SUCH DAMAGE.
  *
  *      @(#)ufs_lockf.c 8.3 (Berkeley) 1/6/94
-*/   
+*/
 
 /*
  * The flock() function is based upon source taken from the Red Hat
  * $RH: flock.c,v 1.2 2000/08/23 17:07:00 nalin Exp $
  */
 
-/* The lockf function has been taken from FreeBSD with the following
+/* The lockf function is based upon FreeBSD sources with the following
  * copyright.
- *
+ */
+/*
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * 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. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *        This product includes software developed by the NetBSD
- *        Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  * 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.
- *
- *
- * $NetBSD: lockf.c,v 1.1 1997/12/20 20:23:18 kleink Exp $
-*/
+ */
 
 #include "winsup.h"
 #include <assert.h>
 #include <sys/file.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include "cygerrno.h"
-#include "perprocess.h"
 #include "security.h"
-#include "cygwin/version.h"
+#include "shared_info.h"
 #include "path.h"
 #include "fhandler.h"
 #include "dtable.h"
 #include "cygheap.h"
-#include "shared_info.h"
 #include "pinfo.h"
 #include "sigproc.h"
 #include "cygtls.h"
 #include <sys/queue.h>
 #include <wchar.h>
 
-/* Right now we implement flock(2) locks using the POSIX semantics
-   in terms of inheritance and removal of locks.
-
-   TODO: How to implement real BSD flock semantics?
-
-        From the Linux man page:
-
-        Locks created by flock() are associated with an open file table
-        entry.  This means that duplicate file descriptors (created by,
-        for example, fork(2) or dup(2)) refer to the same lock, and
-        this lock may be modified or released using any of these
-        descriptors.  Furthermore, the lock is released either by an
-        explicit LOCK_UN operation on any of these duplicate
-        descriptors, or when all such descriptors have been closed.
-
-        If a process uses open(2) (or similar) to obtain more than one
-        descrip- tor for the same file, these descriptors are treated
-        independently by flock().  An attempt to lock the file using
-        one of these file descriptors may be denied by a lock that the
-        calling process has already placed via another descriptor.  */
-
 #define F_WAIT 0x10    /* Wait until lock is granted */
 #define F_FLOCK 0x20   /* Use flock(2) semantics for lock */
 #define F_POSIX        0x40    /* Use POSIX semantics for lock */
@@ -156,12 +123,21 @@ static NO_COPY muto lockf_guard;
 #define INODE_LIST_LOCK()      (lockf_guard.init ("lockf_guard")->acquire ())
 #define INODE_LIST_UNLOCK()    (lockf_guard.release ())
 
-#define LOCK_OBJ_NAME_LEN      56
+#define LOCK_OBJ_NAME_LEN      64
+
+#define FLOCK_INODE_DIR_ACCESS (DIRECTORY_QUERY \
+                                | DIRECTORY_TRAVERSE \
+                                | DIRECTORY_CREATE_OBJECT \
+                                | READ_CONTROL)
+
+#define FLOCK_EVENT_ACCESS     (EVENT_QUERY_STATE \
+                                | SYNCHRONIZE \
+                                | READ_CONTROL)
 
 /* This function takes the own process security descriptor DACL and adds
-   SYNCHRONIZE permissions for everyone.  This allows to wait any process
-   to wait for this process to set the event object to signalled in case
-   the lock gets removed or replaced. */
+   SYNCHRONIZE permissions for everyone.  This allows all processes
+   to wait for this process to die when blocking in a F_SETLKW on a lock
+   which is hold by this process. */
 static void
 allow_others_to_sync ()
 {
@@ -179,7 +155,8 @@ allow_others_to_sync ()
      to avoid having to alloc 64K from the stack. */
   tmp_pathbuf tp;
   PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) tp.w_get ();
-  status = NtQuerySecurityObject (hMainProc, DACL_SECURITY_INFORMATION, sd,
+  status = NtQuerySecurityObject (NtCurrentProcess (),
+                                 DACL_SECURITY_INFORMATION, sd,
                                  NT_MAX_PATH * sizeof (WCHAR), &len);
   if (!NT_SUCCESS (status))
     {
@@ -206,7 +183,7 @@ allow_others_to_sync ()
     }
   dacl->AclSize = (char *) ace - (char *) dacl;
   /* Write the DACL back. */
-  status = NtSetSecurityObject (hMainProc, DACL_SECURITY_INFORMATION, sd);
+  status = NtSetSecurityObject (NtCurrentProcess (), DACL_SECURITY_INFORMATION, sd);
   if (!NT_SUCCESS (status))
     {
       debug_printf ("NtSetSecurityObject: %p", status);
@@ -215,67 +192,20 @@ allow_others_to_sync ()
   done = true;
 }
 
-/* Helper function to create an event security descriptor which only allows
-   SYNCHRONIZE access to everyone.  Only the creating process has all access
-   rights. */
-static PSECURITY_DESCRIPTOR
-everyone_sync_sd ()
-{
-  static PSECURITY_DESCRIPTOR psd;
-
-  if (!psd)
-    {
-      const size_t acl_len = sizeof (ACL) +
-                            sizeof (ACCESS_ALLOWED_ACE) + MAX_SID_LEN;
-      psd = (PSECURITY_DESCRIPTOR)
-           malloc (sizeof (SECURITY_DESCRIPTOR) + acl_len);
-      InitializeSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
-      PACL dacl = (PACL) (psd + 1);
-      InitializeAcl (dacl, acl_len, ACL_REVISION);
-      if (!AddAccessAllowedAce (dacl, ACL_REVISION, SYNCHRONIZE, 
-                               well_known_world_sid))
-       {
-         debug_printf ("AddAccessAllowedAce: %lu", GetLastError ());
-         return NULL;
-       }
-      LPVOID ace;
-      if (!FindFirstFreeAce (dacl, &ace))
-       {
-         debug_printf ("FindFirstFreeAce: %lu", GetLastError ());
-         return NULL;
-       }
-      dacl->AclSize = (char *) ace - (char *) dacl;
-      SetSecurityDescriptorDacl (psd, TRUE, dacl, FALSE);
-    }
-  return psd;
-}
-
-/* This function returns a handle to the top-level directory in the global
-   NT namespace used to implement advisory locking. */
-static HANDLE
-get_lock_parent_dir ()
+/* Get the handle count of an object. */
+static ULONG
+get_obj_handle_count (HANDLE h)
 {
-  static HANDLE dir;
-  UNICODE_STRING uname;
-  OBJECT_ATTRIBUTES attr;
+  OBJECT_BASIC_INFORMATION obi;
   NTSTATUS status;
+  ULONG hdl_cnt = 0;
 
-  INODE_LIST_LOCK();
-  if (!dir)
-    {
-      RtlInitUnicodeString (&uname, L"\\BaseNamedObjects\\cygwin-fcntl-lk");
-      InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT, NULL,
-                                 sec_all.lpSecurityDescriptor);
-      status = NtOpenDirectoryObject (&dir, DIRECTORY_ALL_ACCESS, &attr);
-      if (!NT_SUCCESS (status))
-       {
-         status = NtCreateDirectoryObject (&dir, DIRECTORY_ALL_ACCESS, &attr);
-         if (!NT_SUCCESS (status))
-           api_fatal ("NtCreateDirectoryObject: %p", status);
-       }
-    }
-  INODE_LIST_UNLOCK ();
-  return dir;
+  status = NtQueryObject (h, ObjectBasicInformation, &obi, sizeof obi, NULL);
+  if (!NT_SUCCESS (status))
+    debug_printf ("NtQueryObject: %p\n", status);
+  else
+    hdl_cnt = obi.HandleCount;
+  return hdl_cnt;
 }
 
 /* Per lock class. */
@@ -286,8 +216,8 @@ class lockf_t
     short           lf_type;  /* Lock type: F_RDLCK, F_WRLCK */
     _off64_t        lf_start; /* Byte # of the start of the lock */
     _off64_t        lf_end;   /* Byte # of the end of the lock (-1=EOF) */
-    /* We need the Cygwin PID for F_GETLK, the Win PID for synchronization. */
-    pid_t           lf_id;    /* (P)Id of the resource holding the lock */
+    long long       lf_id;    /* Cygwin PID for POSIX locks, a unique id per
+                                file table entry for BSD flock locks. */
     DWORD           lf_wid;   /* Win PID of the resource holding the lock */
     class lockf_t **lf_head;  /* Back pointer to the head of the lockf_t list */
     class inode_t  *lf_inode; /* Back pointer to the inode_t */
@@ -296,25 +226,30 @@ class lockf_t
 
     lockf_t ()
     : lf_flags (0), lf_type (0), lf_start (0), lf_end (0), lf_id (0),
-      lf_wid (0), lf_head (NULL), lf_inode (NULL), lf_next (NULL), lf_obj (NULL)
+      lf_wid (0), lf_head (NULL), lf_inode (NULL),
+      lf_next (NULL), lf_obj (NULL)
     {}
     lockf_t (class inode_t *node, class lockf_t **head, short flags, short type,
-          _off64_t start, _off64_t end, pid_t id, DWORD wid)
+          _off64_t start, _off64_t end, long long id, DWORD wid)
     : lf_flags (flags), lf_type (type), lf_start (start), lf_end (end),
       lf_id (id), lf_wid (wid), lf_head (head), lf_inode (node),
       lf_next (NULL), lf_obj (NULL)
     {}
     ~lockf_t ();
 
+    /* Used to create all locks list in a given TLS buffer. */
+    void *operator new (size_t size, void *p)
+    { return p; }
+    /* Used to store own lock list in the cygheap. */
     void *operator new (size_t size)
-    { return ccalloc (HEAP_FHANDLER, 1, sizeof (lockf_t)); }
+    { return cmalloc (HEAP_FHANDLER, sizeof (lockf_t)); }
+    /* Never call on node->i_all_lf! */
     void operator delete (void *p)
     { cfree (p); }
 
     void create_lock_obj ();
-    HANDLE open_lock_obj () const;
-    ULONG get_lock_obj_handle_count () const;
-    void del_lock_obj ();
+    bool open_lock_obj ();
+    void del_lock_obj (HANDLE fhdl, bool signal = false);
 };
 
 /* Per inode_t class */
@@ -327,86 +262,160 @@ class inode_t
     lockf_t              *i_lockf;  /* List of locks of this process. */
     lockf_t              *i_all_lf; /* Temp list of all locks for this file. */
 
-    dev_t  i_dev;
-    ino_t  i_ino;
-  private:
-    HANDLE i_dir; /* Not inherited! */
-    HANDLE i_mtx; /* Not inherited! */
+    __dev32_t             i_dev;    /* Device ID */
+    __ino64_t             i_ino;    /* inode number */
 
-    void del_locks (lockf_t **head);
+  private:
+    HANDLE                i_dir;
+    HANDLE                i_mtx;
+    unsigned long        i_wait;   /* Number of blocked threads waiting for
+                                      a blocking lock. */
 
   public:
-    inode_t (dev_t dev, ino_t ino);
+    inode_t (__dev32_t dev, __ino64_t ino);
     ~inode_t ();
 
     void *operator new (size_t size)
-    { return ccalloc (HEAP_FHANDLER, 1, sizeof (inode_t)); }
+    { return cmalloc (HEAP_FHANDLER, sizeof (inode_t)); }
     void operator delete (void *p)
     { cfree (p); }
 
-    static inode_t *get (dev_t dev, ino_t ino);
+    static inode_t *get (__dev32_t dev, __ino64_t ino, bool create_if_missing);
 
     void LOCK () { WaitForSingleObject (i_mtx, INFINITE); }
     void UNLOCK () { ReleaseMutex (i_mtx); }
 
-    void get_all_locks_list ();
-    void del_all_locks_list () { del_locks (&i_all_lf); }
+    void wait () { ++i_wait; }
+    void unwait () { if (i_wait > 0) --i_wait; }
+    bool waiting () { return i_wait > 0; }
 
-    void del_my_locks () { LOCK (); del_locks (&i_lockf); UNLOCK ();
-}
+    lockf_t *get_all_locks_list ();
 
+    bool del_my_locks (long long id, HANDLE fhdl);
 };
 
-/* Used to delete all locks on a file hold by this process.  Called from
-   close(2).  This implements fcntl semantics.
-   TODO: flock(2) semantics. */
-void
-del_my_locks (inode_t *node)
+inode_t::~inode_t ()
 {
-  INODE_LIST_LOCK ();
-  node->del_my_locks ();
-  LIST_REMOVE (node, i_next);
-  INODE_LIST_UNLOCK ();
+  lockf_t *lock, *n_lock;
+  for (lock = i_lockf; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
+    delete lock;
+  NtClose (i_mtx);
+  NtClose (i_dir);
 }
 
-/* The global inode_t list header.  inode_t structs are created when a lock
-   is requested on a file the first time from this process. */
-/* Called in a forked child to get rid of all inodes and locks hold by the
-   parent process.  Child processes don't inherit locks. */
-void
-fixup_lockf_after_fork ()
+bool
+inode_t::del_my_locks (long long id, HANDLE fhdl)
 {
-  inode_t *node, *next_node;
+  lockf_t *lock, *n_lock;
+  lockf_t **prev = &i_lockf;
+  int lc = 0;
+  for (lock = *prev; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
+    {
+      if (lock->lf_flags & F_POSIX)
+       {
+         /* Delete all POSIX locks. */
+         *prev = n_lock;
+         ++lc;
+         delete lock;
+       }
+      else if (id && lock->lf_id == id)
+       {
+         int cnt = 0;
+         cygheap_fdenum cfd (true);
+         while (cfd.next () >= 0)
+           if (cfd->get_unique_id () == lock->lf_id && ++cnt > 1)
+             break;
+         /* Delete BSD flock lock when no other fd in this process references
+            it anymore. */
+         if (cnt <= 1)
+           {
+             *prev = n_lock;
+             lock->del_lock_obj (fhdl);
+             delete lock;
+           }
+       }
+      else
+       prev = &lock->lf_next;
+    }
+  return i_lockf == NULL;
+}
 
-  LIST_FOREACH_SAFE (node, &cygheap->inode_list, i_next, next_node)
-    delete node;
-  LIST_INIT (&cygheap->inode_list);
+/* Used to delete the locks on a file hold by this process.  Called from
+   close(2) and fixup_after_fork, as well as from fixup_after_exec in
+   case the close_on_exec flag is set.  The whole inode is deleted as
+   soon as no lock exists on it anymore. */
+void
+fhandler_base::del_my_locks (del_lock_called_from from)
+{
+  INODE_LIST_LOCK ();
+  inode_t *node = inode_t::get (get_dev (), get_ino (), false);
+  if (node)
+    {
+      /* When we're called from fixup_after_exec, the fhandler is a
+        close-on-exec fhandler.  In this case our io handle is already
+        invalid.  We can't use it to test for the object reference count.
+        However, that shouldn't be necessary for the following reason.
+        After exec, there are no threads in the current process waiting for
+        the lock.  So, either we're the only process accessing the file table
+        entry and there are no threads which require signalling, or we have
+        a parent process still accessing the file object and signalling the
+        lock event would be premature. */
+      bool no_locks_left =
+       node->del_my_locks (from == after_fork ? 0 : get_unique_id (),
+                           from == after_exec ? NULL : get_handle ());
+      if (no_locks_left)
+       {
+         LIST_REMOVE (node, i_next);
+         node->UNLOCK ();
+         delete node;
+       }
+      else
+       node->UNLOCK ();
+    }
+  INODE_LIST_UNLOCK ();
 }
 
 /* Called in an execed child.  The exec'ed process must allow SYNCHRONIZE
    access to everyone if at least one inode exists.
-   The lock owner's Windows PID changed and all lock event objects have to
-   be relabeled so that waiting processes know which process to wait on. */
+   The lock owner's Windows PID changed and all POSIX lock event objects
+   have to be relabeled so that waiting processes know which process to
+   wait on.  If the node has been abandoned due to close_on_exec on the
+   referencing fhandlers, remove the inode entirely. */
 void
 fixup_lockf_after_exec ()
 {
-  inode_t *node;
+  inode_t *node, *next_node;
 
   INODE_LIST_LOCK ();
   if (LIST_FIRST (&cygheap->inode_list))
     allow_others_to_sync ();
-  LIST_FOREACH (node, &cygheap->inode_list, i_next)
+  LIST_FOREACH_SAFE (node, &cygheap->inode_list, i_next, next_node)
     {
-      node->LOCK ();
-      for (lockf_t *lock = node->i_lockf; lock; lock = lock->lf_next)
+      int cnt = 0;
+      cygheap_fdenum cfd (true);
+      while (cfd.next () >= 0)
+       if (cfd->get_dev () == node->i_dev
+           && cfd->get_ino () == node->i_ino
+           && ++cnt > 1)
+         break;
+      if (cnt == 0)
        {
-         lock->del_lock_obj ();
-         lock->lf_wid = myself->dwProcessId;
-         lock->create_lock_obj ();
+         LIST_REMOVE (node, i_next);
+         delete node;
+       }
+      else
+       {
+         node->LOCK ();
+         for (lockf_t *lock = node->i_lockf; lock; lock = lock->lf_next)
+           if (lock->lf_flags & F_POSIX)
+             {
+               lock->del_lock_obj (NULL);
+               lock->lf_wid = myself->dwProcessId;
+               lock->create_lock_obj ();
+             }
+         node->UNLOCK ();
        }
-      node->UNLOCK ();
     }
-  LIST_INIT (&cygheap->inode_list);
   INODE_LIST_UNLOCK ();
 }
 
@@ -414,7 +423,7 @@ fixup_lockf_after_exec ()
    file.  The file is specified by the device and inode_t number.  If inode_t
    doesn't exist, create it. */
 inode_t *
-inode_t::get (dev_t dev, ino_t ino)
+inode_t::get (__dev32_t dev, __ino64_t ino, bool create_if_missing)
 {
   inode_t *node;
 
@@ -422,70 +431,55 @@ inode_t::get (dev_t dev, ino_t ino)
   LIST_FOREACH (node, &cygheap->inode_list, i_next)
     if (node->i_dev == dev && node->i_ino == ino)
       break;
-  if (!node)
+  if (!node && create_if_missing)
     {
       node = new inode_t (dev, ino);
-      LIST_INSERT_HEAD (&cygheap->inode_list, node, i_next);
+      if (node)
+       LIST_INSERT_HEAD (&cygheap->inode_list, node, i_next);
     }
+  if (node)
+    node->LOCK ();
   INODE_LIST_UNLOCK ();
   return node;
 }
 
-inode_t::inode_t (dev_t dev, ino_t ino)
-: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino)
+inode_t::inode_t (__dev32_t dev, __ino64_t ino)
+: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino), i_wait (0L)
 {
   HANDLE parent_dir;
-  WCHAR name[32];
+  WCHAR name[48];
   UNICODE_STRING uname;
   OBJECT_ATTRIBUTES attr;
   NTSTATUS status;
 
-  parent_dir = get_lock_parent_dir ();
+  parent_dir = get_shared_parent_dir ();
   /* Create a subdir which is named after the device and inode_t numbers
      of the given file, in hex notation. */
-  int len = __small_swprintf (name, L"%08x-%016X", dev, ino);
+  int len = __small_swprintf (name, L"flock-%08x-%016X", dev, ino);
   RtlInitCountedUnicodeString (&uname, name, len * sizeof (WCHAR));
-  InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT, parent_dir,
-                             sec_all.lpSecurityDescriptor);
-  status = NtOpenDirectoryObject (&i_dir, DIRECTORY_ALL_ACCESS, &attr);
+  InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT | OBJ_OPENIF,
+                             parent_dir, everyone_sd (FLOCK_INODE_DIR_ACCESS));
+  status = NtCreateDirectoryObject (&i_dir, FLOCK_INODE_DIR_ACCESS, &attr);
   if (!NT_SUCCESS (status))
-    {
-      status = NtCreateDirectoryObject (&i_dir, DIRECTORY_ALL_ACCESS, &attr);
-      if (!NT_SUCCESS (status))
-       api_fatal ("NtCreateDirectoryObject: %p", status);
-    }
+    api_fatal ("NtCreateDirectoryObject(inode): %p", status);
   /* Create a mutex object in the file specific dir, which is used for
      access synchronization on the dir and its objects. */
-  RtlInitUnicodeString (&uname, L"mtx");
-  InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT, i_dir,
-                             sec_all.lpSecurityDescriptor);
-  status = NtOpenMutant (&i_mtx, MUTANT_ALL_ACCESS, &attr);
+  InitializeObjectAttributes (&attr, &ro_u_mtx, OBJ_INHERIT | OBJ_OPENIF, i_dir,
+                             everyone_sd (CYG_MUTANT_ACCESS));
+  status = NtCreateMutant (&i_mtx, CYG_MUTANT_ACCESS, &attr, FALSE);
   if (!NT_SUCCESS (status))
-    {
-      status = NtCreateMutant (&i_mtx, MUTANT_ALL_ACCESS, &attr, FALSE);
-      if (!NT_SUCCESS (status))
-       api_fatal ("NtCreateMutant: %p", status);
-    }
-}
-
-inode_t::~inode_t ()
-{
-  del_locks (&i_lockf);
-}
-
-void
-inode_t::del_locks (lockf_t **head)
-{
-  lockf_t *lock, *n_lock;
-  for (lock = *head; lock && (n_lock = lock->lf_next, 1); lock = n_lock)
-    delete lock;
-  *head = NULL;
+    api_fatal ("NtCreateMutant(inode): %p", status);
 }
 
 /* Enumerate all lock event objects for this file and create a lockf_t
    list in the i_all_lf member.  This list is searched in lf_getblock
    for locks which potentially block our lock request. */
-void
+
+/* Number of lockf_t structs which fit in the temporary buffer. */
+#define MAX_LOCKF_CNT  ((intptr_t)((NT_MAX_PATH * sizeof (WCHAR)) \
+                                   / sizeof (lockf_t)))
+
+lockf_t *
 inode_t::get_all_locks_list ()
 {
   struct fdbi
@@ -495,8 +489,8 @@ inode_t::get_all_locks_list ()
   } f;
   ULONG context;
   NTSTATUS status;
+  lockf_t *lock = i_all_lf;
 
-  del_all_locks_list ();
   for (BOOLEAN restart = TRUE;
        NT_SUCCESS (status = NtQueryDirectoryObject (i_dir, &f, sizeof f, TRUE,
                                                    restart, &context, NULL));
@@ -505,34 +499,43 @@ inode_t::get_all_locks_list ()
       if (f.dbi.ObjectName.Length != LOCK_OBJ_NAME_LEN * sizeof (WCHAR))
        continue;
       wchar_t *wc = f.dbi.ObjectName.Buffer, *endptr;
-      /* "%02x-%01x-%016X-%016X-%08x-%08x",
+      /* "%02x-%01x-%016X-%016X-%016X-%08x",
         lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid */
       wc[LOCK_OBJ_NAME_LEN] = L'\0';
       short flags = wcstol (wc, &endptr, 16);
-      if ((flags & ~(F_WAIT | F_FLOCK | F_POSIX)) != 0
-         || (flags & (F_FLOCK | F_POSIX) == (F_FLOCK | F_POSIX)))
+      if ((flags & ~(F_FLOCK | F_POSIX)) != 0
+         || ((flags & (F_FLOCK | F_POSIX)) == (F_FLOCK | F_POSIX)))
        continue;
       short type = wcstol (endptr + 1, &endptr, 16);
-      if (type != F_RDLCK && type != F_WRLCK || !endptr || *endptr != L'-')
-        continue;
+      if ((type != F_RDLCK && type != F_WRLCK) || !endptr || *endptr != L'-')
+       continue;
       _off64_t start = (_off64_t) wcstoull (endptr + 1, &endptr, 16);
       if (start < 0 || !endptr || *endptr != L'-')
-        continue;
+       continue;
       _off64_t end = (_off64_t) wcstoull (endptr + 1, &endptr, 16);
       if (end < -1LL || (end > 0 && end < start) || !endptr || *endptr != L'-')
-       continue;
-      pid_t id = wcstoul (endptr + 1, &endptr, 16);
-      if (!endptr || *endptr != L'-')
-       continue;
+       continue;
+      long long id = wcstoll (endptr + 1, &endptr, 16);
+      if (!endptr || *endptr != L'-'
+         || ((flags & F_POSIX) && (id < 1 || id > ULONG_MAX)))
+       continue;
       DWORD wid = wcstoul (endptr + 1, &endptr, 16);
       if (endptr && *endptr != L'\0')
-       continue;
-      lockf_t *lock = new lockf_t (this, &i_all_lf, flags, type, start, end,
-                                  id, wid);
-      if (i_all_lf)
-       lock->lf_next = i_all_lf;
-      i_all_lf = lock;
+       continue;
+      if (lock - i_all_lf >= MAX_LOCKF_CNT)
+       {
+         system_printf ("Warning, can't handle more than %d locks per file.",
+                        MAX_LOCKF_CNT);
+         break;
+       }
+      if (lock > i_all_lf)
+       lock[-1].lf_next = lock;
+      new (lock++) lockf_t (this, &i_all_lf, flags, type, start, end, id, wid);
     }
+  /* If no lock has been found, return NULL. */
+  if (lock == i_all_lf)
+    return NULL;
+  return i_all_lf;
 }
 
 /* Create the lock event object in the file's subdir in the NT global
@@ -542,74 +545,66 @@ inode_t::get_all_locks_list ()
 void
 lockf_t::create_lock_obj ()
 {
-  WCHAR name[LOCK_OBJ_NAME_LEN];
+  WCHAR name[LOCK_OBJ_NAME_LEN + 1];
   UNICODE_STRING uname;
   OBJECT_ATTRIBUTES attr;
   NTSTATUS status;
 
-  __small_swprintf (name, L"%02x-%01x-%016X-%016X-%08x-%08x",
-                         lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid);
+  __small_swprintf (name, L"%02x-%01x-%016X-%016X-%016X-%08x",
+                         lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start,
+                         lf_end, lf_id, lf_wid);
   RtlInitCountedUnicodeString (&uname, name,
                               LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
   InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT, lf_inode->i_dir,
-                             everyone_sync_sd ());
-  status = NtCreateEvent (&lf_obj, EVENT_ALL_ACCESS, &attr,
+                             everyone_sd (FLOCK_EVENT_ACCESS));
+  status = NtCreateEvent (&lf_obj, CYG_EVENT_ACCESS, &attr,
                          NotificationEvent, FALSE);
   if (!NT_SUCCESS (status))
-    api_fatal ("NtCreateEvent: %p", status);
+    api_fatal ("NtCreateEvent(lock): %p", status);
 }
 
 /* Open a lock event object for SYNCHRONIZE access (to wait for it). */
-HANDLE
-lockf_t::open_lock_obj () const
+bool
+lockf_t::open_lock_obj ()
 {
-  WCHAR name[LOCK_OBJ_NAME_LEN];
+  WCHAR name[LOCK_OBJ_NAME_LEN + 1];
   UNICODE_STRING uname;
   OBJECT_ATTRIBUTES attr;
   NTSTATUS status;
-  HANDLE obj;
 
-  __small_swprintf (name, L"%02x-%01x-%016X-%016X-%08x-%08x",
-                         lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid);
+  __small_swprintf (name, L"%02x-%01x-%016X-%016X-%016X-%08x",
+                         lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start,
+                         lf_end, lf_id, lf_wid);
   RtlInitCountedUnicodeString (&uname, name,
                               LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
-  InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT, lf_inode->i_dir,
-                             NULL);
-  status = NtOpenEvent (&obj, SYNCHRONIZE, &attr);
+  InitializeObjectAttributes (&attr, &uname, 0, lf_inode->i_dir, NULL);
+  status = NtOpenEvent (&lf_obj, FLOCK_EVENT_ACCESS, &attr);
   if (!NT_SUCCESS (status))
-    api_fatal ("NtOpenEvent: %p", status);
-  return obj;
-}
-
-/* Get the handle count of a lock object. */
-ULONG
-lockf_t::get_lock_obj_handle_count () const
-{
-  OBJECT_BASIC_INFORMATION obi;
-  NTSTATUS status;
-  ULONG hdl_cnt = 0;
-
-  if (lf_obj)
     {
-      status = NtQueryObject (lf_obj, ObjectBasicInformation,
-                             &obi, sizeof obi, NULL);
-      if (!NT_SUCCESS (status))
-       small_printf ("NtQueryObject: %p\n", status);
-      else
-       hdl_cnt = obi.HandleCount;
+      SetLastError (RtlNtStatusToDosError (status));
+      lf_obj = NULL; /* Paranoia... */
     }
-  return hdl_cnt;
+  return lf_obj != NULL;
 }
 
 /* Close a lock event handle.  The important thing here is to signal it
    before closing the handle.  This way all threads waiting for this
    lock can wake up. */
 void
-lockf_t::del_lock_obj ()
+lockf_t::del_lock_obj (HANDLE fhdl, bool signal)
 {
   if (lf_obj)
     {
-      SetEvent (lf_obj);
+      /* Only signal the event if it's either a POSIX lock, or, in case of
+        BSD flock locks, if it's an explicit unlock or if the calling fhandler
+        holds the last reference to the file table entry.  The file table
+        entry in UNIX terms is equivalent to the FILE_OBJECT in Windows NT
+        terms.  It's what the handle/descriptor references when calling
+        CreateFile/open.  Calling DuplicateHandle/dup only creates a new
+        handle/descriptor to the same FILE_OBJECT/file table entry. */
+      if ((lf_flags & F_POSIX) || signal
+         || (fhdl && get_obj_handle_count (fhdl) <= 1))
+       SetEvent (lf_obj);
       NtClose (lf_obj);
       lf_obj = NULL;
     }
@@ -617,14 +612,14 @@ lockf_t::del_lock_obj ()
 
 lockf_t::~lockf_t ()
 {
-  del_lock_obj ();
+  del_lock_obj (NULL);
 }
 
 /*
  * This variable controls the maximum number of processes that will
  * be checked in doing deadlock detection.
  */
-#if 0 /*TODO*/
+#ifndef __CYGWIN__
 #define MAXDEPTH 50
 static int maxlockdepth = MAXDEPTH;
 #endif
@@ -632,13 +627,13 @@ static int maxlockdepth = MAXDEPTH;
 #define NOLOCKF (struct lockf_t *)0
 #define SELF    0x1
 #define OTHERS  0x2
-static int      lf_clearlock (lockf_t *, lockf_t **);
+static int      lf_clearlock (lockf_t *, lockf_t **, HANDLE);
 static int      lf_findoverlap (lockf_t *, lockf_t *, int, lockf_t ***, lockf_t **);
-static lockf_t   *lf_getblock (lockf_t *, inode_t *node);
+static lockf_t *lf_getblock (lockf_t *, inode_t *node);
 static int      lf_getlock (lockf_t *, inode_t *, struct __flock64 *);
-static int      lf_setlock (lockf_t *, inode_t *, lockf_t **);
+static int      lf_setlock (lockf_t *, inode_t *, lockf_t **, HANDLE);
 static void     lf_split (lockf_t *, lockf_t *, lockf_t **);
-static void     lf_wakelock (lockf_t *);
+static void     lf_wakelock (lockf_t *, HANDLE);
 
 int
 fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
@@ -646,11 +641,10 @@ fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
   _off64_t start, end, oadd;
   lockf_t *n;
   int error = 0;
-  struct __stat64 stat;
 
   short a_flags = fl->l_type & (F_POSIX | F_FLOCK);
   short type = fl->l_type & (F_RDLCK | F_WRLCK | F_UNLCK);
-  
+
   if (!a_flags)
     a_flags = F_POSIX; /* default */
   if (a_op == F_SETLKW)
@@ -665,27 +659,29 @@ fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
        a_op = F_UNLCK;
        break;
       case F_RDLCK:
-        if (!(get_access () & GENERIC_READ))
+       /* flock semantics don't specify a requirement that the file has
+          been opened with a specific open mode, in contrast to POSIX locks
+          which require that a file is opened for reading to place a read
+          lock and opened for writing to place a write lock. */
+       if ((a_flags & F_POSIX) && !(get_access () & GENERIC_READ))
          {
            set_errno (EBADF);
            return -1;
          }
        break;
       case F_WRLCK:
-        if (!(get_access () & GENERIC_WRITE))
+       /* See above comment. */
+       if ((a_flags & F_POSIX) && !(get_access () & GENERIC_WRITE))
          {
            set_errno (EBADF);
            return -1;
          }
        break;
       default:
-       set_errno (EINVAL);
+       set_errno (EINVAL);
        return -1;
       }
 
-  if (fstat_by_handle (&stat) == -1)
-    return -1;
-
   /*
    * Convert the flock structure into a start and end.
    */
@@ -701,12 +697,25 @@ fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
       break;
 
     case SEEK_END:
-      if (fl->l_start > 0 && stat.st_size > OFF_MAX - fl->l_start)
-       {
-         set_errno (EOVERFLOW);
-         return -1;
-       }
-      start = stat.st_size + fl->l_start;
+      {
+       NTSTATUS status;
+       IO_STATUS_BLOCK io;
+       FILE_STANDARD_INFORMATION fsi;
+
+       status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
+                                        FileStandardInformation);
+       if (!NT_SUCCESS (status))
+         {
+           __seterrno_from_nt_status (status);
+           return -1;
+         }
+       if (fl->l_start > 0 && fsi.EndOfFile.QuadPart > OFF_MAX - fl->l_start)
+         {
+           set_errno (EOVERFLOW);
+           return -1;
+         }
+       start = fsi.EndOfFile.QuadPart + fl->l_start;
+      }
       break;
 
     default:
@@ -745,53 +754,78 @@ fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
       end = start + oadd;
     }
 
+  inode_t *node = inode_t::get (get_dev (), get_ino (), true);
   if (!node)
     {
-      node = inode_t::get (stat.st_dev, stat.st_ino);
-      need_fork_fixup (true);
+      set_errno (ENOLCK);
+      return -1;
     }
-  /* Unlock the fd table which has been locked in fcntl_worker, otherwise
-     a F_SETLKW waits forever... */
+  need_fork_fixup (true);
+
+  /* Unlock the fd table which has been locked in fcntl_worker/lock_worker,
+     otherwise a blocking F_SETLKW never wakes up on a signal. */
   cygheap->fdtab.unlock ();
 
   lockf_t **head = &node->i_lockf;
 
+#if 0
   /*
    * Avoid the common case of unlocking when inode_t has no locks.
+   *
+   * This shortcut is invalid for Cygwin because the above inode_t::get
+   * call returns with an empty lock list if this process has no locks
+   * on the file yet.
    */
   if (*head == NULL)
     {
       if (a_op != F_SETLK)
        {
+         node->UNLOCK ();
          fl->l_type = F_UNLCK;
          return 0;
        }
     }
+#endif
   /*
    * Allocate a spare structure in case we have to split.
    */
   lockf_t *clean = NULL;
   if (a_op == F_SETLK || a_op == F_UNLCK)
-    clean = new lockf_t ();
+    {
+      clean = new lockf_t ();
+      if (!clean)
+       {
+         node->UNLOCK ();
+         set_errno (ENOLCK);
+         return -1;
+       }
+    }
   /*
    * Create the lockf_t structure
    */
   lockf_t *lock = new lockf_t (node, head, a_flags, type, start, end,
-                              getpid (), myself->dwProcessId);
+                              (a_flags & F_FLOCK) ? get_unique_id ()
+                                                  : getpid (),
+                              myself->dwProcessId);
+  if (!lock)
+    {
+      node->UNLOCK ();
+      set_errno (ENOLCK);
+      return -1;
+    }
 
-  node->LOCK ();
   switch (a_op)
     {
     case F_SETLK:
-      error = lf_setlock (lock, node, &clean);
+      error = lf_setlock (lock, node, &clean, get_handle ());
       break;
 
     case F_UNLCK:
-      error = lf_clearlock (lock, &clean);
+      error = lf_clearlock (lock, &clean, get_handle ());
       lock->lf_next = clean;
-      clean = lock; 
+      clean = lock;
       break;
-    
+
     case F_GETLK:
       error = lf_getlock (lock, node, fl);
       lock->lf_next = clean;
@@ -807,10 +841,20 @@ fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
   for (lock = clean; lock != NULL; )
     {
       n = lock->lf_next;
+      lock->del_lock_obj (get_handle (), a_op == F_UNLCK);
       delete lock;
       lock = n;
     }
-  node->UNLOCK ();
+  if (node->i_lockf == NULL && !node->waiting ())
+    {
+      INODE_LIST_LOCK ();
+      LIST_REMOVE (node, i_next);
+      node->UNLOCK ();
+      delete node;
+      INODE_LIST_UNLOCK ();
+    }
+  else
+    node->UNLOCK ();
   if (error)
     {
       set_errno (error);
@@ -823,12 +867,13 @@ fhandler_disk_file::lock (int a_op, struct __flock64 *fl)
  * Set a byte-range lock.
  */
 static int
-lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
-{ 
+lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
+{
   lockf_t *block;
   lockf_t **head = lock->lf_head;
   lockf_t **prev, *overlap;
   int ovcase, priority, old_prio, needtolink;
+  tmp_pathbuf tp;
 
   /*
    * Set the priority
@@ -839,16 +884,23 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
   /*
    * Scan lock list for this file looking for locks that would block us.
    */
+  /* Create temporary space for the all locks list. */
+  node->i_all_lf = (lockf_t *) tp.w_get ();
   while ((block = lf_getblock(lock, node)))
     {
+      DWORD ret;
+      HANDLE obj = block->lf_obj;
+      block->lf_obj = NULL;
+
       /*
        * Free the structure and return if nonblocking.
        */
       if ((lock->lf_flags & F_WAIT) == 0)
        {
-         node->del_all_locks_list ();
          lock->lf_next = *clean;
          *clean = lock;
+         if (obj)
+           NtClose (obj);
          return EAGAIN;
        }
       /*
@@ -862,13 +914,17 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
        * do not go off into neverland.
        */
       /* FIXME: We check the handle count of all the lock event objects
-                this process holds.  If it's > 1, another process is
+               this process holds.  If it's > 1, another process is
                waiting for one of our locks.  This method isn't overly
                intelligent.  If it turns out to be too dumb, we might
                have to remove it or to find another method. */
       for (lockf_t *lk = node->i_lockf; lk; lk = lk->lf_next)
-       if (lk->get_lock_obj_handle_count () > 1)
-         return EDEADLK;
+       if ((lk->lf_flags & F_POSIX) && get_obj_handle_count (lk->lf_obj) > 1)
+         {
+           if (obj)
+             NtClose (obj);
+           return EDEADLK;
+         }
 
       /*
        * For flock type locks, we must first remove
@@ -878,7 +934,7 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
       if ((lock->lf_flags & F_FLOCK) && lock->lf_type == F_WRLCK)
        {
          lock->lf_type = F_UNLCK;
-         (void) lf_clearlock (lock, clean);
+         (void) lf_clearlock (lock, clean, fhdl);
          lock->lf_type = F_WRLCK;
        }
 
@@ -886,38 +942,82 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
        * Add our lock to the blocked list and sleep until we're free.
        * Remember who blocked us (for deadlock detection).
        */
-      /* TODO */
+      /* Cygwin:  No locked list.  See deadlock recognition above. */
 
       /* Wait for the blocking object and its holding process. */
-      HANDLE obj = block->open_lock_obj ();
-      HANDLE proc = OpenProcess (SYNCHRONIZE, FALSE, block->lf_wid);
-      if (!proc)
-        api_fatal ("OpenProcess: %E");
-      HANDLE w4[3] = { obj, proc, signal_arrived };
-      node->del_all_locks_list ();
-      //SetThreadPriority (GetCurrentThread (), priority);
-      node->UNLOCK ();
-      DWORD ret = WaitForMultipleObjects (3, w4, FALSE, INFINITE);
-      NtClose (proc);
-      NtClose (obj);
+      if (!obj)
+       {
+         /* We can't synchronize on the lock event object.
+            Treat this as a deadlock-like situation for now. */
+         system_printf ("Can't sync with lock object hold by "
+                        "Win32 pid %lu: %E", block->lf_wid);
+         return EDEADLK;
+       }
+      SetThreadPriority (GetCurrentThread (), priority);
+      if (lock->lf_flags & F_POSIX)
+       {
+         HANDLE proc = OpenProcess (SYNCHRONIZE, FALSE, block->lf_wid);
+         if (!proc)
+           {
+             /* If we can't synchronize on the process holding the lock,
+                we will never recognize when the lock has been abandoned.
+                Treat this as a deadlock-like situation for now. */
+             system_printf ("Can't sync with process holding a lock "
+                            "(Win32 pid %lu): %E", block->lf_wid);
+             NtClose (obj);
+             return EDEADLK;
+           }
+         HANDLE w4[3] = { obj, proc, signal_arrived };
+         node->wait ();
+         node->UNLOCK ();
+         ret = WaitForMultipleObjects (3, w4, FALSE, INFINITE);
+         CloseHandle (proc);
+       }
+      else
+       {
+         HANDLE w4[2] = { obj, signal_arrived };
+         node->wait ();
+         node->UNLOCK ();
+         /* Unfortunately, since BSD flock locks are not attached to a
+            specific process, we can't recognize an abandoned lock by
+            sync'ing with a process.  We have to find out if we're the only
+            process left accessing this event object. */
+         do
+           {
+             ret = WaitForMultipleObjects (2, w4, FALSE, 100L);
+           }
+         while (ret == WAIT_TIMEOUT && get_obj_handle_count (obj) > 1);
+         /* There's a good chance that the above loop is left with
+            ret == WAIT_TIMEOUT if another process closes the file handle
+            associated with this lock.  This is for all practical purposes
+            equivalent to a signalled lock object. */
+         if (ret == WAIT_TIMEOUT)
+           ret = WAIT_OBJECT_0;
+       }
       node->LOCK ();
-      //SetThreadPriority (GetCurrentThread (), old_prio);
+      node->unwait ();
+      NtClose (obj);
+      SetThreadPriority (GetCurrentThread (), old_prio);
       switch (ret)
        {
        case WAIT_OBJECT_0:
-       case WAIT_OBJECT_0 + 1:
-         /* The lock object has been set to signalled or the process
-            holding the lock has exited. */ 
+         /* The lock object has been set to signalled. */
          break;
+       case WAIT_OBJECT_0 + 1:
+         /* For POSIX locks, the process holding the lock has exited. */
+         if (lock->lf_flags & F_POSIX)
+           break;
+         /*FALLTHRU*/
        case WAIT_OBJECT_0 + 2:
          /* A signal came in. */
          _my_tls.call_signal_handler ();
          return EINTR;
        default:
+         system_printf ("Shouldn't happen! ret = %lu, error: %lu\n",
+                        ret, GetLastError ());
          return geterrno_from_win_error ();
        }
     }
-  node->del_all_locks_list ();
   allow_others_to_sync ();
   /*
    * No blocks!!  Add the lock.  Note that we will
@@ -945,29 +1045,29 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
        */
       switch (ovcase)
        {
-        case 0: /* no overlap */
+       case 0: /* no overlap */
          if (needtolink)
            {
              *prev = lock;
              lock->lf_next = overlap;
              lock->create_lock_obj ();
-            }
-            break;
+           }
+           break;
 
-        case 1: /* overlap == lock */
+       case 1: /* overlap == lock */
          /*
           * If downgrading lock, others may be
           * able to acquire it.
           * Cygwin: Always wake lock.
           */
-         lf_wakelock (overlap);
+         lf_wakelock (overlap, fhdl);
          overlap->lf_type = lock->lf_type;
          overlap->create_lock_obj ();
          lock->lf_next = *clean;
          *clean = lock;
          break;
 
-        case 2: /* overlap contains lock */
+       case 2: /* overlap contains lock */
          /*
           * Check for common starting point and different types.
           */
@@ -985,20 +1085,20 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
            }
          else
            lf_split (overlap, lock, clean);
-         lf_wakelock (overlap);
+         lf_wakelock (overlap, fhdl);
          overlap->create_lock_obj ();
          lock->create_lock_obj ();
          if (lock->lf_next && !lock->lf_next->lf_obj)
            lock->lf_next->create_lock_obj ();
          break;
 
-        case 3: /* lock contains overlap */
+       case 3: /* lock contains overlap */
          /*
           * If downgrading lock, others may be able to
           * acquire it, otherwise take the list.
           * Cygwin: Always wake old lock and create new lock.
           */
-         lf_wakelock (overlap);
+         lf_wakelock (overlap, fhdl);
          /*
           * Add the new lock if necessary and delete the overlap.
           */
@@ -1016,7 +1116,7 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
          *clean = overlap;
          continue;
 
-        case 4: /* overlap starts before lock */
+       case 4: /* overlap starts before lock */
          /*
           * Add lock after overlap on the list.
           */
@@ -1024,13 +1124,13 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
          overlap->lf_next = lock;
          overlap->lf_end = lock->lf_start - 1;
          prev = &lock->lf_next;
-         lf_wakelock (overlap);
+         lf_wakelock (overlap, fhdl);
          overlap->create_lock_obj ();
          lock->create_lock_obj ();
          needtolink = 0;
          continue;
 
-        case 5: /* overlap ends after lock */
+       case 5: /* overlap ends after lock */
          /*
           * Add the new lock before overlap.
           */
@@ -1039,7 +1139,7 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
              lock->lf_next = overlap;
          }
          overlap->lf_start = lock->lf_end + 1;
-         lf_wakelock (overlap);
+         lf_wakelock (overlap, fhdl);
          lock->create_lock_obj ();
          overlap->create_lock_obj ();
          break;
@@ -1056,7 +1156,7 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean)
  * and remove it (or shrink it), then wakeup anyone we can.
  */
 static int
-lf_clearlock (lockf_t *unlock, lockf_t **clean)
+lf_clearlock (lockf_t *unlock, lockf_t **clean, HANDLE fhdl)
 {
   lockf_t **head = unlock->lf_head;
   lockf_t *lf = *head;
@@ -1071,17 +1171,17 @@ lf_clearlock (lockf_t *unlock, lockf_t **clean)
       /*
        * Wakeup the list of locks to be retried.
        */
-      lf_wakelock (overlap);
+      lf_wakelock (overlap, fhdl);
 
       switch (ovcase)
        {
-        case 1: /* overlap == lock */
+       case 1: /* overlap == lock */
          *prev = overlap->lf_next;
          overlap->lf_next = *clean;
          *clean = overlap;
          break;
 
-        case 2: /* overlap contains lock: split it */
+       case 2: /* overlap contains lock: split it */
          if (overlap->lf_start == unlock->lf_start)
            {
              overlap->lf_start = unlock->lf_end + 1;
@@ -1095,24 +1195,24 @@ lf_clearlock (lockf_t *unlock, lockf_t **clean)
            overlap->lf_next->create_lock_obj ();
          break;
 
-        case 3: /* lock contains overlap */
+       case 3: /* lock contains overlap */
          *prev = overlap->lf_next;
          lf = overlap->lf_next;
          overlap->lf_next = *clean;
          *clean = overlap;
          continue;
 
-        case 4: /* overlap starts before lock */
-            overlap->lf_end = unlock->lf_start - 1;
-            prev = &overlap->lf_next;
-            lf = overlap->lf_next;
+       case 4: /* overlap starts before lock */
+           overlap->lf_end = unlock->lf_start - 1;
+           prev = &overlap->lf_next;
+           lf = overlap->lf_next;
            overlap->create_lock_obj ();
-            continue;
+           continue;
 
-        case 5: /* overlap ends after lock */
-            overlap->lf_start = unlock->lf_end + 1;
+       case 5: /* overlap ends after lock */
+           overlap->lf_start = unlock->lf_end + 1;
            overlap->create_lock_obj ();
-            break;
+           break;
        }
       break;
     }
@@ -1127,9 +1227,14 @@ static int
 lf_getlock (lockf_t *lock, inode_t *node, struct __flock64 *fl)
 {
   lockf_t *block;
+  tmp_pathbuf tp;
 
+  /* Create temporary space for the all locks list. */
+  node->i_all_lf = (lockf_t *) tp.w_get ();
   if ((block = lf_getblock (lock, node)))
     {
+      if (block->lf_obj)
+       NtClose (block->lf_obj);
       fl->l_type = block->lf_type;
       fl->l_whence = SEEK_SET;
       fl->l_start = block->lf_start;
@@ -1138,13 +1243,12 @@ lf_getlock (lockf_t *lock, inode_t *node, struct __flock64 *fl)
       else
        fl->l_len = block->lf_end - block->lf_start + 1;
       if (block->lf_flags & F_POSIX)
-       fl->l_pid = block->lf_id;
+       fl->l_pid = (pid_t) block->lf_id;
       else
        fl->l_pid = -1;
     }
   else
     fl->l_type = F_UNLCK;
-  node->del_all_locks_list ();
   return 0;
 }
 
@@ -1154,10 +1258,12 @@ lf_getlock (lockf_t *lock, inode_t *node, struct __flock64 *fl)
  */
 static lockf_t *
 lf_getblock (lockf_t *lock, inode_t *node)
-{   
-  node->get_all_locks_list ();
-  lockf_t **prev, *overlap, *lf = node->i_all_lf;
+{
+  lockf_t **prev, *overlap;
+  lockf_t *lf = node->get_all_locks_list ();
   int ovcase;
+  NTSTATUS status;
+  EVENT_BASIC_INFORMATION ebi;
 
   prev = lock->lf_head;
   while ((ovcase = lf_findoverlap (lf, lock, OTHERS, &prev, &overlap)))
@@ -1166,7 +1272,20 @@ lf_getblock (lockf_t *lock, inode_t *node)
        * We've found an overlap, see if it blocks us
        */
       if ((lock->lf_type == F_WRLCK || overlap->lf_type == F_WRLCK))
-         return overlap;
+       {
+         /* Open the event object for synchronization. */
+         if (!overlap->open_lock_obj () || (overlap->lf_flags & F_POSIX))
+           return overlap;
+         /* In case of BSD flock locks, check if the event object is
+            signalled.  If so, the overlap doesn't actually exist anymore.
+            There are just a few open handles left. */
+         status = NtQueryEvent (overlap->lf_obj, EventBasicInformation,
+                                &ebi, sizeof ebi, NULL);
+         if (!NT_SUCCESS (status) || ebi.SignalState == 0)
+           return overlap;
+         NtClose (overlap->lf_obj);
+         overlap->lf_obj = NULL;
+       }
       /*
        * Nope, point to the next one on the list and
        * see if it blocks us
@@ -1182,7 +1301,7 @@ lf_getblock (lockf_t *lock, inode_t *node)
  *
  * NOTE: this returns only the FIRST overlapping lock.  There
  *   may be more than one.
- */ 
+ */
 static int
 lf_findoverlap (lockf_t *lf, lockf_t *lock, int type, lockf_t ***prev,
                lockf_t **overlap)
@@ -1197,12 +1316,16 @@ lf_findoverlap (lockf_t *lf, lockf_t *lock, int type, lockf_t ***prev,
   end = lock->lf_end;
   while (lf != NOLOCKF)
     {
-      if ((type & OTHERS) && lf->lf_id == lock->lf_id)
+      if (((type & SELF) && lf->lf_id != lock->lf_id)
+         || ((type & OTHERS) && lf->lf_id == lock->lf_id)
+         /* As on Linux: POSIX locks and BSD flock locks don't interact. */
+         || (lf->lf_flags & (F_POSIX | F_FLOCK))
+            != (lock->lf_flags & (F_POSIX | F_FLOCK)))
        {
          *prev = &lf->lf_next;
          *overlap = lf = lf->lf_next;
          continue;
-        }
+       }
       /*
        * OK, check for overlap
        *
@@ -1223,7 +1346,7 @@ lf_findoverlap (lockf_t *lf, lockf_t *lock, int type, lockf_t ***prev,
          *prev = &lf->lf_next;
          *overlap = lf = lf->lf_next;
          continue;
-        }
+       }
       if ((lf->lf_start == start) && (lf->lf_end == end))
        {
          /* Case 1 */
@@ -1267,7 +1390,7 @@ lf_split (lockf_t *lock1, lockf_t *lock2, lockf_t **split)
 {
   lockf_t *splitlock;
 
-  /* 
+  /*
    * Check to see if spliting into only two pieces.
    */
   if (lock1->lf_start == lock2->lf_start)
@@ -1312,73 +1435,64 @@ lf_split (lockf_t *lock1, lockf_t *lock2, lockf_t **split)
  * all threads waiting for this lock.
  */
 static void
-lf_wakelock (lockf_t *listhead)
+lf_wakelock (lockf_t *listhead, HANDLE fhdl)
 {
-  listhead->del_lock_obj ();
+  listhead->del_lock_obj (fhdl, true);
 }
 
-int
+extern "C" int
 flock (int fd, int operation)
 {
-  int i, cmd;
-  struct __flock64 l = { 0, 0, 0, 0, 0 };
-  if (operation & LOCK_NB)
-    {
-      cmd = F_SETLK;
-    }
-  else
-    {
-      cmd = F_SETLKW;
-    }
-  l.l_whence = SEEK_SET;
+  int res = -1;
+  int cmd;
+  struct __flock64 fl = { 0, SEEK_SET, 0, 0, 0 };
+
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    return -1;
+
+  cygheap_fdget cfd (fd, true);
+  if (cfd < 0)
+    goto done;
+
+  cmd = (operation & LOCK_NB) ? F_SETLK : F_SETLKW;
   switch (operation & (~LOCK_NB))
     {
     case LOCK_EX:
-      l.l_type = F_WRLCK | F_FLOCK;
-      i = fcntl_worker (fd, cmd, &l);
-      if (i == -1)
-       {
-         if ((get_errno () == EAGAIN) || (get_errno () == EACCES))
-           {
-             set_errno (EWOULDBLOCK);
-           }
-       }
+      fl.l_type = F_WRLCK | F_FLOCK;
       break;
     case LOCK_SH:
-      l.l_type = F_RDLCK | F_FLOCK;
-      i = fcntl_worker (fd, cmd, &l);
-      if (i == -1)
-       {
-         if ((get_errno () == EAGAIN) || (get_errno () == EACCES))
-           {
-             set_errno (EWOULDBLOCK);
-           }
-       }
+      fl.l_type = F_RDLCK | F_FLOCK;
       break;
     case LOCK_UN:
-      l.l_type = F_UNLCK | F_FLOCK;
-      i = fcntl_worker (fd, cmd, &l);
-      if (i == -1)
-       {
-         if ((get_errno () == EAGAIN) || (get_errno () == EACCES))
-           {
-             set_errno (EWOULDBLOCK);
-           }
-       }
+      fl.l_type = F_UNLCK | F_FLOCK;
       break;
     default:
-      i = -1;
       set_errno (EINVAL);
-      break;
+      goto done;
     }
-  return i;
+  res = cfd->lock (cmd, &fl);
+  if ((res == -1) && ((get_errno () == EAGAIN) || (get_errno () == EACCES)))
+    set_errno (EWOULDBLOCK);
+done:
+  syscall_printf ("%d = flock (%d, %d)", res, fd, operation);
+  return res;
 }
 
 extern "C" int
 lockf (int filedes, int function, _off64_t size)
 {
-  struct flock fl;
+  int res = -1;
   int cmd;
+  struct __flock64 fl;
+
+  myfault efault;
+  if (efault.faulted (EFAULT))
+    return -1;
+
+  cygheap_fdget cfd (filedes, true);
+  if (cfd < 0)
+    goto done;
 
   fl.l_start = 0;
   fl.l_len = size;
@@ -1400,18 +1514,21 @@ lockf (int filedes, int function, _off64_t size)
       break;
     case F_TEST:
       fl.l_type = F_WRLCK;
-      if (fcntl_worker (filedes, F_GETLK, &fl) == -1)
-       return -1;
+      if (cfd->lock (F_GETLK, &fl) == -1)
+       goto done;
       if (fl.l_type == F_UNLCK || fl.l_pid == getpid ())
-       return 0;
-      errno = EAGAIN;
-      return -1;
+       res = 0;
+      else
+       errno = EAGAIN;
+      goto done;
       /* NOTREACHED */
     default:
       errno = EINVAL;
-      return -1;
+      goto done;
       /* NOTREACHED */
     }
-
-  return fcntl_worker (filedes, cmd, &fl);
+  res = cfd->lock (cmd, &fl);
+done:
+  syscall_printf ("%d = lockf (%d, %d, %D)", res, filedes, function, size);
+  return res;
 }