[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 # SPDX-License-Identifier: CC0-1.0
[main] [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 file_filter = lang/calamares_<lang>.ts
source_file = lang/calamares_en.ts source_file = lang/calamares_en.ts
source_lang = en source_lang = en
type = QT type = QT
[calamares.fdo] [o:calamares:p:calamares:r:fdo]
file_filter = lang/desktop_<lang>.desktop file_filter = lang/desktop_<lang>.desktop
source_file = calamares.desktop source_file = calamares.desktop
source_lang = en source_lang = en
type = DESKTOP type = DESKTOP
[calamares.python] [o:calamares:p:calamares:r:python]
file_filter = lang/python/<lang>/LC_MESSAGES/python.po file_filter = lang/python/<lang>/LC_MESSAGES/python.po
source_file = lang/python.pot source_file = lang/python.pot
source_lang = en 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): This release contains contributions from (alphabetically by first name):
- Adriaan de Groot - Adriaan de Groot
- Aleksey Samoilov
- Anke Boersma - Anke Boersma
- Emir Sari
- Evan James
- Jeremy Attall
- Johannes Kamprad
- Mario Haustein
- Masato TOYOSHIMA
- Paolo Dongilli
- Peter Jung
- Shivanand
- wiz64
## Core ## ## Core ##
- Incompatible module-configuration changes, see #1438. - Incompatible module-configuration changes, see #1438.
- Branding entries use ${var} instead of @{var} for substitutions, - Branding entries use ${var} instead of @{var} for substitutions,
in line with all the other substitution mechanisms used from C++ in line with all the other substitution mechanisms used from C++
core. See documentation in `branding.desc`. core. See documentation in `branding.desc`.
- Boost::Python requires at least version 1.72 - Boost::Python requires at least version 1.72.
- KDE Frameworks must be version 5.58 or later - 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 ## ## Modules ##
- *dracut* added a configurable kernel name. (thanks Anke) - *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) - *localeq* moved to using Drawer instead of ComboBox in UI. (thanks Anke)
- *keyboardq* 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) # 3.3.0-alpha2 (2022-08-23)

View File

