Skip to content

Commit

Permalink
add custom_persist extension
Browse files Browse the repository at this point in the history
  • Loading branch information
Guiiix committed Feb 2, 2025
1 parent c1cdf38 commit 2d64eae
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/qubes-ext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Extensions defined here
.. autoclass:: qubes.ext.admin.AdminExtension
.. autoclass:: qubes.ext.block.BlockDeviceExtension
.. autoclass:: qubes.ext.core_features.CoreFeatures
.. autoclass:: qubes.ext.custom_persist.CustomPersist
.. autoclass:: qubes.ext.gui.GUI
.. autoclass:: qubes.ext.pci.PCIDeviceExtension
.. autoclass:: qubes.ext.r3compatibility.R3Compatibility
Expand Down
102 changes: 102 additions & 0 deletions qubes/ext/custom_persist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# -*- encoding: utf-8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2024 Guillaume Chinal <[email protected]>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, see <https://www.gnu.org/licenses/>.

import qubes.ext
import qubes.config

FEATURE_PREFIX = "custom-persist."
QDB_PREFIX = "/persist/"
QDB_KEY_LIMIT = 63


class CustomPersist(qubes.ext.Extension):
"""This extension allows to create minimal-state APP with by configuring an
exhaustive list of bind dirs(and files)
"""

@staticmethod
def _extract_key_from_feature(feature) -> str:
return feature[len(FEATURE_PREFIX) :]

Check warning on line 35 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L35

Added line #L35 was not covered by tests

@staticmethod
def _is_expected_feature(feature) -> bool:
return feature.startswith(FEATURE_PREFIX)

@staticmethod
def _is_valid_key(key, vm) -> bool:
if not key:
vm.log.warning(

Check warning on line 44 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L43-L44

Added lines #L43 - L44 were not covered by tests
"Got empty custom-persist key, ignoring: {}".format(key)
)
return False

Check warning on line 47 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L47

Added line #L47 was not covered by tests

# QubesDB key length limit
key_maxlen = QDB_KEY_LIMIT - len(QDB_PREFIX)
if len(key) >= key_maxlen:
vm.log.warning(

Check warning on line 52 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L50-L52

Added lines #L50 - L52 were not covered by tests
"custom-persist key is too long (max {}), ignoring: "
"{}".format(key_maxlen, key)
)
return False
return True

Check warning on line 57 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L56-L57

Added lines #L56 - L57 were not covered by tests

def _write_db_value(self, feature, value, vm):
vm.untrusted_qdb.write(

Check warning on line 60 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L60

Added line #L60 was not covered by tests
"{}{}".format(QDB_PREFIX, self._extract_key_from_feature(feature)),
str(value),
)

@qubes.ext.handler("domain-qdb-create")
def on_domain_qdb_create(self, vm, event):
"""Actually export features"""
# pylint: disable=unused-argument
for feature, value in vm.features.items():
if self._is_expected_feature(feature) and self._is_valid_key(
self._extract_key_from_feature(feature), vm
):
self._write_db_value(feature, value, vm)

Check warning on line 73 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L73

Added line #L73 was not covered by tests

@qubes.ext.handler("domain-feature-set:*")
def on_domain_feature_set(self, vm, event, feature, value, oldvalue=None):
"""Inject persist keys in QubesDB in runtime"""
# pylint: disable=unused-argument

if not self._is_expected_feature(feature):
return

if not self._is_valid_key(self._extract_key_from_feature(feature), vm):
return

Check warning on line 84 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L83-L84

Added lines #L83 - L84 were not covered by tests

if not vm.is_running():
return

Check warning on line 87 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L86-L87

Added lines #L86 - L87 were not covered by tests

self._write_db_value(feature, value, vm)

Check warning on line 89 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L89

Added line #L89 was not covered by tests

@qubes.ext.handler("domain-feature-delete:*")
def on_domain_feature_delete(self, vm, event, feature):
"""Update /vm-config/ QubesDB tree in runtime"""
# pylint: disable=unused-argument
if not vm.is_running():
return
if not feature.startswith(FEATURE_PREFIX):
return

Check warning on line 98 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L97-L98

Added lines #L97 - L98 were not covered by tests

vm.untrusted_qdb.rm(

Check warning on line 100 in qubes/ext/custom_persist.py

View check run for this annotation

Codecov / codecov/patch

qubes/ext/custom_persist.py#L100

Added line #L100 was not covered by tests
"{}{}".format(QDB_PREFIX, self._extract_key_from_feature(feature))
)
1 change: 1 addition & 0 deletions rpm_spec/core-dom0.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ done
%{python3_sitelib}/qubes/ext/backup_restore.py
%{python3_sitelib}/qubes/ext/block.py
%{python3_sitelib}/qubes/ext/core_features.py
%{python3_sitelib}/qubes/ext/custom_persist.py
%{python3_sitelib}/qubes/ext/gui.py
%{python3_sitelib}/qubes/ext/audio.py
%{python3_sitelib}/qubes/ext/pci.py
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def run(self):
'qubes.ext.backup_restore = '
'qubes.ext.backup_restore:BackupRestoreExtension',
'qubes.ext.core_features = qubes.ext.core_features:CoreFeatures',
'qubes.ext.custom_persist = qubes.ext.custom_persist:CustomPersist',
'qubes.ext.gui = qubes.ext.gui:GUI',
'qubes.ext.audio = qubes.ext.audio:AUDIO',
'qubes.ext.r3compatibility = qubes.ext.r3compatibility:R3Compatibility',
Expand Down

0 comments on commit 2d64eae

Please sign in to comment.