Fix msync() flags on FreeBSD

This commit is contained in:
Justine Tunney 2023-08-21 04:15:05 -07:00
parent ebf784d4f5
commit fffcd98b0e
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
7 changed files with 95 additions and 15 deletions

View file

@ -25,6 +25,7 @@
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Synchronize memory mapping changes to disk.
@ -37,20 +38,61 @@
* @return 0 on success or -1 w/ errno
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if we needed to block and a signal was delivered instead
* @raise EINVAL if `MS_SYNC` and `MS_ASYNC` were both specified
* @raise EINVAL if unknown `flags` were passed
* @cancellationpoint
*/
int msync(void *addr, size_t size, int flags) {
int rc;
BEGIN_CANCELLATION_POINT;
unassert(((flags & MS_SYNC) ^ (flags & MS_ASYNC)) || !(MS_SYNC && MS_ASYNC));
if (!IsWindows()) {
rc = sys_msync(addr, size, flags);
} else {
rc = sys_msync_nt(addr, size, flags);
if ((flags & ~(MS_SYNC | MS_ASYNC | MS_INVALIDATE)) ||
(flags & (MS_SYNC | MS_ASYNC)) == (MS_SYNC | MS_ASYNC)) {
rc = einval();
goto Finished;
}
// According to POSIX, either MS_SYNC or MS_ASYNC must be specified
// in flags, and indeed failure to include one of these flags will
// cause msync() to fail on some systems. However, Linux permits a
// call to msync() that specifies neither of these flags, with
// semantics that are (currently) equivalent to specifying MS_ASYNC.
// ──Quoth msync(2) of Linux Programmer's Manual
int sysflags = flags;
sysflags = flags;
if (flags & MS_ASYNC) {
sysflags = MS_ASYNC;
} else if (flags & MS_SYNC) {
sysflags = MS_SYNC;
} else {
sysflags = MS_ASYNC;
}
if (flags & MS_INVALIDATE) {
sysflags |= MS_INVALIDATE;
}
// FreeBSD's manual says "The flags argument was both MS_ASYNC and
// MS_INVALIDATE. Only one of these flags is allowed." which makes
// following the POSIX recommendation somewhat difficult.
if (IsFreebsd()) {
if (sysflags == (MS_ASYNC | MS_INVALIDATE)) {
sysflags = MS_INVALIDATE;
}
}
// FreeBSD specifies MS_SYNC as 0 so we shift the Cosmo constants
if (IsFreebsd()) {
sysflags >>= 1;
}
BEGIN_CANCELLATION_POINT;
if (!IsWindows()) {
rc = sys_msync(addr, size, sysflags);
} else {
rc = sys_msync_nt(addr, size, sysflags);
}
END_CANCELLATION_POINT;
Finished:
STRACE("msync(%p, %'zu, %#x) → %d% m", addr, size, flags, rc);
return rc;
}

View file

@ -959,9 +959,9 @@ syscon pf PF_X25 9 9 0 0 0 0 0 0
# msync() flags
#
# group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon ms MS_SYNC 4 4 16 16 0 2 4 4 # faked nt
syscon ms MS_ASYNC 1 1 1 1 1 1 1 1 # consensus (faked nt)
syscon ms MS_INVALIDATE 2 2 2 2 2 4 2 0
syscon ms MS_SYNC 4 4 16 16 1 2 4 4 # faked nt; actually 0 on freebsd
syscon ms MS_ASYNC 1 1 1 1 2 1 1 1 # faked nt; actually 1 on freebsd
syscon ms MS_INVALIDATE 2 2 2 2 4 4 2 0 # actually 2 on freebsd
# statfs() flags
#

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon ms,MS_ASYNC,1,1,1,1,1,1,1,1
.syscon ms,MS_ASYNC,1,1,1,1,2,1,1,1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon ms,MS_INVALIDATE,2,2,2,2,2,4,2,0
.syscon ms,MS_INVALIDATE,2,2,2,2,4,4,2,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon ms,MS_SYNC,4,4,16,16,0,2,4,4
.syscon ms,MS_SYNC,4,4,16,16,1,2,4,4

View file

@ -7,11 +7,10 @@ extern const int MS_SYNC;
extern const int MS_ASYNC;
extern const int MS_INVALIDATE;
#define MS_ASYNC 1
#define MS_SYNC MS_SYNC
#define MS_ASYNC MS_ASYNC
#define MS_INVALIDATE MS_INVALIDATE
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_MSYNC_H_ */