@ -73,7 +73,6 @@ set(CALAMARES_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
### OPTIONS ### OPTIONS
# #
option(INSTALL_CONFIG "Install configuration files" OFF)
option(INSTALL_POLKIT "Install Polkit configuration" ON) option(INSTALL_POLKIT "Install Polkit configuration" ON)
option(INSTALL_COMPLETION "Install shell completions" OFF) option(INSTALL_COMPLETION "Install shell completions" OFF)
# When adding WITH_* that affects the ABI offered by libcalamares, # 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 # Language en (source language) is added later. It isn't listed in
# Transifex either. Get the list of languages and their status # 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. # use ci/txstats.py to automatically check.
# #
# When adding a new language, take care that it is properly loaded # 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 "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_C_FLAGS_DEBUG "-Og -g") set(CMAKE_C_FLAGS_DEBUG "-Og -g")
set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") 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_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 no build type is set, pick a reasonable one
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
@ -542,7 +541,6 @@ add_subdirectory(src)
add_feature_info(Python ${WITH_PYTHON} "Python job modules") add_feature_info(Python ${WITH_PYTHON} "Python job modules")
add_feature_info(Qml ${WITH_QML} "QML UI support") 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(Polkit ${INSTALL_POLKIT} "Install Polkit files")
add_feature_info(KCrash ${BUILD_KF5Crash} "Crash dumps via KCrash") add_feature_info(KCrash ${BUILD_KF5Crash} "Crash dumps via KCrash")
@ -587,10 +585,6 @@ install(
### Miscellaneous installs ### Miscellaneous installs
# #
# #
if(INSTALL_CONFIG)
install(FILES settings.conf DESTINATION share/calamares)
endif()
if(INSTALL_POLKIT) if(INSTALL_POLKIT)
install(FILES com.github.calamares.calamares.policy DESTINATION "${POLKITQT-1_POLICY_FILES_INSTALL_DIR}") install(FILES com.github.calamares.calamares.policy DESTINATION "${POLKITQT-1_POLICY_FILES_INSTALL_DIR}")
endif() endif()
@ -607,7 +601,9 @@ install(FILES calamares.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/application
install(FILES man/calamares.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/) install(FILES man/calamares.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/)
# uninstall target ### Uninstall
#
#
configure_file( configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" "${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) 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 ### CMAKE SUMMARY REPORT
# #
get_directory_property(SKIPPED_MODULES DIRECTORY src/modules DEFINITION LIST_SKIPPED_MODULES) 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 ) get_filename_component( FLEXT ${MODULE_FILE} EXT )
if( "${FLEXT}" STREQUAL ".conf" ) if( "${FLEXT}" STREQUAL ".conf" )
if( INSTALL_CONFIG ) message(STATUS "Config ${MODULE_FILE}")
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE}
DESTINATION ${MODULE_DATA_DESTINATION} )
endif()
list( APPEND MODULE_CONFIG_FILES ${MODULE_FILE} ) list( APPEND MODULE_CONFIG_FILES ${MODULE_FILE} )
else() else()
message(STATUS "Non-Config ${MODULE_FILE}")
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE} install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE}
DESTINATION ${MODULE_DESTINATION} ) DESTINATION ${MODULE_DESTINATION} )
endif() endif()
@ -169,12 +167,7 @@ function( _calamares_add_module_subdirectory_impl )
message( " ${Green}TYPE:${ColorReset} jobmodule" ) message( " ${Green}TYPE:${ColorReset} jobmodule" )
message( " ${Green}MODULE_DESTINATION:${ColorReset} ${MODULE_DESTINATION}" ) message( " ${Green}MODULE_DESTINATION:${ColorReset} ${MODULE_DESTINATION}" )
if( MODULE_CONFIG_FILES ) if( MODULE_CONFIG_FILES )
if ( INSTALL_CONFIG ) message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory only]" )
set( _destination "${MODULE_DATA_DESTINATION}" )
else()
set( _destination "[Build directory only]" )
endif()
message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => ${_destination}" )
endif() endif()
message( "" ) message( "" )
# We copy over the lang directory, if any # 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}" ) message( FATAL_ERROR "${Red}NO_CONFIG${ColorReset} is set, with configuration ${Red}${PLUGIN_CONFIG_FILES}${ColorReset}" )
endif() endif()
set( _destination "(unknown)" ) set( _destination "(unknown)" )
if ( INSTALL_CONFIG AND NOT PLUGIN_NO_INSTALL ) if( NOT PLUGIN_NO_INSTALL )
set( _destination "${PLUGIN_DATA_DESTINATION}" )
elseif( NOT PLUGIN_NO_INSTALL )
# Not INSTALL_CONFIG
set( _destination "[Build directory only]" ) set( _destination "[Build directory only]" )
else() else()
set( _destination "[Skipping installation]" ) set( _destination "[Skipping installation]" )
@ -210,17 +207,12 @@ function( calamares_add_plugin )
set( _warned_config OFF ) set( _warned_config OFF )
foreach( PLUGIN_CONFIG_FILE ${PLUGIN_CONFIG_FILES} ) 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 ) configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY )
else() else()
message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" ) message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" )
set( _warned_config ON ) set( _warned_config ON )
endif() endif()
if ( INSTALL_CONFIG )
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE}
DESTINATION ${PLUGIN_DATA_DESTINATION} )
endif()
endforeach() endforeach()
if ( _warned_config ) if ( _warned_config )
message( "" ) 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) [![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 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 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/github/license/calamares/calamares.svg)](https://github.com/calamares/calamares/blob/calamares/LICENSES/GPL-3.0-or-later.txt) [![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 # .tx/config file to locate files, and overwrites them in the
# filesystem with new (merged) translations. # filesystem with new (merged) translations.
export QT_SELECT=5 export QT_SELECT=5
tx pull --force --source --all transifex-client pull --force --all || exit 1
### CLEANUP TRANSLATIONS ### CLEANUP TRANSLATIONS

View File

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

View File

@ -53,7 +53,7 @@ class TransifexGetter(object):
parser = configparser.ConfigParser() parser = configparser.ConfigParser()
parser.read_file(f) parser.read_file(f)
return parser.get("https://www.transifex.com", "password") return parser.get("https://app.transifex.com", "password")
except IOError as e: except IOError as e:
return None 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 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: src/modules/grubcfg/main.py:28 #: src/modules/bootloader/main.py:46
msgid "Configure GRUB." msgid "Install bootloader."
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}."
msgstr "" msgstr ""
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
#: src/modules/services-systemd/main.py:63 #: src/modules/bootloader/main.py:640
#: src/modules/services-systemd/main.py:69 msgid "Failed to install grub, no partitions defined in global storage"
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}."
msgstr "" msgstr ""
"Unknown systemd commands <code>{command!s}</code> and "
"<code>{suffix!s}</code> for unit {name!s}."
#: src/modules/unpackfs/main.py:34 #: src/modules/bootloader/main.py:895
msgid "Filling up filesystems." msgid "Bootloader installation error"
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."
msgstr "" msgstr ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
#: src/modules/unpackfs/main.py:481 #: src/modules/bootloader/main.py:896
msgid "The destination \"{}\" in the target system is not a directory" msgid ""
msgstr "The destination \"{}\" in the target system is not a directory" "The bootloader could not be installed. The installation command <pre>{!s}</"
"pre> returned error code {!s}."
msgstr ""
#: src/modules/displaymanager/main.py:524 #: src/modules/displaymanager/main.py:507
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
msgid "Cannot write LXDM configuration file" 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" 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" 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" 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" 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." 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" 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" 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." 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 "" msgid ""
"The displaymanagers list is empty or undefined in both globalstorage and " "The displaymanagers list is empty or undefined in both globalstorage and "
"displaymanager.conf." "displaymanager.conf."
msgstr "" 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" msgid "Display manager configuration was incomplete"
msgstr "Display manager configuration was incomplete" msgstr ""
#: src/modules/initcpiocfg/main.py:28 #: src/modules/dracut/main.py:29
msgid "Configuring mkinitcpio." msgid "Creating initramfs with dracut."
msgstr "Configuring mkinitcpio." msgstr ""
#: src/modules/initcpiocfg/main.py:240 src/modules/initramfscfg/main.py:90 #: src/modules/dracut/main.py:63
#: src/modules/openrcdmcryptcfg/main.py:77 src/modules/fstab/main.py:401 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/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." 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 "" msgstr ""
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
"level {level!s}."
#: src/modules/services-openrc/main.py:94 #: src/modules/fstab/main.py:412
msgid "" msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
msgstr "" msgstr ""
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
#: src/modules/services-openrc/main.py:101 #: src/modules/grubcfg/main.py:29
msgid "Target runlevel does not exist" msgid "Configure GRUB."
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."
msgstr "" msgstr ""
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
"exist."
#: src/modules/services-openrc/main.py:110 #: src/modules/hwclock/main.py:26
msgid "Target service does not exist" msgid "Setting hardware clock."
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."
msgstr "" msgstr ""
"The path for service {name!s} is <code>{path!s}</code>, which does not "
"exist."
#: src/modules/plymouthcfg/main.py:27 #: src/modules/initcpiocfg/main.py:27
msgid "Configure Plymouth theme" msgid "Configuring mkinitcpio."
msgstr "Configure Plymouth theme" 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:54 src/modules/packages/main.py:65
#: src/modules/packages/main.py:75 #: src/modules/packages/main.py:75
msgid "Install packages." msgid "Install packages."
msgstr "Install packages." msgstr ""
#: src/modules/packages/main.py:63 #: src/modules/packages/main.py:63
#, python-format #, python-format
msgid "Processing packages (%(count)d / %(total)d)" msgid "Processing packages (%(count)d / %(total)d)"
msgstr "Processing packages (%(count)d / %(total)d)" msgstr ""
#: src/modules/packages/main.py:68 #: src/modules/packages/main.py:68
#, python-format #, python-format
msgid "Installing one package." msgid "Installing one package."
msgid_plural "Installing %(num)d packages." msgid_plural "Installing %(num)d packages."
msgstr[0] "Installing one package." msgstr[0] ""
msgstr[1] "Installing %(num)d packages." msgstr[1] ""
#: src/modules/packages/main.py:71 #: src/modules/packages/main.py:71
#, python-format #, python-format
msgid "Removing one package." msgid "Removing one package."
msgid_plural "Removing %(num)d packages." msgid_plural "Removing %(num)d packages."
msgstr[0] "Removing one package." msgstr[0] ""
msgstr[1] "Removing %(num)d packages." msgstr[1] ""
#: src/modules/packages/main.py:725 src/modules/packages/main.py:737 #: src/modules/packages/main.py:725 src/modules/packages/main.py:737
#: src/modules/packages/main.py:765 #: src/modules/packages/main.py:765
msgid "Package Manager error" msgid "Package Manager error"
msgstr "Package Manager error" msgstr ""
#: src/modules/packages/main.py:726 #: src/modules/packages/main.py:726
msgid "" msgid ""
"The package manager could not prepare updates. The command <pre>{!s}</pre> " "The package manager could not prepare updates. The command <pre>{!s}</pre> "
"returned error code {!s}." "returned error code {!s}."
msgstr "" msgstr ""
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
"returned error code {!s}."
#: src/modules/packages/main.py:738 #: src/modules/packages/main.py:738
msgid "" msgid ""
"The package manager could not update the system. The command <pre>{!s}</pre>" "The package manager could not update the system. The command <pre>{!s}</pre> "
" returned error code {!s}." "returned error code {!s}."
msgstr "" msgstr ""
"The package manager could not update the system. The command <pre>{!s}</pre>"
" returned error code {!s}."
#: src/modules/packages/main.py:766 #: src/modules/packages/main.py:766
msgid "" msgid ""
"The package manager could not make changes to the installed system. The " "The package manager could not make changes to the installed system. The "
"command <pre>{!s}</pre> returned error code {!s}." "command <pre>{!s}</pre> returned error code {!s}."
msgstr "" 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 #: src/modules/plymouthcfg/main.py:27
msgid "Install bootloader." msgid "Configure Plymouth theme"
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}."
msgstr "" msgstr ""
"The bootloader could not be installed. The installation command "
"<pre>{!s}</pre> returned error code {!s}."
#: src/modules/hwclock/main.py:26 #: src/modules/rawfs/main.py:26
msgid "Setting hardware clock." msgid "Installing data."
msgstr "Setting hardware clock." msgstr ""
#: src/modules/mkinitfs/main.py:27 #: src/modules/services-openrc/main.py:29
msgid "Creating initramfs with mkinitfs." msgid "Configure OpenRC services"
msgstr "Creating initramfs with mkinitfs." msgstr ""
#: src/modules/mkinitfs/main.py:49 #: src/modules/services-openrc/main.py:57
msgid "Failed to run mkinitfs on the target" msgid "Cannot add service {name!s} to run-level {level!s}."
msgstr "Failed to run mkinitfs on the target" msgstr ""
#: src/modules/mkinitfs/main.py:50 src/modules/dracut/main.py:50 #: src/modules/services-openrc/main.py:59
msgid "The exit code was {}" msgid "Cannot remove service {name!s} from run-level {level!s}."
msgstr "The exit code was {}" msgstr ""
#: src/modules/dracut/main.py:27 #: src/modules/services-openrc/main.py:61
msgid "Creating initramfs with dracut." msgid ""
msgstr "Creating initramfs with dracut." "Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
"level {level!s}."
msgstr ""
#: src/modules/dracut/main.py:49 #: src/modules/services-openrc/main.py:93
msgid "Failed to run dracut on the target" msgid "Cannot modify service"
msgstr "Failed to run dracut on the target" msgstr ""
#: src/modules/initramfscfg/main.py:32 #: src/modules/services-openrc/main.py:94
msgid "Configuring initramfs." msgid ""
msgstr "Configuring initramfs." "<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
msgstr ""
#: src/modules/openrcdmcryptcfg/main.py:26 #: src/modules/services-openrc/main.py:101
msgid "Configuring OpenRC dmcrypt service." msgid "Target runlevel does not exist"
msgstr "Configuring OpenRC dmcrypt service." msgstr ""
#: src/modules/fstab/main.py:28 #: src/modules/services-openrc/main.py:102
msgid "Writing fstab." msgid ""
msgstr "Writing fstab." "The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
"exist."
msgstr ""
#: src/modules/fstab/main.py:429 #: src/modules/services-openrc/main.py:110
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use." msgid "Target service does not exist"
msgstr "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use." msgstr ""
#: src/modules/dummypython/main.py:35 #: src/modules/services-openrc/main.py:111
msgid "Dummy python job." msgid ""
msgstr "Dummy python job." "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/services-systemd/main.py:26
#: src/modules/dummypython/main.py:94 msgid "Configure systemd units"
msgid "Dummy python step {}" msgstr ""
msgstr "Dummy python step {}"
#: src/modules/localecfg/main.py:31 #: src/modules/services-systemd/main.py:64
msgid "Configuring locales." msgid "Cannot modify unit"
msgstr "Configuring locales." msgstr ""
#: src/modules/networkcfg/main.py:29 #: src/modules/services-systemd/main.py:65
msgid "Saving network configuration." msgid ""
msgstr "Saving network configuration." "<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 - locale
- keyboard - keyboard
- localecfg - localecfg
- luksopenswaphookcfg
- luksbootkeyfile - luksbootkeyfile
- luksopenswaphookcfg
# - dracutlukscfg
- plymouthcfg - plymouthcfg
# - zfshostid
- initcpiocfg - initcpiocfg
- initcpio - initcpio
- users - users

View File

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

View File

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

View File

