https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=a66ed519884da43a325acb5406bcf7ca5341a556

commit a66ed519884da43a325acb5406bcf7ca5341a556
Author:     Corinna Vinschen <cori...@vinschen.de>
AuthorDate: Tue Aug 5 17:08:01 2025 +0200
Commit:     Corinna Vinschen <cori...@vinschen.de>
CommitDate: Tue Aug 5 17:10:08 2025 +0200

    Cygwin: fcntl: implement Open File Description (OFD) locks
    
    Partially quoting the LINUX man page:
    
    Open file description locks are advisory byte-range locks whose opera‐
    tion is in most respects identical to the traditional record locks de‐
    scribed above.
    
    The principal difference between the two lock types is that whereas tra‐
    ditional record locks are associated with a process, open file descrip‐
    tion locks are associated with the open file description on which they
    are acquired, much like locks acquired with flock(2). Consequently (and
    unlike traditional advisory record locks), open file description locks
    are inherited across fork(2) (and clone(2) with CLONE_FILES), and are
    only automatically released on the last close of the open file descrip‐
    tion, instead of being released on any close of the file.
    
    Conflicting lock combinations (i.e., a read lock and a write lock or two
    write locks) where one lock is an open file description lock and the
    other is a traditional record lock conflict even when they are acquired
    by the same process on the same file descriptor.
    
    Just like traditional advisory record locks, OFD locks do not conflict
    with BSD file locks acquired with flock(2).
    
    Signed-off-by: Corinna Vinschen <cori...@vinschen.de>

Diff:
---
 winsup/cygwin/fhandler/base.cc | 11 +++++
 winsup/cygwin/flock.cc         | 91 ++++++++++++++++++++++++------------------
 winsup/cygwin/release/3.7.0    |  2 +
 3 files changed, 66 insertions(+), 38 deletions(-)

diff --git a/winsup/cygwin/fhandler/base.cc b/winsup/cygwin/fhandler/base.cc
index 09b0a35e614f..5321ad7ff175 100644
--- a/winsup/cygwin/fhandler/base.cc
+++ b/winsup/cygwin/fhandler/base.cc
@@ -1503,6 +1503,17 @@ int fhandler_base::fcntl (int cmd, intptr_t arg)
          res = mandatory_locking () ? mand_lock (cmd, fl) : lock (cmd, fl);
        }
       break;
+    case F_OFD_GETLK:
+    case F_OFD_SETLK:
+    case F_OFD_SETLKW:
+       {
+         struct flock *fl = (struct flock *) arg;
+         fl->l_type &= F_RDLCK | F_WRLCK | F_UNLCK;
+         fl->l_type |= F_OFD;
+         /* No mandatory locking support for OFD locks. */
+         res = lock (cmd, fl);
+       }
+      break;
     default:
       set_errno (EINVAL);
       res = -1;
