systemd: merge branch systemd into master

This commit is contained in:
Thomas Haller 2018-12-23 00:46:32 +01:00
commit 9e9320cc0f
68 changed files with 2092 additions and 1509 deletions

View file

@ -1541,9 +1541,14 @@ src_libsystemd_nm_la_SOURCES = \
src/systemd/sd-adapt/locale-util.h \
src/systemd/sd-adapt/memfd-util.h \
src/systemd/sd-adapt/missing.h \
src/systemd/sd-adapt/missing_socket.h \
src/systemd/sd-adapt/missing_syscall.h \
src/systemd/sd-adapt/missing_timerfd.h \
src/systemd/sd-adapt/missing_type.h \
src/systemd/sd-adapt/mkdir.h \
src/systemd/sd-adapt/procfs-util.h \
src/systemd/sd-adapt/raw-clone.h \
src/systemd/sd-adapt/rlimit-util.h \
src/systemd/sd-adapt/sd-daemon.h \
src/systemd/sd-adapt/sd-device.h \
src/systemd/sd-adapt/serialize.h \
@ -1555,6 +1560,8 @@ src_libsystemd_nm_la_SOURCES = \
src/systemd/src/basic/alloc-util.c \
src/systemd/src/basic/alloc-util.h \
src/systemd/src/basic/async.h \
src/systemd/src/basic/env-file.c \
src/systemd/src/basic/env-file.h \
src/systemd/src/basic/env-util.c \
src/systemd/src/basic/env-util.h \
src/systemd/src/basic/escape.c \
@ -1614,6 +1621,8 @@ src_libsystemd_nm_la_SOURCES = \
src/systemd/src/basic/strv.h \
src/systemd/src/basic/time-util.c \
src/systemd/src/basic/time-util.h \
src/systemd/src/basic/tmpfile-util.c \
src/systemd/src/basic/tmpfile-util.h \
src/systemd/src/basic/umask-util.h \
src/systemd/src/basic/utf8.c \
src/systemd/src/basic/utf8.h \

View file

@ -2,6 +2,7 @@ sources = files(
'sd-adapt/nm-sd-adapt.c',
'src/basic/alloc-util.c',
'src/basic/escape.c',
'src/basic/env-file.c',
'src/basic/env-util.c',
'src/basic/ether-addr-util.c',
'src/basic/extract-word.c',
@ -26,6 +27,7 @@ sources = files(
'src/basic/string-util.c',
'src/basic/strv.c',
'src/basic/time-util.c',
'src/basic/tmpfile-util.c',
'src/basic/utf8.c',
'src/basic/util.c',
'src/libsystemd-network/arp-util.c',

View file

@ -1,3 +1,5 @@
#pragma once
/* dummy header */
#include "path-util.h"

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View file

@ -14,7 +14,7 @@ void* memdup(const void *p, size_t l) {
assert(l == 0 || p);
ret = malloc(l);
ret = malloc(l ?: 1);
if (!ret)
return NULL;

View file

@ -8,9 +8,11 @@
#include "macro.h"
typedef void (*free_func_t)(void *p);
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
#define new0(t, n) ((t*) calloc((n), sizeof(t)))
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
#define newa(t, n) \
({ \
@ -75,7 +77,7 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t
if (size_multiply_overflow(size, need))
return NULL;
return malloc(size * need);
return malloc(size * need ?: 1);
}
#if !HAVE_REALLOCARRAY
@ -83,7 +85,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
if (size_multiply_overflow(size, need))
return NULL;
return realloc(p, size * need);
return realloc(p, size * need ?: 1);
}
#endif

View file

@ -0,0 +1,578 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt.h"
#include <stdio_ext.h>
#include "alloc-util.h"
#include "env-file.h"
#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
#include "utf8.h"
static int parse_env_file_internal(
FILE *f,
const char *fname,
int (*push) (const char *filename, unsigned line,
const char *key, char *value, void *userdata, int *n_pushed),
void *userdata,
int *n_pushed) {
size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
_cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
unsigned line = 1;
char *p;
int r;
enum {
PRE_KEY,
KEY,
PRE_VALUE,
VALUE,
VALUE_ESCAPE,
SINGLE_QUOTE_VALUE,
SINGLE_QUOTE_VALUE_ESCAPE,
DOUBLE_QUOTE_VALUE,
DOUBLE_QUOTE_VALUE_ESCAPE,
COMMENT,
COMMENT_ESCAPE
} state = PRE_KEY;
if (f)
r = read_full_stream(f, &contents, NULL);
else
r = read_full_file(fname, &contents, NULL);
if (r < 0)
return r;
for (p = contents; *p; p++) {
char c = *p;
switch (state) {
case PRE_KEY:
if (strchr(COMMENTS, c))
state = COMMENT;
else if (!strchr(WHITESPACE, c)) {
state = KEY;
last_key_whitespace = (size_t) -1;
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
return -ENOMEM;
key[n_key++] = c;
}
break;
case KEY:
if (strchr(NEWLINE, c)) {
state = PRE_KEY;
line++;
n_key = 0;
} else if (c == '=') {
state = PRE_VALUE;
last_value_whitespace = (size_t) -1;
} else {
if (!strchr(WHITESPACE, c))
last_key_whitespace = (size_t) -1;
else if (last_key_whitespace == (size_t) -1)
last_key_whitespace = n_key;
if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
return -ENOMEM;
key[n_key++] = c;
}
break;
case PRE_VALUE:
if (strchr(NEWLINE, c)) {
state = PRE_KEY;
line++;
key[n_key] = 0;
if (value)
value[n_value] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
if (r < 0)
return r;
n_key = 0;
value = NULL;
value_alloc = n_value = 0;
} else if (c == '\'')
state = SINGLE_QUOTE_VALUE;
else if (c == '\"')
state = DOUBLE_QUOTE_VALUE;
else if (c == '\\')
state = VALUE_ESCAPE;
else if (!strchr(WHITESPACE, c)) {
state = VALUE;
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = c;
}
break;
case VALUE:
if (strchr(NEWLINE, c)) {
state = PRE_KEY;
line++;
key[n_key] = 0;
if (value)
value[n_value] = 0;
/* Chomp off trailing whitespace from value */
if (last_value_whitespace != (size_t) -1)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
if (r < 0)
return r;
n_key = 0;
value = NULL;
value_alloc = n_value = 0;
} else if (c == '\\') {
state = VALUE_ESCAPE;
last_value_whitespace = (size_t) -1;
} else {
if (!strchr(WHITESPACE, c))
last_value_whitespace = (size_t) -1;
else if (last_value_whitespace == (size_t) -1)
last_value_whitespace = n_value;
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = c;
}
break;
case VALUE_ESCAPE:
state = VALUE;
if (!strchr(NEWLINE, c)) {
/* Escaped newlines we eat up entirely */
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = c;
}
break;
case SINGLE_QUOTE_VALUE:
if (c == '\'')
state = PRE_VALUE;
else if (c == '\\')
state = SINGLE_QUOTE_VALUE_ESCAPE;
else {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = c;
}
break;
case SINGLE_QUOTE_VALUE_ESCAPE:
state = SINGLE_QUOTE_VALUE;
if (!strchr(NEWLINE, c)) {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = c;
}
break;
case DOUBLE_QUOTE_VALUE:
if (c == '\"')
state = PRE_VALUE;
else if (c == '\\')
state = DOUBLE_QUOTE_VALUE_ESCAPE;
else {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = c;
}
break;
case DOUBLE_QUOTE_VALUE_ESCAPE:
state = DOUBLE_QUOTE_VALUE;
if (!strchr(NEWLINE, c)) {
if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
return -ENOMEM;
value[n_value++] = c;
}
break;
case COMMENT:
if (c == '\\')
state = COMMENT_ESCAPE;
else if (strchr(NEWLINE, c)) {
state = PRE_KEY;
line++;
}
break;
case COMMENT_ESCAPE:
state = COMMENT;
break;
}
}
if (IN_SET(state,
PRE_VALUE,
VALUE,
VALUE_ESCAPE,
SINGLE_QUOTE_VALUE,
SINGLE_QUOTE_VALUE_ESCAPE,
DOUBLE_QUOTE_VALUE,
DOUBLE_QUOTE_VALUE_ESCAPE)) {
key[n_key] = 0;
if (value)
value[n_value] = 0;
if (state == VALUE)
if (last_value_whitespace != (size_t) -1)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
if (last_key_whitespace != (size_t) -1)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
if (r < 0)
return r;
value = NULL;
}
return 0;
}
static int check_utf8ness_and_warn(
const char *filename, unsigned line,
const char *key, char *value) {
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p = NULL;
p = utf8_escape_invalid(key);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s:%u: invalid UTF-8 in key '%s', ignoring.",
strna(filename), line, p);
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *p = NULL;
p = utf8_escape_invalid(value);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
strna(filename), line, key, p);
}
return 0;
}
static int parse_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
const char *k;
va_list aq, *ap = userdata;
int r;
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
va_copy(aq, *ap);
while ((k = va_arg(aq, const char *))) {
char **v;
v = va_arg(aq, char **);
if (streq(key, k)) {
va_end(aq);
free(*v);
*v = value;
if (n_pushed)
(*n_pushed)++;
return 1;
}
}
va_end(aq);
free(value);
return 0;
}
int parse_env_filev(
FILE *f,
const char *fname,
va_list ap) {
int r, n_pushed = 0;
va_list aq;
va_copy(aq, ap);
r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed);
va_end(aq);
if (r < 0)
return r;
return n_pushed;
}
int parse_env_file_sentinel(
FILE *f,
const char *fname,
...) {
va_list ap;
int r;
va_start(ap, fname);
r = parse_env_filev(f, fname, ap);
va_end(ap);
return r;
}
#if 0 /* NM_IGNORED */
static int load_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
char ***m = userdata;
char *p;
int r;
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
r = strv_env_replace(m, p);
if (r < 0) {
free(p);
return r;
}
if (n_pushed)
(*n_pushed)++;
free(value);
return 0;
}
int load_env_file(FILE *f, const char *fname, char ***rl) {
char **m = NULL;
int r;
r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL);
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static int load_env_file_push_pairs(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
char ***m = userdata;
int r;
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
r = strv_extend(m, key);
if (r < 0)
return -ENOMEM;
if (!value) {
r = strv_extend(m, "");
if (r < 0)
return -ENOMEM;
} else {
r = strv_push(m, value);
if (r < 0)
return r;
}
if (n_pushed)
(*n_pushed)++;
return 0;
}
int load_env_file_pairs(FILE *f, const char *fname, char ***rl) {
char **m = NULL;
int r;
r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL);
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static int merge_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
char ***env = userdata;
char *expanded_value;
assert(env);
if (!value) {
log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
return 0;
}
if (!env_name_is_valid(key)) {
log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
free(value);
return 0;
}
expanded_value = replace_env(value, *env,
REPLACE_ENV_USE_ENVIRONMENT|
REPLACE_ENV_ALLOW_BRACELESS|
REPLACE_ENV_ALLOW_EXTENDED);
if (!expanded_value)
return -ENOMEM;
free_and_replace(value, expanded_value);
return load_env_file_push(filename, line, key, value, env, n_pushed);
}
int merge_env_file(
char ***env,
FILE *f,
const char *fname) {
/* NOTE: this function supports braceful and braceless variable expansions,
* plus "extended" substitutions, unlike other exported parsing functions.
*/
return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL);
}
static void write_env_var(FILE *f, const char *v) {
const char *p;
p = strchr(v, '=');
if (!p) {
/* Fallback */
fputs_unlocked(v, f);
fputc_unlocked('\n', f);
return;
}
p++;
fwrite_unlocked(v, 1, p-v, f);
if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
fputc_unlocked('\"', f);
for (; *p; p++) {
if (strchr(SHELL_NEED_ESCAPE, *p))
fputc_unlocked('\\', f);
fputc_unlocked(*p, f);
}
fputc_unlocked('\"', f);
} else
fputs_unlocked(p, f);
fputc_unlocked('\n', f);
}
int write_env_file(const char *fname, char **l) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
char **i;
int r;
assert(fname);
r = fopen_temporary(fname, &f, &p);
if (r < 0)
return r;
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
(void) fchmod_umask(fileno(f), 0644);
STRV_FOREACH(i, l)
write_env_var(f, *i);
r = fflush_and_check(f);
if (r >= 0) {
if (rename(p, fname) >= 0)
return 0;
r = -errno;
}
unlink(p);
return r;
}
#endif /* NM_IGNORED */

