diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2025-08-05 17:08:01 +0200 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2025-08-05 17:10:08 +0200 |
commit | a66ed519884da43a325acb5406bcf7ca5341a556 (patch) | |
tree | 1d19c57c231438fee7eb02a3875684a6e1ec10cb | |
parent | a6c59223014e3feadd0abba2d083b51e5c41ed67 (diff) | |
download | newlib-a66ed519884da43a325acb5406bcf7ca5341a556.zip newlib-a66ed519884da43a325acb5406bcf7ca5341a556.tar.gz newlib-a66ed519884da43a325acb5406bcf7ca5341a556.tar.bz2 |
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 <corinna@vinschen.de>
-rw-r--r-- | winsup/cygwin/fhandler/base.cc | 11 | ||||
-rw-r--r-- | winsup/cygwin/flock.cc | 91 | ||||
-rw-r--r-- | 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 09b0a35..5321ad7 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 246fb02..85800e9 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 7aa3c93..6ec8762 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. |