@ -73,6 +73,7 @@ ExecutionViewStep::ExecutionViewStep( QObject* parent )
{ {
m_widget->setObjectName( "slideshow" ); m_widget->setObjectName( "slideshow" );
m_progressBar->setObjectName( "exec-progress" ); 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" ); m_label->setObjectName( "exec-message" );
QVBoxLayout* layout = new QVBoxLayout( m_widget ); QVBoxLayout* layout = new QVBoxLayout( m_widget );

View File

@ -9,25 +9,31 @@
# should specifically set *efiBootloaderId* to "debian" because that is # should specifically set *efiBootloaderId* to "debian" because that is
# hard-coded in `grubx64.efi`. # 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 # 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" efiBootLoader: "grub"
# systemd-boot configuration files settings, set kernel search path, kernel name # systemd-boot configuration files settings
# and amount of time before default selection boots
# 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" kernelSearchPath: "/usr/lib/modules"
kernelName: "vmlinuz" kernelPattern: "^vmlinuz.*"
timeout: "10"
# additionalInitrdFiles is a comma seperated list of file names # loaderEntries is an array of options to add to loader.conf for systemd-boot
additionalInitrdFiles: # please note that the "default" option is added programmatically
- "/boot/amd-ucode" loaderEntries:
- "/boot/intel-ucode" - "timeout 5"
- "console-mode keep"
# Optionally set the menu entry name to use in systemd-boot. # systemd-boot and refind support custom kernel params
# If not specified here, these settings will be taken from branding.desc. kernelParams: [ "quiet" ]
#
# bootloaderEntryName: "Generic GNU/Linux" # 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 # GRUB 2 binary names and boot directory
# Some distributions (e.g. Fedora) use grub2-* (resp. /boot/grub2/) names. # 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 additionalProperties: false
type: object type: object
properties: properties:
efiBootLoaderVar: { type: string }
efiBootLoader: { type: string } efiBootLoader: { type: string }
kernelSearchPath: { type: string } kernelSearchPath: { type: string }
kernelName: { type: string } kernelName: { type: string }
timeout: { type: string } # Inserted verbatim kernelParams: { type: array, items: { type: string } }
additionalInitrdFiles: loaderEntries: { type: array, items: { type: string } }
type: array refindKernelList: { type: array, items: { type: string } }
items:
type: string
bootloaderEntryName: { type: string }
kernelLine: { type: string }
fallbackKernelLine: { type: string }
# Programs # Programs
grubInstall: { type: string } grubInstall: { type: string }
@ -27,12 +23,3 @@ properties:
efiBootloaderId: { type: string } efiBootloaderId: { type: string }
installEFIFallback: { type: boolean } 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. # Calamares is Free Software: see the License-Identifier above.
# #
import fileinput
import os import os
import re
import shutil import shutil
import subprocess import subprocess
@ -28,8 +30,8 @@ import libcalamares
from libcalamares.utils import check_target_env_call from libcalamares.utils import check_target_env_call
import gettext import gettext
_ = gettext.translation("calamares-python", _ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(), localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(), languages=libcalamares.utils.gettext_languages(),
@ -39,6 +41,7 @@ _ = gettext.translation("calamares-python",
# to make identifiers (or to clean up names to make filenames). # to make identifiers (or to clean up names to make filenames).
file_name_sanitizer = str.maketrans(" /()", "_-__") file_name_sanitizer = str.maketrans(" /()", "_-__")
def pretty_name(): def pretty_name():
return _("Install bootloader.") return _("Install bootloader.")
@ -59,20 +62,6 @@ def get_uuid():
return "" 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): def get_kernel_line(kernel_type):
""" """
Passes 'kernel_line' to other routine based on configuration file. 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" return partition["mountPoint"] == "/" and partition["fs"] == "zfs"
def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kernel, kernel_type, kernel_version): def get_kernel_params(uuid):
""" kernel_params = libcalamares.job.configuration.get("kernelParams", ["quiet"])
Creates systemd-boot configuration files based on given parameters. kernel_params.append("rw")
: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"]
partitions = libcalamares.globalstorage.value("partitions") partitions = libcalamares.globalstorage.value("partitions")
swap_uuid = "" swap_uuid = ""
swap_outer_mappername = None swap_outer_mappername = None
swap_outer_uuid = None
cryptdevice_params = [] cryptdevice_params = []
have_dracut = libcalamares.utils.target_env_call(["sh", "-c", "which dracut"]) == 0
# Take over swap settings: # Take over swap settings:
# - unencrypted swap partition sets swap_uuid # - unencrypted swap partition sets swap_uuid
# - encrypted root sets cryptdevice_params # - encrypted root sets cryptdevice_params
for partition in partitions: for partition in partitions:
if partition["fs"] == "linuxswap" and not partition.get("claimed", None): if partition["fs"] == "linuxswap" and not partition.get("claimed", None):
# Skip foreign swap
continue continue
has_luks = "luksMapperName" in partition has_luks = "luksMapperName" in partition
if partition["fs"] == "linuxswap" and not has_luks: if partition["fs"] == "linuxswap" and not has_luks:
swap_uuid = partition["uuid"] 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_mappername = partition["luksMapperName"]
swap_outer_uuid = partition["luksUuid"]
if partition["mountPoint"] == "/" and has_luks: if partition["mountPoint"] == "/" and has_luks:
cryptdevice_params = ["cryptdevice=UUID=" if have_dracut:
+ partition["luksUuid"] cryptdevice_params = [f"rd.luks.uuid={partition['luksUuid']}"]
+ ":" else:
+ partition["luksMapperName"], cryptdevice_params = [f"cryptdevice=UUID={partition['luksUuid']}:{partition['luksMapperName']}"]
"root=/dev/mapper/" cryptdevice_params.append(f"root=/dev/mapper/{partition['luksMapperName']}")
+ partition["luksMapperName"]]
# btrfs and zfs handling
for partition in partitions: 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 # If a btrfs root subvolume wasn't set, it means the root is directly on the partition
# and this option isn't needed # and this option isn't needed
if is_btrfs_root(partition): 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): if is_zfs_root(partition):
zfs_root_path = get_zfs_root() zfs_root_path = get_zfs_root()
if zfs_root_path is not None: if zfs_root_path is not None:
kernel_params.append("zfs=" + zfs_root_path) kernel_params.append("root=ZFS=" + zfs_root_path)
else: else:
# Something is really broken if we get to this point # Something is really broken if we get to this point
libcalamares.utils.warning("Internal error handling zfs dataset") 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: if swap_uuid:
kernel_params.append("resume=UUID={!s}".format(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: if swap_outer_mappername:
kernel_params.append("resume=/dev/mapper/{!s}".format( kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}")
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" def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, kernel, kernel_version):
initrd = "initrd-fallback" """
else: Creates systemd-boot configuration files based on given parameters.
version_string = kernel_version
initrd = "initrd" :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 # get the machine-id
with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file: with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file:
machine_id = machineid_file.read().rstrip('\n') 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) machine_dir = os.path.join(installation_root_path + efi_dir, machine_id)
os.makedirs(machine_dir, exist_ok=True) os.makedirs(machine_dir, exist_ok=True)
target_efi_files_dir = os.path.join(machine_dir, kernel_version) # Call kernel-install for each kernel
os.makedirs(target_efi_files_dir, exist_ok=True) libcalamares.utils.target_env_process_output(["kernel-install",
"add",
kernel_path = os.path.join(installation_root_path, kernel) kernel_version,
kernel_name = os.path.basename(kernel_path) os.path.join("/", kernel)])
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)
def create_loader(loader_path, entry): def create_loader(loader_path, installation_root_path):
""" """
Writes configuration for loader. Writes configuration for loader.
:param loader_path: :param loader_path: The absolute path to the loader.conf file
:param entry: :param installation_root_path: The path to the root of the target installation
""" """
timeout = libcalamares.job.configuration["timeout"]
lines = [ # get the machine-id
"timeout {!s}\n".format(timeout), with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file:
"default {!s}\n".format(entry), 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: with open(loader_path, 'w') as loader_file:
for line in lines: 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 Wrapper for one of the "generator" classes below to behave like
a proper Python iterator. The iterator is initialized with a a proper Python iterator. The iterator is initialized with a
maximum number of attempts to generate a new suffix. maximum number of attempts to generate a new suffix.
""" """
def __init__(self, attempts, generator): def __init__(self, attempts, generator):
self.generator = generator self.generator = generator
self.attempts = attempts self.attempts = attempts
@ -310,6 +286,7 @@ class serialEfi(object):
""" """
EFI Id generator that appends a serial number to the given name. EFI Id generator that appends a serial number to the given name.
""" """
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
# So the first call to next() will bump it to 0 # 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. EFI Id generator that appends a random 4-digit hex number to the given name.
""" """
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
# So the first call to next() will bump it to 0 # 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: if bootloader_id.endswith("}") and "${" in bootloader_id:
# Do 10 attempts with any suffix generator # 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: else:
# Just one attempt # Just one attempt
g = [bootloader_id] g = [bootloader_id]
@ -444,7 +422,7 @@ def efi_label(efi_directory):
used within @p efi_directory. used within @p efi_directory.
""" """
if "efiBootloaderId" in libcalamares.job.configuration: 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: else:
branding = libcalamares.globalstorage.value("branding") branding = libcalamares.globalstorage.value("branding")
efi_bootloader_id = branding["bootloaderEntryName"] efi_bootloader_id = branding["bootloaderEntryName"]
@ -482,6 +460,7 @@ def efi_boot_next():
if boot_entry: if boot_entry:
subprocess.call([boot_mgr, "-n", boot_entry]) subprocess.call([boot_mgr, "-n", boot_entry])
def get_kernels(installation_root_path): 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. 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 Each 3-tuple contains the kernel, kernel_type and kernel_version
""" """
kernel_search_path = libcalamares.job.configuration["kernelSearchPath"] try:
source_kernel_name = libcalamares.job.configuration["kernelName"] 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 = [] 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 root, dirs, files in os.walk(os.path.join(installation_root_path, kernel_search_path.lstrip('/'))):
for file in files: for file in files:
if file == source_kernel_name: if re.search(kernel_pattern, file):
rel_root = os.path.relpath(root, installation_root_path) 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), "default", os.path.basename(root)))
kernel_list.append((os.path.join(rel_root, file),"fallback",os.path.basename(root)))
return kernel_list return kernel_list
def install_systemd_boot(efi_directory): def install_systemd_boot(efi_directory):
""" """
Installs systemd-boot as bootloader for EFI setups. 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") installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
install_efi_directory = installation_root_path + efi_directory install_efi_directory = installation_root_path + efi_directory
uuid = get_uuid() uuid = get_uuid()
distribution = get_bootloader_entry_name()
distribution_translated = distribution.translate(file_name_sanitizer)
loader_path = os.path.join(install_efi_directory, loader_path = os.path.join(install_efi_directory,
"loader", "loader",
"loader.conf") "loader.conf")
@ -528,14 +517,13 @@ def install_systemd_boot(efi_directory):
for (kernel, kernel_type, kernel_version) in get_kernels(installation_root_path): for (kernel, kernel_type, kernel_version) in get_kernels(installation_root_path):
create_systemd_boot_conf(installation_root_path, create_systemd_boot_conf(installation_root_path,
efi_directory, efi_directory,
uuid, uuid,
distribution, kernel,
kernel, kernel_version)
kernel_type,
kernel_version) create_loader(loader_path, installation_root_path)
create_loader(loader_path, distribution_translated)
def get_grub_efi_parameters(): def get_grub_efi_parameters():
""" """
@ -554,15 +542,16 @@ def get_grub_efi_parameters():
if efi_bitness == "32": if efi_bitness == "32":
# Assume all 32-bitters are legacy x86 # 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": 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": 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": elif efi_bitness == "64":
# If it's not ARM, must by AMD64 # If it's not ARM, must by AMD64
return ("x86_64-efi", "grubx64.efi", "bootx64.efi") 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))) 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 return None
@ -667,8 +656,8 @@ def install_grub(efi_directory, fw_type):
# VFAT is weird, see issue CAL-385 # VFAT is weird, see issue CAL-385
install_efi_directory_firmware = (vfat_correct_case( install_efi_directory_firmware = (vfat_correct_case(
install_efi_directory, install_efi_directory,
"EFI")) "EFI"))
if not os.path.exists(install_efi_directory_firmware): if not os.path.exists(install_efi_directory_firmware):
os.makedirs(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 # most usual they are boot, Boot, BOOT
install_efi_boot_directory = (vfat_correct_case( install_efi_boot_directory = (vfat_correct_case(
install_efi_directory_firmware, install_efi_directory_firmware,
"boot")) "boot"))
if not os.path.exists(install_efi_boot_directory): if not os.path.exists(install_efi_boot_directory):
os.makedirs(install_efi_boot_directory) os.makedirs(install_efi_boot_directory)
@ -712,6 +701,9 @@ def install_secureboot(efi_directory):
install_efi_bin = "shimx64.efi" install_efi_bin = "shimx64.efi"
elif efi_word_size() == "32": elif efi_word_size() == "32":
install_efi_bin = "shimia32.efi" 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, # Copied, roughly, from openSUSE's install script,
# and pythonified. *disk* is something like /dev/sda, # and pythonified. *disk* is something like /dev/sda,
@ -725,7 +717,7 @@ def install_secureboot(efi_directory):
libcalamares.job.configuration["grubProbe"], libcalamares.job.configuration["grubProbe"],
"-t", "disk", "--device-map=", install_efi_directory]).decode("ascii") "-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 # Get the first run of digits from the partition
efi_partition_number = None efi_partition_number = None
c = 0 c = 0
@ -765,6 +757,62 @@ def vfat_correct_case(parent, name):
return os.path.join(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): def prepare_bootloader(fw_type):
""" """
Prepares bootloader. Prepares bootloader.
@ -774,19 +822,47 @@ def prepare_bootloader(fw_type):
:param fw_type: :param fw_type:
:return: :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") efi_directory = libcalamares.globalstorage.value("efiSystemPartition")
if efi_boot_loader == "systemd-boot" and fw_type == "efi": if efi_boot_loader == "systemd-boot" and fw_type == "efi":
install_systemd_boot(efi_directory) install_systemd_boot(efi_directory)
elif efi_boot_loader == "sb-shim" and fw_type == "efi": elif efi_boot_loader == "sb-shim" and fw_type == "efi":
install_secureboot(efi_directory) 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": elif efi_boot_loader == "grub" or fw_type != "efi":
install_grub(efi_directory, fw_type) install_grub(efi_directory, fw_type)
else: else:
libcalamares.utils.debug( "WARNING: the combination of " libcalamares.utils.debug("WARNING: the combination of "
"boot-loader '{!s}' and firmware '{!s}' " "boot-loader '{!s}' and firmware '{!s}' "
"is not supported.".format(efi_boot_loader, fw_type) ) "is not supported.".format(efi_boot_loader, fw_type))
def run(): def run():
@ -798,16 +874,16 @@ def run():
fw_type = libcalamares.globalstorage.value("firmwareType") fw_type = libcalamares.globalstorage.value("firmwareType")
if (libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi"): if libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi":
libcalamares.utils.warning( "Non-EFI system, and no bootloader is set." ) libcalamares.utils.warning("Non-EFI system, and no bootloader is set.")
return None return None
partitions = libcalamares.globalstorage.value("partitions") partitions = libcalamares.globalstorage.value("partitions")
if fw_type == "efi": if fw_type == "efi":
efi_system_partition = libcalamares.globalstorage.value("efiSystemPartition") 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: 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 return None
try: try:
@ -817,7 +893,8 @@ def run():
libcalamares.utils.debug("stdout:" + str(e.stdout)) libcalamares.utils.debug("stdout:" + str(e.stdout))
libcalamares.utils.debug("stderr:" + str(e.stderr)) libcalamares.utils.debug("stderr:" + str(e.stderr))
return (_("Bootloader installation error"), 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)) .format(e.cmd, e.returncode))
return None return None