View file

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdarg.h>
#include <stdio.h>
#include "macro.h"
int parse_env_filev(FILE *f, const char *fname, va_list ap);
int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_;
#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL)
int load_env_file(FILE *f, const char *fname, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, char ***l);
int merge_env_file(char ***env, FILE *f, const char *fname);
int write_env_file(const char *fname, char **l);

View file

@ -10,7 +10,7 @@
#endif /* NM_IGNORED */
#include "string-util.h"
#include "missing.h"
#include "missing_type.h"
/* What characters are special in the shell? */
/* must be escaped outside and inside double-quotes */

View file

@ -30,21 +30,15 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR
return buffer;
}
int ether_addr_compare(const void *a, const void *b) {
assert(a);
assert(b);
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
return memcmp(a, b, ETH_ALEN);
}
static void ether_addr_hash_func(const void *p, struct siphash *state) {
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
siphash24_compress(p, sizeof(struct ether_addr), state);
}
const struct hash_ops ether_addr_hash_ops = {
.hash = ether_addr_hash_func,
.compare = ether_addr_compare
};
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
int ether_addr_from_string(const char *s, struct ether_addr *ret) {
size_t pos = 0, n, field;

View file

@ -12,7 +12,7 @@
#define ETHER_ADDR_TO_STRING_MAX (3*6)
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
int ether_addr_compare(const void *a, const void *b);
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b);
static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) {
return ether_addr_compare(a, b) == 0;
}

View file

@ -25,6 +25,7 @@
#include "socket-util.h"
#include "stdio-util.h"
#include "util.h"
#include "tmpfile-util.h"
int close_nointr(int fd) {
assert(fd >= 0);
@ -115,7 +116,7 @@ FILE* safe_fclose(FILE *f) {
if (f) {
PROTECT_ERRNO;
assert_se(fclose_nointr(f) != EBADF);
assert_se(fclose_nointr(f) != -EBADF);
}
return NULL;
@ -653,7 +654,7 @@ int fd_duplicate_data_fd(int fd) {
if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size);
r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
if (r < 0 && r != -EAGAIN)
return r; /* If we get EAGAIN it could be because of the source or because of
* the destination fd, we can't know, as sendfile() and friends won't

File diff suppressed because it is too large Load diff

View file

@ -44,16 +44,6 @@ int read_full_stream(FILE *f, char **contents, size_t *size);
int verify_file(const char *fn, const char *blob, bool accept_extra_nl);
int parse_env_filev(FILE *f, const char *fname, va_list ap);
int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_;
#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL)
int load_env_file(FILE *f, const char *fname, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, char ***l);
int merge_env_file(char ***env, FILE *f, const char *fname);
int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
@ -66,27 +56,23 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
int mkostemp_safe(char *pattern);
int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
int tempfn_random(const char *p, const char *extra, char **ret);
int tempfn_random_child(const char *p, const char *extra, char **ret);
int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
int open_tmpfile_unlinkable(const char *directory, int flags);
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
int open_serialization_fd(const char *ident);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
} ReadLineFlags;
int link_tmpfile(int fd, const char *path, const char *target);
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);
int read_nul_string(FILE *f, char **ret);
static inline int read_line(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, 0, ret);
}
int mkdtemp_malloc(const char *template, char **ret);
static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret);
}
int read_line(FILE *f, size_t limit, char **ret);
int safe_fgetc(FILE *f, char *ret);

View file

@ -15,8 +15,8 @@
#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "locale-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
@ -29,6 +29,7 @@
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
#include "tmpfile-util.h"
#include "user-util.h"
#include "util.h"
@ -216,31 +217,62 @@ int readlink_and_make_absolute(const char *p, char **r) {
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -1;
assert(path);
/* Under the assumption that we are running privileged we
* first change the access mode and only then hand out
/* Under the assumption that we are running privileged we first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
if (mode != MODE_INVALID)
if (chmod(path, mode) < 0)
fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change mode/owner
* on the same file */
if (fd < 0)
return -errno;
xsprintf(fd_path, "/proc/self/fd/%i", fd);
if (mode != MODE_INVALID) {
if ((mode & S_IFMT) != 0) {
struct stat st;
if (stat(fd_path, &st) < 0)
return -errno;
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
return -EINVAL;
}
if (chmod(fd_path, mode & 07777) < 0)
return -errno;
}
if (uid != UID_INVALID || gid != GID_INVALID)
if (chown(path, uid, gid) < 0)
if (chown(fd_path, uid, gid) < 0)
return -errno;
return 0;
}
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
/* Under the assumption that we are running privileged we
* first change the access mode and only then hand out
/* Under the assumption that we are running privileged we first change the access mode and only then hand out
* ownership to avoid a window where access is too open. */
if (mode != MODE_INVALID)
if (fchmod(fd, mode) < 0)
if (mode != MODE_INVALID) {
if ((mode & S_IFMT) != 0) {
struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
return -EINVAL;
}
if (fchmod(fd, mode & 0777) < 0)
return -errno;
}
if (uid != UID_INVALID || gid != GID_INVALID)
if (fchown(fd, uid, gid) < 0)
@ -270,7 +302,6 @@ int fchmod_opath(int fd, mode_t m) {
* fchownat() does. */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
if (chmod(procfs_path, m) < 0)
return -errno;
@ -642,15 +673,42 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
}
#if 0 /* NM_IGNORED */
static bool safe_transition(const struct stat *a, const struct stat *b) {
static bool unsafe_transition(const struct stat *a, const struct stat *b) {
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
* making us believe we read something safe even though it isn't safe in the specific context we open it in. */
if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
return true;
return false;
return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
}
static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
_cleanup_free_ char *n1 = NULL, *n2 = NULL;
if (!FLAGS_SET(flags, CHASE_WARN))
return -ENOLINK;
(void) fd_get_path(a, &n1);
(void) fd_get_path(b, &n2);
return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
"Detected unsafe path transition %s %s %s during canonicalization of %s.",
n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path);
}
static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
_cleanup_free_ char *n1 = NULL;
if (!FLAGS_SET(flags, CHASE_WARN))
return -EREMOTE;
(void) fd_get_path(fd, &n1);
return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
"Detected autofs mount point %s during canonicalization of %s.",
n1, path);
}
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
@ -713,6 +771,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
* path is fully normalized, and == 0 for each normalization step. This may be combined with
* CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
*
* 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
* unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
* CHASE_WARN is also set a warning describing the unsafe transition is emitted.
*
* 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, the path normalization is
* aborted and -EREMOTE is returned. If CHASE_WARN is also set a warning showing the path of the mount point
* is emitted.
*
* */
/* A root directory of "/" or "" is identical to none */
@ -827,8 +893,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(fd_parent, &st) < 0)
return -errno;
if (!safe_transition(&previous_stat, &st))
return -EPERM;
if (unsafe_transition(&previous_stat, &st))
return log_unsafe_transition(fd, fd_parent, path, flags);
previous_stat = st;
}
@ -868,14 +934,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(child, &st) < 0)
return -errno;
if ((flags & CHASE_SAFE) &&
!safe_transition(&previous_stat, &st))
return -EPERM;
unsafe_transition(&previous_stat, &st))
return log_unsafe_transition(fd, child, path, flags);
previous_stat = st;
if ((flags & CHASE_NO_AUTOFS) &&
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
return -EREMOTE;
return log_autofs_mount_point(child, path, flags);
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
char *joined;
@ -907,8 +973,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(fd, &st) < 0)
return -errno;
if (!safe_transition(&previous_stat, &st))
return -EPERM;
if (unsafe_transition(&previous_stat, &st))
return log_unsafe_transition(child, fd, path, flags);
previous_stat = st;
}

View file

@ -74,6 +74,7 @@ enum {
CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */
CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */
CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */
CHASE_WARN = 1 << 8, /* Emit an appropriate warning when an error is encountered */
};
/* How many iterations to execute before returning -ELOOP */

View file

@ -7,22 +7,14 @@
#include "hash-funcs.h"
#include "path-util.h"
void string_hash_func(const void *p, struct siphash *state) {
void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
}
#if 0 /* NM_IGNORED */
int string_compare_func(const void *a, const void *b) {
return strcmp(a, b);
}
DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
const struct hash_ops string_hash_ops = {
.hash = string_hash_func,
.compare = string_compare_func
};
void path_hash_func(const void *p, struct siphash *state) {
const char *q = p;
void path_hash_func(const char *q, struct siphash *state) {
size_t n;
assert(q);
@ -60,14 +52,11 @@ void path_hash_func(const void *p, struct siphash *state) {
}
}
int path_compare_func(const void *a, const void *b) {
int path_compare_func(const char *a, const char *b) {
return path_compare(a, b);
}
const struct hash_ops path_hash_ops = {
.hash = path_hash_func,
.compare = path_compare_func
};
DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare_func);
#endif /* NM_IGNORED */
void trivial_hash_func(const void *p, struct siphash *state) {
@ -80,41 +69,29 @@ int trivial_compare_func(const void *a, const void *b) {
const struct hash_ops trivial_hash_ops = {
.hash = trivial_hash_func,
.compare = trivial_compare_func
.compare = trivial_compare_func,
};
void uint64_hash_func(const void *p, struct siphash *state) {
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(uint64_t), state);
}
int uint64_compare_func(const void *_a, const void *_b) {
uint64_t a, b;
a = *(const uint64_t*) _a;
b = *(const uint64_t*) _b;
return CMP(a, b);
int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
return CMP(*a, *b);
}
const struct hash_ops uint64_hash_ops = {
.hash = uint64_hash_func,
.compare = uint64_compare_func
};
DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func);
#if 0 /* NM_IGNORED */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const void *p, struct siphash *state) {
void devt_hash_func(const dev_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(dev_t), state);
}
int devt_compare_func(const void *_a, const void *_b) {
dev_t a, b;
a = *(const dev_t*) _a;
b = *(const dev_t*) _b;
return CMP(a, b);
int devt_compare_func(const dev_t *a, const dev_t *b) {
return CMP(*a, *b);
}
const struct hash_ops devt_hash_ops = {
.hash = devt_hash_func,
.compare = devt_compare_func
};
DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func);
#endif
#endif /* NM_IGNORED */

