merge: branch 'ff/sync_systemd'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1923
This commit is contained in:
Fernando Fernandez Mancera 2024-04-22 11:00:28 +02:00
commit 5e043966a1
35 changed files with 509 additions and 319 deletions

View file

@ -223,7 +223,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
/* Only operate on sysfs, i.e. refuse going down into /sys/fs/cgroup/ or similar places where
* things are not arranged as kobjects in kernel, and hence don't necessarily have
* kobject/attribute structure. */
r = getenv_bool_secure("SYSTEMD_DEVICE_VERIFY_SYSFS");
r = secure_getenv_bool("SYSTEMD_DEVICE_VERIFY_SYSFS");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_DEVICE_VERIFY_SYSFS value: %m");
if (r != 0) {

View file

@ -1579,7 +1579,7 @@ static int child_exit_callback(sd_event_source *s, const siginfo_t *si, void *us
static bool shall_use_pidfd(void) {
/* Mostly relevant for debugging, i.e. this is used in test-event.c to test the event loop once with and once without pidfd */
return getenv_bool_secure("SYSTEMD_PIDFD") != 0;
return secure_getenv_bool("SYSTEMD_PIDFD") != 0;
}
_public_ int sd_event_add_child(
@ -4923,13 +4923,13 @@ _public_ int sd_event_get_state(sd_event *e) {
_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(code, -EINVAL);
assert_return(!event_origin_changed(e), -ECHILD);
if (!e->exit_requested)
return -ENODATA;
*code = e->exit_code;
if (code)
*code = e->exit_code;
return 0;
}

View file

@ -143,7 +143,7 @@ int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return fd;
@ -190,7 +190,7 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
fd = xopenat_full(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
if (fd < 0)
return fd;

View file

@ -3,7 +3,6 @@
#define foosddhcpduidhfoo
/***
Copyright © 2013 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or

View file

@ -26,35 +26,14 @@
#include <sys/types.h>
#include "sd-event.h"
#include "sd-ndisc-protocol.h"
#include "sd-ndisc-router.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
/* Neighbor Discovery Options, RFC 4861, Section 4.6 and
* https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
enum {
SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1,
SD_NDISC_OPTION_TARGET_LL_ADDRESS = 2,
SD_NDISC_OPTION_PREFIX_INFORMATION = 3,
SD_NDISC_OPTION_MTU = 5,
SD_NDISC_OPTION_ROUTE_INFORMATION = 24,
SD_NDISC_OPTION_RDNSS = 25,
SD_NDISC_OPTION_FLAGS_EXTENSION = 26,
SD_NDISC_OPTION_DNSSL = 31,
SD_NDISC_OPTION_CAPTIVE_PORTAL = 37,
SD_NDISC_OPTION_PREF64 = 38
};
/* Route preference, RFC 4191, Section 2.1 */
enum {
SD_NDISC_PREFERENCE_LOW = 3U,
SD_NDISC_PREFERENCE_MEDIUM = 0U,
SD_NDISC_PREFERENCE_HIGH = 1U
};
typedef struct sd_ndisc sd_ndisc;
typedef struct sd_ndisc_router sd_ndisc_router;
__extension__ typedef enum sd_ndisc_event_t {
SD_NDISC_EVENT_TIMEOUT,
@ -64,14 +43,16 @@ __extension__ typedef enum sd_ndisc_event_t {
_SD_ENUM_FORCE_S64(NDISC_EVENT)
} sd_ndisc_event_t;
typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata);
typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata);
int sd_ndisc_new(sd_ndisc **ret);
sd_ndisc *sd_ndisc_ref(sd_ndisc *nd);
sd_ndisc *sd_ndisc_unref(sd_ndisc *nd);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
int sd_ndisc_start(sd_ndisc *nd);
int sd_ndisc_stop(sd_ndisc *nd);
int sd_ndisc_is_running(sd_ndisc *nd);
int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority);
int sd_ndisc_detach_event(sd_ndisc *nd);
@ -81,69 +62,9 @@ int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata);
int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret);
int sd_ndisc_set_link_local_address(sd_ndisc *nd, const struct in6_addr *addr);
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret);
/* Generic option access */
int sd_ndisc_router_option_rewind(sd_ndisc_router *rt);
int sd_ndisc_router_option_next(sd_ndisc_router *rt);
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type);
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix_get_valid_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_route_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_RDNSS */
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_rdnss_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
/* Specific option access: SD_NDISC_OPTION_DNSSL */
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret);
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_dnssl_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
/* Specific option access: SD_NDISC_OPTION_CAPTIVE_PORTAL */
int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size);
/* Specific option access: SD_NDISC_OPTION_PREF64 */
int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix64_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
_SD_END_DECLARATIONS;
#endif

View file

