From a5fa90a21f911e33c17f5b624f4e6ae6aa6aabb4 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 15 Sep 2022 12:44:00 -0700 Subject: [PATCH] Fix POSIX advisory locks on Windows --- libc/calls/fcntl-nt.c | 103 +++++++---- libc/intrin/describeflags.internal.h | 2 + libc/intrin/describentlockfileflags.c | 31 ++++ libc/intrin/describentoverlapped.c | 50 ++++++ libc/intrin/describentoverlapped.internal.h | 13 ++ libc/intrin/lockfileex.c | 50 ++++++ libc/intrin/unlockfileex.c | 45 +++++ libc/nt/kernel32/LockFileEx.s | 4 +- libc/nt/kernel32/SuspendThread.s | 13 ++ libc/nt/kernel32/UnlockFileEx.s | 4 +- libc/nt/master.sh | 6 +- test/libc/calls/lock_test.c | 183 ++++++++++++++++++++ test/libc/calls/test.mk | 3 + third_party/sqlite3/main.c | 4 + 14 files changed, 472 insertions(+), 39 deletions(-) create mode 100644 libc/intrin/describentlockfileflags.c create mode 100644 libc/intrin/describentoverlapped.c create mode 100755 libc/intrin/describentoverlapped.internal.h create mode 100644 libc/intrin/lockfileex.c create mode 100644 libc/intrin/unlockfileex.c create mode 100644 test/libc/calls/lock_test.c diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 9ddd29789..05626e755 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -44,22 +44,34 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" +bool __force_sqlite_to_work_until_we_can_fix_it; + static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) { + if (start < 0) return einval(); return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start); } static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) { + int e; struct flock *l; uint32_t flags, err; - struct NtOverlapped ov; int64_t pos, off, len, size; struct NtByHandleFileInformation info; - if (!GetFileInformationByHandle(f->handle, &info)) return __winerr(); - if (!SetFilePointerEx(f->handle, 0, &pos, SEEK_CUR)) return __winerr(); + + if (!GetFileInformationByHandle(f->handle, &info)) { + return __winerr(); + } + + pos = 0; + if (!SetFilePointerEx(f->handle, 0, &pos, SEEK_CUR)) { + return __winerr(); + } + l = (struct flock *)arg; len = l->l_len; off = l->l_start; size = (uint64_t)info.nFileSizeHigh << 32 | info.nFileSizeLow; + switch (l->l_whence) { case SEEK_SET: break; @@ -72,37 +84,64 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) { default: return einval(); } - if (!len) len = size - off; - if (off < 0 || len < 0) return einval(); - _offset2overlap(f->handle, off, &ov); - if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) { - flags = 0; - if (cmd == F_SETLK) flags |= kNtLockfileFailImmediately; - /* TODO: How can we make SQLite locks on Windows to work? */ - /* if (l->l_type == F_WRLCK) flags |= kNtLockfileExclusiveLock; */ - if (LockFileEx(f->handle, flags, 0, len, len >> 32, &ov)) { - return 0; - } else { - err = GetLastError(); - if (err == kNtErrorLockViolation) err = EAGAIN; - errno = err; - return -1; - } - } else if (l->l_type == F_UNLCK) { - if (UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { - return 0; - } else { - err = GetLastError(); - if (err == kNtErrorNotLocked) { - return 0; - } else { - errno = err; - return -1; - } - } - } else { + + if (!len) { + len = size - off; + } + + if (off < 0 || len < 0) { return einval(); } + + bool32 ok; + struct NtOverlapped ov = {.hEvent = f->handle, + .Pointer = (void *)(uintptr_t)off}; + + if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) { + flags = 0; + if (cmd != F_SETLKW) { + flags |= kNtLockfileFailImmediately; + } + if (l->l_type == F_WRLCK) { + flags |= kNtLockfileExclusiveLock; + } + e = errno; + ok = LockFileEx(f->handle, flags, 0, len, len >> 32, &ov); + if (cmd == F_GETLK) { + if (ok) { + l->l_type = F_UNLCK; + if (!UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { + notpossible; + } + } else { + l->l_pid = -1; + ok = true; + } + } + if (ok) { + return 0; + } else if (__force_sqlite_to_work_until_we_can_fix_it) { + errno = e; + return 0; + } else { + return -1; + } + } + + if (l->l_type == F_UNLCK) { + if (cmd == F_GETLK) return einval(); + e = errno; + if (UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { + return 0; + } else if (errno == ENOLCK) { + errno = e; + return 0; + } else { + return 0; + } + } + + return einval(); } textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index afb87b598..8ec3b0aa5 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -30,6 +30,7 @@ const char *DescribeNtFileFlagAttr(char[256], uint32_t); const char *DescribeNtFileMapFlags(char[64], uint32_t); const char *DescribeNtFileShareFlags(char[64], uint32_t); const char *DescribeNtFiletypeFlags(char[64], uint32_t); +const char *DescribeNtLockFileFlags(char[64], uint32_t); const char *DescribeNtMovFileInpFlags(char[256], uint32_t); const char *DescribeNtPageFlags(char[64], uint32_t); const char *DescribeNtPipeModeFlags(char[64], uint32_t); @@ -72,6 +73,7 @@ const char *DescribeWhence(char[12], int); #define DescribeNtFileMapFlags(x) DescribeNtFileMapFlags(alloca(64), x) #define DescribeNtFileShareFlags(x) DescribeNtFileShareFlags(alloca(64), x) #define DescribeNtFiletypeFlags(x) DescribeNtFiletypeFlags(alloca(64), x) +#define DescribeNtLockFileFlags(x) DescribeNtLockFileFlags(alloca(64), x) #define DescribeNtMovFileInpFlags(x) DescribeNtMovFileInpFlags(alloca(256), x) #define DescribeNtPageFlags(x) DescribeNtPageFlags(alloca(64), x) #define DescribeNtPipeModeFlags(x) DescribeNtPipeModeFlags(alloca(64), x) diff --git a/libc/intrin/describentlockfileflags.c b/libc/intrin/describentlockfileflags.c new file mode 100644 index 000000000..d7afc98c6 --- /dev/null +++ b/libc/intrin/describentlockfileflags.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/describeflags.internal.h" +#include "libc/macros.internal.h" +#include "libc/nt/enum/filelockflags.h" + +static const struct DescribeFlags kNtLockFileFlags[] = { + {kNtLockfileFailImmediately, "FailImmediately"}, // + {kNtLockfileExclusiveLock, "ExclusiveLock"}, // +}; + +const char *(DescribeNtLockFileFlags)(char buf[64], uint32_t x) { + return DescribeFlags(buf, 64, kNtLockFileFlags, ARRAYLEN(kNtLockFileFlags), + "kNtLockfile", x); +} diff --git a/libc/intrin/describentoverlapped.c b/libc/intrin/describentoverlapped.c new file mode 100644 index 000000000..3a4b33a74 --- /dev/null +++ b/libc/intrin/describentoverlapped.c @@ -0,0 +1,50 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/describentoverlapped.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/nt/struct/overlapped.h" + +const char *(DescribeNtOverlapped)(char b[128], struct NtOverlapped *o) { + int i = 0, n = 128; + bool gotsome = false; + if (!o) return "NULL"; + i += ksnprintf(b + i, MAX(0, n - i), "{"); + + if (o->hEvent) { + if (gotsome) { + i += ksnprintf(b + i, MAX(0, n - i), ", "); + } else { + gotsome = true; + } + i += ksnprintf(b + i, MAX(0, n - i), ".hEvent = %ld", o->hEvent); + } + + if (o->Pointer) { + if (gotsome) { + i += ksnprintf(b + i, MAX(0, n - i), ", "); + } else { + gotsome = true; + } + i += ksnprintf(b + i, MAX(0, n - i), ".Pointer = (void *)%p", o->Pointer); + } + + i += ksnprintf(b + i, MAX(0, n - i), "}"); + return b; +} diff --git a/libc/intrin/describentoverlapped.internal.h b/libc/intrin/describentoverlapped.internal.h new file mode 100755 index 000000000..8d33a7611 --- /dev/null +++ b/libc/intrin/describentoverlapped.internal.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_DESCRIBENTOVERLAPPED_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_INTRIN_DESCRIBENTOVERLAPPED_INTERNAL_H_ +#include "libc/mem/alloca.h" +#include "libc/nt/struct/overlapped.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +const char *DescribeNtOverlapped(char[128], struct NtOverlapped *); +#define DescribeNtOverlapped(x) DescribeNtOverlapped(alloca(128), x) + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_DESCRIBENTOVERLAPPED_INTERNAL_H_ */ diff --git a/libc/intrin/lockfileex.c b/libc/intrin/lockfileex.c new file mode 100644 index 000000000..b9d97587c --- /dev/null +++ b/libc/intrin/lockfileex.c @@ -0,0 +1,50 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/describentoverlapped.internal.h" +#include "libc/nt/files.h" + +__msabi extern typeof(LockFileEx) *const __imp_LockFileEx; + +/** + * Locks file on the New Technology. + * + * @return handle, or -1 on failure + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +bool32 LockFileEx(int64_t hFile, uint32_t dwFlags, uint32_t dwReserved, + uint32_t nNumberOfBytesToLockLow, + uint32_t nNumberOfBytesToLockHigh, + struct NtOverlapped *lpOverlapped) { + bool32 ok; + STRACE("LockFileEx(%ld, %s, %#x, %'zu, %s) → ...", hFile, + DescribeNtLockFileFlags(dwFlags), dwReserved, + (uint64_t)nNumberOfBytesToLockHigh << 32 | nNumberOfBytesToLockLow, + DescribeNtOverlapped(lpOverlapped)); + ok = __imp_LockFileEx(hFile, dwFlags, dwReserved, nNumberOfBytesToLockLow, + nNumberOfBytesToLockHigh, lpOverlapped); + if (!ok) __winerr(); + STRACE("LockFileEx(%ld, %s, %#x, %'zu, [%s]) → %hhhd% m", hFile, + DescribeNtLockFileFlags(dwFlags), dwReserved, + (uint64_t)nNumberOfBytesToLockHigh << 32 | nNumberOfBytesToLockLow, + DescribeNtOverlapped(lpOverlapped), ok); + return ok; +} diff --git a/libc/intrin/unlockfileex.c b/libc/intrin/unlockfileex.c new file mode 100644 index 000000000..8d2908463 --- /dev/null +++ b/libc/intrin/unlockfileex.c @@ -0,0 +1,45 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/strace.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/intrin/describentoverlapped.internal.h" +#include "libc/nt/files.h" +#include "libc/nt/struct/overlapped.h" + +__msabi extern typeof(UnlockFileEx) *const __imp_UnlockFileEx; + +/** + * Unlocks file on the New Technology. + * + * @return handle, or -1 on failure + * @note this wrapper takes care of ABI, STRACE(), and __winerr() + */ +bool32 UnlockFileEx(int64_t hFile, uint32_t dwReserved, + uint32_t nNumberOfBytesToUnlockLow, + uint32_t nNumberOfBytesToUnlockHigh, + struct NtOverlapped *lpOverlapped) { + bool32 ok; + ok = __imp_UnlockFileEx(hFile, dwReserved, nNumberOfBytesToUnlockLow, + nNumberOfBytesToUnlockHigh, lpOverlapped); + if (!ok) __winerr(); + STRACE("UnlockFileEx(%ld, %#x, %'zu, %s) → %hhhd% m", hFile, dwReserved, + (uint64_t)nNumberOfBytesToUnlockHigh << 32 | nNumberOfBytesToUnlockLow, + DescribeNtOverlapped(lpOverlapped), ok); + return ok; +} diff --git a/libc/nt/kernel32/LockFileEx.s b/libc/nt/kernel32/LockFileEx.s index 9aba0a595..f74843927 100644 --- a/libc/nt/kernel32/LockFileEx.s +++ b/libc/nt/kernel32/LockFileEx.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_LockFileEx,LockFileEx,0 .text.windows -LockFileEx: +__LockFileEx: push %rbp mov %rsp,%rbp .profilable mov __imp_LockFileEx(%rip),%rax jmp __sysv2nt6 - .endfn LockFileEx,globl + .endfn __LockFileEx,globl .previous diff --git a/libc/nt/kernel32/SuspendThread.s b/libc/nt/kernel32/SuspendThread.s index 350b06471..76654fa04 100644 --- a/libc/nt/kernel32/SuspendThread.s +++ b/libc/nt/kernel32/SuspendThread.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_SuspendThread,SuspendThread,0 + + .text.windows +SuspendThread: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_SuspendThread(%rip) + leave + ret + .endfn SuspendThread,globl + .previous diff --git a/libc/nt/kernel32/UnlockFileEx.s b/libc/nt/kernel32/UnlockFileEx.s index 577232d60..0dedd8b8b 100644 --- a/libc/nt/kernel32/UnlockFileEx.s +++ b/libc/nt/kernel32/UnlockFileEx.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_UnlockFileEx,UnlockFileEx,0 .text.windows -UnlockFileEx: +__UnlockFileEx: push %rbp mov %rsp,%rbp .profilable mov __imp_UnlockFileEx(%rip),%rax jmp __sysv2nt6 - .endfn UnlockFileEx,globl + .endfn __UnlockFileEx,globl .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index ae8625595..415068d57 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -700,7 +700,6 @@ imp 'LocalUnlock' LocalUnlock kernel32 0 imp 'LocaleNameToLCID' LocaleNameToLCID kernel32 0 imp 'LocateXStateFeature' LocateXStateFeature kernel32 0 imp 'LockFile' LockFile kernel32 0 5 -imp 'LockFileEx' LockFileEx kernel32 0 6 imp 'LockResource' LockResource kernel32 0 1 imp 'MapUserPhysicalPages' MapUserPhysicalPages kernel32 0 imp 'MapUserPhysicalPagesScatter' MapUserPhysicalPagesScatter kernel32 986 @@ -983,7 +982,7 @@ imp 'SleepConditionVariableSRW' SleepConditionVariableSRW kernel32 0 imp 'SleepEx' SleepEx kernel32 0 2 imp 'SortCloseHandle' SortCloseHandle kernel32 1416 imp 'SortGetHandle' SortGetHandle kernel32 1417 -imp 'SuspendThread' SuspendThread kernel32 0 +imp 'SuspendThread' SuspendThread kernel32 0 1 imp 'SwitchToFiber' SwitchToFiber kernel32 0 imp 'SwitchToThread' SwitchToThread kernel32 0 imp 'SystemTimeToFileTime' SystemTimeToFileTime kernel32 0 2 @@ -1024,7 +1023,6 @@ imp 'UTUnRegister' UTUnRegister kernel32 1459 imp 'UmsThreadYield' UmsThreadYield kernel32 1460 imp 'UnhandledExceptionFilter' UnhandledExceptionFilter kernel32 0 imp 'UnlockFile' UnlockFile kernel32 0 5 -imp 'UnlockFileEx' UnlockFileEx kernel32 0 5 imp 'UnmapViewOfFile2' UnmapViewOfFile2 kernel32 0 2 imp 'UnmapViewOfFileEx' UnmapViewOfFileEx kernel32 0 3 imp 'UnregisterApplicationRecoveryCallback' UnregisterApplicationRecoveryCallback kernel32 1466 @@ -1120,6 +1118,7 @@ imp '__FlushViewOfFile' FlushViewOfFile kernel32 0 2 imp '__GenerateConsoleCtrlEvent' GenerateConsoleCtrlEvent kernel32 0 2 imp '__GetExitCodeProcess' GetExitCodeProcess kernel32 0 2 imp '__GetFileAttributes' GetFileAttributesW kernel32 0 1 +imp '__LockFileEx' LockFileEx kernel32 0 6 imp '__MapViewOfFileEx' MapViewOfFileEx kernel32 0 6 imp '__MapViewOfFileExNuma' MapViewOfFileExNuma kernel32 0 7 imp '__MoveFileEx' MoveFileExW kernel32 0 3 @@ -1128,6 +1127,7 @@ imp '__ReOpenFile' ReOpenFile kernel32 0 4 # TODO(jart): 6.2 and highe imp '__RemoveDirectory' RemoveDirectoryW kernel32 0 1 imp '__SetCurrentDirectory' SetCurrentDirectoryW kernel32 0 1 imp '__TerminateProcess' TerminateProcess kernel32 0 2 +imp '__UnlockFileEx' UnlockFileEx kernel32 0 5 imp '__UnmapViewOfFile' UnmapViewOfFile kernel32 0 1 imp '__VirtualProtect' VirtualProtect kernel32 0 4 imp '__WaitForMultipleObjects' WaitForMultipleObjects kernel32 0 4 diff --git a/test/libc/calls/lock_test.c b/test/libc/calls/lock_test.c new file mode 100644 index 000000000..7858733b0 --- /dev/null +++ b/test/libc/calls/lock_test.c @@ -0,0 +1,183 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/struct/flock.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +/** + * @fileoverview POSIX Advisory Locks Test + */ + +#define PROCESSES 8 +#define THREADS (IsWindows() ? 8 : 0) +#define RATIO 3 +#define ITERATIONS 10 + +char testlib_enable_tmp_setup_teardown; + +_Thread_local const char *kind; + +void Log(const char *fmt, ...) { +#if 0 + va_list va; + char b[512]; + int i, n = sizeof(b); + va_start(va, fmt); + i = ksnprintf(b, n, "%s pid=%d tid=%d ", kind, getpid(), gettid()); + i += kvsnprintf(b + i, MAX(0, n - i), fmt, va); + i += ksnprintf(b + i, MAX(0, n - i), "\n"); + write(2, b, MIN(i, n)); + va_end(va); +#endif +} + +void Lock(int fd, int type, long start, long len) { + int e; + struct flock lock = { + .l_type = type, + .l_whence = SEEK_SET, + .l_start = start, + .l_len = len, + }; + e = errno; + while (fcntl(fd, F_SETLK, &lock)) { + ASSERT_TRUE(errno == EAGAIN || errno == EACCES); + errno = e; + Log("flock spinning on %d", fd); + } +} + +void WriteLock(int fd, long start, long len) { + Lock(fd, F_WRLCK, start, len); + Log("acquired write lock on %d", fd); +} + +void ReadLock(int fd, long start, long len) { + Lock(fd, F_RDLCK, start, len); + Log("acquired read lock on %d", fd); +} + +void Unlock(int fd, long start, long len) { + Lock(fd, F_UNLCK, start, len); + Log("released lock on %d", fd); +} + +void *Reader(void *arg) { + int i, j, fd; + char buf[10]; + kind = arg; + ASSERT_NE(-1, (fd = open("db", O_RDONLY))); + for (j = 0; j < ITERATIONS; ++j) { + ReadLock(fd, 10, 10); + for (i = 0; i < 10; ++i) { + ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i)); + ASSERT_EQ(buf[0], buf[i]); + } + Unlock(fd, 10, 10); + sched_yield(); + } + ASSERT_SYS(0, 0, close(fd)); + return 0; +} + +void *Writer(void *arg) { + int i, j, fd; + char buf[10]; + kind = arg; + ASSERT_NE(-1, (fd = open("db", O_RDWR))); + for (j = 0; j < ITERATIONS; ++j) { + WriteLock(fd, 10, 10); + for (i = 0; i < 10; ++i) { + ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i)); + ASSERT_EQ(buf[0], buf[i]); + } + for (i = 0; i < 10; ++i) { + buf[i]++; + } + for (i = 0; i < 10; ++i) { + ASSERT_SYS(0, 1, pwrite(fd, buf + i, 1, 10 + i)); + } + Unlock(fd, 10, 10); + sched_yield(); + } + ASSERT_SYS(0, 0, close(fd)); + return 0; +} + +TEST(posixAdvisoryLocks, threadsAndProcessesFightingForLock) { + int i, rc, pid, fd, ws; + pthread_t th[THREADS + 1]; + + ASSERT_SYS(0, 3, creat("db", 0644)); + ASSERT_SYS(0, 0, ftruncate(3, 30)); + ASSERT_SYS(0, 0, close(3)); + + for (i = 0; i < THREADS; ++i) { + if (i % RATIO == 0) { + ASSERT_EQ(0, pthread_create(th + i, 0, Reader, "reader thread")); + } else { + ASSERT_EQ(0, pthread_create(th + i, 0, Writer, "writer thread")); + } + } + + for (i = 0; i < PROCESSES; ++i) { + ASSERT_NE(-1, (rc = fork())); + if (!rc) { + if (i % RATIO == 0) { + Writer("writer process"); + } else { + Reader("reader process"); + } + _Exit(0); + } + } + + ASSERT_NE(-1, (fd = open("db", O_RDWR))); + Lock(fd, F_WRLCK, 0, 10); + Lock(fd, F_WRLCK, 20, 10); + + for (i = 0; i < THREADS; ++i) { + ASSERT_EQ(0, pthread_join(th[i], 0)); + } + + kind = "main process"; + for (;;) { + int e = errno; + if ((pid = waitpid(0, &ws, 0)) != -1) { + if (WIFSIGNALED(ws)) { + Log("process %d terminated with %G", pid, WTERMSIG(ws)); + testlib_incrementfailed(); + } else if (WEXITSTATUS(ws)) { + Log("process %d exited with %d", pid, WEXITSTATUS(ws)); + testlib_incrementfailed(); + } + } else { + ASSERT_EQ(ECHILD, errno); + errno = e; + break; + } + } + + ASSERT_SYS(0, 0, close(fd)); +} diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index 16c467504..a4fbbe871 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -103,6 +103,9 @@ o/$(MODE)/test/libc/calls/poll_test.com.runs: \ o/$(MODE)/test/libc/calls/fcntl_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc flock +o/$(MODE)/test/libc/calls/lock_test.com.runs: \ + private .PLEDGE = stdio rpath wpath cpath fattr proc flock + o/$(MODE)/test/libc/calls/openbsd_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc unveil diff --git a/third_party/sqlite3/main.c b/third_party/sqlite3/main.c index 31673e193..02e7fe6a8 100644 --- a/third_party/sqlite3/main.c +++ b/third_party/sqlite3/main.c @@ -3105,6 +3105,10 @@ static int openDatabase( char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ int i; /* Loop counter */ + // TODO(jart): Fix SQLite. + extern bool __force_sqlite_to_work_until_we_can_fix_it; + __force_sqlite_to_work_until_we_can_fix_it = true; + #ifdef SQLITE_ENABLE_API_ARMOR if( ppDb==0 ) return SQLITE_MISUSE_BKPT; #endif