View file

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "alloc-util.h"
#include "macro.h"
#include "siphash24.h"
@ -10,14 +11,74 @@ typedef int (*compare_func_t)(const void *a, const void *b);
struct hash_ops {
hash_func_t hash;
compare_func_t compare;
free_func_t free_key;
free_func_t free_value;
};
void string_hash_func(const void *p, struct siphash *state);
int string_compare_func(const void *a, const void *b) _pure_;
#define _DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, free_key_func, free_value_func, scope) \
_unused_ static void (* UNIQ_T(static_hash_wrapper, uq))(const type *, struct siphash *) = hash_func; \
_unused_ static int (* UNIQ_T(static_compare_wrapper, uq))(const type *, const type *) = compare_func; \
scope const struct hash_ops name = { \
.hash = (hash_func_t) hash_func, \
.compare = (compare_func_t) compare_func, \
.free_key = free_key_func, \
.free_value = free_value_func, \
}
#define _DEFINE_FREE_FUNC(uq, type, wrapper_name, func) \
/* Type-safe free function */ \
static void UNIQ_T(wrapper_name, uq)(void *a) { \
type *_a = a; \
func(_a); \
}
#define _DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(uq, name, type, hash_func, compare_func, free_func, scope) \
_DEFINE_FREE_FUNC(uq, type, static_free_wrapper, free_func); \
_DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
UNIQ_T(static_free_wrapper, uq), NULL, scope)
#define _DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(uq, name, type, hash_func, compare_func, type_value, free_func, scope) \
_DEFINE_FREE_FUNC(uq, type_value, static_free_wrapper, free_func); \
_DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
NULL, UNIQ_T(static_free_wrapper, uq), scope)
#define _DEFINE_HASH_OPS_FULL(uq, name, type, hash_func, compare_func, free_key_func, type_value, free_value_func, scope) \
_DEFINE_FREE_FUNC(uq, type, static_free_key_wrapper, free_key_func); \
_DEFINE_FREE_FUNC(uq, type_value, static_free_value_wrapper, free_value_func); \
_DEFINE_HASH_OPS(uq, name, type, hash_func, compare_func, \
UNIQ_T(static_free_key_wrapper, uq), \
UNIQ_T(static_free_value_wrapper, uq), scope)
#define DEFINE_HASH_OPS(name, type, hash_func, compare_func) \
_DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL,)
#define DEFINE_PRIVATE_HASH_OPS(name, type, hash_func, compare_func) \
_DEFINE_HASH_OPS(UNIQ, name, type, hash_func, compare_func, NULL, NULL, static)
#define DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
_DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func,)
#define DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(name, type, hash_func, compare_func, free_func) \
_DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, free_func, static)
#define DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
_DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func,)
#define DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(name, type, hash_func, compare_func, value_type, free_func) \
_DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(UNIQ, name, type, hash_func, compare_func, value_type, free_func, static)
#define DEFINE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
_DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func,)
#define DEFINE_PRIVATE_HASH_OPS_FULL(name, type, hash_func, compare_func, free_key_func, value_type, free_value_func) \
_DEFINE_HASH_OPS_FULL(UNIQ, name, type, hash_func, compare_func, free_key_func, value_type, free_value_func, static)
void string_hash_func(const char *p, struct siphash *state);
#define string_compare_func strcmp
extern const struct hash_ops string_hash_ops;
void path_hash_func(const void *p, struct siphash *state);
int path_compare_func(const void *a, const void *b) _pure_;
void path_hash_func(const char *p, struct siphash *state);
int path_compare_func(const char *a, const char *b) _pure_;
extern const struct hash_ops path_hash_ops;
/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
@ -28,15 +89,15 @@ extern const struct hash_ops trivial_hash_ops;
/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
* values indirectly, since they don't fit in a pointer. */
void uint64_hash_func(const void *p, struct siphash *state);
int uint64_compare_func(const void *a, const void *b) _pure_;
void uint64_hash_func(const uint64_t *p, struct siphash *state);
int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
* 64bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const void *p, struct siphash *state) _pure_;
int devt_compare_func(const void *a, const void *b) _pure_;
void devt_hash_func(const dev_t *p, struct siphash *state) _pure_;
int devt_compare_func(const dev_t *a, const dev_t *b) _pure_;
extern const struct hash_ops devt_hash_ops;
#else
#define devt_hash_func uint64_hash_func

View file

