diff options
author | Thomas Haller <thaller@redhat.com> | 2019-07-24 16:26:16 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-07-25 23:27:48 +0200 |
commit | 064544cc07878caa248c3557c47f91736e5d2af1 (patch) | |
tree | 4c7662af4c48b3f083a4342db0ac5f8f343b1125 | |
parent | e3b5b1e64b9afe56ba5cd452775ba02a9d92dd2f (diff) | |
download | NetworkManager-064544cc07878caa248c3557c47f91736e5d2af1.tar.gz |
settings: support storing "shadowed-storage" to .nmmeta files
Before, the .nmmeta file could only contain one piece of information:
the loaded-path. This was persisted to disk by writing a "$UUID.nmmeta"
symlink that links to the loaded-path. Also, in practice this is used
for tombstones, so the only valid loaded-path is "/dev/null" (all other
paths are ignored).
Extend the .nmmeta file format to also be able to store additional data: the
shadowed-storage path. We will need that later but the idea is that if
we have a tombstone on disk, then this tombstone might explicitly shadow
another file. The use is when re-adding a profile with the same UUID, then
the existing storage is used (instead of creating a new file). This will
be necessary with Update2(NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED)
flag. This flag first allows to clone a profile from persistent storage
to a profile in /run. Later, when this profile gets deleted, the
original profile will be left on disk. If the same profile then gets
re-created with AddConnection(), then the original filename must be
taken over again. This is to avoid duplication of profiles on disk.
Note that this piece of information is relevent per-UUID, and as such
it's correct to store it in the .nmmeta file. That is related to the
"shadowed-storage" information that we store in the [.nmmeta] section
of keyfiles.
-rw-r--r-- | src/settings/nm-settings.c | 3 | ||||
-rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-plugin.c | 22 | ||||
-rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-plugin.h | 1 | ||||
-rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-storage.c | 17 | ||||
-rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-storage.h | 7 | ||||
-rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-utils.c | 111 | ||||
-rw-r--r-- | src/settings/plugins/keyfile/nms-keyfile-utils.h | 7 | ||||
-rw-r--r-- | src/settings/plugins/keyfile/tests/test-keyfile-settings.c | 6 |
8 files changed, 137 insertions, 37 deletions
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 8679128a23..7dadd4e3e3 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1914,6 +1914,7 @@ nm_settings_delete_connection (NMSettings *self, uuid, FALSE, TRUE, + NULL, &tombstone_1_storage, NULL)) tombstone_in_memory = TRUE; @@ -1927,6 +1928,7 @@ nm_settings_delete_connection (NMSettings *self, uuid, TRUE, TRUE, + NULL, &tombstone_2_storage, NULL)) { nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, @@ -1934,6 +1936,7 @@ nm_settings_delete_connection (NMSettings *self, uuid, TRUE, TRUE, + NULL, &tombstone_2_storage, NULL); } diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index eb3ae5f100..fbe70ef4d2 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -309,6 +309,7 @@ _load_file (NMSKeyfilePlugin *self, if (_ignore_filename (storage_type, filename)) { gs_free char *nmmeta = NULL; gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage_filename = NULL; if (!nms_keyfile_nmmeta_check_filename (filename, NULL)) { if (error) @@ -322,6 +323,7 @@ _load_file (NMSKeyfilePlugin *self, &full_filename, &nmmeta, &loaded_path, + &shadowed_storage_filename, NULL)) { if (error) nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip unreadable nmmeta file"); @@ -349,7 +351,8 @@ _load_file (NMSKeyfilePlugin *self, return nms_keyfile_storage_new_tombstone (self, nmmeta, full_filename, - storage_type); + storage_type, + shadowed_storage_filename); } full_filename = g_build_filename (dirname, filename, NULL); @@ -1052,6 +1055,9 @@ delete_connection (NMSettingsPlugin *plugin, * has no /etc directory configured, this results in a hard failure. * @set: if %TRUE, write the symlink to point to /dev/null. If %FALSE, * delete the nmmeta file (if it exists). + * @shadowed_storage: a tombstone can also shadow an existing storage. + * In combination with @set and @in_memory, this is allowed to store + * the shadowed storage filename. * @out_storage: (transfer full) (allow-none): the storage element that changes, or * NULL if nothing changed. Note that the file on disk is already as * we want to write it, then this still counts as a change. No change only @@ -1076,6 +1082,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, const char *uuid, gboolean in_memory, gboolean set, + const char *shadowed_storage, NMSettingsStorage **out_storage, gboolean *out_hard_failure) { @@ -1092,6 +1099,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, nm_assert (NMS_IS_KEYFILE_PLUGIN (self)); nm_assert (nm_utils_is_uuid (uuid)); nm_assert (!out_storage || !*out_storage); + nm_assert (!shadowed_storage || (set && in_memory)); priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); @@ -1104,7 +1112,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, dirname = priv->dirname_run; } else { if (!priv->dirname_etc) { - _LOGT ("commit: cannot %s%s nmmeta symlink for %s as there is no /etc directory", + _LOGT ("commit: cannot %s%s nmmeta file for %s as there is no /etc directory", simulate ? "simulate " : "", loaded_path ? "write" : "delete", uuid); @@ -1123,13 +1131,15 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, uuid, loaded_path, FALSE, + shadowed_storage, &nmmeta_filename); } - _LOGT ("commit: %s nmmeta symlink \"%s\"%s%s%s %s", + _LOGT ("commit: %s nmmeta file \"%s\"%s%s%s%s%s%s %s", loaded_path ? "writing" : "deleting", nmmeta_filename, NM_PRINT_FMT_QUOTED (loaded_path, " (pointing to \"", loaded_path, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, "\")", ""), simulate ? "simulated" : ( nmmeta_success @@ -1152,8 +1162,12 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, storage = nms_keyfile_storage_new_tombstone (self, uuid, nmmeta_filename, - storage_type); + storage_type, + shadowed_storage); nm_sett_util_storages_add_take (&priv->storages, storage); + } else { + g_free (storage->u.meta_data.shadowed_storage); + storage->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); } storage_result = g_object_ref (storage); diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/settings/plugins/keyfile/nms-keyfile-plugin.h index b2a4a23864..4844096456 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h @@ -68,6 +68,7 @@ gboolean nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, const char *uuid, gboolean in_memory, gboolean set, + const char *shadowed_storage, NMSettingsStorage **out_storage, gboolean *out_hard_failure); diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c index b8bf3e6380..d68d60c8ec 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-storage.c +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c @@ -46,9 +46,13 @@ nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst, nm_assert (dst->storage_type == src->storage_type); nm_assert (dst->is_meta_data == src->is_meta_data); - if (dst->is_meta_data) + if (dst->is_meta_data) { + gs_free char *shadowed_storage_to_free = NULL; + + shadowed_storage_to_free = g_steal_pointer (&dst->u.meta_data.shadowed_storage); dst->u.meta_data = src->u.meta_data; - else { + dst->u.meta_data.shadowed_storage = g_strdup (dst->u.meta_data.shadowed_storage); + } else { gs_unref_object NMConnection *connection_to_free = NULL; gs_free char *shadowed_storage_to_free = NULL; @@ -140,7 +144,8 @@ NMSKeyfileStorage * nms_keyfile_storage_new_tombstone (NMSKeyfilePlugin *plugin, const char *uuid, const char *filename, - NMSKeyfileStorageType storage_type) + NMSKeyfileStorageType storage_type, + const char *shadowed_storage) { NMSKeyfileStorage *self; @@ -152,6 +157,8 @@ nms_keyfile_storage_new_tombstone (NMSKeyfilePlugin *plugin, self = _storage_new (plugin, uuid, filename, TRUE, storage_type); self->u.meta_data.is_tombstone = TRUE; + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) + self->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); return self; } @@ -200,7 +207,9 @@ _storage_clear (NMSKeyfileStorage *self) { c_list_unlink (&self->parent._storage_lst); c_list_unlink (&self->parent._storage_by_uuid_lst); - if (!self->is_meta_data) { + if (self->is_meta_data) + nm_clear_g_free (&self->u.meta_data.shadowed_storage); + else { g_clear_object (&self->u.conn_data.connection); nm_clear_g_free (&self->u.conn_data.shadowed_storage); self->u.conn_data.shadowed_owned = FALSE; diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h index 5c43e79759..2252b47b7e 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-storage.h +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h @@ -35,6 +35,7 @@ typedef struct { /* whether this is a tombstone to hide a UUID (via symlink to /dev/null). */ + char *shadowed_storage; bool is_tombstone:1; } NMSettingsMetaData; @@ -116,7 +117,8 @@ struct _NMSKeyfilePlugin; NMSKeyfileStorage *nms_keyfile_storage_new_tombstone (struct _NMSKeyfilePlugin *self, const char *uuid, const char *filename, - NMSKeyfileStorageType storage_type); + NMSKeyfileStorageType storage_type, + const char *shadowed_storage); NMSKeyfileStorage *nms_keyfile_storage_new_connection (struct _NMSKeyfilePlugin *self, NMConnection *connection_take /* pass reference */, @@ -221,6 +223,9 @@ nm_settings_storage_get_shadowed_storage (const NMSettingsStorage *storage, NM_SET_OUT (out_shadowed_owned, self->u.conn_data.shadowed_owned); return self->u.conn_data.shadowed_storage; } + } else { + NM_SET_OUT (out_shadowed_owned, FALSE); + return self->u.meta_data.shadowed_storage; } } } diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c index e9d5351573..ea03e1b611 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.c +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c @@ -24,6 +24,7 @@ #include <stdlib.h> #include <sys/stat.h> +#include "nm-glib-aux/nm-io-utils.h" #include "nm-keyfile-internal.h" #include "nm-utils.h" #include "nm-setting-wired.h" @@ -33,6 +34,13 @@ /*****************************************************************************/ +#define NMMETA_KF_GROUP_NAME_NMMETA "nmmeta" +#define NMMETA_KF_KEY_NAME_NMMETA_UUID "uuid" +#define NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH "loaded-path" +#define NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE "shadowed-storage" + +/*****************************************************************************/ + const char * nms_keyfile_nmmeta_check_filename (const char *filename, guint *out_uuid_len) @@ -106,12 +114,16 @@ nms_keyfile_nmmeta_read (const char *dirname, char **out_full_filename, char **out_uuid, char **out_loaded_path, + char **out_shadowed_storage, struct stat *out_st) { const char *uuid; guint uuid_len; gs_free char *full_filename = NULL; - gs_free char *ln = NULL; + gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage = NULL; + struct stat st_stack; + struct stat *st = out_st ?: &st_stack; nm_assert (dirname && dirname[0] == '/'); nm_assert (filename && filename[0] && !strchr (filename, '/')); @@ -124,17 +136,43 @@ nms_keyfile_nmmeta_read (const char *dirname, if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMMETA, full_filename, - out_st, + st, NULL)) return FALSE; - ln = nm_utils_read_link_absolute (full_filename, NULL); - if (!ln) - return FALSE; + if (S_ISREG (st->st_mode)) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *v_uuid = NULL; + + kf = g_key_file_new (); + + if (!g_key_file_load_from_file (kf, full_filename, G_KEY_FILE_NONE, NULL)) + return FALSE; + + v_uuid = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, NULL); + if (!nm_streq0 (v_uuid, uuid)) + return FALSE; + + loaded_path = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, NULL); + shadowed_storage = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, NULL); + + if ( !loaded_path + && !shadowed_storage) { + /* if there is no useful information in the file, it is the same as if + * the file is not present. Signal failure. */ + return FALSE; + } + + } else { + loaded_path = nm_utils_read_link_absolute (full_filename, NULL); + if (!loaded_path) + return FALSE; + } NM_SET_OUT (out_uuid, g_strndup (uuid, uuid_len)); NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); - NM_SET_OUT (out_loaded_path, g_steal_pointer (&ln)); + NM_SET_OUT (out_loaded_path, g_steal_pointer (&loaded_path)); + NM_SET_OUT (out_shadowed_storage, g_steal_pointer (&shadowed_storage)); return TRUE; } @@ -143,7 +181,8 @@ nms_keyfile_nmmeta_read_from_file (const char *full_filename, char **out_dirname, char **out_filename, char **out_uuid, - char **out_loaded_path) + char **out_loaded_path, + char **out_shadowed_storage) { gs_free char *dirname = NULL; gs_free char *filename = NULL; @@ -158,6 +197,7 @@ nms_keyfile_nmmeta_read_from_file (const char *full_filename, NULL, out_uuid, out_loaded_path, + out_shadowed_storage, NULL)) return FALSE; @@ -170,7 +210,8 @@ gboolean nms_keyfile_nmmeta_write (const char *dirname, const char *uuid, const char *loaded_path, - gboolean allow_relative, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, char **out_full_filename) { gs_free char *full_filename_tmp = NULL; @@ -180,6 +221,7 @@ nms_keyfile_nmmeta_write (const char *dirname, nm_assert ( nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); nm_assert (!loaded_path || loaded_path[0] == '/'); + nm_assert (!shadowed_storage || loaded_path); full_filename_tmp = nms_keyfile_nmmeta_filename (dirname, uuid, TRUE); @@ -198,7 +240,7 @@ nms_keyfile_nmmeta_write (const char *dirname, return success; } - if (allow_relative) { + if (loaded_path_allow_relative) { const char *f; f = nm_utils_file_is_in_path (loaded_path, dirname); @@ -209,18 +251,40 @@ nms_keyfile_nmmeta_write (const char *dirname, } } - if (symlink (loaded_path, full_filename_tmp) != 0) { - full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; - NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); - return FALSE; - } + full_filename = g_strndup (full_filename_tmp, strlen (full_filename_tmp) - 1); - full_filename = g_strdup (full_filename_tmp); - full_filename[strlen (full_filename) - 1] = '\0'; - if (rename (full_filename_tmp, full_filename) != 0) { - (void) unlink (full_filename_tmp); - NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); - return FALSE; + if (shadowed_storage) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *contents = NULL; + gsize length; + + kf = g_key_file_new (); + + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, uuid); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, loaded_path); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, shadowed_storage); + + contents = g_key_file_to_data (kf, &length, NULL); + + if (!nm_utils_file_set_contents (full_filename, contents, length, 0600, NULL)) { + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; + } + } else { + /* we only have the "loaded_path" to store. That is commonly used for the tombstones to + * link to /dev/null. A symlink is sufficient to store that ammount of information. + * No need to bother with a keyfile. */ + if (symlink (loaded_path, full_filename_tmp) != 0) { + full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; + } + + if (rename (full_filename_tmp, full_filename) != 0) { + (void) unlink (full_filename_tmp); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + return FALSE; + } } NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); @@ -243,9 +307,10 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, return FALSE; } } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) { - if (!S_ISLNK (st->st_mode)) { + if ( !S_ISLNK (st->st_mode) + && !S_ISREG (st->st_mode)) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "file is not a slink"); + "file is neither a symlink nor a regular file"); return FALSE; } } else @@ -259,7 +324,7 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, return FALSE; } - if ( filetype == NMS_KEYFILE_FILETYPE_KEYFILE + if ( S_ISREG (st->st_mode) && (st->st_mode & 0077)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File permissions (%03o) are insecure", diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h index 4f4ad54091..723c443625 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -56,18 +56,21 @@ gboolean nms_keyfile_nmmeta_read (const char *dirname, char **out_full_filename, char **out_uuid, char **out_loaded_path, + char **out_shadowed_storage, struct stat *out_st); gboolean nms_keyfile_nmmeta_read_from_file (const char *full_filename, char **out_dirname, char **out_filename, char **out_uuid, - char **out_loaded_path); + char **out_loaded_path, + char **out_shadowed_storage); gboolean nms_keyfile_nmmeta_write (const char *dirname, const char *uuid, const char *loaded_path, - gboolean allow_relative, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, char **out_full_filename); /*****************************************************************************/ diff --git a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c index ce7540ebda..f96111a2e4 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -2543,7 +2543,7 @@ _assert_keyfile_nmmeta (const char *dirname, nm_clear_g_free (&full_filename); - g_assert (nms_keyfile_nmmeta_write (dirname, uuid, loaded_path, allow_relative, &full_filename)); + g_assert (nms_keyfile_nmmeta_write (dirname, uuid, loaded_path, allow_relative, NULL, &full_filename)); g_assert_cmpstr (full_filename, ==, exp_full_filename); nm_clear_g_free (&full_filename); @@ -2555,7 +2555,7 @@ _assert_keyfile_nmmeta (const char *dirname, g_assert_cmpstr (symlink_target, ==, exp_symlink_target); - success = nms_keyfile_nmmeta_read (dirname, filename, &full_filename, &uuid2, &loaded_path2, NULL); + success = nms_keyfile_nmmeta_read (dirname, filename, &full_filename, &uuid2, &loaded_path2, NULL, NULL); g_assert_cmpint (!!exp_uuid, ==, success); if (success) g_assert_cmpstr (full_filename, ==, exp_full_filename); @@ -2566,7 +2566,7 @@ _assert_keyfile_nmmeta (const char *dirname, g_assert_cmpstr (loaded_path2, ==, exp_loaded_path); - success = nms_keyfile_nmmeta_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3); + success = nms_keyfile_nmmeta_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3, NULL); g_assert_cmpint (!!exp_uuid, ==, success); if (success) { g_assert_cmpstr (dirname3, ==, dirname); |