settings: merge branch 'th/settings-shadowed-storage'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/211
This commit is contained in:
Thomas Haller 2019-07-26 09:03:02 +02:00
commit 7bd16e85e3
23 changed files with 1378 additions and 453 deletions

View file

@ -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 \

155
examples/python/gi/nm-update2.py Executable file
View file

@ -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()

View file

@ -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

View file

@ -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"

View file

@ -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,

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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,
/* persist to /run (in-memory). If the profile is currently on disk,
* delete it from disk. */
NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
/* 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,
/* persist to /run (in-memory). If the profile is currently on disk,
* forget about it, but don't delete it from disk. */
/* 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,
/* 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. */
/* 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,
/* 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;

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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:

View file

@ -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);

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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_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);
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;
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;
}

View file

@ -33,6 +33,12 @@
#define NMS_IS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_STORAGE))
#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;
@ -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;
union {
struct {
NMConnection *connection;
NMSKeyfileStorageType storage_type;
/* 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 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;
/* 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-tombstones). This is to persist and reload these settings flags to
* /run. */
bool is_nm_generated:1;
bool is_volatile:1;
/* 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;
/* 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;
/* 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;
}
/*****************************************************************************/

View file

@ -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,

View file

@ -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);
/*****************************************************************************/

View file

@ -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,

View file

@ -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,

View file

@ -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);