@ -278,7 +278,7 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
};
#if VALGRIND
__attribute__((destructor)) static void cleanup_pools(void) {
_destructor_ static void cleanup_pools(void) {
_cleanup_free_ char *t = NULL;
int r;
@ -781,7 +781,7 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
h->type = type;
h->from_pool = up;
h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops;
h->hash_ops = hash_ops ?: &trivial_hash_ops;
if (type == HASHMAP_TYPE_ORDERED) {
OrderedHashmap *lh = (OrderedHashmap*)h;
@ -850,7 +850,7 @@ int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASH
static void hashmap_free_no_clear(HashmapBase *h) {
assert(!h->has_indirect);
assert(!h->n_direct_entries);
assert(h->n_direct_entries == 0);
#if ENABLE_DEBUG_HASHMAP
assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0);
@ -866,47 +866,42 @@ static void hashmap_free_no_clear(HashmapBase *h) {
free(h);
}
HashmapBase *internal_hashmap_free(HashmapBase *h) {
/* Free the hashmap, but nothing in it */
HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
if (h) {
internal_hashmap_clear(h);
internal_hashmap_clear(h, default_free_key, default_free_value);
hashmap_free_no_clear(h);
}
return NULL;
}
HashmapBase *internal_hashmap_free_free(HashmapBase *h) {
/* Free the hashmap and all data objects in it, but not the
* keys */
if (h) {
internal_hashmap_clear_free(h);
hashmap_free_no_clear(h);
}
return NULL;
}
Hashmap *hashmap_free_free_free(Hashmap *h) {
/* Free the hashmap and all data and key objects in it */
if (h) {
hashmap_clear_free_free(h);
hashmap_free_no_clear(HASHMAP_BASE(h));
}
return NULL;
}
void internal_hashmap_clear(HashmapBase *h) {
void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
free_func_t free_key, free_value;
if (!h)
return;
free_key = h->hash_ops->free_key ?: default_free_key;
free_value = h->hash_ops->free_value ?: default_free_value;
if (free_key || free_value) {
/* If destructor calls are defined, let's destroy things defensively: let's take the item out of the
* hash table, and only then call the destructor functions. If these destructors then try to unregister
* themselves from our hash table a second time, the entry is already gone. */
while (internal_hashmap_size(h) > 0) {
void *v, *k;
v = internal_hashmap_first_key_and_value(h, true, &k);
if (free_key)
free_key(k);
if (free_value)
free_value(v);
}
}
if (h->has_indirect) {
free(h->indirect.storage);
h->has_indirect = false;
@ -923,35 +918,6 @@ void internal_hashmap_clear(HashmapBase *h) {
base_set_dirty(h);
}
void internal_hashmap_clear_free(HashmapBase *h) {
unsigned idx;
if (!h)
return;
for (idx = skip_free_buckets(h, 0); idx != IDX_NIL;
idx = skip_free_buckets(h, idx + 1))
free(entry_value(h, bucket_at(h, idx)));
internal_hashmap_clear(h);
}
void hashmap_clear_free_free(Hashmap *h) {
unsigned idx;
if (!h)
return;
for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL;
idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) {
struct plain_hashmap_entry *e = plain_bucket_at(h, idx);
free((void*)e->b.key);
free(e->value);
}
internal_hashmap_clear(HASHMAP_BASE(h));
}
static int resize_buckets(HashmapBase *h, unsigned entries_add);
/*
@ -1515,8 +1481,8 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_
return 0;
}
void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
struct plain_hashmap_entry *e;
void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
struct hashmap_base_entry *e;
unsigned hash, idx;
if (!h)
@ -1527,8 +1493,8 @@ void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
if (idx == IDX_NIL)
return NULL;
e = plain_bucket_at(h, idx);
if (e->value != value)
e = bucket_at(h, idx);
if (entry_value(h, e) != value)
return NULL;
remove_entry(h, idx);
@ -1742,7 +1708,7 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) {
}
if (r < 0) {
internal_hashmap_free(copy);
internal_hashmap_free(copy, false, false);
return NULL;
}

View file

@ -22,6 +22,8 @@
#define HASH_KEY_SIZE 16
typedef void* (*hashmap_destroy_t)(void *p);
/* The base type for all hashmap and set types. Many functions in the
* implementation take (HashmapBase*) parameters and are run-time polymorphic,
* though the API is not meant to be polymorphic (do not call functions
@ -87,25 +89,33 @@ OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HA
#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
HashmapBase *internal_hashmap_free(HashmapBase *h);
HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline Hashmap *hashmap_free(Hashmap *h) {
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
}
static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
return (void*)internal_hashmap_free(HASHMAP_BASE(h));
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
}
HashmapBase *internal_hashmap_free_free(HashmapBase *h);
static inline Hashmap *hashmap_free_free(Hashmap *h) {
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
}
static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
return (void*)internal_hashmap_free_free(HASHMAP_BASE(h));
return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
}
Hashmap *hashmap_free_free_free(Hashmap *h);
static inline Hashmap *hashmap_free_free_key(Hashmap *h) {
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
}
static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) {
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
}
static inline Hashmap *hashmap_free_free_free(Hashmap *h) {
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
}
static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h));
return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
}
IteratedCache *iterated_cache_free(IteratedCache *cache);
@ -181,7 +191,11 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key,
return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
}
void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value);
}
static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
}
@ -258,25 +272,33 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void
return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
}
void internal_hashmap_clear(HashmapBase *h);
void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline void hashmap_clear(Hashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h));
internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
}
static inline void ordered_hashmap_clear(OrderedHashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h));
internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
}
void internal_hashmap_clear_free(HashmapBase *h);
static inline void hashmap_clear_free(Hashmap *h) {
internal_hashmap_clear_free(HASHMAP_BASE(h));
internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
internal_hashmap_clear_free(HASHMAP_BASE(h));
internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
}
void hashmap_clear_free_free(Hashmap *h);
static inline void hashmap_clear_free_key(Hashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
}
static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
}
static inline void hashmap_clear_free_free(Hashmap *h) {
internal_hashmap_clear(HASHMAP_BASE(h), free, free);
}
static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
hashmap_clear_free_free(PLAIN_HASHMAP(h));
internal_hashmap_clear(HASHMAP_BASE(h), free, free);
}
/*

View file

@ -73,12 +73,12 @@ int gethostname_strict(char **ret) {
return 0;
}
static bool hostname_valid_char(char c) {
bool valid_ldh_char(char c) {
return
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
IN_SET(c, '-', '_', '.');
c == '-';
}
/**
@ -94,7 +94,7 @@ static bool hostname_valid_char(char c) {
bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
unsigned n_dots = 0;
const char *p;
bool dot;
bool dot, hyphen;
if (isempty(s))
return false;
@ -104,23 +104,34 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
* sequence. Also ensures that the length stays below
* HOST_NAME_MAX. */
for (p = s, dot = true; *p; p++) {
for (p = s, dot = hyphen = true; *p; p++)
if (*p == '.') {
if (dot)
if (dot || hyphen)
return false;
dot = true;
hyphen = false;
n_dots++;
} else {
if (!hostname_valid_char(*p))
} else if (*p == '-') {
if (dot)
return false;
dot = false;
hyphen = true;
} else {
if (!valid_ldh_char(*p))
return false;
dot = false;
hyphen = false;
}
}
if (dot && (n_dots < 2 || !allow_trailing_dot))
return false;
if (hyphen)
return false;
if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on
* Linux, but DNS allows domain names
@ -132,29 +143,38 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
char* hostname_cleanup(char *s) {
char *p, *d;
bool dot;
bool dot, hyphen;
assert(s);
strshorten(s, HOST_NAME_MAX);
for (p = s, d = s, dot = true; *p; p++) {
for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
if (*p == '.') {
if (dot)
if (dot || hyphen)
continue;
*(d++) = '.';
dot = true;
} else if (hostname_valid_char(*p)) {
hyphen = false;
} else if (*p == '-') {
if (dot)
continue;
*(d++) = '-';
dot = false;
hyphen = true;
} else if (valid_ldh_char(*p)) {
*(d++) = *p;
dot = false;
hyphen = false;
}
}
if (dot && d > s)
d[-1] = 0;
else
*d = 0;
if (d > s && IN_SET(d[-1], '-', '.'))
/* The dot can occur at most once, but we might have multiple
* hyphens, hence the loop */
d--;
*d = 0;
return s;
}

View file

@ -11,6 +11,7 @@ bool hostname_is_set(void);
char* gethostname_malloc(void);
int gethostname_strict(char **ret);
bool valid_ldh_char(char c) _const_;
bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_;
char* hostname_cleanup(char *s);

View file

@ -366,7 +366,7 @@ int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_u
unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) {
assert(addr);
return 32 - u32ctz(be32toh(addr->s_addr));
return 32U - u32ctz(be32toh(addr->s_addr));
}
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) {
@ -608,15 +608,12 @@ int in_addr_prefix_from_string_auto_internal(
}
void in_addr_data_hash_func(const void *p, struct siphash *state) {
const struct in_addr_data *a = p;
static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
siphash24_compress(&a->family, sizeof(a->family), state);
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
}
int in_addr_data_compare_func(const void *a, const void *b) {
const struct in_addr_data *x = a, *y = b;
static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
int r;
r = CMP(x->family, y->family);
@ -626,8 +623,5 @@ int in_addr_data_compare_func(const void *a, const void *b) {
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
}
const struct hash_ops in_addr_data_hash_ops = {
.hash = in_addr_data_hash_func,
.compare = in_addr_data_compare_func,
};
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
#endif /* NM_IGNORED */

View file

@ -69,6 +69,4 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
void in_addr_data_hash_func(const void *p, struct siphash *state);
int in_addr_data_compare_func(const void *a, const void *b);
extern const struct hash_ops in_addr_data_hash_ops;

View file

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "macro.h"
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define LIST_HEAD(t,name) \
@ -13,8 +15,8 @@
/* Initialize the list's head */
#define LIST_HEAD_INIT(head) \
do { \
(head) = NULL; } \
while (false)
(head) = NULL; \
} while (false)
/* Initialize a list item */
#define LIST_INIT(name,item) \

View file

@ -2,33 +2,38 @@
#pragma once
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#define _printf_(a, b) __attribute__ ((__format__(printf, a, b)))
#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
#ifdef __clang__
# define _alloc_(...)
#else
# define _alloc_(...) __attribute__ ((__alloc_size__(__VA_ARGS__)))
# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
#endif
#define _sentinel_ __attribute__ ((__sentinel__))
#define _unused_ __attribute__ ((__unused__))
#define _destructor_ __attribute__ ((__destructor__))
#define _pure_ __attribute__ ((__pure__))
#define _const_ __attribute__ ((__const__))
#define _deprecated_ __attribute__ ((__deprecated__))
#define _packed_ __attribute__ ((__packed__))
#define _malloc_ __attribute__ ((__malloc__))
#define _weak_ __attribute__ ((__weak__))
#define _sentinel_ __attribute__((__sentinel__))
#define _section_(x) __attribute__((__section__(x)))
#define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__))
#define _destructor_ __attribute__((__destructor__))
#define _pure_ __attribute__((__pure__))
#define _const_ __attribute__((__const__))
#define _deprecated_ __attribute__((__deprecated__))
#define _packed_ __attribute__((__packed__))
#define _malloc_ __attribute__((__malloc__))
#define _weak_ __attribute__((__weak__))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#define _public_ __attribute__ ((__visibility__("default")))
#define _hidden_ __attribute__ ((__visibility__("hidden")))
#define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x)))
#define _align_(x) __attribute__((__aligned__(x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _cleanup_(x) __attribute__((__cleanup__(x)))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
@ -56,6 +61,29 @@
# endif
#endif
#if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
# ifdef __SANITIZE_ADDRESS__
# define HAS_FEATURE_ADDRESS_SANITIZER 1
# elif defined(__has_feature)
# if __has_feature(address_sanitizer)
# define HAS_FEATURE_ADDRESS_SANITIZER 1
# endif
# endif
# if !defined(HAS_FEATURE_ADDRESS_SANITIZER)
# define HAS_FEATURE_ADDRESS_SANITIZER 0
# endif
#endif
/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global
* variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since
* our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be
* padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */
#if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__)
#define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__))
#else
#define _variable_no_sanitize_address_
#endif
#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__)
/* Temporarily disable some warnings */
#define DISABLE_WARNING_FORMAT_NONLITERAL \
@ -255,11 +283,12 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
* computation should be possible in the given type. Therefore, we use
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
* quotient and the remainder, so both should be equally fast. */
#define DIV_ROUND_UP(_x, _y) \
#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
#define __DIV_ROUND_UP(xq, x, yq, y) \
({ \
const typeof(_x) __x = (_x); \
const typeof(_y) __y = (_y); \
(__x / __y + !!(__x % __y)); \
const typeof(x) UNIQ_T(X, xq) = (x); \
const typeof(y) UNIQ_T(Y, yq) = (y); \
(UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \
})
#ifdef __COVERITY__
@ -466,6 +495,11 @@ static inline int __coverity_check__(int condition) {
#endif
#endif
#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
static inline void name(type *p) { \
func(p); \
}
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \

View file

@ -4,6 +4,7 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/oom.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
@ -18,10 +19,12 @@
#include "missing.h"
#include "parse-util.h"
#include "process-util.h"
#include "stat-util.h"
#include "string-util.h"
int parse_boolean(const char *v) {
assert(v);
if (!v)
return -EINVAL;
if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
return 1;
@ -715,18 +718,51 @@ int parse_ip_port(const char *s, uint16_t *ret) {
return 0;
}
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
unsigned l, h;
int r;
r = parse_range(s, &l, &h);
if (r < 0)
return r;
if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
return -EINVAL;
if (h < l)
return -EINVAL;
*low = l;
*high = h;
return 0;
}
int parse_dev(const char *s, dev_t *ret) {
const char *major;
unsigned x, y;
dev_t d;
size_t n;
int r;
if (sscanf(s, "%u:%u", &x, &y) != 2)
n = strspn(s, DIGITS);
if (n == 0)
return -EINVAL;
if (s[n] != ':')
return -EINVAL;
d = makedev(x, y);
if ((unsigned) major(d) != x || (unsigned) minor(d) != y)
return -EINVAL;
major = strndupa(s, n);
r = safe_atou(major, &x);
if (r < 0)
return r;
*ret = d;
r = safe_atou(s + n + 1, &y);
if (r < 0)
return r;
if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
return -ERANGE;
*ret = makedev(x, y);
return 0;
}

View file

@ -115,5 +115,6 @@ int parse_permille(const char *p);
int parse_nice(const char *p, int *ret);
int parse_ip_port(const char *s, uint16_t *ret);
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
int parse_oom_score_adjust(const char *s, int *ret);

View file

@ -113,7 +113,7 @@ int path_make_absolute_cwd(const char *p, char **ret) {
if (r < 0)
return r;
c = path_join(NULL, cwd, p);
c = path_join(cwd, p);
}
if (!c)
return -ENOMEM;
@ -486,18 +486,62 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) {
return path_equal(a, b) || files_same(a, b, flags) > 0;
}
char* path_join(const char *root, const char *path, const char *rest) {
assert(path);
char* path_join_internal(const char *first, ...) {
char *joined, *q;
const char *p;
va_list ap;
bool slash;
size_t sz;
if (!isempty(root))
return strjoin(root, endswith(root, "/") ? "" : "/",
path[0] == '/' ? path+1 : path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
rest && rest[0] == '/' ? rest+1 : rest);
else
return strjoin(path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
rest && rest[0] == '/' ? rest+1 : rest);
/* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin
* already with one so that it is unnecessary. Note that slashes which are already duplicate won't be
* removed. The string returned is hence always equal to or longer than the sum of the lengths of each
* individual string.
*
* Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some
* are optional.
*
* Examples:
*
* path_join("foo", "bar") "foo/bar"
* path_join("foo/", "bar") "foo/bar"
* path_join("", "foo", "", "bar", "") "foo/bar" */
sz = strlen_ptr(first);
va_start(ap, first);
while ((p = va_arg(ap, char*)) != (const char*) -1)
if (!isempty(p))
sz += 1 + strlen(p);
va_end(ap);
joined = new(char, sz + 1);
if (!joined)
return NULL;
if (!isempty(first)) {
q = stpcpy(joined, first);
slash = endswith(first, "/");
} else {
/* Skip empty items */
joined[0] = 0;
q = joined;
slash = true; /* no need to generate a slash anymore */
}
va_start(ap, first);
while ((p = va_arg(ap, char*)) != (const char*) -1) {
if (isempty(p))
continue;
if (!slash && p[0] != '/')
*(q++) = '/';
q = stpcpy(q, p);
slash = endswith(p, "/");
}
va_end(ap);
return joined;
}
int find_binary(const char *name, char **ret) {
@ -755,6 +799,9 @@ const char *last_path_component(const char *path) {
unsigned l, k;
if (!path)
return NULL;
l = k = strlen(path);
if (l == 0) /* special case — an empty string */
return path;
@ -770,6 +817,37 @@ const char *last_path_component(const char *path) {
return path + k;
}
int path_extract_filename(const char *p, char **ret) {
_cleanup_free_ char *a = NULL;
const char *c, *e = NULL, *q;
/* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */
if (!p)
return -EINVAL;
c = last_path_component(p);
for (q = c; *q != 0; q++)
if (*q != '/')
e = q + 1;
if (!e) /* no valid character? */
return -EINVAL;
a = strndup(c, e - c);
if (!a)
return -ENOMEM;
if (!filename_is_valid(a))
return -EINVAL;
*ret = TAKE_PTR(a);
return 0;
}
#endif /* NM_IGNORED */
bool filename_is_valid(const char *p) {
@ -926,8 +1004,7 @@ bool valid_device_allow_pattern(const char *path) {
/* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
* accept it */
if (startswith(path, "block-") ||
startswith(path, "char-"))
if (STARTSWITH_SET(path, "block-", "char-"))
return true;
return valid_device_node_path(path);
@ -1071,6 +1148,13 @@ int path_simplify_and_warn(
return -EINVAL;
}
if (!path_is_valid(path)) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"%s= path has invalid length (%zu bytes)%s.",
lvalue, strlen(path), fatal ? "" : ", ignoring");
return -EINVAL;
}
return 0;
}
#endif /* NM_IGNORED */

View file

@ -7,6 +7,7 @@
#include "macro.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
#if 0 /* NM_IGNORED */
@ -50,7 +51,9 @@ char* path_startswith(const char *path, const char *prefix) _pure_;
int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b, int flags);
char* path_join(const char *root, const char *path, const char *rest);
char* path_join_internal(const char *first, ...);
#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, (const char*) -1)
char* path_simplify(char *path, bool kill_dots);
static inline bool path_equal_ptr(const char *a, const char *b) {
@ -72,13 +75,13 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
#define PATH_STARTSWITH_SET(p, ...) \
({ \
char **s; \
bool _found = false; \
STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \
if (path_startswith(p, *s)) { \
_found = true; \
break; \
} \
const char *_p = (p); \
char *_found = NULL, **_i; \
STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
_found = path_startswith(_p, *_i); \
if (_found) \
break; \
} \
_found; \
})
@ -96,12 +99,24 @@ int mkfs_exists(const char *fstype);
/* Iterates through the path prefixes of the specified path, going up
* the tree, to root. Also returns "" (and not "/"!) for the root
* directory. Excludes the specified directory itself */
#define PATH_FOREACH_PREFIX(prefix, path) \
for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
#define PATH_FOREACH_PREFIX(prefix, path) \
for (char *_slash = ({ \
path_simplify(strcpy(prefix, path), false); \
streq(prefix, "/") ? NULL : strrchr(prefix, '/'); \
}); \
_slash && ((*_slash = 0), true); \
_slash = strrchr((prefix), '/'))
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
for (char *_slash = ({ \
path_simplify(strcpy(prefix, path), false); \
if (streq(prefix, "/")) \
prefix[0] = 0; \
strrchr(prefix, 0); \
}); \
_slash && ((*_slash = 0), true); \
_slash = strrchr((prefix), '/'))
char *prefix_root(const char *root, const char *path);
@ -134,6 +149,7 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar
char* dirname_malloc(const char *path);
const char *last_path_component(const char *path);
int path_extract_filename(const char *p, char **ret);
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid(const char *p) _pure_;

View file

@ -67,9 +67,6 @@ int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
}
static void swap(Prioq *q, unsigned j, unsigned k) {
void *saved_data;
unsigned *saved_idx;
assert(q);
assert(j < q->n_items);
assert(k < q->n_items);
@ -77,12 +74,8 @@ static void swap(Prioq *q, unsigned j, unsigned k) {
assert(!q->items[j].idx || *(q->items[j].idx) == j);
assert(!q->items[k].idx || *(q->items[k].idx) == k);
saved_data = q->items[j].data;
saved_idx = q->items[j].idx;
q->items[j].data = q->items[k].data;
q->items[j].idx = q->items[k].idx;
q->items[k].data = saved_data;
q->items[k].idx = saved_idx;
SWAP_TWO(q->items[j].data, q->items[k].data);
SWAP_TWO(q->items[j].idx, q->items[k].idx);
if (q->items[j].idx)
*q->items[j].idx = j;

View file

@ -39,6 +39,7 @@
#include "missing.h"
#include "process-util.h"
#include "raw-clone.h"
#include "rlimit-util.h"
#include "signal-util.h"
#include "stat-util.h"
#include "string-table.h"
@ -604,12 +605,14 @@ int get_process_root(pid_t pid, char **root) {
return get_process_link_contents(p, root);
}
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
int get_process_environ(pid_t pid, char **env) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *outcome = NULL;
int c;
const char *p;
size_t allocated = 0, sz = 0;
const char *p;
int r;
assert(pid >= 0);
assert(env);
@ -625,23 +628,28 @@ int get_process_environ(pid_t pid, char **env) {
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
while ((c = fgetc(f)) != EOF) {
for (;;) {
char c;
if (sz >= ENVIRONMENT_BLOCK_MAX)
return -ENOBUFS;
if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
return -ENOMEM;
r = safe_fgetc(f, &c);
if (r < 0)
return r;
if (r == 0)
break;
if (c == '\0')
outcome[sz++] = '\n';
else
sz += cescape_char(c, outcome + sz);
}
if (!outcome) {
outcome = strdup("");
if (!outcome)
return -ENOMEM;
} else
outcome[sz] = '\0';
outcome[sz] = '\0';
*env = TAKE_PTR(outcome);
return 0;
@ -874,9 +882,9 @@ int kill_and_sigcont(pid_t pid, int sig) {
int getenv_for_pid(pid_t pid, const char *field, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
char *value = NULL;
bool done = false;
const char *path;
size_t l;
size_t l, sum = 0;
int r;
assert(pid >= 0);
assert(field);
@ -899,6 +907,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
return 1;
}
if (!pid_is_valid(pid))
return -EINVAL;
path = procfs_file_alloca(pid, "environ");
f = fopen(path, "re");
@ -912,24 +923,19 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
l = strlen(field);
for (;;) {
_cleanup_free_ char *line = NULL;
do {
char line[LINE_MAX];
size_t i;
if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
return -ENOBUFS;
for (i = 0; i < sizeof(line)-1; i++) {
int c;
r = read_nul_string(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0) /* EOF */
break;
c = getc(f);
if (_unlikely_(c == EOF)) {
done = true;
break;
} else if (c == 0)
break;
line[i] = c;
}
line[i] = 0;
sum += r;
if (strneq(line, field, l) && line[l] == '=') {
value = strdup(line + l + 1);
@ -939,8 +945,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
*ret = value;
return 1;
}
} while (!done);
}
*ret = NULL;
return 0;
@ -1180,7 +1185,7 @@ void reset_cached_pid(void) {
* headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against
* libpthread, as it is part of glibc anyway. */
extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle);
extern void* __dso_handle __attribute__ ((__weak__));
extern void* __dso_handle _weak_;
pid_t getpid_cached(void) {
static bool installed = false;
@ -1259,25 +1264,17 @@ int safe_fork_full(
original_pid = getpid_cached();
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
/* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
* be sure that SIGTERMs are not lost we might send to the child. */
if (sigfillset(&ss) < 0)
return log_full_errno(prio, errno, "Failed to reset signal set: %m");
assert_se(sigfillset(&ss) >= 0);
block_signals = true;
} else if (flags & FORK_WAIT) {
/* Let's block SIGCHLD at least, so that we can safely watch for the child process */
if (sigemptyset(&ss) < 0)
return log_full_errno(prio, errno, "Failed to clear signal set: %m");
if (sigaddset(&ss, SIGCHLD) < 0)
return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m");
assert_se(sigemptyset(&ss) >= 0);
assert_se(sigaddset(&ss, SIGCHLD) >= 0);
block_signals = true;
}
@ -1410,6 +1407,14 @@ int safe_fork_full(
}
}
if (flags & FORK_RLIMIT_NOFILE_SAFE) {
r = rlimit_nofile_safe();
if (r < 0) {
log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m");
_exit(EXIT_FAILURE);
}
}
if (ret_pid)
*ret_pid = getpid_cached();
@ -1521,6 +1526,8 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret
safe_close_above_stdio(fd);
}
(void) rlimit_nofile_safe();
/* Count arguments */
va_start(ap, path);
for (n = 0; va_arg(ap, char*); n++)

View file

@ -144,15 +144,16 @@ void reset_cached_pid(void);
int must_be_root(void);
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0,
FORK_CLOSE_ALL_FDS = 1 << 1,
FORK_DEATHSIG = 1 << 2,
FORK_NULL_STDIO = 1 << 3,
FORK_REOPEN_LOG = 1 << 4,
FORK_LOG = 1 << 5,
FORK_WAIT = 1 << 6,
FORK_NEW_MOUNTNS = 1 << 7,
FORK_MOUNTNS_SLAVE = 1 << 8,
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */
FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */
FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */
FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */
FORK_WAIT = 1 << 6, /* Wait until child exited */
FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */
FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */
FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);

View file

@ -9,7 +9,6 @@
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/random.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

View file

@ -9,13 +9,11 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
return NULL;
return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL);
}
static inline Set *set_free_free(Set *s) {
internal_hashmap_free_free(HASHMAP_BASE(s));
return NULL;
return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL);
}
/* no set_free_free_free */
@ -76,11 +74,11 @@ static inline unsigned set_buckets(Set *s) {
bool set_iterate(Set *s, Iterator *i, void **value);
static inline void set_clear(Set *s) {
internal_hashmap_clear(HASHMAP_BASE(s));
internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
}
static inline void set_clear_free(Set *s) {
internal_hashmap_clear_free(HASHMAP_BASE(s));
internal_hashmap_clear(HASHMAP_BASE(s), free, NULL);
}
/* no set_clear_free_free */

View file

@ -17,6 +17,7 @@
#include <unistd.h>
#include "alloc-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@ -114,7 +115,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
size_t l;
l = strlen(s+1);
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
* when parsing, even though abstract namespace sockets
* explicitly allow embedded NUL bytes and don't consider
* them special. But it's simply annoying to debug such
@ -264,9 +265,12 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
return 0;
}
int socket_address_verify(const SocketAddress *a) {
int socket_address_verify(const SocketAddress *a, bool strict) {
assert(a);
/* With 'strict' we enforce additional sanity constraints which are not set by the standard,
* but should only apply to sockets we create ourselves. */
switch (socket_address_family(a)) {
case AF_INET:
@ -296,19 +300,20 @@ int socket_address_verify(const SocketAddress *a) {
case AF_UNIX:
if (a->size < offsetof(struct sockaddr_un, sun_path))
return -EINVAL;
if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will
* append a NUL byte if we have path sockets that are above
* sun_path' full size */
if (a->size > sizeof(struct sockaddr_un) + !strict)
/* If !strict, allow one extra byte, since getsockname() on Linux will append
* a NUL byte if we have path sockets that are above sun_path's full size. */
return -EINVAL;
if (a->size > offsetof(struct sockaddr_un, sun_path) &&
a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */
a->sockaddr.un.sun_path[0] != 0 &&
strict) {
/* Only validate file system sockets here, and only in strict mode */
const char *e;
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
if (e) {
/* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */
/* If there's an embedded NUL byte, make sure the size of the socket address matches it */
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
return -EINVAL;
} else {
@ -356,7 +361,10 @@ int socket_address_print(const SocketAddress *a, char **ret) {
assert(a);
assert(ret);
r = socket_address_verify(a);
r = socket_address_verify(a, false); /* We do non-strict validation, because we want to be
* able to pretty-print any socket the kernel considers
* valid. We still need to do validation to know if we
* can meaningfully print the address. */
if (r < 0)
return r;
@ -389,8 +397,8 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
assert(b);
/* Invalid addresses are unequal to all */
if (socket_address_verify(a) < 0 ||
socket_address_verify(b) < 0)
if (socket_address_verify(a, false) < 0 ||
socket_address_verify(b, false) < 0)
return false;
if (a->type != b->type)
@ -574,7 +582,13 @@ int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
}
}
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
int sockaddr_pretty(
const struct sockaddr *_sa,
socklen_t salen,
bool translate_ipv6,
bool include_port,
char **ret) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
char *p;
int r;
@ -645,42 +659,51 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
}
case AF_UNIX:
if (salen <= offsetof(struct sockaddr_un, sun_path)) {
if (salen <= offsetof(struct sockaddr_un, sun_path) ||
(sa->un.sun_path[0] == 0 && salen == offsetof(struct sockaddr_un, sun_path) + 1))
/* The name must have at least one character (and the leading NUL does not count) */
p = strdup("<unnamed>");
if (!p)
return -ENOMEM;
else {
/* Note that we calculate the path pointer here through the .un_buffer[] field, in order to
* outtrick bounds checking tools such as ubsan, which are too smart for their own good: on
* Linux the kernel may return sun_path[] data one byte longer than the declared size of the
* field. */
char *path = (char*) sa->un_buffer + offsetof(struct sockaddr_un, sun_path);
size_t path_len = salen - offsetof(struct sockaddr_un, sun_path);
} else if (sa->un.sun_path[0] == 0) {
/* abstract */
if (path[0] == 0) {
/* Abstract socket. When parsing address information from, we
* explicitly reject overly long paths and paths with embedded NULs.
* But we might get such a socket from the outside. Let's return
* something meaningful and printable in this case. */
/* FIXME: We assume we can print the
* socket path here and that it hasn't
* more than one NUL byte. That is
* actually an invalid assumption */
_cleanup_free_ char *e = NULL;
p = new(char, sizeof(sa->un.sun_path)+1);
if (!p)
return -ENOMEM;
e = cescape_length(path + 1, path_len - 1);
if (!e)
return -ENOMEM;
p[0] = '@';
memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
p[sizeof(sa->un.sun_path)] = 0;
p = strjoin("@", e);
} else {
if (path[path_len - 1] == '\0')
/* We expect a terminating NUL and don't print it */
path_len --;
} else {
p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
if (!p)
return -ENOMEM;
p = cescape_length(path, path_len);
}
}
if (!p)
return -ENOMEM;
break;
case AF_VSOCK:
if (include_port)
r = asprintf(&p,
"vsock:%u:%u",
sa->vm.svm_cid,
sa->vm.svm_port);
else
if (include_port) {
if (sa->vm.svm_cid == VMADDR_CID_ANY)
r = asprintf(&p, "vsock::%u", sa->vm.svm_port);
else
r = asprintf(&p, "vsock:%u:%u", sa->vm.svm_cid, sa->vm.svm_port);
} else
r = asprintf(&p, "vsock:%u", sa->vm.svm_cid);
if (r < 0)
return -ENOMEM;

View file

@ -1,6 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <inttypes.h>
#include <linux/netlink.h>
#include <linux/if_infiniband.h>
#include <linux/if_packet.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <stdbool.h>
@ -8,13 +12,10 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <linux/netlink.h>
#include <linux/if_infiniband.h>
#include <linux/if_packet.h>
#include "macro.h"
#include "missing.h"
#include "util.h"
#include "missing_socket.h"
#include "sparse-endian.h"
union sockaddr_union {
/* The minimal, abstract version */
@ -72,7 +73,7 @@ int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
int socket_address_verify(const SocketAddress *a) _pure_;
int socket_address_verify(const SocketAddress *a, bool strict) _pure_;
int sockaddr_un_unlink(const struct sockaddr_un *sa);

View file

@ -12,11 +12,13 @@
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
#include "stat-util.h"
#include "string-util.h"
@ -300,3 +302,124 @@ int fd_verify_regular(int fd) {
return stat_verify_regular(&st);
}
#if 0 /* NM_IGNORED */
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);
}
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
const char *t;
/* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
if (S_ISCHR(mode))
t = "char";
else if (S_ISBLK(mode))
t = "block";
else
return -ENODEV;
if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
return -ENOMEM;
return 0;
}
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
/* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
assert(ret);
if (major(devno) == 0 && minor(devno) == 0) {
char *s;
/* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
* /dev/block/ and /dev/char/, hence we handle them specially here. */
if (S_ISCHR(mode))
s = strdup("/run/systemd/inaccessible/chr");
else if (S_ISBLK(mode))
s = strdup("/run/systemd/inaccessible/blk");
else
return -ENODEV;
if (!s)
return -ENOMEM;
*ret = s;
return 0;
}
r = device_path_make_major_minor(mode, devno, &p);
if (r < 0)
return r;
return chase_symlinks(p, NULL, 0, ret);
}
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
mode_t mode;
dev_t devno;
int r;
/* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
* paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
* path cannot be parsed like this. */
if (path_equal(path, "/run/systemd/inaccessible/chr")) {
mode = S_IFCHR;
devno = makedev(0, 0);
} else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
mode = S_IFBLK;
devno = makedev(0, 0);
} else {
const char *w;
w = path_startswith(path, "/dev/block/");
if (w)
mode = S_IFBLK;
else {
w = path_startswith(path, "/dev/char/");
if (!w)
return -ENODEV;
mode = S_IFCHR;
}
r = parse_dev(w, &devno);
if (r < 0)
return r;
}
if (ret_mode)
*ret_mode = mode;
if (ret_devno)
*ret_devno = devno;
return 0;
}
#endif /* NM_IGNORED */

View file

@ -59,3 +59,29 @@ int path_is_temporary_fs(const char *path);
int stat_verify_regular(const struct stat *st);
int fd_verify_regular(int fd);
int stat_verify_directory(const struct stat *st);
int fd_verify_directory(int fd);
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
* such a test would be pointless in such a case.) */
#define DEVICE_MAJOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
_x >= _y && _x < (UINT32_C(1) << 12); \
\
})
#define DEVICE_MINOR_VALID(x) \
({ \
typeof(x) _x = (x), _y = 0; \
_x >= _y && _x < (UINT32_C(1) << 20); \
})
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);