View File

@ -59,3 +59,17 @@ basicSetup: false
# *displaymanagers* list (as the only one). # *displaymanagers* list (as the only one).
# #
sysconfigSetup: false 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 ] required: [ executable, desktopFile ]
basicSetup: { type: boolean, default: false } basicSetup: { type: boolean, default: false }
sysconfigSetup: { 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/sway', 'sway'),
DesktopEnvironment('/usr/bin/ukui-session', 'ukui'), DesktopEnvironment('/usr/bin/ukui-session', 'ukui'),
DesktopEnvironment('/usr/bin/cutefish-session', 'cutefish-xsession'), 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") userfile.write("Icon=\n")
def basic_setup(self): def basic_setup(self):
if libcalamares.utils.target_env_call( if libcalamares.utils.target_env_call(
['getent', 'group', 'gdm'] ['getent', 'group', 'gdm']
@ -544,6 +545,11 @@ class DMlightdm(DisplayManager):
name = "lightdm" name = "lightdm"
executable = "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): def set_autologin(self, username, do_autologin, default_desktop_environment):
# Systems with LightDM as Desktop Manager # Systems with LightDM as Desktop Manager
# Ideally, we should use configparser for the ini conf file, # 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) _("LightDM config file {!s} does not exist").format(lightdm_conf_path)
) )
def basic_setup(self): def basic_setup(self):
libcalamares.utils.target_env_call( libcalamares.utils.target_env_call(
['mkdir', '-p', '/run/lightdm'] ['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): def greeter_setup(self):
lightdm_conf_path = os.path.join( lightdm_conf_path = os.path.join(self.root_mount_point, "etc/lightdm/lightdm.conf")
self.root_mount_point, "etc/lightdm/lightdm.conf" greeter_name = self.find_preferred_greeter()
)
# configure lightdm-greeter if greeter_name is not None:
greeter_path = os.path.join( greeter = os.path.basename(greeter_name) # Follow symlinks, hope they are not absolute
self.root_mount_point, "usr/share/xgreeters" if greeter.endswith('.desktop'):
) greeter = greeter[:-8] # Remove ".desktop" from end
if (os.path.exists(greeter_path)): libcalamares.utils.debug("found greeter {!s}".format(greeter))
# configure first found lightdm-greeter os.system(
for entry in os.listdir(greeter_path): "sed -i -e \"s/^.*greeter-session=.*"
if entry.endswith('.desktop'): "/greeter-session={!s}/\" {!s}".format(
greeter = entry.split('.')[0] greeter,
libcalamares.utils.debug( lightdm_conf_path
"found greeter {!s}".format(greeter) )
) )
os.system( libcalamares.utils.debug("{!s} configured as greeter.".format(greeter))
"sed -i -e \"s/^.*greeter-session=.*" else:
"/greeter-session={!s}/\" {!s}".format( libcalamares.utils.error("No greeter found at all, preferred {!s}".format(self.preferred_greeters))
greeter, return (
lightdm_conf_path _("Cannot configure LightDM"),
) _("No LightDM greeter installed.")
) )
libcalamares.utils.debug(
"{!s} configured as greeter.".format(greeter)
)
break
else:
return (
_("Cannot configure LightDM"),
_("No LightDM greeter installed.")
)
class DMslim(DisplayManager): class DMslim(DisplayManager):
@ -817,7 +834,7 @@ class DMgreetd(DisplayManager):
def desktop_environment_setup(self, default_desktop_environment): def desktop_environment_setup(self, default_desktop_environment):
with open(self.environments_path(), 'w') as envs_file: 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") envs_file.write("\n")
def greeter_setup(self): def greeter_setup(self):
@ -828,7 +845,7 @@ class DMgreetd(DisplayManager):
de_command = default_desktop_environment.executable 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")): 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")): elif os.path.exists(self.os_path("usr/bin/tuigreet")):
tuigreet_base_cmd = "tuigreet --remember --time --issue --asterisks --cmd " tuigreet_base_cmd = "tuigreet --remember --time --issue --asterisks --cmd "
self.config_data['default_session']['command'] = tuigreet_base_cmd + de_command self.config_data['default_session']['command'] = tuigreet_base_cmd + de_command
@ -983,6 +1000,11 @@ def run():
# Do the actual configuration and collect messages # Do the actual configuration and collect messages
dm_setup_message = [] dm_setup_message = []
for dm in dm_impl: 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 dm_message = None
if enable_basic_setup: if enable_basic_setup:
dm_message = dm.basic_setup() dm_message = dm.basic_setup()

View File

@ -5,6 +5,6 @@
--- ---
# Dracut defaults to setting initramfs-<kernel-version>.img # Dracut defaults to setting initramfs-<kernel-version>.img
# If you want to specify another filename for the resulting image, # 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 additionalProperties: false
type: object type: object
properties: properties:
kernelName: { type: string } initramfsName: { type: string }

View File

@ -12,9 +12,10 @@
# #
# Calamares is Free Software: see the License-Identifier above. # Calamares is Free Software: see the License-Identifier above.
# #
import subprocess
import libcalamares import libcalamares
from libcalamares.utils import check_target_env_call from libcalamares.utils import target_env_process_output
import gettext import gettext
@ -34,12 +35,20 @@ def run_dracut():
:return: :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 0
return check_target_env_call(['dracut', '-f'])
else:
return check_target_env_call(['dracut', '-f', '{}'.format(kernelName)])
def run(): def run():
@ -50,7 +59,6 @@ def run():
:return: :return:
""" """
return_code = run_dracut() return_code = run_dracut()
if return_code != 0: if return_code != 0:
return (_("Failed to run dracut on the target"), return (_("Failed to run dracut"),
_("The exit code was {}").format(return_code)) _(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_DISABLE_SUBMENU: true
GRUB_TERMINAL_OUTPUT: "console" GRUB_TERMINAL_OUTPUT: "console"
GRUB_DISABLE_RECOVERY: true 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_DISABLE_SUBMENU: { type: boolean, default: true }
GRUB_TERMINAL_OUTPUT: { type: string } GRUB_TERMINAL_OUTPUT: { type: string }
GRUB_DISABLE_RECOVERY: { type: boolean, default: true } 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 libcalamares
import fileinput
import os import os
import re import re
@ -85,6 +86,30 @@ def get_zfs_root():
return None 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): def modify_grub_default(partitions, root_mount_point, distributor):
""" """
Configures '/etc/default/grub' for hibernation and plymouth. Configures '/etc/default/grub' for hibernation and plymouth.
@ -103,7 +128,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
:return: :return:
""" """
default_grub = get_grub_config_path(root_mount_point) default_grub = get_grub_config_path(root_mount_point)
distributor_replace = distributor.replace("'", "'\\''") distributor = distributor.replace("'", "'\\''")
dracut_bin = libcalamares.utils.target_env_call( dracut_bin = libcalamares.utils.target_env_call(
["sh", "-c", "which dracut"] ["sh", "-c", "which dracut"]
) )
@ -127,7 +152,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
zfs_root_path = None zfs_root_path = None
for partition in partitions: 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 no_save_default = True
break break
@ -197,63 +222,46 @@ def modify_grub_default(partitions, root_mount_point, distributor):
if swap_outer_mappername: if swap_outer_mappername:
kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}") kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}")
if "overwrite" in libcalamares.job.configuration: overwrite = libcalamares.job.configuration.get("overwrite", False)
overwrite = libcalamares.job.configuration["overwrite"]
else:
overwrite = False
distributor_line = f"GRUB_DISTRIBUTOR='{distributor_replace}'" grub_config_items = {}
kernel_cmd = f'GRUB_CMDLINE_LINUX_DEFAULT="{" ".join(kernel_params)}"' # read the lines we need from the existing config
have_kernel_cmd = False
have_distributor_line = False
if os.path.exists(default_grub) and not overwrite: if os.path.exists(default_grub) and not overwrite:
with open(default_grub, 'r') as grub_file: with open(default_grub, 'r') as grub_file:
lines = [x.strip() for x in grub_file.readlines()] lines = [x.strip() for x in grub_file.readlines()]
for i in range(len(lines)): for line in lines:
if lines[i].startswith("#GRUB_CMDLINE_LINUX_DEFAULT"): if line.startswith("GRUB_CMDLINE_LINUX_DEFAULT"):
lines[i] = kernel_cmd existing_params = re.sub(r"^GRUB_CMDLINE_LINUX_DEFAULT\s*=\s*", "", line).strip("\"'").split()
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 existing_param in existing_params: 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 # Ensure we aren't adding duplicated params
if existing_param_name not in [ param_exists = False
"quiet", "resume", "splash"]: 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) kernel_params.append(existing_param)
lines[i] = kernel_cmd elif line.startswith("GRUB_DISTRIBUTOR") and libcalamares.job.configuration.get("keep_distributor", False):
have_kernel_cmd = True distributor_parts = line.split("=")
elif (lines[i].startswith("#GRUB_DISTRIBUTOR") if len(distributor_parts) > 1:
or lines[i].startswith("GRUB_DISTRIBUTOR")): distributor = distributor_parts[1].strip("'\"")
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 = []
# 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: if "defaults" in libcalamares.job.configuration:
for key, value in libcalamares.job.configuration[ for key, value in libcalamares.job.configuration["defaults"].items():
"defaults"].items(): if isinstance(value, bool):
if value.__class__.__name__ == "bool":
if value: if value:
escaped_value = "true" escaped_value = "true"
else: else:
@ -261,19 +269,20 @@ def modify_grub_default(partitions, root_mount_point, distributor):
else: else:
escaped_value = str(value).replace("'", "'\\''") escaped_value = str(value).replace("'", "'\\''")
lines.append(f"{key}='{escaped_value}'") grub_config_items[key] = f"'{escaped_value}'"
if not have_kernel_cmd: grub_config_items['GRUB_CMDLINE_LINUX_DEFAULT'] = f"'{' '.join(kernel_params)}'"
lines.append(kernel_cmd) grub_config_items["GRUB_DISTRIBUTOR"] = f"'{distributor}'"
if not have_distributor_line:
lines.append(distributor_line)
if cryptdevice_params and not unencrypted_separate_boot: 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: if overwrite or not os.path.exists(default_grub) or libcalamares.job.configuration.get("prefer_grub_d", False):
grub_file.write("\n".join(lines) + "\n") 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 return None

