diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 3d18cd50b..b54060ab2 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,28 @@ +2013-06-02 Corinna Vinschen + + * autoload.cc (CancelSynchronousIo): Define. + * fcntl.cc (fcntl64): Drop handling of locking commands. + * fhandler.h (class fhandler_disk_file): Add mandatory_locking. + (fhandler_disk_file::fcntl): Declare. + (fhandler_disk_file::mand_lock): Declare. + * fhandler_disk_file.cc (fhandler_disk_file::fhandler_disk_file): + Initialize mandatory_locking. + (fhandler_disk_file::fcntl): New method. Handle F_LCK_MANDATORY and + locking commands. + (fhandler_disk_file::dup): Duplicate mandatory_locking. Fix a bug + when duplicating prw_handle failed. + (fhandler_disk_file::fixup_after_fork): Reset mandatory_locking. + * flock.cc (fhandler_disk_file::lock): Add comment. + (struct lock_parms): New struct to pass parameters to blocking_lock_thr + thread function. + (blocking_lock_thr): New thread function. + (fhandler_disk_file::mand_lock): New methof implementing mandatory + locking with Windows semantics. + * ntdll.h (NtLockFile): Declare. + (NtUnlockFile): Declare. + * include/fcntl.h: Fix a comment. + (F_LCK_MANDATORY): Define. Add lengthy comment to explain. + 2013-06-02 Corinna Vinschen * exceptions.cc (exception::handle): Resurrect accidentally lost diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index f545826c1..75771a2b6 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -574,6 +574,7 @@ LoadDLLfunc (GetIpForwardTable, 12, iphlpapi) LoadDLLfunc (GetNetworkParams, 8, iphlpapi) LoadDLLfunc (GetUdpTable, 12, iphlpapi) +LoadDLLfuncEx (CancelSynchronousIo, 4, kernel32, 1) LoadDLLfunc (CreateSymbolicLinkW, 12, kernel32) LoadDLLfuncEx (GetNamedPipeClientProcessId, 8, kernel32, 1) LoadDLLfunc (LocaleNameToLCID, 8, kernel32) diff --git a/winsup/cygwin/fcntl.cc b/winsup/cygwin/fcntl.cc index f2c2acc25..ea9a71a5e 100644 --- a/winsup/cygwin/fcntl.cc +++ b/winsup/cygwin/fcntl.cc @@ -1,7 +1,7 @@ /* fcntl.cc: fcntl syscall Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2008, 2009, - 2010, 2011, 2012 Red Hat, Inc. + 2010, 2011, 2012, 2013 Red Hat, Inc. This file is part of Cygwin. @@ -70,15 +70,6 @@ fcntl64 (int fd, int cmd, ...) res = -1; } break; - case F_GETLK: - case F_SETLK: - case F_SETLKW: - { - struct flock *fl = (struct flock *) arg; - fl->l_type &= F_RDLCK | F_WRLCK | F_UNLCK; - res = cfd->lock (cmd, fl); - } - break; default: res = cfd->fcntl (cmd, arg); break; diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index e76f67e1f..76e0ea768 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -963,6 +963,7 @@ class fhandler_dev_tape: public fhandler_dev_raw class fhandler_disk_file: public fhandler_base { HANDLE prw_handle; + bool mandatory_locking; int __reg3 readdir_helper (DIR *, dirent *, DWORD, DWORD, PUNICODE_STRING fname); int prw_open (bool); @@ -973,9 +974,11 @@ class fhandler_disk_file: public fhandler_base int open (int flags, mode_t mode); int close (); + int fcntl (int cmd, intptr_t); int dup (fhandler_base *child, int); void fixup_after_fork (HANDLE parent); int lock (int, struct flock *); + int mand_lock (int, struct flock *); bool isdevice () const { return false; } int __reg2 fstat (struct stat *buf); int __reg1 fchmod (mode_t mode); diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index 80ebfce48..1b76a3488 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -1379,12 +1379,12 @@ fhandler_base::utimens_fs (const struct timespec *tvp) } fhandler_disk_file::fhandler_disk_file () : - fhandler_base (), prw_handle (NULL) + fhandler_base (), prw_handle (NULL), mandatory_locking (false) { } fhandler_disk_file::fhandler_disk_file (path_conv &pc) : - fhandler_base (), prw_handle (NULL) + fhandler_base (), prw_handle (NULL), mandatory_locking (false) { set_name (pc); } @@ -1407,6 +1407,33 @@ fhandler_disk_file::close () return fhandler_base::close (); } +int +fhandler_disk_file::fcntl (int cmd, intptr_t arg) +{ + int res; + + switch (cmd) + { + case F_LCK_MANDATORY: + mandatory_locking = !!arg; + res = 0; + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock *fl = (struct flock *) arg; + fl->l_type &= F_RDLCK | F_WRLCK | F_UNLCK; + res = mandatory_locking ? mand_lock (cmd, fl) : lock (cmd, fl); + } + break; + default: + res = fhandler_base::fcntl (cmd, arg); + break; + } + return res; +} + int fhandler_disk_file::dup (fhandler_base *child, int flags) { @@ -1417,7 +1444,8 @@ fhandler_disk_file::dup (fhandler_base *child, int flags) && !DuplicateHandle (GetCurrentProcess (), prw_handle, GetCurrentProcess (), &fhc->prw_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) - prw_handle = NULL; + fhc->prw_handle = NULL; + fhc->mandatory_locking = mandatory_locking; return ret; } @@ -1425,6 +1453,7 @@ void fhandler_disk_file::fixup_after_fork (HANDLE parent) { prw_handle = NULL; + mandatory_locking = false; fhandler_base::fixup_after_fork (parent); } diff --git a/winsup/cygwin/flock.cc b/winsup/cygwin/flock.cc index 1e875113d..f7d19398b 100644 --- a/winsup/cygwin/flock.cc +++ b/winsup/cygwin/flock.cc @@ -1,6 +1,6 @@ /* flock.cc. NT specific implementation of advisory file locking. - Copyright 2003, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. + Copyright 2003, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. This file is part of Cygwin. @@ -915,6 +915,9 @@ 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 *, HANDLE); +/* This is the fcntl advisory lock implementation. For the implementation + of mandatory locks using the Windows mandatory locking functions, see the + fhandler_disk_file::mand_lock method at the end of this file. */ int fhandler_disk_file::lock (int a_op, struct flock *fl) { @@ -1805,3 +1808,199 @@ done: syscall_printf ("%R = lockf(%d, %d, %D)", res, filedes, function, size); return res; } + +/* This is the fcntl lock implementation for mandatory locks using the + Windows mandatory locking functions. For the UNIX-like advisory locking + implementation see the fhandler_disk_file::lock method earlier in this + file. */ +struct lock_parms { + HANDLE h; + PIO_STATUS_BLOCK pio; + PLARGE_INTEGER poff; + PLARGE_INTEGER plen; + BOOL type; + NTSTATUS status; +}; + +static DWORD WINAPI +blocking_lock_thr (LPVOID param) +{ + struct lock_parms *lp = (struct lock_parms *) param; + lp->status = NtLockFile (lp->h, NULL, NULL, NULL, lp->pio, lp->poff, + lp->plen, 0, FALSE, lp->type); + return 0; +} + +int +fhandler_disk_file::mand_lock (int a_op, struct flock *fl) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + FILE_POSITION_INFORMATION fpi; + FILE_STANDARD_INFORMATION fsi; + off_t startpos; + LARGE_INTEGER offset; + LARGE_INTEGER length; + + /* Calculate where to start from, then adjust this by fl->l_start. */ + switch (fl->l_whence) + { + case SEEK_SET: + startpos = 0; + break; + case SEEK_CUR: + status = NtQueryInformationFile (get_handle (), &io, &fpi, sizeof fpi, + FilePositionInformation); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + startpos = fpi.CurrentByteOffset.QuadPart; + break; + case SEEK_END: + status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi, + FileStandardInformation); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + startpos = fsi.EndOfFile.QuadPart; + break; + default: + set_errno (EINVAL); + return -1; + } + /* Adjust start and length until they make sense. */ + offset.QuadPart = startpos + fl->l_start; + if (fl->l_len < 0) + { + offset.QuadPart -= fl->l_len; + length.QuadPart = -fl->l_len; + } + else + length.QuadPart = fl->l_len; + if (offset.QuadPart < 0) + { + length.QuadPart -= -offset.QuadPart; + if (length.QuadPart <= 0) + { + set_errno (EINVAL); + return -1; + } + offset.QuadPart = 0; + } + /* Special case if len == 0. For POSIX this means lock to the end of + the entire file, even when file grows later. */ + if (length.QuadPart == 0) + length.QuadPart = UINT64_MAX; + /* Action! */ + if (fl->l_type == F_UNLCK) + { + status = NtUnlockFile (get_handle (), &io, &offset, &length, 0); + if (status == STATUS_RANGE_NOT_LOCKED) /* Not an error */ + status = STATUS_SUCCESS; + } + else if (a_op == F_SETLKW) + { + /* We open file handles synchronously. To allow asynchronous operation + the file locking functions require a file handle opened in asynchronous + mode. Since Windows locks are per-process/per-file object, we can't + open another handle asynchrously and lock/unlock using that handle: + The original file handle would not have placed the lock and would be + restricted by the lock like any other file handle. + So, what we do here is to start a thread which calls the potentially + blocking NtLockFile call. Then we wait for thread completion in an + interruptible fashion. */ + OBJECT_ATTRIBUTES attr; + HANDLE evt; + struct lock_parms lp = { get_handle (), &io, &offset, &length, + fl->l_type == F_WRLCK, 0 }; + cygthread *thr = NULL; + + InitializeObjectAttributes (&attr, NULL, 0, NULL, NULL); + status = NtCreateEvent (&evt, EVENT_ALL_ACCESS, &attr, + NotificationEvent, FALSE); + if (evt) + thr = new cygthread (blocking_lock_thr, &lp, "blk_lock", evt); + if (!thr) + { + /* Thread creation failed. Fall back to blocking lock call. */ + if (evt) + NtClose (evt); + status = NtLockFile (get_handle (), NULL, NULL, NULL, &io, &offset, + &length, 0, FALSE, fl->l_type == F_WRLCK); + } + else + { + /* F_SETLKW and lock cannot be established. Wait until the lock can + be established, or a signal request arrived. We deliberately + don't handle thread cancel requests here. */ + DWORD wait_res = cygwait (evt, INFINITE, cw_sig | cw_sig_eintr); + NtClose (evt); + switch (wait_res) + { + case WAIT_OBJECT_0: + /* Fetch completion status. */ + status = lp.status; + thr->detach (); + break; + default: + /* Signal arrived. */ + /* Starting with Vista, CancelSynchronousIo works, and we wait + for the thread to exit. lp.status will be either + STATUS_SUCCESS, or STATUS_CANCELLED. We only call + NtUnlockFile in the first case. + Prior to Vista, CancelSynchronousIo doesn't exist, so we + terminated the thread and always call NtUnlockFile since + lp.status was 0 to begin with. */ + if (CancelSynchronousIo (thr->thread_handle ())) + thr->detach (); + else + thr->terminate_thread (); + if (NT_SUCCESS (lp.status)) + NtUnlockFile (get_handle (), &io, &offset, &length, 0); + /* Per SUSv4: If a signal is received while fcntl is waiting, + fcntl shall be interrupted. Upon return from the signal + handler, fcntl shall return -1 with errno set to EINTR, + and the lock operation shall not be done. */ + _my_tls.call_signal_handler (); + set_errno (EINTR); + return -1; + } + } + } + else + { + status = NtLockFile (get_handle (), NULL, NULL, NULL, &io, &offset, + &length, 0, TRUE, fl->l_type == F_WRLCK); + if (a_op == F_GETLK) + { + /* This is non-atomic, but there's no other way on Windows to detect + if another lock is blocking our lock, other than trying to place + the lock, and then having to unlock it again. */ + if (NT_SUCCESS (status)) + { + NtUnlockFile (get_handle (), &io, &offset, &length, 0); + fl->l_type = F_UNLCK; + } + else + { + /* FAKE! FAKE! FAKE! */ + fl->l_type = F_WRLCK; + fl->l_whence = SEEK_SET; + fl->l_start = offset.QuadPart; + fl->l_len = length.QuadPart; + fl->l_pid = (pid_t) -1; + } + status = STATUS_SUCCESS; + } + } + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + return -1; + } + return 0; +} diff --git a/winsup/cygwin/include/fcntl.h b/winsup/cygwin/include/fcntl.h index 9c3ddf926..fe8a1fc42 100644 --- a/winsup/cygwin/include/fcntl.h +++ b/winsup/cygwin/include/fcntl.h @@ -1,6 +1,6 @@ /* fcntl.h - Copyright 1996, 1998, 2000, 2001, 2005, 2006, 2009, 2010 Red Hat, Inc. + Copyright 1996, 1998, 2000, 2001, 2005, 2006, 2009, 2010, 2013 Red Hat, Inc. This file is part of Cygwin. @@ -14,12 +14,29 @@ details. */ #include #define O_NDELAY _FNDELAY -/* sys/fcntl defines values up to 0x40000 (O_NOINHERIT). */ +/* sys/_default_fcntl.h defines values up to 0x40000 (O_NOINHERIT). */ #define _FDIRECT 0x80000 #define _FNOFOLLOW 0x100000 #define _FDIRECTORY 0x200000 #define _FEXECSRCH 0x400000 +/* F_LCK_MANDATORY: Request mandatory locks for this file descriptor. + + Cygwin extension to fcntl file locking mechanism. By default, fcntl file + locks are advisory locks. This works nicely as long as only Cygwin + processes interact. If you have the requirement to interact with native + Windows applications which use Windows mandatory file locking, your have + to use mandatory locking as well. The command + + fcntl (fd, F_LCK_MANDATORY, 1) + + switches subsequent F_GETLK, F_SETLK, F_SETLKW calls to mandatory locking + for this file descriptor and subsequently duplicated ones WITHIN THE SAME + PROCESS. Note that mandatory locks are NOT inherited by child processes, + nor do they survive an execve call. This fully corresponds to Windows + mandatory locking semantics. */ +#define F_LCK_MANDATORY 0x99 + /* POSIX-1.2008 requires this flag and allows to set it to 0 if its functionality is not required. */ #define O_TTY_INIT 0 diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 4d302ffe7..2582bf25f 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -1224,6 +1224,9 @@ extern "C" PVOID, ULONG); NTSTATUS NTAPI NtFlushBuffersFile (HANDLE, PIO_STATUS_BLOCK); NTSTATUS NTAPI NtLoadKey (POBJECT_ATTRIBUTES, POBJECT_ATTRIBUTES); + NTSTATUS NTAPI NtLockFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, + PIO_STATUS_BLOCK, PLARGE_INTEGER, PLARGE_INTEGER, + ULONG, BOOLEAN, BOOLEAN); NTSTATUS NTAPI NtLockVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG); NTSTATUS NTAPI NtMapViewOfSection (HANDLE, HANDLE, PVOID *, ULONG_PTR, SIZE_T, PLARGE_INTEGER, PSIZE_T, SECTION_INHERIT, @@ -1308,6 +1311,8 @@ extern "C" NTSTATUS NTAPI NtSetTimerResolution (ULONG, BOOLEAN, PULONG); NTSTATUS NTAPI NtSetValueKey (HANDLE, PUNICODE_STRING, ULONG, ULONG, PVOID, ULONG); + NTSTATUS NTAPI NtUnlockFile (HANDLE, PIO_STATUS_BLOCK, PLARGE_INTEGER, + PLARGE_INTEGER, ULONG); NTSTATUS NTAPI NtUnlockVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG); NTSTATUS NTAPI NtUnmapViewOfSection (HANDLE, PVOID); NTSTATUS NTAPI NtWriteFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, diff --git a/winsup/cygwin/release/1.7.19 b/winsup/cygwin/release/1.7.19 index 932404153..7cae9ad98 100644 --- a/winsup/cygwin/release/1.7.19 +++ b/winsup/cygwin/release/1.7.19 @@ -10,6 +10,9 @@ What's new: - Add support for the AFS filesystem. +- Preliminary support for mandatory locking via fcntl, using Windows + locking semantics. New F_LCK_MANDATORY fcntl command. + - New APIs: __b64_ntop, __b64_pton, arc4random, arc4random_addrandom, arc4random_buf, arc4random_stir, arc4random_uniform.