View file

@ -144,6 +144,18 @@ void strv_print(char **l);
_x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
})
#define STARTSWITH_SET(p, ...) \
({ \
const char *_p = (p); \
char *_found = NULL, **_i; \
STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
_found = startswith(_p, *_i); \
if (_found) \
break; \
} \
_found; \
})
#define FOREACH_STRING(x, ...) \
for (char **_l = ({ \
char **_ll = STRV_MAKE(__VA_ARGS__); \

View file

@ -22,6 +22,7 @@
#include "io-util.h"
#include "log.h"
#include "macro.h"
#include "missing_timerfd.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
@ -1391,9 +1392,7 @@ int get_timezone(char **tz) {
if (r < 0)
return r; /* returns EINVAL if not a symlink */
e = path_startswith(t, "/usr/share/zoneinfo/");
if (!e)
e = path_startswith(t, "../usr/share/zoneinfo/");
e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
if (!e)
return -EINVAL;

View file

@ -0,0 +1,335 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "nm-sd-adapt.h"
#include <sys/mman.h>
#include "alloc-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "memfd-util.h"
#include "missing_syscall.h"
#include "path-util.h"
#include "process-util.h"
#include "random-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "tmpfile-util.h"
#include "umask-util.h"
int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
FILE *f;
char *t;
int r, fd;
assert(path);
assert(_f);
assert(_temp_path);
r = tempfn_xxxxxx(path, NULL, &t);
if (r < 0)
return r;
fd = mkostemp_safe(t);
if (fd < 0) {
free(t);
return -errno;
}
f = fdopen(fd, "w");
if (!f) {
unlink_noerrno(t);
free(t);
safe_close(fd);
return -errno;
}
*_f = f;
*_temp_path = t;
return 0;
}
/* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern) {
_cleanup_umask_ mode_t u = 0;
int fd;
assert(pattern);
u = umask(077);
fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
return fd;
}
#if 0 /* NM_IGNORED */
int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
int fd;
FILE *f;
fd = mkostemp_safe(pattern);
if (fd < 0)
return fd;
f = fdopen(fd, mode);
if (!f) {
safe_close(fd);
return -errno;
}
*ret_f = f;
return 0;
}
#endif /* NM_IGNORED */
int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
const char *fn;
char *t;
assert(ret);
if (isempty(p))
return -EINVAL;
if (path_equal(p, "/"))
return -EINVAL;
/*
* Turns this:
* /foo/bar/waldo
*
* Into this:
* /foo/bar/.#<extra>waldoXXXXXX
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
if (!t)
return -ENOMEM;
strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
*ret = path_simplify(t, false);
return 0;
}
#if 0 /* NM_IGNORED */
int tempfn_random(const char *p, const char *extra, char **ret) {
const char *fn;
char *t, *x;
uint64_t u;
unsigned i;
assert(ret);
if (isempty(p))
return -EINVAL;
if (path_equal(p, "/"))
return -EINVAL;
/*
* Turns this:
* /foo/bar/waldo
*
* Into this:
* /foo/bar/.#<extra>waldobaa2a261115984a9
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
if (!t)
return -ENOMEM;
x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
u = random_u64();
for (i = 0; i < 16; i++) {
*(x++) = hexchar(u & 0xF);
u >>= 4;
}
*x = 0;
*ret = path_simplify(t, false);
return 0;
}
int tempfn_random_child(const char *p, const char *extra, char **ret) {
char *t, *x;
uint64_t u;
unsigned i;
int r;
assert(ret);
/* Turns this:
* /foo/bar/waldo
* Into this:
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
*/
if (!p) {
r = tmp_dir(&p);
if (r < 0)
return r;
}
extra = strempty(extra);
t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
if (!t)
return -ENOMEM;
if (isempty(p))
x = stpcpy(stpcpy(t, ".#"), extra);
else
x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
u = random_u64();
for (i = 0; i < 16; i++) {
*(x++) = hexchar(u & 0xF);
u >>= 4;
}
*x = 0;
*ret = path_simplify(t, false);
return 0;
}
int open_tmpfile_unlinkable(const char *directory, int flags) {
char *p;
int fd, r;
if (!directory) {
r = tmp_dir(&directory);
if (r < 0)
return r;
} else if (isempty(directory))
return -EINVAL;
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
/* Try O_TMPFILE first, if it is supported */
fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
/* Fall back to unguessable name + unlinking */
p = strjoina(directory, "/systemd-tmp-XXXXXX");
fd = mkostemp_safe(p);
if (fd < 0)
return fd;
(void) unlink(p);
return fd;
}
int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
_cleanup_free_ char *tmp = NULL;
int r, fd;
assert(target);
assert(ret_path);
/* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
assert((flags & O_EXCL) == 0);
/* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE in
* which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
* "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
fd = open_parent(target, O_TMPFILE|flags, 0640);
if (fd >= 0) {
*ret_path = NULL;
return fd;
}
log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
r = tempfn_random(target, NULL, &tmp);
if (r < 0)
return r;
fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
if (fd < 0)
return -errno;
*ret_path = TAKE_PTR(tmp);
return fd;
}
int link_tmpfile(int fd, const char *path, const char *target) {
int r;
assert(fd >= 0);
assert(target);
/* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
* created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
* on the directory, and renameat2() is used instead.
*
* Note that in both cases we will not replace existing files. This is because linkat() does not support this
* operation currently (renameat2() does), and there is no nice way to emulate this. */
if (path) {
r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
if (r < 0)
return r;
} else {
char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
return -errno;
}
return 0;
}
int mkdtemp_malloc(const char *template, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
assert(ret);
if (template)
p = strdup(template);
else {
const char *tmp;
r = tmp_dir(&tmp);
if (r < 0)
return r;
p = strjoin(tmp, "/XXXXXX");
}
if (!p)
return -ENOMEM;
if (!mkdtemp(p))
return -errno;
*ret = TAKE_PTR(p);
return 0;
}
#endif /* NM_IGNORED */

View file

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <stdio.h>
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
int mkostemp_safe(char *pattern);
int fmkostemp_safe(char *pattern, const char *mode, FILE**_f);
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
int tempfn_random(const char *p, const char *extra, char **ret);
int tempfn_random_child(const char *p, const char *extra, char **ret);
int open_tmpfile_unlinkable(const char *directory, int flags);
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
int link_tmpfile(int fd, const char *path, const char *target);
int mkdtemp_malloc(const char *template, char **ret);

View file

@ -9,7 +9,7 @@
#endif /* NM_IGNORED */
#include "macro.h"
#include "missing.h"
#include "missing_type.h"
#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf"

View file

@ -25,6 +25,7 @@
#include "def.h"
#include "device-nodes.h"
#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"

View file

@ -25,7 +25,6 @@
#include "format-util.h"
#include "macro.h"
#include "missing.h"
#include "time-util.h"
size_t page_size(void) _pure_;
@ -148,6 +147,12 @@ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
return memcmp(s1, s2, n);
}
/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */
static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) {
return memcmp_safe(s1, s2, MIN(n1, n2))
?: CMP(n1, n2);
}
int on_ac_power(void);
#define memzero(x,l) \
@ -173,7 +178,7 @@ static inline void _reset_errno_(int *saved_errno) {
}
#define PROTECT_ERRNO \
_cleanup_(_reset_errno_) __attribute__((__unused__)) int _saved_errno_ = errno
_cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
static inline int negative_errno(void) {
/* This helper should be used to shut up gcc if you know 'errno' is
@ -194,7 +199,7 @@ static inline unsigned u64log2(uint64_t n) {
static inline unsigned u32ctz(uint32_t n) {
#if __SIZEOF_INT__ == 4
return __builtin_ctz(n);
return n != 0 ? __builtin_ctz(n) : 32;
#else
#error "Wut?"
#endif

View file

@ -9,11 +9,11 @@
#include "in-addr-util.h"
#include "lldp-internal.h"
#include "lldp-neighbor.h"
#include "missing.h"
#include "unaligned.h"
#include "util.h"
static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
const LLDPNeighborID *id = p;
static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
siphash24_compress(id->chassis_id, id->chassis_id_size, state);
siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
siphash24_compress(id->port_id, id->port_id_size, state);
@ -21,27 +21,12 @@ static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) {
}
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
int r;
r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size));
if (r != 0)
return r;
r = CMP(x->chassis_id_size, y->chassis_id_size);
if (r != 0)
return r;
r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size));
if (r != 0)
return r;
return CMP(x->port_id_size, y->port_id_size);
return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
}
const struct hash_ops lldp_neighbor_id_hash_ops = {
.hash = lldp_neighbor_id_hash_func,
.compare = (__compar_fn_t) lldp_neighbor_id_compare_func,
};
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
sd_lldp_neighbor, lldp_neighbor_unlink);
int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
const sd_lldp_neighbor *x = a, *y = b;
@ -99,7 +84,12 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
if (!n->lldp)
return NULL;
assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n);
/* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
* because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
* ourselves from the hashtable and sometimes are called after we already are de-registered. */
(void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
n->lldp = NULL;

View file

@ -80,7 +80,7 @@ static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) {
return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2;
}
extern const struct hash_ops lldp_neighbor_id_hash_ops;
extern const struct hash_ops lldp_neighbor_hash_ops;
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y);
int lldp_neighbor_prioq_compare_func(const void *a, const void *b);

