diff options
author | Thomas Haller <thaller@redhat.com> | 2021-05-30 09:55:36 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2021-06-01 08:51:36 +0200 |
commit | 22dd4baf5478280ab800c0a8019119e449346dca (patch) | |
tree | 6c05769ac809c8043202e72f4bcf3251e4cce91a | |
parent | 8b629dc16a04fc473d8c55eabb38b2495be15315 (diff) | |
parent | e50fb9d70f509db95af97c2e2a20cb516152c1ba (diff) |
systemd: merge branch systemd into mainth/system
76 files changed, 1853 insertions, 1022 deletions
diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c index 0b48660789..5f7f26e58e 100644 --- a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -1246,9 +1246,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin) /* dirname_libs are a set of read-only directories with lower priority than /etc or /run. * There is nothing complicated about having multiple of such directories, so dirname_libs * is a list (which currently only has at most one directory). */ - priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB), FALSE); + priv->dirname_libs[0] = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_LIB)); priv->dirname_libs[1] = NULL; - priv->dirname_run = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN), FALSE); + priv->dirname_run = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_RUN)); priv->dirname_etc = nm_config_data_get_value(NM_CONFIG_GET_DATA_ORIG, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, @@ -1261,10 +1261,9 @@ nms_keyfile_plugin_init(NMSKeyfilePlugin *plugin) } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') { /* either invalid path or unspecified. Use the default. */ g_free(priv->dirname_etc); - priv->dirname_etc = - nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE); + priv->dirname_etc = nm_sd_utils_path_simplify(g_strdup(NM_KEYFILE_PATH_NAME_ETC_DEFAULT)); } else - nm_sd_utils_path_simplify(priv->dirname_etc, FALSE); + nm_sd_utils_path_simplify(priv->dirname_etc); /* no duplicates */ if (NM_IN_STRSET(priv->dirname_libs[0], priv->dirname_etc, priv->dirname_run)) diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c index 3775f9d2e9..f28c4178d3 100644 --- a/src/core/tests/test-core.c +++ b/src/core/tests/test-core.c @@ -2251,7 +2251,7 @@ test_utils_file_is_in_path(void) g_assert(!nm_utils_file_is_in_path("//b///a/", "/b//")); g_assert(!nm_utils_file_is_in_path("//b///a/", "/b/a/")); g_assert(!nm_utils_file_is_in_path("//b///a", "/b/a/")); - g_assert(nm_utils_file_is_in_path("//b///a/.", "/b/a/")); + g_assert(!nm_utils_file_is_in_path("//b///a/.", "/b/a/")); g_assert(nm_utils_file_is_in_path("//b///a/..", "/b/a/")); } diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c index ec795777f6..12d2b911b7 100644 --- a/src/core/tests/test-systemd.c +++ b/src/core/tests/test-systemd.c @@ -122,46 +122,34 @@ test_sd_event(void) static void test_path_equal(void) { -#define _path_equal_check1(path, kill_dots, expected) \ - G_STMT_START \ - { \ - const gboolean _kill_dots = (kill_dots); \ - const char * _path0 = (path); \ - const char * _expected = (expected); \ - gs_free char * _path = g_strdup(_path0); \ - const char * _path_result; \ - \ - if (!_kill_dots && !nm_sd_utils_path_equal(_path0, _expected)) \ - g_error("Paths \"%s\" and \"%s\" don't compare equal", _path0, _expected); \ - \ - _path_result = nm_sd_utils_path_simplify(_path, _kill_dots); \ - g_assert(_path_result == _path); \ - g_assert_cmpstr(_path, ==, _expected); \ - } \ +#define _path_equal_check(path, expected) \ + G_STMT_START \ + { \ + const char * _path0 = (path); \ + const char * _expected = (expected); \ + gs_free char *_path = g_strdup(_path0); \ + const char * _path_result; \ + \ + _path_result = nm_sd_utils_path_simplify(_path); \ + g_assert(_path_result == _path); \ + g_assert_cmpstr(_path, ==, _expected); \ + } \ G_STMT_END -#define _path_equal_check(path, expected_no_kill_dots, expected_kill_dots) \ - G_STMT_START \ - { \ - _path_equal_check1(path, FALSE, expected_no_kill_dots); \ - _path_equal_check1(path, TRUE, expected_kill_dots ?: expected_no_kill_dots); \ - } \ - G_STMT_END - - _path_equal_check("", "", NULL); - _path_equal_check(".", ".", NULL); - _path_equal_check("..", "..", NULL); - _path_equal_check("/..", "/..", NULL); - _path_equal_check("//..", "/..", NULL); - _path_equal_check("/.", "/.", "/"); - _path_equal_check("./", ".", "."); - _path_equal_check("./.", "./.", "."); - _path_equal_check(".///.", "./.", "."); - _path_equal_check(".///./", "./.", "."); - _path_equal_check(".////", ".", "."); - _path_equal_check("//..//foo/", "/../foo", NULL); - _path_equal_check("///foo//./bar/.", "/foo/./bar/.", "/foo/bar"); - _path_equal_check(".//./foo//./bar/.", "././foo/./bar/.", "foo/bar"); + _path_equal_check("", ""); + _path_equal_check(".", "."); + _path_equal_check("..", ".."); + _path_equal_check("/..", "/.."); + _path_equal_check("//..", "/.."); + _path_equal_check("/.", "/"); + _path_equal_check("./", "."); + _path_equal_check("./.", "."); + _path_equal_check(".///.", "."); + _path_equal_check(".///./", "."); + _path_equal_check(".////", "."); + _path_equal_check("//..//foo/", "/../foo"); + _path_equal_check("///foo//./bar/.", "/foo/bar"); + _path_equal_check(".//./foo//./bar/.", "foo/bar"); } /*****************************************************************************/ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h index c5c851c575..d8c42757aa 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h @@ -42,6 +42,8 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, uint8_t code, size_t optlen, const void *optval); +int dhcp_option_find_option(uint8_t *options, size_t length, uint8_t wanted_code, size_t *ret_offset); +int dhcp_option_remove_option(uint8_t *options, size_t buflen, uint8_t option_code); typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len, const void *option, void *userdata); @@ -66,15 +68,13 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui #define DHCP_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client) -#define log_dhcp_client_errno(client, error, fmt, ...) \ - ({ \ - int _e = (error); \ - if (DEBUG_LOGGING) \ - log_interface_full_errno( \ - sd_dhcp_client_get_ifname(client), \ - LOG_DEBUG, _e, "DHCPv4 client: " fmt, \ - ##__VA_ARGS__); \ - -ERRNO_VALUE(_e); \ - }) +#define log_dhcp_client_errno(client, error, fmt, ...) \ + log_interface_prefix_full_errno( \ + "DHCPv4 client: ", \ + sd_dhcp_client_get_ifname(client), \ + error, fmt, ##__VA_ARGS__) #define log_dhcp_client(client, fmt, ...) \ - log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__) + log_interface_prefix_full_errno_zerook( \ + "DHCPv4 client: ", \ + sd_dhcp_client_get_ifname(client), \ + 0, fmt, ##__VA_ARGS__) diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h index 49392d1bea..35a74dd7c2 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h @@ -53,7 +53,7 @@ struct sd_dhcp_lease { DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; struct sd_dhcp_route *static_route; - size_t static_route_size, static_route_allocated; + size_t static_route_size; uint16_t mtu; /* 0 if unset */ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c index 4477f46f2c..bbfdeb0f8c 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c @@ -188,15 +188,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int return r; } - if (address == INADDR_ANY) { - r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); - if (r < 0) - return r; - + if (port == DHCP_PORT_SERVER) { r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true); if (r < 0) return r; - + if (address == INADDR_ANY) { + /* IP_PKTINFO filter should not be applied when packets are + allowed to enter/leave through the interface other than + DHCP server sits on(BindToInterface option). */ + r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); + if (r < 0) + return r; + } } else { r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true); if (r < 0) diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c index a19f55107b..1710f6962b 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c @@ -16,11 +16,33 @@ #include "strv.h" #include "utf8.h" +/* Append type-length value structure to the options buffer */ +static int dhcp_option_append_tlv(uint8_t options[], size_t size, size_t *offset, uint8_t code, size_t optlen, const void *optval) { + assert(options); + assert(size > 0); + assert(offset); + assert(optlen <= UINT8_MAX); + assert(*offset < size); + + if (*offset + 2 + optlen > size) + return -ENOBUFS; + + options[*offset] = code; + options[*offset + 1] = optlen; + + memcpy_safe(&options[*offset + 2], optval, optlen); + *offset += 2 + optlen; + return 0; +} + static int option_append(uint8_t options[], size_t size, size_t *offset, uint8_t code, size_t optlen, const void *optval) { assert(options); + assert(size > 0); assert(offset); + int r; + if (code != SD_DHCP_OPTION_END) /* always make sure there is space for an END option */ size--; @@ -94,32 +116,94 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, options[*offset] = code; options[*offset + 1] = l; - *offset += 2; ORDERED_SET_FOREACH(p, s) { - options[*offset] = p->option; - options[*offset + 1] = p->length; - memcpy(&options[*offset + 2], p->data, p->length); - *offset += 2 + p->length; + r = dhcp_option_append_tlv(options, size, offset, p->option, p->length, p->data); + if (r < 0) + return r; + } + break; + } + case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION: { +#if 0 /* NM_IGNORED */ + sd_dhcp_server *server = (sd_dhcp_server *) optval; + size_t current_offset = *offset + 2; + + if (server->agent_circuit_id) { + r = dhcp_option_append_tlv(options, size, ¤t_offset, SD_DHCP_RELAY_AGENT_CIRCUIT_ID, + strlen(server->agent_circuit_id), server->agent_circuit_id); + if (r < 0) + return r; + } + if (server->agent_remote_id) { + r = dhcp_option_append_tlv(options, size, ¤t_offset, SD_DHCP_RELAY_AGENT_REMOTE_ID, + strlen(server->agent_remote_id), server->agent_remote_id); + if (r < 0) + return r; } + options[*offset] = code; + options[*offset + 1] = current_offset - *offset - 2; + assert(current_offset - *offset - 2 <= UINT8_MAX); + *offset = current_offset; break; +#endif /* NM_IGNORED */ + g_return_val_if_reached(-EINVAL); } default: - if (*offset + 2 + optlen > size) - return -ENOBUFS; + return dhcp_option_append_tlv(options, size, offset, code, optlen, optval); + } + return 0; +} - options[*offset] = code; - options[*offset + 1] = optlen; +static int option_length(uint8_t *options, size_t length, size_t offset) { + assert(options); + assert(offset < length); - memcpy_safe(&options[*offset + 2], optval, optlen); - *offset += 2 + optlen; + if (IN_SET(options[offset], SD_DHCP_OPTION_PAD, SD_DHCP_OPTION_END)) + return 1; + if (length < offset + 2) + return -ENOBUFS; - break; + /* validating that buffer is long enough */ + if (length < offset + 2 + options[offset + 1]) + return -ENOBUFS; + + return options[offset + 1] + 2; +} + +int dhcp_option_find_option(uint8_t *options, size_t length, uint8_t code, size_t *ret_offset) { + int r; + + assert(options); + assert(ret_offset); + + for (size_t offset = 0; offset < length; offset += r) { + r = option_length(options, length, offset); + if (r < 0) + return r; + + if (code == options[offset]) { + *ret_offset = offset; + return r; + } } + return -ENOENT; +} - return 0; +int dhcp_option_remove_option(uint8_t *options, size_t length, uint8_t option_code) { + int r; + size_t offset; + + assert(options); + + r = dhcp_option_find_option(options, length, option_code, &offset); + if (r < 0) + return r; + + memmove(options + offset, options + offset + r, length - offset - r); + return length - r; } int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, @@ -167,7 +251,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, } else if (r == -ENOBUFS && use_sname) { /* did not fit, but we have more buffers to try close the file array and move the offset to its end */ - r = option_append(message->options, size, offset, SD_DHCP_OPTION_END, 0, NULL); + r = option_append(message->file, sizeof(message->file), &file_offset, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) return r; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h index 274b14b056..34e1f61cb0 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h @@ -104,8 +104,7 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, int dhcp6_option_parse_status(DHCP6Option *option, size_t len); int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code); int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count, - size_t *allocated); + struct in6_addr **addrs, size_t count); int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr); int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str); @@ -119,15 +118,13 @@ int dhcp6_message_type_from_string(const char *s) _pure_; const char *dhcp6_message_status_to_string(int s) _const_; int dhcp6_message_status_from_string(const char *s) _pure_; -#define log_dhcp6_client_errno(client, error, fmt, ...) \ - ({ \ - int _e = (error); \ - if (DEBUG_LOGGING) \ - log_interface_full_errno( \ - sd_dhcp6_client_get_ifname(client), \ - LOG_DEBUG, _e, "DHCPv6 client: " fmt, \ - ##__VA_ARGS__); \ - -ERRNO_VALUE(_e); \ - }) -#define log_dhcp6_client(client, fmt, ...) \ - log_dhcp6_client_errno(client, 0, fmt, ##__VA_ARGS__) +#define log_dhcp6_client_errno(client, error, fmt, ...) \ + log_interface_prefix_full_errno( \ + "DHCPv6 client: ", \ + sd_dhcp6_client_get_ifname(client), \ + error, fmt, ##__VA_ARGS__) +#define log_dhcp6_client(client, fmt, ...) \ + log_interface_prefix_full_errno_zerook( \ + "DHCPv6 client: ", \ + sd_dhcp6_client_get_ifname(client), \ + 0, fmt, ##__VA_ARGS__) diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h index e9e2362d6f..391b4f1fa9 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h @@ -27,12 +27,10 @@ struct sd_dhcp6_lease { struct in6_addr *dns; size_t dns_count; - size_t dns_allocated; char **domains; size_t domains_count; struct in6_addr *ntp; size_t ntp_count; - size_t ntp_allocated; char **ntp_fqdn; size_t ntp_fqdn_count; char *fqdn; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c index d50ce3db28..d9e24585f1 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -604,7 +604,7 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I case SD_DHCP6_OPTION_IA_PD_PREFIX: - if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) + if (ia->type != SD_DHCP6_OPTION_IA_PD) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "IA PD Prefix option not in IA PD option"); @@ -680,14 +680,12 @@ int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6I } int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count, - size_t *allocated) { + struct in6_addr **addrs, size_t count) { if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) return -EINVAL; - if (!GREEDY_REALLOC(*addrs, *allocated, - count * sizeof(struct in6_addr) + optlen)) + if (!GREEDY_REALLOC(*addrs, count * sizeof(struct in6_addr) + optlen)) return -ENOMEM; memcpy(*addrs + count, optval, optlen); @@ -699,10 +697,10 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) { _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; const uint8_t *optval = *data; uint16_t optlen = *len; bool first = true; + size_t n = 0; int r; if (optlen <= 1) @@ -732,7 +730,7 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) optval += c; optlen -= c; - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; if (first) @@ -748,7 +746,7 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) } if (n) { - if (!GREEDY_REALLOC(ret, allocated, n + 1)) + if (!GREEDY_REALLOC(ret, n + 1)) return -ENOMEM; ret[n] = 0; } diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-internal.h b/src/libnm-systemd-core/src/libsystemd-network/lldp-internal.h index f13555d35c..cf0578c5c2 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-internal.h @@ -36,15 +36,13 @@ struct sd_lldp { const char* lldp_event_to_string(sd_lldp_event_t e) _const_; sd_lldp_event_t lldp_event_from_string(const char *s) _pure_; -#define log_lldp_errno(lldp, error, fmt, ...) \ - ({ \ - int _e = (error); \ - if (DEBUG_LOGGING) \ - log_interface_full_errno( \ - sd_lldp_get_ifname(lldp), \ - LOG_DEBUG, _e, "LLDP: " fmt, \ - ##__VA_ARGS__); \ - -ERRNO_VALUE(_e); \ - }) -#define log_lldp(lldp, fmt, ...) \ - log_lldp_errno(lldp, 0, fmt, ##__VA_ARGS__) +#define log_lldp_errno(lldp, error, fmt, ...) \ + log_interface_prefix_full_errno( \ + "LLDP: ", \ + sd_lldp_get_ifname(lldp), \ + error, fmt, ##__VA_ARGS__) +#define log_lldp(lldp, fmt, ...) \ + log_interface_prefix_full_errno_zerook( \ + "LLDP: ", \ + sd_lldp_get_ifname(lldp), \ + 0, fmt, ##__VA_ARGS__) diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-internal.c b/src/libnm-systemd-core/src/libsystemd-network/network-internal.c index efa3dcfa4d..60ce47eab1 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-internal.c +++ b/src/libnm-systemd-core/src/libsystemd-network/network-internal.c @@ -160,13 +160,12 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz fputs("\n", f); } -int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) { +int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string) { _cleanup_free_ struct sd_dhcp_route *routes = NULL; - size_t size = 0, allocated = 0; + size_t size = 0; assert(ret); assert(ret_size); - assert(ret_allocated); assert(string); /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ @@ -182,7 +181,7 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t if (r == 0) break; - if (!GREEDY_REALLOC(routes, allocated, size + 1)) + if (!GREEDY_REALLOC(routes, size + 1)) return -ENOMEM; tok = word; @@ -222,7 +221,6 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t } *ret_size = size; - *ret_allocated = allocated; *ret = TAKE_PTR(routes); return 0; diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-internal.h b/src/libnm-systemd-core/src/libsystemd-network/network-internal.h index e5b853c0cd..895a00d01b 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/network-internal.h @@ -22,7 +22,7 @@ struct sd_dhcp_route; struct sd_dhcp_lease; void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size); -int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string); +int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string); /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size); diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c index 07dc17691a..b220f61dd2 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c @@ -2057,6 +2057,13 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) { return client_initialize_time_events(client); } +int sd_dhcp_client_is_running(sd_dhcp_client *client) { + if (!client) + return 0; + + return !IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED); +} + int sd_dhcp_client_start(sd_dhcp_client *client) { int r; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c index 8f7df6f47c..4cd9295c75 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c @@ -442,14 +442,13 @@ static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_a static int lease_parse_routes( const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { + struct sd_dhcp_route **routes, size_t *routes_size) { struct in_addr addr; assert(option || len <= 0); assert(routes); assert(routes_size); - assert(routes_allocated); if (len <= 0) return 0; @@ -457,7 +456,7 @@ static int lease_parse_routes( if (len % 8 != 0) return -EINVAL; - if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8))) + if (!GREEDY_REALLOC(*routes, *routes_size + (len / 8))) return -ENOMEM; while (len >= 8) { @@ -488,12 +487,11 @@ static int lease_parse_routes( /* parses RFC3442 Classless Static Route Option */ static int lease_parse_classless_routes( const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { + struct sd_dhcp_route **routes, size_t *routes_size) { assert(option || len <= 0); assert(routes); assert(routes_size); - assert(routes_allocated); if (len <= 0) return 0; @@ -504,7 +502,7 @@ static int lease_parse_classless_routes( uint8_t dst_octets; struct sd_dhcp_route *route; - if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1)) + if (!GREEDY_REALLOC(*routes, *routes_size + 1)) return -ENOMEM; route = *routes + *routes_size; @@ -618,7 +616,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_STATIC_ROUTE: - r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated); + r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size); if (r < 0) log_debug_errno(r, "Failed to parse static routes, ignoring: %m"); break; @@ -680,8 +678,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void r = lease_parse_classless_routes( option, len, &lease->static_route, - &lease->static_route_size, - &lease->static_route_allocated); + &lease->static_route_size); if (r < 0) log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); break; @@ -749,7 +746,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d while (pos < len) { _cleanup_free_ char *name = NULL; - size_t n = 0, allocated = 0; + size_t n = 0; size_t jump_barrier = pos, next_chunk = 0; bool first = true; @@ -769,7 +766,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d if (pos >= len) return -EBADMSG; - if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + if (!GREEDY_REALLOC(name, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; if (first) @@ -808,7 +805,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d return -EBADMSG; } - if (!GREEDY_REALLOC(name, allocated, n + 1)) + if (!GREEDY_REALLOC(name, n + 1)) return -ENOMEM; name[n] = 0; @@ -1234,7 +1231,6 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { r = deserialize_dhcp_routes( &lease->static_route, &lease->static_route_size, - &lease->static_route_allocated, routes); if (r < 0) log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes); diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c index 240d1697a3..888dfa2d6b 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c @@ -72,7 +72,6 @@ struct sd_dhcp6_client { bool information_request; bool iaid_set; be16_t *req_opts; - size_t req_opts_allocated; size_t req_opts_len; char *fqdn; char *mudurl; @@ -167,7 +166,7 @@ int sd_dhcp6_client_set_callback( int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { assert_return(client, -EINVAL); assert_return(ifindex > 0, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->ifindex = ifindex; return 0; @@ -197,8 +196,7 @@ int sd_dhcp6_client_set_local_address( assert_return(client, -EINVAL); assert_return(local_address, -EINVAL); assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL); - - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->local_address = *local_address; @@ -213,8 +211,7 @@ int sd_dhcp6_client_set_mac( assert_return(client, -EINVAL); assert_return(addr, -EINVAL); assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); - - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); @@ -244,8 +241,7 @@ int sd_dhcp6_client_set_prefix_delegation_hint( assert_return(client, -EINVAL); assert_return(pd_address, -EINVAL); - - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->hint_pd_prefix.iapdprefix.address = *pd_address; client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen; @@ -290,7 +286,7 @@ static int dhcp6_client_set_duid_internal( assert_return(client, -EINVAL); assert_return(duid_len == 0 || duid != NULL, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); if (duid) { r = dhcp_validate_duid_len(duid_type, duid_len, true); @@ -403,7 +399,7 @@ int sd_dhcp6_client_duid_as_string( int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->ia_na.ia_na.id = htobe32(iaid); client->ia_pd.ia_pd.id = htobe32(iaid); @@ -440,7 +436,7 @@ int sd_dhcp6_client_set_fqdn( int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) { assert_return(client, -EINVAL); - assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->information_request = enabled; @@ -469,8 +465,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) if (client->req_opts[t] == htobe16(option)) return -EEXIST; - if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated, - client->req_opts_len + 1)) + if (!GREEDY_REALLOC(client->req_opts, client->req_opts_len + 1)) return -ENOMEM; client->req_opts[client->req_opts_len++] = htobe16(option); @@ -1715,7 +1710,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->ifindex > 0, -EINVAL); assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL); - if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) + if (client->state != DHCP6_STATE_STOPPED) return -EBUSY; if (!client->information_request && !client->request) diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c index d40fbbb727..e37acfe8d9 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c @@ -205,8 +205,7 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { return 0; r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, - lease->dns_count, - &lease->dns_allocated); + lease->dns_count); if (r < 0) return r; @@ -271,7 +270,6 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { lease->ntp = mfree(lease->ntp); lease->ntp_count = 0; - lease->ntp_allocated = 0; while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, &subval)) >= 0) { @@ -286,8 +284,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { s = dhcp6_option_parse_ip6addrs(subval, sublen, &lease->ntp, - lease->ntp_count, - &lease->ntp_allocated); + lease->ntp_count); if (s < 0) return s; @@ -329,8 +326,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) /* Using deprecated SNTP information */ r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, - lease->ntp_count, - &lease->ntp_allocated); + lease->ntp_count); if (r < 0) return r; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c index af83f51954..031804caf2 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c @@ -77,18 +77,16 @@ struct sd_ipv4acd { void* userdata; }; -#define log_ipv4acd_errno(acd, error, fmt, ...) \ - ({ \ - int _e = (error); \ - if (DEBUG_LOGGING) \ - log_interface_full_errno( \ - sd_ipv4acd_get_ifname(acd), \ - LOG_DEBUG, _e, "IPv4ACD: " fmt, \ - ##__VA_ARGS__); \ - -ERRNO_VALUE(_e); \ - }) +#define log_ipv4acd_errno(acd, error, fmt, ...) \ + log_interface_prefix_full_errno( \ + "IPv4ACD: ", \ + sd_ipv4acd_get_ifname(acd), \ + error, fmt, ##__VA_ARGS__) #define log_ipv4acd(acd, fmt, ...) \ - log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__) + log_interface_prefix_full_errno_zerook( \ + "IPv4ACD: ", \ + sd_ipv4acd_get_ifname(acd), \ + 0, fmt, ##__VA_ARGS__) static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = { [IPV4ACD_STATE_INIT] = "init", diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c index 202700d2cb..56275100ba 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c @@ -51,18 +51,16 @@ struct sd_ipv4ll { void* userdata; }; -#define log_ipv4ll_errno(ll, error, fmt, ...) \ - ({ \ - int _e = (error); \ - if (DEBUG_LOGGING) \ - log_interface_full_errno( \ - sd_ipv4ll_get_ifname(ll), \ - LOG_DEBUG, _e, "IPv4LL: " fmt, \ - ##__VA_ARGS__); \ - -ERRNO_VALUE(_e); \ - }) +#define log_ipv4ll_errno(ll, error, fmt, ...) \ + log_interface_prefix_full_errno( \ + "IPv4LL: ", \ + sd_ipv4ll_get_ifname(ll), \ + error, fmt, ##__VA_ARGS__) #define log_ipv4ll(ll, fmt, ...) \ - log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__) + log_interface_prefix_full_errno_zerook( \ + "IPv4LL: ", \ + sd_ipv4ll_get_ifname(ll), \ + 0, fmt, ##__VA_ARGS__) static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c index 1db618d05c..4492674481 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c @@ -146,7 +146,6 @@ struct sd_event { unsigned n_sources; struct epoll_event *event_queue; - size_t event_queue_allocated; LIST_HEAD(sd_event_source, sources); @@ -3466,11 +3465,11 @@ static int source_dispatch(sd_event_source *s) { * invalidate the event. */ saved_type = s->type; - /* Similar, store a reference to the event loop object, so that we can still access it after the + /* Similarly, store a reference to the event loop object, so that we can still access it after the * callback might have invalidated/disconnected the event source. */ saved_event = sd_event_ref(s->event); - /* Check if we hit the ratelimit for this event source, if so, let's disable it. */ + /* Check if we hit the ratelimit for this event source, and if so, let's disable it. */ assert(!s->ratelimited); if (!ratelimit_below(&s->rate_limit)) { r = event_source_enter_ratelimited(s); @@ -3489,8 +3488,7 @@ static int source_dispatch(sd_event_source *s) { if (s->type != SOURCE_POST) { sd_event_source *z; - /* If we execute a non-post source, let's mark all - * post sources as pending */ + /* If we execute a non-post source, let's mark all post sources as pending. */ SET_FOREACH(z, s->event->post_sources) { if (event_source_is_offline(z)) @@ -3863,38 +3861,45 @@ static int epoll_wait_usec( } static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) { + size_t n_event_queue, m, n_event_max; int64_t min_priority = threshold; bool something_new = false; - size_t n_event_queue, m; int r; assert(e); assert(ret_min_priority); n_event_queue = MAX(e->n_sources, 1u); - if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, n_event_queue)) + if (!GREEDY_REALLOC(e->event_queue, n_event_queue)) return -ENOMEM; + n_event_max = MALLOC_ELEMENTSOF(e->event_queue); + /* If we still have inotify data buffered, then query the other fds, but don't wait on it */ if (e->inotify_data_buffered) timeout = 0; for (;;) { - r = epoll_wait_usec(e->epoll_fd, e->event_queue, e->event_queue_allocated, timeout); + r = epoll_wait_usec( + e->epoll_fd, + e->event_queue, + n_event_max, + timeout); if (r < 0) return r; m = (size_t) r; - if (m < e->event_queue_allocated) + if (m < n_event_max) break; - if (e->event_queue_allocated >= n_event_queue * 10) + if (n_event_max >= n_event_queue * 10) break; - if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, e->event_queue_allocated + n_event_queue)) + if (!GREEDY_REALLOC(e->event_queue, n_event_max + n_event_queue)) return -ENOMEM; + n_event_max = MALLOC_ELEMENTSOF(e->event_queue); timeout = 0; } diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c index b61cfdb02c..3a2bffab7e 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c @@ -215,4 +215,26 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id) { } DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); + +int id128_get_product(sd_id128_t *ret) { + sd_id128_t uuid; + int r; + + assert(ret); + + /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is + * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ + + r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); + if (r == -ENOENT) + r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid); + if (r < 0) + return r; + + if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid)) + return -EADDRNOTAVAIL; /* Recognizable error */ + + *ret = uuid; + return 0; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h index 6b09bcd96a..053ef0a6a8 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h @@ -36,3 +36,5 @@ int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; sd_id128_t id128_make_v4_uuid(sd_id128_t id); + +int id128_get_product(sd_id128_t *ret); diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h index c784cbcb9a..5300234098 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h @@ -90,6 +90,7 @@ enum { SD_DHCP_OPTION_POP3_SERVER = 70, SD_DHCP_OPTION_USER_CLASS = 77, SD_DHCP_OPTION_FQDN = 81, + SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, @@ -105,6 +106,12 @@ enum { SD_DHCP_OPTION_END = 255, }; +/* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */ +enum { + SD_DHCP_RELAY_AGENT_CIRCUIT_ID = 1, + SD_DHCP_RELAY_AGENT_REMOTE_ID = 2, +}; + typedef struct sd_dhcp_client sd_dhcp_client; typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); @@ -199,6 +206,7 @@ int sd_dhcp_client_set_fallback_lease_lifetime( int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v); int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v); +int sd_dhcp_client_is_running(sd_dhcp_client *client); int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); int sd_dhcp_client_send_release(sd_dhcp_client *client); diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h index 02aa318a06..ab209c8c7d 100644 --- a/src/libnm-systemd-core/src/systemd/sd-id128.h +++ b/src/libnm-systemd-core/src/systemd/sd-id128.h @@ -18,6 +18,7 @@ ***/ #include <inttypes.h> +#include <stdarg.h> #include <string.h> #include "_sd-common.h" @@ -119,6 +120,32 @@ _sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) { #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_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) { + for (;;) { + sd_id128_t b = va_arg(ap, sd_id128_t); + + if (sd_id128_is_null(b)) + return 0; + + if (sd_id128_equal(a, b)) + return 1; + } +} + +_sd_pure_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) { + va_list ap; + int r; + + va_start(ap, a); + r = sd_id128_in_setv(a, ap); + va_end(ap); + + return r; +} + +#define sd_id128_in_set(a, ...) \ + sd_id128_in_set_sentinel(a, ##__VA_ARGS__, SD_ID128_NULL) + _SD_END_DECLARATIONS; #endif diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.c b/src/libnm-systemd-shared/nm-sd-utils-shared.c index 8c8934b1a4..0a35016a40 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.c +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.c @@ -28,9 +28,9 @@ nm_sd_utils_path_equal(const char *a, const char *b) } char * -nm_sd_utils_path_simplify(char *path, gboolean kill_dots) +nm_sd_utils_path_simplify(char *path) { - return path_simplify(path, kill_dots); + return path_simplify(path); } const char * diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.h b/src/libnm-systemd-shared/nm-sd-utils-shared.h index 45089c074d..82a5067863 100644 --- a/src/libnm-systemd-shared/nm-sd-utils-shared.h +++ b/src/libnm-systemd-shared/nm-sd-utils-shared.h @@ -10,7 +10,7 @@ gboolean nm_sd_utils_path_equal(const char *a, const char *b); -char *nm_sd_utils_path_simplify(char *path, gboolean kill_dots); +char *nm_sd_utils_path_simplify(char *path); const char *nm_sd_utils_path_startswith(const char *path, const char *prefix); diff --git a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h index e15032eaa2..9d4669c33f 100644 --- a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h +++ b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h @@ -51,6 +51,8 @@ #define LOG_TRACE 0 + #define BUILD_MODE_DEVELOPER (NM_MORE_ASSERTS > 0) + /*****************************************************************************/ /* systemd cannot be compiled with "-Wdeclaration-after-statement". In particular diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.c b/src/libnm-systemd-shared/src/basic/alloc-util.c index 7f7eb43359..e252b6ab34 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.c +++ b/src/libnm-systemd-shared/src/basic/alloc-util.c @@ -19,8 +19,7 @@ void* memdup(const void *p, size_t l) { if (!ret) return NULL; - memcpy(ret, p, l); - return ret; + return memcpy_safe(ret, p, l); } void* memdup_suffix0(const void *p, size_t l) { @@ -37,28 +36,36 @@ void* memdup_suffix0(const void *p, size_t l) { if (!ret) return NULL; - *((uint8_t*) mempcpy(ret, p, l)) = 0; - return ret; + ((uint8_t*) ret)[l] = 0; + return memcpy_safe(ret, p, l); } -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { +void* greedy_realloc( + void **p, + size_t need, + size_t size) { + size_t a, newalloc; void *q; assert(p); - assert(allocated); - if (*allocated >= need) + /* We use malloc_usable_size() for determining the current allocated size. On all systems we care + * about this should be safe to rely on. Should there ever arise the need to avoid relying on this we + * can instead locally fall back to realloc() on every call, rounded up to the next exponent of 2 or + * so. */ + + if (*p && (size == 0 || (MALLOC_SIZEOF_SAFE(*p) / size >= need))) return *p; if (_unlikely_(need > SIZE_MAX/2)) /* Overflow check */ return NULL; - newalloc = need * 2; + if (size_multiply_overflow(newalloc, size)) return NULL; - a = newalloc * size; + if (a < 64) /* Allocate at least 64 bytes */ a = 64; @@ -66,49 +73,34 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { if (!q) return NULL; - if (size > 0) { - size_t bn; - - /* Adjust for the 64 byte minimum */ - newalloc = a / size; - - bn = malloc_usable_size(q) / size; - if (bn > newalloc) { - void *qq; - - /* The actual size allocated is larger than what we asked for. Let's call realloc() again to - * take possession of the extra space. This should be cheap, since libc doesn't have to move - * the memory for this. */ - - qq = reallocarray(q, bn, size); - if (_likely_(qq)) { - *p = qq; - *allocated = bn; - return qq; - } - } - } - - *p = q; - *allocated = newalloc; - return q; + return *p = q; } -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) { - size_t prev; +void* greedy_realloc0( + void **p, + size_t need, + size_t size) { + + size_t before, after; uint8_t *q; assert(p); - assert(allocated); - prev = *allocated; + before = MALLOC_SIZEOF_SAFE(*p); /* malloc_usable_size() will return 0 on NULL input, as per docs */ - q = greedy_realloc(p, allocated, need, size); + q = greedy_realloc(p, need, size); if (!q) return NULL; - if (*allocated > prev) - memzero(q + prev * size, (*allocated - prev) * size); + after = MALLOC_SIZEOF_SAFE(q); + + if (size == 0) /* avoid division by zero */ + before = 0; + else + before = (before / size) * size; /* Round down */ + + if (after > before) + memzero(q + before, after - before); return q; } diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h index 698a6583c5..99fbd3a889 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -66,7 +66,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s size_t _l_ = l; \ assert(_l_ <= ALLOCA_MAX); \ _q_ = alloca(_l_ ?: 1); \ - memcpy(_q_, p, _l_); \ + memcpy_safe(_q_, p, _l_); \ }) #define memdupa_suffix0(p, l) \ @@ -76,7 +76,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s assert(_l_ <= ALLOCA_MAX); \ _q_ = alloca(_l_ + 1); \ ((uint8_t*) _q_)[_l_] = 0; \ - memcpy(_q_, p, _l_); \ + memcpy_safe(_q_, p, _l_); \ }) static inline void freep(void *p) { @@ -121,14 +121,14 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n return memdup_suffix0(p, size * need); } -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); +void* greedy_realloc(void **p, size_t need, size_t size); +void* greedy_realloc0(void **p, size_t need, size_t size); -#define GREEDY_REALLOC(array, allocated, need) \ - greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0])) +#define GREEDY_REALLOC(array, need) \ + greedy_realloc((void**) &(array), (need), sizeof((array)[0])) -#define GREEDY_REALLOC0(array, allocated, need) \ - greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0])) +#define GREEDY_REALLOC0(array, need) \ + greedy_realloc0((void**) &(array), (need), sizeof((array)[0])) #define alloca0(n) \ ({ \ @@ -163,3 +163,23 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); #else # define msan_unpoison(r, s) #endif + +/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), in a way that + * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the + * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of + * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by + * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the + * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory, + * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and + * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner + * case. */ +#define MALLOC_SIZEOF_SAFE(x) \ + MIN(malloc_usable_size(x), __builtin_object_size(x, 0)) + +/* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items + * that fit into the specified memory block */ +#define MALLOC_ELEMENTSOF(x) \ + (__builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ + MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \ + VOID_0)) diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index f79e384147..ce2f4c6589 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -30,6 +30,8 @@ typedef enum CGroupController { /* BPF-based pseudo-controllers, v2 only */ CGROUP_CONTROLLER_BPF_FIREWALL, CGROUP_CONTROLLER_BPF_DEVICES, + CGROUP_CONTROLLER_BPF_FOREIGN, + CGROUP_CONTROLLER_BPF_SOCKET_BIND, _CGROUP_CONTROLLER_MAX, _CGROUP_CONTROLLER_INVALID = -EINVAL, @@ -49,6 +51,8 @@ typedef enum CGroupMask { CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL), CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES), + CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN), + CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND), /* All real cgroup v1 controllers */ CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS, @@ -57,7 +61,7 @@ typedef enum CGroupMask { CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS, /* All cgroup v2 BPF pseudo-controllers */ - CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES, + CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND, _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 } CGroupMask; diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c index bd7b35078e..baab9bd284 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.c +++ b/src/libnm-systemd-shared/src/basic/env-file.c @@ -22,7 +22,7 @@ static int parse_env_file_internal( void *userdata, int *n_pushed) { - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; + size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; unsigned line = 1; char *p; @@ -60,7 +60,7 @@ static int parse_env_file_internal( state = KEY; last_key_whitespace = SIZE_MAX; - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + if (!GREEDY_REALLOC(key, n_key+2)) return -ENOMEM; key[n_key++] = c; @@ -81,7 +81,7 @@ static int parse_env_file_internal( else if (last_key_whitespace == SIZE_MAX) last_key_whitespace = n_key; - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + if (!GREEDY_REALLOC(key, n_key+2)) return -ENOMEM; key[n_key++] = c; @@ -108,7 +108,7 @@ static int parse_env_file_internal( n_key = 0; value = NULL; - value_alloc = n_value = 0; + n_value = 0; } else if (c == '\'') state = SINGLE_QUOTE_VALUE; @@ -119,7 +119,7 @@ static int parse_env_file_internal( else if (!strchr(WHITESPACE, c)) { state = VALUE; - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + if (!GREEDY_REALLOC(value, n_value+2)) return -ENOMEM; value[n_value++] = c; @@ -151,7 +151,7 @@ static int parse_env_file_internal( n_key = 0; value = NULL; - value_alloc = n_value = 0; + n_value = 0; } else if (c == '\\') { state = VALUE_ESCAPE; @@ -162,7 +162,7 @@ static int parse_env_file_internal( else if (last_value_whitespace == SIZE_MAX) last_value_whitespace = n_value; - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + if (!GREEDY_REALLOC(value, n_value+2)) return -ENOMEM; value[n_value++] = c; @@ -175,7 +175,7 @@ static int parse_env_file_internal( if (!strchr(NEWLINE, c)) { /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + if (!GREEDY_REALLOC(value, n_value+2)) return -ENOMEM; value[n_value++] = c; @@ -186,7 +186,7 @@ static int parse_env_file_internal( if (c == '\'') state = PRE_VALUE; else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + if (!GREEDY_REALLOC(value, n_value+2)) return -ENOMEM; value[n_value++] = c; @@ -200,7 +200,7 @@ static int parse_env_file_internal( else if (c == '\\') state = DOUBLE_QUOTE_VALUE_ESCAPE; else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + if (!GREEDY_REALLOC(value, n_value+2)) return -ENOMEM; value[n_value++] = c; @@ -213,13 +213,13 @@ static int parse_env_file_internal( if (strchr(SHELL_NEED_ESCAPE, c)) { /* If this is a char that needs escaping, just unescape it. */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + if (!GREEDY_REALLOC(value, n_value+2)) return -ENOMEM; value[n_value++] = c; } else if (c != '\n') { /* If other char than what needs escaping, keep the "\" in place, like the * real shell does. */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+3)) + if (!GREEDY_REALLOC(value, n_value+3)) return -ENOMEM; value[n_value++] = '\\'; value[n_value++] = c; diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index a13aaac5d7..849ee47e43 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -575,12 +575,9 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { t = strv_env_get_n(env, word+2, e-word-2, flags); - k = strjoin(r, t); - if (!k) + if (!strextend(&r, t)) return NULL; - free_and_replace(r, k); - word = e+1; state = WORD; } else if (*e == ':') { @@ -630,12 +627,9 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { else if (!t && state == DEFAULT_VALUE) t = v = replace_env_n(test_value, e-test_value, env, flags); - k = strjoin(r, t); - if (!k) + if (!strextend(&r, t)) return NULL; - free_and_replace(r, k); - word = e+1; state = WORD; } @@ -649,12 +643,9 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { t = strv_env_get_n(env, word+1, e-word-1, flags); - k = strjoin(r, t); - if (!k) + if (!strextend(&r, t)) return NULL; - free_and_replace(r, k); - word = e--; i--; state = WORD; diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h index 5609820b88..3f2d0af56d 100644 --- a/src/libnm-systemd-shared/src/basic/errno-util.h +++ b/src/libnm-systemd-shared/src/basic/errno-util.h @@ -33,7 +33,7 @@ static inline int negative_errno(void) { static inline const char *strerror_safe(int error) { /* 'safe' here does NOT mean thread safety. */ - return strerror(abs(error)); + return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */ } static inline int errno_or_else(int fallback) { diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c index fcf4ee44cf..6a13802079 100644 --- a/src/libnm-systemd-shared/src/basic/escape.c +++ b/src/libnm-systemd-shared/src/basic/escape.c @@ -363,15 +363,16 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi return t - r; } -char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) { +char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) { char *ans, *t, *prev, *prev2; const char *f; /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be - * reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged. - * This corresponds to non-ASCII printable characters in pre-unicode encodings. + * reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through + * unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings. * - * If console_width is reached, output is truncated and "..." is appended. */ + * If console_width is reached, or XESCAPE_FORCE_ELLIPSIS is set, output is truncated and "..." is + * appended. */ if (console_width == 0) return strdup(""); @@ -383,17 +384,23 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei memset(ans, '_', MIN(strlen(s), console_width) * 4); ans[MIN(strlen(s), console_width) * 4] = 0; + bool force_ellipsis = FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS); + for (f = s, t = prev = prev2 = ans; ; f++) { char *tmp_t = t; if (!*f) { + if (force_ellipsis) + break; + *t = 0; return ans; } - if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) || + if ((unsigned char) *f < ' ' || + (!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) || *f == '\\' || strchr(bad, *f)) { - if ((size_t) (t - ans) + 4 > console_width) + if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width) break; *(t++) = '\\'; @@ -401,7 +408,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei *(t++) = hexchar(*f >> 4); *(t++) = hexchar(*f); } else { - if ((size_t) (t - ans) + 1 > console_width) + if ((size_t) (t - ans) + 1 + 3 * force_ellipsis > console_width) break; *(t++) = *f; @@ -430,11 +437,13 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, bool ei return ans; } -char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) { - if (eight_bit) - return xescape_full(str, "", console_width, true); +char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) { + if (FLAGS_SET(flags, XESCAPE_8_BIT)) + return xescape_full(str, "", console_width, flags); else - return utf8_escape_non_printable_full(str, console_width); + return utf8_escape_non_printable_full(str, + console_width, + FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS)); } #endif /* NM_IGNORED */ @@ -466,88 +475,74 @@ char* octescape(const char *s, size_t len) { } -static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { +static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) { assert(bad); - for (; *s; s++) { - if (escape_tab_nl && IN_SET(*s, '\n', '\t')) { - *(t++) = '\\'; - *(t++) = *s == '\n' ? 'n' : 't'; - continue; + for (; *s; s++) + if (char_is_cc(*s)) + t += cescape_char(*s, t); + else { + if (*s == '\\' || strchr(bad, *s)) + *(t++) = '\\'; + *(t++) = *s; } - if (*s == '\\' || strchr(bad, *s)) - *(t++) = '\\'; - - *(t++) = *s; - } - return t; } char* shell_escape(const char *s, const char *bad) { - char *r, *t; + char *buf, *t; - r = new(char, strlen(s)*2+1); - if (!r) + buf = new(char, strlen(s)*4+1); + if (!buf) return NULL; - t = strcpy_backslash_escaped(r, s, bad, false); + t = strcpy_backslash_escaped(buf, s, bad); *t = 0; - return r; + return buf; } -char* shell_maybe_quote(const char *s, EscapeStyle style) { +char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) { const char *p; - char *r, *t; + char *buf, *t; assert(s); - /* Encloses a string in quotes if necessary to make it OK as a shell - * string. Note that we treat benign UTF-8 characters as needing - * escaping too, but that should be OK. */ + /* Encloses a string in quotes if necessary to make it OK as a shell string. */ + + if (FLAGS_SET(flags, SHELL_ESCAPE_EMPTY) && isempty(s)) + return strdup("\"\""); /* We don't use $'' here in the POSIX mode. "" is fine too. */ for (p = s; *p; p++) - if (*p <= ' ' || - *p >= 127 || - strchr(SHELL_NEED_QUOTES, *p)) + if (char_is_cc(*p) || + strchr(WHITESPACE SHELL_NEED_QUOTES, *p)) break; if (!*p) return strdup(s); - r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1); - if (!r) + buf = new(char, FLAGS_SET(flags, SHELL_ESCAPE_POSIX) + 1 + strlen(s)*4 + 1 + 1); + if (!buf) return NULL; - t = r; - switch (style) { - case ESCAPE_BACKSLASH: - case ESCAPE_BACKSLASH_ONELINE: - *(t++) = '"'; - break; - case ESCAPE_POSIX: + t = buf; + if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) { *(t++) = '$'; *(t++) = '\''; - break; - default: - assert_not_reached("Bad EscapeStyle"); - } + } else + *(t++) = '"'; t = mempcpy(t, s, p - s); - if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, - style == ESCAPE_BACKSLASH_ONELINE); - else - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); + t = strcpy_backslash_escaped(t, p, + FLAGS_SET(flags, SHELL_ESCAPE_POSIX) ? SHELL_NEED_ESCAPE_POSIX : SHELL_NEED_ESCAPE); - if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE)) - *(t++) = '"'; - else + if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) *(t++) = '\''; + else + *(t++) = '"'; *t = 0; - return r; + return str_realloc(buf); } diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h index 691b6d802c..907b572bd4 100644 --- a/src/libnm-systemd-shared/src/basic/escape.h +++ b/src/libnm-systemd-shared/src/basic/escape.h @@ -33,15 +33,13 @@ typedef enum UnescapeFlags { UNESCAPE_ACCEPT_NUL = 1 << 1, } UnescapeFlags; -typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, /* Add shell quotes ("") so the shell will consider this a single - argument, possibly multiline. Tabs and newlines are not escaped. */ - ESCAPE_BACKSLASH_ONELINE = 2, /* Similar to ESCAPE_BACKSLASH, but always produces a single-line - string instead. Shell escape sequences are produced for tabs and - newlines. */ - ESCAPE_POSIX = 3, /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape - * syntax (a string enclosed in $'') instead of plain quotes. */ -} EscapeStyle; +typedef enum ShellEscapeFlags { + /* The default is to add shell quotes ("") so the shell will consider this a single argument. + * Tabs and newlines are escaped. */ + + SHELL_ESCAPE_POSIX = 1 << 1, /* Use POSIX shell escape syntax (a string enclosed in $'') instead of plain quotes. */ + SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */ +} ShellEscapeFlags; char* cescape(const char *s); char* cescape_length(const char *s, size_t n); @@ -56,12 +54,17 @@ static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) { } int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul); -char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits); +typedef enum XEscapeFlags { + XESCAPE_8_BIT = 1 << 0, + XESCAPE_FORCE_ELLIPSIS = 1 << 1, +} XEscapeFlags; + +char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags); static inline char* xescape(const char *s, const char *bad) { - return xescape_full(s, bad, SIZE_MAX, false); + return xescape_full(s, bad, SIZE_MAX, 0); } char* octescape(const char *s, size_t len); -char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit); +char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags); char* shell_escape(const char *s, const char *bad); -char* shell_maybe_quote(const char *s, EscapeStyle style); +char* shell_maybe_quote(const char *s, ShellEscapeFlags flags); diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c index ae83eade9b..f60e1042a0 100644 --- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c @@ -45,6 +45,22 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR return buffer; } +int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret) { + char *buf; + + assert(addr); + assert(ret); + + buf = new(char, ETHER_ADDR_TO_STRING_MAX); + if (!buf) + return -ENOMEM; + + ether_addr_to_string(addr, buf); + + *ret = buf; + return 0; +} + int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) { return memcmp(a, b, ETH_ALEN); } diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h index 942ce55621..712c927796 100644 --- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h @@ -35,6 +35,7 @@ char* hw_addr_to_string(const hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_ #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_to_string_alloc(const struct ether_addr *addr, char **ret); 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) { diff --git a/src/libnm-systemd-shared/src/basic/extract-word.c b/src/libnm-systemd-shared/src/basic/extract-word.c index 950af41073..b339eae28f 100644 --- a/src/libnm-systemd-shared/src/basic/extract-word.c +++ b/src/libnm-systemd-shared/src/basic/extract-word.c @@ -21,7 +21,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { _cleanup_free_ char *s = NULL; - size_t allocated = 0, sz = 0; + size_t sz = 0; char quote = 0; /* 0 or ' or " */ bool backslash = false; /* whether we've just seen a backslash */ char c; @@ -44,7 +44,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra * the pointer *p at the first invalid character. */ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) + if (!GREEDY_REALLOC(s, sz+1)) return -ENOMEM; for (;; (*p)++, c = **p) { @@ -59,7 +59,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra /* We found a non-blank character, so we will always * want to return a string (even if it is empty), * allocate it here. */ - if (!GREEDY_REALLOC(s, allocated, sz+1)) + if (!GREEDY_REALLOC(s, sz+1)) return -ENOMEM; break; } @@ -67,7 +67,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra for (;; (*p)++, c = **p) { if (backslash) { - if (!GREEDY_REALLOC(s, allocated, sz+7)) + if (!GREEDY_REALLOC(s, sz+7)) return -ENOMEM; if (c == 0) { @@ -130,7 +130,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra backslash = true; break; } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) + if (!GREEDY_REALLOC(s, sz+2)) return -ENOMEM; s[sz++] = c; @@ -162,7 +162,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra goto finish; } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) + if (!GREEDY_REALLOC(s, sz+2)) return -ENOMEM; s[sz++] = c; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index e5bf8de72d..670b72ff49 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -458,7 +458,7 @@ bool fdname_is_valid(const char *s) { return false; } - return p - s < 256; + return p - s <= FDNAME_MAX; } #if 0 /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index f05c2b5a15..aa8e082b38 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -8,6 +8,9 @@ #include "macro.h" +/* maximum length of fdname */ +#define FDNAME_MAX 255 + /* Make sure we can distinguish fd 0 and NULL */ #define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) #define PTR_TO_FD(p) (PTR_TO_INT(p)-1) diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index 27ec9b96c8..de78f70ee6 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -59,7 +59,7 @@ int fdopen_unlocked(int fd, const char *options, FILE **ret) { } int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) { - int r; + int r; assert(fd); @@ -289,7 +289,7 @@ fail: /* OK, the operation failed, but let's see if the right * contents in place already. If so, eat up the error. */ - q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)); if (q <= 0) return r; @@ -368,32 +368,38 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { } #endif /* NM_IGNORED */ -int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) { +int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) { _cleanup_free_ char *buf = NULL; _cleanup_close_ int fd = -1; - struct stat st; size_t n, size; int n_retries; - - assert(ret_contents); - - /* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work - * with two sorts of virtual files. One sort uses "seq_file", and the results of - * the first read are buffered for the second read. The other sort uses "raw" - * reads which always go direct to the device. In the latter case, the content of - * the virtual file must be retrieved with a single read otherwise a second read - * might get the new value instead of finding EOF immediately. That's the reason - * why the usage of fread(3) is prohibited in this case as it always performs a - * second call to read(2) looking for EOF. See issue 13585. */ + bool truncated = false; + + /* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work with two sorts of + * virtual files. One sort uses "seq_file", and the results of the first read are buffered for the + * second read. The other sort uses "raw" reads which always go direct to the device. In the latter + * case, the content of the virtual file must be retrieved with a single read otherwise a second read + * might get the new value instead of finding EOF immediately. That's the reason why the usage of + * fread(3) is prohibited in this case as it always performs a second call to read(2) looking for + * EOF. See issue #13585. + * + * max_size specifies a limit on the bytes read. If max_size is SIZE_MAX, the full file is read. If + * the full file is too large to read, an error is returned. For other values of max_size, *partial + * contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on + * partial success, 1 if untruncated contents were read. */ fd = open(filename, O_RDONLY|O_CLOEXEC); if (fd < 0) return -errno; + assert(max_size <= READ_FULL_BYTES_MAX || max_size == SIZE_MAX); + /* Limit the number of attempts to read the number of bytes returned by fstat(). */ n_retries = 3; for (;;) { + struct stat st; + if (fstat(fd, &st) < 0) return -errno; @@ -402,22 +408,35 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re /* Be prepared for files from /proc which generally report a file size of 0. */ assert_cc(READ_FULL_BYTES_MAX < SSIZE_MAX); - if (st.st_size > 0) { - if (st.st_size > READ_FULL_BYTES_MAX) - return -EFBIG; + if (st.st_size > 0 && n_retries > 1) { + /* Let's use the file size if we have more than 1 attempt left. On the last attempt + * we'll ignore the file size */ + + if (st.st_size > SSIZE_MAX) { /* Avoid overflow with 32-bit size_t and 64-bit off_t. */ + + if (max_size == SIZE_MAX) + return -EFBIG; + + size = max_size; + } else { + size = MIN((size_t) st.st_size, max_size); + + if (size > READ_FULL_BYTES_MAX) + return -EFBIG; + } - size = st.st_size; n_retries--; } else { - size = READ_FULL_BYTES_MAX; + size = MIN(READ_FULL_BYTES_MAX, max_size); n_retries = 0; } buf = malloc(size + 1); if (!buf) return -ENOMEM; + /* Use a bigger allocation if we got it anyway, but not more than the limit. */ - size = MIN(malloc_usable_size(buf) - 1, READ_FULL_BYTES_MAX); + size = MIN3(MALLOC_SIZEOF_SAFE(buf) - 1, max_size, READ_FULL_BYTES_MAX); for (;;) { ssize_t k; @@ -439,13 +458,20 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re if (n <= size) break; - /* Hmm... either we read too few bytes from /proc or less likely the content - * of the file might have been changed (and is now bigger) while we were - * processing, let's try again either with a bigger guessed size or the new - * file size. */ + /* If a maximum size is specified and we already read as much, no need to try again */ + if (max_size != SIZE_MAX && n >= max_size) { + n = max_size; + truncated = true; + break; + } + /* We have no further attempts left? Then the file is apparently larger than our limits. Give up. */ if (n_retries <= 0) - return st.st_size > 0 ? -EIO : -EFBIG; + return -EFBIG; + + /* Hmm... either we read too few bytes from /proc or less likely the content of the file + * might have been changed (and is now bigger) while we were processing, let's try again + * either with the new file size. */ if (lseek(fd, 0, SEEK_SET) < 0) return -errno; @@ -453,28 +479,32 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re buf = mfree(buf); } - if (n < size) { - char *p; - - /* Return rest of the buffer to libc */ - p = realloc(buf, n + 1); - if (!p) - return -ENOMEM; - buf = p; - } + if (ret_contents) { - if (ret_size) - *ret_size = n; - else if (memchr(buf, 0, n)) /* Safety check: if the caller doesn't want to know the size of what we just read it will * rely on the trailing NUL byte. But if there's an embedded NUL byte, then we should refuse * operation as otherwise there'd be ambiguity about what we just read. */ - return -EBADMSG; + if (!ret_size && memchr(buf, 0, n)) + return -EBADMSG; - buf[n] = 0; - *ret_contents = TAKE_PTR(buf); + if (n < size) { + char *p; - return 0; + /* Return rest of the buffer to libc */ + p = realloc(buf, n + 1); + if (!p) + return -ENOMEM; + buf = p; + } + + buf[n] = 0; + *ret_contents = TAKE_PTR(buf); + } + + if (ret_size) + *ret_size = n; + + return !truncated; } int read_full_stream_full( @@ -556,7 +586,7 @@ int read_full_stream_full( buf = t; /* Unless a size has been explicitly specified, try to read as much as fits into the memory * we allocated (minus 1, to leave one byte for the safety NUL byte) */ - n = size == SIZE_MAX ? malloc_usable_size(buf) - 1 : n_next; + n = size == SIZE_MAX ? MALLOC_SIZEOF_SAFE(buf) - 1 : n_next; errno = 0; k = fread(buf + l, 1, n - l, f); @@ -921,12 +951,19 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r } #if 0 /* NM_IGNORED */ -static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { +static int search_and_fopen_internal( + const char *path, + const char *mode, + const char *root, + char **search, + FILE **ret, + char **ret_path) { + char **i; assert(path); assert(mode); - assert(_f); + assert(ret); if (!path_strv_resolve_uniq(search, root)) return -ENOMEM; @@ -941,7 +978,10 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c f = fopen(p, mode); if (f) { - *_f = f; + if (ret_path) + *ret_path = path_simplify(TAKE_PTR(p)); + + *ret = f; return 0; } @@ -952,52 +992,84 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c return -ENOENT; } -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) { +int search_and_fopen( + const char *filename, + const char *mode, + const char *root, + const char **search, + FILE **ret, + char **ret_path) { + _cleanup_strv_free_ char **copy = NULL; - assert(path); + assert(filename); assert(mode); - assert(_f); + assert(ret); - if (path_is_absolute(path)) { - FILE *f; + if (path_is_absolute(filename)) { + _cleanup_fclose_ FILE *f = NULL; - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; + f = fopen(filename, mode); + if (!f) + return -errno; + + if (ret_path) { + char *p; + + p = strdup(filename); + if (!p) + return -ENOMEM; + + *ret_path = path_simplify(p); } - return -errno; + *ret = TAKE_PTR(f); + return 0; } copy = strv_copy((char**) search); if (!copy) return -ENOMEM; - return search_and_fopen_internal(path, mode, root, copy, _f); + return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path); } -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) { +int search_and_fopen_nulstr( + const char *filename, + const char *mode, + const char *root, + const char *search, + FILE **ret, + char **ret_path) { + _cleanup_strv_free_ char **s = NULL; - if (path_is_absolute(path)) { - FILE *f; + if (path_is_absolute(filename)) { + _cleanup_fclose_ FILE *f = NULL; - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; + f = fopen(filename, mode); + if (!f) + return -errno; + + if (ret_path) { + char *p; + + p = strdup(filename); + if (!p) + return -ENOMEM; + + *ret_path = path_simplify(p); } - return -errno; + *ret = TAKE_PTR(f); + return 0; } s = strv_split_nulstr(search); if (!s) return -ENOMEM; - return search_and_fopen_internal(path, mode, root, s, _f); + return search_and_fopen_internal(filename, mode, root, s, ret, ret_path); } int chase_symlinks_and_fopen_unlocked( @@ -1011,7 +1083,6 @@ int chase_symlinks_and_fopen_unlocked( _cleanup_close_ int fd = -1; _cleanup_free_ char *final_path = NULL; int mode_flags, r; - FILE *f; assert(path); assert(open_flags); @@ -1025,12 +1096,10 @@ int chase_symlinks_and_fopen_unlocked( if (fd < 0) return fd; - r = fdopen_unlocked(fd, open_flags, &f); + r = take_fdopen_unlocked(&fd, open_flags, ret_file); if (r < 0) return r; - TAKE_FD(fd); - *ret_file = f; if (ret_path) *ret_path = TAKE_PTR(final_path); return 0; @@ -1166,8 +1235,8 @@ static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) { DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, funlockfile, NULL); int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { - size_t n = 0, allocated = 0, count = 0; _cleanup_free_ char *buffer = NULL; + size_t n = 0, count = 0; int r; assert(f); @@ -1197,7 +1266,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { * If a line shall be skipped ret may be initialized as NULL. */ if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, 1)) + if (!GREEDY_REALLOC(buffer, 1)) return -ENOMEM; } @@ -1269,7 +1338,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { } if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, n + 2)) + if (!GREEDY_REALLOC(buffer, n + 2)) return -ENOMEM; buffer[n] = c; @@ -1347,7 +1416,7 @@ int rename_and_apply_smack_floor_label(const char *from, const char *to) { if (rename(from, to) < 0) return -errno; -#ifdef SMACK_RUN_LABEL +#if HAVE_SMACK_RUN_LABEL r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); if (r < 0) return r; diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h index fe49a1f756..1a8b7dec98 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.h +++ b/src/libnm-systemd-shared/src/basic/fileio.h @@ -19,16 +19,17 @@ #define LONG_LINE_MAX (1U*1024U*1024U) typedef enum { - WRITE_STRING_FILE_CREATE = 1 << 0, - WRITE_STRING_FILE_TRUNCATE = 1 << 1, - WRITE_STRING_FILE_ATOMIC = 1 << 2, - WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4, - WRITE_STRING_FILE_SYNC = 1 << 5, - WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 6, - WRITE_STRING_FILE_NOFOLLOW = 1 << 7, - WRITE_STRING_FILE_MKDIR_0755 = 1 << 8, - WRITE_STRING_FILE_MODE_0600 = 1 << 9, + WRITE_STRING_FILE_CREATE = 1 << 0, + WRITE_STRING_FILE_TRUNCATE = 1 << 1, + WRITE_STRING_FILE_ATOMIC = 1 << 2, + WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4, + WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5, + WRITE_STRING_FILE_SYNC = 1 << 6, + WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7, + WRITE_STRING_FILE_NOFOLLOW = 1 << 8, + WRITE_STRING_FILE_MKDIR_0755 = 1 << 9, + WRITE_STRING_FILE_MODE_0600 = 1 << 10, /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() @@ -68,7 +69,12 @@ int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_ static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) { return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); } -int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); + +int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size); +static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) { + return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size); +} + int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size); static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) { return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size); @@ -83,8 +89,8 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin DIR *xopendirat(int dirfd, const char *name, int flags); int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret); -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); +int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path); +int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path); int chase_symlinks_and_fopen_unlocked( const char *path, diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index ff096852cf..ee8eec90bc 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -26,6 +26,7 @@ #include "path-util.h" #include "process-util.h" #include "random-util.h" +#include "ratelimit.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -231,7 +232,7 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return fchmod_and_chown(fd, mode, uid, gid); } -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { +int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) { bool do_chown, do_chmod; struct stat st; int r; @@ -242,7 +243,11 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { * unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does * on chown(). * - * This call is happy with O_PATH fds. */ + * This call is happy with O_PATH fds. + * + * If path is given, allow a fallback path which does not use /proc/self/fd/. On any normal system + * /proc will be mounted, but in certain improperly assembled environments it might not be. This is + * less secure (potential TOCTOU), so should only be used after consideration. */ if (fstat(fd, &st) < 0) return -errno; @@ -267,8 +272,14 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { if (((minimal ^ st.st_mode) & 07777) != 0) { r = fchmod_opath(fd, minimal & 07777); - if (r < 0) - return r; + if (r < 0) { + if (!path || r != -ENOSYS) + return r; + + /* Fallback path which doesn't use /proc/self/fd/. */ + if (chmod(path, minimal & 07777) < 0) + return -errno; + } } } @@ -278,8 +289,14 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { if (do_chmod) { r = fchmod_opath(fd, mode & 07777); - if (r < 0) - return r; + if (r < 0) { + if (!path || r != -ENOSYS) + return r; + + /* Fallback path which doesn't use /proc/self/fd/. */ + if (chmod(path, mode & 07777) < 0) + return -errno; + } } return do_chown || do_chmod; @@ -553,10 +570,10 @@ int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { } int get_files_in_directory(const char *path, char ***list) { + _cleanup_strv_free_ char **l = NULL; _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - size_t bufsize = 0, n = 0; - _cleanup_strv_free_ char **l = NULL; + size_t n = 0; assert(path); @@ -576,7 +593,7 @@ int get_files_in_directory(const char *path, char ***list) { if (list) { /* one extra slot is needed for the terminating NULL */ - if (!GREEDY_REALLOC(l, bufsize, n + 2)) + if (!GREEDY_REALLOC(l, n + 2)) return -ENOMEM; l[n] = strdup(de->d_name); @@ -769,9 +786,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; _cleanup_close_ int fd = -1; unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ + bool exists = true, append_trail_slash = false; struct stat previous_stat; - bool exists = true; - char *todo; + const char *todo; int r; assert(path); @@ -860,7 +877,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * anyway. Moreover at the end of this function after processing everything we'll always turn * the empty string back to "/". */ delete_trailing_chars(root, "/"); - path_simplify(root, true); + path_simplify(root); if (flags & CHASE_PREFIX_ROOT) { /* We don't support relative paths in combination with a root directory */ @@ -879,84 +896,51 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fd < 0) return -errno; - if (flags & CHASE_SAFE) { + if (flags & CHASE_SAFE) if (fstat(fd, &previous_stat) < 0) return -errno; - } - if (root) { - _cleanup_free_ char *absolute = NULL; - const char *e; + if (flags & CHASE_TRAIL_SLASH) + append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/."); + if (root) { /* If we are operating on a root directory, let's take the root directory as it is. */ - e = path_startswith(buffer, root); - if (!e) + todo = path_startswith(buffer, root); + if (!todo) return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(ECHRNG), "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", path, root); done = strdup(root); - if (!done) - return -ENOMEM; - - /* Make sure "todo" starts with a slash */ - absolute = strjoin("/", e); - if (!absolute) - return -ENOMEM; - - free_and_replace(buffer, absolute); + } else { + todo = buffer; + done = strdup("/"); } - todo = buffer; for (;;) { _cleanup_free_ char *first = NULL; _cleanup_close_ int child = -1; struct stat st; - size_t n, m; - - /* Determine length of first component in the path */ - n = strspn(todo, "/"); /* The slashes */ - - if (n > 1) { - /* If we are looking at more than a single slash then skip all but one, so that when - * we are done with everything we have a normalized path with only single slashes - * separating the path components. */ - todo += n - 1; - n = 1; - } - - m = n + strcspn(todo + n, "/"); /* The entire length of the component */ - - /* Extract the first component. */ - first = strndup(todo, m); - if (!first) - return -ENOMEM; - - todo += m; - - /* Empty? Then we reached the end. */ - if (isempty(first)) - break; - - /* Just a single slash? Then we reached the end. */ - if (path_equal(first, "/")) { - /* Preserve the trailing slash */ + const char *e; - if (flags & CHASE_TRAIL_SLASH) + r = path_find_first_component(&todo, true, &e); + if (r < 0) + return r; + if (r == 0) { /* We reached the end. */ + if (append_trail_slash) if (!strextend(&done, "/")) return -ENOMEM; - break; } - /* Just a dot? Then let's eat this up. */ - if (path_equal(first, "/.")) - continue; + first = strndup(e, r); + if (!first) + return -ENOMEM; /* Two dots? Then chop off the last bit of what we already found out. */ - if (path_equal(first, "/..")) { + if (path_equal(first, "..")) { _cleanup_free_ char *parent = NULL; _cleanup_close_ int fd_parent = -1; @@ -1001,22 +985,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, } /* Otherwise let's see what this is. */ - child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); + child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH); if (child < 0) { - if (errno == ENOENT && (flags & CHASE_NONEXISTENT) && - (isempty(todo) || path_is_normalized(todo))) { - - /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return - * what we got so far. But don't allow this if the remaining path contains "../ or "./" - * or something else weird. */ - - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq_ptr(done, "/")) - *done = '\0'; + (isempty(todo) || path_is_safe(todo))) { + /* If CHASE_NONEXISTENT is set, and the path does not exist, then + * that's OK, return what we got so far. But don't allow this if the + * remaining path contains "../" or something else weird. */ - if (!strextend(&done, first, todo)) + if (!path_extend(&done, first, todo)) return -ENOMEM; exists = false; @@ -1039,15 +1017,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return log_autofs_mount_point(child, path, flags); if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { - char *joined; _cleanup_free_ char *destination = NULL; - /* This is a symlink, in this case read the destination. But let's make sure we don't follow - * symlinks without bounds. */ + /* This is a symlink, in this case read the destination. But let's make sure we + * don't follow symlinks without bounds. */ if (--max_follow <= 0) return -ELOOP; - r = readlinkat_malloc(fd, first + n, &destination); + r = readlinkat_malloc(fd, first, &destination); if (r < 0) return r; if (isempty(destination)) @@ -1073,27 +1050,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, previous_stat = st; } - free(done); - /* Note that we do not revalidate the root, we take it as is. */ - if (isempty(root)) - done = NULL; - else { - done = strdup(root); - if (!done) - return -ENOMEM; - } + r = free_and_strdup(&done, empty_to_root(root)); + if (r < 0) + return r; + } - /* Prefix what's left to do with what we just read, and start the loop again, but - * remain in the current directory. */ - joined = path_join(destination, todo); - } else - joined = path_join("/", destination, todo); - if (!joined) + /* Prefix what's left to do with what we just read, and start the loop again, but + * remain in the current directory. */ + if (!path_extend(&destination, todo)) return -ENOMEM; - free(buffer); - todo = buffer = joined; + free_and_replace(buffer, destination); + todo = buffer; if (flags & CHASE_STEP) goto chased_one; @@ -1102,29 +1071,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, } /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!done) - done = TAKE_PTR(first); - else { - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq(done, "/")) - *done = '\0'; - - if (!strextend(&done, first)) - return -ENOMEM; - } + if (!path_extend(&done, first)) + return -ENOMEM; /* And iterate again, but go one directory further down. */ safe_close(fd); fd = TAKE_FD(child); } - if (!done) { - /* Special case, turn the empty string into "/", to indicate the root directory. */ - done = strdup("/"); - if (!done) - return -ENOMEM; - } - if (ret_path) *ret_path = TAKE_PTR(done); @@ -1143,13 +1097,23 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, chased_one: if (ret_path) { - char *c; + const char *e; - c = strjoin(strempty(done), todo); - if (!c) - return -ENOMEM; + /* todo may contain slashes at the beginning. */ + r = path_find_first_component(&todo, true, &e); + if (r < 0) + return r; + if (r == 0) + *ret_path = TAKE_PTR(done); + else { + char *c; - *ret_path = c; + c = path_join(done, e); + if (!c) + return -ENOMEM; + + *ret_path = c; + } } return 0; @@ -1729,3 +1693,23 @@ do_rename: return 1; } + +int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) { + RateLimit rl; + int r; + + r = posix_fallocate(fd, offset, size); /* returns positive errnos on error */ + if (r != EINTR) + return -r; /* Let's return negative errnos, like common in our codebase */ + + /* On EINTR try a couple of times more, but protect against busy looping + * (not more than 16 times per 10s) */ + rl = (RateLimit) { 10 * USEC_PER_SEC, 16 }; + while (ratelimit_below(&rl)) { + r = posix_fallocate(fd, offset, size); + if (r != EINTR) + return -r; + } + + return -EINTR; +} diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index 45115fd3db..7bac25704f 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -34,7 +34,10 @@ int readlink_value(const char *p, char **ret); 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); -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid); +static inline int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { + return fchmod_and_chown_with_fallback(fd, NULL, mode, uid, gid); /* no fallback */ +} int fchmod_umask(int fd, mode_t mode); int fchmod_opath(int fd, mode_t m); @@ -146,3 +149,5 @@ int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const static inline int conservative_rename(const char *oldpath, const char *newpath) { return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath); } + +int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c index 6f540b29a4..c1d28ff636 100644 --- a/src/libnm-systemd-shared/src/basic/hash-funcs.c +++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c @@ -16,11 +16,11 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, char, string_hash_func, string_compare_func, free); DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, char, string_hash_func, string_compare_func, free, - char, free); + void, free); #if 0 /* NM_IGNORED */ void path_hash_func(const char *q, struct siphash *state) { - size_t n; + bool add_slash = false; assert(q); assert(state); @@ -30,36 +30,40 @@ void path_hash_func(const char *q, struct siphash *state) { * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those * which begin in a slash or not) will hash differently though. */ - n = strspn(q, "/"); - if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */ - siphash24_compress(q, 1, state); - q += n; - } + /* if path is absolute, add one "/" to the hash. */ + if (path_is_absolute(q)) + siphash24_compress("/", 1, state); for (;;) { - /* Determine length of next component */ - n = strcspn(q, "/"); - if (n == 0) /* Reached the end? */ - break; - - /* Add this component to the hash and skip over it */ - siphash24_compress(q, n, state); - q += n; - - /* How many slashes follow this component? */ - n = strspn(q, "/"); - if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */ - break; - - /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */ - siphash24_compress(q, 1, state); - q += n; + const char *e; + int r; + + r = path_find_first_component(&q, true, &e); + if (r == 0) + return; + + if (add_slash) + siphash24_compress_byte('/', state); + + if (r < 0) { + /* if a component is invalid, then add remaining part as a string. */ + string_hash_func(q, state); + return; + } + + /* Add this component to the hash. */ + siphash24_compress(e, r, state); + + add_slash = true; } } DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare); DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free, char, path_hash_func, path_compare, free); +DEFINE_HASH_OPS_FULL(path_hash_ops_free_free, + char, path_hash_func, path_compare, free, + void, free); #endif /* NM_IGNORED */ void trivial_hash_func(const void *p, struct siphash *state) { diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h index 5672df1da4..023cfdf530 100644 --- a/src/libnm-systemd-shared/src/basic/hash-funcs.h +++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h @@ -82,6 +82,7 @@ extern const struct hash_ops string_hash_ops_free_free; void path_hash_func(const char *p, struct siphash *state); extern const struct hash_ops path_hash_ops; extern const struct hash_ops path_hash_ops_free; +extern const struct hash_ops path_hash_ops_free_free; /* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings * or suchlike. */ diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index 85e0335876..28db939dd3 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -232,7 +232,7 @@ struct Set { typedef struct CacheMem { const void **ptr; - size_t n_populated, n_allocated; + size_t n_populated; bool active:1; } CacheMem; @@ -1901,10 +1901,10 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags } /* expand the cachemem if needed, return true if newly (re)activated. */ -static int cachemem_maintain(CacheMem *mem, unsigned size) { +static int cachemem_maintain(CacheMem *mem, size_t size) { assert(mem); - if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) { + if (!GREEDY_REALLOC(mem->ptr, size)) { if (size > 0) return -ENOMEM; } @@ -1919,7 +1919,7 @@ static int cachemem_maintain(CacheMem *mem, unsigned size) { int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) { bool sync_keys = false, sync_values = false; - unsigned size; + size_t size; int r; assert(cache); @@ -1992,8 +1992,8 @@ IteratedCache* iterated_cache_free(IteratedCache *cache) { } int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret) { - size_t separator_len, allocated = 0, len = 0; _cleanup_free_ char *str = NULL; + size_t separator_len, len = 0; const char *value; bool first; @@ -2017,7 +2017,7 @@ int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char ** if (l == 0) continue; - if (!GREEDY_REALLOC(str, allocated, len + l + (first ? 0 : separator_len) + (wrap_with_separator ? separator_len : 0) + 1)) + if (!GREEDY_REALLOC(str, len + l + (first ? 0 : separator_len) + (wrap_with_separator ? separator_len : 0) + 1)) return -ENOMEM; if (separator_len > 0 && !first) { diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h index c20ee8eb4b..f7ade2e774 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.h +++ b/src/libnm-systemd-shared/src/basic/hashmap.h @@ -51,6 +51,7 @@ typedef struct { #define _IDX_ITERATOR_FIRST (UINT_MAX - 1) #define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) +#define ITERATOR_IS_FIRST(i) ((i).idx == _IDX_ITERATOR_FIRST) /* Macros for type checking */ #define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ @@ -371,28 +372,26 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { return _hashmap_first_key(HASHMAP_BASE(h), false); } -#define hashmap_clear_with_destructor(_s, _f) \ +#define hashmap_clear_with_destructor(h, f) \ ({ \ + Hashmap *_h = (h); \ void *_item; \ - while ((_item = hashmap_steal_first(_s))) \ - _f(_item); \ + while ((_item = hashmap_steal_first(_h))) \ + f(_item); \ + _h; \ }) -#define hashmap_free_with_destructor(_s, _f) \ - ({ \ - hashmap_clear_with_destructor(_s, _f); \ - hashmap_free(_s); \ - }) -#define ordered_hashmap_clear_with_destructor(_s, _f) \ +#define hashmap_free_with_destructor(h, f) \ + hashmap_free(hashmap_clear_with_destructor(h, f)) +#define ordered_hashmap_clear_with_destructor(h, f) \ ({ \ + OrderedHashmap *_h = (h); \ void *_item; \ - while ((_item = ordered_hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define ordered_hashmap_free_with_destructor(_s, _f) \ - ({ \ - ordered_hashmap_clear_with_destructor(_s, _f); \ - ordered_hashmap_free(_s); \ + while ((_item = ordered_hashmap_steal_first(_h))) \ + f(_item); \ + _h; \ }) +#define ordered_hashmap_free_with_destructor(h, f) \ + ordered_hashmap_free(ordered_hashmap_clear_with_destructor(h, f)) /* no hashmap_next */ void* ordered_hashmap_next(OrderedHashmap *h, const void *key); diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c index ecdeea9d3d..6eb9343f21 100644 --- a/src/libnm-systemd-shared/src/basic/hexdecoct.c +++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c @@ -117,8 +117,6 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_ uint8_t *z; int r; - assert(ret); - assert(ret_len); assert(p || l == 0); if (l == SIZE_MAX) @@ -152,8 +150,10 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_ *z = 0; - *ret_len = (size_t) (z - buf); - *ret = TAKE_PTR(buf); + if (ret_len) + *ret_len = (size_t) (z - buf); + if (ret) + *ret = TAKE_PTR(buf); return 0; @@ -711,8 +711,6 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r int r; assert(p || l == 0); - assert(ret); - assert(ret_size); if (l == SIZE_MAX) l = strlen(p); @@ -808,8 +806,10 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r *z = 0; - *ret_size = (size_t) (z - buf); - *ret = TAKE_PTR(buf); + if (ret_size) + *ret_size = (size_t) (z - buf); + if (ret) + *ret = TAKE_PTR(buf); return 0; diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h index 576ca083e0..c3fc6752cb 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.h +++ b/src/libnm-systemd-shared/src/basic/hostname-util.h @@ -28,3 +28,8 @@ static inline bool is_gateway_hostname(const char *hostname) { /* This tries to identify the valid syntaxes for the our synthetic "gateway" host. */ return STRCASE_IN_SET(hostname, "_gateway", "_gateway."); } + +static inline bool is_outbound_hostname(const char *hostname) { + /* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */ + return STRCASE_IN_SET(hostname, "_outbound", "_outbound."); +} diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c index 00bdbfcc9a..18c44cf178 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -53,7 +53,7 @@ bool in4_addr_is_link_local(const struct in_addr *a) { bool in6_addr_is_link_local(const struct in6_addr *a) { assert(a); - return IN6_IS_ADDR_LINKLOCAL(a); + return IN6_IS_ADDR_LINKLOCAL(a); /* lgtm [cpp/potentially-dangerous-function] */ } int in_addr_is_link_local(int family, const union in_addr_union *u) { @@ -118,7 +118,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) { return in4_addr_is_localhost(&u->in); if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); + return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ return -EAFNOSUPPORT; } @@ -812,6 +812,9 @@ int in_addr_prefix_from_string_auto_internal( } static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) { + assert(a); + assert(state); + siphash24_compress(&a->family, sizeof(a->family), state); siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); } @@ -819,6 +822,9 @@ static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) { int r; + assert(x); + assert(y); + r = CMP(x->family, y->family); if (r != 0) return r; @@ -828,13 +834,46 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func); +static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) { + assert(a); + assert(state); + + siphash24_compress(&a->family, sizeof(a->family), state); + siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); + siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); +} + +static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) { + int r; + + assert(x); + assert(y); + + r = CMP(x->family, y->family); + if (r != 0) + return r; + + r = CMP(x->prefixlen, y->prefixlen); + if (r != 0) + return r; + + return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); +} + +DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free); + void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) { assert(addr); + assert(state); siphash24_compress(addr, sizeof(*addr), state); } int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) { + assert(a); + assert(b); + return memcmp(a, b, sizeof(*a)); } diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h index 519ee53b3a..906b3fe97e 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -20,6 +20,12 @@ struct in_addr_data { union in_addr_union address; }; +struct in_addr_prefix { + int family; + uint8_t prefixlen; + union in_addr_union address; +}; + bool in4_addr_is_null(const struct in_addr *a); static inline bool in4_addr_is_set(const struct in_addr *a) { return !in4_addr_is_null(a); @@ -67,7 +73,13 @@ int in_addr_prefix_range( union in_addr_union *ret_start, union in_addr_union *ret_end); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); +static inline int in6_addr_to_string(const struct in6_addr *u, char **ret) { + return in_addr_to_string(AF_INET6, (const union in_addr_union*) u, ret); +} int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); +static inline int in6_addr_prefix_to_string(const struct in6_addr *u, unsigned prefixlen, char **ret) { + return in_addr_prefix_to_string(AF_INET6, (const union in_addr_union*) u, prefixlen, ret); +} int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret); @@ -111,4 +123,13 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state); int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b); extern const struct hash_ops in_addr_data_hash_ops; +extern const struct hash_ops in_addr_prefix_hash_ops; +extern const struct hash_ops in_addr_prefix_hash_ops_free; extern const struct hash_ops in6_addr_hash_ops; + +#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u" +#define IPV4_ADDRESS_FMT_VAL(address) \ + be32toh((address).s_addr) >> 24, \ + (be32toh((address).s_addr) >> 16) & 0xFFu, \ + (be32toh((address).s_addr) >> 8) & 0xFFu, \ + be32toh((address).s_addr) & 0xFFu diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c index f06dc5f65d..d5cbb8999e 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.c +++ b/src/libnm-systemd-shared/src/basic/io-util.c @@ -295,7 +295,6 @@ void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) { iovw->iovec = mfree(iovw->iovec); iovw->count = 0; - iovw->size_bytes = 0; } struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) { @@ -314,7 +313,7 @@ int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) { if (iovw->count >= IOV_MAX) return -E2BIG; - if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) + if (!GREEDY_REALLOC(iovw->iovec, iovw->count + 1)) return -ENOMEM; iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len); diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h index d98817f760..39728e06bc 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.h +++ b/src/libnm-systemd-shared/src/basic/io-util.h @@ -25,22 +25,25 @@ int fd_wait_for_event(int fd, int event, usec_t timeout); ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) { - size_t j, r = 0; + size_t r = 0; - for (j = 0; j < n; j++) + for (size_t j = 0; j < n; j++) r += i[j].iov_len; return r; } -static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) { - size_t j; +static inline bool IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) { + /* Returns true if there is nothing else to send (bytes written cover all of the iovec), + * false if there's still work to do. */ - for (j = 0; j < n; j++) { + for (size_t j = 0; j < n; j++) { size_t sub; - if (_unlikely_(k <= 0)) - break; + if (i[j].iov_len == 0) + continue; + if (k == 0) + return false; sub = MIN(i[j].iov_len, k); i[j].iov_len -= sub; @@ -48,7 +51,9 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) { k -= sub; } - return k; + assert(k == 0); /* Anything else would mean that we wrote more bytes than available, + * or the kernel reported writing more bytes than sent. */ + return true; } static inline bool FILE_SIZE_VALID(uint64_t l) { @@ -80,7 +85,6 @@ char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const ch struct iovec_wrapper { struct iovec *iovec; size_t count; - size_t size_bytes; }; struct iovec_wrapper *iovw_new(void); diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 0664977562..9ed01698a6 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -4,6 +4,7 @@ #include <stdarg.h> #include <stdbool.h> #include <stdlib.h> +#include <string.h> #include <syslog.h> #include "macro.h" @@ -325,20 +326,39 @@ void log_assert_failed_return( #endif /* NM_IGNORED */ /* Logging with level */ -#define log_full_errno(level, error, ...) \ +#define log_full_errno_zerook(level, error, ...) \ ({ \ int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ + _e = (log_get_max_level() >= LOG_PRI(_level)) \ ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ : -ERRNO_VALUE(_e); \ + _e < 0 ? _e : -ESTRPIPE; \ + }) + +#if BUILD_MODE_DEVELOPER && !defined(TEST_CODE) +# define ASSERT_NON_ZERO(x) assert((x) != 0) +#else +# define ASSERT_NON_ZERO(x) +#endif + +#define log_full_errno(level, error, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_full_errno_zerook(level, _error, __VA_ARGS__); \ }) -#define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__) +#define log_full(level, fmt, ...) \ + ({ \ + if (BUILD_MODE_DEVELOPER) \ + assert(!strstr(fmt, "%m")); \ + (void) log_full_errno_zerook(level, 0, fmt, ##__VA_ARGS__); \ + }) int log_emergency_level(void); /* Normal logging */ -#define log_debug(...) log_full_errno(LOG_DEBUG, 0, __VA_ARGS__) +#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) #define log_info(...) log_full(LOG_INFO, __VA_ARGS__) #define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) #define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) @@ -353,6 +373,29 @@ int log_emergency_level(void); #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) #define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__) +/* This logs at the specified level the first time it is called, and then + * logs at debug. If the specified level is debug, this logs only the first + * time it is called. */ +#define log_once(level, ...) \ + ({ \ + if (ONCE) \ + log_full(level, __VA_ARGS__); \ + else if (LOG_PRI(level) != LOG_DEBUG) \ + log_debug(__VA_ARGS__); \ + }) + +#define log_once_errno(level, error, ...) \ + ({ \ + int _err = (error); \ + if (ONCE) \ + _err = log_full_errno(level, _err, __VA_ARGS__); \ + else if (LOG_PRI(level) != LOG_DEBUG) \ + _err = log_debug_errno(_err, __VA_ARGS__); \ + else \ + _err = -ERRNO_VALUE(_err); \ + _err; \ + }) + #if LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) #else diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index f3f696e1d5..b0852ce5a9 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -18,8 +18,6 @@ # define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) #endif #define _sentinel_ __attribute__((__sentinel__)) -#define _section_(x) __attribute__((__section__(x))) -#define _used_ __attribute__((__used__)) #define _destructor_ __attribute__((__destructor__)) #define _deprecated_ __attribute__((__deprecated__)) #define _packed_ __attribute__((__packed__)) @@ -30,7 +28,6 @@ #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*)))) #if __GNUC__ >= 7 @@ -147,9 +144,6 @@ /* automake test harness */ #define EXIT_TEST_SKIP 77 -#define XSTRINGIFY(x) #x -#define STRINGIFY(x) XSTRINGIFY(x) - /* builtins */ #if __SIZEOF_INT__ == 4 #define BUILTIN_FFS_U32(x) __builtin_ffs(x); diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h index 179edd247b..0b04278ab4 100644 --- a/src/libnm-systemd-shared/src/basic/memory-util.h +++ b/src/libnm-systemd-shared/src/basic/memory-util.h @@ -16,11 +16,11 @@ size_t page_size(void) _pure_; #define PAGE_OFFSET(l) ((l) & (page_size() - 1)) /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { +static inline void *memcpy_safe(void *dst, const void *src, size_t n) { if (n == 0) - return; + return dst; assert(src); - memcpy(dst, src, n); + return memcpy(dst, src, n); } /* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ @@ -88,7 +88,7 @@ static inline void* erase_and_free(void *p) { if (!p) return NULL; - l = malloc_usable_size(p); + l = MALLOC_SIZEOF_SAFE(p); explicit_bzero_safe(p, l); return mfree(p); } diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index e8f7ad7f1b..ac3cab2918 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -430,4 +430,99 @@ static inline int missing_epoll_pwait2( # define epoll_pwait2 missing_epoll_pwait2 #endif + +/* ======================================================================= */ + +#if !HAVE_MOUNT_SETATTR + +#if !HAVE_STRUCT_MOUNT_ATTR +struct mount_attr { + uint64_t attr_set; + uint64_t attr_clr; + uint64_t propagation; + uint64_t userns_fd; +}; +#else +struct mount_attr; +#endif + +#ifndef MOUNT_ATTR_IDMAP +#define MOUNT_ATTR_IDMAP 0x00100000 +#endif + +#ifndef AT_RECURSIVE +#define AT_RECURSIVE 0x8000 +#endif + +static inline int missing_mount_setattr( + int dfd, + const char *path, + unsigned flags, + struct mount_attr *attr, + size_t size) { + +# if defined __NR_mount_setattr && __NR_mount_setattr >= 0 + return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define mount_setattr missing_mount_setattr +#endif + +/* ======================================================================= */ + +#if !HAVE_OPEN_TREE + +#ifndef OPEN_TREE_CLONE +#define OPEN_TREE_CLONE 1 +#endif + +#ifndef OPEN_TREE_CLOEXEC +#define OPEN_TREE_CLOEXEC O_CLOEXEC +#endif + +static inline int missing_open_tree( + int dfd, + const char *filename, + unsigned flags) { + +# if defined __NR_open_tree && __NR_open_tree >= 0 + return syscall(__NR_open_tree, dfd, filename, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define open_tree missing_open_tree +#endif + +/* ======================================================================= */ + +#if !HAVE_MOVE_MOUNT + +#ifndef MOVE_MOUNT_F_EMPTY_PATH +#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */ +#endif + +static inline int missing_move_mount( + int from_dfd, + const char *from_pathname, + int to_dfd, + const char *to_pathname, + unsigned flags) { + +# if defined __NR_move_mount && __NR_move_mount >= 0 + return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define move_mount missing_move_mount +#endif #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h index a377f20b1f..3ee47350b3 100644 --- a/src/libnm-systemd-shared/src/basic/ordered-set.h +++ b/src/libnm-systemd-shared/src/basic/ordered-set.h @@ -34,6 +34,10 @@ static inline int ordered_set_put(OrderedSet *s, void *p) { return ordered_hashmap_put((OrderedHashmap*) s, p, p); } +static inline void *ordered_set_get(OrderedSet *s, const void *p) { + return ordered_hashmap_get((OrderedHashmap*) s, p); +} + static inline unsigned ordered_set_size(OrderedSet *s) { return ordered_hashmap_size((OrderedHashmap*) s); } @@ -75,6 +79,17 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s); #define ORDERED_SET_FOREACH(e, s) \ _ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ)) +#define ordered_set_clear_with_destructor(s, f) \ + ({ \ + OrderedSet *_s = (s); \ + void *_item; \ + while ((_item = ordered_set_steal_first(_s))) \ + f(_item); \ + _s; \ + }) +#define ordered_set_free_with_destructor(s, f) \ + ordered_set_free(ordered_set_clear_with_destructor(s, f)) + DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index a867e65a26..55e4d31769 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -110,93 +110,96 @@ int path_make_absolute_cwd(const char *p, char **ret) { return 0; } -int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *f, *t, *r, *p; - unsigned n_parents = 0; - - assert(from_dir); - assert(to_path); - assert(_r); +int path_make_relative(const char *from, const char *to, char **ret) { + _cleanup_free_ char *result = NULL; + unsigned n_parents; + const char *f, *t; + int r, k; + char *p; + + assert(from); + assert(to); + assert(ret); /* Strips the common part, and adds ".." elements as necessary. */ - if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) + if (!path_is_absolute(from) || !path_is_absolute(to)) return -EINVAL; - f = strdupa(from_dir); - t = strdupa(to_path); + for (;;) { + r = path_find_first_component(&from, true, &f); + if (r < 0) + return r; - path_simplify(f, true); - path_simplify(t, true); + k = path_find_first_component(&to, true, &t); + if (k < 0) + return k; + + if (r == 0) { + /* end of 'from' */ + if (k == 0) { + /* from and to are equivalent. */ + result = strdup("."); + if (!result) + return -ENOMEM; + } else { + /* 'to' is inside of 'from'. */ + result = strdup(t); + if (!result) + return -ENOMEM; - /* Skip the common part. */ - for (;;) { - size_t a, b; - - f += *f == '/'; - t += *t == '/'; - - if (!*f) { - if (!*t) - /* from_dir equals to_path. */ - r = strdup("."); - else - /* from_dir is a parent directory of to_path. */ - r = strdup(t); - if (!r) - return -ENOMEM; - - *_r = r; - return 0; - } + path_simplify(result); - if (!*t) - break; + if (!path_is_valid(result)) + return -EINVAL; + } - a = strcspn(f, "/"); - b = strcspn(t, "/"); + *ret = TAKE_PTR(result); + return 0; + } - if (a != b || memcmp(f, t, a) != 0) + if (r != k || !strneq(f, t, r)) break; - - f += a; - t += b; } /* If we're here, then "from_dir" has one or more elements that need to * be replaced with "..". */ - /* Count the number of necessary ".." elements. */ - for (; *f;) { - size_t w; + for (n_parents = 1;; n_parents++) { + /* If this includes ".." we can't do a simple series of "..". */ + r = path_find_first_component(&from, false, &f); + if (r < 0) + return r; + if (r == 0) + break; + } - w = strcspn(f, "/"); + if (isempty(t) && n_parents * 3 > PATH_MAX) + /* PATH_MAX is counted *with* the trailing NUL byte */ + return -EINVAL; - /* If this includes ".." we can't do a simple series of "..", refuse */ - if (w == 2 && f[0] == '.' && f[1] == '.') - return -EINVAL; + result = new(char, n_parents * 3 + !isempty(t) + strlen_ptr(t)); + if (!result) + return -ENOMEM; - /* Count number of elements */ - n_parents++; + for (p = result; n_parents > 0; n_parents--) + p = mempcpy(p, "../", 3); - f += w; - f += *f == '/'; + if (isempty(t)) { + /* Remove trailing slash and terminate string. */ + *(--p) = '\0'; + *ret = TAKE_PTR(result); + return 0; } - r = new(char, n_parents * 3 + strlen(t) + 1); - if (!r) - return -ENOMEM; + strcpy(p, t); - for (p = r; n_parents > 0; n_parents--) - p = mempcpy(p, "../", 3); + path_simplify(result); - if (*t) - strcpy(p, t); - else - /* Remove trailing slash */ - *(--p) = 0; + if (!path_is_valid(result)) + return -EINVAL; - *_r = r; + *ret = TAKE_PTR(result); return 0; } @@ -227,7 +230,7 @@ int path_strv_make_absolute_cwd(char **l) { if (r < 0) return r; - path_simplify(t, false); + path_simplify(t); free_and_replace(*s, t); } @@ -328,63 +331,53 @@ char **path_strv_resolve_uniq(char **l, const char *root) { } #endif /* NM_IGNORED */ -char *path_simplify(char *path, bool kill_dots) { - char *f, *t; - bool slash = false, ignore_slash = false, absolute; +char *path_simplify(char *path) { + bool add_slash = false; + char *f = path; + int r; assert(path); - /* Removes redundant inner and trailing slashes. Also removes unnecessary dots - * if kill_dots is true. Modifies the passed string in-place. + /* Removes redundant inner and trailing slashes. Also removes unnecessary dots. + * Modifies the passed string in-place. * - * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) - * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) - * .//./foo//./bar/. becomes ././foo/./bar/. (if kill_dots is false) - * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) + * ///foo//./bar/. becomes /foo/bar + * .//./foo//./bar/. becomes foo/bar */ if (isempty(path)) return path; - absolute = path_is_absolute(path); - - f = path; - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) { - ignore_slash = true; + if (path_is_absolute(path)) f++; - } - for (t = path; *f; f++) { + for (const char *p = f;;) { + const char *e; - if (*f == '/') { - slash = true; - continue; - } + r = path_find_first_component(&p, true, &e); + if (r == 0) + break; - if (slash) { - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) - continue; + if (add_slash) + *f++ = '/'; - slash = false; - if (ignore_slash) - ignore_slash = false; - else - *(t++) = '/'; + if (r < 0) { + /* if path is invalid, then refuse to simplify remaining part. */ + memmove(f, p, strlen(p) + 1); + return path; } - *(t++) = *f; - } + memmove(f, e, r); + f += r; - /* Special rule, if we stripped everything, we either need a "/" (for the root directory) - * or "." for the current directory */ - if (t == path) { - if (absolute) - *(t++) = '/'; - else - *(t++) = '.'; + add_slash = true; } - *t = 0; + /* Special rule, if we stripped everything, we need a "." for the current directory. */ + if (f == path) + *f++ = '.'; + + *f = '\0'; return path; } @@ -420,7 +413,7 @@ int path_simplify_and_warn( lvalue, fatal ? "" : ", ignoring", path); } - path_simplify(path, true); + path_simplify(path); if (!path_is_valid(path)) return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), @@ -436,7 +429,7 @@ int path_simplify_and_warn( } #endif /* NM_IGNORED */ -char* path_startswith(const char *path, const char *prefix) { +char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) { assert(path); assert(prefix); @@ -454,33 +447,30 @@ char* path_startswith(const char *path, const char *prefix) { return NULL; for (;;) { - size_t a, b; + const char *p, *q; + int r, k; - path += strspn(path, "/"); - prefix += strspn(prefix, "/"); - - if (*prefix == 0) - return (char*) path; + r = path_find_first_component(&path, accept_dot_dot, &p); + if (r < 0) + return NULL; - if (*path == 0) + k = path_find_first_component(&prefix, accept_dot_dot, &q); + if (k < 0) return NULL; - a = strcspn(path, "/"); - b = strcspn(prefix, "/"); + if (k == 0) + return (char*) (p ?: path); - if (a != b) + if (r != k) return NULL; - if (memcmp(path, prefix, a) != 0) + if (!strneq(p, q, r)) return NULL; - - path += a; - prefix += b; } } int path_compare(const char *a, const char *b) { - int d; + int r; assert(a); assert(b); @@ -488,40 +478,45 @@ int path_compare(const char *a, const char *b) { /* A relative path and an absolute path must not compare as equal. * Which one is sorted before the other does not really matter. * Here a relative path is ordered before an absolute path. */ - d = (a[0] == '/') - (b[0] == '/'); - if (d != 0) - return d; + r = CMP(path_is_absolute(a), path_is_absolute(b)); + if (r != 0) + return r; for (;;) { - size_t j, k; + const char *aa, *bb; + int j, k; - a += strspn(a, "/"); - b += strspn(b, "/"); + j = path_find_first_component(&a, true, &aa); + k = path_find_first_component(&b, true, &bb); - if (*a == 0 && *b == 0) - return 0; + if (j < 0 || k < 0) { + /* When one of paths is invalid, order invalid path after valid one. */ + r = CMP(j < 0, k < 0); + if (r != 0) + return r; + + /* fallback to use strcmp() if both paths are invalid. */ + return strcmp(a, b); + } /* Order prefixes first: "/foo" before "/foo/bar" */ - if (*a == 0) + if (j == 0) { + if (k == 0) + return 0; return -1; - if (*b == 0) + } + if (k == 0) return 1; - j = strcspn(a, "/"); - k = strcspn(b, "/"); - /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ - d = memcmp(a, b, MIN(j, k)); - if (d != 0) - return (d > 0) - (d < 0); /* sign of d */ + r = memcmp(aa, bb, MIN(j, k)); + if (r != 0) + return r; /* Sort "/foo/a" before "/foo/aaa" */ - d = (j > k) - (j < k); /* sign of (j - k) */ - if (d != 0) - return d; - - a += j; - b += k; + r = CMP(j, k); + if (r != 0) + return r; } } @@ -535,18 +530,43 @@ bool path_equal_or_files_same(const char *a, const char *b, int flags) { } #endif /* NM_IGNORED */ -char* path_join_internal(const char *first, ...) { - char *joined, *q; +bool path_equal_filename(const char *a, const char *b) { + _cleanup_free_ char *a_basename = NULL, *b_basename = NULL; + int r; + + assert(a); + assert(b); + + r = path_extract_filename(a, &a_basename); + if (r < 0) { + log_debug_errno(r, "Failed to parse basename of %s: %m", a); + return false; + } + r = path_extract_filename(b, &b_basename); + if (r < 0) { + log_debug_errno(r, "Failed to parse basename of %s: %m", b); + return false; + } + + return path_equal(a_basename, b_basename); +} + +char* path_extend_internal(char **x, ...) { + size_t sz, old_sz; + char *q, *nx; const char *p; va_list ap; bool slash; - size_t sz; /* 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. * + * The first argument may be an already allocated string that is extended via realloc() if + * non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the + * first parameter to distinguish the two operations. + * * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some * are optional. * @@ -556,28 +576,40 @@ char* path_join_internal(const char *first, ...) { * 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*)) != POINTER_MAX) - if (!isempty(p)) - sz += 1 + strlen(p); + sz = old_sz = x ? strlen_ptr(*x) : 0; + va_start(ap, x); + while ((p = va_arg(ap, char*)) != POINTER_MAX) { + size_t add; + + if (isempty(p)) + continue; + + add = 1 + strlen(p); + if (sz > SIZE_MAX - add) { /* overflow check */ + va_end(ap); + return NULL; + } + + sz += add; + } va_end(ap); - joined = new(char, sz + 1); - if (!joined) + nx = realloc(x ? *x : NULL, GREEDY_ALLOC_ROUND_UP(sz+1)); + if (!nx) return NULL; + if (x) + *x = nx; - if (!isempty(first)) { - q = stpcpy(joined, first); - slash = endswith(first, "/"); - } else { - /* Skip empty items */ - joined[0] = 0; - q = joined; + if (old_sz > 0) + slash = nx[old_sz-1] == '/'; + else { + nx[old_sz] = 0; slash = true; /* no need to generate a slash anymore */ } - va_start(ap, first); + q = nx + old_sz; + + va_start(ap, x); while ((p = va_arg(ap, char*)) != POINTER_MAX) { if (isempty(p)) continue; @@ -590,7 +622,7 @@ char* path_join_internal(const char *first, ...) { } va_end(ap); - return joined; + return nx; } #if 0 /* NM_IGNORED */ @@ -654,7 +686,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file /* Resolve a single-component name to a full path */ for (;;) { - _cleanup_free_ char *j = NULL, *element = NULL; + _cleanup_free_ char *element = NULL; _cleanup_close_ int fd = -1; r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); @@ -666,11 +698,10 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file if (!path_is_absolute(element)) continue; - j = path_join(element, name); - if (!j) + if (!path_extend(&element, name)) return -ENOMEM; - r = check_x_access(j, ret_fd ? &fd : NULL); + r = check_x_access(element, ret_fd ? &fd : NULL); if (r < 0) { /* PATH entries which we don't have access to are ignored, as per tradition. */ if (r != -EACCES) @@ -680,7 +711,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file /* Found it! */ if (ret_filename) - *ret_filename = path_simplify(TAKE_PTR(j), false); + *ret_filename = path_simplify(TAKE_PTR(element)); if (ret_fd) *ret_fd = TAKE_FD(fd); @@ -783,6 +814,193 @@ char* dirname_malloc(const char *path) { } #endif /* NM_IGNORED */ +static const char *skip_slash_or_dot(const char *p) { + for (; !isempty(p); p++) { + if (*p == '/') + continue; + if (startswith(p, "./")) { + p++; + continue; + } + break; + } + return p; +} + +int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret) { + const char *q, *first, *end_first, *next; + size_t len; + + assert(p); + + /* When a path is input, then returns the pointer to the first component and its length, and + * move the input pointer to the next component or nul. This skips both over any '/' + * immediately *before* and *after* the first component before returning. + * + * Examples + * Input: p: "//.//aaa///bbbbb/cc" + * Output: p: "bbbbb///cc" + * ret: "aaa///bbbbb/cc" + * return value: 3 (== strlen("aaa")) + * + * Input: p: "aaa//" + * Output: p: (pointer to NUL) + * ret: "aaa//" + * return value: 3 (== strlen("aaa")) + * + * Input: p: "/", ".", "" + * Output: p: (pointer to NUL) + * ret: NULL + * return value: 0 + * + * Input: p: NULL + * Output: p: NULL + * ret: NULL + * return value: 0 + * + * Input: p: "(too long component)" + * Output: return value: -EINVAL + * + * (when accept_dot_dot is false) + * Input: p: "//..//aaa///bbbbb/cc" + * Output: return value: -EINVAL + */ + + q = *p; + + first = skip_slash_or_dot(q); + if (isempty(first)) { + *p = first; + if (ret) + *ret = NULL; + return 0; + } + if (streq(first, ".")) { + *p = first + 1; + if (ret) + *ret = NULL; + return 0; + } + + end_first = strchrnul(first, '/'); + len = end_first - first; + + if (len > NAME_MAX) + return -EINVAL; + if (!accept_dot_dot && len == 2 && first[0] == '.' && first[1] == '.') + return -EINVAL; + + next = skip_slash_or_dot(end_first); + + *p = next + streq(next, "."); + if (ret) + *ret = first; + return len; +} + +static const char *skip_slash_or_dot_backward(const char *path, const char *q) { + assert(path); + + for (; q >= path; q--) { + if (*q == '/') + continue; + if (q > path && strneq(q - 1, "/.", 2)) + continue; + break; + } + return q; +} + +int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret) { + const char *q, *last_end, *last_begin; + size_t len; + + /* Similar to path_find_first_component(), but search components from the end. + * + * Examples + * Input: path: "//.//aaa///bbbbb/cc//././" + * next: NULL + * Output: next: "/cc//././" + * ret: "cc//././" + * return value: 2 (== strlen("cc")) + * + * Input: path: "//.//aaa///bbbbb/cc//././" + * next: "/cc//././" + * Output: next: "///bbbbb/cc//././" + * ret: "bbbbb/cc//././" + * return value: 5 (== strlen("bbbbb")) + * + * Input: path: "/", ".", "", or NULL + * Output: next: equivalent to path + * ret: NULL + * return value: 0 + * + * Input: path: "(too long component)" + * Output: return value: -EINVAL + * + * (when accept_dot_dot is false) + * Input: path: "//..//aaa///bbbbb/cc/..//" + * Output: return value: -EINVAL + */ + + if (isempty(path)) { + if (next) + *next = path; + if (ret) + *ret = NULL; + return 0; + } + + if (next && *next) { + if (*next < path || *next > path + strlen(path)) + return -EINVAL; + if (*next == path) { + if (ret) + *ret = NULL; + return 0; + } + if (!IN_SET(**next, '\0', '/')) + return -EINVAL; + q = *next - 1; + } else + q = path + strlen(path) - 1; + + q = skip_slash_or_dot_backward(path, q); + if ((q < path) || /* the root directory */ + (q == path && *q == '.')) { /* path is "." or "./" */ + if (next) + *next = path; + if (ret) + *ret = NULL; + return 0; + } + + last_end = q + 1; + + while (q >= path && *q != '/') + q--; + + last_begin = q + 1; + len = last_end - last_begin; + + if (len > NAME_MAX) + return -EINVAL; + if (!accept_dot_dot && len == 2 && strneq(last_begin, "..", 2)) + return -EINVAL; + + if (next) { + q = skip_slash_or_dot_backward(path, q); + if (q < path) + *next = path; + else + *next = q + 1; + } + + if (ret) + *ret = last_begin; + return len; +} + const char *last_path_component(const char *path) { /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory. @@ -826,82 +1044,81 @@ const char *last_path_component(const char *path) { return path + k; } -int path_extract_filename(const char *p, char **ret) { +int path_extract_filename(const char *path, char **ret) { _cleanup_free_ char *a = NULL; - const char *c; - size_t n; + const char *c, *next = NULL; + int r; /* 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. Returns: * - * -EINVAL → if the passed in path is not a valid path - * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir itself is specified + * -EINVAL → if the path is not valid + * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir + * itself or "." is specified * -ENOMEM → no memory * - * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to indicate - * the referenced file must be a directory. + * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to + * indicate the referenced file must be a directory. * * This function guarantees to return a fully valid filename, i.e. one that passes * filename_is_valid() – this means "." and ".." are not accepted. */ - if (!path_is_valid(p)) + if (!path_is_valid(path)) return -EINVAL; - /* Special case the root dir, because in that case we simply have no filename, but - * last_path_component() won't complain */ - if (path_equal(p, "/")) + r = path_find_last_component(path, false, &next, &c); + if (r < 0) + return r; + if (r == 0) /* root directory */ return -EADDRNOTAVAIL; - c = last_path_component(p); - n = strcspn(c, "/"); - - a = strndup(c, n); + a = strndup(c, r); if (!a) return -ENOMEM; - if (!filename_is_valid(a)) - return -EINVAL; - *ret = TAKE_PTR(a); - return c[n] == '/' ? O_DIRECTORY : 0; + return strlen(c) > (size_t)r ? O_DIRECTORY : 0; } -int path_extract_directory(const char *p, char **ret) { +int path_extract_directory(const char *path, char **ret) { _cleanup_free_ char *a = NULL; - const char *c; + const char *c, *next = NULL; + int r; /* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns: * - * -EINVAL → if the passed in path is not a valid path + * -EINVAL → if the path is not valid * -EDESTADDRREQ → if no directory was specified in the passed in path, i.e. only a filename was passed - * -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e. the root dir itself was specified + * -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e. + * the root dir itself or "." was specified * -ENOMEM → no memory (surprise!) * * This function guarantees to return a fully valid path, i.e. one that passes path_is_valid(). */ - if (!path_is_valid(p)) - return -EINVAL; - - /* Special case the root dir, because otherwise for an input of "///" last_path_component() returns - * the pointer to the last slash only, which might be seen as a valid path below. */ - if (path_equal(p, "/")) - return -EADDRNOTAVAIL; - - c = last_path_component(p); - - /* Delete trailing slashes, but keep one */ - while (c > p+1 && c[-1] == '/') - c--; - - if (p == c) /* No path whatsoever? Then return a recognizable error */ - return -EDESTADDRREQ; + r = path_find_last_component(path, false, &next, &c); + if (r < 0) + return r; + if (r == 0) /* empty or root */ + return isempty(path) ? -EINVAL : -EADDRNOTAVAIL; + if (next == path) { + if (*path != '/') /* filename only */ + return -EDESTADDRREQ; + + a = strdup("/"); + if (!a) + return -ENOMEM; + *ret = TAKE_PTR(a); + return 0; + } - a = strndup(p, c - p); + a = strndup(path, next - path); if (!a) return -ENOMEM; + path_simplify(a); + if (!path_is_valid(a)) return -EINVAL; @@ -928,44 +1145,30 @@ bool filename_is_valid(const char *p) { return true; } -bool path_is_valid(const char *p) { - +bool path_is_valid_full(const char *p, bool accept_dot_dot) { if (isempty(p)) return false; for (const char *e = p;;) { - size_t n; + int r; + + r = path_find_first_component(&e, accept_dot_dot, NULL); + if (r < 0) + return false; - /* Skip over slashes */ - e += strspn(e, "/"); if (e - p >= PATH_MAX) /* Already reached the maximum length for a path? (PATH_MAX is counted * *with* the trailing NUL byte) */ return false; if (*e == 0) /* End of string? Yay! */ return true; - - /* Skip over one component */ - n = strcspn(e, "/"); - if (n > NAME_MAX) /* One component larger than NAME_MAX? (NAME_MAX is counted *without* the - * trailing NUL byte) */ - return false; - - e += n; } } bool path_is_normalized(const char *p) { - - if (!path_is_valid(p)) - return false; - - if (dot_or_dot_dot(p)) + if (!path_is_safe(p)) return false; - if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) - return false; - - if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) + if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) return false; if (strstr(p, "//")) @@ -1167,15 +1370,15 @@ bool dot_or_dot_dot(const char *path) { } #if 0 /* NM_IGNORED */ -bool empty_or_root(const char *root) { +bool empty_or_root(const char *path) { /* For operations relative to some root directory, returns true if the specified root directory is redundant, * i.e. either / or NULL or the empty string or any equivalent. */ - if (!root) + if (isempty(path)) return true; - return root[strspn(root, "/")] == 0; + return path_equal(path, "/"); } bool path_strv_contains(char **l, const char *path) { diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h index 48f0c873db..8a7d5e2571 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.h +++ b/src/libnm-systemd-shared/src/basic/path-util.h @@ -58,15 +58,22 @@ int path_split_and_make_absolute(const char *p, char ***ret); char* path_make_absolute(const char *p, const char *prefix); int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); -int path_make_relative(const char *from_dir, const char *to_path, char **_r); -char* path_startswith(const char *path, const char *prefix) _pure_; +int path_make_relative(const char *from, const char *to, char **ret); +char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_; +static inline char* path_startswith(const char *path, const char *prefix) { + return path_startswith_full(path, prefix, true); +} 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_internal(const char *first, ...); -#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, POINTER_MAX) +/* Compares only the last portion of the input paths, ie: the filenames */ +bool path_equal_filename(const char *a, const char *b); + +char* path_extend_internal(char **x, ...); +#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX) +#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX) -char* path_simplify(char *path, bool kill_dots); +char* path_simplify(char *path); enum { PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */ @@ -104,7 +111,7 @@ int fsck_exists(const char *fstype); * directory. Excludes the specified directory itself */ #define PATH_FOREACH_PREFIX(prefix, path) \ for (char *_slash = ({ \ - path_simplify(strcpy(prefix, path), false); \ + path_simplify(strcpy(prefix, path)); \ streq(prefix, "/") ? NULL : strrchr(prefix, '/'); \ }); \ _slash && ((*_slash = 0), true); \ @@ -113,7 +120,7 @@ int fsck_exists(const char *fstype); /* 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); \ + path_simplify(strcpy(prefix, path)); \ if (streq(prefix, "/")) \ prefix[0] = 0; \ strrchr(prefix, 0); \ @@ -147,12 +154,20 @@ int fsck_exists(const char *fstype); }) char* dirname_malloc(const char *path); +int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret); +int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret); const char *last_path_component(const char *path); -int path_extract_filename(const char *p, char **ret); -int path_extract_directory(const char *p, char **ret); +int path_extract_filename(const char *path, char **ret); +int path_extract_directory(const char *path, char **ret); bool filename_is_valid(const char *p) _pure_; -bool path_is_valid(const char *p) _pure_; +bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_; +static inline bool path_is_valid(const char *p) { + return path_is_valid_full(p, true); +} +static inline bool path_is_safe(const char *p) { + return path_is_valid_full(p, false); +} bool path_is_normalized(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); @@ -178,7 +193,7 @@ static inline const char *skip_dev_prefix(const char *p) { return e ?: p; } -bool empty_or_root(const char *root); +bool empty_or_root(const char *path); static inline const char *empty_to_root(const char *path) { return isempty(path) ? "/" : path; } diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 2d9f852541..d538dda7a8 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -129,64 +129,136 @@ int get_process_comm(pid_t pid, char **ret) { return 0; } -int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) { - _cleanup_free_ char *t = NULL, *ans = NULL; +static int get_process_cmdline_nulstr( + pid_t pid, + size_t max_size, + ProcessCmdlineFlags flags, + char **ret, + size_t *ret_size) { + const char *p; + char *t; size_t k; int r; - assert(line); - assert(pid >= 0); - - /* Retrieves a process' command line. Replaces non-utf8 bytes by replacement character (�). If - * max_columns is != -1 will return a string of the specified console width at most, abbreviated with - * an ellipsis. If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command - * line set (the case for kernel threads), or has a command line that resolves to the empty string - * will return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of - * input data. + /* Retrieves a process' command line as a "sized nulstr", i.e. possibly without the last NUL, but + * with a specified size. * - * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and - * comm_fallback is false). Returns 0 and sets *line otherwise. */ + * If PROCESS_CMDLINE_COMM_FALLBACK is specified in flags and the process has no command line set + * (the case for kernel threads), or has a command line that resolves to the empty string, will + * return the "comm" name of the process instead. This will use at most _SC_ARG_MAX bytes of input + * data. + * + * Returns an error, 0 if output was read but is truncated, 1 otherwise. + */ p = procfs_file_alloca(pid, "cmdline"); - r = read_full_virtual_file(p, &t, &k); + r = read_virtual_file(p, max_size, &t, &k); /* Let's assume that each input byte results in >= 1 + * columns of output. We ignore zero-width codepoints. */ if (r == -ENOENT) return -ESRCH; if (r < 0) return r; - if (k > 0) { - /* Arguments are separated by NULs. Let's replace those with spaces. */ - for (size_t i = 0; i < k - 1; i++) - if (t[i] == '\0') - t[i] = ' '; - } else { + if (k == 0) { + t = mfree(t); + if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK)) return -ENOENT; /* Kernel threads have no argv[] */ - _cleanup_free_ char *t2 = NULL; + _cleanup_free_ char *comm = NULL; - r = get_process_comm(pid, &t2); + r = get_process_comm(pid, &comm); if (r < 0) return r; - free(t); - t = strjoin("[", t2, "]"); + t = strjoin("[", comm, "]"); if (!t) return -ENOMEM; + + k = strlen(t); + r = k <= max_size; + if (r == 0) /* truncation */ + t[max_size] = '\0'; } - delete_trailing_chars(t, WHITESPACE); + *ret = t; + *ret_size = k; + return r; +} - bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8(); +int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) { + _cleanup_free_ char *t = NULL; + size_t k; + char *ans; - ans = escape_non_printable_full(t, max_columns, eight_bit); - if (!ans) - return -ENOMEM; + assert(line); + assert(pid >= 0); + + /* Retrieve and format a commandline. See above for discussion of retrieval options. + * + * There are two main formatting modes: + * + * - when PROCESS_CMDLINE_QUOTE is specified, output is quoted in C/Python style. If no shell special + * characters are present, this output can be copy-pasted into the terminal to execute. UTF-8 + * output is assumed. + * + * - otherwise, a compact non-roundtrippable form is returned. Non-UTF8 bytes are replaced by �. The + * returned string is of the specified console width at most, abbreviated with an ellipsis. + * + * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and + * PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */ + + int full = get_process_cmdline_nulstr(pid, max_columns, flags, &t, &k); + if (full < 0) + return full; + + if (flags & (PROCESS_CMDLINE_QUOTE | PROCESS_CMDLINE_QUOTE_POSIX)) { + ShellEscapeFlags shflags = SHELL_ESCAPE_EMPTY | + FLAGS_SET(flags, PROCESS_CMDLINE_QUOTE_POSIX) * SHELL_ESCAPE_POSIX; + + assert(!(flags & PROCESS_CMDLINE_USE_LOCALE)); + + _cleanup_strv_free_ char **args = NULL; + + args = strv_parse_nulstr(t, k); + if (!args) + return -ENOMEM; + + for (size_t i = 0; args[i]; i++) { + char *e; + + e = shell_maybe_quote(args[i], shflags); + if (!e) + return -ENOMEM; + + free_and_replace(args[i], e); + } + + ans = strv_join(args, " "); + if (!ans) + return -ENOMEM; + + } else { + /* Arguments are separated by NULs. Let's replace those with spaces. */ + for (size_t i = 0; i < k - 1; i++) + if (t[i] == '\0') + t[i] = ' '; + + delete_trailing_chars(t, WHITESPACE); + + bool eight_bit = (flags & PROCESS_CMDLINE_USE_LOCALE) && !is_locale_utf8(); + + ans = escape_non_printable_full(t, max_columns, + eight_bit * XESCAPE_8_BIT | !full * XESCAPE_FORCE_ELLIPSIS); + if (!ans) + return -ENOMEM; + + ans = str_realloc(ans); + } - (void) str_realloc(&ans); - *line = TAKE_PTR(ans); + *line = ans; return 0; } @@ -535,7 +607,7 @@ int get_process_root(pid_t pid, char **root) { int get_process_environ(pid_t pid, char **env) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *outcome = NULL; - size_t allocated = 0, sz = 0; + size_t sz = 0; const char *p; int r; @@ -556,7 +628,7 @@ int get_process_environ(pid_t pid, char **env) { if (sz >= ENVIRONMENT_BLOCK_MAX) return -ENOBUFS; - if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) + if (!GREEDY_REALLOC(outcome, sz + 5)) return -ENOMEM; r = safe_fgetc(f, &c); @@ -1244,8 +1316,10 @@ int safe_fork_full( saved_ssp = &saved_ss; } - if (flags & FORK_NEW_MOUNTNS) - pid = raw_clone(SIGCHLD|CLONE_NEWNS); + if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0) + pid = raw_clone(SIGCHLD| + (FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) | + (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0)); else pid = fork(); if (pid < 0) diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index f7850944e5..21e4eb9121 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -35,6 +35,8 @@ typedef enum ProcessCmdlineFlags { PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0, PROCESS_CMDLINE_USE_LOCALE = 1 << 1, + PROCESS_CMDLINE_QUOTE = 1 << 2, + PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3, } ProcessCmdlineFlags; int get_process_comm(pid_t pid, char **name); @@ -165,6 +167,7 @@ typedef enum ForkFlags { FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */ FORK_FLUSH_STDIO = 1 << 12, /* fflush() stdout (and stderr) before forking */ + FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */ } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h index 52b6f4984c..0f8673934f 100644 --- a/src/libnm-systemd-shared/src/basic/set.h +++ b/src/libnm-systemd-shared/src/basic/set.h @@ -95,17 +95,16 @@ static inline void *set_steal_first(Set *s) { return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } -#define set_clear_with_destructor(_s, _f) \ +#define set_clear_with_destructor(s, f) \ ({ \ + Set *_s = (s); \ void *_item; \ while ((_item = set_steal_first(_s))) \ - _f(_item); \ - }) -#define set_free_with_destructor(_s, _f) \ - ({ \ - set_clear_with_destructor(_s, _f); \ - set_free(_s); \ + f(_item); \ + _s; \ }) +#define set_free_with_destructor(s, f) \ + set_free(set_clear_with_destructor(s, f)) /* no set_steal_first_key */ /* no set_first_key */ diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index 0a16cf1de8..c991a396b8 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -153,7 +153,7 @@ char *delete_chars(char *s, const char *bad) { } char *delete_trailing_chars(char *s, const char *bad) { - char *p, *c = s; + char *c = s; /* Drops all specified bad characters, at the end of the string */ @@ -163,7 +163,7 @@ char *delete_trailing_chars(char *s, const char *bad) { if (!bad) bad = WHITESPACE; - for (p = s; *p; p++) + for (char *p = s; *p; p++) if (!strchr(bad, *p)) c = p + 1; @@ -197,34 +197,28 @@ char ascii_toupper(char x) { } char *ascii_strlower(char *t) { - char *p; - assert(t); - for (p = t; *p; p++) + for (char *p = t; *p; p++) *p = ascii_tolower(*p); return t; } char *ascii_strupper(char *t) { - char *p; - assert(t); - for (p = t; *p; p++) + for (char *p = t; *p; p++) *p = ascii_toupper(*p); return t; } char *ascii_strlower_n(char *t, size_t n) { - size_t i; - if (n <= 0) return t; - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) t[i] = ascii_tolower(t[i]); return t; @@ -256,10 +250,8 @@ int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { } bool chars_intersect(const char *a, const char *b) { - const char *p; - /* Returns true if any of the chars in a are in b. */ - for (p = a; *p; p++) + for (const char *p = a; *p; p++) if (strchr(b, *p)) return true; @@ -267,8 +259,6 @@ bool chars_intersect(const char *a, const char *b) { } bool string_has_cc(const char *p, const char *ok) { - const char *t; - assert(p); /* @@ -277,14 +267,11 @@ bool string_has_cc(const char *p, const char *ok) { * considered OK. */ - for (t = p; *t; t++) { + for (const char *t = p; *t; t++) { if (ok && strchr(ok, *t)) continue; - if (*t > 0 && *t < ' ') - return true; - - if (*t == 127) + if (char_is_cc(*t)) return true; } @@ -473,7 +460,7 @@ char *cellescape(char *buf, size_t len, const char *s) { * very end. */ - size_t i = 0, last_char_width[4] = {}, k = 0, j; + size_t i = 0, last_char_width[4] = {}, k = 0; assert(len > 0); /* at least a terminating NUL */ @@ -502,7 +489,7 @@ char *cellescape(char *buf, size_t len, const char *s) { /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4 * characters ideally, but the buffer is shorter than that in the first place take what we can get */ - for (j = 0; j < ELEMENTSOF(last_char_width); j++) { + for (size_t j = 0; j < ELEMENTSOF(last_char_width); j++) { if (i + 4 <= len) /* nice, we reached our space goal */ break; @@ -541,7 +528,7 @@ char* strshorten(char *s, size_t l) { } char *strreplace(const char *text, const char *old_string, const char *new_string) { - size_t l, old_len, new_len, allocated = 0; + size_t l, old_len, new_len; char *t, *ret = NULL; const char *f; @@ -555,7 +542,7 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin new_len = strlen(new_string); l = strlen(text); - if (!GREEDY_REALLOC(ret, allocated, l+1)) + if (!GREEDY_REALLOC(ret, l+1)) return NULL; f = text; @@ -571,7 +558,7 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin d = t - ret; nl = l - old_len + new_len; - if (!GREEDY_REALLOC(ret, allocated, nl + 1)) + if (!GREEDY_REALLOC(ret, nl + 1)) return mfree(ret); l = nl; @@ -811,6 +798,92 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) { } #endif /* NM_IGNORED */ +int strextendf_with_separator(char **x, const char *separator, const char *format, ...) { + size_t m, a, l_separator; + va_list ap; + int l; + + /* Appends a formatted string to the specified string. Don't use this in inner loops, since then + * we'll spend a tonload of time in determining the length of the string passed in, over and over + * again. */ + + assert(x); + assert(format); + + l_separator = isempty(*x) ? 0 : strlen_ptr(separator); + + /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */ + if (*x) { + m = strlen(*x); + a = MALLOC_SIZEOF_SAFE(*x); + assert(a >= m + 1); + } else + m = a = 0; + + if (a - m < 17 + l_separator) { /* if there's less than 16 chars space, then enlarge the buffer first */ + char *n; + + if (_unlikely_(l_separator > SIZE_MAX - 64)) /* overflow check #1 */ + return -ENOMEM; + if (_unlikely_(m > SIZE_MAX - 64 - l_separator)) /* overflow check #2 */ + return -ENOMEM; + + n = realloc(*x, m + 64 + l_separator); + if (!n) + return -ENOMEM; + + *x = n; + a = MALLOC_SIZEOF_SAFE(*x); + } + + /* Now, let's try to format the string into it */ + memcpy_safe(*x + m, separator, l_separator); + va_start(ap, format); + l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap); + va_end(ap); + + assert(l >= 0); + + if ((size_t) l < a - m - l_separator) { + char *n; + + /* Nice! This worked. We are done. But first, let's return the extra space we don't + * need. This should be a cheap operation, since we only lower the allocation size here, + * never increase. */ + n = realloc(*x, m + (size_t) l + l_separator + 1); + if (n) + *x = n; + } else { + char *n; + + /* Wasn't enough. Then let's allocate exactly what we need. */ + + if (_unlikely_((size_t) l > SIZE_MAX - (l_separator + 1))) /* overflow check #1 */ + goto oom; + if (_unlikely_(m > SIZE_MAX - ((size_t) l + l_separator + 1))) /* overflow check #2 */ + goto oom; + + a = m + (size_t) l + l_separator + 1; + n = realloc(*x, a); + if (!n) + goto oom; + *x = n; + + va_start(ap, format); + l = vsnprintf(*x + m + l_separator, a - m - l_separator, format, ap); + va_end(ap); + + assert((size_t) l < a - m - l_separator); + } + + return 0; + +oom: + /* truncate the bytes added after the first vsnprintf() attempt again */ + (*x)[m] = 0; + return -ENOMEM; +} + char *strrep(const char *s, unsigned n) { char *r, *p; size_t l; @@ -911,14 +984,12 @@ int free_and_strndup(char **p, const char *s, size_t l) { } bool string_is_safe(const char *p) { - const char *t; - if (!p) return false; /* Checks if the specified string contains no quotes or control characters */ - for (t = p; *t; t++) { + for (const char *t = p; *t; t++) { if (*t > 0 && *t < ' ') /* no control characters */ return false; diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index cb2881b64d..9155e50ba8 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -129,6 +129,14 @@ static inline bool _pure_ in_charset(const char *s, const char* charset) { return s[strspn(s, charset)] == '\0'; } +static inline bool char_is_cc(char p) { + /* char is unsigned on some architectures, e.g. aarch64. So, compiler may warn the condition + * p >= 0 is always true. See #19543. Hence, let's cast to unsigned before the comparison. Note + * that the cast in the right hand side is redundant, as according to the C standard, compilers + * automatically cast a signed value to unsigned when comparing with an unsigned variable. Just + * for safety and readability. */ + return (uint8_t) p < (uint8_t) ' ' || p == 127; +} bool string_has_cc(const char *p, const char *ok) _pure_; char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); @@ -148,10 +156,12 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); char *strextend_with_separator_internal(char **x, const char *separator, ...) _sentinel_; - #define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL) #define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL) +int strextendf_with_separator(char **x, const char *separator, const char *format, ...) _printf_(3,4); +#define strextendf(x, ...) strextendf_with_separator(x, NULL, __VA_ARGS__) + char *strrep(const char *s, unsigned n); int split_pair(const char *s, const char *sep, char **l, char **r); @@ -214,17 +224,13 @@ static inline void *memory_startswith_no_case(const void *p, size_t sz, const ch return (uint8_t*) p + n; } -static inline char* str_realloc(char **p) { - /* Reallocate *p to actual size */ - - if (!*p) - return NULL; +static inline char* str_realloc(char *p) { + /* Reallocate *p to actual size. Ignore failure, and return the original string on error. */ - char *t = realloc(*p, strlen(*p) + 1); - if (!t) + if (!p) return NULL; - return (*p = t); + return realloc(p, strlen(p) + 1) ?: p; } char* string_erase(char *x); diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index 469875c17e..e24ef5352f 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -270,7 +270,7 @@ int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) { int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) { _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; + size_t n = 0; int r; assert(t); @@ -285,7 +285,7 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla if (r == 0) break; - if (!GREEDY_REALLOC(l, allocated, n + 2)) + if (!GREEDY_REALLOC(l, n + 2)) return -ENOMEM; l[n++] = TAKE_PTR(word); @@ -307,7 +307,7 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla #if 0 /* NM_IGNORED */ int strv_split_colon_pairs(char ***t, const char *s) { _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; + size_t n = 0; int r; assert(t); @@ -337,7 +337,7 @@ int strv_split_colon_pairs(char ***t, const char *s) { if (!second_or_empty) return -ENOMEM; - if (!GREEDY_REALLOC(l, allocated, n + 3)) + if (!GREEDY_REALLOC(l, n + 3)) return -ENOMEM; l[n++] = TAKE_PTR(first); @@ -716,9 +716,9 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { * is provided separately. */ - size_t n_allocated = 0, n = 0; _cleanup_free_ char *m = NULL; char * const *i; + size_t n = 0; assert(ret); assert(ret_size); @@ -728,7 +728,7 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { z = strlen(*i); - if (!GREEDY_REALLOC(m, n_allocated, n + z + 2)) + if (!GREEDY_REALLOC(m, n + z + 2)) return -ENOMEM; memcpy(m + n, *i, z + 1); diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index f16856f602..4b7042f78d 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -9,7 +9,6 @@ #include <sys/mman.h> #include <sys/time.h> #include <sys/timerfd.h> -#include <sys/timex.h> #include <sys/types.h> #include <unistd.h> @@ -1250,25 +1249,10 @@ int parse_nsec(const char *t, nsec_t *nsec) { return 0; } -bool ntp_synced(void) { - struct timex txc = {}; - - if (adjtimex(&txc) < 0) - return false; - - /* Consider the system clock synchronized if the reported maximum error is smaller than the maximum - * value (16 seconds). Ignore the STA_UNSYNC flag as it may have been set to prevent the kernel from - * touching the RTC. */ - if (txc.maxerror >= 16000000) - return false; - - return true; -} - int get_timezones(char ***ret) { _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **zones = NULL; - size_t n_zones = 0, n_allocated = 0; + size_t n_zones = 0; int r; assert(ret); @@ -1277,14 +1261,13 @@ int get_timezones(char ***ret) { if (!zones) return -ENOMEM; - n_allocated = 2; n_zones = 1; f = fopen("/usr/share/zoneinfo/zone1970.tab", "re"); if (f) { for (;;) { - _cleanup_free_ char *line = NULL; - char *p, *w; + _cleanup_free_ char *line = NULL, *w = NULL; + char *p; size_t k; r = read_line(f, LONG_LINE_MAX, &line); @@ -1315,12 +1298,10 @@ int get_timezones(char ***ret) { if (!w) return -ENOMEM; - if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) { - free(w); + if (!GREEDY_REALLOC(zones, n_zones + 2)) return -ENOMEM; - } - zones[n_zones++] = w; + zones[n_zones++] = TAKE_PTR(w); zones[n_zones] = NULL; } @@ -1554,7 +1535,7 @@ int time_change_fd(void) { .it_value.tv_sec = TIME_T_MAX, }; - _cleanup_close_ int fd; + _cleanup_close_ int fd = -1; assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h index d716074fbe..cfde189818 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.h +++ b/src/libnm-systemd-shared/src/basic/time-util.h @@ -133,8 +133,6 @@ int parse_sec_def_infinity(const char *t, usec_t *usec); int parse_time(const char *t, usec_t *usec, usec_t default_unit); int parse_nsec(const char *t, nsec_t *nsec); -bool ntp_synced(void); - int get_timezones(char ***l); bool timezone_is_valid(const char *name, int log_level); diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index 870676c706..fd470515a5 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -67,7 +67,7 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { /* This is much like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern) { - int fd = -1; /* avoid false maybe-uninitialized warning */ + int fd = -1; /* avoid false maybe-uninitialized warning */ assert(pattern); @@ -127,13 +127,10 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { return -EINVAL; if (d) { - char *j; - - j = path_join(d, nf); - if (!j) + if (!path_extend(&d, nf)) return -ENOMEM; - *ret = path_simplify(j, false); + *ret = path_simplify(TAKE_PTR(d)); } else *ret = TAKE_PTR(nf); @@ -173,13 +170,10 @@ int tempfn_random(const char *p, const char *extra, char **ret) { return -EINVAL; if (d) { - char *j; - - j = path_join(d, nf); - if (!j) + if (!path_extend(&d, nf)) return -ENOMEM; - *ret = path_simplify(j, false); + *ret = path_simplify(TAKE_PTR(d)); } else *ret = TAKE_PTR(nf); @@ -224,7 +218,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { *x = 0; - *ret = path_simplify(t, false); + *ret = path_simplify(t); return 0; } diff --git a/src/libnm-systemd-shared/src/basic/user-util.h b/src/libnm-systemd-shared/src/basic/user-util.h index 20ff415e2e..fd00b47b76 100644 --- a/src/libnm-systemd-shared/src/basic/user-util.h +++ b/src/libnm-systemd-shared/src/basic/user-util.h @@ -109,3 +109,14 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream); #endif bool is_nologin_shell(const char *shell); + +int is_this_me(const char *username); + +/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */ +#define PASSWORD_LOCKED_AND_INVALID "!*" + +/* A password indicating "look in shadow file, please!" for "struct passwd"'s .pw_passwd */ +#define PASSWORD_SEE_SHADOW "x" + +/* A password indicating "hey, no password required for login" */ +#define PASSWORD_NONE "" diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c index f73530a222..dfa010e847 100644 --- a/src/libnm-systemd-shared/src/basic/utf8.c +++ b/src/libnm-systemd-shared/src/basic/utf8.c @@ -198,8 +198,7 @@ char *utf8_escape_invalid(const char *str) { } *s = '\0'; - (void) str_realloc(&p); - return p; + return str_realloc(p); } #if 0 /* NM_IGNORED */ @@ -216,7 +215,7 @@ static int utf8_char_console_width(const char *str) { return unichar_iswide(c) ? 2 : 1; } -char *utf8_escape_non_printable_full(const char *str, size_t console_width) { +char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis) { char *p, *s, *prev_s; size_t n = 0; /* estimated print width */ @@ -233,8 +232,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) { int len; char *saved_s = s; - if (!*str) /* done! */ - goto finish; + if (!*str) { /* done! */ + if (force_ellipsis) + goto truncation; + else + goto finish; + } len = utf8_encoded_valid_unichar(str, SIZE_MAX); if (len > 0) { @@ -278,15 +281,14 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) { truncation: /* Try to go back one if we don't have enough space for the ellipsis */ - if (n + 1 >= console_width) + if (n + 1 > console_width) s = prev_s; s = mempcpy(s, "…", strlen("…")); finish: *s = '\0'; - (void) str_realloc(&p); - return p; + return str_realloc(p); } #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h index 219ca89184..b0e969f655 100644 --- a/src/libnm-systemd-shared/src/basic/utf8.h +++ b/src/libnm-systemd-shared/src/basic/utf8.h @@ -25,9 +25,9 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin #define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) char *utf8_escape_invalid(const char *s); -char *utf8_escape_non_printable_full(const char *str, size_t console_width); +char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis); static inline char *utf8_escape_non_printable(const char *str) { - return utf8_escape_non_printable_full(str, SIZE_MAX); + return utf8_escape_non_printable_full(str, SIZE_MAX, false); } size_t utf8_encode_unichar(char *out_utf8, char32_t g); diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index 790920eb23..967518600d 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -7,11 +7,17 @@ #include "type.h" +#define _align_(x) __attribute__((__aligned__(x))) #define _const_ __attribute__((__const__)) #define _pure_ __attribute__((__pure__)) +#define _section_(x) __attribute__((__section__(x))) +#define _used_ __attribute__((__used__)) #define _unused_ __attribute__((__unused__)) #define _cleanup_(x) __attribute__((__cleanup__(x))) +#define XSTRINGIFY(x) #x +#define STRINGIFY(x) XSTRINGIFY(x) + #ifndef __COVERITY__ # define VOID_0 ((void)0) #else @@ -44,6 +50,17 @@ #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) #define UNIQ __COUNTER__ +/* Note that this works differently from pthread_once(): this macro does + * not synchronize code execution, i.e. code that is run conditionalized + * on this macro will run concurrently to all other code conditionalized + * the same way, there's no ordering or completion enforced. */ +#define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) +#define __ONCE(o) \ + ({ \ + static bool (o) = false; \ + __sync_bool_compare_and_swap(&(o), false, true); \ + }) + #undef MAX #define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) #define __MAX(aq, a, bq, b) \ diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c index 95e4a93afd..473f6a0250 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.c +++ b/src/libnm-systemd-shared/src/shared/dns-domain.c @@ -413,7 +413,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, 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; + size_t n = 0; const char *p; bool first = true; int r; @@ -445,7 +445,7 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r } if (_ret) { - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); @@ -477,12 +477,12 @@ finish: if (_ret) { if (n == 0) { /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ - if (!GREEDY_REALLOC(ret, allocated, 2)) + if (!GREEDY_REALLOC(ret, 2)) return -ENOMEM; ret[n++] = '.'; } else { - if (!GREEDY_REALLOC(ret, allocated, n + 1)) + if (!GREEDY_REALLOC(ret, n + 1)) return -ENOMEM; } @@ -994,7 +994,7 @@ bool dns_service_name_is_valid(const char *name) { l = strlen(name); if (l <= 0) return false; - if (l > 63) + if (l > DNS_LABEL_MAX) return false; return true; @@ -1200,13 +1200,11 @@ int dns_name_skip(const char *a, unsigned n_labels, const char **ret) { int dns_name_count_labels(const char *name) { unsigned n = 0; - const char *p; int r; assert(name); - p = name; - for (;;) { + for (const char *p = name;;) { r = dns_name_parent(&p); if (r < 0) return r; @@ -1219,7 +1217,7 @@ int dns_name_count_labels(const char *name) { n++; } - return (int) n; + return n; } int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) { @@ -1346,7 +1344,7 @@ int dns_name_apply_idna(const char *name, char **ret) { return -EINVAL; #elif HAVE_LIBIDN _cleanup_free_ char *buf = NULL; - size_t n = 0, allocated = 0; + size_t n = 0; bool first = true; int r, q; @@ -1368,7 +1366,7 @@ int dns_name_apply_idna(const char *name, char **ret) { if (q > 0) r = q; - if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + if (!GREEDY_REALLOC(buf, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX); @@ -1386,7 +1384,7 @@ int dns_name_apply_idna(const char *name, char **ret) { if (n > DNS_HOSTNAME_MAX) return -EINVAL; - if (!GREEDY_REALLOC(buf, allocated, n + 1)) + if (!GREEDY_REALLOC(buf, n + 1)) return -ENOMEM; buf[n] = 0; diff --git a/src/libnm-systemd-shared/src/shared/log-link.h b/src/libnm-systemd-shared/src/shared/log-link.h index 3a4dcaa267..51eaa0c06e 100644 --- a/src/libnm-systemd-shared/src/shared/log-link.h +++ b/src/libnm-systemd-shared/src/shared/log-link.h @@ -3,13 +3,38 @@ #include "log.h" -#define log_interface_full_errno(ifname, level, error, ...) \ +#define log_interface_full_errno_zerook(ifname, level, error, ...) \ ({ \ const char *_ifname = (ifname); \ _ifname ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _ifname, NULL, NULL, ##__VA_ARGS__) : \ log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \ }) +#define log_interface_full_errno(ifname, level, error, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_interface_full_errno_zerook(ifname, level, _error, __VA_ARGS__); \ + }) + +#define log_interface_prefix_full_errno_zerook(prefix, ifname_expr, error, fmt, ...) \ + ({ \ + int _e = (error); \ + if (DEBUG_LOGGING) \ + log_interface_full_errno_zerook( \ + ifname_expr, \ + LOG_DEBUG, _e, prefix fmt, \ + ##__VA_ARGS__); \ + -ERRNO_VALUE(_e); \ + }) + +#define log_interface_prefix_full_errno(prefix, ifname_expr, error, fmt, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_interface_prefix_full_errno_zerook(prefix, ifname_expr, _error, fmt, ##__VA_ARGS__); \ + }) + /* * The following macros append INTERFACE= to the message. * The macros require a struct named 'Link' which contains 'char *ifname': @@ -21,15 +46,22 @@ * See, network/networkd-link.h for example. */ -#define log_link_full_errno(link, level, error, ...) \ +#define log_link_full_errno_zerook(link, level, error, ...) \ ({ \ const Link *_l = (link); \ - log_interface_full_errno(_l ? _l->ifname : NULL, level, error, ##__VA_ARGS__); \ + log_interface_full_errno_zerook(_l ? _l->ifname : NULL, level, error, __VA_ARGS__); \ + }) + +#define log_link_full_errno(link, level, error, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_link_full_errno_zerook(link, level, _error, __VA_ARGS__); \ }) -#define log_link_full(link, level, ...) (void) log_link_full_errno(link, level, 0, __VA_ARGS__) +#define log_link_full(link, level, ...) (void) log_link_full_errno_zerook(link, level, 0, __VA_ARGS__) -#define log_link_debug(link, ...) log_link_full_errno(link, LOG_DEBUG, 0, __VA_ARGS__) +#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, __VA_ARGS__) #define log_link_info(link, ...) log_link_full(link, LOG_INFO, __VA_ARGS__) #define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, __VA_ARGS__) #define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, __VA_ARGS__) |