all: merge branch 'th/uuid-normalize'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/880
This commit is contained in:
Thomas Haller 2021-06-04 09:47:30 +02:00
commit 0d928c670d
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
18 changed files with 470 additions and 119 deletions

View file

@ -7,6 +7,7 @@
#include "nm-settings-storage.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "nm-utils.h"
#include "nm-settings-plugin.h"
@ -72,7 +73,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
case PROP_UUID:
/* construct-only */
self->_uuid = g_value_dup_string(value);
nm_assert(!self->_uuid || nm_utils_is_uuid(self->_uuid));
nm_assert(!self->_uuid || nm_uuid_is_normalized(self->_uuid));
break;
case PROP_FILENAME:
/* construct-only */
@ -97,7 +98,7 @@ NMSettingsStorage *
nm_settings_storage_new(NMSettingsPlugin *plugin, const char *uuid, const char *filename)
{
nm_assert(NM_IS_SETTINGS_PLUGIN(plugin));
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
return g_object_new(NM_TYPE_SETTINGS_STORAGE,
NM_SETTINGS_STORAGE_PLUGIN,

View file

@ -60,14 +60,14 @@ nm_settings_storage_get_plugin(const NMSettingsStorage *self)
return self->_plugin;
}
gboolean nm_uuid_is_valid_full(const char *str);
gboolean nm_uuid_is_normalized_full(const char *str);
static inline const char *
nm_settings_storage_get_uuid(const NMSettingsStorage *self)
{
g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
nm_assert(nm_uuid_is_valid_full(self->_uuid));
nm_assert(nm_uuid_is_normalized_full(self->_uuid));
return self->_uuid;
}
@ -76,7 +76,7 @@ nm_settings_storage_get_uuid_opt(const NMSettingsStorage *self)
{
g_return_val_if_fail(NM_IS_SETTINGS_STORAGE(self), NULL);
nm_assert(!self->_uuid || nm_uuid_is_valid_full(self->_uuid));
nm_assert(!self->_uuid || nm_uuid_is_normalized_full(self->_uuid));
return self->_uuid;
}

View file

@ -21,6 +21,7 @@
#endif
#include "libnm-core-aux-intern/nm-common-macros.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "libnm-glib-aux/nm-keyfile-aux.h"
#include "libnm-core-intern/nm-keyfile-internal.h"
#include "nm-dbus-interface.h"
@ -139,7 +140,7 @@ nm_assert_storage_data_lst(CList *head)
u = nm_settings_storage_get_uuid(sd->storage);
if (!uuid) {
uuid = u;
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
} else
nm_assert(nm_streq0(uuid, u));
}
@ -182,7 +183,7 @@ _sett_conn_entry_new(const char *uuid)
SettConnEntry *sett_conn_entry;
gsize l_p_1;
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
l_p_1 = strlen(uuid) + 1;
@ -1461,7 +1462,7 @@ _add_connection_to_first_plugin(NMSettings * self,
uuid = nm_connection_get_uuid(new_connection);
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
for (iter = priv->plugins; iter; iter = iter->next) {
NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN(iter->data);
@ -2295,7 +2296,7 @@ nm_settings_delete_connection(NMSettings * self,
nm_assert(NM_IS_SETTINGS_STORAGE(cur_storage));
uuid = nm_settings_storage_get_uuid(cur_storage);
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
sett_conn_entry = _sett_conn_entries_get(self, uuid);

View file

@ -7,6 +7,7 @@
#include "nms-ifcfg-rh-storage.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "nm-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-connection.h"
@ -88,7 +89,7 @@ static NMSIfcfgRHStorage *
_storage_new(NMSIfcfgRHPlugin *plugin, const char *uuid, const char *filename)
{
nm_assert(NMS_IS_IFCFG_RH_PLUGIN(plugin));
nm_assert(!uuid || nm_utils_is_uuid(uuid));
nm_assert(!uuid || nm_uuid_is_normalized(uuid));
nm_assert(filename && filename[0] == '/');
return g_object_new(NMS_TYPE_IFCFG_RH_STORAGE,

View file

@ -15,6 +15,7 @@
#include "libnm-std-aux/c-list-util.h"
#include "libnm-glib-aux/nm-c-list.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "libnm-glib-aux/nm-io-utils.h"
#include "nm-connection.h"
@ -233,7 +234,7 @@ _read_from_file(const char * full_filename,
nm_assert(!connection
|| (_nm_connection_verify(connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
nm_assert(!connection || nm_utils_is_uuid(nm_connection_get_uuid(connection)));
nm_assert(!connection || nm_uuid_is_normalized(nm_connection_get_uuid(connection)));
return connection;
}
@ -260,7 +261,7 @@ _nm_assert_storage(gpointer plugin /* NMSKeyfilePlugin */,
uuid = nms_keyfile_storage_get_uuid(storage);
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
nm_assert(((NMSKeyfileStorage *) storage)->is_meta_data
|| !(((NMSKeyfileStorage *) storage)->u.conn_data.connection)
@ -1106,7 +1107,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone(NMSKeyfilePlugin * self,
const char * dirname;
nm_assert(NMS_IS_KEYFILE_PLUGIN(self));
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
nm_assert(!out_storage || !*out_storage);
nm_assert(!shadowed_storage || (set && in_memory));

View file

@ -7,6 +7,7 @@
#include "nms-keyfile-storage.h"
#include "libnm-glib-aux/nm-uuid.h"
#include "nm-utils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "nms-keyfile-plugin.h"
@ -108,7 +109,7 @@ _storage_new(NMSKeyfilePlugin * plugin,
NMSKeyfileStorage *self;
nm_assert(NMS_IS_KEYFILE_PLUGIN(plugin));
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
nm_assert(filename && filename[0] == '/');
self = g_object_new(NMS_TYPE_KEYFILE_STORAGE,
@ -135,7 +136,7 @@ nms_keyfile_storage_new_tombstone(NMSKeyfilePlugin * plugin,
{
NMSKeyfileStorage *self;
nm_assert(nm_utils_is_uuid(uuid));
nm_assert(nm_uuid_is_normalized(uuid));
nm_assert(filename && filename[0] == '/');
nm_assert(nms_keyfile_nmmeta_check_filename(filename, NULL));
nm_assert(NM_IN_SET(storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, NMS_KEYFILE_STORAGE_TYPE_RUN));

View file

@ -10,6 +10,7 @@
#include <stdlib.h>
#include <sys/stat.h>
#include "libnm-glib-aux/nm-uuid.h"
#include "libnm-glib-aux/nm-io-utils.h"
#include "libnm-core-intern/nm-keyfile-internal.h"
#include "nm-utils.h"
@ -30,9 +31,9 @@
const char *
nms_keyfile_nmmeta_check_filename(const char *filename, guint *out_uuid_len)
{
const char *uuid;
const char *s;
gsize len;
char uuid[37];
s = strrchr(filename, '/');
if (s)
@ -50,17 +51,18 @@ nms_keyfile_nmmeta_check_filename(const char *filename, guint *out_uuid_len)
len -= NM_STRLEN(NM_KEYFILE_PATH_SUFFIX_NMMETA);
if (!NM_IN_SET(len, 36, 40)) {
if (len != 36) {
/* the remaining part of the filename has not the right length to
* contain a UUID (according to nm_utils_is_uuid()). */
* contain a UUID (according to nm_uuid_is_normalized()). */
return NULL;
}
uuid = nm_strndup_a(100, filename, len, NULL);
if (!nm_utils_is_uuid(uuid))
memcpy(uuid, filename, 36);
uuid[36] = '\0';
if (!nm_uuid_is_normalized(uuid))
return NULL;
NM_SET_OUT(out_uuid_len, len);
NM_SET_OUT(out_uuid_len, 36);
return filename;
}
@ -71,7 +73,7 @@ nms_keyfile_nmmeta_filename(const char *dirname, const char *uuid, gboolean temp
char *s;
nm_assert(dirname && dirname[0] == '/');
nm_assert(nm_utils_is_uuid(uuid) && !strchr(uuid, '/'));
nm_assert(nm_uuid_is_normalized(uuid) && !strchr(uuid, '/'));
if (g_snprintf(filename,
sizeof(filename),
@ -80,7 +82,7 @@ nms_keyfile_nmmeta_filename(const char *dirname, const char *uuid, gboolean temp
NM_KEYFILE_PATH_SUFFIX_NMMETA,
temporary ? "~" : "")
>= sizeof(filename)) {
/* valid uuids are limited in length (nm_utils_is_uuid). The buffer should always
/* valid uuids are limited in length (nm_uuid_is_normalized). The buffer should always
* be large enough. */
nm_assert_not_reached();
}
@ -137,7 +139,11 @@ nms_keyfile_nmmeta_read(const char * dirname,
NMMETA_KF_GROUP_NAME_NMMETA,
NMMETA_KF_KEY_NAME_NMMETA_UUID,
NULL);
if (!nm_streq0(v_uuid, uuid))
if (!v_uuid)
return FALSE;
if (strncmp(v_uuid, uuid, uuid_len) != 0)
return FALSE;
if (v_uuid[uuid_len] != '\0')
return FALSE;
loaded_path = g_key_file_get_string(kf,
@ -211,7 +217,7 @@ nms_keyfile_nmmeta_write(const char *dirname,
int errsv;
nm_assert(dirname && dirname[0] == '/');
nm_assert(nm_utils_is_uuid(uuid) && !strchr(uuid, '/'));
nm_assert(nm_uuid_is_normalized(uuid) && !strchr(uuid, '/'));
nm_assert(!loaded_path || loaded_path[0] == '/');
nm_assert(!shadowed_storage || loaded_path);

View file

@ -27,6 +27,8 @@ gboolean _nm_connection_detect_slave_type_full(NMSettingConnection *s_con,
const char *_nm_connection_detect_bluetooth_type(NMConnection *self);
gboolean _nm_setting_connection_verify_secondaries(GArray *secondaries, GError **error);
gboolean _nm_connection_verify_required_interface_name(NMConnection *connection, GError **error);
int _nm_setting_ovs_interface_verify_interface_type(NMSettingOvsInterface *self,

View file

@ -725,6 +725,113 @@ _normalize_connection_uuid(NMConnection *self)
return TRUE;
}
gboolean
_nm_setting_connection_verify_secondaries(GArray *secondaries, GError **error)
{
const char *const *strv;
const guint len = nm_g_array_len(secondaries);
guint has_normalizable = FALSE;
gboolean has_invalid = FALSE;
gboolean has_duplicate = FALSE;
guint i;
if (len == 0)
return TRUE;
/* For historic reasons, the secondaries were not normalized/validated.
*
* Now, when we find any invalid/non-normalized values, we reject/normalize
* them. We also filter out duplicates. */
strv = nm_strvarray_get_strv_non_empty(secondaries, NULL);
for (i = 0; i < len; i++) {
const char *uuid = strv[i];
gboolean normalized;
if (!nm_uuid_is_valid_nm(uuid, &normalized, NULL)) {
has_invalid = TRUE;
goto out;
}
if (normalized)
has_normalizable = TRUE;
}
if (has_normalizable)
goto out;
if (len > 1) {
gs_free const char **strv_to_free = NULL;
const char ** strv2;
strv2 = nm_utils_strv_dup_shallow_maybe_a(20, strv, len, &strv_to_free);
nm_utils_strv_sort(strv2, len);
has_duplicate = nm_strv_has_duplicate(strv2, len, TRUE);
}
out:
if (has_invalid) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("has an invalid UUID"));
} else if (has_normalizable) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("has a UUID that requires normalization"));
} else if (has_duplicate) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("has duplicate UUIDs"));
} else
return TRUE;
g_prefix_error(error,
"%s.%s: ",
NM_SETTING_CONNECTION_SETTING_NAME,
NM_SETTING_CONNECTION_SECONDARIES);
return FALSE;
}
static gboolean
_normalize_connection_secondaries(NMConnection *self)
{
NMSettingConnection *s_con = nm_connection_get_setting_connection(self);
GArray * secondaries;
gs_strfreev char ** strv = NULL;
guint i;
guint j;
nm_assert(s_con);
secondaries = _nm_setting_connection_get_secondaries(s_con);
if (nm_g_array_len(secondaries) == 0)
return FALSE;
if (_nm_setting_connection_verify_secondaries(secondaries, NULL))
return FALSE;
strv = nm_strvarray_get_strv_non_empty_dup(secondaries, NULL);
for (i = 0, j = 0; strv[i]; i++) {
gs_free char *s = g_steal_pointer(&strv[i]);
char uuid_normalized[37];
gboolean uuid_is_normalized;
if (!nm_uuid_is_valid_nm(s, &uuid_is_normalized, uuid_normalized))
continue;
if (nm_utils_strv_find_first(strv, j, uuid_is_normalized ? uuid_normalized : s) >= 0)
continue;
strv[j++] = uuid_is_normalized ? g_strdup(uuid_normalized) : g_steal_pointer(&s);
}
strv[j] = NULL;
g_object_set(s_con, NM_SETTING_CONNECTION_SECONDARIES, strv, NULL);
return TRUE;
}
static gboolean
_normalize_connection_type(NMConnection *self)
{
@ -1617,6 +1724,7 @@ _connection_normalize(NMConnection *connection,
was_modified |= _normalize_connection_uuid(connection);
was_modified |= _normalize_connection_type(connection);
was_modified |= _normalize_connection_slave_type(connection);
was_modified |= _normalize_connection_secondaries(connection);
was_modified |= _normalize_required_settings(connection);
was_modified |= _normalize_invalid_slave_port_settings(connection);
was_modified |= _normalize_ip_config(connection, parameters);
@ -1740,7 +1848,7 @@ _nm_connection_ensure_normalized(NMConnection * connection,
nm_assert(NM_IS_CONNECTION(connection));
nm_assert(!out_connection_clone || !*out_connection_clone);
nm_assert(!expected_uuid || nm_utils_is_uuid(expected_uuid));
nm_assert(!expected_uuid || nm_uuid_is_normalized(expected_uuid));
if (expected_uuid) {
if (nm_streq0(expected_uuid, nm_connection_get_uuid(connection)))

View file

@ -70,26 +70,26 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection,
PROP_MUD_URL, );
typedef struct {
GArray *permissions;
GSList *secondaries; /* secondary connections to activate with the base connection */
char * id;
char * uuid;
char * stable_id;
char * interface_name;
char * type;
char * master;
char * slave_type;
char * zone;
char * mud_url;
guint64 timestamp;
int autoconnect_priority;
int autoconnect_retries;
int multi_connect;
int auth_retries;
int mdns;
int llmnr;
int wait_device_timeout;
guint gateway_ping_timeout;
GArray * permissions;
GArray * secondaries;
char * id;
char * uuid;
char * stable_id;
char * interface_name;
char * type;
char * master;
char * slave_type;
char * zone;
char * mud_url;
guint64 timestamp;
int autoconnect_priority;
int autoconnect_retries;
int multi_connect;
int auth_retries;
int mdns;
int llmnr;
int wait_device_timeout;
guint gateway_ping_timeout;
NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
NMMetered metered;
NMSettingConnectionLldp lldp;
@ -741,6 +741,12 @@ nm_setting_connection_get_autoconnect_slaves(NMSettingConnection *setting)
return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->autoconnect_slaves;
}
GArray *
_nm_setting_connection_get_secondaries(NMSettingConnection *setting)
{
return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries;
}
/**
* nm_setting_connection_get_num_secondaries:
* @setting: the #NMSettingConnection
@ -752,27 +758,37 @@ nm_setting_connection_get_num_secondaries(NMSettingConnection *setting)
{
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), 0);
return g_slist_length(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries);
return nm_g_array_len(NM_SETTING_CONNECTION_GET_PRIVATE(setting)->secondaries);
}
/**
* nm_setting_connection_get_secondary:
* @setting: the #NMSettingConnection
* @idx: the zero-based index of the secondary connection UUID entry
* @idx: the zero-based index of the secondary connection UUID entry.
* Access one past the length of secondaries is ok and will return
* %NULL. Otherwise, it is a user error.
*
* Returns: the secondary connection UUID at index @idx
* Returns: the secondary connection UUID at index @idx or
* %NULL if @idx is the number of secondaries.
**/
const char *
nm_setting_connection_get_secondary(NMSettingConnection *setting, guint32 idx)
{
NMSettingConnectionPrivate *priv;
guint secondaries_len;
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), NULL);
priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
g_return_val_if_fail(idx <= g_slist_length(priv->secondaries), NULL);
return (const char *) g_slist_nth_data(priv->secondaries, idx);
secondaries_len = nm_g_array_len(priv->secondaries);
if (idx >= secondaries_len) {
/* access one past the length is OK. */
g_return_val_if_fail(idx == secondaries_len, NULL);
return NULL;
}
return nm_strvarray_get_idx(priv->secondaries, idx);
}
/**
@ -806,19 +822,16 @@ gboolean
nm_setting_connection_add_secondary(NMSettingConnection *setting, const char *sec_uuid)
{
NMSettingConnectionPrivate *priv;
GSList * iter;
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE);
g_return_val_if_fail(sec_uuid != NULL, FALSE);
g_return_val_if_fail(sec_uuid[0] != '\0', FALSE);
g_return_val_if_fail(sec_uuid, FALSE);
priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
for (iter = priv->secondaries; iter; iter = g_slist_next(iter)) {
if (!strcmp(sec_uuid, (char *) iter->data))
return FALSE;
}
priv->secondaries = g_slist_append(priv->secondaries, g_strdup(sec_uuid));
if (nm_strvarray_find_first(priv->secondaries, sec_uuid) >= 0)
return FALSE;
nm_strvarray_add(nm_strvarray_ensure(&priv->secondaries), sec_uuid);
_notify(setting, PROP_SECONDARIES);
return TRUE;
}
@ -834,16 +847,14 @@ void
nm_setting_connection_remove_secondary(NMSettingConnection *setting, guint32 idx)
{
NMSettingConnectionPrivate *priv;
GSList * elt;
g_return_if_fail(NM_IS_SETTING_CONNECTION(setting));
priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
elt = g_slist_nth(priv->secondaries, idx);
g_return_if_fail(elt != NULL);
g_free(elt->data);
priv->secondaries = g_slist_delete_link(priv->secondaries, elt);
g_return_if_fail(idx < nm_g_array_len(priv->secondaries));
g_array_remove_index(priv->secondaries, idx);
_notify(setting, PROP_SECONDARIES);
}
@ -860,19 +871,15 @@ gboolean
nm_setting_connection_remove_secondary_by_value(NMSettingConnection *setting, const char *sec_uuid)
{
NMSettingConnectionPrivate *priv;
GSList * iter;
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE);
g_return_val_if_fail(sec_uuid != NULL, FALSE);
g_return_val_if_fail(sec_uuid[0] != '\0', FALSE);
g_return_val_if_fail(sec_uuid, FALSE);
priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
for (iter = priv->secondaries; iter; iter = g_slist_next(iter)) {
if (!strcmp(sec_uuid, (char *) iter->data)) {
priv->secondaries = g_slist_delete_link(priv->secondaries, iter);
_notify(setting, PROP_SECONDARIES);
return TRUE;
}
if (nm_strvarray_remove_first(priv->secondaries, sec_uuid)) {
_notify(setting, PROP_SECONDARIES);
return TRUE;
}
return FALSE;
}
@ -1474,6 +1481,9 @@ after_interface_name:
return NM_SETTING_VERIFY_NORMALIZABLE;
}
if (!_nm_setting_connection_verify_secondaries(priv->secondaries, error))
return NM_SETTING_VERIFY_NORMALIZABLE;
return TRUE;
}
@ -1622,7 +1632,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
g_value_set_enum(value, nm_setting_connection_get_autoconnect_slaves(setting));
break;
case PROP_SECONDARIES:
g_value_take_boxed(value, _nm_utils_slist_to_strv(priv->secondaries, TRUE));
g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(priv->secondaries, NULL));
break;
case PROP_GATEWAY_PING_TIMEOUT:
g_value_set_uint(value, priv->gateway_ping_timeout);
@ -1734,8 +1744,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
priv->autoconnect_slaves = g_value_get_enum(value);
break;
case PROP_SECONDARIES:
g_slist_free_full(priv->secondaries, g_free);
priv->secondaries = _nm_utils_strv_to_slist(g_value_get_boxed(value), TRUE);
nm_strvarray_set_strv(&priv->secondaries, g_value_get_boxed(value));
break;
case PROP_GATEWAY_PING_TIMEOUT:
priv->gateway_ping_timeout = g_value_get_uint(value);
@ -1814,7 +1823,7 @@ finalize(GObject *object)
g_free(priv->slave_type);
g_free(priv->mud_url);
nm_clear_pointer(&priv->permissions, g_array_unref);
g_slist_free_full(priv->secondaries, g_free);
nm_clear_pointer(&priv->secondaries, g_array_unref);
G_OBJECT_CLASS(nm_setting_connection_parent_class)->finalize(object);
}

View file

@ -80,7 +80,7 @@ nm_setting_match_get_interface_name(NMSettingMatch *setting, int idx)
g_return_val_if_fail(setting->interface_name && idx >= 0 && idx < setting->interface_name->len,
NULL);
return g_array_index(setting->interface_name, const char *, idx);
return nm_strvarray_get_idx(setting->interface_name, idx);
}
/**
@ -96,8 +96,7 @@ void
nm_setting_match_add_interface_name(NMSettingMatch *setting, const char *interface_name)
{
g_return_if_fail(NM_IS_SETTING_MATCH(setting));
g_return_if_fail(interface_name != NULL);
g_return_if_fail(interface_name[0] != '\0');
g_return_if_fail(interface_name);
nm_strvarray_add(nm_strvarray_ensure(&setting->interface_name), interface_name);
_notify(setting, PROP_INTERFACE_NAME);
@ -138,8 +137,7 @@ gboolean
nm_setting_match_remove_interface_name_by_value(NMSettingMatch *setting, const char *interface_name)
{
g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE);
g_return_val_if_fail(interface_name != NULL, FALSE);
g_return_val_if_fail(interface_name[0] != '\0', FALSE);
g_return_val_if_fail(interface_name, FALSE);
if (nm_strvarray_remove_first(setting->interface_name, interface_name)) {
_notify(setting, PROP_INTERFACE_NAME);
@ -225,7 +223,7 @@ nm_setting_match_get_kernel_command_line(NMSettingMatch *setting, guint idx)
g_return_val_if_fail(setting->kernel_command_line && idx < setting->kernel_command_line->len,
NULL);
return g_array_index(setting->kernel_command_line, const char *, idx);
return nm_strvarray_get_idx(setting->kernel_command_line, idx);
}
/**
@ -241,8 +239,7 @@ void
nm_setting_match_add_kernel_command_line(NMSettingMatch *setting, const char *kernel_command_line)
{
g_return_if_fail(NM_IS_SETTING_MATCH(setting));
g_return_if_fail(kernel_command_line != NULL);
g_return_if_fail(kernel_command_line[0] != '\0');
g_return_if_fail(kernel_command_line);
nm_strvarray_add(nm_strvarray_ensure(&setting->kernel_command_line), kernel_command_line);
_notify(setting, PROP_KERNEL_COMMAND_LINE);
@ -284,8 +281,7 @@ nm_setting_match_remove_kernel_command_line_by_value(NMSettingMatch *setting,
const char * kernel_command_line)
{
g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE);
g_return_val_if_fail(kernel_command_line != NULL, FALSE);
g_return_val_if_fail(kernel_command_line[0] != '\0', FALSE);
g_return_val_if_fail(kernel_command_line, FALSE);
if (nm_strvarray_remove_first(setting->kernel_command_line, kernel_command_line)) {
_notify(setting, PROP_KERNEL_COMMAND_LINE);
@ -367,7 +363,7 @@ nm_setting_match_get_driver(NMSettingMatch *setting, guint idx)
g_return_val_if_fail(setting->driver && idx < setting->driver->len, NULL);
return g_array_index(setting->driver, const char *, idx);
return nm_strvarray_get_idx(setting->driver, idx);
}
/**
@ -383,8 +379,7 @@ void
nm_setting_match_add_driver(NMSettingMatch *setting, const char *driver)
{
g_return_if_fail(NM_IS_SETTING_MATCH(setting));
g_return_if_fail(driver != NULL);
g_return_if_fail(driver[0] != '\0');
g_return_if_fail(driver);
nm_strvarray_add(nm_strvarray_ensure(&setting->driver), driver);
_notify(setting, PROP_DRIVER);
@ -425,8 +420,7 @@ gboolean
nm_setting_match_remove_driver_by_value(NMSettingMatch *setting, const char *driver)
{
g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE);
g_return_val_if_fail(driver != NULL, FALSE);
g_return_val_if_fail(driver[0] != '\0', FALSE);
g_return_val_if_fail(driver, FALSE);
if (nm_strvarray_remove_first(setting->driver, driver)) {
_notify(setting, PROP_DRIVER);
@ -508,7 +502,7 @@ nm_setting_match_get_path(NMSettingMatch *setting, guint idx)
g_return_val_if_fail(setting->path && idx < setting->path->len, NULL);
return g_array_index(setting->path, const char *, idx);
return nm_strvarray_get_idx(setting->path, idx);
}
/**
@ -524,8 +518,7 @@ void
nm_setting_match_add_path(NMSettingMatch *setting, const char *path)
{
g_return_if_fail(NM_IS_SETTING_MATCH(setting));
g_return_if_fail(path != NULL);
g_return_if_fail(path[0] != '\0');
g_return_if_fail(path);
nm_strvarray_add(nm_strvarray_ensure(&setting->path), path);
_notify(setting, PROP_PATH);
@ -566,8 +559,7 @@ gboolean
nm_setting_match_remove_path_by_value(NMSettingMatch *setting, const char *path)
{
g_return_val_if_fail(NM_IS_SETTING_MATCH(setting), FALSE);
g_return_val_if_fail(path != NULL, FALSE);
g_return_val_if_fail(path[0] != '\0', FALSE);
g_return_val_if_fail(path, FALSE);
if (nm_strvarray_remove_first(setting->path, path)) {
_notify(setting, PROP_PATH);
@ -624,16 +616,17 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
switch (prop_id) {
case PROP_INTERFACE_NAME:
g_value_set_boxed(value, nm_strvarray_get_strv_non_empty(self->interface_name, NULL));
g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->interface_name, NULL));
break;
case PROP_KERNEL_COMMAND_LINE:
g_value_set_boxed(value, nm_strvarray_get_strv_non_empty(self->kernel_command_line, NULL));
g_value_take_boxed(value,
nm_strvarray_get_strv_non_empty_dup(self->kernel_command_line, NULL));
break;
case PROP_DRIVER:
g_value_set_boxed(value, nm_strvarray_get_strv_non_empty(self->driver, NULL));
g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->driver, NULL));
break;
case PROP_PATH:
g_value_set_boxed(value, nm_strvarray_get_strv_non_empty(self->path, NULL));
g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(self->path, NULL));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@ -707,7 +700,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
if (self->interface_name) {
for (i = 0; i < self->interface_name->len; i++) {
if (nm_str_is_empty(g_array_index(self->interface_name, const char *, i))) {
if (nm_str_is_empty(nm_strvarray_get_idx(self->interface_name, i))) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
@ -723,7 +716,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
if (self->kernel_command_line) {
for (i = 0; i < self->kernel_command_line->len; i++) {
if (nm_str_is_empty(g_array_index(self->kernel_command_line, const char *, i))) {
if (nm_str_is_empty(nm_strvarray_get_idx(self->kernel_command_line, i))) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
@ -739,7 +732,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
if (self->driver) {
for (i = 0; i < self->driver->len; i++) {
if (nm_str_is_empty(g_array_index(self->driver, const char *, i))) {
if (nm_str_is_empty(nm_strvarray_get_idx(self->driver, i))) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
@ -755,7 +748,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
if (self->path) {
for (i = 0; i < self->path->len; i++) {
if (nm_str_is_empty(g_array_index(self->path, const char *, i))) {
if (nm_str_is_empty(nm_strvarray_get_idx(self->path, i))) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,

View file

@ -5060,7 +5060,7 @@ test_setting_connection_changed_signal(void)
ASSERT_CHANGED(nm_setting_connection_add_secondary(s_con, uuid));
ASSERT_CHANGED(nm_setting_connection_remove_secondary(s_con, 0));
NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(elt != NULL));
NMTST_EXPECT_LIBNM_CRITICAL(NMTST_G_RETURN_MSG(idx < nm_g_array_len(priv->secondaries)));
ASSERT_UNCHANGED(nm_setting_connection_remove_secondary(s_con, 1));
g_test_assert_expected_messages();
@ -7788,7 +7788,7 @@ static void
__test_uuid(const char *expected_uuid, const char *str, gssize slen, char *uuid_test)
{
g_assert(uuid_test);
g_assert(nm_utils_is_uuid(uuid_test));
g_assert(nm_uuid_is_normalized(uuid_test));
if (strcmp(uuid_test, expected_uuid)) {
g_error("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s",

View file

@ -4465,6 +4465,123 @@ test_setting_metadata(void)
/*****************************************************************************/
static void
test_setting_connection_secondaries_verify(void)
{
gs_unref_object NMConnection *con = NULL;
NMSettingConnection * s_con;
guint i_run;
guint i_word;
con = nmtst_create_minimal_connection("test-sec", NULL, NM_SETTING_WIRED_SETTING_NAME, &s_con);
nmtst_connection_normalize(con);
for (i_run = 0; i_run < 100; i_run++) {
guint word_len = nmtst_get_rand_word_length(NULL);
gs_unref_ptrarray GPtrArray *arr = NULL;
gs_unref_ptrarray GPtrArray *arr_norm = NULL;
gboolean was_normalized;
gboolean was_normalized2;
/* create a random list of invalid, normalizable and normalized UUIDs. */
arr = g_ptr_array_new();
for (i_word = 0; i_word < word_len; i_word++) {
g_ptr_array_add(arr,
(char *) nmtst_rand_select((const char *) "",
"52c3feb9-3aa2-46f6-a07b-1765918699eb",
"52C3feb9-3aa2-46f6-a07b-1765918699eb",
"52C3feb9-3aa2-46f6-a07b-1765918699eb",
"f86dfb13df764894ab3836b7cdb9d82dde8b27c4",
"52C3feb93-aa2-46f6-a07b-1765918699eb",
"bogus"));
}
g_ptr_array_add(arr, NULL);
/* set the new list of secondaries, and assert that the result is as expected. */
nmtst_assert_connection_verifies_without_normalization(con);
g_object_set(s_con, NM_SETTING_CONNECTION_SECONDARIES, arr->pdata, NULL);
#define _assert_secondaries(s_con, expected) \
G_STMT_START \
{ \
NMSettingConnection *const _s_con = (s_con); \
const char *const * _expected = (expected); \
GArray * _secondaries; \
const guint _expected_len = NM_PTRARRAY_LEN(_expected); \
gs_strfreev char ** _sec_strv = NULL; \
guint _i; \
\
g_assert(_expected); \
\
if (nmtst_get_rand_bool()) { \
_secondaries = _nm_setting_connection_get_secondaries(_s_con); \
g_assert_cmpint(_expected_len, ==, nm_g_array_len(_secondaries)); \
g_assert((_expected_len == 0) == (!_secondaries)); \
g_assert(nm_utils_strv_equal(_expected, nm_strvarray_get_strv(&_secondaries, NULL))); \
} \
\
if (nmtst_get_rand_bool()) { \
g_object_get(_s_con, NM_SETTING_CONNECTION_SECONDARIES, &_sec_strv, NULL); \
g_assert_cmpint(_expected_len, ==, NM_PTRARRAY_LEN(_sec_strv)); \
g_assert((_expected_len == 0) == (!_sec_strv)); \
g_assert(nm_utils_strv_equal(_expected, _sec_strv ?: NM_STRV_EMPTY())); \
} \
\
g_assert_cmpint(nm_setting_connection_get_num_secondaries(_s_con), ==, _expected_len); \
if (nmtst_get_rand_bool()) { \
for (_i = 0; _i < _expected_len; _i++) { \
g_assert_cmpstr(nm_setting_connection_get_secondary(_s_con, _i), \
==, \
_expected[_i]); \
} \
g_assert_null(nm_setting_connection_get_secondary(_s_con, _expected_len)); \
} \
} \
G_STMT_END
_assert_secondaries(s_con, (const char *const *) arr->pdata);
/* reimplement the normalization that we expect to happen and
* create an array @arr_norm with the expected result after normalization. */
arr_norm = g_ptr_array_new_with_free_func(g_free);
for (i_word = 0; i_word < word_len; i_word++) {
const char *s = arr->pdata[i_word];
gboolean is_normalized;
char uuid_normalized[37];
if (!nm_uuid_is_valid_nm(s, &is_normalized, uuid_normalized))
continue;
if (is_normalized)
s = uuid_normalized;
if (nm_utils_strv_find_first((char **) arr_norm->pdata, arr_norm->len, s) >= 0)
continue;
g_ptr_array_add(arr_norm, g_strdup(s));
}
g_ptr_array_add(arr_norm, NULL);
was_normalized = !nm_utils_strv_equal((char **) arr->pdata, (char **) arr_norm->pdata);
if (was_normalized)
nmtst_assert_connection_verifies_and_normalizable(con);
else
nmtst_assert_connection_verifies_without_normalization(con);
if (was_normalized || nmtst_get_rand_bool()) {
was_normalized2 = nmtst_connection_normalize(con);
g_assert(was_normalized == was_normalized2);
}
_assert_secondaries(s_con, (const char *const *) arr_norm->pdata);
}
}
/*****************************************************************************/
NMTST_DEFINE();
int
@ -4483,6 +4600,9 @@ main(int argc, char **argv)
test_8021x);
g_test_add_data_func("/libnm/setting-8021x/pkcs12", "test-cert.p12, test", test_8021x);
g_test_add_func("/libnm/settings/test_setting_connection_secondaries_verify",
test_setting_connection_secondaries_verify);
g_test_add_func("/libnm/settings/bond/verify", test_bond_verify);
g_test_add_func("/libnm/settings/bond/compare", test_bond_compare);
g_test_add_func("/libnm/settings/bond/normalize", test_bond_normalize);

View file

@ -515,6 +515,10 @@ GPtrArray *_nm_setting_bridge_port_get_vlans(NMSettingBridgePort *setting);
/*****************************************************************************/
GArray *_nm_setting_connection_get_secondaries(NMSettingConnection *setting);
/*****************************************************************************/
NMSettingBluetooth *_nm_connection_get_setting_bluetooth_for_nap(NMConnection *connection);
/*****************************************************************************/

View file

@ -2290,6 +2290,36 @@ nm_utils_strv_find_first(char **list, gssize len, const char *needle)
return -1;
}
gboolean
nm_strv_has_duplicate(const char *const *strv, gssize len, gboolean is_sorted)
{
gsize l;
gsize i;
gsize j;
l = len < 0 ? NM_PTRARRAY_LEN(strv) : (gsize) len;
if (is_sorted) {
#if NM_MORE_ASSERTS > 10
for (i = 1; i < l; i++)
nm_assert(nm_strcmp0(strv[i - 1], strv[i]) <= 0);
#endif
for (i = 1; i < l; i++) {
if (nm_streq0(strv[i - 1], strv[i]))
return TRUE;
}
} else {
for (i = 1; i < l; i++) {
for (j = 0; j < i; j++) {
if (nm_streq0(strv[j], strv[i]))
return TRUE;
}
}
}
return FALSE;
}
char **
_nm_utils_strv_cleanup(char ** strv,
gboolean strip_whitespace,

View file

@ -710,6 +710,8 @@ nm_utils_strsplit_set(const char *str, const char *delimiters)
gssize nm_utils_strv_find_first(char **list, gssize len, const char *needle);
gboolean nm_strv_has_duplicate(const char *const *list, gssize len, gboolean is_sorted);
char **_nm_utils_strv_cleanup(char ** strv,
gboolean strip_whitespace,
gboolean skip_empty,
@ -1907,6 +1909,34 @@ const char **_nm_utils_strv_dup_packed(const char *const *strv, gssize len);
#define nm_utils_strv_dup_packed(strv, len) _nm_utils_strv_dup_packed(NM_CAST_STRV_CC(strv), (len))
#define nm_utils_strv_dup_shallow_maybe_a(alloca_maxlen, strv, len, to_free) \
({ \
const char *const *const _strv = NM_CAST_STRV_CC(strv); \
const gssize _len = (len); \
const char ** _result = NULL; \
const char ***const _to_free = (to_free); \
\
G_STATIC_ASSERT_EXPR((alloca_maxlen) <= 500u / sizeof(const char *)); \
G_STATIC_ASSERT_EXPR((alloca_maxlen) > 0u); \
nm_assert(_to_free && !*_to_free); \
\
if (_len >= 0 || _strv) { \
const gsize _l = (_len < 0) ? NM_PTRARRAY_LEN(_strv) : ((gsize) _len); \
\
if (G_LIKELY(_l < (alloca_maxlen))) { \
_result = g_newa(const char *, _l + 1); \
} else { \
_result = g_new(const char *, _l + 1); \
*_to_free = _result; \
} \
if (_l > 0) \
memcpy(_result, _strv, _l * sizeof(const char *)); \
_result[_l] = NULL; \
} \
\
_result; \
})
/*****************************************************************************/
GSList *nm_utils_g_slist_find_str(const GSList *list, const char *needle);
@ -2717,6 +2747,15 @@ nm_strvarray_add(GArray *array, const char *str)
g_array_append_val(array, s);
}
static inline const char *
nm_strvarray_get_idx(GArray *array, guint idx)
{
nm_assert(array);
nm_assert(idx < array->len);
return g_array_index(array, const char *, idx);
}
static inline const char *const *
nm_strvarray_get_strv_non_empty(GArray *arr, guint *length)
{
@ -2729,6 +2768,21 @@ nm_strvarray_get_strv_non_empty(GArray *arr, guint *length)
return &g_array_index(arr, const char *, 0);
}
static inline char **
nm_strvarray_get_strv_non_empty_dup(GArray *arr, guint *length)
{
const char *const *strv;
if (!arr || arr->len == 0) {
NM_SET_OUT(length, 0);
return NULL;
}
NM_SET_OUT(length, arr->len);
strv = &g_array_index(arr, const char *, 0);
return nm_utils_strv_dup(strv, arr->len, TRUE);
}
static inline const char *const *
nm_strvarray_get_strv(GArray **arr, guint *length)
{
@ -2756,8 +2810,8 @@ nm_strvarray_set_strv(GArray **array, const char *const *strv)
nm_strvarray_add(*array, strv[0]);
}
static inline gboolean
nm_strvarray_remove_first(GArray *strv, const char *needle)
static inline gssize
nm_strvarray_find_first(GArray *strv, const char *needle)
{
guint i;
@ -2765,13 +2819,25 @@ nm_strvarray_remove_first(GArray *strv, const char *needle)
if (strv) {
for (i = 0; i < strv->len; i++) {
if (nm_streq(needle, g_array_index(strv, const char *, i))) {
g_array_remove_index(strv, i);
return TRUE;
}
if (nm_streq(needle, g_array_index(strv, const char *, i)))
return i;
}
}
return FALSE;
return -1;
}
static inline gboolean
nm_strvarray_remove_first(GArray *strv, const char *needle)
{
gssize idx;
nm_assert(needle);
idx = nm_strvarray_find_first(strv, needle);
if (idx < 0)
return FALSE;
g_array_remove_index(strv, idx);
return TRUE;
}
/*****************************************************************************/

View file

@ -130,13 +130,13 @@ nm_uuid_generate_random(NMUuid *out_uuid)
/*****************************************************************************/
gboolean
nm_uuid_is_valid_full(const char *str)
nm_uuid_is_normalized_full(const char *str)
{
/* The only reason why this exists is that nm_uuid_is_valid() is an inline function.
/* The only reason why this exists is that nm_uuid_is_normalized() is an inline function.
* If you need to forward declare the function, that won't work.
*
* Usually, you wouldn't use this variant! */
return nm_uuid_is_valid(str);
return nm_uuid_is_normalized(str);
}
/*****************************************************************************/

View file

@ -33,14 +33,22 @@ gboolean nm_uuid_is_null(const NMUuid *uuid);
/*****************************************************************************/
gboolean nm_uuid_is_valid_full(const char *str);
static inline gboolean
nm_uuid_is_valid(const char *str)
{
return str && nm_uuid_parse_full(str, NULL, NULL);
}
gboolean nm_uuid_is_normalized_full(const char *str);
static inline gboolean
nm_uuid_is_normalized(const char *str)
{
gboolean is_normalized;
return str && nm_uuid_parse_full(str, NULL, &is_normalized) && is_normalized;
}
/*****************************************************************************/
gboolean nm_uuid_is_valid_nmlegacy(const char *str);