View File

@ -147,6 +147,7 @@ def find_initcpio_features(partitions, root_mount_point):
"base", "base",
"udev", "udev",
"autodetect", "autodetect",
"kms",
"modconf", "modconf",
"block", "block",
"keyboard", "keyboard",
@ -176,8 +177,6 @@ def find_initcpio_features(partitions, root_mount_point):
hooks.append("bootsplash-{!s}".format(bootsplash_theme)) hooks.append("bootsplash-{!s}".format(bootsplash_theme))
for partition in partitions: for partition in partitions:
hooks.extend(["filesystems"])
if partition["fs"] == "linuxswap" and not partition.get("claimed", None): if partition["fs"] == "linuxswap" and not partition.get("claimed", None):
# Skip foreign swap # Skip foreign swap
continue continue
@ -225,9 +224,11 @@ def find_initcpio_features(partitions, root_mount_point):
hooks.append("zfs") hooks.append("zfs")
if swap_uuid != "": if swap_uuid != "":
hooks.extend(["resume"])
if encrypt_hook and openswap_hook: if encrypt_hook and openswap_hook:
hooks.extend(["openswap"]) hooks.extend(["openswap"])
hooks.extend(["resume", "filesystems"])
else:
hooks.extend(["filesystems"])
if uses_btrfs: if uses_btrfs:
modules.append("crc32c-intel" if cpuinfo().is_intel else "crc32c") 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 # Mount the subvolumes
swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap") swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap")
for s in btrfs_subvolumes: for s in btrfs_subvolumes:
mount_option = "subvol={}".format(s['subvolume'])
if s['subvolume'] == swap_subvol: 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: 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'] 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, if libcalamares.utils.mount(device,
subvolume_mountpoint, subvolume_mountpoint,
fstype, fstype,
@ -308,6 +308,14 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun
libcalamares.utils.warning("Cannot mount {}".format(device)) 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(): def run():
""" """
Mount all the partitions from GlobalStorage and from the job configuration. Mount all the partitions from GlobalStorage and from the job configuration.
@ -321,6 +329,11 @@ def run():
return (_("Configuration Error"), return (_("Configuration Error"),
_("No partitions are defined for <pre>{!s}</pre> to use.").format("mount")) _("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-") root_mount_point = tempfile.mkdtemp(prefix="calamares-root-")
# Get the mountOptions, if this is None, that is OK and will be handled later # 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 ) if ( index.column() == NameColumn )
{ {
PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() ); PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() );
if ( item->isImmutable() ) if ( item->isImmutable() || item->isNoncheckable() )
{ {
return QAbstractItemModel::flags( index ); //Qt::NoItemFlags; return QAbstractItemModel::flags( index ); //Qt::NoItemFlags;
} }

View File

@ -49,6 +49,7 @@ PackageTreeItem::PackageTreeItem( const QString& packageName, PackageTreeItem* p
, m_isGroup( false ) , m_isGroup( false )
, m_isCritical( parent ? parent->isCritical() : false ) , m_isCritical( parent ? parent->isCritical() : false )
, m_showReadOnly( parent ? parent->isImmutable() : 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_isGroup( false )
, m_isCritical( parent.parent ? parent.parent->isCritical() : false ) , m_isCritical( parent.parent ? parent.parent->isCritical() : false )
, m_showReadOnly( parent.parent ? parent.parent->isImmutable() : 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_isCritical( parentCriticality( groupData, parent.parent ) )
, m_isHidden( CalamaresUtils::getBool( groupData, "hidden", false ) ) , m_isHidden( CalamaresUtils::getBool( groupData, "hidden", false ) )
, m_showReadOnly( CalamaresUtils::getBool( groupData, "immutable", false ) ) , m_showReadOnly( CalamaresUtils::getBool( groupData, "immutable", false ) )
, m_showNoncheckable( CalamaresUtils::getBool( groupData, "noncheckable", false ) )
, m_startExpanded( CalamaresUtils::getBool( groupData, "expanded", false ) ) , m_startExpanded( CalamaresUtils::getBool( groupData, "expanded", false ) )
{ {
} }

View File

@ -109,6 +109,13 @@ public:
*/ */
bool isImmutable() const { return m_showReadOnly; } 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? /** @brief is this item selected?
* *
* Groups may be partially selected; packages are only on or off. * Groups may be partially selected; packages are only on or off.
@ -165,6 +172,7 @@ private:
bool m_isCritical = false; bool m_isCritical = false;
bool m_isHidden = false; bool m_isHidden = false;
bool m_showReadOnly = false; bool m_showReadOnly = false;
bool m_showNoncheckable = false;
bool m_startExpanded = false; bool m_startExpanded = false;
}; };

View File

@ -183,6 +183,9 @@ label:
# really only makes sense in combination with *selected* set to true, # really only makes sense in combination with *selected* set to true,
# so that the packages will be installed. (Setting a group to immutable # so that the packages will be installed. (Setting a group to immutable
# can be seen as removing it from the user-interface.) # 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, # - *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- # not-collapsed) in the treeview on start. This only affects the user-
# interface. Only top-level groups are show expanded-initially. # interface. Only top-level groups are show expanded-initially.

View File

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

View File

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

View File

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

View File

@ -25,23 +25,52 @@ Config::Config( QObject* parent )
const NamedEnumTable< Config::InstallChoice >& const NamedEnumTable< Config::InstallChoice >&
Config::installChoiceNames() Config::installChoiceNames()
{ {
static const NamedEnumTable< InstallChoice > names { { QStringLiteral( "none" ), InstallChoice::NoChoice }, // *INDENT-OFF*
{ QStringLiteral( "nochoice" ), InstallChoice::NoChoice }, // clang-format off
{ QStringLiteral( "alongside" ), InstallChoice::Alongside }, static const NamedEnumTable< InstallChoice > names {
{ QStringLiteral( "erase" ), InstallChoice::Erase }, { QStringLiteral( "none" ), InstallChoice::NoChoice },
{ QStringLiteral( "replace" ), InstallChoice::Replace }, { QStringLiteral( "nochoice" ), InstallChoice::NoChoice },
{ QStringLiteral( "manual" ), InstallChoice::Manual } }; { QStringLiteral( "alongside" ), InstallChoice::Alongside },
{ QStringLiteral( "erase" ), InstallChoice::Erase },
{ QStringLiteral( "replace" ), InstallChoice::Replace },
{ QStringLiteral( "manual" ), InstallChoice::Manual },
};
// clang-format on
// *INDENT-ON*
return names; return names;
} }
const NamedEnumTable< Config::SwapChoice >& const NamedEnumTable< Config::SwapChoice >&
Config::swapChoiceNames() Config::swapChoiceNames()
{ {
static const NamedEnumTable< SwapChoice > names { { QStringLiteral( "none" ), SwapChoice::NoSwap }, // *INDENT-OFF*
{ QStringLiteral( "small" ), SwapChoice::SmallSwap }, // clang-format off
{ QStringLiteral( "suspend" ), SwapChoice::FullSwap }, static const NamedEnumTable< SwapChoice > names {
{ QStringLiteral( "reuse" ), SwapChoice::ReuseSwap }, { QStringLiteral( "none" ), SwapChoice::NoSwap },
{ QStringLiteral( "file" ), SwapChoice::SwapFile } }; { 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; return names;
} }
@ -213,7 +242,7 @@ Config::setSwapChoice( Config::SwapChoice c )
void void
Config::setEraseFsTypeChoice( const QString& choice ) Config::setEraseFsTypeChoice( const QString& choice )
{ {
QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr ); const QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr );
if ( canonicalChoice != m_eraseFsTypeChoice ) if ( canonicalChoice != m_eraseFsTypeChoice )
{ {
m_eraseFsTypeChoice = canonicalChoice; 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 bool
Config::acceptPartitionTableType( PartitionTable::TableType tableType ) const Config::acceptPartitionTableType( PartitionTable::TableType tableType ) const
{ {
@ -252,8 +292,8 @@ fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configu
gs->insert( "efiSystemPartitionSize_i", part_size.toBytes() ); gs->insert( "efiSystemPartitionSize_i", part_size.toBytes() );
// Assign long long int to long unsigned int to prevent compilation warning // Assign long long int to long unsigned int to prevent compilation warning
size_t unsigned_part_size = part_size.toBytes(); auto byte_part_size = part_size.toBytes();
if ( unsigned_part_size != PartUtils::efiFilesystemMinimumSize() ) if ( byte_part_size != PartUtils::efiFilesystemMinimumSize() )
{ {
cWarning() << "EFI partition size" << sizeString << "has been adjusted to" cWarning() << "EFI partition size" << sizeString << "has been adjusted to"
<< PartUtils::efiFilesystemMinimumSize() << "bytes"; << 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.isEmpty() );
Q_ASSERT( m_eraseFsTypes.contains( fsRealName ) ); Q_ASSERT( m_eraseFsTypes.contains( fsRealName ) );
m_eraseFsTypeChoice = fsRealName; m_eraseFsTypeChoice = fsRealName;
m_replaceFileSystemChoice = fsRealName;
Q_EMIT eraseModeFilesystemChanged( m_eraseFsTypeChoice ); Q_EMIT eraseModeFilesystemChanged( m_eraseFsTypeChoice );
Q_EMIT replaceModeFilesystemChanged( m_replaceFileSystemChoice );
} }
void void
Config::setConfigurationMap( const QVariantMap& configurationMap ) Config::setConfigurationMap( const QVariantMap& configurationMap )
{ {
@ -356,6 +409,8 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
} }
setSwapChoice( m_initialSwapChoice ); setSwapChoice( m_initialSwapChoice );
m_allowZfsEncryption = CalamaresUtils::getBool( configurationMap, "allowZfsEncryption", true );
m_allowManualPartitioning = CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true ); m_allowManualPartitioning = CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true );
m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" ); m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" );

View File

@ -31,6 +31,9 @@ class Config : public QObject
Q_PROPERTY( Q_PROPERTY(
QString eraseModeFilesystem READ eraseFsType WRITE setEraseFsTypeChoice NOTIFY eraseModeFilesystemChanged ) 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 ) Q_PROPERTY( bool allowManualPartitioning READ allowManualPartitioning CONSTANT FINAL )
public: public:
@ -63,6 +66,15 @@ public:
using EraseFsTypesSet = QStringList; 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& ); void setConfigurationMap( const QVariantMap& );
/** @brief Set GS values where other modules configuration has priority /** @brief Set GS values where other modules configuration has priority
* *
@ -122,6 +134,9 @@ public:
*/ */
QString eraseFsType() const { return m_eraseFsTypeChoice; } 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) /** @brief Configured default FS type (for other modes than erase)
* *
* This is not "Unknown" or "Unformatted" * This is not "Unknown" or "Unformatted"
@ -140,33 +155,44 @@ public:
/// @brief Returns list of acceptable types. May be empty. /// @brief Returns list of acceptable types. May be empty.
QStringList partitionTableTypes() const { return m_requiredPartitionTableType; } 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: public Q_SLOTS:
void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice
void setInstallChoice( InstallChoice ); void setInstallChoice( InstallChoice );
void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice
void setSwapChoice( SwapChoice ); void setSwapChoice( SwapChoice );
void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem
void setReplaceFilesystemChoice( const QString& filesystemName );
Q_SIGNALS: Q_SIGNALS:
void installChoiceChanged( InstallChoice ); void installChoiceChanged( InstallChoice );
void swapChoiceChanged( SwapChoice ); void swapChoiceChanged( SwapChoice );
void eraseModeFilesystemChanged( const QString& ); void eraseModeFilesystemChanged( const QString& );
void replaceModeFilesystemChanged( const QString& );
private: private:
/** @brief Handle FS-type configuration, for erase and default */ /** @brief Handle FS-type configuration, for erase and default */
void fillConfigurationFSTypes( const QVariantMap& configurationMap ); void fillConfigurationFSTypes( const QVariantMap& configurationMap );
EraseFsTypesSet m_eraseFsTypes; EraseFsTypesSet m_eraseFsTypes;
QString m_eraseFsTypeChoice; QString m_eraseFsTypeChoice;
QString m_replaceFileSystemChoice;
FileSystem::Type m_defaultFsType; FileSystem::Type m_defaultFsType;
SwapChoiceSet m_swapChoices; SwapChoiceSet m_swapChoices;
SwapChoice m_initialSwapChoice = NoSwap; SwapChoice m_initialSwapChoice = NoSwap;
SwapChoice m_swapChoice = NoSwap; SwapChoice m_swapChoice = NoSwap;
LuksGeneration m_luksFileSystemType = LuksGeneration::Luks1;
InstallChoice m_initialInstallChoice = NoChoice; InstallChoice m_initialInstallChoice = NoChoice;
InstallChoice m_installChoice = NoChoice; InstallChoice m_installChoice = NoChoice;
qreal m_requiredStorageGiB = 0.0; // May duplicate setting in the welcome module qreal m_requiredStorageGiB = 0.0; // May duplicate setting in the welcome module
QStringList m_requiredPartitionTableType; QStringList m_requiredPartitionTableType;
bool m_allowZfsEncryption = true;
bool m_allowManualPartitioning = true; bool m_allowManualPartitioning = true;
}; };

View File

@ -447,7 +447,6 @@ PartitionViewStep::onActivate()
{ {
m_choicePage->applyActionChoice( Config::InstallChoice::Alongside ); m_choicePage->applyActionChoice( Config::InstallChoice::Alongside );
// m_choicePage->reset(); // 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 // If the root partition is encrypted, and there's a separate boot
// partition which is not encrypted // 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" ); message = tr( "Boot partition not encrypted" );
description = tr( "A separate boot partition was set up together with " 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 if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
&& !partition->fileSystem().uuid().isEmpty() ) && !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() ); FS::luks& luksFs = dynamic_cast< FS::luks& >( partition->fileSystem() );
if ( !luksFs.outerUuid().isEmpty() && s_partitionColorsCache.contains( luksFs.outerUuid() ) ) if ( !luksFs.outerUuid().isEmpty() && s_partitionColorsCache.contains( luksFs.outerUuid() ) )
@ -146,7 +146,7 @@ colorForPartition( Partition* partition )
if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
&& !partition->fileSystem().uuid().isEmpty() ) && !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() ); FS::luks& luksFs = dynamic_cast< FS::luks& >( partition->fileSystem() );
if ( !luksFs.outerUuid().isEmpty() ) if ( !luksFs.outerUuid().isEmpty() )