@ -312,19 +312,17 @@ char **strv_env_delete(char **x, size_t n_lists, ...) {
return TAKE_PTR(t);
}
char **strv_env_unset(char **l, const char *p) {
char **f, **t;
char** strv_env_unset(char **l, const char *p) {
assert(p);
if (!l)
return NULL;
assert(p);
/* Drops every occurrence of the env var setting p in the
* string list. Edits in-place. */
char **f, **t;
for (f = t = l; *f; f++) {
if (env_match(*f, p)) {
free(*f);
continue;
@ -337,14 +335,13 @@ char **strv_env_unset(char **l, const char *p) {
return l;
}
char **strv_env_unset_many(char **l, ...) {
char **f, **t;
char** strv_env_unset_many_internal(char **l, ...) {
if (!l)
return NULL;
/* Like strv_env_unset() but applies many at once. Edits in-place. */
char **f, **t;
for (f = t = l; *f; f++) {
bool found = false;
const char *p;
@ -352,12 +349,11 @@ char **strv_env_unset_many(char **l, ...) {
va_start(ap, l);
while ((p = va_arg(ap, const char*))) {
while ((p = va_arg(ap, const char*)))
if (env_match(*f, p)) {
found = true;
break;
}
}
va_end(ap);
@ -972,7 +968,7 @@ int getenv_bool(const char *p) {
return parse_boolean(e);
}
int getenv_bool_secure(const char *p) {
int secure_getenv_bool(const char *p) {
const char *e;
e = secure_getenv(p);
@ -982,8 +978,7 @@ int getenv_bool_secure(const char *p) {
return parse_boolean(e);
}
#if 0 /* NM_IGNORED */
int getenv_uint64_secure(const char *p, uint64_t *ret) {
int secure_getenv_uint64(const char *p, uint64_t *ret) {
const char *e;
assert(p);
@ -1017,6 +1012,7 @@ int putenv_dup(const char *assignment, bool override) {
return RET_NERRNO(setenv(n, e + 1, override));
}
#if 0 /* NM_IGNORED */
int setenv_systemd_exec_pid(bool update_only) {
const char *e;
int r;
@ -1037,6 +1033,17 @@ int setenv_systemd_exec_pid(bool update_only) {
return 1;
}
int setenv_systemd_log_level(void) {
_cleanup_free_ char *val = NULL;
int r;
r = log_level_to_string_alloc(log_get_max_level(), &val);
if (r < 0)
return r;
return RET_NERRNO(setenv("SYSTEMD_LOG_LEVEL", val, /* overwrite= */ true));
}
int getenv_path_list(const char *name, char ***ret_paths) {
_cleanup_strv_free_ char **l = NULL;
const char *e;

View file

@ -43,8 +43,9 @@ char** _strv_env_merge(char **first, ...);
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
char** strv_env_unset(char **l, const char *p); /* In place ... */
char** strv_env_unset_many_internal(char **l, ...) _sentinel_;
#define strv_env_unset_many(l, ...) strv_env_unset_many_internal(l, __VA_ARGS__, NULL)
int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
@ -61,9 +62,9 @@ static inline char* strv_env_get(char * const *x, const char *n) {
char *strv_env_pairs_get(char **l, const char *name) _pure_;
int getenv_bool(const char *p);
int getenv_bool_secure(const char *p);
int secure_getenv_bool(const char *p);
int getenv_uint64_secure(const char *p, uint64_t *ret);
int secure_getenv_uint64(const char *p, uint64_t *ret);
/* Like setenv, but calls unsetenv if value == NULL. */
int set_unset_env(const char *name, const char *value, bool overwrite);
@ -72,6 +73,7 @@ int set_unset_env(const char *name, const char *value, bool overwrite);
int putenv_dup(const char *assignment, bool override);
int setenv_systemd_exec_pid(bool update_only);
int setenv_systemd_log_level(void);
/* Parses and does sanity checks on an environment variable containing
* PATH-like colon-separated absolute paths */

View file

@ -247,52 +247,43 @@ int extract_first_word_and_warn(
* Let's make sure that ExtractFlags fits into an unsigned int. */
assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned));
int extract_many_words(const char **p, const char *separators, unsigned flags, ...) {
int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
unsigned n = 0;
int r;
/* Parses a number of words from a string, stripping any
* quotes if necessary. */
/* Parses a number of words from a string, stripping any quotes if necessary. */
assert(p);
/* Count how many words are expected */
va_start(ap, flags);
for (;;) {
if (!va_arg(ap, char **))
break;
while (va_arg(ap, char**))
n++;
}
va_end(ap);
if (n <= 0)
if (n == 0)
return 0;
/* Read all words into a temporary array */
l = newa0(char*, n);
for (c = 0; c < n; c++) {
char **l = newa0(char*, n);
unsigned c;
for (c = 0; c < n; c++) {
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
free_many_charp(l, c);
return r;
}
if (r == 0)
break;
}
/* If we managed to parse all words, return them in the passed
* in parameters */
/* If we managed to parse all words, return them in the passed in parameters */
va_start(ap, flags);
for (i = 0; i < n; i++) {
char **v;
v = va_arg(ap, char **);
assert(v);
*v = l[i];
FOREACH_ARRAY(i, l, n) {
char **v = ASSERT_PTR(va_arg(ap, char**));
*v = *i;
}
va_end(ap);

View file

@ -19,4 +19,7 @@ typedef enum ExtractFlags {
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_;
int extract_many_words_internal(const char **p, const char *separators, unsigned flags, ...) _sentinel_;
#define extract_many_words(p, separators, flags, ...) \
extract_many_words_internal(p, separators, flags, ##__VA_ARGS__, NULL)

View file

@ -311,7 +311,7 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
case 0:
/* Close everything. Yay! */
if (close_range(3, -1, 0) >= 0)
if (close_range(3, INT_MAX, 0) >= 0)
return 1;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
@ -422,7 +422,7 @@ int close_all_fds(const int except[], size_t n_except) {
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
return 0;
if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) >= 0)
return 0;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
@ -467,6 +467,53 @@ int close_all_fds(const int except[], size_t n_except) {
return r;
}
int pack_fds(int fds[], size_t n_fds) {
if (n_fds <= 0)
return 0;
/* Shifts around the fds in the provided array such that they
* all end up packed next to each-other, in order, starting
* from SD_LISTEN_FDS_START. This must be called after close_all_fds();
* it is likely to freeze up otherwise. You should probably use safe_fork_full
* with FORK_CLOSE_ALL_FDS|FORK_PACK_FDS set, to ensure that this is done correctly.
* The fds array is modified in place with the new FD numbers. */
assert(fds);
for (int start = 0;;) {
int restart_from = -1;
for (int i = start; i < (int) n_fds; i++) {
int nfd;
/* Already at right index? */
if (fds[i] == i + 3)
continue;
nfd = fcntl(fds[i], F_DUPFD, i + 3);
if (nfd < 0)
return -errno;
safe_close(fds[i]);
fds[i] = nfd;
/* Hmm, the fd we wanted isn't free? Then
* let's remember that and try again from here */
if (nfd != i + 3 && restart_from < 0)
restart_from = i;
}
if (restart_from < 0)
break;
start = restart_from;
}
assert(fds[0] == 3);
return 0;
}
int same_fd(int a, int b) {
struct stat sta, stb;
pid_t pid;
@ -875,6 +922,38 @@ int fd_is_opath(int fd) {
return FLAGS_SET(r, O_PATH);
}
int fd_verify_safe_flags(int fd) {
int flags, unexpected_flags;
/* Check if an extrinsic fd is safe to work on (by a privileged service). This ensures that clients
* can't trick a privileged service into giving access to a file the client doesn't already have
* access to (especially via something like O_PATH).
*
* O_NOFOLLOW: For some reason the kernel will return this flag from fcntl; it doesn't go away
* immediately after open(). It should have no effect whatsoever to an already-opened FD,
* and since we refuse O_PATH it should be safe.
*
* RAW_O_LARGEFILE: glibc secretly sets this and neglects to hide it from us if we call fcntl.
* See comment in missing_fcntl.h for more details about this.
*
* O_DIRECTORY: this is set for directories, which are totally fine
*/
assert(fd >= 0);
flags = fcntl(fd, F_GETFL);
if (flags < 0)
return -errno;
unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|O_DIRECTORY);
if (unexpected_flags != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
"Unexpected flags set for extrinsic fd: 0%o",
(unsigned) unexpected_flags);
return 0;
}
int read_nr_open(void) {
_cleanup_free_ char *nr_open = NULL;
int r;

View file

@ -8,6 +8,7 @@
#include <sys/socket.h>
#include "macro.h"
#include "missing_fcntl.h"
#include "stdio-util.h"
/* maximum length of fdname */
@ -77,6 +78,8 @@ int get_max_fd(void);
int close_all_fds(const int except[], size_t n_except);
int close_all_fds_without_malloc(const int except[], size_t n_except);
int pack_fds(int fds[], size_t n);
int same_fd(int a, int b);
void cmsg_close_all(struct msghdr *mh);
@ -109,7 +112,10 @@ static inline int make_null_stdio(void) {
int fd_reopen(int fd, int flags);
int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd);
int fd_is_opath(int fd);
int fd_verify_safe_flags(int fd);
int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);

View file

@ -18,6 +18,14 @@ assert_cc(sizeof(uid_t) == sizeof(uint32_t));
assert_cc(sizeof(gid_t) == sizeof(uint32_t));
#define GID_FMT "%" PRIu32
/* Note: the lifetime of the compound literal is the immediately surrounding block,
* see C11 §6.5.2.5, and
* https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
#define FORMAT_UID(uid) \
snprintf_ok((char[DECIMAL_STR_MAX(uid_t)]){}, DECIMAL_STR_MAX(uid_t), UID_FMT, uid)
#define FORMAT_GID(gid) \
snprintf_ok((char[DECIMAL_STR_MAX(gid_t)]){}, DECIMAL_STR_MAX(gid_t), GID_FMT, gid)
#if SIZEOF_TIME_T == 8
# define PRI_TIME PRIi64
#elif SIZEOF_TIME_T == 4

View file

@ -120,7 +120,11 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
int readlinkat_malloc(int fd, const char *p, char **ret) {
size_t l = PATH_MAX;
assert(p);
assert(fd >= 0 || fd == AT_FDCWD);
if (fd < 0 && isempty(p))
return -EISDIR; /* In this case, the fd points to the current working directory, and is
* definitely not a symlink. Let's return earlier. */
for (;;) {
_cleanup_free_ char *c = NULL;
@ -130,7 +134,7 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
if (!c)
return -ENOMEM;
n = readlinkat(fd, p, c, l);
n = readlinkat(fd, strempty(p), c, l);
if (n < 0)
return -errno;
@ -1060,7 +1064,7 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
path = fname;
}
fd = xopenat(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
fd = xopenat_full(dirfd, path, flags|O_CREAT|O_DIRECTORY|O_NOFOLLOW, /* xopen_flags = */ 0, mode);
if (IN_SET(fd, -ELOOP, -ENOTDIR))
return -EEXIST;
if (fd < 0)
@ -1116,7 +1120,7 @@ int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, b
}
}
int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode) {
_cleanup_close_ int fd = -EBADF;
bool made = false;
int r;
@ -1198,7 +1202,7 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
}
#if 0 /* NM_IGNORED */
int xopenat_lock(
int xopenat_lock_full(
int dir_fd,
const char *path,
int open_flags,
@ -1221,7 +1225,7 @@ int xopenat_lock(
for (;;) {
struct stat st;
fd = xopenat(dir_fd, path, open_flags, xopen_flags, mode);
fd = xopenat_full(dir_fd, path, open_flags, xopen_flags, mode);
if (fd < 0)
return fd;

View file

@ -137,6 +137,12 @@ typedef enum XOpenFlags {
XO_SUBVOLUME = 1 << 1,
} XOpenFlags;
int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);
static inline int xopenat(int dir_fd, const char *path, int open_flags) {
return xopenat_full(dir_fd, path, open_flags, 0, 0);
}
int xopenat_lock(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode, LockType locktype, int operation);
int xopenat_lock_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode, LockType locktype, int operation);
static inline int xopenat_lock(int dir_fd, const char *path, int open_flags, LockType locktype, int operation) {
return xopenat_lock_full(dir_fd, path, open_flags, 0, 0, locktype, operation);
}

View file

@ -43,6 +43,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_TREE_SPACE] = " ",
[SPECIAL_GLYPH_TREE_TOP] = ",-",
[SPECIAL_GLYPH_VERTICAL_DOTTED] = ":",
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = "-",
[SPECIAL_GLYPH_HORIZONTAL_FAT] = "=",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
[SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
[SPECIAL_GLYPH_WHITE_CIRCLE] = "*",
@ -76,6 +78,10 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_SPARKLES] = "*",
[SPECIAL_GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
[SPECIAL_GLYPH_RED_CIRCLE] = "o",
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
},
/* UTF-8 */
@ -89,6 +95,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* Single glyphs in both cases */
[SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"",
[SPECIAL_GLYPH_HORIZONTAL_DOTTED] = u8"",
[SPECIAL_GLYPH_HORIZONTAL_FAT] = u8"",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"",
[SPECIAL_GLYPH_BLACK_CIRCLE] = u8"",
[SPECIAL_GLYPH_WHITE_CIRCLE] = u8"",
@ -138,6 +146,11 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
[SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
[SPECIAL_GLYPH_WORLD] = u8"🌍",
[SPECIAL_GLYPH_RED_CIRCLE] = u8"🔴",
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
},
};

View file

@ -13,6 +13,8 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_TREE_SPACE,
SPECIAL_GLYPH_TREE_TOP,
SPECIAL_GLYPH_VERTICAL_DOTTED,
SPECIAL_GLYPH_HORIZONTAL_DOTTED,
SPECIAL_GLYPH_HORIZONTAL_FAT,
SPECIAL_GLYPH_TRIANGULAR_BULLET,
SPECIAL_GLYPH_BLACK_CIRCLE,
SPECIAL_GLYPH_WHITE_CIRCLE,
@ -49,6 +51,10 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_WARNING_SIGN,
SPECIAL_GLYPH_COMPUTER_DISK,
SPECIAL_GLYPH_WORLD,
SPECIAL_GLYPH_RED_CIRCLE,
SPECIAL_GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;

View file

@ -2126,24 +2126,27 @@ static int hashmap_entry_compare(
return compare((*a)->key, (*b)->key);
}
int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
_cleanup_free_ struct hashmap_base_entry **entries = NULL;
static int _hashmap_dump_entries_sorted(
HashmapBase *h,
void ***ret,
size_t *ret_n) {
_cleanup_free_ void **entries = NULL;
Iterator iter;
unsigned idx;
size_t n = 0;
assert(ret);
assert(ret_n);
if (_hashmap_size(h) == 0) {
*ret = NULL;
if (ret_n)
*ret_n = 0;
*ret_n = 0;
return 0;
}
/* We append one more element than needed so that the resulting array can be used as a strv. We
* don't count this entry in the returned size. */
entries = new(struct hashmap_base_entry*, _hashmap_size(h) + 1);
entries = new(void*, _hashmap_size(h) + 1);
if (!entries)
return -ENOMEM;
@ -2153,13 +2156,47 @@ int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
assert(n == _hashmap_size(h));
entries[n] = NULL;
typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);
typesafe_qsort_r((struct hashmap_base_entry**) entries, n,
hashmap_entry_compare, h->hash_ops->compare);
*ret = TAKE_PTR(entries);
*ret_n = n;
return 0;
}
int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
_cleanup_free_ void **entries = NULL;
size_t n;
int r;
r = _hashmap_dump_entries_sorted(h, &entries, &n);
if (r < 0)
return r;
/* Reuse the array. */
FOREACH_ARRAY(e, entries, n)
*e = entry_value(h, *e);
*e = (void*) (*(struct hashmap_base_entry**) e)->key;
*ret = (void**) TAKE_PTR(entries);
*ret = TAKE_PTR(entries);
if (ret_n)
*ret_n = n;
return 0;
}
int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
_cleanup_free_ void **entries = NULL;
size_t n;
int r;
r = _hashmap_dump_entries_sorted(h, &entries, &n);
if (r < 0)
return r;
/* Reuse the array. */
FOREACH_ARRAY(e, entries, n)
*e = entry_value(h, *(struct hashmap_base_entry**) e);
*ret = TAKE_PTR(entries);
if (ret_n)
*ret_n = n;
return 0;

View file

@ -39,8 +39,8 @@ typedef struct IteratedCache IteratedCache; /* Caches the iterated order of on
* by hashmap users, so the definition has to be here. Do not use its fields
* directly. */
typedef struct {
unsigned idx; /* index of an entry to be iterated next */
const void *next_key; /* expected value of that entry's key pointer */
unsigned idx; /* index of an entry to be iterated next */
#if ENABLE_DEBUG_HASHMAP
unsigned put_count; /* hashmap's put_count recorded at start of iteration */
unsigned rem_count; /* hashmap's rem_count in previous iteration */
@ -409,6 +409,14 @@ static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
int _hashmap_dump_keys_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
static inline int hashmap_dump_keys_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_keys_sorted(HASHMAP_BASE(h), ret, ret_n);
}
static inline int ordered_hashmap_dump_keys_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_keys_sorted(HASHMAP_BASE(h), ret, ret_n);
}
/*
* Hashmaps are iterated in unpredictable order.
* OrderedHashmaps are an exception to this. They are iterated in the order

View file

@ -93,14 +93,26 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) {
be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001);
}
bool in4_addr_is_multicast(const struct in_addr *a) {
assert(a);
return IN_MULTICAST(be32toh(a->s_addr));
}
bool in6_addr_is_multicast(const struct in6_addr *a) {
assert(a);
return IN6_IS_ADDR_MULTICAST(a);
}
int in_addr_is_multicast(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
return IN_MULTICAST(be32toh(u->in.s_addr));
return in4_addr_is_multicast(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_MULTICAST(&u->in6);
return in6_addr_is_multicast(&u->in6);
return -EAFNOSUPPORT;
}

View file

@ -40,6 +40,8 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) {
return in_addr_data_is_null(a);
}
bool in4_addr_is_multicast(const struct in_addr *a);
bool in6_addr_is_multicast(const struct in6_addr *a);
int in_addr_is_multicast(int family, const union in_addr_union *u);
bool in4_addr_is_link_local(const struct in_addr *a);

View file

@ -263,7 +263,10 @@ bool locale_is_valid(const char *name) {
if (!filename_is_valid(name))
return false;
if (!string_is_safe(name))
/* Locales look like: ll_CC.ENC@variant, where ll and CC are alphabetic, ENC is alphanumeric with
* dashes, and variant seems to be alphabetic.
* See: https://www.gnu.org/software/gettext/manual/html_node/Locale-Names.html */
if (!in_charset(name, ALPHANUMERICAL "_.-@"))
return false;
return true;
@ -296,7 +299,7 @@ bool is_locale_utf8(void) {
if (cached_answer >= 0)
goto out;
r = getenv_bool_secure("SYSTEMD_UTF8");
r = secure_getenv_bool("SYSTEMD_UTF8");
if (r >= 0) {
cached_answer = r;
goto out;

View file

@ -524,7 +524,7 @@ typedef struct LogRateLimit {
RateLimit ratelimit;
} LogRateLimit;
#define log_ratelimit_internal(_level, _error, _ratelimit, _format, _file, _line, _func, ...) \
#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...) \
({ \
int _log_ratelimit_error = (_error); \
int _log_ratelimit_level = (_level); \
@ -548,7 +548,7 @@ typedef struct LogRateLimit {
({ \
int _level = (level), _e = (error); \
_e = (log_get_max_level() >= LOG_PRI(_level)) \
? log_ratelimit_internal(_level, _e, _ratelimit, format, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__) \
? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
: -ERRNO_VALUE(_e); \
_e < 0 ? _e : -ESTRPIPE; \
})

View file

@ -69,9 +69,26 @@
/* So O_LARGEFILE is generally implied by glibc, and defined to zero hence, because we only build in LFS
* mode. However, when invoking fcntl(F_GETFL) the flag is ORed into the result anyway glibc does not mask
* it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves. */
* it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves.
*
* The precise definition is arch specific, so we use the values defined in the kernel (note that some
* are hexa and others are octal; duplicated as-is from the kernel definitions):
* - alpha, arm, arm64, m68k, mips, parisc, powerpc, sparc: each has a specific value;
* - others: they use the "generic" value (defined in include/uapi/asm-generic/fcntl.h) */
#if O_LARGEFILE != 0
#define RAW_O_LARGEFILE O_LARGEFILE
#else
#define RAW_O_LARGEFILE 0100000
#if defined(__alpha__) || defined(__arm__) || defined(__aarch64__) || defined(__m68k__)
#define RAW_O_LARGEFILE 0400000
#elif defined(__mips__)
#define RAW_O_LARGEFILE 0x2000
#elif defined(__parisc__) || defined(__hppa__)
#define RAW_O_LARGEFILE 000004000
#elif defined(__powerpc__)
#define RAW_O_LARGEFILE 0200000
#elif defined(__sparc__)
#define RAW_O_LARGEFILE 0x40000
#else
#define RAW_O_LARGEFILE 00100000
#endif
#endif

View file

@ -419,23 +419,14 @@ static inline int missing_execveat(int dirfd, const char *pathname,
/* ======================================================================= */
#if !HAVE_CLOSE_RANGE
static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) {
static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsigned flags) {
# ifdef __NR_close_range
/* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while
* userspace exclusively uses signed integers for fds. We don't know just yet how glibc is going to
* wrap this syscall, but let's assume it's going to be similar to what they do for close(),
* i.e. make the same unsigned signed type change from the raw kernel syscall compared to the
* userspace wrapper. There's only one caveat for this: unlike for close() there's the special
* UINT_MAX fd value for the 'end_fd' argument. Let's safely map that to -1 here. And let's refuse
* any other negative values. */
if ((first_fd < 0) || (end_fd < 0 && end_fd != -1)) {
errno = -EBADF;
return -1;
}
* userspace exclusively uses signed integers for fds. glibc chose to expose it 1:1 however, hence we
* do so here too, even if we end up passing signed fds to it most of the time. */
return syscall(__NR_close_range,
(unsigned) first_fd,
end_fd == -1 ? UINT_MAX : (unsigned) end_fd, /* Of course, the compiler should figure out that this is the identity mapping IRL */
first_fd,
end_fd,
flags);
# else
errno = ENOSYS;

View file

@ -53,3 +53,5 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
int userns_acquire(const char *uid_map, const char *gid_map);
int netns_acquire(void);
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);

View file

@ -514,7 +514,7 @@ static int get_process_link_contents(pid_t pid, const char *proc_file, char **re
p = procfs_file_alloca(pid, proc_file);
r = readlink_malloc(p, ret);
return r == -ENOENT ? -ESRCH : r;
return (r == -ENOENT && proc_mounted() > 0) ? -ESRCH : r;
}
int get_process_exe(pid_t pid, char **ret) {
@ -1309,7 +1309,7 @@ int opinionated_personality(unsigned long *ret) {
if (current < 0)
return current;
if (((unsigned long) current & 0xffff) == PER_LINUX32)
if (((unsigned long) current & OPINIONATED_PERSONALITY_MASK) == PER_LINUX32)
*ret = PER_LINUX32;
else
*ret = PER_LINUX;
@ -1476,7 +1476,7 @@ static int fork_flags_to_signal(ForkFlags flags) {
int safe_fork_full(
const char *name,
const int stdio_fds[3],
const int except_fds[],
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
pid_t *ret_pid) {
@ -1705,6 +1705,19 @@ int safe_fork_full(
}
}
if (flags & FORK_PACK_FDS) {
/* FORK_CLOSE_ALL_FDS ensures that except_fds are the only FDs >= 3 that are
* open, this is including the log. This is required by pack_fds, which will
* get stuck in an infinite loop of any FDs other than except_fds are open. */
assert(FLAGS_SET(flags, FORK_CLOSE_ALL_FDS));
r = pack_fds(except_fds, n_except_fds);
if (r < 0) {
log_full_errno(prio, r, "Failed to pack file descriptors: %m");
_exit(EXIT_FAILURE);
}
}
if (flags & FORK_CLOEXEC_OFF) {
r = fd_cloexec_many(except_fds, n_except_fds, false);
if (r < 0) {
@ -1744,7 +1757,7 @@ int safe_fork_full(
int pidref_safe_fork_full(
const char *name,
const int stdio_fds[3],
const int except_fds[],
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
PidRef *ret_pid) {
@ -1768,7 +1781,7 @@ int pidref_safe_fork_full(
int namespace_fork(
const char *outer_name,
const char *inner_name,
const int except_fds[],
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
int pidns_fd,

View file

@ -101,12 +101,17 @@ bool is_main_thread(void);
bool oom_score_adjust_is_valid(int oa);
#ifndef PERSONALITY_INVALID
/* personality(7) documents that 0xffffffffUL is used for querying the
/* personality(2) documents that 0xFFFFFFFFUL is used for querying the
* current personality, hence let's use that here as error
* indicator. */
#define PERSONALITY_INVALID 0xffffffffLU
#define PERSONALITY_INVALID 0xFFFFFFFFUL
#endif
/* The personality() syscall returns a 32-bit value where the top three bytes are reserved for flags that
* emulate historical or architectural quirks, and only the least significant byte reflects the actual
* personality we're interested in. */
#define OPINIONATED_PERSONALITY_MASK 0xFFUL
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
@ -182,12 +187,13 @@ typedef enum ForkFlags {
FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_PACK_FDS = 1 << 20, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */
} ForkFlags;
int safe_fork_full(
const char *name,
const int stdio_fds[3],
const int except_fds[],
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
pid_t *ret_pid);
@ -199,7 +205,7 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
int pidref_safe_fork_full(
const char *name,
const int stdio_fds[3],
const int except_fds[],
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
PidRef *ret_pid);
@ -208,7 +214,18 @@ static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *re
return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
}
int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int namespace_fork(
const char *outer_name,
const char *inner_name,
int except_fds[],
size_t n_except_fds,
ForkFlags flags,
int pidns_fd,
int mntns_fd,
int netns_fd,
int userns_fd,
int root_fd,
pid_t *ret_pid);
int set_oom_score_adjust(int value);
int get_oom_score_adjust(int *ret);

View file

@ -21,7 +21,7 @@ int reset_all_signal_handlers(void) {
.sa_handler = SIG_DFL,
.sa_flags = SA_RESTART,
};
int r = 0;
int ret = 0, r;
for (int sig = 1; sig < _NSIG; sig++) {
@ -29,14 +29,14 @@ int reset_all_signal_handlers(void) {
if (IN_SET(sig, SIGKILL, SIGSTOP))
continue;
/* On Linux the first two RT signals are reserved by
* glibc, and sigaction() will return EINVAL for them. */
if (sigaction(sig, &sa, NULL) < 0)
if (errno != EINVAL && r >= 0)
r = -errno;
/* On Linux the first two RT signals are reserved by glibc, and sigaction() will return
* EINVAL for them. */
r = RET_NERRNO(sigaction(sig, &sa, NULL));
if (r != -EINVAL)
RET_GATHER(ret, r);
}
return r;
return ret;
}
int reset_signal_mask(void) {
@ -60,10 +60,7 @@ int sigaction_many_internal(const struct sigaction *sa, ...) {
if (sig == 0)
continue;
if (sigaction(sig, sa, NULL) < 0) {
if (r >= 0)
r = -errno;
}
RET_GATHER(r, RET_NERRNO(sigaction(sig, sa, NULL)));
}
va_end(ap);
@ -90,7 +87,7 @@ static int sigset_add_many_ap(sigset_t *ss, va_list ap) {
return r;
}
int sigset_add_many(sigset_t *ss, ...) {
int sigset_add_many_internal(sigset_t *ss, ...) {
va_list ap;
int r;
@ -101,7 +98,7 @@ int sigset_add_many(sigset_t *ss, ...) {
return r;
}
int sigprocmask_many(int how, sigset_t *old, ...) {
int sigprocmask_many_internal(int how, sigset_t *old, ...) {
va_list ap;
sigset_t ss;
int r;
@ -116,10 +113,7 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
if (r < 0)
return r;
if (sigprocmask(how, &ss, old) < 0)
return -errno;
return 0;
return RET_NERRNO(sigprocmask(how, &ss, old));
}
static const char *const static_signal_table[] = {

View file

@ -31,8 +31,11 @@ int sigaction_many_internal(const struct sigaction *sa, ...);
#define sigaction_many(sa, ...) \
sigaction_many_internal(sa, __VA_ARGS__, -1)
int sigset_add_many(sigset_t *ss, ...);
int sigprocmask_many(int how, sigset_t *old, ...);
int sigset_add_many_internal(sigset_t *ss, ...);
#define sigset_add_many(...) sigset_add_many_internal(__VA_ARGS__, -1)
int sigprocmask_many_internal(int how, sigset_t *old, ...);
#define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1)
const char *signal_to_string(int i) _const_;
int signal_from_string(const char *s) _pure_;
@ -46,7 +49,7 @@ static inline void block_signals_reset(sigset_t *ss) {
#define BLOCK_SIGNALS(...) \
_cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \
sigset_t _t; \
assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \
assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__) >= 0); \
_t; \
})

View file

@ -27,46 +27,130 @@
#include "stat-util.h"
#include "string-util.h"
#if 0 /* NM_IGNORED */
int is_symlink(const char *path) {
struct stat info;
assert(path);
if (lstat(path, &info) < 0)
return -errno;
return !!S_ISLNK(info.st_mode);
}
#endif /* NM_IGNORED */
int is_dir_full(int atfd, const char* path, bool follow) {
static int verify_stat_at(
int fd,
const char *path,
bool follow,
int (*verify_func)(const struct stat *st),
bool verify) {
struct stat st;
int r;
assert(atfd >= 0 || atfd == AT_FDCWD);
assert(atfd >= 0 || path);
assert(fd >= 0 || fd == AT_FDCWD);
assert(!isempty(path) || !follow);
assert(verify_func);
if (path)
r = fstatat(atfd, path, &st, follow ? 0 : AT_SYMLINK_NOFOLLOW);
else
r = fstat(atfd, &st);
if (r < 0)
if (fstatat(fd, strempty(path), &st,
(isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
return -errno;
return !!S_ISDIR(st.st_mode);
r = verify_func(&st);
return verify ? r : r >= 0;
}
int stat_verify_regular(const struct stat *st) {
assert(st);
/* Checks whether the specified stat() structure refers to a regular file. If not returns an
* appropriate error code. */
if (S_ISDIR(st->st_mode))
return -EISDIR;
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (!S_ISREG(st->st_mode))
return -EBADFD;
return 0;
}
int verify_regular_at(int fd, const char *path, bool follow) {
return verify_stat_at(fd, path, follow, stat_verify_regular, true);
}
int fd_verify_regular(int fd) {
assert(fd >= 0);
return verify_regular_at(fd, NULL, false);
}
int stat_verify_directory(const struct stat *st) {
assert(st);
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (!S_ISDIR(st->st_mode))
return -ENOTDIR;
return 0;
}
int fd_verify_directory(int fd) {
assert(fd >= 0);
return verify_stat_at(fd, NULL, false, stat_verify_directory, true);
}
int is_dir_at(int fd, const char *path, bool follow) {
return verify_stat_at(fd, path, follow, stat_verify_directory, false);
}
int is_dir(const char *path, bool follow) {
assert(!isempty(path));
return is_dir_at(AT_FDCWD, path, follow);
}
int stat_verify_symlink(const struct stat *st) {
assert(st);
if (S_ISDIR(st->st_mode))
return -EISDIR;
if (!S_ISLNK(st->st_mode))
return -ENOLINK;
return 0;
}
int is_symlink(const char *path) {
assert(!isempty(path));
return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
}
int stat_verify_linked(const struct stat *st) {
assert(st);
if (st->st_nlink <= 0)
return -EIDRM; /* recognizable error. */
return 0;
}
int fd_verify_linked(int fd) {
assert(fd >= 0);
return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
}
int stat_verify_device_node(const struct stat *st) {
assert(st);
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (S_ISDIR(st->st_mode))
return -EISDIR;
if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
return -ENOTTY;
return 0;
}
#if 0 /* NM_IGNORED */
int is_device_node(const char *path) {
struct stat info;
assert(path);
if (lstat(path, &info) < 0)
return -errno;
return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
assert(!isempty(path));
return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false);
}
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
@ -268,72 +352,6 @@ int path_is_network_fs(const char *path) {
}
#endif /* NM_IGNORED */
int stat_verify_regular(const struct stat *st) {
assert(st);
/* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
* code. */
if (S_ISDIR(st->st_mode))
return -EISDIR;
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (!S_ISREG(st->st_mode))
return -EBADFD;
return 0;
}
int fd_verify_regular(int fd) {
struct stat st;
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
return stat_verify_regular(&st);
}
#if 0 /* NM_IGNORED */
int verify_regular_at(int dir_fd, const char *path, bool follow) {
struct stat st;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
if (fstatat(dir_fd, path, &st, (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
return -errno;
return stat_verify_regular(&st);
}
int stat_verify_directory(const struct stat *st) {
assert(st);
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (!S_ISDIR(st->st_mode))
return -ENOTDIR;
return 0;
}
int fd_verify_directory(int fd) {
struct stat st;
assert(fd >= 0);
if (fstat(fd, &st) < 0)
return -errno;
return stat_verify_directory(&st);
}
#endif /* NM_IGNORED */
int proc_mounted(void) {
int r;
@ -480,7 +498,7 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(ret);
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return fd;

View file

@ -14,14 +14,22 @@
#include "siphash24.h"
#include "time-util.h"
int stat_verify_regular(const struct stat *st);
int verify_regular_at(int fd, const char *path, bool follow);
int fd_verify_regular(int fd);
int stat_verify_directory(const struct stat *st);
int fd_verify_directory(int fd);
int is_dir_at(int fd, const char *path, bool follow);
int is_dir(const char *path, bool follow);
int stat_verify_symlink(const struct stat *st);
int is_symlink(const char *path);
int is_dir_full(int atfd, const char *fname, bool follow);
static inline int is_dir(const char *path, bool follow) {
return is_dir_full(AT_FDCWD, path, follow);
}
static inline int is_dir_fd(int fd) {
return is_dir_full(fd, NULL, false);
}
int stat_verify_linked(const struct stat *st);
int fd_verify_linked(int fd);
int stat_verify_device_node(const struct stat *st);
int is_device_node(const char *path);
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup);
@ -72,13 +80,6 @@ int path_is_network_fs(const char *path);
*/
#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
int stat_verify_regular(const struct stat *st);
int fd_verify_regular(int fd);
int verify_regular_at(int dir_fd, const char *path, bool follow);
int stat_verify_directory(const struct stat *st);
int fd_verify_directory(int fd);
int proc_mounted(void);
bool stat_inode_same(const struct stat *a, const struct stat *b);

View file

@ -625,6 +625,9 @@ char *cellescape(char *buf, size_t len, const char *s) {
char* strshorten(char *s, size_t l) {
assert(s);
if (l >= SIZE_MAX-1) /* Would not change anything */
return s;
if (strnlen(s, l+1) > l)
s[l] = 0;

View file

@ -363,7 +363,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
const char *p = tuple;
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
&first, &second, NULL);
&first, &second);
if (r < 0)
return r;
if (r == 0)

View file

@ -1434,7 +1434,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) {
/* Line format is:
* 'country codes' 'coordinates' 'timezone' 'comments' */
r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
r = extract_many_words(&p, NULL, 0, &cc, &co, &tz);
if (r < 0)
continue;
@ -1479,7 +1479,7 @@ static int get_timezones_from_tzdata_zi(char ***ret) {
* Link line format is:
* 'Link' 'target' 'alias'
* See 'man zic' for more detail. */
r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL);
r = extract_many_words(&p, NULL, 0, &type, &f1, &f2);
if (r < 0)
continue;
@ -1579,7 +1579,7 @@ int verify_timezone(const char *name, int log_level) {
r = fd_verify_regular(fd);
if (r < 0)
return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
r = loop_read_exact(fd, buf, 4, false);
if (r < 0)

View file

@ -264,6 +264,30 @@
})
#endif /* NM_IGNORED */
#define ADD_SAFE(ret, a, b) (!__builtin_add_overflow(a, b, ret))
#define INC_SAFE(a, b) __INC_SAFE(UNIQ, a, b)
#define __INC_SAFE(q, a, b) \
({ \
const typeof(a) UNIQ_T(A, q) = (a); \
ADD_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b); \
})
#define SUB_SAFE(ret, a, b) (!__builtin_sub_overflow(a, b, ret))
#define DEC_SAFE(a, b) __DEC_SAFE(UNIQ, a, b)
#define __DEC_SAFE(q, a, b) \
({ \
const typeof(a) UNIQ_T(A, q) = (a); \
SUB_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b); \
})
#define MUL_SAFE(ret, a, b) (!__builtin_mul_overflow(a, b, ret))
#define MUL_ASSIGN_SAFE(a, b) __MUL_ASSIGN_SAFE(UNIQ, a, b)
#define __MUL_ASSIGN_SAFE(q, a, b) \
({ \
const typeof(a) UNIQ_T(A, q) = (a); \
MUL_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b); \
})
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
({ \
@ -313,7 +337,7 @@
const typeof(y) UNIQ_T(A, q) = (y); \
const typeof(x) UNIQ_T(B, q) = DIV_ROUND_UP((x), UNIQ_T(A, q)); \
typeof(x) UNIQ_T(C, q); \
__builtin_mul_overflow(UNIQ_T(B, q), UNIQ_T(A, q), &UNIQ_T(C, q)) ? (typeof(x)) -1 : UNIQ_T(C, q); \
MUL_SAFE(&UNIQ_T(C, q), UNIQ_T(B, q), UNIQ_T(A, q)) ? UNIQ_T(C, q) : (typeof(x)) -1; \
})
#define ROUND_UP(x, y) __ROUND_UP(UNIQ, (x), (y))