On musl, O_ACCMODE is defined as (03|O_SEARCH), unlike glibc which
defines it as (O_RDONLY|O_WRONLY|O_RDWR). Additionally, O_SEARCH is
simply defined as O_PATH.
This causes problems for systemd on musl, as it changes the
behaviour of open_mkdir_at_full() to return -EINVAL if O_PATH is
included in flags due to the fact that O_ACCMODE includes O_SEARCH
(i.e. O_PATH). Consequently, this makes the test-fs-util test fail.
Upstream musl seems content with this behaviour and doesn't seem
interested in matching glibc's behaviour due to that defining it this
way allows for O_SEARCH to match POSIX better by allowing it to open
directories where read permission is missing. Apparently musl does some
emulation in other places to make this work more consistently as well.
Initially I took the approach of working around this by redefining
O_SEARCH as O_RDONLY if O_SEARCH == O_PATH. This fixes the test and is
the approach taken by both XZ[1] and Gzip[2][3], but was not taken as
redefining system headers potentially could be problematic.
Instead, introduce O_ACCMODE_STRICT which just is a copy of glibc's
O_ACCMODE and use it everywhere. This way we don't have to deal with
unusual definitions of O_ACCMODE from C standard libraries other than
glibc.
[1]: https://git.tukaani.org/?p=xz.git;a=blob;f=src/xz/file_io.c;h=
8c83269b13fa31284f7ea5f3627a1dfbce7d6e14;hb=HEAD#l72
[2]: https://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/fcntl.in.h
(lines 380 and 396, commit
d7f551b30f3f2a0fa57c1b10c12f4eea41a9b89e)
[3]: https://lists.gnu.org/archive/html/bug-gzip/2025-01/msg00000.html
if (flags < 0)
return -errno;
- unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
+ unexpected_flags = flags & ~(O_ACCMODE_STRICT|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
if (unexpected_flags != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
"Unexpected flags set for extrinsic fd: 0%o",
(unsigned) unexpected_flags);
- return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */
+ return flags & (O_ACCMODE_STRICT | extra_flags); /* return the flags variable, but remove the noise */
}
int read_nr_open(void) {
}
const char* accmode_to_string(int flags) {
- switch (flags & O_ACCMODE) {
+ switch (flags & O_ACCMODE_STRICT) {
case O_RDONLY:
return "ro";
case O_WRONLY:
if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
return -EINVAL;
- if ((flags & O_ACCMODE) != O_RDONLY)
+ if ((flags & O_ACCMODE_STRICT) != O_RDONLY)
return -EINVAL;
/* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
#ifndef AT_HANDLE_FID
#define AT_HANDLE_FID AT_REMOVEDIR
#endif
+
+/* On musl, O_ACCMODE is defined as (03|O_SEARCH), unlike glibc which defines it as
+ * (O_RDONLY|O_WRONLY|O_RDWR). Additionally, O_SEARCH is simply defined as O_PATH. This changes the behaviour
+ * of O_ACCMODE in certain situations, which we don't want. This definition is copied from glibc and works
+ * around the problems with musl's definition. */
+#define O_ACCMODE_STRICT (O_RDONLY|O_WRONLY|O_RDWR)
assert(path);
- if (IN_SET(flags & O_ACCMODE, O_WRONLY, O_RDWR))
+ if (IN_SET(flags & O_ACCMODE_STRICT, O_WRONLY, O_RDWR))
flags |= O_CREAT;
fd = open(path, flags|O_NOCTTY, mode);
if (r < 0)
return r;
- if ((flags & O_ACCMODE) == O_RDONLY)
+ if ((flags & O_ACCMODE_STRICT) == O_RDONLY)
r = shutdown(fd, SHUT_WR);
- else if ((flags & O_ACCMODE) == O_WRONLY)
+ else if ((flags & O_ACCMODE_STRICT) == O_WRONLY)
r = shutdown(fd, SHUT_RD);
else
r = 0;
JournalFile, journal_file_close);
static int mmap_prot_from_open_flags(int flags) {
- switch (flags & O_ACCMODE) {
+ switch (flags & O_ACCMODE_STRICT) {
case O_RDONLY:
return PROT_READ;
case O_WRONLY:
assert(mmap_cache);
assert(ret);
- if (!IN_SET((open_flags & O_ACCMODE), O_RDONLY, O_RDWR))
+ if (!IN_SET((open_flags & O_ACCMODE_STRICT), O_RDONLY, O_RDWR))
return -EINVAL;
- if ((open_flags & O_ACCMODE) == O_RDONLY && FLAGS_SET(open_flags, O_CREAT))
+ if ((open_flags & O_ACCMODE_STRICT) == O_RDONLY && FLAGS_SET(open_flags, O_CREAT))
return -EINVAL;
if (fname && (open_flags & O_CREAT) && !endswith(fname, ".journal"))
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <fcntl.h>
#include <inttypes.h>
#include <sys/uio.h>
#include "compress.h"
#include "hashmap.h"
#include "journal-def.h"
+#include "missing_fcntl.h"
#include "mmap-cache.h"
#include "sparse-endian.h"
#include "time-util.h"
static inline bool journal_file_writable(JournalFile *f) {
assert(f);
- return (f->open_flags & O_ACCMODE) != O_RDONLY;
+ return (f->open_flags & O_ACCMODE_STRICT) != O_RDONLY;
}
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
return -errno;
- if ((flags & O_ACCMODE) != O_RDWR)
+ if ((flags & O_ACCMODE_STRICT) != O_RDWR)
return -EACCES;
if (FLAGS_SET(flags, O_PATH))
return -ENOTTY;
if (fl < 0)
return log_debug_errno(fl, "Image file descriptor has unsafe flags set: %m");
- switch (fl & O_ACCMODE) {
+ switch (fl & O_ACCMODE_STRICT) {
case O_RDONLY:
p->read_only = true;
assert(fd >= 0);
assert(name);
- assert(IN_SET(mode & O_ACCMODE, O_RDONLY, O_RDWR));
+ assert(IN_SET(mode & O_ACCMODE_STRICT, O_RDONLY, O_RDWR));
assert((mode & ~(O_RDONLY|O_RDWR|O_CLOEXEC)) == 0);
if (fstat(fd, &st) < 0)
return -errno;
- ro = (mode & O_ACCMODE) == O_RDONLY;
+ ro = (mode & O_ACCMODE_STRICT) == O_RDONLY;
exec = st.st_mode & 0111;
mfd = memfd_create_wrapper(name,
-EIDRM)) /* File has been deleted */
return r;
- if ((open_flags & O_ACCMODE) == O_RDONLY)
+ if ((open_flags & O_ACCMODE_STRICT) == O_RDONLY)
return r;
if (!(open_flags & O_CREAT))
/* The file is corrupted. Try opening it read-only as the template before rotating to inherit its
* sequence number and ID. */
r = journal_file_open(-EBADF, fname,
- (open_flags & ~(O_ACCMODE|O_CREAT|O_EXCL)) | O_RDONLY,
+ (open_flags & ~(O_ACCMODE_STRICT|O_CREAT|O_EXCL)) | O_RDONLY,
file_flags, 0, compress_threshold_bytes, NULL,
mmap_cache, /* template = */ NULL, &old_file);
if (r < 0)
.block_size = sector_size,
.info = {
/* Use the specified flags, but configure the read-only flag from the open flags, and force autoclear */
- .lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((open_flags & O_ACCMODE) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR,
+ .lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((open_flags & O_ACCMODE_STRICT) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR,
.lo_offset = offset,
.lo_sizelimit = size == UINT64_MAX ? 0 : size,
},
zero(buf);
assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
ASSERT_STREQ(buf, TEST_TEXT);
- assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDONLY);
+ assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE_STRICT) == O_RDONLY);
assert_se(FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
f = safe_fclose(f);
zero(buf);
assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
ASSERT_STREQ(buf, TEST_TEXT);
- assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDONLY);
+ assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE_STRICT) == O_RDONLY);
assert_se(!FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
f = safe_fclose(f);
zero(buf);
assert_se(fread(buf, 1, sizeof(buf), f) == strlen(TEST_TEXT));
ASSERT_STREQ(buf, TEST_TEXT);
- assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE) == O_RDWR);
+ assert_se((fcntl(fileno(f), F_GETFL) & O_ACCMODE_STRICT) == O_RDWR);
assert_se(FLAGS_SET(fcntl(fileno(f), F_GETFD), FD_CLOEXEC));
f = safe_fclose(f);
}