View file

@ -7,6 +7,7 @@
#include "fd-util.h"
#include "lldp-network.h"
#include "missing.h"
#include "socket-util.h"
int lldp_network_bind_raw_socket(int ifindex) {

View file

@ -374,36 +374,6 @@ int config_parse_hwaddrs(const char *unit,
return 0;
}
int config_parse_iaid(const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
uint32_t iaid;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = safe_atou32(rvalue, &iaid);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Unable to read IAID, ignoring assignment: %s", rvalue);
return 0;
}
*((uint32_t *)data) = iaid;
return 0;
}
int config_parse_bridge_port_priority(
const char *unit,
const char *filename,

View file

@ -36,7 +36,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority);
int net_get_unique_predictable_data(sd_device *device, uint64_t *result);

View file

@ -25,10 +25,11 @@
#include "dns-domain.h"
#include "event-util.h"
#include "hostname-util.h"
#include "io-util.h"
#include "random-util.h"
#include "string-util.h"
#include "util.h"
#include "strv.h"
#include "util.h"
#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
@ -343,8 +344,9 @@ int sd_dhcp_client_set_client_id(
*/
static int dhcp_client_set_iaid_duid_internal(
sd_dhcp_client *client,
bool iaid_append,
bool iaid_set,
uint32_t iaid,
bool append_iaid,
uint16_t duid_type,
const void *duid,
size_t duid_len,
@ -366,17 +368,17 @@ static int dhcp_client_set_iaid_duid_internal(
zero(client->client_id);
client->client_id.type = 255;
if (append_iaid) {
/* If IAID is not configured, generate it. */
if (iaid == 0) {
if (iaid_append) {
if (iaid_set)
client->client_id.ns.iaid = htobe32(iaid);
else {
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
client->mac_addr_len,
true,
&client->client_id.ns.iaid);
if (r < 0)
return r;
} else
client->client_id.ns.iaid = htobe32(iaid);
}
}
if (duid != NULL) {
@ -416,10 +418,10 @@ static int dhcp_client_set_iaid_duid_internal(
}
client->client_id_len = sizeof(client->client_id.type) + len +
(append_iaid ? sizeof(client->client_id.ns.iaid) : 0);
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : "");
log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
sd_dhcp_client_start(client);
}
@ -429,18 +431,20 @@ static int dhcp_client_set_iaid_duid_internal(
int sd_dhcp_client_set_iaid_duid(
sd_dhcp_client *client,
bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
const void *duid,
size_t duid_len) {
return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0);
return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
}
int sd_dhcp_client_set_iaid_duid_llt(
sd_dhcp_client *client,
bool iaid_set,
uint32_t iaid,
usec_t llt_time) {
return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time);
return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
}
int sd_dhcp_client_set_duid(
@ -448,13 +452,13 @@ int sd_dhcp_client_set_duid(
uint16_t duid_type,
const void *duid,
size_t duid_len) {
return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0);
return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
}
int sd_dhcp_client_set_duid_llt(
sd_dhcp_client *client,
usec_t llt_time) {
return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time);
return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
}
#endif /* NM_IGNORED */
@ -1784,8 +1788,7 @@ static int client_receive_message_raw(
if (!packet)
return -ENOMEM;
iov.iov_base = packet;
iov.iov_len = buflen;
iov = IOVEC_MAKE(packet, buflen);
len = recvmsg(fd, &msg, 0);
if (len < 0) {

View file

@ -18,6 +18,7 @@
#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
#include "dns-domain.h"
#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
#include "hexdecoct.h"
@ -28,6 +29,7 @@
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
#include "unaligned.h"
int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
@ -355,7 +357,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
return 0;
}
r = dns_name_normalize(name, &normalized);
r = dns_name_normalize(name, 0, &normalized);
if (r < 0)
return r;

View file

@ -58,7 +58,7 @@ struct sd_dhcp6_client {
struct sd_dhcp6_lease *lease;
int fd;
bool information_request;
bool has_iaid;
bool iaid_set;
be16_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_len;
@ -278,7 +278,7 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
client->ia_na.ia_na.id = htobe32(iaid);
client->ia_pd.ia_pd.id = htobe32(iaid);
client->has_iaid = true;
client->iaid_set = true;
return 0;
}
@ -802,7 +802,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
assert(client);
if (client->has_iaid)
if (client->iaid_set)
return 0;
r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
@ -811,7 +811,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
client->ia_na.ia_na.id = iaid;
client->ia_pd.ia_pd.id = iaid;
client->has_iaid = true;
client->iaid_set = true;
return 0;
}

