[merge] with upstream

This commit is contained in:
Philip Mueller 2023-05-26 10:46:16 +02:00
commit 448ebd639c
66 changed files with 1511 additions and 1621 deletions

View File

@ -2,23 +2,23 @@
# SPDX-License-Identifier: CC0-1.0
[main]
host = https://www.transifex.com
host = https://app.transifex.com
[calamares.calamares]
[o:calamares:p:calamares:r:calamares]
file_filter = lang/calamares_<lang>.ts
source_file = lang/calamares_en.ts
source_lang = en
type = QT
type = QT
[calamares.fdo]
[o:calamares:p:calamares:r:fdo]
file_filter = lang/desktop_<lang>.desktop
source_file = calamares.desktop
source_lang = en
type = DESKTOP
type = DESKTOP
[calamares.python]
[o:calamares:p:calamares:r:python]
file_filter = lang/python/<lang>/LC_MESSAGES/python.po
source_file = lang/python.pot
source_lang = en
type = PO
type = PO

View File

@ -12,20 +12,42 @@ the history of the 3.2 series (2018-05 - 2022-08).
This release contains contributions from (alphabetically by first name):
- Adriaan de Groot
- Aleksey Samoilov
- Anke Boersma
- Emir Sari
- Evan James
- Jeremy Attall
- Johannes Kamprad
- Mario Haustein
- Masato TOYOSHIMA
- Paolo Dongilli
- Peter Jung
- Shivanand
- wiz64
## Core ##
- Incompatible module-configuration changes, see #1438.
- Branding entries use ${var} instead of @{var} for substitutions,
in line with all the other substitution mechanisms used from C++
core. See documentation in `branding.desc`.
- Boost::Python requires at least version 1.72
- KDE Frameworks must be version 5.58 or later
- Boost::Python requires at least version 1.72.
- KDE Frameworks must be version 5.58 or later.
- The `INSTALL_CONFIG` option has been removed. If you are installing
the example configuration files from the Calamares repository, just
stop. That was never a good idea, and you should keep your configs elsewhere.
- Progress percentage during install can now be localized. (thanks Emir)
## Modules ##
- *dracut* added a configurable kernel name. (thanks Anke)
- *initcpiocfg* orders hookds slightly differently. (thanks Peter)
- *localeq* moved to using Drawer instead of ComboBox in UI. (thanks Anke)
- *keyboardq* moved to using Drawer instead of ComboBox in UI. (thanks Anke)
- *netinstall* now has a new *noncheckable* option for groups, which prevent
it a group from being checked/uncheckd as a whole. You can still check
individual items **in** the group though. (thanks Shivanand)
- *partition* can now pick LUKS or LUKS2. (thanks Jeremy)
- *zfs* creates a hostid through zgenhostid.
- *zfshostid* new module to copy zfs generated /etc/hostid
# 3.3.0-alpha2 (2022-08-23)

View File

@ -73,7 +73,6 @@ set(CALAMARES_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
### OPTIONS
#
option(INSTALL_CONFIG "Install configuration files" OFF)
option(INSTALL_POLKIT "Install Polkit configuration" ON)
option(INSTALL_COMPLETION "Install shell completions" OFF)
# When adding WITH_* that affects the ABI offered by libcalamares,
@ -136,7 +135,7 @@ set(CALAMARES_DESCRIPTION_SUMMARY "The distribution-independent installer framew
#
# Language en (source language) is added later. It isn't listed in
# Transifex either. Get the list of languages and their status
# from https://transifex.com/calamares/calamares/ , or (preferably)
# from https://app.transifex.com/calamares/calamares/ , or (preferably)
# use ci/txstats.py to automatically check.
#
# When adding a new language, take care that it is properly loaded
@ -214,10 +213,10 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_C_FLAGS_DEBUG "-Og -g")
set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "-O4 -DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined -Wl,--fatal-warnings")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined -Wl,--fatal-warnings ${CMAKE_SHARED_LINKER_FLAGS}")
# If no build type is set, pick a reasonable one
if(NOT CMAKE_BUILD_TYPE)
@ -542,7 +541,6 @@ add_subdirectory(src)
add_feature_info(Python ${WITH_PYTHON} "Python job modules")
add_feature_info(Qml ${WITH_QML} "QML UI support")
add_feature_info(Config ${INSTALL_CONFIG} "Install Calamares configuration")
add_feature_info(Polkit ${INSTALL_POLKIT} "Install Polkit files")
add_feature_info(KCrash ${BUILD_KF5Crash} "Crash dumps via KCrash")
@ -587,10 +585,6 @@ install(
### Miscellaneous installs
#
#
if(INSTALL_CONFIG)
install(FILES settings.conf DESTINATION share/calamares)
endif()
if(INSTALL_POLKIT)
install(FILES com.github.calamares.calamares.policy DESTINATION "${POLKITQT-1_POLICY_FILES_INSTALL_DIR}")
endif()
@ -607,7 +601,9 @@ install(FILES calamares.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/application
install(FILES man/calamares.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/)
# uninstall target
### Uninstall
#
#
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
@ -617,6 +613,17 @@ configure_file(
add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
### Developer convenience
#
# The module support files -- .desc files, .conf files -- are copied into the build
# directory so that it is possible to run `calamares -d` from there. Copy the
# top-level settings.conf as well, into the build directory.
if( settings.conf IS_NEWER_THAN ${CMAKE_BINARY_DIR}/settings.conf )
configure_file(settings.conf ${CMAKE_BINARY_DIR}/settings.conf COPYONLY)
endif()
### CMAKE SUMMARY REPORT
#
get_directory_property(SKIPPED_MODULES DIRECTORY src/modules DEFINITION LIST_SKIPPED_MODULES)

View File

@ -153,12 +153,10 @@ function( _calamares_add_module_subdirectory_impl )
get_filename_component( FLEXT ${MODULE_FILE} EXT )
if( "${FLEXT}" STREQUAL ".conf" )
if( INSTALL_CONFIG )
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE}
DESTINATION ${MODULE_DATA_DESTINATION} )
endif()
message(STATUS "Config ${MODULE_FILE}")
list( APPEND MODULE_CONFIG_FILES ${MODULE_FILE} )
else()
message(STATUS "Non-Config ${MODULE_FILE}")
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE}
DESTINATION ${MODULE_DESTINATION} )
endif()
@ -169,12 +167,7 @@ function( _calamares_add_module_subdirectory_impl )
message( " ${Green}TYPE:${ColorReset} jobmodule" )
message( " ${Green}MODULE_DESTINATION:${ColorReset} ${MODULE_DESTINATION}" )
if( MODULE_CONFIG_FILES )
if ( INSTALL_CONFIG )
set( _destination "${MODULE_DATA_DESTINATION}" )
else()
set( _destination "[Build directory only]" )
endif()
message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => ${_destination}" )
message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory only]" )
endif()
message( "" )
# We copy over the lang directory, if any

View File

@ -104,10 +104,7 @@ function( calamares_add_plugin )
message( FATAL_ERROR "${Red}NO_CONFIG${ColorReset} is set, with configuration ${Red}${PLUGIN_CONFIG_FILES}${ColorReset}" )
endif()
set( _destination "(unknown)" )
if ( INSTALL_CONFIG AND NOT PLUGIN_NO_INSTALL )
set( _destination "${PLUGIN_DATA_DESTINATION}" )
elseif( NOT PLUGIN_NO_INSTALL )
# Not INSTALL_CONFIG
if( NOT PLUGIN_NO_INSTALL )
set( _destination "[Build directory only]" )
else()
set( _destination "[Skipping installation]" )
@ -210,17 +207,12 @@ function( calamares_add_plugin )
set( _warned_config OFF )
foreach( PLUGIN_CONFIG_FILE ${PLUGIN_CONFIG_FILES} )
if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} OR INSTALL_CONFIG )
if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} )
configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY )
else()
message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" )
set( _warned_config ON )
endif()
if ( INSTALL_CONFIG )
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE}
DESTINATION ${PLUGIN_DATA_DESTINATION} )
endif()
endforeach()
if ( _warned_config )
message( "" )

View File