View File

@ -84,6 +84,7 @@ createNewEncryptedPartition( PartitionNode* parent,
const QString& fsLabel, const QString& fsLabel,
qint64 firstSector, qint64 firstSector,
qint64 lastSector, qint64 lastSector,
Config::LuksGeneration luksFsType,
const QString& passphrase, const QString& passphrase,
PartitionTable::Flags flags ) PartitionTable::Flags flags )
{ {
@ -93,8 +94,10 @@ createNewEncryptedPartition( PartitionNode* parent,
newRoles |= PartitionRole::Luks; newRoles |= PartitionRole::Luks;
} }
FileSystem::Type luksType = luksGenerationToFSName( luksFsType );
FS::luks* fs = dynamic_cast< FS::luks* >( FS::luks* fs = dynamic_cast< FS::luks* >(
FileSystemFactory::create( FileSystem::Luks, firstSector, lastSector, device.logicalSize() ) ); FileSystemFactory::create( luksType, firstSector, lastSector, device.logicalSize() ) );
if ( !fs ) if ( !fs )
{ {
cError() << "cannot create LUKS filesystem. Giving up."; cError() << "cannot create LUKS filesystem. Giving up.";
@ -296,6 +299,24 @@ cryptVersion( Partition* partition )
return luksVersion; 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 Calamares::JobResult
execute( Operation& operation, const QString& failureMessage ) execute( Operation& operation, const QString& failureMessage )
{ {

View File

@ -11,6 +11,7 @@
#ifndef KPMHELPERS_H #ifndef KPMHELPERS_H
#define KPMHELPERS_H #define KPMHELPERS_H
#include "Config.h"
#include "Job.h" #include "Job.h"
#include <kpmcore/core/partitiontable.h> #include <kpmcore/core/partitiontable.h>
@ -83,6 +84,7 @@ Partition* createNewEncryptedPartition( PartitionNode* parent,
const QString& fsLabel, const QString& fsLabel,
qint64 firstSector, qint64 firstSector,
qint64 lastSector, qint64 lastSector,
Config::LuksGeneration luksFsType,
const QString& passphrase, const QString& passphrase,
PartitionTable::Flags flags ); PartitionTable::Flags flags );
@ -119,6 +121,17 @@ bool cryptLabel( Partition* partition, const QString& label );
*/ */
int cryptVersion( Partition* partition ); 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 /** @brief Return a result for an @p operation
* *
* Executes the operation, and if successful, returns a success result. * 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; lastSectorForRoot -= suggestedSwapSizeB / sectorSize + 1;
} }
core->layoutApply( dev, firstFreeSector, lastSectorForRoot, o.luksPassphrase ); core->layoutApply( dev, firstFreeSector, lastSectorForRoot, o.luksFsType, o.luksPassphrase );
if ( shouldCreateSwap ) if ( shouldCreateSwap )
{ {
@ -194,6 +194,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
QStringLiteral( "swap" ), QStringLiteral( "swap" ),
lastSectorForRoot + 1, lastSectorForRoot + 1,
dev->totalLogical() - 1, dev->totalLogical() - 1,
o.luksFsType,
o.luksPassphrase, o.luksPassphrase,
KPM_PARTITION_FLAG( None ) ); KPM_PARTITION_FLAG( None ) );
} }
@ -216,6 +217,12 @@ doReplacePartition( PartitionCoreModule* core, Device* dev, Partition* partition
cDebug() << "doReplacePartition for device" << partition->partitionPath(); 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() ); PartitionRole newRoles( partition->roles() );
if ( partition->roles().has( PartitionRole::Extended ) ) if ( partition->roles().has( PartitionRole::Extended ) )
{ {
@ -244,7 +251,7 @@ doReplacePartition( PartitionCoreModule* core, Device* dev, Partition* partition
core->deletePartition( dev, partition ); core->deletePartition( dev, partition );
} }
core->layoutApply( dev, firstSector, lastSector, o.luksPassphrase ); core->layoutApply( dev, firstSector, lastSector, o.luksFsType, o.luksPassphrase );
core->dumpQueue(); core->dumpQueue();
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@
* SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2019 Collabora Ltd * SPDX-FileCopyrightText: 2019 Collabora Ltd
* SPDX-FileCopyrightText: 2021 Anubhav Choudhary <ac.10edu@gmail.com> * 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 * SPDX-License-Identifier: GPL-3.0-or-later
* *
* Calamares is Free Software: see the License-Identifier above. * Calamares is Free Software: see the License-Identifier above.
@ -28,7 +29,6 @@
#include "gui/PartitionBarsView.h" #include "gui/PartitionBarsView.h"
#include "gui/PartitionLabelsView.h" #include "gui/PartitionLabelsView.h"
#include "gui/PartitionSplitterWidget.h" #include "gui/PartitionSplitterWidget.h"
#include "gui/ReplaceWidget.h"
#include "gui/ScanningDialog.h" #include "gui/ScanningDialog.h"
#include "Branding.h" #include "Branding.h"
@ -288,6 +288,16 @@ ChoicePage::setupChoices()
m_eraseFsTypesChoiceComboBox, &QComboBox::currentTextChanged, m_config, &Config::setEraseFsTypeChoice ); m_eraseFsTypesChoiceComboBox, &QComboBox::currentTextChanged, m_config, &Config::setEraseFsTypeChoice );
connect( m_config, &Config::eraseModeFilesystemChanged, this, &ChoicePage::onActionChanged ); connect( m_config, &Config::eraseModeFilesystemChanged, this, &ChoicePage::onActionChanged );
m_eraseButton->addOptionsComboBox( m_eraseFsTypesChoiceComboBox ); 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 ); m_itemsLayout->addWidget( m_alongsideButton );
@ -303,13 +313,8 @@ ChoicePage::setupChoices()
m_itemsLayout->addStretch(); 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, connect( m_grp,
buttonSignal, &QButtonGroup::idToggled,
this, this,
[ this ]( int id, bool checked ) [ this ]( int id, bool checked )
{ {
@ -449,7 +454,6 @@ ChoicePage::continueApplyDeviceChoice()
if ( m_lastSelectedDeviceIndex != m_drivesCombo->currentIndex() ) if ( m_lastSelectedDeviceIndex != m_drivesCombo->currentIndex() )
{ {
m_lastSelectedDeviceIndex = m_drivesCombo->currentIndex(); m_lastSelectedDeviceIndex = m_drivesCombo->currentIndex();
m_lastSelectedActionIndex = -1;
m_config->setInstallChoice( m_config->initialInstallChoice() ); m_config->setInstallChoice( m_config->initialInstallChoice() );
checkInstallChoiceRadioButton( m_config->installChoice() ); checkInstallChoiceRadioButton( m_config->installChoice() );
} }
@ -461,6 +465,18 @@ ChoicePage::continueApplyDeviceChoice()
void void
ChoicePage::onActionChanged() 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(); Device* currd = selectedDevice();
if ( currd ) if ( currd )
{ {
@ -483,9 +499,9 @@ ChoicePage::onEraseSwapChoiceChanged()
void void
ChoicePage::applyActionChoice( InstallChoice choice ) ChoicePage::applyActionChoice( InstallChoice choice )
{ {
cDebug() << "Prev" << m_lastSelectedActionIndex << "InstallChoice" << choice cDebug() << "InstallChoice" << choice << Config::installChoiceNames().find( choice );
<< Config::installChoiceNames().find( choice );
m_beforePartitionBarsView->selectionModel()->disconnect( SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ) ); m_beforePartitionBarsView->selectionModel()->disconnect( SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ) );
auto priorSelection = m_beforePartitionBarsView->selectionModel()->currentIndex();
m_beforePartitionBarsView->selectionModel()->clearSelection(); m_beforePartitionBarsView->selectionModel()->clearSelection();
m_beforePartitionBarsView->selectionModel()->clearCurrentIndex(); m_beforePartitionBarsView->selectionModel()->clearCurrentIndex();
@ -496,6 +512,7 @@ ChoicePage::applyActionChoice( InstallChoice choice )
auto gs = Calamares::JobQueue::instance()->globalStorage(); auto gs = Calamares::JobQueue::instance()->globalStorage();
PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(), PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(),
m_config->eraseFsType(), m_config->eraseFsType(),
m_config->luksFileSystemType(),
m_encryptWidget->passphrase(), m_encryptWidget->passphrase(),
gs->value( "efiSystemPartition" ).toString(), gs->value( "efiSystemPartition" ).toString(),
CalamaresUtils::GiBtoBytes( CalamaresUtils::GiBtoBytes(
@ -543,6 +560,12 @@ ChoicePage::applyActionChoice( InstallChoice choice )
this, this,
SLOT( onPartitionToReplaceSelected( QModelIndex, QModelIndex ) ), SLOT( onPartitionToReplaceSelected( QModelIndex, QModelIndex ) ),
Qt::UniqueConnection ); Qt::UniqueConnection );
// Maintain the selection for replace
if ( priorSelection.isValid() )
{
m_beforePartitionBarsView->selectionModel()->setCurrentIndex( priorSelection, QItemSelectionModel::Select );
}
break; break;
case InstallChoice::Alongside: case InstallChoice::Alongside:
@ -752,6 +775,7 @@ ChoicePage::doAlongsideApply()
m_core->layoutApply( dev, m_core->layoutApply( dev,
newLastSector + 2, newLastSector + 2,
oldLastSector, oldLastSector,
m_config->luksFileSystemType(),
m_encryptWidget->passphrase(), m_encryptWidget->passphrase(),
candidate->parent(), candidate->parent(),
candidate->roles() ); candidate->roles() );
@ -826,6 +850,7 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
m_core->layoutApply( selectedDevice(), m_core->layoutApply( selectedDevice(),
selectedPartition->firstSector(), selectedPartition->firstSector(),
selectedPartition->lastSector(), selectedPartition->lastSector(),
m_config->luksFileSystemType(),
m_encryptWidget->passphrase(), m_encryptWidget->passphrase(),
newParent, newParent,
newRoles ); newRoles );
@ -857,7 +882,8 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
selectedDevice(), selectedDevice(),
selectedPartition, selectedPartition,
{ gs->value( "defaultPartitionType" ).toString(), { gs->value( "defaultPartitionType" ).toString(),
gs->value( "defaultFileSystemType" ).toString(), m_config->replaceModeFilesystem(),
m_config->luksFileSystemType(),
m_encryptWidget->passphrase() } ); m_encryptWidget->passphrase() } );
Partition* homePartition = findPartitionByPath( { selectedDevice() }, *homePartitionPath ); Partition* homePartition = findPartitionByPath( { selectedDevice() }, *homePartitionPath );
@ -1731,10 +1757,20 @@ ChoicePage::createBootloaderPanel()
return panelWidget; 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 bool suitableFS = true;
// support the kind of encryption we enable here. if ( !m_config->allowZfsEncryption()
const bool suitableFS = m_eraseFsTypesChoiceComboBox ? m_eraseFsTypesChoiceComboBox->currentText() != "zfs" : true; && ( ( m_eraseFsTypesChoiceComboBox && m_eraseFsTypesChoiceComboBox->isVisible()
return (choice == InstallChoice::Erase) && m_enableEncryptionWidget && suitableFS; && 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: 2014-2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org> * SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2019 Collabora Ltd * SPDX-FileCopyrightText: 2019 Collabora Ltd
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* Calamares is Free Software: see the License-Identifier above. * Calamares is Free Software: see the License-Identifier above.
@ -152,6 +153,7 @@ private:
Calamares::Widgets::PrettyRadioButton* m_somethingElseButton; Calamares::Widgets::PrettyRadioButton* m_somethingElseButton;
QComboBox* m_eraseSwapChoiceComboBox = nullptr; // UI, see also Config's swap choice 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_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; DeviceInfoWidget* m_deviceInfoWidget;
@ -166,7 +168,6 @@ private:
QPointer< QComboBox > m_efiComboBox; QPointer< QComboBox > m_efiComboBox;
int m_lastSelectedDeviceIndex = -1; int m_lastSelectedDeviceIndex = -1;
int m_lastSelectedActionIndex = -1;
bool m_enableEncryptionWidget = false; bool m_enableEncryptionWidget = false;

View File

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

View File

@ -2,6 +2,7 @@
* *
* SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org> * SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@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 * SPDX-License-Identifier: GPL-3.0-or-later
* *
* Calamares is Free Software: see the License-Identifier above. * Calamares is Free Software: see the License-Identifier above.
@ -17,6 +18,8 @@
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h" #include "utils/Retranslator.h"
constexpr int ZFS_MIN_LENGTH = 8;
/** @brief Does this system support whole-disk encryption? /** @brief Does this system support whole-disk encryption?
* *
* Returns @c true if the system is likely to support encryption * Returns @c true if the system is likely to support encryption
@ -143,7 +146,7 @@ applyPixmap( QLabel* label, CalamaresUtils::ImageType pixmap )
} }
void void
EncryptWidget::updateState() EncryptWidget::updateState( const bool notify )
{ {
if ( m_ui->m_passphraseLineEdit->isVisible() ) if ( m_ui->m_passphraseLineEdit->isVisible() )
{ {
@ -155,6 +158,11 @@ EncryptWidget::updateState()
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusWarning ); applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusWarning );
m_ui->m_iconLabel->setToolTip( tr( "Please enter the same passphrase in both boxes." ) ); 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 ) else if ( p1 == p2 )
{ {
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusOk ); applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusOk );
@ -172,7 +180,10 @@ EncryptWidget::updateState()
if ( newState != m_state ) if ( newState != m_state )
{ {
m_state = newState; m_state = newState;
Q_EMIT stateChanged( m_state ); if ( notify )
{
Q_EMIT stateChanged( m_state );
}
} }
} }
@ -201,3 +212,13 @@ EncryptWidget::onCheckBoxStateChanged( int checked )
updateState(); 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: 2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@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 * SPDX-License-Identifier: GPL-3.0-or-later
* *
* Calamares is Free Software: see the License-Identifier above. * Calamares is Free Software: see the License-Identifier above.
@ -14,6 +15,8 @@
#include <QWidget> #include <QWidget>
#include <kpmcore/fs/filesystem.h>
namespace Ui namespace Ui
{ {
class EncryptWidget; class EncryptWidget;
@ -38,6 +41,12 @@ public:
Encryption state() const; Encryption state() const;
void setText( const QString& text ); 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; QString passphrase() const;
void retranslate(); void retranslate();
@ -46,12 +55,14 @@ signals:
void stateChanged( Encryption ); void stateChanged( Encryption );
private: private:
void updateState(); void updateState( const bool notify = true );
void onPassphraseEdited(); void onPassphraseEdited();
void onCheckBoxStateChanged( int checked ); void onCheckBoxStateChanged( int checked );
Ui::EncryptWidget* m_ui; Ui::EncryptWidget* m_ui;
Encryption m_state; Encryption m_state;
FileSystem::Type m_filesystem;
}; };
#endif // ENCRYPTWIDGET_H #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/core/partition.h>
#include <kpmcore/fs/filesystem.h> #include <kpmcore/fs/filesystem.h>
#include <kpmcore/fs/luks.h> #include <kpmcore/fs/luks.h>
#include <kpmcore/fs/luks2.h>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
@ -91,11 +92,18 @@ mapForPartition( Partition* partition, const QString& uuid )
map[ "parttype" ] = partition->type(); map[ "parttype" ] = partition->type();
map[ "partattrs" ] = partition->attributes(); map[ "partattrs" ] = partition->attributes();
map[ "features" ] = partition->fileSystem().features(); map[ "features" ] = partition->fileSystem().features();
if ( partition->fileSystem().type() == FileSystem::Luks if ( partition->fileSystem().type() == FileSystem::Luks
&& dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() ) && dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() )
{ {
map[ "fs" ] = untranslatedFS( 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[ "uuid" ] = uuid;
map[ "claimed" ] = PartitionInfo::format( partition ); // If we formatted it, it's ours map[ "claimed" ] = PartitionInfo::format( partition ); // If we formatted it, it's ours

View File

@ -64,6 +64,30 @@ userSwapChoices:
# ensureSuspendToDisk: true # ensureSuspendToDisk: true
# neverCreateSwap: false # 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. # Correctly draw nested (e.g. logical) partitions as such.
drawNestedPartitions: false drawNestedPartitions: false

View File

@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@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 # SPDX-License-Identifier: GPL-3.0-or-later
--- ---
$schema: https://json-schema.org/schema# $schema: https://json-schema.org/schema#
@ -14,13 +15,16 @@ properties:
# ensureSuspendToDisk: { type: boolean, default: true } # Legacy # ensureSuspendToDisk: { type: boolean, default: true } # Legacy
# neverCreateSwap: { type: boolean, default: false } # Legacy # neverCreateSwap: { type: boolean, default: false } # Legacy
allowZfsEncryption: { type: boolean, default: true }
drawNestedPartitions: { type: boolean, default: false } drawNestedPartitions: { type: boolean, default: false }
alwaysShowPartitionLabels: { type: boolean, default: true } alwaysShowPartitionLabels: { type: boolean, default: true }
defaultFileSystemType: { type: string } defaultFileSystemType: { type: string }
availableFileSystemTypes: { type: array, items: { 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 } enableLuksAutomatedPartitioning: { type: boolean, default: false }
allowManualPartitioning: { type: boolean, default: true } allowManualPartitioning: { type: boolean, default: true }
partitionLayout: { type: array } # TODO: specify items partitionLayout: { type: array } # TODO: specify items
initialPartitioningChoice: { type: string, enum: [ none, erase, replace, alongside, manual ] } initialPartitioningChoice: { type: string, enum: [ none, erase, replace, alongside, manual ] }

View File

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

View File

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

View File

@ -11,4 +11,4 @@
--- ---
# Setting emergency to true will make it so this module is still run # Setting emergency to true will make it so this module is still run
# when a prior module fails # 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 // Create the zpool
ZfsResult zfsResult; ZfsResult zfsResult;
if ( encrypt ) 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