View file

@ -4,6 +4,7 @@
#include <arpa/inet.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include "sd-lldp.h"
@ -29,12 +30,9 @@ static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
static void lldp_flush_neighbors(sd_lldp *lldp) {
sd_lldp_neighbor *n;
assert(lldp);
while ((n = hashmap_first(lldp->neighbor_by_id)))
lldp_neighbor_unlink(n);
hashmap_clear(lldp->neighbor_by_id);
}
static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
@ -377,7 +375,7 @@ _public_ int sd_lldp_new(sd_lldp **ret) {
.capability_mask = (uint16_t) -1,
};
lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops);
lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
if (!lldp->neighbor_by_id)
return -ENOMEM;

View file

@ -1375,8 +1375,7 @@ static int event_make_inotify_data(
return 1;
}
static int inode_data_compare(const void *a, const void *b) {
const struct inode_data *x = a, *y = b;
static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) {
int r;
assert(x);
@ -1389,19 +1388,14 @@ static int inode_data_compare(const void *a, const void *b) {
return CMP(x->ino, y->ino);
}
static void inode_data_hash_func(const void *p, struct siphash *state) {
const struct inode_data *d = p;
assert(p);
static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
assert(d);
siphash24_compress(&d->dev, sizeof(d->dev), state);
siphash24_compress(&d->ino, sizeof(d->ino), state);
}
const struct hash_ops inode_data_hash_ops = {
.hash = inode_data_hash_func,
.compare = inode_data_compare
};
DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
static void event_free_inode_data(
sd_event *e,

View file

@ -187,16 +187,13 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) {
return id128_write_fd(fd, f, id, do_sync);
}
void id128_hash_func(const void *p, struct siphash *state) {
siphash24_compress(p, 16, state);
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(sd_id128_t), state);
}
int id128_compare_func(const void *a, const void *b) {
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
return memcmp(a, b, 16);
}
const struct hash_ops id128_hash_ops = {
.hash = id128_hash_func,
.compare = id128_compare_func,
};
DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
#endif /* NM_IGNORED */

View file

@ -28,6 +28,6 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
void id128_hash_func(const void *p, struct siphash *state);
int id128_compare_func(const void *a, const void *b) _pure_;
void id128_hash_func(const sd_id128_t *p, struct siphash *state);
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
extern const struct hash_ops id128_hash_ops;

View file