@ -7,11 +7,11 @@
[![Current issue](https://img.shields.io/badge/issue-in_progress-FE9B48)](https://github.com/calamares/calamares/labels/hacking%3A%20in-progress)
[![GitHub release](https://img.shields.io/github/release/calamares/calamares.svg)](https://github.com/calamares/calamares/releases)
[![GitHub Build Status](https://img.shields.io/github/workflow/status/calamares/calamares/ci?label=GH%20build)](https://github.com/calamares/calamares/actions?query=workflow%3Aci)
[![GitHub license](https://img.shields.io/github/license/calamares/calamares.svg)](https://github.com/calamares/calamares/blob/calamares/LICENSES/GPL-3.0-or-later.txt)
[![GitHub Build Status](https://img.shields.io/github/actions/workflow/status/calamares/calamares/push.yml)](https://github.com/calamares/calamares/actions?query=workflow%3Aci)
[![GitHub license](https://img.shields.io/badge/license-Multiple-green)](https://github.com/calamares/calamares/tree/calamares/LICENSES)
| [Report a Bug](https://github.com/calamares/calamares/issues/new) | [Translate](https://www.transifex.com/projects/p/calamares/) | [Contribute](CONTRIBUTING.md) | [Matrix: #calamares:kde.org](https://webchat.kde.org/#/room/%23calamares:kde.org) | [IRC: Libera.Chat #calamares](https://kiwiirc.com/client/irc.libera.chat/#calamares) | [Wiki](https://github.com/calamares/calamares/wiki) |
| [Report a Bug](https://github.com/calamares/calamares/issues/new) | [Translate](https://app.transifex.com/calamares/calamares/) | [Contribute](CONTRIBUTING.md) | [Matrix: #calamares:kde.org](https://webchat.kde.org/#/room/%23calamares:kde.org) | [IRC: Libera.Chat #calamares](https://kiwiirc.com/client/irc.libera.chat/#calamares) | [Wiki](https://github.com/calamares/calamares/wiki) |
|:--:|:--:|:--:|:--:|:--:|:--:|

View File

@ -55,7 +55,7 @@ done
# .tx/config file to locate files, and overwrites them in the
# filesystem with new (merged) translations.
export QT_SELECT=5
tx pull --force --source --all
transifex-client pull --force --all || exit 1
### CLEANUP TRANSLATIONS

View File

@ -53,6 +53,10 @@ if test "x$1" = "x--no-tx" ; then
}
else
# tx is the regular transifex command
tx() {
transifex-client "$@"
}
# txtag is used to tag in git to measure changes
txtag() {
git tag -f translation
@ -82,6 +86,15 @@ do
done
# XMLLINT is optional
if sed --version 2>&1 | grep -q GNU ; then
reinplace() {
sed -i'' "$@"
}
else
reinplace() {
sed -i '' "$@"
}
fi
### CREATE TRANSLATIONS
#
@ -108,8 +121,8 @@ if test -n "$XMLLINT" ; then
$XMLLINT --c14n11 "$TS_FILE" | { echo "<!DOCTYPE TS>" ; cat - ; } | $XMLLINT --format --encode utf-8 -o "$TS_FILE".new - && mv "$TS_FILE".new "$TS_FILE"
fi
tx push --source --no-interactive -r calamares.calamares
tx push --source --no-interactive -r calamares.fdo
tx push --source -r calamares.calamares || exit 1
tx push --source -r calamares.fdo || exit 1
### PYTHON MODULES
@ -132,9 +145,8 @@ for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d | sort) ; d
${PYGETTEXT} -p ${MODULE_DIR}/lang -d ${MODULE_NAME} -o ${MODULE_NAME}.pot ${MODULE_DIR}/*.py
POTFILE="${MODULE_DIR}/lang/${MODULE_NAME}.pot"
if [ -f "$POTFILE" ]; then
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
tx set -r calamares.${MODULE_NAME} --source -l en "$POTFILE"
tx push --source --no-interactive -r calamares.${MODULE_NAME}
reinplace '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
tx push --source -r calamares.${MODULE_NAME}
fi
else
SHARED_PYTHON="$SHARED_PYTHON $FILES"
@ -145,9 +157,8 @@ done
if test -n "$SHARED_PYTHON" ; then
${PYGETTEXT} -p lang -d python -o python.pot $SHARED_PYTHON
POTFILE="lang/python.pot"
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
tx set -r calamares.python --source -l en "$POTFILE"
tx push --source --no-interactive -r calamares.python
reinplace '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
tx push --source -r calamares.python
fi
txtag

View File

@ -53,7 +53,7 @@ class TransifexGetter(object):
parser = configparser.ConfigParser()
parser.read_file(f)
return parser.get("https://www.transifex.com", "password")
return parser.get("https://app.transifex.com", "password")
except IOError as e:
return None

File diff suppressed because it is too large Load Diff

View File

@ -2,426 +2,393 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-05-29 16:17+0200\n"
"POT-Creation-Date: 2023-04-24 23:48+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: src/modules/grubcfg/main.py:28
msgid "Configure GRUB."
msgstr "Configure GRUB."
#: src/modules/mount/main.py:42
msgid "Mounting partitions."
msgstr "Mounting partitions."
#: src/modules/mount/main.py:88 src/modules/mount/main.py:124
msgid "Internal error mounting zfs datasets"
msgstr "Internal error mounting zfs datasets"
#: src/modules/mount/main.py:100
msgid "Failed to import zpool"
msgstr "Failed to import zpool"
#: src/modules/mount/main.py:116
msgid "Failed to unlock zpool"
msgstr "Failed to unlock zpool"
#: src/modules/mount/main.py:133 src/modules/mount/main.py:138
msgid "Failed to set zfs mountpoint"
msgstr "Failed to set zfs mountpoint"
#: src/modules/mount/main.py:229 src/modules/initcpiocfg/main.py:235
#: src/modules/initcpiocfg/main.py:239 src/modules/rawfs/main.py:164
#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89
#: src/modules/openrcdmcryptcfg/main.py:72
#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/fstab/main.py:394
#: src/modules/fstab/main.py:400 src/modules/fstab/main.py:428
#: src/modules/localecfg/main.py:140 src/modules/networkcfg/main.py:105
msgid "Configuration Error"
msgstr "Configuration Error"
#: src/modules/mount/main.py:230 src/modules/initcpiocfg/main.py:236
#: src/modules/rawfs/main.py:165 src/modules/initramfscfg/main.py:86
#: src/modules/openrcdmcryptcfg/main.py:73 src/modules/fstab/main.py:395
msgid "No partitions are defined for <pre>{!s}</pre> to use."
msgstr "No partitions are defined for <pre>{!s}</pre> to use."
#: src/modules/mount/main.py:253
msgid "zfs mounting error"
msgstr "zfs mounting error"
#: src/modules/services-systemd/main.py:26
msgid "Configure systemd services"
msgstr "Configure systemd services"
#: src/modules/services-systemd/main.py:59
#: src/modules/services-openrc/main.py:93
msgid "Cannot modify service"
msgstr "Cannot modify service"
#: src/modules/services-systemd/main.py:60
msgid ""
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
#: src/modules/bootloader/main.py:46
msgid "Install bootloader."
msgstr ""
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
#: src/modules/services-systemd/main.py:63
#: src/modules/services-systemd/main.py:69
msgid "Cannot enable systemd service <code>{name!s}</code>."
msgstr "Cannot enable systemd service <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:65
msgid "Cannot enable systemd target <code>{name!s}</code>."
msgstr "Cannot enable systemd target <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:67
msgid "Cannot enable systemd timer <code>{name!s}</code>."
msgstr "Cannot enable systemd timer <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:71
msgid "Cannot disable systemd target <code>{name!s}</code>."
msgstr "Cannot disable systemd target <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:73
msgid "Cannot mask systemd unit <code>{name!s}</code>."
msgstr "Cannot mask systemd unit <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:75
msgid ""
"Unknown systemd commands <code>{command!s}</code> and "
"<code>{suffix!s}</code> for unit {name!s}."
#: src/modules/bootloader/main.py:640
msgid "Failed to install grub, no partitions defined in global storage"
msgstr ""
"Unknown systemd commands <code>{command!s}</code> and "
"<code>{suffix!s}</code> for unit {name!s}."
#: src/modules/unpackfs/main.py:34
msgid "Filling up filesystems."
msgstr "Filling up filesystems."
#: src/modules/unpackfs/main.py:254
msgid "rsync failed with error code {}."
msgstr "rsync failed with error code {}."
#: src/modules/unpackfs/main.py:299
msgid "Unpacking image {}/{}, file {}/{}"
msgstr "Unpacking image {}/{}, file {}/{}"
#: src/modules/unpackfs/main.py:314
msgid "Starting to unpack {}"
msgstr "Starting to unpack {}"
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:467
msgid "Failed to unpack image \"{}\""
msgstr "Failed to unpack image \"{}\""
#: src/modules/unpackfs/main.py:430
msgid "No mount point for root partition"
msgstr "No mount point for root partition"
#: src/modules/unpackfs/main.py:431
msgid "globalstorage does not contain a \"rootMountPoint\" key."
msgstr "globalstorage does not contain a \"rootMountPoint\" key."
#: src/modules/unpackfs/main.py:434
msgid "Bad mount point for root partition"
msgstr "Bad mount point for root partition"
#: src/modules/unpackfs/main.py:435
msgid "rootMountPoint is \"{}\", which does not exist."
msgstr "rootMountPoint is \"{}\", which does not exist."
#: src/modules/unpackfs/main.py:439 src/modules/unpackfs/main.py:455
#: src/modules/unpackfs/main.py:459 src/modules/unpackfs/main.py:465
#: src/modules/unpackfs/main.py:480
msgid "Bad unpackfs configuration"
msgstr "Bad unpackfs configuration"
#: src/modules/unpackfs/main.py:440
msgid "There is no configuration information."
msgstr "There is no configuration information."
#: src/modules/unpackfs/main.py:456
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
msgstr "The filesystem for \"{}\" ({}) is not supported by your current kernel"
#: src/modules/unpackfs/main.py:460
msgid "The source filesystem \"{}\" does not exist"
msgstr "The source filesystem \"{}\" does not exist"
#: src/modules/unpackfs/main.py:466
msgid ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
#: src/modules/bootloader/main.py:895
msgid "Bootloader installation error"
msgstr ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
#: src/modules/unpackfs/main.py:481
msgid "The destination \"{}\" in the target system is not a directory"
msgstr "The destination \"{}\" in the target system is not a directory"
#: src/modules/bootloader/main.py:896
msgid ""
"The bootloader could not be installed. The installation command <pre>{!s}</"
"pre> returned error code {!s}."
msgstr ""
#: src/modules/displaymanager/main.py:524
msgid "Cannot write KDM configuration file"
msgstr "Cannot write KDM configuration file"
#: src/modules/displaymanager/main.py:525
msgid "KDM config file {!s} does not exist"
msgstr "KDM config file {!s} does not exist"
#: src/modules/displaymanager/main.py:586
#: src/modules/displaymanager/main.py:507
msgid "Cannot write LXDM configuration file"
msgstr "Cannot write LXDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:587
#: src/modules/displaymanager/main.py:508
msgid "LXDM config file {!s} does not exist"
msgstr "LXDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:670
#: src/modules/displaymanager/main.py:596
msgid "Cannot write LightDM configuration file"
msgstr "Cannot write LightDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:671
#: src/modules/displaymanager/main.py:597
msgid "LightDM config file {!s} does not exist"
msgstr "LightDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:745
#: src/modules/displaymanager/main.py:682
msgid "Cannot configure LightDM"
msgstr "Cannot configure LightDM"
msgstr ""
#: src/modules/displaymanager/main.py:746
#: src/modules/displaymanager/main.py:683
msgid "No LightDM greeter installed."
msgstr "No LightDM greeter installed."
msgstr ""
#: src/modules/displaymanager/main.py:777
#: src/modules/displaymanager/main.py:714
msgid "Cannot write SLIM configuration file"
msgstr "Cannot write SLIM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:778
#: src/modules/displaymanager/main.py:715
msgid "SLIM config file {!s} does not exist"
msgstr "SLIM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:992
#: src/modules/displaymanager/main.py:933
msgid "No display managers selected for the displaymanager module."
msgstr "No display managers selected for the displaymanager module."
msgstr ""
#: src/modules/displaymanager/main.py:993
#: src/modules/displaymanager/main.py:934
msgid ""
"The displaymanagers list is empty or undefined in both globalstorage and "
"displaymanager.conf."
msgstr ""
"The displaymanagers list is empty or undefined in both globalstorage and "
"displaymanager.conf."
#: src/modules/displaymanager/main.py:1075
#: src/modules/displaymanager/main.py:1021
msgid "Display manager configuration was incomplete"
msgstr "Display manager configuration was incomplete"
msgstr ""
#: src/modules/initcpiocfg/main.py:28
msgid "Configuring mkinitcpio."
msgstr "Configuring mkinitcpio."
#: src/modules/dracut/main.py:29
msgid "Creating initramfs with dracut."
msgstr ""
#: src/modules/initcpiocfg/main.py:240 src/modules/initramfscfg/main.py:90
#: src/modules/openrcdmcryptcfg/main.py:77 src/modules/fstab/main.py:401
#: src/modules/dracut/main.py:63
msgid "Failed to run dracut"
msgstr ""
#: src/modules/dracut/main.py:64
#, python-brace-format
msgid "Dracut failed to run on the target with return code: {return_code}"
msgstr ""
#: src/modules/dummypython/main.py:35
msgid "Dummy python job."
msgstr ""
#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
#: src/modules/dummypython/main.py:94
msgid "Dummy python step {}"
msgstr ""
#: src/modules/fstab/main.py:28
msgid "Writing fstab."
msgstr ""
#: src/modules/fstab/main.py:377 src/modules/fstab/main.py:383
#: src/modules/fstab/main.py:411 src/modules/initcpiocfg/main.py:245
#: src/modules/initcpiocfg/main.py:249 src/modules/initramfscfg/main.py:85
#: src/modules/initramfscfg/main.py:89 src/modules/localecfg/main.py:140
#: src/modules/mount/main.py:329 src/modules/networkcfg/main.py:105
#: src/modules/openrcdmcryptcfg/main.py:72
#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/rawfs/main.py:164
msgid "Configuration Error"
msgstr ""
#: src/modules/fstab/main.py:378 src/modules/initramfscfg/main.py:86
#: src/modules/mount/main.py:330 src/modules/openrcdmcryptcfg/main.py:73
#: src/modules/rawfs/main.py:165
msgid "No partitions are defined for <pre>{!s}</pre> to use."
msgstr ""
#: src/modules/fstab/main.py:384 src/modules/initramfscfg/main.py:90
#: src/modules/localecfg/main.py:141 src/modules/networkcfg/main.py:106
#: src/modules/openrcdmcryptcfg/main.py:77
msgid "No root mount point is given for <pre>{!s}</pre> to use."
msgstr "No root mount point is given for <pre>{!s}</pre> to use."
#: src/modules/rawfs/main.py:26
msgid "Installing data."
msgstr "Installing data."
#: src/modules/services-openrc/main.py:29
msgid "Configure OpenRC services"
msgstr "Configure OpenRC services"
#: src/modules/services-openrc/main.py:57
msgid "Cannot add service {name!s} to run-level {level!s}."
msgstr "Cannot add service {name!s} to run-level {level!s}."
#: src/modules/services-openrc/main.py:59
msgid "Cannot remove service {name!s} from run-level {level!s}."
msgstr "Cannot remove service {name!s} from run-level {level!s}."
#: src/modules/services-openrc/main.py:61
msgid ""
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
"level {level!s}."
msgstr ""
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
"level {level!s}."
#: src/modules/services-openrc/main.py:94
msgid ""
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
#: src/modules/fstab/main.py:412
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
msgstr ""
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
#: src/modules/services-openrc/main.py:101
msgid "Target runlevel does not exist"
msgstr "Target runlevel does not exist"
#: src/modules/services-openrc/main.py:102
msgid ""
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
"exist."
#: src/modules/grubcfg/main.py:29
msgid "Configure GRUB."
msgstr ""
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
"exist."
#: src/modules/services-openrc/main.py:110
msgid "Target service does not exist"
msgstr "Target service does not exist"
#: src/modules/services-openrc/main.py:111
msgid ""
"The path for service {name!s} is <code>{path!s}</code>, which does not "
"exist."
#: src/modules/hwclock/main.py:26
msgid "Setting hardware clock."
msgstr ""
"The path for service {name!s} is <code>{path!s}</code>, which does not "
"exist."
#: src/modules/plymouthcfg/main.py:27
msgid "Configure Plymouth theme"
msgstr "Configure Plymouth theme"
#: src/modules/initcpiocfg/main.py:27
msgid "Configuring mkinitcpio."
msgstr ""
#: src/modules/initcpiocfg/main.py:246
msgid "No partitions are defined for <pre>initcpiocfg</pre>."
msgstr ""
#: src/modules/initcpiocfg/main.py:250
msgid "No root mount point for <pre>initcpiocfg</pre>."
msgstr ""
#: src/modules/initramfscfg/main.py:32
msgid "Configuring initramfs."
msgstr ""
#: src/modules/localecfg/main.py:31
msgid "Configuring locales."
msgstr ""
#: src/modules/mkinitfs/main.py:27
msgid "Creating initramfs with mkinitfs."
msgstr ""
#: src/modules/mkinitfs/main.py:49
msgid "Failed to run mkinitfs on the target"
msgstr ""
#: src/modules/mkinitfs/main.py:50
msgid "The exit code was {}"
msgstr ""
#: src/modules/mount/main.py:43
msgid "Mounting partitions."
msgstr ""
#: src/modules/mount/main.py:164 src/modules/mount/main.py:200
msgid "Internal error mounting zfs datasets"
msgstr ""
#: src/modules/mount/main.py:176
msgid "Failed to import zpool"
msgstr ""
#: src/modules/mount/main.py:192
msgid "Failed to unlock zpool"
msgstr ""
#: src/modules/mount/main.py:209 src/modules/mount/main.py:214
msgid "Failed to set zfs mountpoint"
msgstr ""
#: src/modules/mount/main.py:365
msgid "zfs mounting error"
msgstr ""
#: src/modules/networkcfg/main.py:29
msgid "Saving network configuration."
msgstr ""
#: src/modules/openrcdmcryptcfg/main.py:26
msgid "Configuring OpenRC dmcrypt service."
msgstr ""
#: src/modules/packages/main.py:54 src/modules/packages/main.py:65
#: src/modules/packages/main.py:75
msgid "Install packages."
msgstr "Install packages."
msgstr ""
#: src/modules/packages/main.py:63
#, python-format
msgid "Processing packages (%(count)d / %(total)d)"
msgstr "Processing packages (%(count)d / %(total)d)"
msgstr ""
#: src/modules/packages/main.py:68
#, python-format
msgid "Installing one package."
msgid_plural "Installing %(num)d packages."
msgstr[0] "Installing one package."
msgstr[1] "Installing %(num)d packages."
msgstr[0] ""
msgstr[1] ""
#: src/modules/packages/main.py:71
#, python-format
msgid "Removing one package."
msgid_plural "Removing %(num)d packages."
msgstr[0] "Removing one package."
msgstr[1] "Removing %(num)d packages."
msgstr[0] ""
msgstr[1] ""
#: src/modules/packages/main.py:725 src/modules/packages/main.py:737
#: src/modules/packages/main.py:765
msgid "Package Manager error"
msgstr "Package Manager error"
msgstr ""
#: src/modules/packages/main.py:726
msgid ""
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
"returned error code {!s}."
msgstr ""
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
"returned error code {!s}."
#: src/modules/packages/main.py:738
msgid ""
"The package manager could not update the system. The command <pre>{!s}</pre>"
" returned error code {!s}."
"The package manager could not update the system. The command <pre>{!s}</pre> "
"returned error code {!s}."
msgstr ""
"The package manager could not update the system. The command <pre>{!s}</pre>"
" returned error code {!s}."
#: src/modules/packages/main.py:766
msgid ""
"The package manager could not make changes to the installed system. The "
"command <pre>{!s}</pre> returned error code {!s}."
msgstr ""
"The package manager could not make changes to the installed system. The "
"command <pre>{!s}</pre> returned error code {!s}."
#: src/modules/bootloader/main.py:43
msgid "Install bootloader."
msgstr "Install bootloader."
#: src/modules/bootloader/main.py:614
msgid "Failed to install grub, no partitions defined in global storage"
msgstr "Failed to install grub, no partitions defined in global storage"
#: src/modules/bootloader/main.py:782
msgid "Bootloader installation error"
msgstr "Bootloader installation error"
#: src/modules/bootloader/main.py:783
msgid ""
"The bootloader could not be installed. The installation command "
"<pre>{!s}</pre> returned error code {!s}."
#: src/modules/plymouthcfg/main.py:27
msgid "Configure Plymouth theme"
msgstr ""
"The bootloader could not be installed. The installation command "
"<pre>{!s}</pre> returned error code {!s}."
#: src/modules/hwclock/main.py:26
msgid "Setting hardware clock."
msgstr "Setting hardware clock."
#: src/modules/rawfs/main.py:26
msgid "Installing data."
msgstr ""
#: src/modules/mkinitfs/main.py:27
msgid "Creating initramfs with mkinitfs."
msgstr "Creating initramfs with mkinitfs."
#: src/modules/services-openrc/main.py:29
msgid "Configure OpenRC services"
msgstr ""
#: src/modules/mkinitfs/main.py:49
msgid "Failed to run mkinitfs on the target"
msgstr "Failed to run mkinitfs on the target"
#: src/modules/services-openrc/main.py:57
msgid "Cannot add service {name!s} to run-level {level!s}."
msgstr ""
#: src/modules/mkinitfs/main.py:50 src/modules/dracut/main.py:50
msgid "The exit code was {}"
msgstr "The exit code was {}"
#: src/modules/services-openrc/main.py:59
msgid "Cannot remove service {name!s} from run-level {level!s}."
msgstr ""
#: src/modules/dracut/main.py:27
msgid "Creating initramfs with dracut."
msgstr "Creating initramfs with dracut."
#: src/modules/services-openrc/main.py:61
msgid ""
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
"level {level!s}."
msgstr ""
#: src/modules/dracut/main.py:49
msgid "Failed to run dracut on the target"
msgstr "Failed to run dracut on the target"
#: src/modules/services-openrc/main.py:93
msgid "Cannot modify service"
msgstr ""
#: src/modules/initramfscfg/main.py:32
msgid "Configuring initramfs."
msgstr "Configuring initramfs."
#: src/modules/services-openrc/main.py:94
msgid ""
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
msgstr ""
#: src/modules/openrcdmcryptcfg/main.py:26
msgid "Configuring OpenRC dmcrypt service."
msgstr "Configuring OpenRC dmcrypt service."
#: src/modules/services-openrc/main.py:101
msgid "Target runlevel does not exist"
msgstr ""
#: src/modules/fstab/main.py:28
msgid "Writing fstab."
msgstr "Writing fstab."
#: src/modules/services-openrc/main.py:102
msgid ""
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
"exist."
msgstr ""
#: src/modules/fstab/main.py:429
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
msgstr "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
#: src/modules/services-openrc/main.py:110
msgid "Target service does not exist"
msgstr ""
#: src/modules/dummypython/main.py:35
msgid "Dummy python job."
msgstr "Dummy python job."
#: src/modules/services-openrc/main.py:111
msgid ""
"The path for service {name!s} is <code>{path!s}</code>, which does not exist."
msgstr ""
#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
#: src/modules/dummypython/main.py:94
msgid "Dummy python step {}"
msgstr "Dummy python step {}"
#: src/modules/services-systemd/main.py:26
msgid "Configure systemd units"
msgstr ""
#: src/modules/localecfg/main.py:31
msgid "Configuring locales."
msgstr "Configuring locales."
#: src/modules/services-systemd/main.py:64
msgid "Cannot modify unit"
msgstr ""
#: src/modules/networkcfg/main.py:29
msgid "Saving network configuration."
msgstr "Saving network configuration."
#: src/modules/services-systemd/main.py:65
msgid ""
"<code>systemctl {_action!s}</code> call in chroot returned error code "
"{_exit_code!s}."
msgstr ""
#: src/modules/services-systemd/main.py:66
msgid "Cannot {_action!s} systemd unit <code>{_name!s}</code>."
msgstr ""
#: src/modules/unpackfs/main.py:34
msgid "Filling up filesystems."
msgstr ""
#: src/modules/unpackfs/main.py:254
msgid "rsync failed with error code {}."
msgstr ""
#: src/modules/unpackfs/main.py:299
msgid "Unpacking image {}/{}, file {}/{}"
msgstr ""
#: src/modules/unpackfs/main.py:314
msgid "Starting to unpack {}"
msgstr ""
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:467
msgid "Failed to unpack image \"{}\""
msgstr ""
#: src/modules/unpackfs/main.py:430
msgid "No mount point for root partition"
msgstr ""
#: src/modules/unpackfs/main.py:431
msgid "globalstorage does not contain a \"rootMountPoint\" key."
msgstr ""
#: src/modules/unpackfs/main.py:434
msgid "Bad mount point for root partition"
msgstr ""
#: src/modules/unpackfs/main.py:435
msgid "rootMountPoint is \"{}\", which does not exist."
msgstr ""
#: src/modules/unpackfs/main.py:439 src/modules/unpackfs/main.py:455
#: src/modules/unpackfs/main.py:459 src/modules/unpackfs/main.py:465
#: src/modules/unpackfs/main.py:480
msgid "Bad unpackfs configuration"
msgstr ""
#: src/modules/unpackfs/main.py:440
msgid "There is no configuration information."
msgstr ""
#: src/modules/unpackfs/main.py:456
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
msgstr ""
#: src/modules/unpackfs/main.py:460
msgid "The source filesystem \"{}\" does not exist"
msgstr ""
#: src/modules/unpackfs/main.py:466
msgid ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
msgstr ""
#: src/modules/unpackfs/main.py:481
msgid "The destination \"{}\" in the target system is not a directory"
msgstr ""
#: src/modules/zfshostid/main.py:27
msgid "Copying zfs generated hostid."
msgstr ""

View File

@ -132,9 +132,11 @@ sequence:
- locale
- keyboard
- localecfg
- luksopenswaphookcfg
- luksbootkeyfile
- luksopenswaphookcfg
# - dracutlukscfg
- plymouthcfg
# - zfshostid
- initcpiocfg
- initcpio
- users

View File

@ -19,7 +19,7 @@ static const char s_header[]
static const char s_footer[]
= QT_TRANSLATE_NOOP( "AboutData",
"Thanks to <a href=\"https://calamares.io/team/\">the Calamares team</a> "
"and the <a href=\"https://www.transifex.com/calamares/calamares/\">Calamares "
"and the <a href=\"https://app.transifex.com/calamares/calamares/\">Calamares "
"translators team</a>.<br/><br/>"
"<a href=\"https://calamares.io/\">Calamares</a> "
"development is sponsored by <br/>"

View File

@ -46,6 +46,7 @@ prettyNameForFileSystemType( FileSystem::Type t )
case FileSystem::Ufs:
case FileSystem::Hpfs:
case FileSystem::Luks:
case FileSystem::Luks2:
case FileSystem::Ocfs2:
case FileSystem::Zfs:
case FileSystem::Nilfs2:

View File

@ -73,6 +73,7 @@ ExecutionViewStep::ExecutionViewStep( QObject* parent )
{
m_widget->setObjectName( "slideshow" );
m_progressBar->setObjectName( "exec-progress" );
m_progressBar->setFormat(tr("%p%", "Progress percentage indicator: %p is where the number 0..100 is placed"));
m_label->setObjectName( "exec-message" );
QVBoxLayout* layout = new QVBoxLayout( m_widget );

View File

@ -9,25 +9,31 @@
# should specifically set *efiBootloaderId* to "debian" because that is
# hard-coded in `grubx64.efi`.
---
# A variable from global storage which overrides the value of efiBootLoader
#efiBootLoaderVar: "packagechooser_bootloader"
# Define which bootloader you want to use for EFI installations
# Possible options are 'grub', 'sb-shim' and 'systemd-boot'.
# Possible options are 'grub', 'sb-shim', 'refind` and 'systemd-boot'.
efiBootLoader: "grub"
# systemd-boot configuration files settings, set kernel search path, kernel name
# and amount of time before default selection boots
# systemd-boot configuration files settings
# kernelSearchPath is the path relative to the root of the install to search for kernels
# A kernel is identified by finding files which match regular expression, kernelPattern
kernelSearchPath: "/usr/lib/modules"
kernelName: "vmlinuz"
timeout: "10"
kernelPattern: "^vmlinuz.*"
# additionalInitrdFiles is a comma seperated list of file names
additionalInitrdFiles:
- "/boot/amd-ucode"
- "/boot/intel-ucode"
# loaderEntries is an array of options to add to loader.conf for systemd-boot
# please note that the "default" option is added programmatically
loaderEntries:
- "timeout 5"
- "console-mode keep"
# Optionally set the menu entry name to use in systemd-boot.
# If not specified here, these settings will be taken from branding.desc.
#
# bootloaderEntryName: "Generic GNU/Linux"
# systemd-boot and refind support custom kernel params
kernelParams: [ "quiet" ]
# A list of kernel names that refind should accept as kernels
#refindKernelList: [ "linux","linux-lts","linux-zen","linux-hardened" ]
# GRUB 2 binary names and boot directory
# Some distributions (e.g. Fedora) use grub2-* (resp. /boot/grub2/) names.

View File

@ -6,17 +6,13 @@ $id: https://calamares.io/schemas/bootloader
additionalProperties: false
type: object
properties:
efiBootLoaderVar: { type: string }
efiBootLoader: { type: string }
kernelSearchPath: { type: string }
kernelName: { type: string }
timeout: { type: string } # Inserted verbatim
additionalInitrdFiles:
type: array
items:
type: string
bootloaderEntryName: { type: string }
kernelLine: { type: string }
fallbackKernelLine: { type: string }
kernelParams: { type: array, items: { type: string } }
loaderEntries: { type: array, items: { type: string } }
refindKernelList: { type: array, items: { type: string } }
# Programs
grubInstall: { type: string }
@ -27,12 +23,3 @@ properties:
efiBootloaderId: { type: string }
installEFIFallback: { type: boolean }
required:
- efiBootLoader
- kernelSearchPath
- kernelName
- grubInstall
- grubMkconfig
- grubCfg
- grubProbe

View File

@ -20,7 +20,9 @@
# Calamares is Free Software: see the License-Identifier above.
#
import fileinput
import os
import re
import shutil
import subprocess
@ -28,8 +30,8 @@ import libcalamares
from libcalamares.utils import check_target_env_call
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
@ -39,6 +41,7 @@ _ = gettext.translation("calamares-python",
# to make identifiers (or to clean up names to make filenames).
file_name_sanitizer = str.maketrans(" /()", "_-__")
def pretty_name():
return _("Install bootloader.")
@ -59,20 +62,6 @@ def get_uuid():
return ""
def get_bootloader_entry_name():
"""
Passes 'bootloader_entry_name' to other routine based
on configuration file.
:return:
"""
if "bootloaderEntryName" in libcalamares.job.configuration:
return libcalamares.job.configuration["bootloaderEntryName"]
else:
branding = libcalamares.globalstorage.value("branding")
return branding["bootloaderEntryName"]
def get_kernel_line(kernel_type):
"""
Passes 'kernel_line' to other routine based on configuration file.
@ -136,49 +125,43 @@ def is_zfs_root(partition):
return partition["mountPoint"] == "/" and partition["fs"] == "zfs"
def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kernel, kernel_type, kernel_version):
"""
Creates systemd-boot configuration files based on given parameters.
:param installation_root_path: A string containing the absolute path to the root of the installation
:param efi_dir: A string containing the path to the efi dir relative to the root of the installation
:param uuid: A string containing the UUID of the root volume
:param entry: A string containing the name of the entry as it will be displayed on boot
:param kernel: A string containing the path to the kernel relative to the root of the installation
:param kernel_type: A string which should be set if there is a special version of the entry, for example "fallback"
:param kernel_version: The kernel version string
"""
kernel_params = ["quiet"]
def get_kernel_params(uuid):
kernel_params = libcalamares.job.configuration.get("kernelParams", ["quiet"])
kernel_params.append("rw")
partitions = libcalamares.globalstorage.value("partitions")
swap_uuid = ""
swap_outer_mappername = None
swap_outer_uuid = None
cryptdevice_params = []
have_dracut = libcalamares.utils.target_env_call(["sh", "-c", "which dracut"]) == 0
# Take over swap settings:
# - unencrypted swap partition sets swap_uuid
# - encrypted root sets cryptdevice_params
for partition in partitions:
if partition["fs"] == "linuxswap" and not partition.get("claimed", None):
# Skip foreign swap
continue
has_luks = "luksMapperName" in partition
if partition["fs"] == "linuxswap" and not has_luks:
swap_uuid = partition["uuid"]
if (partition["fs"] == "linuxswap" and has_luks):
if partition["fs"] == "linuxswap" and has_luks:
swap_outer_mappername = partition["luksMapperName"]
swap_outer_uuid = partition["luksUuid"]
if partition["mountPoint"] == "/" and has_luks:
cryptdevice_params = ["cryptdevice=UUID="
+ partition["luksUuid"]
+ ":"
+ partition["luksMapperName"],
"root=/dev/mapper/"
+ partition["luksMapperName"]]
if have_dracut:
cryptdevice_params = [f"rd.luks.uuid={partition['luksUuid']}"]
else:
cryptdevice_params = [f"cryptdevice=UUID={partition['luksUuid']}:{partition['luksMapperName']}"]
cryptdevice_params.append(f"root=/dev/mapper/{partition['luksMapperName']}")
# btrfs and zfs handling
for partition in partitions:
# systemd-boot with a BTRFS root filesystem needs to be told abouut the root subvolume.
# If a btrfs root subvolume wasn't set, it means the root is directly on the partition
# and this option isn't needed
if is_btrfs_root(partition):
@ -190,7 +173,7 @@ def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kerne
if is_zfs_root(partition):
zfs_root_path = get_zfs_root()
if zfs_root_path is not None:
kernel_params.append("zfs=" + zfs_root_path)
kernel_params.append("root=ZFS=" + zfs_root_path)
else:
# Something is really broken if we get to this point
libcalamares.utils.warning("Internal error handling zfs dataset")
@ -204,93 +187,86 @@ def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kerne
if swap_uuid:
kernel_params.append("resume=UUID={!s}".format(swap_uuid))
if have_dracut and swap_outer_uuid:
kernel_params.append(f"rd.luks.uuid={swap_outer_uuid}")
if swap_outer_mappername:
kernel_params.append("resume=/dev/mapper/{!s}".format(
swap_outer_mappername))
kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}")
libcalamares.utils.debug("Configure: \"{!s}\"".format(f"{entry} {kernel_version}"))
return kernel_params
if kernel_type == "fallback":
version_string = kernel_version + "-fallback"
initrd = "initrd-fallback"
else:
version_string = kernel_version
initrd = "initrd"
def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, kernel, kernel_version):
"""
Creates systemd-boot configuration files based on given parameters.
:param installation_root_path: A string containing the absolute path to the root of the installation
:param efi_dir: A string containing the path to the efi dir relative to the root of the installation
:param uuid: A string containing the UUID of the root volume
:param kernel: A string containing the path to the kernel relative to the root of the installation
:param kernel_version: The kernel version string
"""
# Get the kernel params and write them to /etc/kernel/cmdline
# This file is used by kernel-install
kernel_params = " ".join(get_kernel_params(uuid))
kernel_cmdline_path = os.path.join(installation_root_path, "etc", "kernel")
os.makedirs(kernel_cmdline_path, exist_ok=True)
with open(os.path.join(kernel_cmdline_path, "cmdline"), "w") as cmdline_file:
cmdline_file.write(kernel_params)
libcalamares.utils.debug(f"Configuring kernel version {kernel_version}")
# get the machine-id
with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file:
machine_id = machineid_file.read().rstrip('\n')
# Copy kernel to a subdirectory of /efi partition
# Ensure the directory exists
machine_dir = os.path.join(installation_root_path + efi_dir, machine_id)
os.makedirs(machine_dir, exist_ok=True)
target_efi_files_dir = os.path.join(machine_dir, kernel_version)
os.makedirs(target_efi_files_dir, exist_ok=True)
kernel_path = os.path.join(installation_root_path, kernel)
kernel_name = os.path.basename(kernel_path)
shutil.copyfile(kernel_path, os.path.join(target_efi_files_dir, "linux"))
# write the entry
lines = [
'## Generated by Calamares\n',
'\n',
"title {!s}\n".format(entry),
"version {!s}\n".format(version_string),
"machine-id {!s}\n".format(machine_id),
"linux {!s}\n".format(os.path.join("/", machine_id, kernel_version, "linux")),
]
try:
additional_initrd_files = libcalamares.job.configuration["additionalInitrdFiles"]
for initrd_file in additional_initrd_files:
libcalamares.utils.debug("Attempting to handle initrd image " + initrd_file)
if os.path.isfile(os.path.join(installation_root_path, initrd_file.lstrip('/'))):
libcalamares.utils.debug("Found image " + initrd_file)
shutil.copyfile(os.path.join(installation_root_path, initrd_file.lstrip('/')), os.path.join(target_efi_files_dir, os.path.basename(initrd_file)))
lines.append("initrd {!s}\n".format(os.path.join("/", machine_id, kernel_version, os.path.basename(initrd_file))))
except KeyError: # If the configuration option isn't set, we can just move on
libcalamares.utils.debug("Failed to find key additionalInitrdFiles")
pass
lines.append("initrd {!s}\n".format(os.path.join("/", machine_id, kernel_version, initrd)))
lines.append("options {!s} rw\n".format(" ".join(kernel_params)))
conf_path = os.path.join(installation_root_path + efi_dir,
"loader",
"entries",
machine_id + "-" + version_string + ".conf")
with open(conf_path, 'w') as conf_file:
for line in lines:
conf_file.write(line)
# Call kernel-install for each kernel
libcalamares.utils.target_env_process_output(["kernel-install",
"add",
kernel_version,
os.path.join("/", kernel)])
def create_loader(loader_path, entry):
def create_loader(loader_path, installation_root_path):
"""
Writes configuration for loader.
:param loader_path:
:param entry:
:param loader_path: The absolute path to the loader.conf file
:param installation_root_path: The path to the root of the target installation
"""
timeout = libcalamares.job.configuration["timeout"]
lines = [
"timeout {!s}\n".format(timeout),
"default {!s}\n".format(entry),
]
# get the machine-id
with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file:
machine_id = machineid_file.read().rstrip('\n')
try:
loader_entries = libcalamares.job.configuration["loaderEntries"]
except KeyError:
libcalamares.utils.debug("No aditional loader entries found in config")
loader_entries = []
pass
lines = [f"default {machine_id}*"]
lines.extend(loader_entries)
with open(loader_path, 'w') as loader_file:
for line in lines:
loader_file.write(line)
loader_file.write(line + "\n")
class suffix_iterator(object):
class SuffixIterator(object):
"""
Wrapper for one of the "generator" classes below to behave like
a proper Python iterator. The iterator is initialized with a
maximum number of attempts to generate a new suffix.
"""
def __init__(self, attempts, generator):
self.generator = generator
self.attempts = attempts
@ -310,6 +286,7 @@ class serialEfi(object):
"""
EFI Id generator that appends a serial number to the given name.
"""
def __init__(self, name):
self.name = name
# So the first call to next() will bump it to 0
@ -354,6 +331,7 @@ class randomEfi(object):
"""
EFI Id generator that appends a random 4-digit hex number to the given name.
"""
def __init__(self, name):
self.name = name
# So the first call to next() will bump it to 0
@ -427,7 +405,7 @@ def change_efi_suffix(efi_directory, bootloader_id):
"""
if bootloader_id.endswith("}") and "${" in bootloader_id:
# Do 10 attempts with any suffix generator
g = suffix_iterator(10, get_efi_suffix_generator(bootloader_id))
g = SuffixIterator(10, get_efi_suffix_generator(bootloader_id))
else:
# Just one attempt
g = [bootloader_id]
@ -444,7 +422,7 @@ def efi_label(efi_directory):
used within @p efi_directory.
"""
if "efiBootloaderId" in libcalamares.job.configuration:
efi_bootloader_id = change_efi_suffix( efi_directory, libcalamares.job.configuration["efiBootloaderId"] )
efi_bootloader_id = change_efi_suffix(efi_directory, libcalamares.job.configuration["efiBootloaderId"])
else:
branding = libcalamares.globalstorage.value("branding")
efi_bootloader_id = branding["bootloaderEntryName"]
@ -482,6 +460,7 @@ def efi_boot_next():
if boot_entry:
subprocess.call([boot_mgr, "-n", boot_entry])
def get_kernels(installation_root_path):
"""
Gets a list of kernels and associated values for each kernel. This will work as is for many distros.
@ -493,20 +472,32 @@ def get_kernels(installation_root_path):
Each 3-tuple contains the kernel, kernel_type and kernel_version
"""
kernel_search_path = libcalamares.job.configuration["kernelSearchPath"]
source_kernel_name = libcalamares.job.configuration["kernelName"]
try:
kernel_search_path = libcalamares.job.configuration["kernelSearchPath"]
except KeyError:
libcalamares.utils.warning("No kernel pattern found in configuration, using '/usr/lib/modules'")
kernel_search_path = "/usr/lib/modules"
pass
kernel_list = []
# find all the installed kernels and generate default and fallback entries for each
try:
kernel_pattern = libcalamares.job.configuration["kernelPattern"]
except KeyError:
libcalamares.utils.warning("No kernel pattern found in configuration, using 'vmlinuz'")
kernel_pattern = "vmlinuz"
pass
# find all the installed kernels
for root, dirs, files in os.walk(os.path.join(installation_root_path, kernel_search_path.lstrip('/'))):
for file in files:
if file == source_kernel_name:
if re.search(kernel_pattern, file):
rel_root = os.path.relpath(root, installation_root_path)
kernel_list.append((os.path.join(rel_root, file),"default",os.path.basename(root)))
kernel_list.append((os.path.join(rel_root, file),"fallback",os.path.basename(root)))
kernel_list.append((os.path.join(rel_root, file), "default", os.path.basename(root)))
return kernel_list
def install_systemd_boot(efi_directory):
"""
Installs systemd-boot as bootloader for EFI setups.
@ -517,8 +508,6 @@ def install_systemd_boot(efi_directory):
installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
install_efi_directory = installation_root_path + efi_directory
uuid = get_uuid()
distribution = get_bootloader_entry_name()
distribution_translated = distribution.translate(file_name_sanitizer)
loader_path = os.path.join(install_efi_directory,
"loader",
"loader.conf")
@ -528,14 +517,13 @@ def install_systemd_boot(efi_directory):
for (kernel, kernel_type, kernel_version) in get_kernels(installation_root_path):
create_systemd_boot_conf(installation_root_path,
efi_directory,
uuid,
distribution,
kernel,
kernel_type,
kernel_version)
efi_directory,
uuid,
kernel,
kernel_version)
create_loader(loader_path, installation_root_path)
create_loader(loader_path, distribution_translated)
def get_grub_efi_parameters():
"""
@ -554,15 +542,16 @@ def get_grub_efi_parameters():
if efi_bitness == "32":
# Assume all 32-bitters are legacy x86
return ("i386-efi", "grubia32.efi", "bootia32.efi")
return "i386-efi", "grubia32.efi", "bootia32.efi"
elif efi_bitness == "64" and cpu_type == "aarch64":
return ("arm64-efi", "grubaa64.efi", "bootaa64.efi")
return "arm64-efi", "grubaa64.efi", "bootaa64.efi"
elif efi_bitness == "64" and cpu_type == "loongarch64":
return ("loongarch64-efi", "grubloongarch64.efi", "bootloongarch64.efi")
return "loongarch64-efi", "grubloongarch64.efi", "bootloongarch64.efi"
elif efi_bitness == "64":
# If it's not ARM, must by AMD64
return ("x86_64-efi", "grubx64.efi", "bootx64.efi")
libcalamares.utils.warning("Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type)))
return "x86_64-efi", "grubx64.efi", "bootx64.efi"
libcalamares.utils.warning(
"Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type)))
return None
@ -667,8 +656,8 @@ def install_grub(efi_directory, fw_type):
# VFAT is weird, see issue CAL-385
install_efi_directory_firmware = (vfat_correct_case(
install_efi_directory,
"EFI"))
install_efi_directory,
"EFI"))
if not os.path.exists(install_efi_directory_firmware):
os.makedirs(install_efi_directory_firmware)
@ -676,8 +665,8 @@ def install_grub(efi_directory, fw_type):
# most usual they are boot, Boot, BOOT
install_efi_boot_directory = (vfat_correct_case(
install_efi_directory_firmware,
"boot"))
install_efi_directory_firmware,
"boot"))
if not os.path.exists(install_efi_boot_directory):
os.makedirs(install_efi_boot_directory)
@ -712,6 +701,9 @@ def install_secureboot(efi_directory):
install_efi_bin = "shimx64.efi"
elif efi_word_size() == "32":
install_efi_bin = "shimia32.efi"
else:
libcalamares.utils.warning(f"Unknown efi word size of {efi_word_size()} found")
return None
# Copied, roughly, from openSUSE's install script,
# and pythonified. *disk* is something like /dev/sda,
@ -725,7 +717,7 @@ def install_secureboot(efi_directory):
libcalamares.job.configuration["grubProbe"],
"-t", "disk", "--device-map=", install_efi_directory]).decode("ascii")
efi_drive_partition = efi_drive.replace("(","").replace(")","").split(",")[1]
efi_drive_partition = efi_drive.replace("(", "").replace(")", "").split(",")[1]
# Get the first run of digits from the partition
efi_partition_number = None
c = 0
@ -765,6 +757,62 @@ def vfat_correct_case(parent, name):
return os.path.join(parent, name)
def efi_partitions(efi_boot_path):
"""
The (one) partition mounted on @p efi_boot_path, or an empty list.
"""
return [p for p in libcalamares.globalstorage.value("partitions") if p["mountPoint"] == efi_boot_path]
def update_refind_config(efi_directory, installation_root_path):
"""
:param efi_directory: The path to the efi directory relative to the root
:param installation_root_path: The path to the root of the installation
"""
try:
kernel_list = libcalamares.job.configuration["refindKernelList"]
except KeyError:
libcalamares.utils.warning('refindKernelList not set. Skipping updating refind.conf')
return
# Update the config in the file
for line in fileinput.input(installation_root_path + efi_directory + "/EFI/refind/refind.conf", inplace=True):
line = line.strip()
if line.startswith("#extra_kernel_version_strings") or line.startswith("extra_kernel_version_strings"):
line = line.lstrip("#")
for kernel in kernel_list:
if kernel not in line:
line += "," + kernel
print(line)
def install_refind(efi_directory):
try:
installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
except KeyError:
libcalamares.utils.warning('Global storage value "rootMountPoint" missing')
install_efi_directory = installation_root_path + efi_directory
uuid = get_uuid()
kernel_params = " ".join(get_kernel_params(uuid))
conf_path = os.path.join(installation_root_path, "boot/refind_linux.conf")
check_target_env_call(["refind-install"])
with open(conf_path, "r") as refind_file:
filedata = [x.strip() for x in refind_file.readlines()]
with open(conf_path, 'w') as refind_file:
for line in filedata:
if line.startswith('"Boot with standard options"'):
line = f'"Boot with standard options" "{kernel_params}"'
elif line.startswith('"Boot to single-user mode"'):
line = f'"Boot to single-user mode" "{kernel_params}" single'
refind_file.write(line + "\n")
update_refind_config(efi_directory, installation_root_path)
def prepare_bootloader(fw_type):
"""
Prepares bootloader.
@ -774,19 +822,47 @@ def prepare_bootloader(fw_type):
:param fw_type:
:return:
"""
efi_boot_loader = libcalamares.job.configuration["efiBootLoader"]
# Get the boot loader selection from global storage if it is set in the config file
try:
gs_name = libcalamares.job.configuration["efiBootLoaderVar"]
if libcalamares.globalstorage.contains(gs_name):
efi_boot_loader = libcalamares.globalstorage.value(gs_name)
else:
libcalamares.utils.warning(
f"Specified global storage value not found in global storage")
return None
except KeyError:
# If the conf value for using global storage is not set, use the setting from the config file.
try:
efi_boot_loader = libcalamares.job.configuration["efiBootLoader"]
except KeyError:
if fw_type == "efi":
libcalamares.utils.warning("Configuration missing both efiBootLoader and efiBootLoaderVar on an EFI "
"system, bootloader not installed")
return
else:
pass
# If the user has selected not to install bootloader, bail out here
if efi_boot_loader.casefold() == "none":
libcalamares.utils.debug("Skipping bootloader installation since no bootloader was selected")
return None
efi_directory = libcalamares.globalstorage.value("efiSystemPartition")
if efi_boot_loader == "systemd-boot" and fw_type == "efi":
install_systemd_boot(efi_directory)
elif efi_boot_loader == "sb-shim" and fw_type == "efi":
install_secureboot(efi_directory)
elif efi_boot_loader == "refind" and fw_type == "efi":
install_refind(efi_directory)
elif efi_boot_loader == "grub" or fw_type != "efi":
install_grub(efi_directory, fw_type)
else:
libcalamares.utils.debug( "WARNING: the combination of "
"boot-loader '{!s}' and firmware '{!s}' "
"is not supported.".format(efi_boot_loader, fw_type) )
libcalamares.utils.debug("WARNING: the combination of "
"boot-loader '{!s}' and firmware '{!s}' "
"is not supported.".format(efi_boot_loader, fw_type))
def run():
@ -798,16 +874,16 @@ def run():
fw_type = libcalamares.globalstorage.value("firmwareType")
if (libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi"):
libcalamares.utils.warning( "Non-EFI system, and no bootloader is set." )
if libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi":
libcalamares.utils.warning("Non-EFI system, and no bootloader is set.")
return None
partitions = libcalamares.globalstorage.value("partitions")
if fw_type == "efi":
efi_system_partition = libcalamares.globalstorage.value("efiSystemPartition")
esp_found = [ p for p in partitions if p["mountPoint"] == efi_system_partition ]
esp_found = [p for p in partitions if p["mountPoint"] == efi_system_partition]
if not esp_found:
libcalamares.utils.warning( "EFI system, but nothing mounted on {!s}".format(efi_system_partition) )
libcalamares.utils.warning("EFI system, but nothing mounted on {!s}".format(efi_system_partition))
return None
try:
@ -817,7 +893,8 @@ def run():
libcalamares.utils.debug("stdout:" + str(e.stdout))
libcalamares.utils.debug("stderr:" + str(e.stderr))
return (_("Bootloader installation error"),
_("The bootloader could not be installed. The installation command <pre>{!s}</pre> returned error code {!s}.")
_("The bootloader could not be installed. The installation command <pre>{!s}</pre> returned error "
"code {!s}.")
.format(e.cmd, e.returncode))
return None

View File

@ -59,3 +59,17 @@ basicSetup: false
# *displaymanagers* list (as the only one).
#
sysconfigSetup: false
# Some DMs have specific settings. These can be customized here.
#
# greetd has configurable user and group; the user and group is created if it
# does not exist, and the user is set as default-session user.
#
# lightdm has a list of greeters to look for, preferring them in order if
# they are installed (if not, picks the alphabetically first greeter that is installed).
#
greetd:
greeter_user: "tom_bombadil"
greeter_group: "wheel"
lightdm:
preferred_greeters: ["lightdm-greeter.desktop", "slick-greeter.desktop"]

View File

@ -20,3 +20,13 @@ properties:
required: [ executable, desktopFile ]
basicSetup: { type: boolean, default: false }
sysconfigSetup: { type: boolean, default: false }
greetd:
type: object
properties:
greeter_user: { type: string }
greeter_group: { type: string }
additionalProperties: false
lightdm:
type: object
properties:
preferred_greeters: { type: array, items: { type: string } }

View File

@ -197,6 +197,8 @@ desktop_environments = [
DesktopEnvironment('/usr/bin/sway', 'sway'),
DesktopEnvironment('/usr/bin/ukui-session', 'ukui'),
DesktopEnvironment('/usr/bin/cutefish-session', 'cutefish-xsession'),
DesktopEnvironment('/usr/bin/river', 'river'),
DesktopEnvironment('/usr/bin/Hyprland', 'hyprland'),
]
@ -444,7 +446,6 @@ class DMgdm(DisplayManager):
userfile.write("Icon=\n")
def basic_setup(self):
if libcalamares.utils.target_env_call(
['getent', 'group', 'gdm']
@ -544,6 +545,11 @@ class DMlightdm(DisplayManager):
name = "lightdm"
executable = "lightdm"
# Can be overridden in the .conf file. With no value it won't match any
# desktop file in the xgreeters directory and instead we end up picking
# the alphabetically first file there.
preferred_greeters = []
def set_autologin(self, username, do_autologin, default_desktop_environment):
# Systems with LightDM as Desktop Manager
# Ideally, we should use configparser for the ini conf file,
@ -593,7 +599,6 @@ class DMlightdm(DisplayManager):
_("LightDM config file {!s} does not exist").format(lightdm_conf_path)
)
def basic_setup(self):
libcalamares.utils.target_env_call(
['mkdir', '-p', '/run/lightdm']
@ -633,40 +638,52 @@ class DMlightdm(DisplayManager):
)
)
def find_preferred_greeter(self):
"""
On Debian, lightdm-greeter.desktop is typically a symlink managed
by update-alternatives pointing to /etc/alternatives/lightdm-greeter
which is also a symlink to a real .desktop file back in /usr/share/xgreeters/
Returns a path *into the mounted target* of the preferred greeter -- usually
a .desktop file that specifies where the actual executable is. May return
None to indicate nothing-was-found.
"""
greeters_dir = "usr/share/xgreeters"
greeters_target_path = os.path.join(self.root_mount_point, greeters_dir)
available_names = os.listdir(greeters_target_path)
available_names.sort()
desktop_names = [n for n in self.preferred_greeters if n in available_names] # Preferred ones
if desktop_names:
return desktop_names[0]
desktop_names = [n for n in available_names if n.endswith(".desktop")] # .. otherwise any .desktop
if desktop_names:
return desktop_names[0]
return None
def greeter_setup(self):
lightdm_conf_path = os.path.join(
self.root_mount_point, "etc/lightdm/lightdm.conf"
)
lightdm_conf_path = os.path.join(self.root_mount_point, "etc/lightdm/lightdm.conf")
greeter_name = self.find_preferred_greeter()
# configure lightdm-greeter
greeter_path = os.path.join(
self.root_mount_point, "usr/share/xgreeters"
)
if greeter_name is not None:
greeter = os.path.basename(greeter_name) # Follow symlinks, hope they are not absolute
if greeter.endswith('.desktop'):
greeter = greeter[:-8] # Remove ".desktop" from end
if (os.path.exists(greeter_path)):
# configure first found lightdm-greeter
for entry in os.listdir(greeter_path):
if entry.endswith('.desktop'):
greeter = entry.split('.')[0]
libcalamares.utils.debug(
"found greeter {!s}".format(greeter)
)
os.system(
"sed -i -e \"s/^.*greeter-session=.*"
"/greeter-session={!s}/\" {!s}".format(
greeter,
lightdm_conf_path
)
)
libcalamares.utils.debug(
"{!s} configured as greeter.".format(greeter)
)
break
else:
return (
_("Cannot configure LightDM"),
_("No LightDM greeter installed.")
)
libcalamares.utils.debug("found greeter {!s}".format(greeter))
os.system(
"sed -i -e \"s/^.*greeter-session=.*"
"/greeter-session={!s}/\" {!s}".format(
greeter,
lightdm_conf_path
)
)
libcalamares.utils.debug("{!s} configured as greeter.".format(greeter))
else:
libcalamares.utils.error("No greeter found at all, preferred {!s}".format(self.preferred_greeters))
return (
_("Cannot configure LightDM"),
_("No LightDM greeter installed.")
)
class DMslim(DisplayManager):
@ -817,7 +834,7 @@ class DMgreetd(DisplayManager):
def desktop_environment_setup(self, default_desktop_environment):
with open(self.environments_path(), 'w') as envs_file:
envs_file.write(default_desktop_environment.desktop_file)
envs_file.write(default_desktop_environment.executable)
envs_file.write("\n")
def greeter_setup(self):
@ -828,7 +845,7 @@ class DMgreetd(DisplayManager):
de_command = default_desktop_environment.executable
if os.path.exists(self.os_path("usr/bin/gtkgreet")) and os.path.exists(self.os_path("usr/bin/cage")):
self.config_data['default_session']['command'] = "cage -s -- gtkgreet"
self.config_data['default_session']['command'] = "cage -d -s -- gtkgreet"
elif os.path.exists(self.os_path("usr/bin/tuigreet")):
tuigreet_base_cmd = "tuigreet --remember --time --issue --asterisks --cmd "
self.config_data['default_session']['command'] = tuigreet_base_cmd + de_command
@ -983,6 +1000,11 @@ def run():
# Do the actual configuration and collect messages
dm_setup_message = []
for dm in dm_impl:
dm_specific_configuration = libcalamares.job.configuration.get(dm.name, None)
if dm_specific_configuration and isinstance(dm_specific_configuration, dict):
for k, v in dm_specific_configuration.items():
if hasattr(dm, k):
setattr(dm, k, v)
dm_message = None
if enable_basic_setup:
dm_message = dm.basic_setup()

View File

@ -5,6 +5,6 @@
---
# Dracut defaults to setting initramfs-<kernel-version>.img
# If you want to specify another filename for the resulting image,
# set a custom kernel name, including the path
# set a custom name, including the path
#
# kernelName: /boot/initramfs-linux.img
initramfsName: /boot/initramfs-freebsd.img

View File

@ -6,4 +6,4 @@ $id: https://calamares.io/schemas/dracut
additionalProperties: false
type: object
properties:
kernelName: { type: string }
initramfsName: { type: string }

View File

@ -12,9 +12,10 @@
#
# Calamares is Free Software: see the License-Identifier above.
#
import subprocess
import libcalamares
from libcalamares.utils import check_target_env_call
from libcalamares.utils import target_env_process_output
import gettext
@ -34,12 +35,20 @@ def run_dracut():
:return:
"""
kernelName = libcalamares.job.configuration['kernelName']
try:
initramfs_name = libcalamares.job.configuration['initramfsName']
target_env_process_output(['dracut', '-f', initramfs_name])
except KeyError:
try:
target_env_process_output(['dracut', '-f'])
except subprocess.CalledProcessError as cpe:
libcalamares.utils.warning(f"Dracut failed with output: {cpe.output}")
return cpe.returncode
except subprocess.CalledProcessError as cpe:
libcalamares.utils.warning(f"Dracut failed with output: {cpe.output}")
return cpe.returncode
if not kernelName:
return check_target_env_call(['dracut', '-f'])
else:
return check_target_env_call(['dracut', '-f', '{}'.format(kernelName)])
return 0
def run():
@ -50,7 +59,6 @@ def run():
:return:
"""
return_code = run_dracut()
if return_code != 0:
return (_("Failed to run dracut on the target"),
_("The exit code was {}").format(return_code))
return (_("Failed to run dracut"),
_(f"Dracut failed to run on the target with return code: {return_code}"))

View File

@ -46,3 +46,6 @@ defaults:
GRUB_DISABLE_SUBMENU: true
GRUB_TERMINAL_OUTPUT: "console"
GRUB_DISABLE_RECOVERY: true
# Set to true to force defaults to be used even when not overwriting
always_use_defaults: false

View File

@ -19,4 +19,5 @@ properties:
GRUB_DISABLE_SUBMENU: { type: boolean, default: true }
GRUB_TERMINAL_OUTPUT: { type: string }
GRUB_DISABLE_RECOVERY: { type: boolean, default: true }
required: [ GRUB_TIMEOUT, GRUB_DEFAULT, GRUB_TERMINAL_OUTPUT ]
required: [ GRUB_TIMEOUT, GRUB_DEFAULT ]
always_use_defaults: { type: boolean, default: false }

View File

@ -14,6 +14,7 @@
#
import libcalamares
import fileinput
import os
import re
@ -85,6 +86,30 @@ def get_zfs_root():
return None
def update_existing_config(default_grub, grub_config_items):
"""
Updates the existing grub configuration file with any items present in @p grub_config_items
Items that exist in the file will be updated and new items will be appended to the end
:param default_grub: The absolute path to the grub config file
:param grub_config_items: A dict holding the key value pairs representing the items
"""
for line in fileinput.input(default_grub, inplace=True):
line = line.strip()
if "=" in line:
# This may be a key, strip the leading comment if it has one
key = line.lstrip("#").split("=")[0].strip()
# check if this is one of the keys we care about
if key in grub_config_items.keys():
print(f"{key}={grub_config_items[key]}")
else:
print(line)
else:
print(line)
def modify_grub_default(partitions, root_mount_point, distributor):
"""
Configures '/etc/default/grub' for hibernation and plymouth.
@ -103,7 +128,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
:return:
"""
default_grub = get_grub_config_path(root_mount_point)
distributor_replace = distributor.replace("'", "'\\''")
distributor = distributor.replace("'", "'\\''")
dracut_bin = libcalamares.utils.target_env_call(
["sh", "-c", "which dracut"]
)
@ -127,7 +152,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
zfs_root_path = None
for partition in partitions:
if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs"):
if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs", "zfs"):
no_save_default = True
break
@ -197,63 +222,46 @@ def modify_grub_default(partitions, root_mount_point, distributor):
if swap_outer_mappername:
kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}")
if "overwrite" in libcalamares.job.configuration:
overwrite = libcalamares.job.configuration["overwrite"]
else:
overwrite = False
overwrite = libcalamares.job.configuration.get("overwrite", False)
distributor_line = f"GRUB_DISTRIBUTOR='{distributor_replace}'"
kernel_cmd = f'GRUB_CMDLINE_LINUX_DEFAULT="{" ".join(kernel_params)}"'
have_kernel_cmd = False
have_distributor_line = False
grub_config_items = {}
# read the lines we need from the existing config
if os.path.exists(default_grub) and not overwrite:
with open(default_grub, 'r') as grub_file:
lines = [x.strip() for x in grub_file.readlines()]
for i in range(len(lines)):
if lines[i].startswith("#GRUB_CMDLINE_LINUX_DEFAULT"):
lines[i] = kernel_cmd
have_kernel_cmd = True
elif lines[i].startswith("GRUB_CMDLINE_LINUX_DEFAULT"):
regex = re.compile(r"^GRUB_CMDLINE_LINUX_DEFAULT\s*=\s*")
line = regex.sub("", lines[i])
line = line.lstrip()
line = line.lstrip("\"")
line = line.lstrip("'")
line = line.rstrip()
line = line.rstrip("\"")
line = line.rstrip("'")
existing_params = line.split()
for line in lines:
if line.startswith("GRUB_CMDLINE_LINUX_DEFAULT"):
existing_params = re.sub(r"^GRUB_CMDLINE_LINUX_DEFAULT\s*=\s*", "", line).strip("\"'").split()
for existing_param in existing_params:
existing_param_name = existing_param.split("=")[0]
existing_param_name = existing_param.split("=")[0].strip()
# the only ones we ever add
if existing_param_name not in [
"quiet", "resume", "splash"]:
# Ensure we aren't adding duplicated params
param_exists = False
for param in kernel_params:
if param.split("=")[0].strip() == existing_param_name:
param_exists = True
break
if not param_exists and existing_param_name not in ["quiet", "resume", "splash"]:
kernel_params.append(existing_param)
lines[i] = kernel_cmd
have_kernel_cmd = True
elif (lines[i].startswith("#GRUB_DISTRIBUTOR")
or lines[i].startswith("GRUB_DISTRIBUTOR")):
if libcalamares.job.configuration.get("keep_distributor", False):
lines[i] = distributor_line
have_distributor_line = True
else:
# We're not updating because of *keep_distributor*, but if
# this was a comment line, then it's still not been set.
have_distributor_line = have_distributor_line or not lines[i].startswith("#")
# If btrfs or f2fs is used, don't save default
if no_save_default and lines[i].startswith("GRUB_SAVEDEFAULT="):
lines[i] = "#GRUB_SAVEDEFAULT=\"true\""
else:
lines = []
elif line.startswith("GRUB_DISTRIBUTOR") and libcalamares.job.configuration.get("keep_distributor", False):
distributor_parts = line.split("=")
if len(distributor_parts) > 1:
distributor = distributor_parts[1].strip("'\"")
# If a filesystem grub can't write to is used, disable save default
if no_save_default and line.strip().startswith("GRUB_SAVEDEFAULT"):
grub_config_items["GRUB_SAVEDEFAULT"] = "false"
always_use_defaults = libcalamares.job.configuration.get("always_use_defaults", False)
# If applicable add the items from defaults to the dict containing the grub config to wirte/modify
if always_use_defaults or overwrite or not os.path.exists(default_grub):
if "defaults" in libcalamares.job.configuration:
for key, value in libcalamares.job.configuration[
"defaults"].items():
if value.__class__.__name__ == "bool":
for key, value in libcalamares.job.configuration["defaults"].items():
if isinstance(value, bool):
if value:
escaped_value = "true"
else:
@ -261,19 +269,20 @@ def modify_grub_default(partitions, root_mount_point, distributor):
else:
escaped_value = str(value).replace("'", "'\\''")
lines.append(f"{key}='{escaped_value}'")
grub_config_items[key] = f"'{escaped_value}'"
if not have_kernel_cmd:
lines.append(kernel_cmd)
if not have_distributor_line:
lines.append(distributor_line)
grub_config_items['GRUB_CMDLINE_LINUX_DEFAULT'] = f"'{' '.join(kernel_params)}'"
grub_config_items["GRUB_DISTRIBUTOR"] = f"'{distributor}'"
if cryptdevice_params and not unencrypted_separate_boot:
lines.append("GRUB_ENABLE_CRYPTODISK=y")
grub_config_items["GRUB_ENABLE_CRYPTODISK"] = "y"
with open(default_grub, 'w') as grub_file:
grub_file.write("\n".join(lines) + "\n")
if overwrite or not os.path.exists(default_grub) or libcalamares.job.configuration.get("prefer_grub_d", False):
with open(default_grub, 'w') as grub_file:
for key, value in grub_config_items.items():
grub_file.write(f"{key}={value}\n")
else:
update_existing_config(default_grub, grub_config_items)
return None

View File

@ -147,6 +147,7 @@ def find_initcpio_features(partitions, root_mount_point):
"base",
"udev",
"autodetect",
"kms",
"modconf",
"block",
"keyboard",
@ -176,8 +177,6 @@ def find_initcpio_features(partitions, root_mount_point):
hooks.append("bootsplash-{!s}".format(bootsplash_theme))
for partition in partitions:
hooks.extend(["filesystems"])
if partition["fs"] == "linuxswap" and not partition.get("claimed", None):
# Skip foreign swap
continue
@ -225,9 +224,11 @@ def find_initcpio_features(partitions, root_mount_point):
hooks.append("zfs")
if swap_uuid != "":
hooks.extend(["resume"])
if encrypt_hook and openswap_hook:
hooks.extend(["openswap"])
hooks.extend(["resume", "filesystems"])
else:
hooks.extend(["filesystems"])
if uses_btrfs:
modules.append("crc32c-intel" if cpuinfo().is_intel else "crc32c")

View File

@ -294,13 +294,13 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun
# Mount the subvolumes
swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap")
for s in btrfs_subvolumes:
mount_option = "subvol={}".format(s['subvolume'])
if s['subvolume'] == swap_subvol:
mount_option += "," + get_mount_options("btrfs_swap", mount_options, partition)
mount_option_no_subvol = get_mount_options("btrfs_swap", mount_options, partition)
else:
mount_option += "," + get_mount_options(fstype, mount_options, partition)
mount_option_no_subvol = get_mount_options(fstype, mount_options, partition)
mount_option = f"subvol={s['subvolume']},{mount_option_no_subvol}"
subvolume_mountpoint = mount_point[:-1] + s['mountPoint']
mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option})
mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option_no_subvol})
if libcalamares.utils.mount(device,
subvolume_mountpoint,
fstype,
@ -308,6 +308,14 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun
libcalamares.utils.warning("Cannot mount {}".format(device))
def enable_swap_partition(devices):
try:
for d in devices:
libcalamares.utils.host_env_process_output(["swapon", d])
except subprocess.CalledProcessError:
libcalamares.utils.warning(f"Failed to enable swap for devices: {devices}")
def run():
"""
Mount all the partitions from GlobalStorage and from the job configuration.
@ -321,6 +329,11 @@ def run():
return (_("Configuration Error"),
_("No partitions are defined for <pre>{!s}</pre> to use.").format("mount"))
# Find existing swap partitions that are part of the installation and enable them now
swap_devices = [p['device'] for p in partitions if (p['fs'] == 'linuxswap' and p['claimed'])]
enable_swap_partition(swap_devices)
root_mount_point = tempfile.mkdtemp(prefix="calamares-root-")
# Get the mountOptions, if this is None, that is OK and will be handled later

View File

@ -188,7 +188,7 @@ PackageModel::flags( const QModelIndex& index ) const
if ( index.column() == NameColumn )
{
PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() );
if ( item->isImmutable() )
if ( item->isImmutable() || item->isNoncheckable() )
{
return QAbstractItemModel::flags( index ); //Qt::NoItemFlags;
}

View File

@ -49,6 +49,7 @@ PackageTreeItem::PackageTreeItem( const QString& packageName, PackageTreeItem* p
, m_isGroup( false )
, m_isCritical( parent ? parent->isCritical() : false )
, m_showReadOnly( parent ? parent->isImmutable() : false )
, m_showNoncheckable( false )
{
}
@ -60,6 +61,7 @@ PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, PackageTag&& par
, m_isGroup( false )
, m_isCritical( parent.parent ? parent.parent->isCritical() : false )
, m_showReadOnly( parent.parent ? parent.parent->isImmutable() : false )
, m_showNoncheckable( false )
{
}
@ -75,6 +77,7 @@ PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, GroupTag&& paren
, m_isCritical( parentCriticality( groupData, parent.parent ) )
, m_isHidden( CalamaresUtils::getBool( groupData, "hidden", false ) )
, m_showReadOnly( CalamaresUtils::getBool( groupData, "immutable", false ) )
, m_showNoncheckable( CalamaresUtils::getBool( groupData, "noncheckable", false ) )
, m_startExpanded( CalamaresUtils::getBool( groupData, "expanded", false ) )
{
}

View File

@ -109,6 +109,13 @@ public:
*/
bool isImmutable() const { return m_showReadOnly; }
/** @brief Is this a non-checkable item?
*
* Groups can be non-checkable: then you can't toggle the selected
* state of the group. This does not affect subgroups or packages.
*/
bool isNoncheckable() const { return m_showNoncheckable; }
/** @brief is this item selected?
*
* Groups may be partially selected; packages are only on or off.
@ -165,6 +172,7 @@ private:
bool m_isCritical = false;
bool m_isHidden = false;
bool m_showReadOnly = false;
bool m_showNoncheckable = false;
bool m_startExpanded = false;
};

View File

@ -183,6 +183,9 @@ label:
# really only makes sense in combination with *selected* set to true,
# so that the packages will be installed. (Setting a group to immutable
# can be seen as removing it from the user-interface.)
# - *noncheckable*: if true, the entire group cannot be selected or
# deselected by a single click. This does not affect any subgroups
# or child packages
# - *expanded*: if true, the group is shown in an expanded form (that is,
# not-collapsed) in the treeview on start. This only affects the user-
# interface. Only top-level groups are show expanded-initially.

View File

@ -33,6 +33,7 @@ definitions:
selected: { type: boolean }
critical: { type: boolean, default: false }
immutable: { type: boolean }
noncheckable: { type: boolean }
expanded: { type: boolean }
subgroups:
type: array

View File

@ -282,12 +282,12 @@ class PMDnf(PackageManager):
backend = "dnf"
def install(self, pkgs, from_local=False):
check_target_env_call(["dnf", "-y", "install"] + pkgs)
check_target_env_call(["dnf-3", "-y", "install"] + pkgs)
def remove(self, pkgs):
# ignore the error code for now because dnf thinks removing a
# nonexistent package is an error
target_env_call(["dnf", "--disablerepo=*", "-C", "-y",
target_env_call(["dnf-3", "--disablerepo=*", "-C", "-y",
"remove"] + pkgs)
def update_db(self):
@ -295,7 +295,7 @@ class PMDnf(PackageManager):
pass
def update_system(self):
check_target_env_call(["dnf", "-y", "upgrade"])
check_target_env_call(["dnf-3", "-y", "upgrade"])
class PMDummy(PackageManager):

View File

@ -88,7 +88,6 @@ if(KPMcore_FOUND)
gui/PartitionSplitterWidget.cpp
gui/ResizeVolumeGroupDialog.cpp
gui/ScanningDialog.cpp
gui/ReplaceWidget.cpp
gui/VolumeGroupBaseDialog.cpp
jobs/AutoMountManagementJob.cpp
jobs/ChangeFilesystemLabelJob.cpp
@ -113,7 +112,6 @@ if(KPMcore_FOUND)
gui/EditExistingPartitionDialog.ui
gui/EncryptWidget.ui
gui/PartitionPage.ui
gui/ReplaceWidget.ui
gui/VolumeGroupBaseDialog.ui
LINK_PRIVATE_LIBRARIES
calamares::kpmcore

View File

@ -25,23 +25,52 @@ Config::Config( QObject* parent )
const NamedEnumTable< Config::InstallChoice >&
Config::installChoiceNames()
{
static const NamedEnumTable< InstallChoice > names { { QStringLiteral( "none" ), InstallChoice::NoChoice },
{ QStringLiteral( "nochoice" ), InstallChoice::NoChoice },
{ QStringLiteral( "alongside" ), InstallChoice::Alongside },
{ QStringLiteral( "erase" ), InstallChoice::Erase },
{ QStringLiteral( "replace" ), InstallChoice::Replace },
{ QStringLiteral( "manual" ), InstallChoice::Manual } };
// *INDENT-OFF*
// clang-format off
static const NamedEnumTable< InstallChoice > names {
{ QStringLiteral( "none" ), InstallChoice::NoChoice },
{ QStringLiteral( "nochoice" ), InstallChoice::NoChoice },
{ QStringLiteral( "alongside" ), InstallChoice::Alongside },
{ QStringLiteral( "erase" ), InstallChoice::Erase },
{ QStringLiteral( "replace" ), InstallChoice::Replace },
{ QStringLiteral( "manual" ), InstallChoice::Manual },
};
// clang-format on
// *INDENT-ON*
return names;
}
const NamedEnumTable< Config::SwapChoice >&
Config::swapChoiceNames()
{
static const NamedEnumTable< SwapChoice > names { { QStringLiteral( "none" ), SwapChoice::NoSwap },
{ QStringLiteral( "small" ), SwapChoice::SmallSwap },
{ QStringLiteral( "suspend" ), SwapChoice::FullSwap },
{ QStringLiteral( "reuse" ), SwapChoice::ReuseSwap },
{ QStringLiteral( "file" ), SwapChoice::SwapFile } };
// *INDENT-OFF*
// clang-format off
static const NamedEnumTable< SwapChoice > names {
{ QStringLiteral( "none" ), SwapChoice::NoSwap },
{ QStringLiteral( "small" ), SwapChoice::SmallSwap },
{ QStringLiteral( "suspend" ), SwapChoice::FullSwap },
{ QStringLiteral( "reuse" ), SwapChoice::ReuseSwap },
{ QStringLiteral( "file" ), SwapChoice::SwapFile },
};
// clang-format on
// *INDENT-ON*
return names;
}
const NamedEnumTable< Config::LuksGeneration >&
Config::luksGenerationNames()
{
// *INDENT-OFF*
// clang-format off
static const NamedEnumTable< LuksGeneration > names {
{ QStringLiteral( "luks1" ), LuksGeneration::Luks1 },
{ QStringLiteral( "luks" ), LuksGeneration::Luks1 },
{ QStringLiteral( "luks2" ), LuksGeneration::Luks2 },
};
// clang-format on
// *INDENT-ON*
return names;
}
@ -213,7 +242,7 @@ Config::setSwapChoice( Config::SwapChoice c )
void
Config::setEraseFsTypeChoice( const QString& choice )
{
QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr );
const QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr );
if ( canonicalChoice != m_eraseFsTypeChoice )
{
m_eraseFsTypeChoice = canonicalChoice;
@ -221,6 +250,17 @@ Config::setEraseFsTypeChoice( const QString& choice )
}
}
void
Config::setReplaceFilesystemChoice( const QString& filesystemName )
{
const QString canonicalChoice = PartUtils::canonicalFilesystemName( filesystemName, nullptr );
if ( canonicalChoice != m_replaceFileSystemChoice )
{
m_replaceFileSystemChoice = canonicalChoice;
Q_EMIT replaceModeFilesystemChanged( canonicalChoice );
}
}
bool
Config::acceptPartitionTableType( PartitionTable::TableType tableType ) const
{
@ -252,8 +292,8 @@ fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configu
gs->insert( "efiSystemPartitionSize_i", part_size.toBytes() );
// Assign long long int to long unsigned int to prevent compilation warning
size_t unsigned_part_size = part_size.toBytes();
if ( unsigned_part_size != PartUtils::efiFilesystemMinimumSize() )
auto byte_part_size = part_size.toBytes();
if ( byte_part_size != PartUtils::efiFilesystemMinimumSize() )
{
cWarning() << "EFI partition size" << sizeString << "has been adjusted to"
<< PartUtils::efiFilesystemMinimumSize() << "bytes";
@ -323,13 +363,26 @@ Config::fillConfigurationFSTypes( const QVariantMap& configurationMap )
}
}
// Set LUKS file system based on luksGeneration provided, defaults to 'luks'.
bool nameFound = false;
Config::LuksGeneration luksGeneration
= luksGenerationNames().find( CalamaresUtils::getString( configurationMap, "luksGeneration" ), nameFound );
if ( !nameFound )
{
cWarning() << "Partition-module setting *luksGeneration* not found or invalid. Defaulting to luks1.";
luksGeneration = Config::LuksGeneration::Luks1;
}
m_luksFileSystemType = luksGeneration;
gs->insert( "luksFileSystemType", luksGenerationNames().find( luksGeneration ) );
Q_ASSERT( !m_eraseFsTypes.isEmpty() );
Q_ASSERT( m_eraseFsTypes.contains( fsRealName ) );
m_eraseFsTypeChoice = fsRealName;
m_replaceFileSystemChoice = fsRealName;
Q_EMIT eraseModeFilesystemChanged( m_eraseFsTypeChoice );
Q_EMIT replaceModeFilesystemChanged( m_replaceFileSystemChoice );
}
void
Config::setConfigurationMap( const QVariantMap& configurationMap )
{
@ -356,6 +409,8 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
}
setSwapChoice( m_initialSwapChoice );
m_allowZfsEncryption = CalamaresUtils::getBool( configurationMap, "allowZfsEncryption", true );
m_allowManualPartitioning = CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true );
m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" );

View File

@ -31,6 +31,9 @@ class Config : public QObject
Q_PROPERTY(
QString eraseModeFilesystem READ eraseFsType WRITE setEraseFsTypeChoice NOTIFY eraseModeFilesystemChanged )
Q_PROPERTY( QString replaceModeFilesystem READ replaceModeFilesystem WRITE setReplaceFilesystemChoice NOTIFY
replaceModeFilesystemChanged )
Q_PROPERTY( bool allowManualPartitioning READ allowManualPartitioning CONSTANT FINAL )
public:
@ -63,6 +66,15 @@ public:
using EraseFsTypesSet = QStringList;
/** @brief Choice of LUKS disk encryption generation */
enum class LuksGeneration
{
Luks1, // First generation of LUKS
Luks2, // Second generation of LUKS, default since cryptsetup >= 2.1.0
};
Q_ENUM( LuksGeneration )
static const NamedEnumTable< LuksGeneration >& luksGenerationNames();
void setConfigurationMap( const QVariantMap& );
/** @brief Set GS values where other modules configuration has priority
*
@ -122,6 +134,9 @@ public:
*/
QString eraseFsType() const { return m_eraseFsTypeChoice; }
/// @brief Currently-selected FS type for *replace* mode
QString replaceModeFilesystem() const { return m_replaceFileSystemChoice; }
/** @brief Configured default FS type (for other modes than erase)
*
* This is not "Unknown" or "Unformatted"
@ -140,33 +155,44 @@ public:
/// @brief Returns list of acceptable types. May be empty.
QStringList partitionTableTypes() const { return m_requiredPartitionTableType; }
/** @brief The configured LUKS generation (1 or 2)
*/
LuksGeneration luksFileSystemType() const { return m_luksFileSystemType; }
/// @brief If zfs encryption should be allowed
bool allowZfsEncryption() const { return m_allowZfsEncryption; }
public Q_SLOTS:
void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice
void setInstallChoice( InstallChoice );
void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice
void setSwapChoice( SwapChoice );
void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem
void setReplaceFilesystemChoice( const QString& filesystemName );
Q_SIGNALS:
void installChoiceChanged( InstallChoice );
void swapChoiceChanged( SwapChoice );
void eraseModeFilesystemChanged( const QString& );
void replaceModeFilesystemChanged( const QString& );
private:
/** @brief Handle FS-type configuration, for erase and default */
void fillConfigurationFSTypes( const QVariantMap& configurationMap );
EraseFsTypesSet m_eraseFsTypes;
QString m_eraseFsTypeChoice;
QString m_replaceFileSystemChoice;
FileSystem::Type m_defaultFsType;
SwapChoiceSet m_swapChoices;
SwapChoice m_initialSwapChoice = NoSwap;
SwapChoice m_swapChoice = NoSwap;
LuksGeneration m_luksFileSystemType = LuksGeneration::Luks1;
InstallChoice m_initialInstallChoice = NoChoice;
InstallChoice m_installChoice = NoChoice;
qreal m_requiredStorageGiB = 0.0; // May duplicate setting in the welcome module
QStringList m_requiredPartitionTableType;
bool m_allowZfsEncryption = true;
bool m_allowManualPartitioning = true;
};

View File

@ -447,7 +447,6 @@ PartitionViewStep::onActivate()
{
m_choicePage->applyActionChoice( Config::InstallChoice::Alongside );
// m_choicePage->reset();
//FIXME: ReplaceWidget should be reset maybe?
}
}
@ -616,7 +615,9 @@ PartitionViewStep::onLeave()
// If the root partition is encrypted, and there's a separate boot
// partition which is not encrypted
if ( root_p->fileSystem().type() == FileSystem::Luks && boot_p->fileSystem().type() != FileSystem::Luks )
if ( ( root_p->fileSystem().type() == FileSystem::Luks && boot_p->fileSystem().type() != FileSystem::Luks )
|| ( root_p->fileSystem().type() == FileSystem::Luks2
&& boot_p->fileSystem().type() != FileSystem::Luks2 ) )
{
message = tr( "Boot partition not encrypted" );
description = tr( "A separate boot partition was set up together with "

View File

@ -98,7 +98,7 @@ colorForPartition( Partition* partition )
if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
&& !partition->fileSystem().uuid().isEmpty() )
{
if ( partition->fileSystem().type() == FileSystem::Luks )
if ( partition->fileSystem().type() == FileSystem::Luks || partition->fileSystem().type() == FileSystem::Luks2 )
{
FS::luks& luksFs = dynamic_cast< FS::luks& >( partition->fileSystem() );
if ( !luksFs.outerUuid().isEmpty() && s_partitionColorsCache.contains( luksFs.outerUuid() ) )
@ -146,7 +146,7 @@ colorForPartition( Partition* partition )
if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
&& !partition->fileSystem().uuid().isEmpty() )
{
if ( partition->fileSystem().type() == FileSystem::Luks )
if ( partition->fileSystem().type() == FileSystem::Luks || partition->fileSystem().type() == FileSystem::Luks2 )
{
FS::luks& luksFs = dynamic_cast< FS::luks& >( partition->fileSystem() );
if ( !luksFs.outerUuid().isEmpty() )

View File

@ -84,6 +84,7 @@ createNewEncryptedPartition( PartitionNode* parent,
const QString& fsLabel,
qint64 firstSector,
qint64 lastSector,
Config::LuksGeneration luksFsType,
const QString& passphrase,
PartitionTable::Flags flags )
{
@ -93,8 +94,10 @@ createNewEncryptedPartition( PartitionNode* parent,
newRoles |= PartitionRole::Luks;
}
FileSystem::Type luksType = luksGenerationToFSName( luksFsType );
FS::luks* fs = dynamic_cast< FS::luks* >(
FileSystemFactory::create( FileSystem::Luks, firstSector, lastSector, device.logicalSize() ) );
FileSystemFactory::create( luksType, firstSector, lastSector, device.logicalSize() ) );
if ( !fs )
{
cError() << "cannot create LUKS filesystem. Giving up.";
@ -296,6 +299,24 @@ cryptVersion( Partition* partition )
return luksVersion;
}
FileSystem::Type
luksGenerationToFSName( Config::LuksGeneration luksGeneration )
{
// Convert luksGenerationChoice from partition.conf into its
// corresponding file system type from KPMCore.
switch ( luksGeneration )
{
case Config::LuksGeneration::Luks2:
return FileSystem::Type::Luks2;
case Config::LuksGeneration::Luks1:
return FileSystem::Type::Luks;
default:
cWarning() << "luksGeneration not supported, defaulting to \"luks\"";
return FileSystem::Type::Luks;
}
}
Calamares::JobResult
execute( Operation& operation, const QString& failureMessage )
{

View File

@ -11,6 +11,7 @@
#ifndef KPMHELPERS_H
#define KPMHELPERS_H
#include "Config.h"
#include "Job.h"
#include <kpmcore/core/partitiontable.h>
@ -83,6 +84,7 @@ Partition* createNewEncryptedPartition( PartitionNode* parent,
const QString& fsLabel,
qint64 firstSector,
qint64 lastSector,
Config::LuksGeneration luksFsType,
const QString& passphrase,
PartitionTable::Flags flags );
@ -119,6 +121,17 @@ bool cryptLabel( Partition* partition, const QString& label );
*/
int cryptVersion( Partition* partition );
/** @brief Convert a luksGeneration into its FS type for KPMCore.
*
* Will convert Luks1 into FileSystem::Type::luks and Luks2 into
* FileSystem::Type::luks2 for KPMCore partitioning functions.
*
* @return The LUKS FS type (default @c luks )
*/
FileSystem::Type
luksGenerationToFSName( Config::LuksGeneration luksGeneration );
/** @brief Return a result for an @p operation
*
* Executes the operation, and if successful, returns a success result.

View File

@ -169,7 +169,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
lastSectorForRoot -= suggestedSwapSizeB / sectorSize + 1;
}
core->layoutApply( dev, firstFreeSector, lastSectorForRoot, o.luksPassphrase );
core->layoutApply( dev, firstFreeSector, lastSectorForRoot, o.luksFsType, o.luksPassphrase );
if ( shouldCreateSwap )
{
@ -194,6 +194,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
QStringLiteral( "swap" ),
lastSectorForRoot + 1,
dev->totalLogical() - 1,
o.luksFsType,
o.luksPassphrase,
KPM_PARTITION_FLAG( None ) );
}
@ -216,6 +217,12 @@ doReplacePartition( PartitionCoreModule* core, Device* dev, Partition* partition
cDebug() << "doReplacePartition for device" << partition->partitionPath();
// Looking up the defaultFsType (which should name a filesystem type)
// will log an error and set the type to Unknown if there's something wrong.
FileSystem::Type type = FileSystem::Unknown;
PartUtils::canonicalFilesystemName( o.defaultFsType, &type );
core->partitionLayout().setDefaultFsType( type == FileSystem::Unknown ? FileSystem::Ext4 : type );
PartitionRole newRoles( partition->roles() );
if ( partition->roles().has( PartitionRole::Extended ) )
{
@ -244,7 +251,7 @@ doReplacePartition( PartitionCoreModule* core, Device* dev, Partition* partition
core->deletePartition( dev, partition );
}
core->layoutApply( dev, firstSector, lastSector, o.luksPassphrase );
core->layoutApply( dev, firstSector, lastSector, o.luksFsType, o.luksPassphrase );
core->dumpQueue();
}

View File

@ -31,12 +31,17 @@ struct ReplacePartitionOptions
{
QString defaultPartitionTableType; // e.g. "gpt" or "msdos"
QString defaultFsType; // e.g. "ext4" or "btrfs"
Config::LuksGeneration luksFsType = Config::LuksGeneration::Luks1; // optional ("luks", "luks2")
QString luksPassphrase; // optional
ReplacePartitionOptions( const QString& pt, const QString& fs, const QString& luks )
ReplacePartitionOptions( const QString& pt,
const QString& fs,
Config::LuksGeneration luksFs,
const QString& luksPassphrase )
: defaultPartitionTableType( pt )
, defaultFsType( fs )
, luksPassphrase( luks )
, luksFsType( luksFs )
, luksPassphrase( luksPassphrase )
{
}
};
@ -49,11 +54,12 @@ struct AutoPartitionOptions : ReplacePartitionOptions
AutoPartitionOptions( const QString& pt,
const QString& fs,
const QString& luks,
Config::LuksGeneration luksFs,
const QString& luksPassphrase,
const QString& efi,
qint64 requiredBytes,
Config::SwapChoice s )
: ReplacePartitionOptions( pt, fs, luks )
: ReplacePartitionOptions( pt, fs, luksFs, luksPassphrase )
, efiPartitionMountPoint( efi )
, requiredSpaceB( requiredBytes > 0 ? quint64( requiredBytes ) : 0U )
, swap( s )

View File

@ -952,13 +952,14 @@ void
PartitionCoreModule::layoutApply( Device* dev,
qint64 firstSector,
qint64 lastSector,
Config::LuksGeneration luksFsType,
QString luksPassphrase,
PartitionNode* parent,
const PartitionRole& role )
{
bool isEfi = PartUtils::isEfiSystem();
const bool isEfi = PartUtils::isEfiSystem();
QList< Partition* > partList
= m_partLayout.createPartitions( dev, firstSector, lastSector, luksPassphrase, parent, role );
= m_partLayout.createPartitions( dev, firstSector, lastSector, luksFsType, luksPassphrase, parent, role );
// Partition::mountPoint() tells us where it is mounted **now**, while
// PartitionInfo::mountPoint() says where it will be mounted in the target system.
@ -999,10 +1000,19 @@ PartitionCoreModule::layoutApply( Device* dev,
}
void
PartitionCoreModule::layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase )
PartitionCoreModule::layoutApply( Device* dev,
qint64 firstSector,
qint64 lastSector,
Config::LuksGeneration luksFsType,
QString luksPassphrase )
{
layoutApply(
dev, firstSector, lastSector, luksPassphrase, dev->partitionTable(), PartitionRole( PartitionRole::Primary ) );
layoutApply( dev,
firstSector,
lastSector,
luksFsType,
luksPassphrase,
dev->partitionTable(),
PartitionRole( PartitionRole::Primary ) );
}
void

View File

@ -12,6 +12,7 @@
#ifndef PARTITIONCOREMODULE_H
#define PARTITIONCOREMODULE_H
#include "Config.h"
#include "core/KPMHelpers.h"
#include "core/PartitionLayout.h"
#include "core/PartitionModel.h"
@ -166,10 +167,11 @@ public:
*/
PartitionLayout& partitionLayout() { return m_partLayout; }
void layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase );
void layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, Config::LuksGeneration luksFsType, QString luksPassphrase );
void layoutApply( Device* dev,
qint64 firstSector,
qint64 lastSector,
Config::LuksGeneration luksFsType,
QString luksPassphrase,
PartitionNode* parent,
const PartitionRole& role );

View File

@ -204,6 +204,7 @@ QList< Partition* >
PartitionLayout::createPartitions( Device* dev,
qint64 firstSector,
qint64 lastSector,
Config::LuksGeneration luksFsType,
QString luksPassphrase,
PartitionNode* parent,
const PartitionRole& role )
@ -317,6 +318,7 @@ PartitionLayout::createPartitions( Device* dev,
entry.partLabel,
currentSector,
currentSector + sectors - 1,
luksFsType,
luksPassphrase,
KPM_PARTITION_FLAG( None ) );
}

View File

@ -11,9 +11,9 @@
#ifndef PARTITIONLAYOUT_H
#define PARTITIONLAYOUT_H
#include "partition/PartitionSize.h"
#include "Config.h"
#include "core/PartUtils.h"
#include "partition/PartitionSize.h"
// KPMcore
#include <kpmcore/core/partitiontable.h>
@ -116,6 +116,7 @@ public:
QList< Partition* > createPartitions( Device* dev,
qint64 firstSector,
qint64 lastSector,
Config::LuksGeneration luksFsType,
QString luksPassphrase,
PartitionNode* parent,
const PartitionRole& role );

View File

@ -4,6 +4,7 @@
* SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2019 Collabora Ltd
* SPDX-FileCopyrightText: 2021 Anubhav Choudhary <ac.10edu@gmail.com>
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
@ -28,7 +29,6 @@
#include "gui/PartitionBarsView.h"
#include "gui/PartitionLabelsView.h"
#include "gui/PartitionSplitterWidget.h"
#include "gui/ReplaceWidget.h"
#include "gui/ScanningDialog.h"
#include "Branding.h"
@ -288,6 +288,16 @@ ChoicePage::setupChoices()
m_eraseFsTypesChoiceComboBox, &QComboBox::currentTextChanged, m_config, &Config::setEraseFsTypeChoice );
connect( m_config, &Config::eraseModeFilesystemChanged, this, &ChoicePage::onActionChanged );
m_eraseButton->addOptionsComboBox( m_eraseFsTypesChoiceComboBox );
// Also offer it for "replace
m_replaceFsTypesChoiceComboBox = new QComboBox;
m_replaceFsTypesChoiceComboBox->addItems( m_config->eraseFsTypes() );
connect( m_replaceFsTypesChoiceComboBox,
&QComboBox::currentTextChanged,
m_config,
&Config::setReplaceFilesystemChoice );
connect( m_config, &Config::replaceModeFilesystemChanged, this, &ChoicePage::onActionChanged );
m_replaceButton->addOptionsComboBox( m_replaceFsTypesChoiceComboBox );
}
m_itemsLayout->addWidget( m_alongsideButton );
@ -303,13 +313,8 @@ ChoicePage::setupChoices()
m_itemsLayout->addStretch();
#if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) )
auto buttonSignal = QOverload< int, bool >::of( &QButtonGroup::buttonToggled );
#else
auto buttonSignal = &QButtonGroup::idToggled;
#endif
connect( m_grp,
buttonSignal,
&QButtonGroup::idToggled,
this,
[ this ]( int id, bool checked )
{
@ -449,7 +454,6 @@ ChoicePage::continueApplyDeviceChoice()
if ( m_lastSelectedDeviceIndex != m_drivesCombo->currentIndex() )
{
m_lastSelectedDeviceIndex = m_drivesCombo->currentIndex();
m_lastSelectedActionIndex = -1;
m_config->setInstallChoice( m_config->initialInstallChoice() );
checkInstallChoiceRadioButton( m_config->installChoice() );
}
@ -461,6 +465,18 @@ ChoicePage::continueApplyDeviceChoice()
void
ChoicePage::onActionChanged()
{
if ( m_enableEncryptionWidget )
{
if ( m_config->installChoice() == InstallChoice::Erase && m_eraseFsTypesChoiceComboBox )
{
m_encryptWidget->setFilesystem( FileSystem::typeForName( m_eraseFsTypesChoiceComboBox->currentText() ) );
}
else if ( m_config->installChoice() == InstallChoice::Replace && m_replaceFsTypesChoiceComboBox )
{
m_encryptWidget->setFilesystem( FileSystem::typeForName( m_replaceFsTypesChoiceComboBox->currentText() ) );
}
}
Device* currd = selectedDevice();
if ( currd )
{
@ -483,9 +499,9 @@ ChoicePage::onEraseSwapChoiceChanged()
void
ChoicePage::applyActionChoice( InstallChoice choice )
{
cDebug() << "Prev" << m_lastSelectedActionIndex << "InstallChoice" << choice
<< Config::installChoiceNames().find( choice );
cDebug() << "InstallChoice" << choice << Config::installChoiceNames().find( choice );
m_beforePartitionBarsView->selectionModel()->disconnect( SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ) );
auto priorSelection = m_beforePartitionBarsView->selectionModel()->currentIndex();
m_beforePartitionBarsView->selectionModel()->clearSelection();
m_beforePartitionBarsView->selectionModel()->clearCurrentIndex();
@ -496,6 +512,7 @@ ChoicePage::applyActionChoice( InstallChoice choice )
auto gs = Calamares::JobQueue::instance()->globalStorage();
PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(),
m_config->eraseFsType(),
m_config->luksFileSystemType(),
m_encryptWidget->passphrase(),
gs->value( "efiSystemPartition" ).toString(),
CalamaresUtils::GiBtoBytes(
@ -543,6 +560,12 @@ ChoicePage::applyActionChoice( InstallChoice choice )
this,
SLOT( onPartitionToReplaceSelected( QModelIndex, QModelIndex ) ),
Qt::UniqueConnection );
// Maintain the selection for replace
if ( priorSelection.isValid() )
{
m_beforePartitionBarsView->selectionModel()->setCurrentIndex( priorSelection, QItemSelectionModel::Select );
}
break;
case InstallChoice::Alongside:
@ -752,6 +775,7 @@ ChoicePage::doAlongsideApply()
m_core->layoutApply( dev,
newLastSector + 2,
oldLastSector,
m_config->luksFileSystemType(),
m_encryptWidget->passphrase(),
candidate->parent(),
candidate->roles() );
@ -826,6 +850,7 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
m_core->layoutApply( selectedDevice(),
selectedPartition->firstSector(),
selectedPartition->lastSector(),
m_config->luksFileSystemType(),
m_encryptWidget->passphrase(),
newParent,
newRoles );
@ -857,7 +882,8 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
selectedDevice(),
selectedPartition,
{ gs->value( "defaultPartitionType" ).toString(),
gs->value( "defaultFileSystemType" ).toString(),
m_config->replaceModeFilesystem(),
m_config->luksFileSystemType(),
m_encryptWidget->passphrase() } );
Partition* homePartition = findPartitionByPath( { selectedDevice() }, *homePartitionPath );
@ -1731,10 +1757,20 @@ ChoicePage::createBootloaderPanel()
return panelWidget;
}
bool ChoicePage::shouldShowEncryptWidget( Config::InstallChoice choice ) const
bool
ChoicePage::shouldShowEncryptWidget( Config::InstallChoice choice ) const
{
// If there are any choices for FS, check it's not ZFS because that doesn't
// support the kind of encryption we enable here.
const bool suitableFS = m_eraseFsTypesChoiceComboBox ? m_eraseFsTypesChoiceComboBox->currentText() != "zfs" : true;
return (choice == InstallChoice::Erase) && m_enableEncryptionWidget && suitableFS;
bool suitableFS = true;
if ( !m_config->allowZfsEncryption()
&& ( ( m_eraseFsTypesChoiceComboBox && m_eraseFsTypesChoiceComboBox->isVisible()
&& m_eraseFsTypesChoiceComboBox->currentText() == "zfs" )
|| ( m_replaceFsTypesChoiceComboBox && m_replaceFsTypesChoiceComboBox->isVisible()
&& m_replaceFsTypesChoiceComboBox->currentText() == "zfs" ) ) )
{
suitableFS = false;
}
const bool suitableChoice
= choice == InstallChoice::Erase || choice == InstallChoice::Alongside || choice == InstallChoice::Replace;
return suitableChoice && m_enableEncryptionWidget && suitableFS;
}

View File

@ -3,6 +3,7 @@
* SPDX-FileCopyrightText: 2014-2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2019 Collabora Ltd
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
@ -152,6 +153,7 @@ private:
Calamares::Widgets::PrettyRadioButton* m_somethingElseButton;
QComboBox* m_eraseSwapChoiceComboBox = nullptr; // UI, see also Config's swap choice
QComboBox* m_eraseFsTypesChoiceComboBox = nullptr; // UI, see also Config's erase-mode FS
QComboBox* m_replaceFsTypesChoiceComboBox = nullptr; // UI, see also Config's erase-mode FS
DeviceInfoWidget* m_deviceInfoWidget;
@ -166,7 +168,6 @@ private:
QPointer< QComboBox > m_efiComboBox;
int m_lastSelectedDeviceIndex = -1;
int m_lastSelectedActionIndex = -1;
bool m_enableEncryptionWidget = false;

View File

@ -33,6 +33,7 @@
#include <kpmcore/fs/filesystem.h>
#include <kpmcore/fs/filesystemfactory.h>
#include <kpmcore/fs/luks.h>
#include <kpmcore/fs/luks2.h>
#include <QComboBox>
#include <QDir>
@ -223,6 +224,8 @@ CreatePartitionDialog::initGptPartitionTypeUi()
Partition*
CreatePartitionDialog::getNewlyCreatedPartition()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( m_role.roles() == PartitionRole::None )
{
m_role = PartitionRole( m_ui->extendedRadioButton->isChecked() ? PartitionRole::Extended
@ -242,12 +245,21 @@ CreatePartitionDialog::getNewlyCreatedPartition()
// newFlags() and the consumer (see PartitionPage::onCreateClicked)
// does so, to set up the partition for create-and-then-set-flags.
Partition* partition = nullptr;
QString luksFsType = gs->value( "luksFileSystemType" ).toString();
QString luksPassphrase = m_ui->encryptWidget->passphrase();
if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty()
&& fsType != FileSystem::Zfs )
{
partition = KPMHelpers::createNewEncryptedPartition(
m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() );
partition = KPMHelpers::createNewEncryptedPartition( m_parent,
*m_device,
m_role,
fsType,
fsLabel,
first,
last,
Config::luksGenerationNames().find(luksFsType, Config::LuksGeneration::Luks1),
luksPassphrase,
PartitionTable::Flags() );
}
else
{
@ -308,6 +320,12 @@ CreatePartitionDialog::updateMountPointUi()
m_ui->encryptWidget->show();
m_ui->encryptWidget->reset();
}
else if ( FileSystemFactory::map()[ FileSystem::Type::Luks2 ]->supportCreate()
&& FS::luks2::canEncryptType( type ) && !m_role.has( PartitionRole::Extended ) )
{
m_ui->encryptWidget->show();
m_ui->encryptWidget->reset();
}
else
{
m_ui->encryptWidget->reset();

View File

@ -2,6 +2,7 @@
*
* SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
@ -17,6 +18,8 @@
#include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h"
constexpr int ZFS_MIN_LENGTH = 8;
/** @brief Does this system support whole-disk encryption?
*
* Returns @c true if the system is likely to support encryption
@ -143,7 +146,7 @@ applyPixmap( QLabel* label, CalamaresUtils::ImageType pixmap )
}
void
EncryptWidget::updateState()
EncryptWidget::updateState( const bool notify )
{
if ( m_ui->m_passphraseLineEdit->isVisible() )
{
@ -155,6 +158,11 @@ EncryptWidget::updateState()
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusWarning );
m_ui->m_iconLabel->setToolTip( tr( "Please enter the same passphrase in both boxes." ) );
}
else if ( m_filesystem == FileSystem::Zfs && p1.length() < ZFS_MIN_LENGTH )
{
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusError );
m_ui->m_iconLabel->setToolTip( tr( "Password must be a minimum of %1 characters" ).arg( ZFS_MIN_LENGTH ) );
}
else if ( p1 == p2 )
{
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusOk );
@ -172,7 +180,10 @@ EncryptWidget::updateState()
if ( newState != m_state )
{
m_state = newState;
Q_EMIT stateChanged( m_state );
if ( notify )
{
Q_EMIT stateChanged( m_state );
}
}
}
@ -201,3 +212,13 @@ EncryptWidget::onCheckBoxStateChanged( int checked )
updateState();
}
void
EncryptWidget::setFilesystem( const FileSystem::Type fs )
{
m_filesystem = fs;
if ( m_state != Encryption::Disabled )
{
updateState( false );
}
}

View File

@ -2,6 +2,7 @@
*
* SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
@ -14,6 +15,8 @@
#include <QWidget>
#include <kpmcore/fs/filesystem.h>
namespace Ui
{
class EncryptWidget;
@ -38,6 +41,12 @@ public:
Encryption state() const;
void setText( const QString& text );
/**
* @brief setFilesystem sets the filesystem name used for password validation
* @param fs A QString containing the name of the filesystem
*/
void setFilesystem( const FileSystem::Type fs );
QString passphrase() const;
void retranslate();
@ -46,12 +55,14 @@ signals:
void stateChanged( Encryption );
private:
void updateState();
void updateState( const bool notify = true );
void onPassphraseEdited();
void onCheckBoxStateChanged( int checked );
Ui::EncryptWidget* m_ui;
Encryption m_state;
FileSystem::Type m_filesystem;
};
#endif // ENCRYPTWIDGET_H

View File

@ -1,393 +0,0 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
* SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "ReplaceWidget.h"
#include "ui_ReplaceWidget.h"
#include "core/DeviceModel.h"
#include "core/PartitionActions.h"
#include "core/PartitionCoreModule.h"
#include "core/PartitionInfo.h"
#include "Branding.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "partition/FileSystem.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h"
#include <kpmcore/core/partition.h>
#include <kpmcore/fs/filesystem.h>
#include <QComboBox>
using CalamaresUtils::Partition::untranslatedFS;
using CalamaresUtils::Partition::userVisibleFS;
ReplaceWidget::ReplaceWidget( PartitionCoreModule* core, QComboBox* devicesComboBox, QWidget* parent )
: QWidget( parent )
, m_ui( new Ui_ReplaceWidget )
, m_core( core )
, m_isEfi( false )
{
m_ui->setupUi( this );
m_ui->bootComboBox->hide();
m_ui->bootComboBox->clear();
m_ui->bootStatusLabel->hide();
m_ui->bootStatusLabel->clear();
updateFromCurrentDevice( devicesComboBox );
connect( devicesComboBox,
&QComboBox::currentTextChanged,
this,
[ = ]( const QString& /* text */ ) { updateFromCurrentDevice( devicesComboBox ); } );
CALAMARES_RETRANSLATE( onPartitionSelected(); );
}
ReplaceWidget::~ReplaceWidget() {}
bool
ReplaceWidget::isNextEnabled() const
{
return m_nextEnabled;
}
void
ReplaceWidget::reset()
{
//moo;
}
void
ReplaceWidget::applyChanges()
{
auto gs = Calamares::JobQueue::instance()->globalStorage();
PartitionModel* model = qobject_cast< PartitionModel* >( m_ui->partitionTreeView->model() );
if ( model )
{
Partition* partition = model->partitionForIndex( m_ui->partitionTreeView->currentIndex() );
if ( partition )
{
Device* dev = model->device();
PartitionActions::doReplacePartition( m_core,
dev,
partition,
{ gs->value( "defaultPartitionTableType" ).toString(),
gs->value( "defaultFileSystemType" ).toString(),
QString() } );
if ( m_isEfi )
{
QList< Partition* > efiSystemPartitions = m_core->efiSystemPartitions();
if ( efiSystemPartitions.count() == 1 )
{
PartitionInfo::setMountPoint( efiSystemPartitions.first(),
gs->value( "efiSystemPartition" ).toString() );
}
else if ( efiSystemPartitions.count() > 1 )
{
PartitionInfo::setMountPoint( efiSystemPartitions.at( m_ui->bootComboBox->currentIndex() ),
gs->value( "efiSystemPartition" ).toString() );
}
}
m_core->dumpQueue();
}
}
}
void
ReplaceWidget::onPartitionSelected()
{
if ( Calamares::JobQueue::instance()->globalStorage()->value( "firmwareType" ) == "efi" )
{
m_isEfi = true;
}
const auto* branding = Calamares::Branding::instance();
if ( m_ui->partitionTreeView->currentIndex() == QModelIndex() )
{
updateStatus( CalamaresUtils::PartitionPartition,
tr( "Select where to install %1.<br/>"
"<font color=\"red\">Warning: </font>this will delete all files "
"on the selected partition." )
.arg( branding->versionedName() ) );
setNextEnabled( false );
return;
}
bool ok = false;
double requiredSpaceB
= Calamares::JobQueue::instance()->globalStorage()->value( "requiredStorageGiB" ).toDouble( &ok ) * 1024 * 1024
* 1024;
PartitionModel* model = qobject_cast< PartitionModel* >( m_ui->partitionTreeView->model() );
if ( model && ok )
{
const QStringList osproberLines
= Calamares::JobQueue::instance()->globalStorage()->value( "osproberLines" ).toStringList();
Partition* partition = model->partitionForIndex( m_ui->partitionTreeView->currentIndex() );
if ( !partition || partition->state() != KPM_PARTITION_STATE( None ) )
{
updateStatus( CalamaresUtils::Fail, tr( "The selected item does not appear to be a valid partition." ) );
setNextEnabled( false );
return;
}
if ( partition->roles().has( PartitionRole::Unallocated ) )
{
updateStatus( CalamaresUtils::Fail,
tr( "%1 cannot be installed on empty space. Please select an "
"existing partition." )
.arg( branding->versionedName() ) );
setNextEnabled( false );
return;
}
if ( partition->roles().has( PartitionRole::Extended ) )
{
updateStatus( CalamaresUtils::Fail,
tr( "%1 cannot be installed on an extended partition. Please select an "
"existing primary or logical partition." )
.arg( branding->versionedName() ) );
setNextEnabled( false );
return;
}
if ( partition->partitionPath().isEmpty() )
{
updateStatus( CalamaresUtils::Fail,
tr( "%1 cannot be installed on this partition." ).arg( branding->versionedName() ) );
setNextEnabled( false );
return;
}
QString fsNameForUser = userVisibleFS( partition->fileSystem() );
QString prettyName = tr( "Data partition (%1)" ).arg( fsNameForUser );
for ( const auto& line : osproberLines )
{
QStringList lineColumns = line.split( ':' );
QString path = lineColumns.value( 0 ).simplified();
if ( path == partition->partitionPath() )
{
QString osName;
if ( !lineColumns.value( 1 ).simplified().isEmpty() )
{
osName = lineColumns.value( 1 ).simplified();
}
else if ( !lineColumns.value( 2 ).simplified().isEmpty() )
{
osName = lineColumns.value( 2 ).simplified();
}
if ( osName.isEmpty() )
{
prettyName = tr( "Unknown system partition (%1)" ).arg( fsNameForUser );
}
else
{
prettyName = tr( "%1 system partition (%2)" )
.arg( osName.replace( 0, 1, osName.at( 0 ).toUpper() ) )
.arg( fsNameForUser );
}
break;
}
}
// The loss of precision is ok; we're not going to fall over from a single byte
if ( static_cast< double >( partition->capacity() ) < requiredSpaceB )
{
updateStatus( CalamaresUtils::Fail,
tr( "<strong>%4</strong><br/><br/>"
"The partition %1 is too small for %2. Please select a partition "
"with capacity at least %3 GiB." )
.arg( partition->partitionPath() )
.arg( branding->versionedName() )
.arg( requiredSpaceB / ( 1024. * 1024. * 1024. ), 0, 'f', 1 )
.arg( prettyName ) );
setNextEnabled( false );
return;
}
m_ui->bootComboBox->hide();
m_ui->bootComboBox->clear();
m_ui->bootStatusLabel->hide();
m_ui->bootStatusLabel->clear();
if ( m_isEfi )
{
QList< Partition* > efiSystemPartitions = m_core->efiSystemPartitions();
if ( efiSystemPartitions.count() == 0 )
{
updateStatus( CalamaresUtils::Fail,
tr( "<strong>%2</strong><br/><br/>"
"An EFI system partition cannot be found anywhere "
"on this system. Please go back and use manual "
"partitioning to set up %1." )
.arg( branding->shortProductName() )
.arg( prettyName ) );
setNextEnabled( false );
}
else if ( efiSystemPartitions.count() == 1 )
{
updateStatus( CalamaresUtils::PartitionPartition,
tr( "<strong>%3</strong><br/><br/>"
"%1 will be installed on %2.<br/>"
"<font color=\"red\">Warning: </font>all data on partition "
"%2 will be lost." )
.arg( branding->versionedName() )
.arg( partition->partitionPath() )
.arg( prettyName ) );
m_ui->bootStatusLabel->show();
m_ui->bootStatusLabel->setText( tr( "The EFI system partition at %1 will be used for starting %2." )
.arg( efiSystemPartitions.first()->partitionPath() )
.arg( branding->shortProductName() ) );
setNextEnabled( true );
}
else
{
updateStatus( CalamaresUtils::PartitionPartition,
tr( "<strong>%3</strong><br/><br/>"
"%1 will be installed on %2.<br/>"
"<font color=\"red\">Warning: </font>all data on partition "
"%2 will be lost." )
.arg( branding->versionedName() )
.arg( partition->partitionPath() )
.arg( prettyName ) );
m_ui->bootStatusLabel->show();
m_ui->bootStatusLabel->setText( tr( "EFI system partition:" ) );
m_ui->bootComboBox->show();
for ( int i = 0; i < efiSystemPartitions.count(); ++i )
{
Partition* efiPartition = efiSystemPartitions.at( i );
m_ui->bootComboBox->addItem( efiPartition->partitionPath(), i );
if ( efiPartition->devicePath() == partition->devicePath() && efiPartition->number() == 1 )
{
m_ui->bootComboBox->setCurrentIndex( i );
}
}
setNextEnabled( true );
}
}
else
{
updateStatus( CalamaresUtils::PartitionPartition,
tr( "<strong>%3</strong><br/><br/>"
"%1 will be installed on %2.<br/>"
"<font color=\"red\">Warning: </font>all data on partition "
"%2 will be lost." )
.arg( branding->versionedName() )
.arg( partition->partitionPath() )
.arg( prettyName ) );
setNextEnabled( true );
}
}
}
void
ReplaceWidget::setNextEnabled( bool enabled )
{
if ( enabled == m_nextEnabled )
{
return;
}
m_nextEnabled = enabled;
Q_EMIT nextStatusChanged( enabled );
}
void
ReplaceWidget::updateStatus( CalamaresUtils::ImageType imageType, const QString& text )
{
int iconSize = CalamaresUtils::defaultFontHeight() * 6;
m_ui->selectedIconLabel->setPixmap(
CalamaresUtils::defaultPixmap( imageType, CalamaresUtils::Original, QSize( iconSize, iconSize ) ) );
m_ui->selectedIconLabel->setFixedHeight( iconSize );
m_ui->selectedStatusLabel->setText( text );
}
void
ReplaceWidget::updateFromCurrentDevice( QComboBox* devicesComboBox )
{
QModelIndex index = m_core->deviceModel()->index( devicesComboBox->currentIndex(), 0 );
if ( !index.isValid() )
{
return;
}
Device* device = m_core->deviceModel()->deviceForIndex( index );
QAbstractItemModel* oldModel = m_ui->partitionTreeView->model();
if ( oldModel )
{
disconnect( oldModel, nullptr, this, nullptr );
}
PartitionModel* model = m_core->partitionModelForDevice( device );
m_ui->partitionTreeView->setModel( model );
m_ui->partitionTreeView->expandAll();
// Must be done here because we need to have a model set to define
// individual column resize mode
QHeaderView* header = m_ui->partitionTreeView->header();
header->setSectionResizeMode( QHeaderView::ResizeToContents );
header->setSectionResizeMode( 0, QHeaderView::Stretch );
//updateButtons();
// Establish connection here because selection model is destroyed when
// model changes
connect( m_ui->partitionTreeView->selectionModel(),
&QItemSelectionModel::currentRowChanged,
this,
&ReplaceWidget::onPartitionViewActivated );
connect( model, &QAbstractItemModel::modelReset, this, &ReplaceWidget::onPartitionModelReset );
}
void
ReplaceWidget::onPartitionViewActivated()
{
QModelIndex index = m_ui->partitionTreeView->currentIndex();
if ( !index.isValid() )
{
return;
}
const PartitionModel* model = static_cast< const PartitionModel* >( index.model() );
Q_ASSERT( model );
Partition* partition = model->partitionForIndex( index );
Q_ASSERT( partition );
onPartitionSelected();
}
void
ReplaceWidget::onPartitionModelReset()
{
m_ui->partitionTreeView->expandAll();
onPartitionSelected();
}

View File

@ -1,61 +0,0 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef REPLACEWIDGET_H
#define REPLACEWIDGET_H
#include "utils/CalamaresUtilsGui.h"
#include <QScopedPointer>
#include <QWidget>
class Ui_ReplaceWidget;
class QComboBox;
class PartitionCoreModule;
class Partition;
class ReplaceWidget : public QWidget
{
Q_OBJECT
public:
explicit ReplaceWidget( PartitionCoreModule* core, QComboBox* devicesComboBox, QWidget* parent = nullptr );
virtual ~ReplaceWidget() override;
bool isNextEnabled() const;
void reset();
void applyChanges();
signals:
void nextStatusChanged( bool );
private slots:
void onPartitionSelected();
private:
QScopedPointer< Ui_ReplaceWidget > m_ui;
void setNextEnabled( bool enabled );
void updateStatus( CalamaresUtils::ImageType imageType, const QString& text );
PartitionCoreModule* m_core;
bool m_nextEnabled;
bool m_isEfi;
void updateFromCurrentDevice( QComboBox* devicesComboBox );
void onPartitionViewActivated();
void onPartitionModelReset();
};
#endif // REPLACEWIDGET_H

View File

@ -1,135 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<author>
SPDX-FileCopyrightText: 2014 Teo Mrnjavac &lt;teo@kde.org&gt;
SPDX-License-Identifier: GPL-3.0-or-later
</author>
<class>ReplaceWidget</class>
<widget class="QWidget" name="ReplaceWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>643</width>
<height>187</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTreeView" name="partitionTreeView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="selectedIconLabel">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="selectedStatusLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="bootStatusLabel">
<property name="toolTip">
<string notr="true"/>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="bootComboBox"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -26,6 +26,7 @@
#include <kpmcore/core/partition.h>
#include <kpmcore/fs/filesystem.h>
#include <kpmcore/fs/luks.h>
#include <kpmcore/fs/luks2.h>
#include <QDebug>
#include <QDir>
@ -91,11 +92,18 @@ mapForPartition( Partition* partition, const QString& uuid )
map[ "parttype" ] = partition->type();
map[ "partattrs" ] = partition->attributes();
map[ "features" ] = partition->fileSystem().features();
if ( partition->fileSystem().type() == FileSystem::Luks
&& dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() )
{
map[ "fs" ] = untranslatedFS( dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() );
}
if ( partition->fileSystem().type() == FileSystem::Luks2
&& dynamic_cast< FS::luks2& >( partition->fileSystem() ).innerFS() )
{
map[ "fs" ] = untranslatedFS( dynamic_cast< FS::luks2& >( partition->fileSystem() ).innerFS() );
}
map[ "uuid" ] = uuid;
map[ "claimed" ] = PartitionInfo::format( partition ); // If we formatted it, it's ours

View File

@ -64,6 +64,30 @@ userSwapChoices:
# ensureSuspendToDisk: true
# neverCreateSwap: false
# This setting specifies the LUKS generation (i.e LUKS1, LUKS2) used internally by
# cryptsetup when creating an encrypted partition.
#
# This option is set to luks1 by default, as grub doesn't support LUKS2 + Argon2id
# currently. On the other hand grub does support LUKS2 with PBKDF2 and could therefore be
# also set to luks2. Also there are some patches for grub and Argon2.
# See: https://aur.archlinux.org/packages/grub-improved-luks2-git
#
# Choices: luks1, luks2 (in addition, "luks" means "luks1")
#
# The default is luks1
#
luksGeneration: luks1
# This setting determines if encryption should be allowed when using zfs. This
# setting has no effect unless zfs support is provided.
#
# This setting is to handle the fact that some bootloaders(such as grub) do not
# support zfs encryption.
#
# The default is true
#
# allowZfsEncryption: true
# Correctly draw nested (e.g. logical) partitions as such.
drawNestedPartitions: false

View File

@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
# SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
---
$schema: https://json-schema.org/schema#
@ -14,13 +15,16 @@ properties:
# ensureSuspendToDisk: { type: boolean, default: true } # Legacy
# neverCreateSwap: { type: boolean, default: false } # Legacy
allowZfsEncryption: { type: boolean, default: true }
drawNestedPartitions: { type: boolean, default: false }
alwaysShowPartitionLabels: { type: boolean, default: true }
defaultFileSystemType: { type: string }
availableFileSystemTypes: { type: array, items: { type: string } }
luksGeneration: { type: string, enum: [luks1, luks2] } # Also allows "luks" as alias of "luks1"
enableLuksAutomatedPartitioning: { type: boolean, default: false }
allowManualPartitioning: { type: boolean, default: true }
partitionLayout: { type: array } # TODO: specify items
initialPartitioningChoice: { type: string, enum: [ none, erase, replace, alongside, manual ] }

View File

@ -63,8 +63,8 @@ CreateLayoutsTests::testFixedSizePartition()
QFAIL( qPrintable( "Unable to create / partition" ) );
}
partitions
= layout.createPartitions( static_cast< Device* >( &dev ), 0, dev.totalLogical(), nullptr, nullptr, role );
partitions = layout.createPartitions(
static_cast< Device* >( &dev ), 0, dev.totalLogical(), Config::LuksGeneration::Luks1, nullptr, nullptr, role );
QCOMPARE( partitions.count(), 1 );
@ -84,8 +84,8 @@ CreateLayoutsTests::testPercentSizePartition()
QFAIL( qPrintable( "Unable to create / partition" ) );
}
partitions
= layout.createPartitions( static_cast< Device* >( &dev ), 0, dev.totalLogical(), nullptr, nullptr, role );
partitions = layout.createPartitions(
static_cast< Device* >( &dev ), 0, dev.totalLogical(), Config::LuksGeneration::Luks1, nullptr, nullptr, role );
QCOMPARE( partitions.count(), 1 );
@ -115,8 +115,8 @@ CreateLayoutsTests::testMixedSizePartition()
QFAIL( qPrintable( "Unable to create /bkup partition" ) );
}
partitions
= layout.createPartitions( static_cast< Device* >( &dev ), 0, dev.totalLogical(), nullptr, nullptr, role );
partitions = layout.createPartitions(
static_cast< Device* >( &dev ), 0, dev.totalLogical(), Config::LuksGeneration::Luks1, nullptr, nullptr, role );
QCOMPARE( partitions.count(), 3 );

View File

@ -37,7 +37,8 @@
#
# - name: "cups.socket"
# action: "disable"
# mandatory: false
# # The property "mandatory" is taken to be false by default here
# # because it is not specified
#
# - name: "graphical"
# action: "enable"

View File

@ -11,4 +11,4 @@
---
# Setting emergency to true will make it so this module is still run
# when a prior module fails
emergency: false
emergency: true

View File

@ -241,6 +241,13 @@ ZfsJob::exec()
}
}
// Generate the zfs hostid file
auto i = system->runCommand( { "zgenhostid" }, std::chrono::seconds( 3 ) );
if ( i.getExitCode() != 0 )
{
cWarning() << "Failed to create /etc/hostid";
}
// Create the zpool
ZfsResult zfsResult;
if ( encrypt )

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# === This file is part of Calamares - <https://calamares.io> ===
#
# SPDX-FileCopyrightText: 2022 Anke Boersma <demm@kaosx.us>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Calamares is Free Software: see the License-Identifier above.
#
import os
import shutil
import libcalamares
from libcalamares.utils import gettext_path, gettext_languages
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Copying zfs generated hostid.")
def run():
zfs = libcalamares.globalstorage.value("zfsDatasets")
root_mount_point = libcalamares.globalstorage.value("rootMountPoint")
if zfs:
hostid_source = '/etc/hostid'
hostid_destination = '{!s}/etc/hostid'.format(root_mount_point)
try:
shutil.copy2(hostid_source, hostid_destination)
except Exception as e:
libcalamares.utils.warning("Could not copy hostid")
return None

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
---
type: "job"
name: "zfshostid"
interface: "python"
script: "main.py"
noconfig: true

View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2022 Anke Boersma <demm@kaosx.us>
# SPDX-License-Identifier: GPL-3.0-or-later
---
$schema: https://json-schema.org/schema#
$id: https://calamares.io/schemas/zfshostid
additionalProperties: false
type: object