diff --git a/winsup/cygwin/flock.cc b/winsup/cygwin/flock.cc
index 246fb02e149a..85800e971462 100644
--- a/winsup/cygwin/flock.cc
+++ b/winsup/cygwin/flock.cc
@@ -224,12 +224,13 @@ struct lockfattr_t
 class lockf_t
 {
   public:
-    uint16_t       lf_flags; /* Semantics: F_POSIX, F_FLOCK, F_WAIT */
+    uint16_t       lf_flags; /* Semantics: F_OFD, F_POSIX, F_FLOCK, F_WAIT */
     uint16_t       lf_type;  /* Lock type: F_RDLCK, F_WRLCK */
     off_t          lf_start; /* Byte # of the start of the lock */
     off_t          lf_end;   /* Byte # of the end of the lock (-1=EOF) */
     int64_t         lf_id;    /* Cygwin PID for POSIX locks, a unique id per
-                                file table entry for BSD flock locks. */
+                                file table entry for OFD and BSD flock
+                                locks. */
     DWORD          lf_wid;   /* Win PID of the resource holding the lock */
     uint16_t       lf_ver;   /* Version number of the lock.  If a released
                                 lock event yet exists because another process
@@ -373,8 +374,8 @@ inode_t::del_my_locks (long long id, HANDLE fhdl)
          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. */
+         /* Delete OFD and BSD flock lock when no other fd in this process
+            references it anymore. */
          if (cnt <= 1)
            {
              *prev = n_lock;
@@ -546,8 +547,9 @@ lockf_t::from_obj_name (inode_t *node, lockf_t **head, 
const wchar_t *name)
   /* "%02x-%01x-%016X-%016X-%016X-%08x-%04x",
      lf_flags, lf_type, lf_start, lf_end, lf_id, lf_wid, lf_ver */
   lf_flags = wcstol (name, &endptr, 16);
-  if ((lf_flags & ~(F_FLOCK | F_POSIX)) != 0
-      || ((lf_flags & (F_FLOCK | F_POSIX)) == (F_FLOCK | F_POSIX)))
+  /* Make sure exactly one semantics flag is set. */
+  if ((lf_flags & ~(F_FLOCK | F_POSIX | F_OFD)) != 0
+      || ((lf_flags & (lf_flags - 1)) != 0))
     return false;
   lf_type = wcstol (endptr + 1, &endptr, 16);
   if ((lf_type != F_RDLCK && lf_type != F_WRLCK) || !endptr || *endptr != L'-')
@@ -632,8 +634,8 @@ POBJECT_ATTRIBUTES
 lockf_t::create_lock_obj_attr (lockfattr_t *attr, ULONG flags, void *sd_buf)
 {
   __small_swprintf (attr->name, LOCK_OBJ_NAME_FMT,
-                   lf_flags & (F_POSIX | F_FLOCK), lf_type, lf_start, lf_end,
-                   lf_id, lf_wid, lf_ver);
+                   lf_flags & (F_OFD | F_POSIX | F_FLOCK),
+                   lf_type, lf_start, lf_end, lf_id, lf_wid, lf_ver);
   RtlInitCountedUnicodeString (&attr->uname, attr->name,
                               LOCK_OBJ_NAME_LEN * sizeof (WCHAR));
   InitializeObjectAttributes (&attr->attr, &attr->uname, flags, 
lf_inode->i_dir,
@@ -734,7 +736,8 @@ delete_lock_in_parent (PVOID param)
       {
        for (prev = &node->i_lockf, lock = *prev; lock; lock = *prev)
          {
-           if ((lock->lf_flags & F_FLOCK) && IsEventSignalled (lock->lf_obj))
+           if ((lock->lf_flags & (F_OFD | F_FLOCK))
+               && IsEventSignalled (lock->lf_obj))
              {
                *prev = lock->lf_next;
                delete lock;
@@ -789,8 +792,8 @@ lockf_t::create_lock_obj ()
        }
     }
   while (!NT_SUCCESS (status));
-  /* For BSD locks, notify the parent process. */
-  if (lf_flags & F_FLOCK)
+  /* For OFD and BSD locks, notify the parent process. */
+  if (lf_flags & (F_OFD | F_FLOCK))
     {
       HANDLE parent_proc, parent_thread, parent_lf_obj;
 
@@ -865,20 +868,20 @@ lockf_t::del_lock_obj (HANDLE fhdl, bool signal)
   if (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. */
+        OFD and 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))
        {
          NTSTATUS status = NtSetEvent (lf_obj, NULL);
          if (!NT_SUCCESS (status))
            system_printf ("NtSetEvent, %y", status);
-         /* For BSD locks, notify the parent process. */
-         if (lf_flags & F_FLOCK)
+         /* For OFD and BSD locks, notify the parent process. */
+         if (lf_flags & (F_OFD | F_FLOCK))
            {
              HANDLE parent_proc, parent_thread;
 
@@ -943,7 +946,7 @@ fhandler_base::lock (int a_op, struct flock *fl)
   off_t start, end, oadd;
   int error = 0;
 
-  short a_flags = fl->l_type & (F_POSIX | F_FLOCK);
+  short a_flags = fl->l_type & (F_OFD | F_POSIX | F_FLOCK);
   short type = fl->l_type & (F_RDLCK | F_WRLCK | F_UNLCK);
 
   if (!a_flags)
@@ -954,14 +957,19 @@ fhandler_base::lock (int a_op, struct flock *fl)
 
   /* FIXME: For BSD flock(2) we need a valid, per file table entry OS handle.
      Therefore we can't allow using flock(2) on nohandle devices. */
-  if ((a_flags & F_FLOCK) && nohandle ())
+  if ((a_flags & (F_OFD | F_FLOCK)) && nohandle ())
     {
       set_errno (EINVAL);
-      debug_printf ("BSD locking on nohandle and old-style console devices "
-                   "not supported");
+      debug_printf ("OFD or BSD locking on nohandle and old-style console "
+                   "devices not supported");
       return -1;
     }
 
+  /* Simplify further checks. We don't need to differ the OPs, the semantics
+     are in fl->l_type anyway. */
+  if (a_op >= F_OFD_GETLK && a_op <= F_OFD_SETLKW)
+    a_op = a_op - (F_OFD_GETLK - F_GETLK);
+
   if (a_op == F_SETLKW)
     {
       a_op = F_SETLK;
@@ -975,14 +983,14 @@ fhandler_base::lock (int a_op, struct flock *fl)
        break;
       case F_RDLCK:
        /* 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. */
+          been opened with a specific open mode, in contrast to OFD and
+          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. */
        /* CV 2013-10-22: Test POSIX R/W mode flags rather than Windows R/W
           access flags.  The reason is that POSIX mode flags are set for
           all types of fhandlers, while Windows access flags are only set
           for most of the actual Windows device backed fhandlers. */
-       if ((a_flags & F_POSIX)
+       if ((a_flags & (F_OFD | F_POSIX))
            && ((get_flags () & O_ACCMODE) == O_WRONLY))
          {
            debug_printf ("request F_RDLCK on O_WRONLY file: EBADF");
@@ -992,7 +1000,7 @@ fhandler_base::lock (int a_op, struct flock *fl)
        break;
       case F_WRLCK:
        /* See above comment. */
-       if ((a_flags & F_POSIX)
+       if ((a_flags & (F_OFD | F_POSIX))
            && ((get_flags () & O_ACCMODE) == O_RDONLY))
          {
            debug_printf ("request F_WRLCK on O_RDONLY file: EBADF");
@@ -1131,8 +1139,8 @@ restart:  /* Entry point after a restartable signal came 
in. */
    * Create the lockf_t structure
    */
   lockf_t *lock = new lockf_t (node, head, a_flags, type, start, end,
-                              (a_flags & F_FLOCK) ? get_unique_id ()
-                                                  : getpid (),
+                              (a_flags & (F_OFD | F_FLOCK))
+                              ? get_unique_id () : getpid (),
                               myself->dwProcessId, 0);
   if (!lock)
     {
@@ -1144,6 +1152,7 @@ restart:  /* Entry point after a restartable signal came 
in. */
   switch (a_op)
     {
     case F_SETLK:
+    case F_OFD_SETLK:
       error = lf_setlock (lock, node, &clean, get_handle ());
       break;
 
@@ -1153,7 +1162,7 @@ restart:  /* Entry point after a restartable signal came 
in. */
       clean = lock;
       break;
 
-    case F_GETLK:
+    case F_OFD_GETLK:
       error = lf_getlock (lock, node, fl);
       lock->lf_next = clean;
       clean = lock;
@@ -1251,9 +1260,10 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t 
**clean, HANDLE fhdl)
                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. */
-      if (lock->lf_flags & F_POSIX)
+      if (lock->lf_flags & (F_OFD | F_POSIX))
        for (lockf_t *lk = node->i_lockf; lk; lk = lk->lf_next)
-         if ((lk->lf_flags & F_POSIX) && get_obj_handle_count (lk->lf_obj) > 1)
+         if ((lk->lf_flags & (F_OFD | F_POSIX))
+             && get_obj_handle_count (lk->lf_obj) > 1)
            {
              NtClose (obj);
              return EDEADLK;
@@ -1705,11 +1715,16 @@ lf_findoverlap (lockf_t *lf, lockf_t *lock, int type, 
lockf_t ***prev,
   end = lock->lf_end;
   while (lf != NOLOCKF)
     {
-      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)))
+      /* As on Linux: POSIX/OFD locks and BSD flock locks don't interact. */
+      bool bsd_flock = (lf->lf_flags & F_FLOCK) != (lock->lf_flags & F_FLOCK);
+
+      /* We're "self" only if the semantics and the id matches.  OFD and POSIX
+         locks potentially block each other.  This is true even for OFD and
+        POSIX locks created by the same process. */
+      bool self = (lf->lf_flags == lock->lf_flags)
+                 && (lf->lf_id == lock->lf_id);
+
+      if (bsd_flock || ((type & SELF) && !self) || ((type & OTHERS) && self))
        {
          *prev = &lf->lf_next;
          *overlap = lf = lf->lf_next;
diff --git a/winsup/cygwin/release/3.7.0 b/winsup/cygwin/release/3.7.0
index 7aa3c9384a31..6ec876263e60 100644
--- a/winsup/cygwin/release/3.7.0
+++ b/winsup/cygwin/release/3.7.0
@@ -2,3 +2,5 @@ What's new:
 -----------
 
 - Support for CLOCK_TAI clock and timer.
+
+- Support for Open File Description (OFD) locks.

Reply via email to