@ -21,6 +21,7 @@
#include "dns-domain.h"
#include "hashmap.h"
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "parse-util.h"
@ -28,9 +29,9 @@
#include "strv.h"
#include "utf8.h"
int dns_label_unescape(const char **name, char *dest, size_t sz) {
int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
const char *n;
char *d;
char *d, last_char = 0;
int r = 0;
assert(name);
@ -40,14 +41,16 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
d = dest;
for (;;) {
if (*n == '.') {
n++;
if (*n == 0 || *n == '.') {
if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
/* Trailing dash */
return -EINVAL;
if (*n == '.')
n++;
break;
}
if (*n == 0)
break;
if (r >= DNS_LABEL_MAX)
return -EINVAL;
@ -56,6 +59,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
if (*n == '\\') {
/* Escaped character */
if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
return -EINVAL;
n++;
@ -66,6 +71,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
else if (IN_SET(*n, '\\', '.')) {
/* Escaped backslash or dot */
if (FLAGS_SET(flags, DNS_LABEL_LDH))
return -EINVAL;
last_char = *n;
if (d)
*(d++) = *n;
sz--;
@ -94,6 +103,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
if (k > 255)
return -EINVAL;
if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
!valid_ldh_char((char) k))
return -EINVAL;
last_char = (char) k;
if (d)
*(d++) = (char) k;
sz--;
@ -107,6 +121,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) {
/* Normal character */
if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
if (!valid_ldh_char(*n))
return -EINVAL;
if (r == 0 && *n == '-')
/* Leading dash */
return -EINVAL;
}
last_char = *n;
if (d)
*(d++) = *n;
sz--;
@ -189,7 +212,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
terminal--;
}
r = dns_label_unescape(&name, dest, sz);
r = dns_label_unescape(&name, dest, sz, 0);
if (r < 0)
return r;
@ -386,7 +409,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
#endif
#endif /* NM_IGNORED */
int dns_name_concat(const char *a, const char *b, char **_ret) {
int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
const char *p;
@ -403,7 +426,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
for (;;) {
char label[DNS_LABEL_MAX];
r = dns_label_unescape(&p, label, sizeof(label));
r = dns_label_unescape(&p, label, sizeof label, flags);
if (r < 0)
return r;
if (r == 0) {
@ -469,8 +492,7 @@ finish:
}
#if 0 /* NM_IGNORED */
void dns_name_hash_func(const void *s, struct siphash *state) {
const char *p = s;
void dns_name_hash_func(const char *p, struct siphash *state) {
int r;
assert(p);
@ -478,7 +500,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
for (;;) {
char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, label, sizeof(label));
r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
break;
if (r == 0)
@ -493,15 +515,15 @@ void dns_name_hash_func(const void *s, struct siphash *state) {
string_hash_func("", state);
}
int dns_name_compare_func(const void *a, const void *b) {
int dns_name_compare_func(const char *a, const char *b) {
const char *x, *y;
int r, q;
assert(a);
assert(b);
x = (const char *) a + strlen(a);
y = (const char *) b + strlen(b);
x = a + strlen(a);
y = b + strlen(b);
for (;;) {
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
@ -520,10 +542,7 @@ int dns_name_compare_func(const void *a, const void *b) {
}
}
const struct hash_ops dns_name_hash_ops = {
.hash = dns_name_hash_func,
.compare = dns_name_compare_func
};
DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
int dns_name_equal(const char *x, const char *y) {
int r, q;
@ -534,11 +553,11 @@ int dns_name_equal(const char *x, const char *y) {
for (;;) {
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
r = dns_label_unescape(&x, la, sizeof(la));
r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
return r;
q = dns_label_unescape(&y, lb, sizeof(lb));
q = dns_label_unescape(&y, lb, sizeof lb, 0);
if (q < 0)
return q;
@ -565,14 +584,14 @@ int dns_name_endswith(const char *name, const char *suffix) {
for (;;) {
char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
r = dns_label_unescape(&n, ln, sizeof(ln));
r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
return r;
if (!saved_n)
saved_n = n;
q = dns_label_unescape(&s, ls, sizeof(ls));
q = dns_label_unescape(&s, ls, sizeof ls, 0);
if (q < 0)
return q;
@ -603,13 +622,13 @@ int dns_name_startswith(const char *name, const char *prefix) {
for (;;) {
char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
r = dns_label_unescape(&p, lp, sizeof(lp));
r = dns_label_unescape(&p, lp, sizeof lp, 0);
if (r < 0)
return r;
if (r == 0)
return true;
q = dns_label_unescape(&n, ln, sizeof(ln));
q = dns_label_unescape(&n, ln, sizeof ln, 0);
if (q < 0)
return q;
@ -638,14 +657,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
if (!saved_before)
saved_before = n;
r = dns_label_unescape(&n, ln, sizeof(ln));
r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
return r;
if (!saved_after)
saved_after = n;
q = dns_label_unescape(&s, ls, sizeof(ls));
q = dns_label_unescape(&s, ls, sizeof ls, 0);
if (q < 0)
return q;
@ -668,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
/* Found it! Now generate the new name */
prefix = strndupa(name, saved_before - name);
r = dns_name_concat(prefix, new_suffix, ret);
r = dns_name_concat(prefix, new_suffix, 0, ret);
if (r < 0)
return r;
@ -746,7 +765,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
for (i = 0; i < ELEMENTSOF(a); i++) {
char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, label, sizeof(label));
r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
return r;
if (r == 0)
@ -783,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
char label[DNS_LABEL_MAX+1];
int x, y;
r = dns_label_unescape(&p, label, sizeof(label));
r = dns_label_unescape(&p, label, sizeof label, 0);
if (r <= 0)
return r;
if (r != 1)
@ -792,7 +811,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) {
if (x < 0)
return -EINVAL;
r = dns_label_unescape(&p, label, sizeof(label));
r = dns_label_unescape(&p, label, sizeof label, 0);
if (r <= 0)
return r;
if (r != 1)
@ -861,7 +880,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo
* dns_label_unescape() returns 0 when it hits the end
* of the domain name, which we rely on here to encode
* the trailing NUL byte. */
r = dns_label_unescape(&domain, (char *) out, len);
r = dns_label_unescape(&domain, (char *) out, len, 0);
if (r < 0)
return r;
@ -929,7 +948,7 @@ bool dns_srv_type_is_valid(const char *name) {
/* This more or less implements RFC 6335, Section 5.1 */
r = dns_label_unescape(&name, label, sizeof(label));
r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
return false;
if (r == 0)
@ -989,7 +1008,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
return -EINVAL;
if (!name)
return dns_name_concat(type, domain, ret);
return dns_name_concat(type, domain, 0, ret);
if (!dns_service_name_is_valid(name))
return -EINVAL;
@ -998,11 +1017,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha
if (r < 0)
return r;
r = dns_name_concat(type, domain, &n);
r = dns_name_concat(type, domain, 0, &n);
if (r < 0)
return r;
return dns_name_concat(escaped, n, ret);
return dns_name_concat(escaped, n, 0, ret);
}
static bool dns_service_name_label_is_valid(const char *label, size_t n) {
@ -1027,7 +1046,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
assert(joined);
/* Get first label from the full name */
an = dns_label_unescape(&p, a, sizeof(a));
an = dns_label_unescape(&p, a, sizeof(a), 0);
if (an < 0)
return an;
@ -1035,7 +1054,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
x++;
/* If there was a first label, try to get the second one */
bn = dns_label_unescape(&p, b, sizeof(b));
bn = dns_label_unescape(&p, b, sizeof(b), 0);
if (bn < 0)
return bn;
@ -1044,7 +1063,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
/* If there was a second label, try to get the third one */
q = p;
cn = dns_label_unescape(&p, c, sizeof(c));
cn = dns_label_unescape(&p, c, sizeof(c), 0);
if (cn < 0)
return cn;
@ -1094,7 +1113,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
d = joined;
finish:
r = dns_name_normalize(d, &domain);
r = dns_name_normalize(d, 0, &domain);
if (r < 0)
return r;
@ -1110,7 +1129,7 @@ finish:
return 0;
}
static int dns_name_build_suffix_table(const char *name, const char*table[]) {
static int dns_name_build_suffix_table(const char *name, const char *table[]) {
const char *p;
unsigned n = 0;
int r;
@ -1239,12 +1258,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
}
x = a_labels[n - 1 - k];
r = dns_label_unescape(&x, la, sizeof(la));
r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
return r;
y = b_labels[m - 1 - k];
q = dns_label_unescape(&y, lb, sizeof(lb));
q = dns_label_unescape(&y, lb, sizeof lb, 0);
if (q < 0)
return q;
@ -1312,13 +1331,13 @@ int dns_name_apply_idna(const char *name, char **ret) {
for (;;) {
char label[DNS_LABEL_MAX];
r = dns_label_unescape(&name, label, sizeof(label));
r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
return r;
if (r == 0)
break;
q = dns_label_apply_idna(label, r, label, sizeof(label));
q = dns_label_apply_idna(label, r, label, sizeof label);
if (q < 0)
return q;
if (q > 0)

View file

@ -24,13 +24,18 @@
/* Maximum number of labels per valid hostname */
#define DNS_N_LABELS_MAX 127
int dns_label_unescape(const char **name, char *dest, size_t sz);
typedef enum DNSLabelFlags {
DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */
DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */
} DNSLabelFlags;
int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags);
int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz);
int dns_label_escape(const char *p, size_t l, char *dest, size_t sz);
int dns_label_escape_new(const char *p, size_t l, char **ret);
static inline int dns_name_parent(const char **name) {
return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0);
}
#if 0 /* NM_IGNORED */
@ -40,18 +45,18 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
#endif
#endif /* NM_IGNORED */
int dns_name_concat(const char *a, const char *b, char **ret);
int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret);
static inline int dns_name_normalize(const char *s, char **ret) {
static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) {
/* dns_name_concat() normalizes as a side-effect */
return dns_name_concat(s, NULL, ret);
return dns_name_concat(s, NULL, flags, ret);
}
static inline int dns_name_is_valid(const char *s) {
int r;
/* dns_name_normalize() verifies as a side effect */
r = dns_name_normalize(s, NULL);
r = dns_name_normalize(s, 0, NULL);
if (r == -EINVAL)
return 0;
if (r < 0)
@ -59,8 +64,19 @@ static inline int dns_name_is_valid(const char *s) {
return 1;
}
void dns_name_hash_func(const void *s, struct siphash *state);
int dns_name_compare_func(const void *a, const void *b);
static inline int dns_name_is_valid_ldh(const char *s) {
int r;
r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL);
if (r == -EINVAL)
return 0;
if (r < 0)
return r;
return 1;
}
void dns_name_hash_func(const char *s, struct siphash *state);
int dns_name_compare_func(const char *a, const char *b);
extern const struct hash_ops dns_name_hash_ops;
int dns_name_between(const char *a, const char *b, const char *c);

View file

@ -23,9 +23,11 @@
# error "Do not include _sd-common.h directly; it is a private header."
#endif
typedef void (*_sd_destroy_t)(void *userdata);
#ifndef _sd_printf_
# if __GNUC__ >= 4
# define _sd_printf_(a,b) __attribute__ ((__format__(printf, a, b)))
# define _sd_printf_(a,b) __attribute__((__format__(printf, a, b)))
# else
# define _sd_printf_(a,b)
# endif

View file

@ -23,6 +23,7 @@
#include <net/ethernet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <stdbool.h>
#include "sd-dhcp-lease.h"
#include "sd-event.h"
@ -127,12 +128,14 @@ int sd_dhcp_client_set_client_id(
size_t data_len);
int sd_dhcp_client_set_iaid_duid(
sd_dhcp_client *client,
bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
const void *duid,
size_t duid_len);
int sd_dhcp_client_set_iaid_duid_llt(
sd_dhcp_client *client,
bool iaid_set,
uint32_t iaid,
uint64_t llt_time);
int sd_dhcp_client_set_duid(

View file

@ -77,7 +77,7 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si,
typedef void* sd_event_child_handler_t;
#endif
typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata);
typedef void (*sd_event_destroy_t)(void *userdata);
typedef _sd_destroy_t sd_event_destroy_t;
int sd_event_default(sd_event **e);

View file

@ -41,19 +41,20 @@ int sd_id128_from_string(const char *s, sd_id128_t *ret);
int sd_id128_randomize(sd_id128_t *ret);
int sd_id128_get_machine(sd_id128_t *ret);
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }})
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
{ .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}
#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15))
/* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16
* times. It is hence not a good idea to call this macro with an
* expensive function as parameter or an expression with side
@ -109,7 +110,12 @@ _sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
return a.qwords[0] == 0 && a.qwords[1] == 0;
}
_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
}
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
_SD_END_DECLARATIONS;