summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-07-26 09:03:02 +0200
committerThomas Haller <thaller@redhat.com>2019-07-26 09:03:02 +0200
commit7bd16e85e3b9e0e5f827272e5ed6e3769445e7ea (patch)
treeb77303cc9da9435b4348c5725b562753f5e70a6f
parent40355d03a49920b4c87119d306d7216ee0990b41 (diff)
parentdf252a620d570ac85dec40c68aac36fc380762a7 (diff)
downloadNetworkManager-7bd16e85e3b9e0e5f827272e5ed6e3769445e7ea.tar.gz
settings: merge branch 'th/settings-shadowed-storage'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/211
-rw-r--r--Makefile.examples1
-rwxr-xr-xexamples/python/gi/nm-update2.py155
-rw-r--r--libnm-core/nm-dbus-interface.h41
-rw-r--r--libnm-core/nm-keyfile-internal.h2
-rw-r--r--src/devices/nm-device.c2
-rw-r--r--src/devices/wifi/nm-iwd-manager.c1
-rw-r--r--src/nm-checkpoint.c2
-rw-r--r--src/nm-manager.c4
-rw-r--r--src/settings/nm-settings-connection.c71
-rw-r--r--src/settings/nm-settings-connection.h49
-rw-r--r--src/settings/nm-settings.c892
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.c1
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c140
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.h7
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c12
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.h2
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.c118
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-storage.h183
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.c117
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h9
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c24
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.h2
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile-settings.c8
23 files changed, 1384 insertions, 459 deletions
diff --git a/Makefile.examples b/Makefile.examples
index dcd1ac9d18..385a8353ed 100644
--- a/Makefile.examples
+++ b/Makefile.examples
@@ -178,6 +178,7 @@ EXTRA_DIST += \
examples/python/gi/list-connections.py \
examples/python/gi/nm-add-connection2.py \
examples/python/gi/nm-connection-update-stable-id.py \
+ examples/python/gi/nm-update2.py \
examples/python/gi/nm-wg-set \
examples/python/gi/setting-user-data.py \
examples/python/gi/show-wifi-networks.py \
diff --git a/examples/python/gi/nm-update2.py b/examples/python/gi/nm-update2.py
new file mode 100755
index 0000000000..36449fd893
--- /dev/null
+++ b/examples/python/gi/nm-update2.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright 2019 Red Hat, Inc.
+#
+
+import sys
+import re
+
+import gi
+gi.require_version('NM', '1.0')
+from gi.repository import GLib, NM
+
+def find_connections(nm_client, arg_type, arg_id):
+ for c in nm_client.get_connections():
+ if arg_type in [None, 'id'] and c.get_id() == arg_id:
+ yield c
+ if arg_type in [None, 'uuid'] and c.get_uuid() == arg_id:
+ yield c
+
+def find_connection_first(nm_client, arg_type, arg_id):
+ for f in find_connections(nm_client, arg_type, arg_id):
+ return f
+
+def con_to_str(con):
+ s_con = con.get_setting_connection()
+ return '"%s" (%s)' % (s_con.get_id(), s_con.get_uuid())
+
+def usage():
+ print('Usage: %s [[id] <id>]' % (sys.argv[0]))
+ print(' %s [[uuid] <uuid>]' % (sys.argv[0]))
+ return 1
+
+def die(msg, print_usage=False):
+ print(msg)
+ if print_usage:
+ usage()
+ sys.exit(1)
+
+def main():
+
+ main_loop = GLib.MainLoop()
+
+ nm_client = NM.Client.new(None)
+
+ arg_mode = None
+ arg_block_autoconnect = NM.SettingsUpdate2Flags.NONE
+ arg_volatile = NM.SettingsUpdate2Flags.NONE
+ arg_no_reapply = NM.SettingsUpdate2Flags.NONE
+
+ cons = []
+
+ argv = list(sys.argv[1:])
+ while argv:
+ if argv[0] in ['id', 'uuid']:
+ if cons:
+ die('cannot specify multiple connections')
+ if len(argv) < 2:
+ die('missing argument for "%s" specifier' % (argv[0]))
+ cons.extend(find_connections(nm_client, argv[0], argv[1]))
+ if len(cons) == 0:
+ die('could not find connection for "%s %s"' % (argv[0], argv[1]))
+ if len(cons) != 1:
+ die('could not find unique connection for "%s %s"' % (argv[0], argv[1]))
+ argv = argv[2:]
+ continue
+ if argv[0] in ['--block-autoconnect']:
+ arg_block_autoconnect = NM.SettingsUpdate2Flags.BLOCK_AUTOCONNECT
+ argv = argv[1:]
+ continue
+ if argv[0] in ['--volatile']:
+ arg_volatile = NM.SettingsUpdate2Flags.VOLATILE
+ argv = argv[1:]
+ continue
+ if argv[0] in ['--no-reapply']:
+ arg_no_reapply = NM.SettingsUpdate2Flags.NO_REAPPLY
+ argv = argv[1:]
+ continue
+ if argv[0] in ['--to-disk', '--in-memory', '--in-memory-detached', '--in-memory-only']:
+ if argv[0] == '--to-disk':
+ v = NM.SettingsUpdate2Flags.TO_DISK
+ elif argv[0] == '--in-memory':
+ v = NM.SettingsUpdate2Flags.IN_MEMORY
+ elif argv[0] == '--in-memory-detached':
+ v = NM.SettingsUpdate2Flags.IN_MEMORY_DETACHED
+ elif argv[0] == '--in-memory-only':
+ v = NM.SettingsUpdate2Flags.IN_MEMORY_ONLY
+ elif argv[0] == '--keep':
+ v = NM.SettingsUpdate2Flags.NONE
+ else:
+ assert(False)
+ if arg_mode is not None:
+ die('duplicate storage modes ("%s")' % (argv[0]))
+ arg_mode = v
+ argv = argv[1:]
+ continue
+ if cons:
+ die('unknown argument "%s"' % (argv[0]))
+ cons.extend(find_connections(nm_client, None, argv[0]))
+ if len(cons) == 0:
+ die('could not find connection for "%s"' % (argv[0]))
+ if len(cons) != 1:
+ die('could not find unique connection for "%s"' % (argv[0]))
+ argv = argv[1:]
+ continue
+
+ if len(cons) != 1:
+ die('missing connection argument', True)
+
+ con = cons[0]
+
+ con2 = NM.SimpleConnection.new_clone(con)
+
+ result = {}
+ def _update2_cb(con, async_result, user_data):
+ try:
+ r = con.update2_finish(async_result)
+ except Exception as e:
+ result['error'] = e
+ else:
+ result['result'] = r
+ main_loop.quit()
+
+ con.update2(con2.to_dbus(NM.ConnectionSerializationFlags.ALL),
+ (arg_mode if arg_mode is not None else NM.SettingsUpdate2Flags.NONE)
+ | arg_block_autoconnect
+ | arg_volatile
+ | arg_no_reapply,
+ None,
+ None,
+ _update2_cb,
+ None)
+
+ main_loop.run()
+
+ if 'error' in result:
+ die('update connection %s failed [%s]: %s' % (con_to_str(con2), ' '.join(sys.argv), result['error']))
+
+ print('update connection %s succeeded [%s]: %s' % (con_to_str(con2), ' '.join(sys.argv), result['result']))
+
+if __name__ == '__main__':
+ main()
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index b2569af1ef..35496e82c2 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -1030,26 +1030,31 @@ typedef enum { /*< flags >*/
* NMSettingsUpdate2Flags:
* @NM_SETTINGS_UPDATE2_FLAG_NONE: an alias for numeric zero, no flags set.
* @NM_SETTINGS_UPDATE2_FLAG_TO_DISK: to persist the connection to disk.
- * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY: to make the connection in-memory only.
- * If the connection was previously persistent, the corresponding file on disk
- * is not deleted but merely the connection is decoupled from the file
- * on disk. If you later delete an in-memory connection, the connection
- * on disk will be deleted as well.
- * Note: with 1.20, this flag is no longer implemented because in-memory connections
- * are also persisted under /run. For the moment, this behaves the same as
- * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED.
- * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED: this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY,
- * but if the connection has a corresponding file on disk, the association between
- * the connection and the file is forgotten but the file is not modified.
- * The difference to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY is if you later
- * save the connection again to disk, a new file name will be chosen without
- * overwriting the remaining file on disk. Also, if you delete the connection
- * later, the file on disk will not be deleted.
+ * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY: makes the profile in-memory.
+ * Note that such profiles are stored in keyfile format under /run.
+ * If the file is already in-memory, the file in /run is updated in-place.
+ * Otherwise, the previous storage for the profile is left unchanged
+ * on disk, and the in-memory copy shadows it.
+ * Note that the original filename of the previous persistent storage (if any)
+ * is remembered. That means, when later persisting the profile again to disk,
+ * the file on disk will be overwritten again.
+ * Likewise, when finally deleting the profile, both the storage from /run
+ * and persistent storage are deleted (or if the persistent storage does not
+ * allow deletion, and nmmeta file is written to mark the UUID as deleted).
+ * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED: this is almost the same as
+ * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, with one difference: when later deleting
+ * the profile, the original profile will not be deleted. Instead a nmmeta
+ * file is written to /run to indicate that the profile is gone.
+ * Note that if such a nmmeta tombstone file exists and hides a file in persistant
+ * storage, then when re-adding the profile with the same UUID, then the original
+ * storage is taken over again.
* @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY: this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY,
- * but if the connection has a corresponding file on disk, the file on
- * disk will be deleted.
+ * but if the connection has a corresponding file on persistent storage, the file
+ * will be deleted right away. If the profile is later again persisted to disk,
+ * a new, unused filename will be chosen.
* @NM_SETTINGS_UPDATE2_FLAG_VOLATILE: This can be specified with either
- * %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED or %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY.
+ * %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED
+ * or %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY.
* After making the connection in-memory only, the connection is marked
* as volatile. That means, if the connection is currently not active
* it will be deleted right away. Otherwise, it is marked to for deletion
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
index e1fb10c2b4..ced4ed975f 100644
--- a/libnm-core/nm-keyfile-internal.h
+++ b/libnm-core/nm-keyfile-internal.h
@@ -174,6 +174,8 @@ gboolean _nm_keyfile_has_values (GKeyFile *keyfile);
#define NM_KEYFILE_GROUP_NMMETA ".nmmeta"
#define NM_KEYFILE_KEY_NMMETA_NM_GENERATED "nm-generated"
#define NM_KEYFILE_KEY_NMMETA_VOLATILE "volatile"
+#define NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE "shadowed-storage"
+#define NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED "shadowed-owned"
#define NM_KEYFILE_PATH_NAME_LIB NMLIBDIR "/system-connections"
#define NM_KEYFILE_PATH_NAME_ETC_DEFAULT NMCONFDIR "/system-connections"
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 04a94db8a1..da34602427 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -12420,7 +12420,7 @@ nm_device_set_ip_config (NMDevice *self,
nm_settings_connection_update (settings_connection,
new_connection,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE,
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index b696f128a7..5b550ea46d 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -436,7 +436,6 @@ mirror_8021x_connection (NMIwdManager *self,
NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
NM_SETTING_CONNECTION_ID, name,
NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid),
- NM_SETTING_CONNECTION_READ_ONLY, TRUE,
NULL));
nm_connection_add_setting (connection, setting);
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index 1f7723c539..9bc979bb94 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -254,7 +254,7 @@ restore_and_activate_connection (NMCheckpoint *self,
_LOGD ("rollback: adding connection %s again",
nm_connection_get_uuid (dev_checkpoint->settings_connection));
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
if (!nm_settings_add_connection (NM_SETTINGS_GET,
dev_checkpoint->settings_connection,
persist_mode,
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 4f114e4d13..6ea535021a 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -5454,7 +5454,7 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
const char *device_path;
const char *specific_object_path;
gs_free NMConnection **conns = NULL;
- NMSettingsConnectionPersistMode persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ NMSettingsConnectionPersistMode persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
gboolean is_volatile = FALSE;
gboolean bind_dbus_client = FALSE;
AsyncOpType async_op_type;
@@ -5485,7 +5485,7 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj,
s = g_variant_get_string (option_value, NULL);
is_volatile = FALSE;
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
if (nm_streq (s, "volatile")) {
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 2093342c9c..25a27e68cc 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -44,6 +44,11 @@
#define AUTOCONNECT_RETRIES_FOREVER -1
#define AUTOCONNECT_RESET_RETRIES_TIMER 300
+#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES ((NMSettingsUpdate2Flags) ( NM_SETTINGS_UPDATE2_FLAG_TO_DISK \
+ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \
+ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED \
+ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))
+
/*****************************************************************************/
NMConnection **
@@ -1319,37 +1324,6 @@ auth_start (NMSettingsConnection *self,
/**** DBus method handlers ************************************/
-static gboolean
-check_writable (NMConnection *self, GError **error)
-{
- NMSettingConnection *s_con;
-
- g_return_val_if_fail (NM_IS_CONNECTION (self), FALSE);
-
- s_con = nm_connection_get_setting_connection (self);
- if (!s_con) {
- g_set_error_literal (error,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_INVALID_CONNECTION,
- "Connection did not have required 'connection' setting");
- return FALSE;
- }
-
- /* If the connection is read-only, that has to be changed at the source of
- * the problem (ex a system settings plugin that can't write connections out)
- * instead of over D-Bus.
- */
- if (nm_setting_connection_get_read_only (s_con)) {
- g_set_error_literal (error,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_READ_ONLY_CONNECTION,
- "Connection is read-only");
- return FALSE;
- }
-
- return TRUE;
-}
-
static void
get_settings_auth_cb (NMSettingsConnection *self,
GDBusMethodInvocation *context,
@@ -1504,10 +1478,14 @@ update_auth_cb (NMSettingsConnection *self,
}
}
+ nm_assert ( !NM_FLAGS_ANY (info->flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)
+ || nm_utils_is_power_of_two (info->flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES));
+
if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_TO_DISK))
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
- else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
- | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED))
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
+ else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY))
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY;
+ else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED))
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
@@ -1594,13 +1572,6 @@ settings_connection_update (NMSettingsConnection *self,
UpdateInfo *info;
const char *permission;
- /* If the connection is read-only, that has to be changed at the source of
- * the problem (ex a system settings plugin that can't write connections out)
- * instead of over D-Bus.
- */
- if (!check_writable (nm_settings_connection_get_connection (self), &error))
- goto error;
-
/* Check if the settings are valid first */
if (new_settings) {
if (!g_variant_is_of_type (new_settings, NM_VARIANT_TYPE_CONNECTION)) {
@@ -1725,14 +1696,10 @@ impl_settings_connection_update2 (NMDBusObject *obj,
GVariantIter iter;
const char *args_name;
NMSettingsUpdate2Flags flags;
- const NMSettingsUpdate2Flags ALL_PERSIST_MODES = NM_SETTINGS_UPDATE2_FLAG_TO_DISK
- | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
- | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED
- | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY;
g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
- if (NM_FLAGS_ANY (flags_u, ~((guint32) ( ALL_PERSIST_MODES
+ if (NM_FLAGS_ANY (flags_u, ~((guint32) ( _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES
| NM_SETTINGS_UPDATE2_FLAG_VOLATILE
| NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT
| NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) {
@@ -1745,11 +1712,12 @@ impl_settings_connection_update2 (NMDBusObject *obj,
flags = (NMSettingsUpdate2Flags) flags_u;
- if ( ( NM_FLAGS_ANY (flags, ALL_PERSIST_MODES)
- && !nm_utils_is_power_of_two (flags & ALL_PERSIST_MODES))
+ if ( ( NM_FLAGS_ANY (flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)
+ && !nm_utils_is_power_of_two (flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES))
|| ( NM_FLAGS_HAS (flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
- && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED |
- NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) {
+ && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
+ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED
+ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) {
error = g_error_new_literal (NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
"Conflicting flags");
@@ -1831,9 +1799,6 @@ impl_settings_connection_delete (NMDBusObject *obj,
nm_assert (nm_settings_connection_still_valid (self));
- if (!check_writable (nm_settings_connection_get_connection (self), &error))
- goto err;
-
subject = _new_auth_subject (invocation, &error);
if (!subject)
goto err;
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index ab23dc1ff5..61d5c246c3 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -85,21 +85,46 @@ typedef enum {
* if the profile is on-disk, update it on-disk, and keep it. */
NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
- /* persist to disk. If the profile is currenly in-memory, remove
- * it from /run. */
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ /* persist to disk. If the profile is currently in-memory, remove
+ * it from /run. Depending on the shadowed-storage, the pre-existing
+ * file is reused when moving the storage.
+ *
+ * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_TO_DISK. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK,
+
+ /* Update in-memory (i.e. persist to /run). If the profile is currently on disk,
+ * then a reference to the profile is remembered as "shadowed-storage".
+ * Later, when storing again to persistant storage, the shawowed-storage is
+ * updated. When deleting the profile, the shadowed-storage is also deleted
+ * from disk.
+ *
+ * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
+
+ /* Update in-memory (i.e. persist to /run). This is almost like
+ * %NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, except the in-memory profile
+ * remembers not to own the shadowed-storage ("shadowed-owned").
+ * The diffrence is that when deleting the in-memory profile, the original
+ * profile is not deleted but instead the nmmeta tombstone remembers the
+ * shadowed-storage and re-used it when re-adding the profile.
+ *
+ * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED. */
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
- /* persist to /run (in-memory). If the profile is currently on disk,
- * delete it from disk. */
+ /* Update in-memory (i.e. persist to /run). If the profile is currently on disk,
+ * delete it from disk.
+ *
+ * If the profile is in-memory and has a shadowed-storage, the original profile
+ * will be deleted from disk.
+ *
+ * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY. */
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
- /* persist to /run (in-memory). If the profile is currently on disk,
- * forget about it, but don't delete it from disk. */
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
-
- /* this only updates the connection in-memory. Note that "in-memory" above
- * means to write to keyfile in /run. This really means to not notify the
- * settings plugin about the change. */
+ /* This only updates the connection in-memory. Note that "in-memory" above
+ * means to write to keyfile in /run. This mode really means to not notify the
+ * settings plugin about the change. This should be only used for updating
+ * secrets.
+ */
NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
} NMSettingsConnectionPersistMode;
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 6961134c6c..430d277693 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -168,16 +168,12 @@ nm_assert_storage_data_lst (CList *head)
static gboolean
_storage_data_is_alive (StorageData *sd)
{
- if (sd->connection)
- return TRUE;
-
- if (nm_settings_storage_is_keyfile_tombstone (sd->storage)) {
- /* entry does not have a profile, but it's here as a tombstone to
- * hide/shadow other connections. That's also relevant. */
- return TRUE;
- }
-
- return FALSE;
+ /* If the storage tracks a connection, it is considered alive.
+ *
+ * Meta-data storages are special: they never track a connection.
+ * We need to check them specially to know when to drop them. */
+ return sd->connection
+ || nm_settings_storage_is_meta_data_alive (sd->storage);
}
/*****************************************************************************/
@@ -232,6 +228,110 @@ _sett_conn_entry_get_conn (SettConnEntry *sett_conn_entry)
return sett_conn_entry ? sett_conn_entry->sett_conn : NULL;
}
+/**
+ * _sett_conn_entry_storage_find_conflicting_storage:
+ * @sett_conn_entry: the list of settings-storages for the given UUID.
+ * @target_plugin: the settings plugin to check
+ * @storage_check_including: (allow-none): optionally compare against this storage.
+ * @plugins: the list of plugins sorted in descending priority. This determines
+ * the priority and whether a storage conflicts.
+ *
+ * If we were to add the a storage to @target_plugin, then this function checks
+ * whether there are already other storages that would hide the storage after we
+ * add it. Those conflicting/hiding storages are a problem, because they have higher
+ * priority, so we cannot add the storage.
+ *
+ * @storage_check_including is optional, and if given then it checks whether updating
+ * the profile in this storage would result in confict. This is the check before
+ * update-connection. If this parameter is omitted, then it's about what happens
+ * when adding a new profile (add-connection).
+ *
+ * Returns: the conflicting storage or %NULL if there is none.
+ */
+static NMSettingsStorage *
+_sett_conn_entry_storage_find_conflicting_storage (SettConnEntry *sett_conn_entry,
+ NMSettingsPlugin *target_plugin,
+ NMSettingsStorage *storage_check_including,
+ const GSList *plugins)
+{
+ StorageData *sd;
+
+ if (!sett_conn_entry)
+ return NULL;
+
+ if ( storage_check_including
+ && nm_settings_storage_is_keyfile_run (storage_check_including)) {
+ /* the storage we check against is in-memory. It always has highest
+ * priority, so there can be no other conflicting storages. */
+ return NULL;
+ }
+
+ /* Finds the first (highest priority) storage that has a connection.
+ * Note that due to tombstones (that have a high priority), the connection
+ * may not actually be exposed. This is to find hidden/shadowed storages
+ * that provide a connection. */
+ c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) {
+ nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage));
+
+ if (!sd->connection) {
+ /* We only consider storages with connection. In particular,
+ * tombstones are not relevant, because we can delete them to
+ * resolve the conflict. */
+ continue;
+ }
+
+ if (sd->storage == storage_check_including) {
+ /* ok, the storage is the one we are about to check. All other
+ * storages are lower priority, so there is no storage that hides
+ * our storage_check_including. */
+ return NULL;
+ }
+
+ if (nm_settings_plugin_cmp_by_priority (nm_settings_storage_get_plugin (sd->storage),
+ target_plugin,
+ plugins) <= 0) {
+ /* the plugin of the existing storage is less important than @target_plugin.
+ * We have no conflicting/hiding storage. */
+ return NULL;
+ }
+
+ /* Found. If we would add the profile to @target_plugin, then it would be hidden
+ * by existing_storage. */
+ return sd->storage;
+ }
+
+ return NULL;
+}
+
+static NMSettingsStorage *
+_sett_conn_entry_find_shadowed_storage (SettConnEntry *sett_conn_entry,
+ const char *shadowed_storage_filename,
+ NMSettingsStorage *blacklisted_storage)
+{
+ StorageData *sd;
+
+ if (!shadowed_storage_filename)
+ return NULL;
+
+ c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) {
+
+ nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage));
+
+ if (!sd->connection)
+ continue;
+
+ if (blacklisted_storage == sd->storage)
+ continue;
+
+ if (!nm_streq0 (nm_settings_storage_get_filename_for_shadowed_storage (sd->storage), shadowed_storage_filename))
+ continue;
+
+ return sd->storage;
+ }
+
+ return NULL;
+}
+
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE (NMSettings,
@@ -725,23 +825,82 @@ _sett_conn_entries_remove_and_destroy (NMSettings *self,
/*****************************************************************************/
static int
+_sett_conn_entry_sds_update_cmp_ascending (const StorageData *sd_a,
+ const StorageData *sd_b,
+ const GSList *plugins)
+{
+ const NMSettingsMetaData *meta_data_a;
+ const NMSettingsMetaData *meta_data_b;
+ bool is_keyfile_run_a;
+ bool is_keyfile_run_b;
+
+ /* Sort storages by priority. More important storages are sorted
+ * higher (ascending sort). For example, if "sd_a" is more important than
+ * "sd_b" (sd_a>sd_b), a positive integer is returned. */
+
+ meta_data_a = nm_settings_storage_is_meta_data (sd_a->storage);
+ meta_data_b = nm_settings_storage_is_meta_data (sd_b->storage);
+
+ /* runtime storages (both connections and meta-data) are always more
+ * important. */
+ is_keyfile_run_a = nm_settings_storage_is_keyfile_run (sd_a->storage);
+ is_keyfile_run_b = nm_settings_storage_is_keyfile_run (sd_b->storage);
+ if (is_keyfile_run_a != is_keyfile_run_b) {
+
+ if ( !meta_data_a
+ && !meta_data_b) {
+ /* Ok, both are non-meta-data providing actual profiles. But one is in /run and one is in
+ * another storage. In this case we first honor whether one of the storages is explicitly
+ * prioritized. The prioritize flag is an in-memory hack to overwrite relative priorities
+ * contrary to what exists on-disk.
+ *
+ * This is done because when we use explicit D-Bus API (like update-connection)
+ * to update a profile, then we really want to prioritize the candidate
+ * despite having multiple other profiles.
+ *
+ * The example is if you have the same UUID twice in /run (one of them shadowed).
+ * If you move it to disk, then one of the profiles gets deleted and re-created
+ * on disk, but that on-disk profile must win against the remainging profile in
+ * /run. At least until the next reload/restart. */
+ NM_CMP_FIELD_UNSAFE (sd_a, sd_b, prioritize);
+ }
+
+ /* in-memory has higher priority. That is regardless of whether any of
+ * them is meta-data/tombstone or a profile.
+ *
+ * That works, because if any of them are tombstones/metadata, then we are in full
+ * control. There can by only one meta-data file, which is fully owned (and accordingly
+ * created/deleted) by NetworkManager.
+ *
+ * The only case where this might not be right is if we have profiles
+ * in /run that are shadowed. When we move such a profile to disk, then
+ * a conflict might arise. That is handled by "prioritize" above! */
+ NM_CMP_DIRECT (is_keyfile_run_a, is_keyfile_run_b);
+ }
+
+ /* After we determined that both profiles are either in /run or not,
+ * tombstones are always more important than non-tombstones. */
+ NM_CMP_DIRECT (meta_data_a && meta_data_a->is_tombstone,
+ meta_data_b && meta_data_b->is_tombstone);
+
+ /* Again, prioritized entries are sorted first (higher priority). */
+ NM_CMP_FIELD_UNSAFE (sd_a, sd_b, prioritize);
+
+ /* finally, compare the storages. This basically honors the timestamp
+ * of the profile and the relative order of the source plugin (via the
+ * @plugins list). */
+ return nm_settings_storage_cmp (sd_a->storage, sd_b->storage, plugins);
+}
+
+static int
_sett_conn_entry_sds_update_cmp (const CList *ls_a,
const CList *ls_b,
gconstpointer user_data)
{
- const GSList *plugins = user_data;
- StorageData *sd_a = c_list_entry (ls_a, StorageData, sd_lst);
- StorageData *sd_b = c_list_entry (ls_b, StorageData, sd_lst);
-
- /* prioritized entries are sorted first (higher priority). */
- NM_CMP_FIELD_UNSAFE (sd_b, sd_a, prioritize);
-
- /* nm_settings_storage_cmp() compares in ascending order. Meaning,
- * if the storage has higher priority, it gives a positive number (as one
- * would expect).
- *
- * We want to sort the list in reverse though, with highest priority first. */
- return nm_settings_storage_cmp (sd_b->storage, sd_a->storage, plugins);
+ /* we sort highest priority storages first (descending). Hence, the order is swapped. */
+ return _sett_conn_entry_sds_update_cmp_ascending (c_list_entry (ls_b, StorageData, sd_lst),
+ c_list_entry (ls_a, StorageData, sd_lst),
+ user_data);
}
static void
@@ -1192,19 +1351,28 @@ _connection_changed_track (NMSettings *self,
if (_LOGT_ENABLED ()) {
const char *filename;
+ const NMSettingsMetaData *meta_data;
+ const char *shadowed_storage;
+ gboolean shadowed_owned;
filename = nm_settings_storage_get_filename (storage);
if (connection) {
- _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event with connection \"%s\"%s%s%s",
+ shadowed_storage = nm_settings_storage_get_shadowed_storage (storage, &shadowed_owned);
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event with connection \"%s\"%s%s%s%s%s%s",
sett_conn_entry->uuid,
NM_SETTINGS_STORAGE_PRINT_ARG (storage),
nm_connection_get_id (connection),
- NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
- } else if (nm_settings_storage_is_keyfile_tombstone (storage)) {
- _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for hiding profile%s%s%s",
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""),
+ NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", ""));
+ } else if ((meta_data = nm_settings_storage_is_meta_data (storage))) {
+ nm_assert (meta_data->is_tombstone);
+ shadowed_storage = nm_settings_storage_get_shadowed_storage (storage, &shadowed_owned);
+ _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for %shiding profile%s%s%s%s%s%s",
sett_conn_entry->uuid,
NM_SETTINGS_STORAGE_PRINT_ARG (storage),
- NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
+ nm_settings_storage_is_meta_data_alive (storage) ? "" : "dropping ",
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""),
+ NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", ""));
} else {
_LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for dropping profile%s%s%s",
sett_conn_entry->uuid,
@@ -1276,10 +1444,12 @@ _plugin_connections_reload (NMSettings *self)
static gboolean
_add_connection_to_first_plugin (NMSettings *self,
+ SettConnEntry *sett_conn_entry,
NMConnection *new_connection,
gboolean in_memory,
- gboolean is_nm_generated,
- gboolean is_volatile,
+ NMSettingsConnectionIntFlags sett_flags,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
NMSettingsStorage **out_new_storage,
NMConnection **out_new_connection,
GError **error)
@@ -1304,20 +1474,44 @@ _add_connection_to_first_plugin (NMSettings *self,
gboolean success;
const char *filename;
+ if (!in_memory) {
+ NMSettingsStorage *conflicting_storage;
+
+ conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage (sett_conn_entry, plugin, NULL, priv->plugins);
+ if (conflicting_storage) {
+ /* we have a connection provided by a plugin with higher priority than the one
+ * we would want to add the connection. We cannot do that, because doing so
+ * would result in adding a connection that gets hidden by the existing profile.
+ * Also, since we test the plugins in order of priority, all following plugins
+ * are unsuitable.
+ *
+ * Multiple connection plugins are so cumbersome, especially if they are unable
+ * to add the connection. I suggest to disable all plugins except keyfile. */
+ _LOGT ("add-connection: failed to add %s/'%s': there is an existing storage "NM_SETTINGS_STORAGE_PRINT_FMT" with higher priority",
+ nm_connection_get_uuid (new_connection),
+ nm_connection_get_id (new_connection),
+ NM_SETTINGS_STORAGE_PRINT_ARG (conflicting_storage));
+ nm_assert (first_error);
+ break;
+ }
+ }
+
if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) {
success = nms_keyfile_plugin_add_connection (priv->keyfile_plugin,
new_connection,
- is_nm_generated,
- is_volatile,
in_memory,
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE),
+ shadowed_storage,
+ shadowed_owned,
&storage,
&connection_to_add,
&add_error);
} else {
if (in_memory)
continue;
- nm_assert (!is_nm_generated);
- nm_assert (!is_volatile);
+ nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED));
+ nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE));
success = nm_settings_plugin_add_connection (plugin,
new_connection,
&storage,
@@ -1371,6 +1565,97 @@ _add_connection_to_first_plugin (NMSettings *self,
return FALSE;
}
+static gboolean
+_update_connection_to_plugin (NMSettings *self,
+ NMSettingsStorage *storage,
+ NMConnection *connection,
+ NMSettingsConnectionIntFlags sett_flags,
+ gboolean force_rename,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
+ NMSettingsStorage **out_new_storage,
+ NMConnection **out_new_connection,
+ GError **error)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ NMSettingsPlugin *plugin;
+ gboolean success;
+
+ plugin = nm_settings_storage_get_plugin (storage);
+
+ if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) {
+ success = nms_keyfile_plugin_update_connection (priv->keyfile_plugin,
+ storage,
+ connection,
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
+ NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE),
+ shadowed_storage,
+ shadowed_owned,
+ force_rename,
+ out_new_storage,
+ out_new_connection,
+ error);
+ } else {
+ nm_assert (!shadowed_storage);
+ nm_assert (!shadowed_owned);
+ success = nm_settings_plugin_update_connection (plugin,
+ storage,
+ connection,
+ out_new_storage,
+ out_new_connection,
+ error);
+ }
+
+ return success;
+}
+
+static void
+_set_nmmeta_tombstone (NMSettings *self,
+ const char *uuid,
+ gboolean tombstone_on_disk,
+ gboolean tombstone_in_memory,
+ const char *shadowed_storage)
+{
+ NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+ gs_unref_object NMSettingsStorage *tombstone_1_storage = NULL;
+ gs_unref_object NMSettingsStorage *tombstone_2_storage = NULL;
+
+ if (tombstone_on_disk) {
+ if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin,
+ FALSE,
+ uuid,
+ FALSE,
+ TRUE,
+ NULL,
+ &tombstone_1_storage,
+ NULL))
+ tombstone_in_memory = TRUE;
+ if (tombstone_1_storage)
+ _connection_changed_track (self, tombstone_1_storage, NULL, FALSE);
+ }
+
+ if (tombstone_in_memory) {
+ if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin,
+ FALSE,
+ uuid,
+ TRUE,
+ TRUE,
+ shadowed_storage,
+ &tombstone_2_storage,
+ NULL)) {
+ nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin,
+ TRUE,
+ uuid,
+ TRUE,
+ TRUE,
+ shadowed_storage,
+ &tombstone_2_storage,
+ NULL);
+ }
+ _connection_changed_track (self, tombstone_2_storage, NULL, FALSE);
+ }
+}
+
/**
* nm_settings_add_connection:
* @self: the #NMSettings object
@@ -1396,24 +1681,34 @@ nm_settings_add_connection (NMSettings *self,
NMSettingsConnection **out_sett_conn,
GError **error)
{
+ NMSettingsPrivate *priv;
gs_unref_object NMConnection *connection_cloned_1 = NULL;
gs_unref_object NMConnection *new_connection = NULL;
gs_unref_object NMSettingsStorage *new_storage = NULL;
+ gs_unref_object NMSettingsStorage *shadowed_storage = NULL;
+ NMSettingsStorage *update_storage = NULL;
gs_free_error GError *local = NULL;
SettConnEntry *sett_conn_entry;
const char *uuid;
StorageData *sd;
+ gboolean new_in_memory;
+ gboolean success;
+ const char *shadowed_storage_filename = NULL;
+
+ priv = NM_SETTINGS_GET_PRIVATE (self);
- nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY));
- nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
+ new_in_memory = (persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK);
- nm_assert ( !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
- || persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
+ nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK));
- nm_assert ( !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)
- || persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY);
+ if (NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
+ | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
+ nm_assert (new_in_memory);
+ new_in_memory = TRUE;
+ }
nm_assert (!NM_FLAGS_ANY (add_reason, ~NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT));
@@ -1421,8 +1716,8 @@ nm_settings_add_connection (NMSettings *self,
uuid = nm_connection_get_uuid (connection);
- /* Make sure a connection with this UUID doesn't already exist */
- if (_sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid))) {
+ sett_conn_entry = _sett_conn_entries_get (self, uuid);
+ if (_sett_conn_entry_get_conn (sett_conn_entry)) {
g_set_error_literal (error,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_UUID_EXISTS,
@@ -1446,47 +1741,152 @@ nm_settings_add_connection (NMSettings *self,
if (connection_cloned_1)
connection = connection_cloned_1;
- if (!_add_connection_to_first_plugin (self,
- connection,
- ( persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK
- || NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE
- | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)),
- NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
- NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE),
- &new_storage,
- &new_connection,
- &local)) {
- g_set_error (error,
- NM_SETTINGS_ERROR,
- NM_SETTINGS_ERROR_FAILED,
- "failure adding connection: %s",
- local->message);
+ if (sett_conn_entry) {
+ c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) {
+ if (!nm_settings_storage_is_meta_data (sd->storage))
+ continue;
+ shadowed_storage = nm_g_object_ref (_sett_conn_entry_find_shadowed_storage (sett_conn_entry,
+ nm_settings_storage_get_shadowed_storage (sd->storage, NULL),
+ NULL));
+ if (shadowed_storage) {
+ /* We have a nmmeta tombstone that indicates that a storage is shadowed.
+ *
+ * This happens when deleting a in-memory profile that was decoupled from
+ * the persitant storage with NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED.
+ * We need to take over this storage again... */
+ break;
+ }
+ }
+ }
+
+ if ( shadowed_storage
+ && !new_in_memory) {
+ NMSettingsStorage *conflicting_storage;
+
+ conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage (sett_conn_entry,
+ nm_settings_storage_get_plugin (shadowed_storage),
+ shadowed_storage,
+ priv->plugins);
+ if (conflicting_storage) {
+ /* We cannot add the profile as @shadowed_storage, because there is another, existing storage
+ * that would hide it. Just add it as new storage. In general, this leads to duplication of profiles,
+ * but the circumstances where this happens are very exotic (you need at least one additional settings
+ * plugin, then going through the paths of making shadowed_storage in-memory-detached and delete it,
+ * and finally adding the conflicting storage outside of NM and restart/reload). */
+ _LOGT ("ignore shadowed storage "NM_SETTINGS_STORAGE_PRINT_FMT" due to conflicting storage "NM_SETTINGS_STORAGE_PRINT_FMT,
+ NM_SETTINGS_STORAGE_PRINT_ARG (shadowed_storage),
+ NM_SETTINGS_STORAGE_PRINT_ARG (conflicting_storage));
+ } else
+ update_storage = shadowed_storage;
+ }
+
+ shadowed_storage_filename = ( shadowed_storage
+ && !update_storage)
+ ? nm_settings_storage_get_filename_for_shadowed_storage (shadowed_storage)
+ : NULL;
+
+again_add_connection:
+
+ if (!update_storage) {
+ success = _add_connection_to_first_plugin (self,
+ sett_conn_entry,
+ connection,
+ new_in_memory,
+ sett_flags,
+ shadowed_storage_filename,
+ FALSE,
+ &new_storage,
+ &new_connection,
+ &local);
+ } else {
+ success = _update_connection_to_plugin (self,
+ update_storage,
+ connection,
+ sett_flags,
+ FALSE,
+ shadowed_storage_filename,
+ FALSE,
+ &new_storage,
+ &new_connection,
+ &local);
+ if (!success) {
+ if (!NMS_IS_KEYFILE_STORAGE (update_storage)) {
+ /* hm, the intended storage is not keyfile (it's ifcfg-rh). This settings
+ * plugin may not support the new connection. So step back and retry adding
+ * the profile anew. */
+ _LOGT ("failure to add profile as existing storage \"%s\": %s",
+ nm_settings_storage_get_filename (update_storage),
+ local->message);
+ update_storage = NULL;
+ g_clear_object (&shadowed_storage);
+ shadowed_storage_filename = NULL;
+ g_clear_error (&local);
+ goto again_add_connection;
+ }
+ }
+ }
+
+ if (!success) {
+ if (!update_storage) {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_FAILED,
+ "failure adding connection: %s",
+ local->message);
+ } else {
+ g_set_error (error,
+ NM_SETTINGS_ERROR,
+ NM_SETTINGS_ERROR_FAILED,
+ "failure writing connection to existing storage \"%s\": %s",
+ nm_settings_storage_get_filename (update_storage),
+ local->message);
+ }
return FALSE;
}
sett_conn_entry = _connection_changed_track (self, new_storage, new_connection, TRUE);
c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) {
-
- if (!nm_settings_storage_is_keyfile_tombstone (sd->storage))
+ const NMSettingsMetaData *meta_data;
+ gs_unref_object NMSettingsStorage *new_tombstone_storage = NULL;
+ gboolean in_memory;
+ gboolean simulate;
+
+ meta_data = nm_settings_storage_is_meta_data_alive (sd->storage);
+ if ( !meta_data
+ || !meta_data->is_tombstone)
continue;
- if (nm_settings_storage_is_keyfile_run (sd->storage)) {
- /* We remove this file from /run. */
- } else {
+ if (nm_settings_storage_is_keyfile_run (sd->storage))
+ in_memory = TRUE;
+ else {
if (nm_settings_storage_is_keyfile_run (new_storage)) {
/* Don't remove the file from /etc if we just wrote an in-memory connection */
continue;
}
+ in_memory = FALSE;
}
- nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (sd->storage),
- sd->storage,
- NULL);
-
- nm_assert (!nm_settings_storage_is_keyfile_tombstone (sd->storage));
-
- _connection_changed_track (self, sd->storage, NULL, FALSE);
+ simulate = FALSE;
+again_delete_tombstone:
+ if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin,
+ simulate,
+ uuid,
+ in_memory,
+ FALSE,
+ NULL,
+ &new_tombstone_storage,
+ NULL)) {
+ /* Ups, something went wrong. We really need to get rid of the tombstone. At least
+ * forget about it in-memory. Upong next restart/reload, this might be reverted
+ * however :( .*/
+ if (!simulate) {
+ simulate = TRUE;
+ goto again_delete_tombstone;
+ }
+ }
+ if (new_tombstone_storage)
+ _connection_changed_track (self, new_tombstone_storage, NULL, FALSE);
}
_connection_changed_process_all_dirty (self,
@@ -1520,16 +1920,19 @@ nm_settings_update_connection (NMSettings *self,
const char *log_context_name,
GError **error)
{
- NMSettingsPrivate *priv;
gs_unref_object NMConnection *connection_cloned_1 = NULL;
gs_unref_object NMConnection *new_connection_cloned = NULL;
gs_unref_object NMConnection *new_connection = NULL;
NMConnection *new_connection_real;
gs_unref_object NMSettingsStorage *cur_storage = NULL;
gs_unref_object NMSettingsStorage *new_storage = NULL;
+ NMSettingsStorage *drop_storage = NULL;
+ SettConnEntry *sett_conn_entry;
gboolean cur_in_memory;
gboolean new_in_memory;
const char *uuid;
+ gboolean tombstone_in_memory = FALSE;
+ gboolean tombstone_on_disk = FALSE;
g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn), FALSE);
@@ -1539,18 +1942,20 @@ nm_settings_update_connection (NMSettings *self,
nm_assert (!NM_FLAGS_ANY (sett_flags, ~sett_mask));
nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY));
- priv = NM_SETTINGS_GET_PRIVATE (self);
-
cur_storage = g_object_ref (nm_settings_connection_get_storage (sett_conn));
uuid = nm_settings_storage_get_uuid (cur_storage);
nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage));
- nm_assert (_sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid)) == sett_conn);
+
+ sett_conn_entry = _sett_conn_entries_get (self, uuid);
+
+ nm_assert (_sett_conn_entry_get_conn (sett_conn_entry) == sett_conn);
if (connection) {
gs_free_error GError *local = NULL;
@@ -1581,8 +1986,8 @@ nm_settings_update_connection (NMSettings *self,
if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP) {
persist_mode = cur_in_memory
- ? NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED
- : NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ ? NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY
+ : NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
}
if ( NM_FLAGS_HAS (sett_mask, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
@@ -1602,11 +2007,15 @@ nm_settings_update_connection (NMSettings *self,
default_wired_clear_tag (self, device, sett_conn, FALSE);
- if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)) {
- /* making a default-wired-connection a regulard connection implies persisting
- * it to disk (unless specified differently). */
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)) {
+ /* making a default-wired-connection a regular connection implies persisting
+ * it to disk (unless specified differently).
+ *
+ * Actually, this line is probably unreached, because we should not use
+ * NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST to toggle the nm-generated
+ * flag. */
+ nm_assert_not_reached ();
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
}
}
}
@@ -1621,12 +2030,13 @@ nm_settings_update_connection (NMSettings *self,
* in-memory. The caller did not request to persist this to disk, however we need
* to store the flags in run. */
nm_assert (cur_in_memory);
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY;
}
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK)
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK)
new_in_memory = FALSE;
- else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+ else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY))
new_in_memory = TRUE;
else {
@@ -1651,60 +2061,96 @@ nm_settings_update_connection (NMSettings *self,
| NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE);
}
-
if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST) {
new_storage = g_object_ref (cur_storage);
- new_connection = g_object_ref (connection);
+ new_connection_real = connection;
_LOGT ("update[%s]: %s: update profile \"%s\" (not persisted)",
nm_settings_storage_get_uuid (cur_storage),
log_context_name,
nm_connection_get_id (connection));
} else {
- gboolean success;
- gboolean migrate_storage;
+ NMSettingsStorage *shadowed_storage;
+ const char *cur_shadowed_storage_filename;
+ const char *new_shadowed_storage_filename = NULL;
+ gboolean cur_shadowed_owned;
+ gboolean new_shadowed_owned = FALSE;
+ NMSettingsStorage *update_storage = NULL;
gs_free_error GError *local = NULL;
+ gboolean success;
+
+ cur_shadowed_storage_filename = nm_settings_storage_get_shadowed_storage (cur_storage, &cur_shadowed_owned);
+
+ shadowed_storage = _sett_conn_entry_find_shadowed_storage (sett_conn_entry, cur_shadowed_storage_filename, cur_storage);
+ if (!shadowed_storage) {
+ cur_shadowed_storage_filename = NULL;
+ cur_shadowed_owned = FALSE;
+ }
- if (new_in_memory != cur_in_memory)
- migrate_storage = TRUE;
- else if ( !new_in_memory
- && nm_settings_storage_is_keyfile_lib (cur_storage)) {
+ if ( new_in_memory
+ && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) {
+ if (cur_in_memory) {
+ drop_storage = shadowed_storage;
+ update_storage = cur_storage;
+ } else
+ drop_storage = cur_storage;
+ } else if ( !new_in_memory
+ && cur_in_memory
+ && shadowed_storage) {
+ drop_storage = cur_storage;
+ update_storage = shadowed_storage;
+ } else if (new_in_memory != cur_in_memory) {
+ if (!new_in_memory)
+ drop_storage = cur_storage;
+ else if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY)
+ drop_storage = cur_storage;
+ else {
+ nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED));
+ }
+ } else if (nm_settings_storage_is_keyfile_lib (cur_storage)) {
/* the profile is a keyfile in /usr/lib. It cannot be overwritten, we must migrate it
* from /usr/lib to /etc. */
- migrate_storage = TRUE;
} else
- migrate_storage = FALSE;
+ update_storage = cur_storage;
+
+ if (new_in_memory) {
+ if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) {
+ /* pass */
+ } else if (!cur_in_memory) {
+ new_shadowed_storage_filename = nm_settings_storage_get_filename_for_shadowed_storage (cur_storage);
+ if ( new_shadowed_storage_filename
+ && persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED)
+ new_shadowed_owned = TRUE;
+ } else {
+ new_shadowed_storage_filename = cur_shadowed_storage_filename;
+ if ( new_shadowed_storage_filename
+ && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY)
+ new_shadowed_owned = TRUE;
+ }
+ }
- if (migrate_storage) {
+ if (!update_storage) {
success = _add_connection_to_first_plugin (self,
+ sett_conn_entry,
connection,
new_in_memory,
- NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
- NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE),
+ sett_flags,
+ new_shadowed_storage_filename,
+ new_shadowed_owned,
&new_storage,
&new_connection,
&local);
} else {
- NMSettingsPlugin *plugin;
-
- plugin = nm_settings_storage_get_plugin (cur_storage);
- if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) {
- success = nms_keyfile_plugin_update_connection (priv->keyfile_plugin,
- cur_storage,
- connection,
- NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED),
- NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE),
- NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME),
- &new_storage,
- &new_connection,
- &local);
- } else {
- success = nm_settings_plugin_update_connection (nm_settings_storage_get_plugin (cur_storage),
- cur_storage,
- connection,
- &new_storage,
- &new_connection,
- &local);
- }
+ success = _update_connection_to_plugin (self,
+ update_storage,
+ connection,
+ sett_flags,
+ update_reason,
+ new_shadowed_storage_filename,
+ new_shadowed_owned,
+ &new_storage,
+ &new_connection,
+ &local);
}
if (!success) {
gboolean ignore_failure;
@@ -1715,7 +2161,7 @@ nm_settings_update_connection (NMSettings *self,
nm_settings_storage_get_uuid (cur_storage),
log_context_name,
ignore_failure ? "ignore " : "",
- migrate_storage ? "write" : "update",
+ update_storage ? "update" : "write",
nm_connection_get_id (connection),
local->message);
if (!ignore_failure) {
@@ -1723,76 +2169,77 @@ nm_settings_update_connection (NMSettings *self,
NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_INVALID_CONNECTION,
"failed to %s connection: %s",
- migrate_storage ? "write" : "update",
+ update_storage ? "update" : "write",
local->message);
return FALSE;
}
+
new_storage = g_object_ref (cur_storage);
- new_connection = g_object_ref (connection);
+ new_connection_real = connection;
} else {
+ gs_unref_variant GVariant *agent_owned_secrets = NULL;
+
_LOGT ("update[%s]: %s: %s profile \"%s\"",
nm_settings_storage_get_uuid (cur_storage),
log_context_name,
- migrate_storage ? "write" : "update",
+ update_storage ? "update" : "write",
nm_connection_get_id (connection));
- }
- }
-
- nm_assert_valid_settings_storage (NULL, new_storage);
- nm_assert (NM_IS_CONNECTION (new_connection));
- nm_assert (nm_streq (uuid, nm_settings_storage_get_uuid (new_storage)));
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)
- new_connection_real = new_connection;
- else {
- gs_unref_variant GVariant *agent_owned_secrets = NULL;
-
- agent_owned_secrets = nm_connection_to_dbus (connection,
- NM_CONNECTION_SERIALIZE_ONLY_SECRETS
- | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
- new_connection_real = _connection_changed_normalize_connection (new_storage,
- new_connection,
- agent_owned_secrets,
- &new_connection_cloned);
- if (!new_connection_real) {
- nm_assert_not_reached ();
- new_connection_real = new_connection;
+ nm_assert_valid_settings_storage (NULL, new_storage);
+ nm_assert (NM_IS_CONNECTION (new_connection));
+ nm_assert (nm_streq (uuid, nm_settings_storage_get_uuid (new_storage)));
+
+
+ agent_owned_secrets = nm_connection_to_dbus (connection,
+ NM_CONNECTION_SERIALIZE_ONLY_SECRETS
+ | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED);
+ new_connection_real = _connection_changed_normalize_connection (new_storage,
+ new_connection,
+ agent_owned_secrets,
+ &new_connection_cloned);
+ if (!new_connection_real) {
+ nm_assert_not_reached ();
+ new_connection_real = new_connection;
+ }
}
}
+ nm_assert (NM_IS_SETTINGS_STORAGE (new_storage));
nm_assert (NM_IS_CONNECTION (new_connection_real));
_connection_changed_track (self, new_storage, new_connection_real, TRUE);
- if (new_storage != cur_storage) {
+ if ( drop_storage
+ && drop_storage != new_storage) {
gs_free_error GError *local = NULL;
- gboolean remove_from_disk;
- if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED)
- remove_from_disk = FALSE;
- else if (nm_settings_storage_is_keyfile_lib (cur_storage))
- remove_from_disk = FALSE;
- else
- remove_from_disk = TRUE;
-
- if (remove_from_disk) {
- if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage),
- cur_storage,
- &local)) {
- const char *filename;
-
- filename = nm_settings_storage_get_filename (cur_storage);
- _LOGW ("update[%s]: failed to delete moved storage "NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s: %s",
- nm_settings_storage_get_uuid (cur_storage),
- NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage),
- local->message,
- NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""));
- }
+ if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (drop_storage),
+ drop_storage,
+ &local)) {
+ const char *filename;
- _connection_changed_track (self, cur_storage, NULL, FALSE);
- }
+ filename = nm_settings_storage_get_filename (drop_storage);
+ _LOGT ("update[%s]: failed to delete moved storage "NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s: %s",
+ nm_settings_storage_get_uuid (drop_storage),
+ NM_SETTINGS_STORAGE_PRINT_ARG (drop_storage),
+ NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""),
+ local->message);
+ /* there is no aborting back form this. We must get rid of the connection and
+ * cannot do better than log a message. Proceed, but remember to write tombstones. */
+ if (nm_settings_storage_is_keyfile_run (cur_storage))
+ tombstone_in_memory = TRUE;
+ else
+ tombstone_on_disk = TRUE;
+ } else
+ _connection_changed_track (self, drop_storage, NULL, FALSE);
}
+ _set_nmmeta_tombstone (self,
+ uuid,
+ tombstone_on_disk,
+ tombstone_in_memory,
+ NULL);
+
_connection_changed_process_all_dirty (self,
FALSE,
sett_flags,
@@ -1808,23 +2255,24 @@ nm_settings_delete_connection (NMSettings *self,
NMSettingsConnection *sett_conn,
gboolean allow_add_to_no_auto_default)
{
- NMSettingsPrivate *priv;
NMSettingsStorage *cur_storage;
+ NMSettingsStorage *shadowed_storage;
+ NMSettingsStorage *shadowed_storage_unowned = NULL;
+ NMSettingsStorage *drop_storages[2] = { };
gs_free_error GError *local = NULL;
SettConnEntry *sett_conn_entry;
+ const char *cur_shadowed_storage_filename;
+ const char *new_shadowed_storage_filename = NULL;
+ gboolean cur_shadowed_owned;
const char *uuid;
- gboolean delete;
gboolean tombstone_in_memory = FALSE;
gboolean tombstone_on_disk = FALSE;
- gs_unref_object NMSettingsStorage *tombstone_1_storage = NULL;
- gs_unref_object NMSettingsStorage *tombstone_2_storage = NULL;
+ int i;
g_return_if_fail (NM_IS_SETTINGS (self));
g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
g_return_if_fail (nm_settings_has_connection (self, sett_conn));
- priv = NM_SETTINGS_GET_PRIVATE (self);
-
cur_storage = nm_settings_connection_get_storage (sett_conn);
nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage));
@@ -1843,38 +2291,62 @@ nm_settings_delete_connection (NMSettings *self,
if (NM_IN_SET (s->storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN,
NMS_KEYFILE_STORAGE_TYPE_ETC))
- delete = TRUE;
- else {
+ drop_storages[0] = cur_storage;
+ else
tombstone_on_disk = TRUE;
- delete = FALSE;
- }
} else
- delete = TRUE;
+ drop_storages[0] = cur_storage;
- if (delete) {
+ cur_shadowed_storage_filename = nm_settings_storage_get_shadowed_storage (cur_storage, &cur_shadowed_owned);
+
+ shadowed_storage = _sett_conn_entry_find_shadowed_storage (sett_conn_entry, cur_shadowed_storage_filename, cur_storage);
+ if (shadowed_storage) {
+ if (!cur_shadowed_owned)
+ shadowed_storage_unowned = g_steal_pointer (&shadowed_storage);
+ }
+ drop_storages[1] = shadowed_storage;
+
+ for (i = 0; i < (int) G_N_ELEMENTS (drop_storages); i++) {
+ NMSettingsStorage *storage;
StorageData *sd;
- if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (cur_storage),
- cur_storage,
+ storage = drop_storages[i];
+ if (!storage)
+ continue;
+
+ if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (storage),
+ storage,
&local)) {
- _LOGW ("delete-connection: failed to delete storage "NM_SETTINGS_STORAGE_PRINT_FMT": %s",
- NM_SETTINGS_STORAGE_PRINT_ARG (cur_storage),
+ _LOGT ("delete-connection: failed to delete storage "NM_SETTINGS_STORAGE_PRINT_FMT": %s",
+ NM_SETTINGS_STORAGE_PRINT_ARG (storage),
local->message);
g_clear_error (&local);
/* there is no aborting back form this. We must get rid of the connection and
- * cannot do better than warn. Proceed... */
- tombstone_in_memory = TRUE;
- }
-
- sett_conn_entry = _connection_changed_track (self, cur_storage, NULL, FALSE);
+ * cannot do better than log a message. Proceed, but remember to write tombstones. */
+ if (nm_settings_storage_is_keyfile_run (cur_storage))
+ tombstone_in_memory = TRUE;
+ else
+ tombstone_on_disk = TRUE;
+ sett_conn_entry = _sett_conn_entries_get (self, uuid);
+ } else
+ sett_conn_entry = _connection_changed_track (self, storage, NULL, FALSE);
c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) {
- if (sd->storage == cur_storage)
- continue;
- if (nm_settings_storage_is_keyfile_tombstone (sd->storage))
+ if (NM_IN_SET (sd->storage, drop_storages[0],
+ drop_storages[1]))
continue;
if (!_storage_data_is_alive (sd))
continue;
+ if (nm_settings_storage_is_meta_data (sd->storage))
+ continue;
+
+ if (sd->storage == shadowed_storage_unowned) {
+ /* this only happens if we leak a profile on disk after NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED.
+ * We need to write a tombstone and remember the shadowed-storage. */
+ tombstone_in_memory = TRUE;
+ new_shadowed_storage_filename = nm_settings_storage_get_filename (shadowed_storage_unowned);
+ continue;
+ }
/* we have still conflicting storages. We need to hide them with tombstones. */
if (nm_settings_storage_is_keyfile_run (sd->storage)) {
@@ -1885,37 +2357,11 @@ nm_settings_delete_connection (NMSettings *self,
}
}
- if (tombstone_on_disk) {
- if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin,
- FALSE,
- uuid,
- FALSE,
- TRUE,
- &tombstone_1_storage,
- NULL))
- tombstone_in_memory = TRUE;
- if (tombstone_1_storage)
- _connection_changed_track (self, tombstone_1_storage, NULL, FALSE);
- }
-
- if (tombstone_in_memory) {
- if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin,
- FALSE,
- uuid,
- TRUE,
- TRUE,
- &tombstone_2_storage,
- NULL)) {
- nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin,
- TRUE,
- uuid,
- TRUE,
- TRUE,
- &tombstone_2_storage,
- NULL);
- }
- _connection_changed_track (self, tombstone_2_storage, NULL, FALSE);
- }
+ _set_nmmeta_tombstone (self,
+ uuid,
+ tombstone_on_disk,
+ tombstone_in_memory,
+ new_shadowed_storage_filename);
_connection_changed_process_all_dirty (self,
allow_add_to_no_auto_default,
@@ -2164,7 +2610,7 @@ settings_add_connection_helper (NMSettings *self,
}
if (NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK))
- persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+ persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
else {
nm_assert (NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY));
persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
index b65013a431..41b20850ad 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -676,7 +676,6 @@ ifupdown_new_connection_from_if_block (if_block *block,
NM_SETTING_CONNECTION_INTERFACE_NAME, block->name,
NM_SETTING_CONNECTION_ID, idstr,
NM_SETTING_CONNECTION_UUID, uuid,
- NM_SETTING_CONNECTION_READ_ONLY, TRUE,
NM_SETTING_CONNECTION_AUTOCONNECT, (gboolean) (!!autoconnect),
NULL);
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index de076c0122..fbe70ef4d2 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -216,13 +216,22 @@ _read_from_file (const char *full_filename,
struct stat *out_stat,
NMTernary *out_is_nm_generated,
NMTernary *out_is_volatile,
+ char **out_shadowed_storage,
+ NMTernary *out_shadowed_owned,
GError **error)
{
NMConnection *connection;
nm_assert (full_filename && full_filename[0] == '/');
- connection = nms_keyfile_reader_from_file (full_filename, plugin_dir, out_stat, out_is_nm_generated, out_is_volatile, error);
+ connection = nms_keyfile_reader_from_file (full_filename,
+ plugin_dir,
+ out_stat,
+ out_is_nm_generated,
+ out_is_volatile,
+ out_shadowed_storage,
+ out_shadowed_owned,
+ error);
nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS));
nm_assert (!connection || nm_utils_is_uuid (nm_connection_get_uuid (connection)));
@@ -244,7 +253,7 @@ _nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */,
nm_assert (!plugin || NMS_IS_KEYFILE_PLUGIN (plugin));
nm_assert (NMS_IS_KEYFILE_STORAGE (storage));
nm_assert (!plugin || plugin == nm_settings_storage_get_plugin (storage));
- nm_assert (!((NMSKeyfileStorage *) storage)->is_tombstone || !(((NMSKeyfileStorage *) storage)->connection));
+
nm_assert (({
const char *f = nms_keyfile_storage_get_filename (storage);
f && f[0] == '/';
@@ -254,6 +263,11 @@ _nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */,
nm_assert (nm_utils_is_uuid (uuid));
+ nm_assert ( ((NMSKeyfileStorage *) storage)->is_meta_data
+ || !(((NMSKeyfileStorage *) storage)->u.conn_data.connection)
+ || ( NM_IS_CONNECTION ((((NMSKeyfileStorage *) storage)->u.conn_data.connection))
+ && nm_streq0 (uuid, nm_connection_get_uuid ((((NMSKeyfileStorage *) storage)->u.conn_data.connection)))));
+
nm_assert ( !tracked
|| !plugin
|| c_list_contains (&NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages._storage_lst_head,
@@ -264,7 +278,8 @@ _nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */,
|| storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_filename,
nms_keyfile_storage_get_filename (storage)));
- if (tracked && plugin) {
+ if ( tracked
+ && plugin) {
sbuh = g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_uuid, &uuid);
nm_assert (sbuh);
nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &((NMSKeyfileStorage *) storage)->parent._storage_by_uuid_lst));
@@ -285,6 +300,8 @@ _load_file (NMSKeyfilePlugin *self,
gs_unref_object NMConnection *connection = NULL;
NMTernary is_volatile_opt;
NMTernary is_nm_generated_opt;
+ NMTernary shadowed_owned_opt;
+ gs_free char *shadowed_storage = NULL;
gs_free_error GError *local = NULL;
gs_free char *full_filename = NULL;
struct stat st;
@@ -292,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)
@@ -305,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");
@@ -332,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);
@@ -344,6 +364,8 @@ _load_file (NMSKeyfilePlugin *self,
&st,
&is_nm_generated_opt,
&is_volatile_opt,
+ &shadowed_storage,
+ &shadowed_owned_opt,
&local);
if (!connection) {
if (error)
@@ -359,6 +381,8 @@ _load_file (NMSKeyfilePlugin *self,
storage_type,
is_nm_generated_opt,
is_volatile_opt,
+ shadowed_storage,
+ shadowed_owned_opt,
&st.st_mtim);
}
@@ -454,7 +478,7 @@ _storages_consolidate (NMSKeyfilePlugin *self,
c_list_init (&storages_deleted);
c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst)
- storage_old->dirty = TRUE;
+ storage_old->is_dirty = TRUE;
c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) {
storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage_new));
@@ -465,28 +489,28 @@ _storages_consolidate (NMSKeyfilePlugin *self,
|| !nm_streq (nms_keyfile_storage_get_uuid (storage_new), nms_keyfile_storage_get_uuid (storage_old))) {
if (storage_old) {
nm_sett_util_storages_steal (&priv->storages, storage_old);
- c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst);
+ c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst);
}
- storage_new->dirty = FALSE;
+ storage_new->is_dirty = FALSE;
nm_sett_util_storages_add_take (&priv->storages, storage_new);
g_ptr_array_add (storages_modified, g_object_ref (storage_new));
continue;
}
- storage_old->dirty = FALSE;
+ storage_old->is_dirty = FALSE;
nms_keyfile_storage_copy_content (storage_old, storage_new);
nms_keyfile_storage_destroy (storage_new);
g_ptr_array_add (storages_modified, g_object_ref (storage_old));
}
c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) {
- if (!storage_old->dirty)
+ if (!storage_old->is_dirty)
continue;
if ( replace_all
|| ( storages_replaced
&& g_hash_table_contains (storages_replaced, storage_old))) {
nm_sett_util_storages_steal (&priv->storages, storage_old);
- c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst);
+ c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst);
}
}
@@ -494,7 +518,7 @@ _storages_consolidate (NMSKeyfilePlugin *self,
for (i = 0; i < storages_modified->len; i++) {
storage = storages_modified->pdata[i];
- storage->dirty = TRUE;
+ storage->is_dirty = TRUE;
}
for (i = 0; i < storages_modified->len; i++) {
@@ -502,19 +526,22 @@ _storages_consolidate (NMSKeyfilePlugin *self,
storage = storages_modified->pdata[i];
- if (!storage->dirty) {
- /* the entry is no longer dirty. In the meantime we already emited
+ if (!storage->is_dirty) {
+ /* the entry is no longer is_dirty. In the meantime we already emited
* another signal for it. */
continue;
}
- storage->dirty = FALSE;
- if (storage != nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage))) {
+ storage->is_dirty = FALSE;
+
+ if (c_list_is_empty (&storage->parent._storage_lst)) {
/* hm? The profile was deleted in the meantime? That is only possible
* if the signal handler called again into the plugin. In any case, the event
* was already emitted. Skip. */
continue;
}
+ nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage)));
+
connection = nms_keyfile_storage_steal_connection (storage);
callback (NM_SETTINGS_PLUGIN (self),
@@ -523,9 +550,8 @@ _storages_consolidate (NMSKeyfilePlugin *self,
user_data);
}
- while ((storage = c_list_first_entry (&storages_deleted, NMSKeyfileStorage, parent._storage_lst))) {
- c_list_unlink (&storage->parent._storage_lst);
- storage->is_tombstone = FALSE;
+ while ((storage = c_list_first_entry (&storages_deleted, NMSKeyfileStorage, parent._storage_by_uuid_lst))) {
+ c_list_unlink (&storage->parent._storage_by_uuid_lst);
callback (NM_SETTINGS_PLUGIN (self),
NM_SETTINGS_STORAGE (storage),
NULL,
@@ -717,9 +743,11 @@ load_connections (NMSettingsPlugin *plugin,
gboolean
nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
NMConnection *connection,
+ gboolean in_memory,
gboolean is_nm_generated,
gboolean is_volatile,
- gboolean in_memory,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
NMSettingsStorage **out_storage,
NMConnection **out_connection,
GError **error)
@@ -739,8 +767,16 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
nm_assert (out_storage && !*out_storage);
nm_assert (out_connection && !*out_connection);
+ nm_assert ( in_memory
+ || ( !is_nm_generated
+ && !is_volatile
+ && !shadowed_storage
+ && !shadowed_owned));
+
uuid = nm_connection_get_uuid (connection);
+ /* Note that even if the caller requests persistent storage, we may switch to in-memory, if
+ * no /etc directory is configured. */
storage_type = !in_memory && priv->dirname_etc
? NMS_KEYFILE_STORAGE_TYPE_ETC
: NMS_KEYFILE_STORAGE_TYPE_RUN;
@@ -748,6 +784,8 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
if (!nms_keyfile_writer_connection (connection,
is_nm_generated,
is_volatile,
+ shadowed_storage,
+ shadowed_owned,
storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC
? priv->dirname_etc
: priv->dirname_run,
@@ -779,11 +817,12 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
nm_assert (full_filename && full_filename[0] == '/');
nm_assert (!nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename));
- _LOGT ("commit: %s (%s) added as \"%s\"%s",
+ _LOGT ("commit: %s (%s) added as \"%s\"%s%s%s%s",
uuid,
nm_connection_get_id (connection),
full_filename,
- _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile));
+ _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile),
+ NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, shadowed_owned ? "\", owned)" : "\")", ""));
storage = nms_keyfile_storage_new_connection (self,
g_steal_pointer (&reread),
@@ -791,6 +830,8 @@ nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
storage_type,
is_nm_generated ? NM_TERNARY_TRUE : NM_TERNARY_FALSE,
is_volatile ? NM_TERNARY_TRUE : NM_TERNARY_FALSE,
+ shadowed_storage,
+ shadowed_owned ? NM_TERNARY_TRUE : NM_TERNARY_FALSE,
nm_sett_util_stat_mtime (full_filename, FALSE, &mtime));
nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage));
@@ -813,6 +854,8 @@ add_connection (NMSettingsPlugin *plugin,
FALSE,
FALSE,
FALSE,
+ NULL,
+ FALSE,
out_storage,
out_connection,
error);
@@ -824,6 +867,8 @@ nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
NMConnection *connection,
gboolean is_nm_generated,
gboolean is_volatile,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
gboolean force_rename,
NMSettingsStorage **out_storage,
NMConnection **out_connection,
@@ -839,6 +884,7 @@ nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
const char *previous_filename;
gboolean reread_same;
const char *uuid;
+ char strbuf[100];
_nm_assert_storage (self, storage, TRUE);
nm_assert (NM_IS_CONNECTION (connection));
@@ -847,8 +893,13 @@ nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
nm_assert (!error || !*error);
nm_assert (NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC,
NMS_KEYFILE_STORAGE_TYPE_RUN));
- nm_assert ( (!is_nm_generated && !is_volatile)
- || storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN);
+ nm_assert (!storage->is_meta_data);
+ nm_assert ( storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN
+ || ( !is_nm_generated
+ && !is_volatile
+ && !shadowed_storage
+ && !shadowed_owned));
+ nm_assert (!shadowed_owned || shadowed_storage);
nm_assert ( priv->dirname_etc
|| storage->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC);
@@ -858,6 +909,8 @@ nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
if (!nms_keyfile_writer_connection (connection,
is_nm_generated,
is_volatile,
+ shadowed_storage,
+ shadowed_owned,
storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC
? priv->dirname_etc
: priv->dirname_run,
@@ -890,14 +943,17 @@ nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS);
nm_assert (nm_streq (nm_connection_get_uuid (reread), uuid));
- _LOGT ("commit: \"%s\": profile %s (%s) written",
+ _LOGT ("commit: \"%s\": profile %s (%s) written%s%s%s%s",
full_filename,
uuid,
- nm_connection_get_id (connection));
+ nm_connection_get_id (connection),
+ _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile),
+ NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", ""));
- storage->is_nm_generated = is_nm_generated;
- storage->is_volatile = is_volatile;
- storage->stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime);
+ storage->u.conn_data.is_nm_generated = is_nm_generated;
+ storage->u.conn_data.is_volatile = is_volatile;
+ storage->u.conn_data.stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime);
+ storage->u.conn_data.shadowed_owned = shadowed_owned;
*out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage));
*out_connection = g_steal_pointer (&reread);
@@ -917,6 +973,8 @@ update_connection (NMSettingsPlugin *plugin,
connection,
FALSE,
FALSE,
+ NULL,
+ FALSE,
FALSE,
out_storage,
out_connection,
@@ -969,14 +1027,13 @@ delete_connection (NMSettingsPlugin *plugin,
_LOGT ("commit: deleted \"%s\", %s %s (%s%s%s%s)",
previous_filename,
- storage->is_tombstone ? "tombstone" : "profile",
+ storage->is_meta_data ? "meta-data" : "profile",
uuid,
operation_message,
NM_PRINT_FMT_QUOTED (remove_from_disk_errmsg, ": ", remove_from_disk_errmsg, "", ""));
if (success) {
nm_sett_util_storages_steal (&priv->storages, storage);
- storage->is_tombstone = FALSE;
nms_keyfile_storage_destroy (storage);
}
@@ -998,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
@@ -1022,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)
{
@@ -1038,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);
@@ -1050,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);
@@ -1069,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
@@ -1088,7 +1152,7 @@ nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self,
storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, nmmeta_filename);
nm_assert ( !storage
- || ( storage->is_tombstone
+ || ( storage->is_meta_data
&& storage->storage_type == storage_type
&& nm_streq (nms_keyfile_storage_get_uuid (storage), uuid)));
@@ -1098,16 +1162,18 @@ 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);
} else {
- if (storage) {
+ if (storage)
storage_result = nm_sett_util_storages_steal (&priv->storages, storage);
- storage_result->is_tombstone = FALSE;
- }
}
out:
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/settings/plugins/keyfile/nms-keyfile-plugin.h
index f3e6870861..4844096456 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h
@@ -42,9 +42,11 @@ NMSKeyfilePlugin *nms_keyfile_plugin_new (void);
gboolean nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self,
NMConnection *connection,
+ gboolean in_memory,
gboolean is_nm_generated,
gboolean is_volatile,
- gboolean in_memory,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
NMSettingsStorage **out_storage,
NMConnection **out_connection,
GError **error);
@@ -54,6 +56,8 @@ gboolean nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self,
NMConnection *connection,
gboolean is_nm_generated,
gboolean is_volatile,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
gboolean force_rename,
NMSettingsStorage **out_storage,
NMConnection **out_connection,
@@ -64,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-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index a6562a04dc..8d1f5599fe 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -164,6 +164,8 @@ nms_keyfile_reader_from_file (const char *full_filename,
struct stat *out_stat,
NMTernary *out_is_nm_generated,
NMTernary *out_is_volatile,
+ char **out_shadowed_storage,
+ NMTernary *out_shadowed_owned,
GError **error)
{
gs_unref_keyfile GKeyFile *key_file = NULL;
@@ -210,6 +212,16 @@ nms_keyfile_reader_from_file (const char *full_filename,
NM_KEYFILE_KEY_NMMETA_VOLATILE,
NM_TERNARY_DEFAULT));
+ NM_SET_OUT (out_shadowed_storage, g_key_file_get_string (key_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE,
+ NULL));
+
+ NM_SET_OUT (out_shadowed_owned, nm_key_file_get_boolean (key_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED,
+ NM_TERNARY_DEFAULT));
+
return connection;
}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h
index b17b6dd77b..f20e6d93ee 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h
@@ -37,6 +37,8 @@ NMConnection *nms_keyfile_reader_from_file (const char *full_filename,
struct stat *out_stat,
NMTernary *out_is_nm_generated,
NMTernary *out_is_volatile,
+ char **out_shadowed_storage,
+ NMTernary *out_shadowed_owned,
GError **error);
#endif /* __NMS_KEYFILE_READER_H__ */
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c
index d38e187d22..d68d60c8ec 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-storage.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c
@@ -41,24 +41,39 @@ nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst,
{
nm_assert (src != dst);
nm_assert (nm_streq (nms_keyfile_storage_get_uuid (dst), nms_keyfile_storage_get_uuid (src)));
- nm_assert (nms_keyfile_storage_get_filename (dst) && nm_streq (nms_keyfile_storage_get_filename (dst), nms_keyfile_storage_get_filename (src)));
-
- nm_g_object_ref_set (&dst->connection, src->connection);
- dst->storage_type = src->storage_type;
- dst->stat_mtime = src->stat_mtime;
- dst->is_nm_generated = src->is_nm_generated;
- dst->is_volatile = src->is_volatile;
- dst->is_tombstone = src->is_tombstone;
+ nm_assert ( nms_keyfile_storage_get_filename (dst)
+ && nm_streq (nms_keyfile_storage_get_filename (dst), nms_keyfile_storage_get_filename (src)));
+ nm_assert (dst->storage_type == src->storage_type);
+ nm_assert (dst->is_meta_data == src->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;
+ 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;
+
+ connection_to_free = g_steal_pointer (&dst->u.conn_data.connection);
+ shadowed_storage_to_free = g_steal_pointer (&dst->u.conn_data.shadowed_storage);
+ dst->u.conn_data = src->u.conn_data;
+ nm_g_object_ref (dst->u.conn_data.connection);
+ dst->u.conn_data.shadowed_storage = g_strdup (dst->u.conn_data.shadowed_storage);
+ }
}
NMConnection *
nms_keyfile_storage_steal_connection (NMSKeyfileStorage *self)
{
nm_assert (NMS_IS_KEYFILE_STORAGE (self));
- nm_assert ( (!self->connection && self->is_tombstone)
- || NM_IS_CONNECTION (self->connection));
+ nm_assert ( self->is_meta_data
+ || NM_IS_CONNECTION (self->u.conn_data.connection));
- return g_steal_pointer (&self->connection);
+ return self->is_meta_data
+ ? NULL
+ : g_steal_pointer (&self->u.conn_data.connection);
}
/*****************************************************************************/
@@ -75,16 +90,19 @@ cmp_fcn (const NMSKeyfileStorage *a,
* (inverse) priority. */
NM_CMP_FIELD_UNSAFE (b, a, storage_type);
- /* tombstones are more important. */
- nm_assert (a->is_tombstone == nm_settings_storage_is_keyfile_tombstone (NM_SETTINGS_STORAGE (a)));
- nm_assert (b->is_tombstone == nm_settings_storage_is_keyfile_tombstone (NM_SETTINGS_STORAGE (b)));
- NM_CMP_FIELD_UNSAFE (a, b, is_tombstone);
+ /* meta-data is more important. */
+ NM_CMP_FIELD_UNSAFE (a, b, is_meta_data);
- /* newer files are more important. */
- NM_CMP_FIELD (a, b, stat_mtime.tv_sec);
- NM_CMP_FIELD (a, b, stat_mtime.tv_nsec);
+ if (a->is_meta_data) {
+ nm_assert (nm_streq (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b)));
+ NM_CMP_FIELD_UNSAFE (a, b, u.meta_data.is_tombstone);
+ } else {
+ /* newer files are more important. */
+ NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_sec);
+ NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_nsec);
- NM_CMP_DIRECT_STRCMP (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b));
+ NM_CMP_DIRECT_STRCMP (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b));
+ }
return 0;
}
@@ -99,24 +117,35 @@ nms_keyfile_storage_init (NMSKeyfileStorage *self)
static NMSKeyfileStorage *
_storage_new (NMSKeyfilePlugin *plugin,
const char *uuid,
- const char *filename)
+ const char *filename,
+ gboolean is_meta_data,
+ NMSKeyfileStorageType storage_type)
+
{
+ NMSKeyfileStorage *self;
+
nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin));
nm_assert (nm_utils_is_uuid (uuid));
nm_assert (filename && filename[0] == '/');
- return g_object_new (NMS_TYPE_KEYFILE_STORAGE,
+ self = g_object_new (NMS_TYPE_KEYFILE_STORAGE,
NM_SETTINGS_STORAGE_PLUGIN, plugin,
NM_SETTINGS_STORAGE_UUID, uuid,
NM_SETTINGS_STORAGE_FILENAME, filename,
NULL);
+
+ *((bool *) &self->is_meta_data) = is_meta_data;
+ *((NMSKeyfileStorageType *) &self->storage_type) = storage_type;
+
+ return self;
}
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;
@@ -126,12 +155,10 @@ nms_keyfile_storage_new_tombstone (NMSKeyfilePlugin *plugin,
nm_assert (NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC,
NMS_KEYFILE_STORAGE_TYPE_RUN));
- self = _storage_new (plugin, uuid, filename);
-
- self->is_tombstone = TRUE;
-
- self->storage_type = storage_type;
-
+ 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;
}
@@ -142,6 +169,8 @@ nms_keyfile_storage_new_connection (NMSKeyfilePlugin *plugin,
NMSKeyfileStorageType storage_type,
NMTernary is_nm_generated_opt,
NMTernary is_volatile_opt,
+ const char *shadowed_storage,
+ NMTernary shadowed_owned_opt,
const struct timespec *stat_mtime)
{
NMSKeyfileStorage *self;
@@ -154,19 +183,21 @@ nms_keyfile_storage_new_connection (NMSKeyfilePlugin *plugin,
&& storage_type <= _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST);
nmtst_connection_assert_unchanging (connection_take);
- self = _storage_new (plugin, nm_connection_get_uuid (connection_take), filename);
+ self = _storage_new (plugin, nm_connection_get_uuid (connection_take), filename, FALSE, storage_type);
- self->connection = connection_take; /* take reference. */
+ self->u.conn_data.connection = connection_take; /* take reference. */
- if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) {
- self->is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE);
- self->is_volatile = (is_volatile_opt == NM_TERNARY_TRUE);
- }
+ self->u.conn_data.shadowed_storage = g_strdup (shadowed_storage);
if (stat_mtime)
- self->stat_mtime = *stat_mtime;
+ self->u.conn_data.stat_mtime = *stat_mtime;
- self->storage_type = storage_type;
+ if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) {
+ self->u.conn_data.is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE);
+ self->u.conn_data.is_volatile = (is_volatile_opt == NM_TERNARY_TRUE);
+ self->u.conn_data.shadowed_owned = shadowed_storage
+ && (shadowed_owned_opt == NM_TERNARY_TRUE);
+ }
return self;
}
@@ -176,7 +207,13 @@ _storage_clear (NMSKeyfileStorage *self)
{
c_list_unlink (&self->parent._storage_lst);
c_list_unlink (&self->parent._storage_by_uuid_lst);
- g_clear_object (&self->connection);
+ 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;
+ }
}
static void
@@ -226,12 +263,15 @@ nm_settings_storage_load_sett_flags (NMSettingsStorage *self,
return;
s = NMS_KEYFILE_STORAGE (self);
+
+ if (s->is_meta_data)
+ return;
if (s->storage_type != NMS_KEYFILE_STORAGE_TYPE_RUN)
return;
- if (s->is_nm_generated)
+ if (s->u.conn_data.is_nm_generated)
*sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED;
- if (s->is_volatile)
+ if (s->u.conn_data.is_volatile)
*sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE;
}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h
index 0cc537b7bf..2252b47b7e 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-storage.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h
@@ -34,6 +34,12 @@
#define NMS_KEYFILE_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass))
typedef struct {
+ /* whether this is a tombstone to hide a UUID (via symlink to /dev/null). */
+ char *shadowed_storage;
+ bool is_tombstone:1;
+} NMSettingsMetaData;
+
+typedef struct {
NMSettingsStorage parent;
/* The connection. Note that there are tombstones (loaded-uuid files to /dev/null)
@@ -42,35 +48,63 @@ typedef struct {
* Also, we don't actually remember the loaded connection after returning it
* to NMSettings. So, also for regular storages (non-tombstones) this field
* is often cleared. */
- NMConnection *connection;
-
- NMSKeyfileStorageType storage_type;
-
- /* the timestamp (stat's mtime) of the keyfile. For tombstones this
- * is irrelevant. The purpose is that if the same storage type (directory) has
- * multiple files with the same UUID, then the newer file gets preferred. */
- struct timespec stat_mtime;
-
- /* these flags are only relevant for storages with %NMS_KEYFILE_STORAGE_TYPE_RUN
- * (and non-tombstones). This is to persist and reload these settings flags to
- * /run. */
- bool is_nm_generated:1;
- bool is_volatile:1;
-
- /* whether this is a tombstone to hide a UUID (via the loaded uuid symlinks).
- * If this is falls, the storage contains a profile, though note that
- * the connection field will be cleared when it's not used. So, a non-tombstone
- * has a connection in principle, but the connection field may still be %NULL.
- *
- * Note that a tombstone instance doesn't have a connection, but NMSettings
- * considers it alive because is_tombstone is %TRUE. That means, once a tombstone
- * gets removed, this flag is cleared. Then the storage instance has no connnection
- * and is no longer a tombstone, and NMSettings considers it ready for deletion.
- */
- bool is_tombstone:1;
+ union {
+ struct {
+ NMConnection *connection;
+
+ /* when we move a profile from permanent storage to unsaved (/run), then
+ * we may leave the profile on disk (depending on options for Update2()).
+ *
+ * Later, when we save the profile again to disk, we want to re-use that filename.
+ * Likewise, we delete the (now in-memory) profile, we may want to also delete
+ * the original filename.
+ *
+ * This is the original filename, and we store it inside [.nmmeta] in the
+ * keyfile in /run. Note that we don't store this in the .nmmeta file, because
+ * the information is tied to the particular keyfile in /run, not to all UUIDs
+ * in general. */
+ char *shadowed_storage;
+
+ /* the timestamp (stat's mtime) of the keyfile. For meta-data this
+ * is irrelevant. The purpose is that if the same storage type (directory) has
+ * multiple files with the same UUID, then the newer file gets preferred. */
+ struct timespec stat_mtime;
+
+ /* these flags are only relevant for storages with %NMS_KEYFILE_STORAGE_TYPE_RUN
+ * (and non-metadata). This is to persist and reload these settings flags to
+ * /run.
+ *
+ * Note that these flags are not stored in as meta-data. The reason is that meta-data
+ * is per UUID. But these flags are only relevant for a particular keyfile on disk.
+ * That is, it must be tied to the actual keyfile, and not to the UUID. */
+ bool is_nm_generated:1;
+ bool is_volatile:1;
+
+ /* if shadowed_storage is set, then this flag indicates whether the file
+ * is owned. The difference comes into play when deleting the in-memory,
+ * shadowing profile: a owned profile will also be deleted. */
+ bool shadowed_owned:1;
+
+ } conn_data;
+
+ /* the content from the .nmmeta file. Note that the nmmeta file has the UUID
+ * in the filename, that means there can be only two variants of this file:
+ * in /etc and in /run. As such, this is really meta-data about the entire profile
+ * (the UUID), and not about the individual keyfile. */
+ NMSettingsMetaData meta_data;
+
+ } u;
+
+ /* The storage type. This is directly related to the filename. Since
+ * the filename cannot change, this value is unchanging. */
+ const NMSKeyfileStorageType storage_type;
+
+ /* whether union "u" has meta_data or conn_data. Since the type of the storage
+ * depends on the (immutable) filename, this is also const. */
+ const bool is_meta_data;
/* this flag is only used during reload to mark and prune old entries. */
- bool dirty:1;
+ bool is_dirty:1;
} NMSKeyfileStorage;
@@ -83,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 */,
@@ -91,6 +126,8 @@ NMSKeyfileStorage *nms_keyfile_storage_new_connection (struct _NMSKeyfilePlugin
NMSKeyfileStorageType storage_type,
NMTernary is_nm_generated_opt,
NMTernary is_volatile_opt,
+ const char *shadowed_storage,
+ NMTernary shadowed_owned_opt,
const struct timespec *stat_mtime);
void nms_keyfile_storage_destroy (NMSKeyfileStorage *storage);
@@ -132,18 +169,88 @@ nm_settings_storage_is_keyfile_lib (const NMSettingsStorage *self)
&& (((NMSKeyfileStorage *) self)->storage_type >= NMS_KEYFILE_STORAGE_TYPE_LIB_BASE);
}
-static inline gboolean
-nm_settings_storage_is_keyfile_tombstone (const NMSettingsStorage *self)
+static inline const NMSettingsMetaData *
+nm_settings_storage_is_meta_data (const NMSettingsStorage *storage)
{
- /* Only keyfile storage supports tombstones. They indicate that a uuid
- * is shadowed via a symlink to /dev/null.
+ const NMSKeyfileStorage *self;
+
+ if (!NMS_IS_KEYFILE_STORAGE (storage))
+ return NULL;
+
+ self = (NMSKeyfileStorage *) storage;
+
+ if (!self->is_meta_data)
+ return NULL;
+
+ return &self->u.meta_data;
+}
+
+static inline const NMSettingsMetaData *
+nm_settings_storage_is_meta_data_alive (const NMSettingsStorage *storage)
+{
+ const NMSettingsMetaData *meta_data;
+
+ meta_data = nm_settings_storage_is_meta_data (storage);
+
+ if (!meta_data)
+ return NULL;
+
+ /* Regular (all other) storages are alive as long as they report a NMConnection, and
+ * they will be dropped, once they have no more connection.
*
- * Note that tombstones don't have a NMConnection instead they shadow
- * a UUID. As such, NMSettings considers them alive also if they have
- * not profile. That means, when a tombstone gets removed for good,
- * the is_tombstone must be cleared (so that it becomes truly dead). */
- return NMS_IS_KEYFILE_STORAGE (self)
- && ((NMSKeyfileStorage *) self)->is_tombstone;
+ * Meta-data storages are special: they never report a NMConnection.
+ * So, a meta-data storage is alive as long as it is tracked by the
+ * settings plugin.
+ *
+ * This function is used to ckeck for that. */
+
+ if (c_list_is_empty (&storage->_storage_lst))
+ return NULL;
+
+ return meta_data;
+}
+
+static inline const char *
+nm_settings_storage_get_shadowed_storage (const NMSettingsStorage *storage,
+ gboolean *out_shadowed_owned)
+{
+ if (NMS_IS_KEYFILE_STORAGE (storage)) {
+ const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage;
+
+ if (self->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) {
+ if (!self->is_meta_data) {
+ if (self->u.conn_data.shadowed_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;
+ }
+ }
+ }
+
+ NM_SET_OUT (out_shadowed_owned, FALSE);
+ return NULL;
+}
+
+static inline const char *
+nm_settings_storage_get_filename_for_shadowed_storage (const NMSettingsStorage *storage)
+{
+ g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), NULL);
+
+ if (!storage->_filename)
+ return NULL;
+
+ if (NMS_IS_KEYFILE_STORAGE (storage)) {
+ const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage;
+
+ if ( self->is_meta_data
+ || self->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC)
+ return NULL;
+ }
+
+ return storage->_filename;
}
/*****************************************************************************/
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c
index 0d03132793..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, '/'));
@@ -122,19 +134,45 @@ nms_keyfile_nmmeta_read (const char *dirname,
full_filename = g_build_filename (dirname, filename, NULL);
- if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMLOADED,
+ 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));
@@ -242,10 +306,11 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype,
"file is not a regular file");
return FALSE;
}
- } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) {
- if (!S_ISLNK (st->st_mode)) {
+ } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) {
+ 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",
@@ -289,7 +354,7 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype,
"cannot access file: %s", nm_strerror_native (errsv));
return FALSE;
}
- } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) {
+ } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) {
if (lstat (filename, &st) != 0) {
errsv = errno;
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h
index 1f5280f727..723c443625 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h
@@ -24,7 +24,7 @@
typedef enum {
NMS_KEYFILE_FILETYPE_KEYFILE,
- NMS_KEYFILE_FILETYPE_NMLOADED,
+ NMS_KEYFILE_FILETYPE_NMMETA,
} NMSKeyfileFiletype;
typedef enum {
@@ -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/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index 54e62b7373..5fbbb7a1c8 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -170,6 +170,8 @@ static gboolean
_internal_write_connection (NMConnection *connection,
gboolean is_nm_generated,
gboolean is_volatile,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
const char *keyfile_dir,
const char *profile_dir,
gboolean with_extension,
@@ -201,6 +203,8 @@ _internal_write_connection (NMConnection *connection,
nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
+ nm_assert (!shadowed_owned || shadowed_storage);
+
rename = force_rename
|| existing_path_read_only
|| ( existing_path
@@ -229,6 +233,20 @@ _internal_write_connection (NMConnection *connection,
TRUE);
}
+ if (shadowed_storage) {
+ g_key_file_set_string (kf_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE,
+ shadowed_storage);
+ }
+
+ if (shadowed_owned) {
+ g_key_file_set_boolean (kf_file,
+ NM_KEYFILE_GROUP_NMMETA,
+ NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED,
+ TRUE);
+ }
+
kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error);
if (!kf_content_buf)
return FALSE;
@@ -356,6 +374,8 @@ gboolean
nms_keyfile_writer_connection (NMConnection *connection,
gboolean is_nm_generated,
gboolean is_volatile,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
const char *keyfile_dir,
const char *profile_dir,
const char *existing_path,
@@ -371,6 +391,8 @@ nms_keyfile_writer_connection (NMConnection *connection,
return _internal_write_connection (connection,
is_nm_generated,
is_volatile,
+ shadowed_storage,
+ shadowed_owned,
keyfile_dir,
profile_dir,
TRUE,
@@ -400,6 +422,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection,
return _internal_write_connection (connection,
FALSE,
FALSE,
+ NULL,
+ FALSE,
keyfile_dir,
keyfile_dir,
FALSE,
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h
index 4fb9a20638..99e86025ae 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h
@@ -29,6 +29,8 @@ typedef gboolean (*NMSKeyfileWriterAllowFilenameCb) (const char *check_filename,
gboolean nms_keyfile_writer_connection (NMConnection *connection,
gboolean is_nm_generated,
gboolean is_volatile,
+ const char *shadowed_storage,
+ gboolean shadowed_owned,
const char *keyfile_dir,
const char *profile_dir,
const char *existing_path,
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
index 5942f04bb1..f96111a2e4 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -75,6 +75,8 @@ check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int
NULL, \
NULL, \
NULL, \
+ NULL, \
+ NULL, \
(nmtst_get_rand_uint32 () % 2) ? &_error : NULL); \
nmtst_assert_success (_connection, _error); \
nmtst_assert_connection_verifies_without_normalization (_connection); \
@@ -2541,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);
@@ -2553,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);
@@ -2564,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);