diff --git a/.tx/config b/.tx/config index 65a8521b5..12d56dd73 100644 --- a/.tx/config +++ b/.tx/config @@ -2,23 +2,23 @@ # SPDX-License-Identifier: CC0-1.0 [main] -host = https://www.transifex.com +host = https://app.transifex.com -[calamares.calamares] +[o:calamares:p:calamares:r:calamares] file_filter = lang/calamares_.ts source_file = lang/calamares_en.ts source_lang = en -type = QT +type = QT -[calamares.fdo] +[o:calamares:p:calamares:r:fdo] file_filter = lang/desktop_.desktop source_file = calamares.desktop source_lang = en -type = DESKTOP +type = DESKTOP -[calamares.python] +[o:calamares:p:calamares:r:python] file_filter = lang/python//LC_MESSAGES/python.po source_file = lang/python.pot source_lang = en -type = PO +type = PO diff --git a/CHANGES-3.3 b/CHANGES-3.3 index a31f9bbb2..28c7bdaca 100644 --- a/CHANGES-3.3 +++ b/CHANGES-3.3 @@ -12,20 +12,42 @@ the history of the 3.2 series (2018-05 - 2022-08). This release contains contributions from (alphabetically by first name): - Adriaan de Groot + - Aleksey Samoilov - Anke Boersma + - Emir Sari + - Evan James + - Jeremy Attall + - Johannes Kamprad + - Mario Haustein + - Masato TOYOSHIMA + - Paolo Dongilli + - Peter Jung + - Shivanand + - wiz64 ## Core ## - Incompatible module-configuration changes, see #1438. - Branding entries use ${var} instead of @{var} for substitutions, in line with all the other substitution mechanisms used from C++ core. See documentation in `branding.desc`. - - Boost::Python requires at least version 1.72 - - KDE Frameworks must be version 5.58 or later + - Boost::Python requires at least version 1.72. + - KDE Frameworks must be version 5.58 or later. + - The `INSTALL_CONFIG` option has been removed. If you are installing + the example configuration files from the Calamares repository, just + stop. That was never a good idea, and you should keep your configs elsewhere. + - Progress percentage during install can now be localized. (thanks Emir) ## Modules ## - *dracut* added a configurable kernel name. (thanks Anke) + - *initcpiocfg* orders hookds slightly differently. (thanks Peter) - *localeq* moved to using Drawer instead of ComboBox in UI. (thanks Anke) - *keyboardq* moved to using Drawer instead of ComboBox in UI. (thanks Anke) + - *netinstall* now has a new *noncheckable* option for groups, which prevent + it a group from being checked/uncheckd as a whole. You can still check + individual items **in** the group though. (thanks Shivanand) + - *partition* can now pick LUKS or LUKS2. (thanks Jeremy) + - *zfs* creates a hostid through zgenhostid. + - *zfshostid* new module to copy zfs generated /etc/hostid # 3.3.0-alpha2 (2022-08-23) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04fbf687a..ffe61077b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,6 @@ set(CALAMARES_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") ### OPTIONS # -option(INSTALL_CONFIG "Install configuration files" OFF) option(INSTALL_POLKIT "Install Polkit configuration" ON) option(INSTALL_COMPLETION "Install shell completions" OFF) # When adding WITH_* that affects the ABI offered by libcalamares, @@ -136,7 +135,7 @@ set(CALAMARES_DESCRIPTION_SUMMARY "The distribution-independent installer framew # # Language en (source language) is added later. It isn't listed in # Transifex either. Get the list of languages and their status -# from https://transifex.com/calamares/calamares/ , or (preferably) +# from https://app.transifex.com/calamares/calamares/ , or (preferably) # use ci/txstats.py to automatically check. # # When adding a new language, take care that it is properly loaded @@ -214,10 +213,10 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS_DEBUG "-Og -g") set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") -set(CMAKE_C_FLAGS_RELEASE "-O4 -DNDEBUG") +set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") -set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined -Wl,--fatal-warnings") +set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined -Wl,--fatal-warnings ${CMAKE_SHARED_LINKER_FLAGS}") # If no build type is set, pick a reasonable one if(NOT CMAKE_BUILD_TYPE) @@ -542,7 +541,6 @@ add_subdirectory(src) add_feature_info(Python ${WITH_PYTHON} "Python job modules") add_feature_info(Qml ${WITH_QML} "QML UI support") -add_feature_info(Config ${INSTALL_CONFIG} "Install Calamares configuration") add_feature_info(Polkit ${INSTALL_POLKIT} "Install Polkit files") add_feature_info(KCrash ${BUILD_KF5Crash} "Crash dumps via KCrash") @@ -587,10 +585,6 @@ install( ### Miscellaneous installs # # -if(INSTALL_CONFIG) - install(FILES settings.conf DESTINATION share/calamares) -endif() - if(INSTALL_POLKIT) install(FILES com.github.calamares.calamares.policy DESTINATION "${POLKITQT-1_POLICY_FILES_INSTALL_DIR}") endif() @@ -607,7 +601,9 @@ install(FILES calamares.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/application install(FILES man/calamares.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/) -# uninstall target +### Uninstall +# +# configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @@ -617,6 +613,17 @@ configure_file( add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +### Developer convenience +# +# The module support files -- .desc files, .conf files -- are copied into the build +# directory so that it is possible to run `calamares -d` from there. Copy the +# top-level settings.conf as well, into the build directory. +if( settings.conf IS_NEWER_THAN ${CMAKE_BINARY_DIR}/settings.conf ) + configure_file(settings.conf ${CMAKE_BINARY_DIR}/settings.conf COPYONLY) +endif() + + + ### CMAKE SUMMARY REPORT # get_directory_property(SKIPPED_MODULES DIRECTORY src/modules DEFINITION LIST_SKIPPED_MODULES) diff --git a/CMakeModules/CalamaresAddModuleSubdirectory.cmake b/CMakeModules/CalamaresAddModuleSubdirectory.cmake index e0eed223e..55cf440ce 100644 --- a/CMakeModules/CalamaresAddModuleSubdirectory.cmake +++ b/CMakeModules/CalamaresAddModuleSubdirectory.cmake @@ -153,12 +153,10 @@ function( _calamares_add_module_subdirectory_impl ) get_filename_component( FLEXT ${MODULE_FILE} EXT ) if( "${FLEXT}" STREQUAL ".conf" ) - if( INSTALL_CONFIG ) - install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE} - DESTINATION ${MODULE_DATA_DESTINATION} ) - endif() + message(STATUS "Config ${MODULE_FILE}") list( APPEND MODULE_CONFIG_FILES ${MODULE_FILE} ) else() + message(STATUS "Non-Config ${MODULE_FILE}") install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE} DESTINATION ${MODULE_DESTINATION} ) endif() @@ -169,12 +167,7 @@ function( _calamares_add_module_subdirectory_impl ) message( " ${Green}TYPE:${ColorReset} jobmodule" ) message( " ${Green}MODULE_DESTINATION:${ColorReset} ${MODULE_DESTINATION}" ) if( MODULE_CONFIG_FILES ) - if ( INSTALL_CONFIG ) - set( _destination "${MODULE_DATA_DESTINATION}" ) - else() - set( _destination "[Build directory only]" ) - endif() - message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => ${_destination}" ) + message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory only]" ) endif() message( "" ) # We copy over the lang directory, if any diff --git a/CMakeModules/CalamaresAddPlugin.cmake b/CMakeModules/CalamaresAddPlugin.cmake index 7e2a3f583..a86ee6e71 100644 --- a/CMakeModules/CalamaresAddPlugin.cmake +++ b/CMakeModules/CalamaresAddPlugin.cmake @@ -104,10 +104,7 @@ function( calamares_add_plugin ) message( FATAL_ERROR "${Red}NO_CONFIG${ColorReset} is set, with configuration ${Red}${PLUGIN_CONFIG_FILES}${ColorReset}" ) endif() set( _destination "(unknown)" ) - if ( INSTALL_CONFIG AND NOT PLUGIN_NO_INSTALL ) - set( _destination "${PLUGIN_DATA_DESTINATION}" ) - elseif( NOT PLUGIN_NO_INSTALL ) - # Not INSTALL_CONFIG + if( NOT PLUGIN_NO_INSTALL ) set( _destination "[Build directory only]" ) else() set( _destination "[Skipping installation]" ) @@ -210,17 +207,12 @@ function( calamares_add_plugin ) set( _warned_config OFF ) foreach( PLUGIN_CONFIG_FILE ${PLUGIN_CONFIG_FILES} ) - if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} OR INSTALL_CONFIG ) + if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} ) configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) else() message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" ) set( _warned_config ON ) endif() - if ( INSTALL_CONFIG ) - install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} - DESTINATION ${PLUGIN_DATA_DESTINATION} ) - endif() endforeach() if ( _warned_config ) message( "" ) diff --git a/README.md b/README.md index 570adf575..43a336de1 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ [![Current issue](https://img.shields.io/badge/issue-in_progress-FE9B48)](https://github.com/calamares/calamares/labels/hacking%3A%20in-progress) [![GitHub release](https://img.shields.io/github/release/calamares/calamares.svg)](https://github.com/calamares/calamares/releases) -[![GitHub Build Status](https://img.shields.io/github/workflow/status/calamares/calamares/ci?label=GH%20build)](https://github.com/calamares/calamares/actions?query=workflow%3Aci) -[![GitHub license](https://img.shields.io/github/license/calamares/calamares.svg)](https://github.com/calamares/calamares/blob/calamares/LICENSES/GPL-3.0-or-later.txt) +[![GitHub Build Status](https://img.shields.io/github/actions/workflow/status/calamares/calamares/push.yml)](https://github.com/calamares/calamares/actions?query=workflow%3Aci) +[![GitHub license](https://img.shields.io/badge/license-Multiple-green)](https://github.com/calamares/calamares/tree/calamares/LICENSES) -| [Report a Bug](https://github.com/calamares/calamares/issues/new) | [Translate](https://www.transifex.com/projects/p/calamares/) | [Contribute](CONTRIBUTING.md) | [Matrix: #calamares:kde.org](https://webchat.kde.org/#/room/%23calamares:kde.org) | [IRC: Libera.Chat #calamares](https://kiwiirc.com/client/irc.libera.chat/#calamares) | [Wiki](https://github.com/calamares/calamares/wiki) | +| [Report a Bug](https://github.com/calamares/calamares/issues/new) | [Translate](https://app.transifex.com/calamares/calamares/) | [Contribute](CONTRIBUTING.md) | [Matrix: #calamares:kde.org](https://webchat.kde.org/#/room/%23calamares:kde.org) | [IRC: Libera.Chat #calamares](https://kiwiirc.com/client/irc.libera.chat/#calamares) | [Wiki](https://github.com/calamares/calamares/wiki) | |:--:|:--:|:--:|:--:|:--:|:--:| diff --git a/ci/txpull.sh b/ci/txpull.sh index 0f4a2aa47..31ebcedd8 100755 --- a/ci/txpull.sh +++ b/ci/txpull.sh @@ -55,7 +55,7 @@ done # .tx/config file to locate files, and overwrites them in the # filesystem with new (merged) translations. export QT_SELECT=5 -tx pull --force --source --all +transifex-client pull --force --all || exit 1 ### CLEANUP TRANSLATIONS diff --git a/ci/txpush.sh b/ci/txpush.sh index 1a0a7249b..666cddc4a 100755 --- a/ci/txpush.sh +++ b/ci/txpush.sh @@ -53,6 +53,10 @@ if test "x$1" = "x--no-tx" ; then } else # tx is the regular transifex command + tx() { + transifex-client "$@" + } + # txtag is used to tag in git to measure changes txtag() { git tag -f translation @@ -82,6 +86,15 @@ do done # XMLLINT is optional +if sed --version 2>&1 | grep -q GNU ; then + reinplace() { + sed -i'' "$@" + } +else + reinplace() { + sed -i '' "$@" + } +fi ### CREATE TRANSLATIONS # @@ -108,8 +121,8 @@ if test -n "$XMLLINT" ; then $XMLLINT --c14n11 "$TS_FILE" | { echo "" ; cat - ; } | $XMLLINT --format --encode utf-8 -o "$TS_FILE".new - && mv "$TS_FILE".new "$TS_FILE" fi -tx push --source --no-interactive -r calamares.calamares -tx push --source --no-interactive -r calamares.fdo +tx push --source -r calamares.calamares || exit 1 +tx push --source -r calamares.fdo || exit 1 ### PYTHON MODULES @@ -132,9 +145,8 @@ for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d | sort) ; d ${PYGETTEXT} -p ${MODULE_DIR}/lang -d ${MODULE_NAME} -o ${MODULE_NAME}.pot ${MODULE_DIR}/*.py POTFILE="${MODULE_DIR}/lang/${MODULE_NAME}.pot" if [ -f "$POTFILE" ]; then - sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE" - tx set -r calamares.${MODULE_NAME} --source -l en "$POTFILE" - tx push --source --no-interactive -r calamares.${MODULE_NAME} + reinplace '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE" + tx push --source -r calamares.${MODULE_NAME} fi else SHARED_PYTHON="$SHARED_PYTHON $FILES" @@ -145,9 +157,8 @@ done if test -n "$SHARED_PYTHON" ; then ${PYGETTEXT} -p lang -d python -o python.pot $SHARED_PYTHON POTFILE="lang/python.pot" - sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE" - tx set -r calamares.python --source -l en "$POTFILE" - tx push --source --no-interactive -r calamares.python + reinplace '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE" + tx push --source -r calamares.python fi txtag diff --git a/ci/txstats.py b/ci/txstats.py index 5fddc6bd6..f4aa8e196 100755 --- a/ci/txstats.py +++ b/ci/txstats.py @@ -53,7 +53,7 @@ class TransifexGetter(object): parser = configparser.ConfigParser() parser.read_file(f) - return parser.get("https://www.transifex.com", "password") + return parser.get("https://app.transifex.com", "password") except IOError as e: return None diff --git a/lang/calamares_en.ts b/lang/calamares_en.ts index 19c979644..7a92656b3 100644 --- a/lang/calamares_en.ts +++ b/lang/calamares_en.ts @@ -10,8 +10,8 @@ - Thanks to <a href="https://calamares.io/team/">the Calamares team</a> and the <a href="https://www.transifex.com/calamares/calamares/">Calamares translators team</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> development is sponsored by <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software. - Thanks to <a href="https://calamares.io/team/">the Calamares team</a> and the <a href="https://www.transifex.com/calamares/calamares/">Calamares translators team</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> development is sponsored by <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software. + Thanks to <a href="https://calamares.io/team/">the Calamares team</a> and the <a href="https://app.transifex.com/calamares/calamares/">Calamares translators team</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> development is sponsored by <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software. + @@ -49,27 +49,27 @@ BootLoaderModel - + Master Boot Record of %1 Master Boot Record of %1 - + Boot Partition Boot Partition - + System Partition System Partition - + Do not install a boot loader Do not install a boot loader - + %1 (%2) %1 (%2) @@ -156,7 +156,7 @@ Widget Tree - + Debug information Debug information @@ -164,12 +164,18 @@ Calamares::ExecutionViewStep - + + %p% + Progress percentage indicator: %p is where the number 0..100 is placed + + + + Set up Set up - + Install Install @@ -276,8 +282,8 @@ Calamares::RequirementsChecker - Requirements checking for module <i>%1</i> is complete. - Requirements checking for module <i>%1</i> is complete. + Requirements checking for module '%1' is complete. + @@ -507,12 +513,12 @@ The installer will quit and all changes will be lost. CalamaresWindow - + %1 Setup Program %1 Setup Program - + %1 Installer %1 Installer @@ -520,17 +526,18 @@ The installer will quit and all changes will be lost. ChangeFilesystemLabelJob - + Set filesystem label on %1. Set filesystem label on %1. - + Set filesystem label <strong>%1</strong> to partition <strong>%2</strong>. Set filesystem label <strong>%1</strong> to partition <strong>%2</strong>. - + + The installer failed to update partition table on disk '%1'. The installer failed to update partition table on disk '%1'. @@ -551,149 +558,149 @@ The installer will quit and all changes will be lost. Form - + Select storage de&vice: Select storage de&vice: - - - - + + + + Current: Current: - + After: After: - + <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + Reuse %1 as home partition for %2. Reuse %1 as home partition for %2. - + <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> - + %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. - + Boot loader location: Boot loader location: - + <strong>Select a partition to install on</strong> <strong>Select a partition to install on</strong> - + An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. - + The EFI system partition at %1 will be used for starting %2. The EFI system partition at %1 will be used for starting %2. - + EFI system partition: EFI system partition: - + This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. + + - - - + <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. - - - - + + + + <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. - - - - + + + + <strong>Replace a partition</strong><br/>Replaces a partition with %1. <strong>Replace a partition</strong><br/>Replaces a partition with %1. - + This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device already has an operating system on it, but the partition table <strong>%1</strong> is different from the needed <strong>%2</strong>.<br/> This storage device already has an operating system on it, but the partition table <strong>%1</strong> is different from the needed <strong>%2</strong>.<br/> - + This storage device has one of its partitions <strong>mounted</strong>. This storage device has one of its partitions <strong>mounted</strong>. - + This storage device is a part of an <strong>inactive RAID</strong> device. This storage device is a part of an <strong>inactive RAID</strong> device. - + No Swap No Swap - + Reuse Swap Reuse Swap - + Swap (no Hibernate) Swap (no Hibernate) - + Swap (with Hibernate) Swap (with Hibernate) - + Swap to file Swap to file @@ -762,46 +769,40 @@ The installer will quit and all changes will be lost. CommandList - - + Could not run command. Could not run command. - - The command runs in the host environment and needs to know the root path, but no rootMountPoint is defined. - The command runs in the host environment and needs to know the root path, but no rootMountPoint is defined. - - - - The command needs to know the user's name, but no username is defined. - The command needs to know the user's name, but no username is defined. + + The commands use variables that are not defined. Missing variables are: %1. + Config - + Set keyboard model to %1.<br/> Set keyboard model to %1.<br/> - + Set keyboard layout to %1/%2. Set keyboard layout to %1/%2. - + Set timezone to %1/%2. Set timezone to %1/%2. - + The system language will be set to %1. The system language will be set to %1. - + The numbers and dates locale will be set to %1. The numbers and dates locale will be set to %1. @@ -837,96 +838,96 @@ The installer will quit and all changes will be lost. - This computer does not satisfy the minimum requirements for setting up %1.<br/>Setup cannot continue. <a href="#details">Details...</a> - This computer does not satisfy the minimum requirements for setting up %1.<br/>Setup cannot continue. <a href="#details">Details...</a> + This computer does not satisfy the minimum requirements for setting up %1.<br/>Setup cannot continue. + - - This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. <a href="#details">Details...</a> - This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. <a href="#details">Details...</a> + + This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. + - + This computer does not satisfy some of the recommended requirements for setting up %1.<br/>Setup can continue, but some features might be disabled. This computer does not satisfy some of the recommended requirements for setting up %1.<br/>Setup can continue, but some features might be disabled. - + This computer does not satisfy some of the recommended requirements for installing %1.<br/>Installation can continue, but some features might be disabled. This computer does not satisfy some of the recommended requirements for installing %1.<br/>Installation can continue, but some features might be disabled. - + This program will ask you some questions and set up %2 on your computer. This program will ask you some questions and set up %2 on your computer. - + <h1>Welcome to the Calamares setup program for %1</h1> <h1>Welcome to the Calamares setup program for %1</h1> - + <h1>Welcome to %1 setup</h1> <h1>Welcome to %1 setup</h1> - + <h1>Welcome to the Calamares installer for %1</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>Welcome to the %1 installer</h1> <h1>Welcome to the %1 installer</h1> - + Your username is too long. Your username is too long. - + '%1' is not allowed as username. '%1' is not allowed as username. - + Your username must start with a lowercase letter or underscore. Your username must start with a lowercase letter or underscore. - + Only lowercase letters, numbers, underscore and hyphen are allowed. Only lowercase letters, numbers, underscore and hyphen are allowed. - + Your hostname is too short. Your hostname is too short. - + Your hostname is too long. Your hostname is too long. - + '%1' is not allowed as hostname. '%1' is not allowed as hostname. - + Only letters, numbers, underscore and hyphen are allowed. Only letters, numbers, underscore and hyphen are allowed. - + Your passwords do not match! Your passwords do not match! - + OK! OK! @@ -1082,22 +1083,22 @@ The installer will quit and all changes will be lost. FS Label: - + En&crypt En&crypt - + Logical Logical - + Primary Primary - + GPT GPT @@ -1115,43 +1116,43 @@ The installer will quit and all changes will be lost. CreatePartitionJob - + Create new %1MiB partition on %3 (%2) with entries %4. Create new %1MiB partition on %3 (%2) with entries %4. - + Create new %1MiB partition on %3 (%2). Create new %1MiB partition on %3 (%2). - + Create new %2MiB partition on %4 (%3) with file system %1. Create new %2MiB partition on %4 (%3) with file system %1. - + Create new <strong>%1MiB</strong> partition on <strong>%3</strong> (%2) with entries <em>%4</em>. Create new <strong>%1MiB</strong> partition on <strong>%3</strong> (%2) with entries <em>%4</em>. - + Create new <strong>%1MiB</strong> partition on <strong>%3</strong> (%2). Create new <strong>%1MiB</strong> partition on <strong>%3</strong> (%2). - + Create new <strong>%2MiB</strong> partition on <strong>%4</strong> (%3) with file system <strong>%1</strong>. Create new <strong>%2MiB</strong> partition on <strong>%4</strong> (%3) with file system <strong>%1</strong>. - - + + Creating new %1 partition on %2. Creating new %1 partition on %2. - + The installer failed to create partition on disk '%1'. The installer failed to create partition on disk '%1'. @@ -1317,7 +1318,7 @@ The installer will quit and all changes will be lost. DeviceInfoWidget - + This device has a <strong>%1</strong> partition table. This device has a <strong>%1</strong> partition table. @@ -1327,7 +1328,7 @@ The installer will quit and all changes will be lost. This is a <strong>loop</strong> device.<br><br>It is a pseudo-device with no partition table that makes a file accessible as a block device. This kind of setup usually only contains a single filesystem. - + This installer <strong>cannot detect a partition table</strong> on the selected storage device.<br><br>The device either has no partition table, or the partition table is corrupted or of an unknown type.<br>This installer can create a new partition table for you, either automatically, or through the manual partitioning page. This installer <strong>cannot detect a partition table</strong> on the selected storage device.<br><br>The device either has no partition table, or the partition table is corrupted or of an unknown type.<br>This installer can create a new partition table for you, either automatically, or through the manual partitioning page. @@ -1342,7 +1343,7 @@ The installer will quit and all changes will be lost. <br><br>This partition table type is only advisable on older systems which start from a <strong>BIOS</strong> boot environment. GPT is recommended in most other cases.<br><br><strong>Warning:</strong> the MBR partition table is an obsolete MS-DOS era standard.<br>Only 4 <em>primary</em> partitions may be created, and of those 4, one can be an <em>extended</em> partition, which may in turn contain many <em>logical</em> partitions. - + The type of <strong>partition table</strong> on the selected storage device.<br><br>The only way to change the partition table type is to erase and recreate the partition table from scratch, which destroys all data on the storage device.<br>This installer will keep the current partition table unless you explicitly choose otherwise.<br>If unsure, on modern systems GPT is preferred. The type of <strong>partition table</strong> on the selected storage device.<br><br>The only way to change the partition table type is to erase and recreate the partition table from scratch, which destroys all data on the storage device.<br>This installer will keep the current partition table unless you explicitly choose otherwise.<br>If unsure, on modern systems GPT is preferred. @@ -1489,11 +1490,16 @@ The installer will quit and all changes will be lost. Confirm passphrase - - + + Please enter the same passphrase in both boxes. Please enter the same passphrase in both boxes. + + + Password must be a minimum of %1 characters + + ErrorDialog @@ -1511,57 +1517,57 @@ The installer will quit and all changes will be lost. FillGlobalStorageJob - + Set partition information Set partition information - + Install %1 on <strong>new</strong> %2 system partition with features <em>%3</em> Install %1 on <strong>new</strong> %2 system partition with features <em>%3</em> - + Install %1 on <strong>new</strong> %2 system partition. Install %1 on <strong>new</strong> %2 system partition. - + Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong> and features <em>%3</em>. Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong> and features <em>%3</em>. - + Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong>%3. Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong>%3. - + Install %2 on %3 system partition <strong>%1</strong> with features <em>%4</em>. Install %2 on %3 system partition <strong>%1</strong> with features <em>%4</em>. - + Set up %3 partition <strong>%1</strong> with mount point <strong>%2</strong> and features <em>%4</em>. Set up %3 partition <strong>%1</strong> with mount point <strong>%2</strong> and features <em>%4</em>. - + Set up %3 partition <strong>%1</strong> with mount point <strong>%2</strong>%4. Set up %3 partition <strong>%1</strong> with mount point <strong>%2</strong>%4. - + Install %2 on %3 system partition <strong>%1</strong>. Install %2 on %3 system partition <strong>%1</strong>. - + Install boot loader on <strong>%1</strong>. Install boot loader on <strong>%1</strong>. - + Setting up mount points. Setting up mount points. @@ -1657,75 +1663,131 @@ The installer will quit and all changes will be lost. GeneralRequirements - - has at least %1 GiB available drive space - has at least %1 GiB available drive space + + Please ensure the system has at least %1 GiB available drive space. + - + + Available drive space is all of the hard disks and SSDs connected to the system. + + + + There is not enough drive space. At least %1 GiB is required. There is not enough drive space. At least %1 GiB is required. - + has at least %1 GiB working memory has at least %1 GiB working memory - + The system does not have enough working memory. At least %1 GiB is required. The system does not have enough working memory. At least %1 GiB is required. - + is plugged in to a power source is plugged in to a power source - + The system is not plugged in to a power source. The system is not plugged in to a power source. - + is connected to the Internet is connected to the Internet - + The system is not connected to the Internet. The system is not connected to the Internet. - + is running the installer as an administrator (root) is running the installer as an administrator (root) - + The setup program is not running with administrator rights. The setup program is not running with administrator rights. - + The installer is not running with administrator rights. The installer is not running with administrator rights. - + has a screen large enough to show the whole installer has a screen large enough to show the whole installer - + The screen is too small to display the setup program. The screen is too small to display the setup program. - + The screen is too small to display the installer. The screen is too small to display the installer. + + + is always false + + + + + The computer says no. + + + + + is always false (slowly) + + + + + The computer says no (slowly). + + + + + is always true + + + + + The computer says yes. + + + + + is always true (slowly) + + + + + The computer says yes (slowly). + + + + + is checked three times. + + + + + The snark has not been checked three times. + The (some mythological beast) has not been checked three times. + + HostInfoJob @@ -1878,32 +1940,32 @@ The installer will quit and all changes will be lost. <h1>License Agreement</h1> - + I accept the terms and conditions above. I accept the terms and conditions above. - + Please review the End User License Agreements (EULAs). Please review the End User License Agreements (EULAs). - + This setup procedure will install proprietary software that is subject to licensing terms. This setup procedure will install proprietary software that is subject to licensing terms. - + If you do not agree with the terms, the setup procedure cannot continue. If you do not agree with the terms, the setup procedure cannot continue. - + This setup procedure can install proprietary software that is subject to licensing terms in order to provide additional features and enhance the user experience. This setup procedure can install proprietary software that is subject to licensing terms in order to provide additional features and enhance the user experience. - + If you do not agree with the terms, proprietary software will not be installed, and open source alternatives will be used instead. If you do not agree with the terms, proprietary software will not be installed, and open source alternatives will be used instead. @@ -2006,7 +2068,7 @@ The installer will quit and all changes will be lost. LocaleTests - + Quit Quit @@ -2014,7 +2076,7 @@ The installer will quit and all changes will be lost. LocaleViewStep - + Location Location @@ -2229,12 +2291,12 @@ The installer will quit and all changes will be lost. OEMViewStep - + OEM Configuration OEM Configuration - + Set the OEM Batch Identifier to <code>%1</code>. Set the OEM Batch Identifier to <code>%1</code>. @@ -2242,29 +2304,29 @@ The installer will quit and all changes will be lost. Offline - + Select your preferred Region, or use the default settings. Select your preferred Region, or use the default settings. - - - + + + Timezone: %1 Timezone: %1 - + Select your preferred Zone within your Region. Select your preferred Zone within your Region. - + Zones Zones - + You can fine-tune Language and Locale settings below. You can fine-tune Language and Locale settings below. @@ -2539,7 +2601,7 @@ The installer will quit and all changes will be lost. Unknown error - + Password is empty Password is empty @@ -2852,17 +2914,17 @@ The installer will quit and all changes will be lost. I&nstall boot loader on: - + Are you sure you want to create a new partition table on %1? Are you sure you want to create a new partition table on %1? - + Can not create new partition Can not create new partition - + The partition table on %1 already has %2 primary partitions, and no more can be added. Please remove one primary partition and add an extended partition, instead. The partition table on %1 already has %2 primary partitions, and no more can be added. Please remove one primary partition and add an extended partition, instead. @@ -2905,72 +2967,72 @@ The installer will quit and all changes will be lost. After: - + No EFI system partition configured No EFI system partition configured - + EFI system partition configured incorrectly EFI system partition configured incorrectly - + An EFI system partition is necessary to start %1.<br/><br/>To configure an EFI system partition, go back and select or create a suitable filesystem. An EFI system partition is necessary to start %1.<br/><br/>To configure an EFI system partition, go back and select or create a suitable filesystem. - + The filesystem must be mounted on <strong>%1</strong>. The filesystem must be mounted on <strong>%1</strong>. - + The filesystem must have type FAT32. The filesystem must have type FAT32. - + The filesystem must be at least %1 MiB in size. The filesystem must be at least %1 MiB in size. - + The filesystem must have flag <strong>%1</strong> set. The filesystem must have flag <strong>%1</strong> set. - + You can continue without setting up an EFI system partition but your system may fail to start. You can continue without setting up an EFI system partition but your system may fail to start. - + Option to use GPT on BIOS Option to use GPT on BIOS - + A GPT partition table is the best option for all systems. This installer supports such a setup for BIOS systems too.<br/><br/>To configure a GPT partition table on BIOS, (if not done so already) go back and set the partition table to GPT, next create a 8 MB unformatted partition with the <strong>%2</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. A GPT partition table is the best option for all systems. This installer supports such a setup for BIOS systems too.<br/><br/>To configure a GPT partition table on BIOS, (if not done so already) go back and set the partition table to GPT, next create a 8 MB unformatted partition with the <strong>%2</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. - + Boot partition not encrypted Boot partition not encrypted - + A separate boot partition was set up together with an encrypted root partition, but the boot partition is not encrypted.<br/><br/>There are security concerns with this kind of setup, because important system files are kept on an unencrypted partition.<br/>You may continue if you wish, but filesystem unlocking will happen later during system startup.<br/>To encrypt the boot partition, go back and recreate it, selecting <strong>Encrypt</strong> in the partition creation window. A separate boot partition was set up together with an encrypted root partition, but the boot partition is not encrypted.<br/><br/>There are security concerns with this kind of setup, because important system files are kept on an unencrypted partition.<br/>You may continue if you wish, but filesystem unlocking will happen later during system startup.<br/>To encrypt the boot partition, go back and recreate it, selecting <strong>Encrypt</strong> in the partition creation window. - + has at least one disk device available. has at least one disk device available. - + There are no partitions to install on. There are no partitions to install on. @@ -3018,17 +3080,17 @@ The installer will quit and all changes will be lost. PreserveFiles - + Saving files for later ... Saving files for later ... - + No files configured to save for later. No files configured to save for later. - + Not all of the configured files could be preserved. Not all of the configured files could be preserved. @@ -3105,7 +3167,7 @@ Output: QObject - + %1 (%2) %1 (%2) @@ -3217,92 +3279,17 @@ Output: The installer failed to remove a volume group named '%1'. - - ReplaceWidget - - - Form - Form - - - - Select where to install %1.<br/><font color="red">Warning: </font>this will delete all files on the selected partition. - Select where to install %1.<br/><font color="red">Warning: </font>this will delete all files on the selected partition. - - - - The selected item does not appear to be a valid partition. - The selected item does not appear to be a valid partition. - - - - %1 cannot be installed on empty space. Please select an existing partition. - %1 cannot be installed on empty space. Please select an existing partition. - - - - %1 cannot be installed on an extended partition. Please select an existing primary or logical partition. - %1 cannot be installed on an extended partition. Please select an existing primary or logical partition. - - - - %1 cannot be installed on this partition. - %1 cannot be installed on this partition. - - - - Data partition (%1) - Data partition (%1) - - - - Unknown system partition (%1) - Unknown system partition (%1) - - - - %1 system partition (%2) - %1 system partition (%2) - - - - <strong>%4</strong><br/><br/>The partition %1 is too small for %2. Please select a partition with capacity at least %3 GiB. - <strong>%4</strong><br/><br/>The partition %1 is too small for %2. Please select a partition with capacity at least %3 GiB. - - - - <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. - <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. - - - - - - <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. - <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. - - - - The EFI system partition at %1 will be used for starting %2. - The EFI system partition at %1 will be used for starting %2. - - - - EFI system partition: - EFI system partition: - - Requirements - + <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> - + <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> @@ -3317,63 +3304,63 @@ Output: Resize Filesystem Job - + Invalid configuration Invalid configuration - + The file-system resize job has an invalid configuration and will not run. The file-system resize job has an invalid configuration and will not run. - + KPMCore not Available KPMCore not Available - + Calamares cannot start KPMCore for the file-system resize job. Calamares cannot start KPMCore for the file-system resize job. - - - - - + + + + + Resize Failed Resize Failed - + The filesystem %1 could not be found in this system, and cannot be resized. The filesystem %1 could not be found in this system, and cannot be resized. - + The device %1 could not be found in this system, and cannot be resized. The device %1 could not be found in this system, and cannot be resized. - - + + The filesystem %1 cannot be resized. The filesystem %1 cannot be resized. - - + + The device %1 cannot be resized. The device %1 cannot be resized. - + The filesystem %1 must be resized, but cannot. The filesystem %1 must be resized, but cannot. - + The device %1 must be resized, but cannot The device %1 must be resized, but cannot @@ -3429,16 +3416,11 @@ Output: - ResultsListDialog + ResultsListWidget - - For best results, please ensure that this computer: - For best results, please ensure that this computer: - - - - System requirements - System requirements + + Checking requirements again in a few seconds ... + @@ -4066,12 +4048,12 @@ Output: %1 support - + About %1 setup About %1 setup - + About %1 installer About %1 installer @@ -4121,17 +4103,17 @@ Output: - + Failed to create zpool Failed to create zpool - + Failed to create dataset Failed to create dataset - + The output was: The output was: @@ -4140,14 +4122,26 @@ Output: calamares-sidebar + About About + Debug Debug + + + Show information about Calamares + + + + + Show debug information + + finishedq @@ -4206,63 +4200,54 @@ Output: Restart - - i18n - - - <h1>Languages</h1> </br> - The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>. - <h1>Languages</h1> </br> - The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>. - - - - <h1>Locales</h1> </br> - The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - <h1>Locales</h1> </br> - The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - - - - Back - Back - - keyboardq - + To activate keyboard preview, select a layout. To activate keyboard preview, select a layout. - - Keyboard Model: - Keyboard Model: + + <b>Keyboard Model:&nbsp;&nbsp;</b> + - - Layouts - Layouts + + Layout + - + + Variant + + + + Type here to test your keyboard Type here to test your keyboard - - - Variants - Variants - localeq - + + Change Change + + + <h3>Languages</h3> </br> + The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>. + + + + + <h3>Locales</h3> </br> + The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. + + notesqml diff --git a/lang/python.pot b/lang/python.pot index 7321113a2..386a07071 100644 --- a/lang/python.pot +++ b/lang/python.pot @@ -2,426 +2,393 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-05-29 16:17+0200\n" +"POT-Creation-Date: 2023-04-24 23:48+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: \n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: src/modules/grubcfg/main.py:28 -msgid "Configure GRUB." -msgstr "Configure GRUB." - -#: src/modules/mount/main.py:42 -msgid "Mounting partitions." -msgstr "Mounting partitions." - -#: src/modules/mount/main.py:88 src/modules/mount/main.py:124 -msgid "Internal error mounting zfs datasets" -msgstr "Internal error mounting zfs datasets" - -#: src/modules/mount/main.py:100 -msgid "Failed to import zpool" -msgstr "Failed to import zpool" - -#: src/modules/mount/main.py:116 -msgid "Failed to unlock zpool" -msgstr "Failed to unlock zpool" - -#: src/modules/mount/main.py:133 src/modules/mount/main.py:138 -msgid "Failed to set zfs mountpoint" -msgstr "Failed to set zfs mountpoint" - -#: src/modules/mount/main.py:229 src/modules/initcpiocfg/main.py:235 -#: src/modules/initcpiocfg/main.py:239 src/modules/rawfs/main.py:164 -#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89 -#: src/modules/openrcdmcryptcfg/main.py:72 -#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/fstab/main.py:394 -#: src/modules/fstab/main.py:400 src/modules/fstab/main.py:428 -#: src/modules/localecfg/main.py:140 src/modules/networkcfg/main.py:105 -msgid "Configuration Error" -msgstr "Configuration Error" - -#: src/modules/mount/main.py:230 src/modules/initcpiocfg/main.py:236 -#: src/modules/rawfs/main.py:165 src/modules/initramfscfg/main.py:86 -#: src/modules/openrcdmcryptcfg/main.py:73 src/modules/fstab/main.py:395 -msgid "No partitions are defined for
{!s}
to use." -msgstr "No partitions are defined for
{!s}
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 "" -"systemctl {arg!s} call in chroot returned error code {num!s}." +#: src/modules/bootloader/main.py:46 +msgid "Install bootloader." msgstr "" -"systemctl {arg!s} call in chroot returned error code {num!s}." -#: src/modules/services-systemd/main.py:63 -#: src/modules/services-systemd/main.py:69 -msgid "Cannot enable systemd service {name!s}." -msgstr "Cannot enable systemd service {name!s}." - -#: src/modules/services-systemd/main.py:65 -msgid "Cannot enable systemd target {name!s}." -msgstr "Cannot enable systemd target {name!s}." - -#: src/modules/services-systemd/main.py:67 -msgid "Cannot enable systemd timer {name!s}." -msgstr "Cannot enable systemd timer {name!s}." - -#: src/modules/services-systemd/main.py:71 -msgid "Cannot disable systemd target {name!s}." -msgstr "Cannot disable systemd target {name!s}." - -#: src/modules/services-systemd/main.py:73 -msgid "Cannot mask systemd unit {name!s}." -msgstr "Cannot mask systemd unit {name!s}." - -#: src/modules/services-systemd/main.py:75 -msgid "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." +#: src/modules/bootloader/main.py:640 +msgid "Failed to install grub, no partitions defined in global storage" msgstr "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." -#: src/modules/unpackfs/main.py:34 -msgid "Filling up filesystems." -msgstr "Filling up filesystems." - -#: src/modules/unpackfs/main.py:254 -msgid "rsync failed with error code {}." -msgstr "rsync failed with error code {}." - -#: src/modules/unpackfs/main.py:299 -msgid "Unpacking image {}/{}, file {}/{}" -msgstr "Unpacking image {}/{}, file {}/{}" - -#: src/modules/unpackfs/main.py:314 -msgid "Starting to unpack {}" -msgstr "Starting to unpack {}" - -#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:467 -msgid "Failed to unpack image \"{}\"" -msgstr "Failed to unpack image \"{}\"" - -#: src/modules/unpackfs/main.py:430 -msgid "No mount point for root partition" -msgstr "No mount point for root partition" - -#: src/modules/unpackfs/main.py:431 -msgid "globalstorage does not contain a \"rootMountPoint\" key." -msgstr "globalstorage does not contain a \"rootMountPoint\" key." - -#: src/modules/unpackfs/main.py:434 -msgid "Bad mount point for root partition" -msgstr "Bad mount point for root partition" - -#: src/modules/unpackfs/main.py:435 -msgid "rootMountPoint is \"{}\", which does not exist." -msgstr "rootMountPoint is \"{}\", which does not exist." - -#: src/modules/unpackfs/main.py:439 src/modules/unpackfs/main.py:455 -#: src/modules/unpackfs/main.py:459 src/modules/unpackfs/main.py:465 -#: src/modules/unpackfs/main.py:480 -msgid "Bad unpackfs configuration" -msgstr "Bad unpackfs configuration" - -#: src/modules/unpackfs/main.py:440 -msgid "There is no configuration information." -msgstr "There is no configuration information." - -#: src/modules/unpackfs/main.py:456 -msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel" -msgstr "The filesystem for \"{}\" ({}) is not supported by your current kernel" - -#: src/modules/unpackfs/main.py:460 -msgid "The source filesystem \"{}\" does not exist" -msgstr "The source filesystem \"{}\" does not exist" - -#: src/modules/unpackfs/main.py:466 -msgid "" -"Failed to find unsquashfs, make sure you have the squashfs-tools package " -"installed." +#: src/modules/bootloader/main.py:895 +msgid "Bootloader installation error" msgstr "" -"Failed to find unsquashfs, make sure you have the squashfs-tools package " -"installed." -#: src/modules/unpackfs/main.py:481 -msgid "The destination \"{}\" in the target system is not a directory" -msgstr "The destination \"{}\" in the target system is not a directory" +#: src/modules/bootloader/main.py:896 +msgid "" +"The bootloader could not be installed. The installation command
{!s} returned error code {!s}."
+msgstr ""
 
-#: src/modules/displaymanager/main.py:524
-msgid "Cannot write KDM configuration file"
-msgstr "Cannot write KDM configuration file"
-
-#: src/modules/displaymanager/main.py:525
-msgid "KDM config file {!s} does not exist"
-msgstr "KDM config file {!s} does not exist"
-
-#: src/modules/displaymanager/main.py:586
+#: src/modules/displaymanager/main.py:507
 msgid "Cannot write LXDM configuration file"
-msgstr "Cannot write LXDM configuration file"
+msgstr ""
 
-#: src/modules/displaymanager/main.py:587
+#: src/modules/displaymanager/main.py:508
 msgid "LXDM config file {!s} does not exist"
-msgstr "LXDM config file {!s} does not exist"
+msgstr ""
 
-#: src/modules/displaymanager/main.py:670
+#: src/modules/displaymanager/main.py:596
 msgid "Cannot write LightDM configuration file"
-msgstr "Cannot write LightDM configuration file"
+msgstr ""
 
-#: src/modules/displaymanager/main.py:671
+#: src/modules/displaymanager/main.py:597
 msgid "LightDM config file {!s} does not exist"
-msgstr "LightDM config file {!s} does not exist"
+msgstr ""
 
-#: src/modules/displaymanager/main.py:745
+#: src/modules/displaymanager/main.py:682
 msgid "Cannot configure LightDM"
-msgstr "Cannot configure LightDM"
+msgstr ""
 
-#: src/modules/displaymanager/main.py:746
+#: src/modules/displaymanager/main.py:683
 msgid "No LightDM greeter installed."
-msgstr "No LightDM greeter installed."
+msgstr ""
 
-#: src/modules/displaymanager/main.py:777
+#: src/modules/displaymanager/main.py:714
 msgid "Cannot write SLIM configuration file"
-msgstr "Cannot write SLIM configuration file"
+msgstr ""
 
-#: src/modules/displaymanager/main.py:778
+#: src/modules/displaymanager/main.py:715
 msgid "SLIM config file {!s} does not exist"
-msgstr "SLIM config file {!s} does not exist"
+msgstr ""
 
-#: src/modules/displaymanager/main.py:992
+#: src/modules/displaymanager/main.py:933
 msgid "No display managers selected for the displaymanager module."
-msgstr "No display managers selected for the displaymanager module."
+msgstr ""
 
-#: src/modules/displaymanager/main.py:993
+#: src/modules/displaymanager/main.py:934
 msgid ""
 "The displaymanagers list is empty or undefined in both globalstorage and "
 "displaymanager.conf."
 msgstr ""
-"The displaymanagers list is empty or undefined in both globalstorage and "
-"displaymanager.conf."
 
-#: src/modules/displaymanager/main.py:1075
+#: src/modules/displaymanager/main.py:1021
 msgid "Display manager configuration was incomplete"
-msgstr "Display manager configuration was incomplete"
+msgstr ""
 
-#: src/modules/initcpiocfg/main.py:28
-msgid "Configuring mkinitcpio."
-msgstr "Configuring mkinitcpio."
+#: src/modules/dracut/main.py:29
+msgid "Creating initramfs with dracut."
+msgstr ""
 
-#: src/modules/initcpiocfg/main.py:240 src/modules/initramfscfg/main.py:90
-#: src/modules/openrcdmcryptcfg/main.py:77 src/modules/fstab/main.py:401
+#: src/modules/dracut/main.py:63
+msgid "Failed to run dracut"
+msgstr ""
+
+#: src/modules/dracut/main.py:64
+#, python-brace-format
+msgid "Dracut failed to run on the target with return code: {return_code}"
+msgstr ""
+
+#: src/modules/dummypython/main.py:35
+msgid "Dummy python job."
+msgstr ""
+
+#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
+#: src/modules/dummypython/main.py:94
+msgid "Dummy python step {}"
+msgstr ""
+
+#: src/modules/fstab/main.py:28
+msgid "Writing fstab."
+msgstr ""
+
+#: src/modules/fstab/main.py:377 src/modules/fstab/main.py:383
+#: src/modules/fstab/main.py:411 src/modules/initcpiocfg/main.py:245
+#: src/modules/initcpiocfg/main.py:249 src/modules/initramfscfg/main.py:85
+#: src/modules/initramfscfg/main.py:89 src/modules/localecfg/main.py:140
+#: src/modules/mount/main.py:329 src/modules/networkcfg/main.py:105
+#: src/modules/openrcdmcryptcfg/main.py:72
+#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/rawfs/main.py:164
+msgid "Configuration Error"
+msgstr ""
+
+#: src/modules/fstab/main.py:378 src/modules/initramfscfg/main.py:86
+#: src/modules/mount/main.py:330 src/modules/openrcdmcryptcfg/main.py:73
+#: src/modules/rawfs/main.py:165
+msgid "No partitions are defined for 
{!s}
to use." +msgstr "" + +#: src/modules/fstab/main.py:384 src/modules/initramfscfg/main.py:90 #: src/modules/localecfg/main.py:141 src/modules/networkcfg/main.py:106 +#: src/modules/openrcdmcryptcfg/main.py:77 msgid "No root mount point is given for
{!s}
to use." -msgstr "No root mount point is given for
{!s}
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 {arg!s} for service {name!s} in run-" -"level {level!s}." msgstr "" -"Unknown service-action {arg!s} for service {name!s} in run-" -"level {level!s}." -#: src/modules/services-openrc/main.py:94 -msgid "" -"rc-update {arg!s} call in chroot returned error code {num!s}." +#: src/modules/fstab/main.py:412 +msgid "No
{!s}
configuration is given for
{!s}
to use." msgstr "" -"rc-update {arg!s} call in chroot returned error code {num!s}." -#: src/modules/services-openrc/main.py:101 -msgid "Target runlevel does not exist" -msgstr "Target runlevel does not exist" - -#: src/modules/services-openrc/main.py:102 -msgid "" -"The path for runlevel {level!s} is {path!s}, which does not " -"exist." +#: src/modules/grubcfg/main.py:29 +msgid "Configure GRUB." msgstr "" -"The path for runlevel {level!s} is {path!s}, which does not " -"exist." -#: src/modules/services-openrc/main.py:110 -msgid "Target service does not exist" -msgstr "Target service does not exist" - -#: src/modules/services-openrc/main.py:111 -msgid "" -"The path for service {name!s} is {path!s}, which does not " -"exist." +#: src/modules/hwclock/main.py:26 +msgid "Setting hardware clock." msgstr "" -"The path for service {name!s} is {path!s}, which does not " -"exist." -#: src/modules/plymouthcfg/main.py:27 -msgid "Configure Plymouth theme" -msgstr "Configure Plymouth theme" +#: src/modules/initcpiocfg/main.py:27 +msgid "Configuring mkinitcpio." +msgstr "" + +#: src/modules/initcpiocfg/main.py:246 +msgid "No partitions are defined for
initcpiocfg
." +msgstr "" + +#: src/modules/initcpiocfg/main.py:250 +msgid "No root mount point for
initcpiocfg
." +msgstr "" + +#: src/modules/initramfscfg/main.py:32 +msgid "Configuring initramfs." +msgstr "" + +#: src/modules/localecfg/main.py:31 +msgid "Configuring locales." +msgstr "" + +#: src/modules/mkinitfs/main.py:27 +msgid "Creating initramfs with mkinitfs." +msgstr "" + +#: src/modules/mkinitfs/main.py:49 +msgid "Failed to run mkinitfs on the target" +msgstr "" + +#: src/modules/mkinitfs/main.py:50 +msgid "The exit code was {}" +msgstr "" + +#: src/modules/mount/main.py:43 +msgid "Mounting partitions." +msgstr "" + +#: src/modules/mount/main.py:164 src/modules/mount/main.py:200 +msgid "Internal error mounting zfs datasets" +msgstr "" + +#: src/modules/mount/main.py:176 +msgid "Failed to import zpool" +msgstr "" + +#: src/modules/mount/main.py:192 +msgid "Failed to unlock zpool" +msgstr "" + +#: src/modules/mount/main.py:209 src/modules/mount/main.py:214 +msgid "Failed to set zfs mountpoint" +msgstr "" + +#: src/modules/mount/main.py:365 +msgid "zfs mounting error" +msgstr "" + +#: src/modules/networkcfg/main.py:29 +msgid "Saving network configuration." +msgstr "" + +#: src/modules/openrcdmcryptcfg/main.py:26 +msgid "Configuring OpenRC dmcrypt service." +msgstr "" #: src/modules/packages/main.py:54 src/modules/packages/main.py:65 #: src/modules/packages/main.py:75 msgid "Install packages." -msgstr "Install packages." +msgstr "" #: src/modules/packages/main.py:63 #, python-format msgid "Processing packages (%(count)d / %(total)d)" -msgstr "Processing packages (%(count)d / %(total)d)" +msgstr "" #: src/modules/packages/main.py:68 #, python-format msgid "Installing one package." msgid_plural "Installing %(num)d packages." -msgstr[0] "Installing one package." -msgstr[1] "Installing %(num)d packages." +msgstr[0] "" +msgstr[1] "" #: src/modules/packages/main.py:71 #, python-format msgid "Removing one package." msgid_plural "Removing %(num)d packages." -msgstr[0] "Removing one package." -msgstr[1] "Removing %(num)d packages." +msgstr[0] "" +msgstr[1] "" #: src/modules/packages/main.py:725 src/modules/packages/main.py:737 #: src/modules/packages/main.py:765 msgid "Package Manager error" -msgstr "Package Manager error" +msgstr "" #: src/modules/packages/main.py:726 msgid "" "The package manager could not prepare updates. The command
{!s}
" "returned error code {!s}." msgstr "" -"The package manager could not prepare updates. The command
{!s}
" -"returned error code {!s}." #: src/modules/packages/main.py:738 msgid "" -"The package manager could not update the system. The command
{!s}
" -" returned error code {!s}." +"The package manager could not update the system. The command
{!s}
" +"returned error code {!s}." msgstr "" -"The package manager could not update the system. The command
{!s}
" -" returned error code {!s}." #: src/modules/packages/main.py:766 msgid "" "The package manager could not make changes to the installed system. The " "command
{!s}
returned error code {!s}." msgstr "" -"The package manager could not make changes to the installed system. The " -"command
{!s}
returned error code {!s}." -#: src/modules/bootloader/main.py:43 -msgid "Install bootloader." -msgstr "Install bootloader." - -#: src/modules/bootloader/main.py:614 -msgid "Failed to install grub, no partitions defined in global storage" -msgstr "Failed to install grub, no partitions defined in global storage" - -#: src/modules/bootloader/main.py:782 -msgid "Bootloader installation error" -msgstr "Bootloader installation error" - -#: src/modules/bootloader/main.py:783 -msgid "" -"The bootloader could not be installed. The installation command " -"
{!s}
returned error code {!s}." +#: src/modules/plymouthcfg/main.py:27 +msgid "Configure Plymouth theme" msgstr "" -"The bootloader could not be installed. The installation command " -"
{!s}
returned error code {!s}." -#: src/modules/hwclock/main.py:26 -msgid "Setting hardware clock." -msgstr "Setting hardware clock." +#: src/modules/rawfs/main.py:26 +msgid "Installing data." +msgstr "" -#: src/modules/mkinitfs/main.py:27 -msgid "Creating initramfs with mkinitfs." -msgstr "Creating initramfs with mkinitfs." +#: src/modules/services-openrc/main.py:29 +msgid "Configure OpenRC services" +msgstr "" -#: src/modules/mkinitfs/main.py:49 -msgid "Failed to run mkinitfs on the target" -msgstr "Failed to run mkinitfs on the target" +#: src/modules/services-openrc/main.py:57 +msgid "Cannot add service {name!s} to run-level {level!s}." +msgstr "" -#: src/modules/mkinitfs/main.py:50 src/modules/dracut/main.py:50 -msgid "The exit code was {}" -msgstr "The exit code was {}" +#: src/modules/services-openrc/main.py:59 +msgid "Cannot remove service {name!s} from run-level {level!s}." +msgstr "" -#: src/modules/dracut/main.py:27 -msgid "Creating initramfs with dracut." -msgstr "Creating initramfs with dracut." +#: src/modules/services-openrc/main.py:61 +msgid "" +"Unknown service-action {arg!s} for service {name!s} in run-" +"level {level!s}." +msgstr "" -#: src/modules/dracut/main.py:49 -msgid "Failed to run dracut on the target" -msgstr "Failed to run dracut on the target" +#: src/modules/services-openrc/main.py:93 +msgid "Cannot modify service" +msgstr "" -#: src/modules/initramfscfg/main.py:32 -msgid "Configuring initramfs." -msgstr "Configuring initramfs." +#: src/modules/services-openrc/main.py:94 +msgid "" +"rc-update {arg!s} call in chroot returned error code {num!s}." +msgstr "" -#: src/modules/openrcdmcryptcfg/main.py:26 -msgid "Configuring OpenRC dmcrypt service." -msgstr "Configuring OpenRC dmcrypt service." +#: src/modules/services-openrc/main.py:101 +msgid "Target runlevel does not exist" +msgstr "" -#: src/modules/fstab/main.py:28 -msgid "Writing fstab." -msgstr "Writing fstab." +#: src/modules/services-openrc/main.py:102 +msgid "" +"The path for runlevel {level!s} is {path!s}, which does not " +"exist." +msgstr "" -#: src/modules/fstab/main.py:429 -msgid "No
{!s}
configuration is given for
{!s}
to use." -msgstr "No
{!s}
configuration is given for
{!s}
to use." +#: src/modules/services-openrc/main.py:110 +msgid "Target service does not exist" +msgstr "" -#: src/modules/dummypython/main.py:35 -msgid "Dummy python job." -msgstr "Dummy python job." +#: src/modules/services-openrc/main.py:111 +msgid "" +"The path for service {name!s} is {path!s}, which does not exist." +msgstr "" -#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93 -#: src/modules/dummypython/main.py:94 -msgid "Dummy python step {}" -msgstr "Dummy python step {}" +#: src/modules/services-systemd/main.py:26 +msgid "Configure systemd units" +msgstr "" -#: src/modules/localecfg/main.py:31 -msgid "Configuring locales." -msgstr "Configuring locales." +#: src/modules/services-systemd/main.py:64 +msgid "Cannot modify unit" +msgstr "" -#: src/modules/networkcfg/main.py:29 -msgid "Saving network configuration." -msgstr "Saving network configuration." +#: src/modules/services-systemd/main.py:65 +msgid "" +"systemctl {_action!s} call in chroot returned error code " +"{_exit_code!s}." +msgstr "" + +#: src/modules/services-systemd/main.py:66 +msgid "Cannot {_action!s} systemd unit {_name!s}." +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 "" diff --git a/settings.conf b/settings.conf index 34d7c96d4..5012df2a6 100644 --- a/settings.conf +++ b/settings.conf @@ -132,9 +132,11 @@ sequence: - locale - keyboard - localecfg - - luksopenswaphookcfg - luksbootkeyfile + - luksopenswaphookcfg +# - dracutlukscfg - plymouthcfg +# - zfshostid - initcpiocfg - initcpio - users diff --git a/src/libcalamares/CalamaresAbout.cpp b/src/libcalamares/CalamaresAbout.cpp index 86e597b4b..a90866846 100644 --- a/src/libcalamares/CalamaresAbout.cpp +++ b/src/libcalamares/CalamaresAbout.cpp @@ -19,7 +19,7 @@ static const char s_header[] static const char s_footer[] = QT_TRANSLATE_NOOP( "AboutData", "Thanks to the Calamares team " - "and the Calamares " + "and the Calamares " "translators team.

" "Calamares " "development is sponsored by
" diff --git a/src/libcalamares/partition/FileSystem.cpp b/src/libcalamares/partition/FileSystem.cpp index 0dcd6fd32..c6bfc5aba 100644 --- a/src/libcalamares/partition/FileSystem.cpp +++ b/src/libcalamares/partition/FileSystem.cpp @@ -46,6 +46,7 @@ prettyNameForFileSystemType( FileSystem::Type t ) case FileSystem::Ufs: case FileSystem::Hpfs: case FileSystem::Luks: + case FileSystem::Luks2: case FileSystem::Ocfs2: case FileSystem::Zfs: case FileSystem::Nilfs2: diff --git a/src/libcalamaresui/viewpages/ExecutionViewStep.cpp b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp index 998d9f38b..7eae78cbc 100644 --- a/src/libcalamaresui/viewpages/ExecutionViewStep.cpp +++ b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp @@ -73,6 +73,7 @@ ExecutionViewStep::ExecutionViewStep( QObject* parent ) { m_widget->setObjectName( "slideshow" ); m_progressBar->setObjectName( "exec-progress" ); + m_progressBar->setFormat(tr("%p%", "Progress percentage indicator: %p is where the number 0..100 is placed")); m_label->setObjectName( "exec-message" ); QVBoxLayout* layout = new QVBoxLayout( m_widget ); diff --git a/src/modules/bootloader/bootloader.conf b/src/modules/bootloader/bootloader.conf index c5b94bbc7..9679f6696 100644 --- a/src/modules/bootloader/bootloader.conf +++ b/src/modules/bootloader/bootloader.conf @@ -9,25 +9,31 @@ # should specifically set *efiBootloaderId* to "debian" because that is # hard-coded in `grubx64.efi`. --- +# A variable from global storage which overrides the value of efiBootLoader +#efiBootLoaderVar: "packagechooser_bootloader" + # Define which bootloader you want to use for EFI installations -# Possible options are 'grub', 'sb-shim' and 'systemd-boot'. +# Possible options are 'grub', 'sb-shim', 'refind` and 'systemd-boot'. efiBootLoader: "grub" -# systemd-boot configuration files settings, set kernel search path, kernel name -# and amount of time before default selection boots +# systemd-boot configuration files settings + +# kernelSearchPath is the path relative to the root of the install to search for kernels +# A kernel is identified by finding files which match regular expression, kernelPattern kernelSearchPath: "/usr/lib/modules" -kernelName: "vmlinuz" -timeout: "10" +kernelPattern: "^vmlinuz.*" -# additionalInitrdFiles is a comma seperated list of file names -additionalInitrdFiles: - - "/boot/amd-ucode" - - "/boot/intel-ucode" +# loaderEntries is an array of options to add to loader.conf for systemd-boot +# please note that the "default" option is added programmatically +loaderEntries: + - "timeout 5" + - "console-mode keep" -# Optionally set the menu entry name to use in systemd-boot. -# If not specified here, these settings will be taken from branding.desc. -# -# bootloaderEntryName: "Generic GNU/Linux" +# systemd-boot and refind support custom kernel params +kernelParams: [ "quiet" ] + +# A list of kernel names that refind should accept as kernels +#refindKernelList: [ "linux","linux-lts","linux-zen","linux-hardened" ] # GRUB 2 binary names and boot directory # Some distributions (e.g. Fedora) use grub2-* (resp. /boot/grub2/) names. diff --git a/src/modules/bootloader/bootloader.schema.yaml b/src/modules/bootloader/bootloader.schema.yaml index ef61721c0..2505dc73e 100644 --- a/src/modules/bootloader/bootloader.schema.yaml +++ b/src/modules/bootloader/bootloader.schema.yaml @@ -6,17 +6,13 @@ $id: https://calamares.io/schemas/bootloader additionalProperties: false type: object properties: + efiBootLoaderVar: { type: string } efiBootLoader: { type: string } kernelSearchPath: { type: string } kernelName: { type: string } - timeout: { type: string } # Inserted verbatim - additionalInitrdFiles: - type: array - items: - type: string - bootloaderEntryName: { type: string } - kernelLine: { type: string } - fallbackKernelLine: { type: string } + kernelParams: { type: array, items: { type: string } } + loaderEntries: { type: array, items: { type: string } } + refindKernelList: { type: array, items: { type: string } } # Programs grubInstall: { type: string } @@ -27,12 +23,3 @@ properties: efiBootloaderId: { type: string } installEFIFallback: { type: boolean } - -required: - - efiBootLoader - - kernelSearchPath - - kernelName - - grubInstall - - grubMkconfig - - grubCfg - - grubProbe diff --git a/src/modules/bootloader/main.py b/src/modules/bootloader/main.py index c10d8bf2e..4a5a93f60 100644 --- a/src/modules/bootloader/main.py +++ b/src/modules/bootloader/main.py @@ -20,7 +20,9 @@ # Calamares is Free Software: see the License-Identifier above. # +import fileinput import os +import re import shutil import subprocess @@ -28,8 +30,8 @@ import libcalamares from libcalamares.utils import check_target_env_call - import gettext + _ = gettext.translation("calamares-python", localedir=libcalamares.utils.gettext_path(), languages=libcalamares.utils.gettext_languages(), @@ -39,6 +41,7 @@ _ = gettext.translation("calamares-python", # to make identifiers (or to clean up names to make filenames). file_name_sanitizer = str.maketrans(" /()", "_-__") + def pretty_name(): return _("Install bootloader.") @@ -59,20 +62,6 @@ def get_uuid(): return "" -def get_bootloader_entry_name(): - """ - Passes 'bootloader_entry_name' to other routine based - on configuration file. - - :return: - """ - if "bootloaderEntryName" in libcalamares.job.configuration: - return libcalamares.job.configuration["bootloaderEntryName"] - else: - branding = libcalamares.globalstorage.value("branding") - return branding["bootloaderEntryName"] - - def get_kernel_line(kernel_type): """ Passes 'kernel_line' to other routine based on configuration file. @@ -136,49 +125,43 @@ def is_zfs_root(partition): return partition["mountPoint"] == "/" and partition["fs"] == "zfs" -def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kernel, kernel_type, kernel_version): - """ - Creates systemd-boot configuration files based on given parameters. - - :param installation_root_path: A string containing the absolute path to the root of the installation - :param efi_dir: A string containing the path to the efi dir relative to the root of the installation - :param uuid: A string containing the UUID of the root volume - :param entry: A string containing the name of the entry as it will be displayed on boot - :param kernel: A string containing the path to the kernel relative to the root of the installation - :param kernel_type: A string which should be set if there is a special version of the entry, for example "fallback" - :param kernel_version: The kernel version string - """ - kernel_params = ["quiet"] +def get_kernel_params(uuid): + kernel_params = libcalamares.job.configuration.get("kernelParams", ["quiet"]) + kernel_params.append("rw") partitions = libcalamares.globalstorage.value("partitions") swap_uuid = "" swap_outer_mappername = None + swap_outer_uuid = None cryptdevice_params = [] + have_dracut = libcalamares.utils.target_env_call(["sh", "-c", "which dracut"]) == 0 + # Take over swap settings: # - unencrypted swap partition sets swap_uuid # - encrypted root sets cryptdevice_params for partition in partitions: if partition["fs"] == "linuxswap" and not partition.get("claimed", None): + # Skip foreign swap continue has_luks = "luksMapperName" in partition if partition["fs"] == "linuxswap" and not has_luks: swap_uuid = partition["uuid"] - if (partition["fs"] == "linuxswap" and has_luks): + if partition["fs"] == "linuxswap" and has_luks: swap_outer_mappername = partition["luksMapperName"] + swap_outer_uuid = partition["luksUuid"] if partition["mountPoint"] == "/" and has_luks: - cryptdevice_params = ["cryptdevice=UUID=" - + partition["luksUuid"] - + ":" - + partition["luksMapperName"], - "root=/dev/mapper/" - + partition["luksMapperName"]] + if have_dracut: + cryptdevice_params = [f"rd.luks.uuid={partition['luksUuid']}"] + else: + cryptdevice_params = [f"cryptdevice=UUID={partition['luksUuid']}:{partition['luksMapperName']}"] + cryptdevice_params.append(f"root=/dev/mapper/{partition['luksMapperName']}") + # btrfs and zfs handling for partition in partitions: - # systemd-boot with a BTRFS root filesystem needs to be told abouut the root subvolume. # If a btrfs root subvolume wasn't set, it means the root is directly on the partition # and this option isn't needed if is_btrfs_root(partition): @@ -190,7 +173,7 @@ def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kerne if is_zfs_root(partition): zfs_root_path = get_zfs_root() if zfs_root_path is not None: - kernel_params.append("zfs=" + zfs_root_path) + kernel_params.append("root=ZFS=" + zfs_root_path) else: # Something is really broken if we get to this point libcalamares.utils.warning("Internal error handling zfs dataset") @@ -204,93 +187,86 @@ def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kerne if swap_uuid: kernel_params.append("resume=UUID={!s}".format(swap_uuid)) + if have_dracut and swap_outer_uuid: + kernel_params.append(f"rd.luks.uuid={swap_outer_uuid}") + if swap_outer_mappername: - kernel_params.append("resume=/dev/mapper/{!s}".format( - swap_outer_mappername)) + kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}") - libcalamares.utils.debug("Configure: \"{!s}\"".format(f"{entry} {kernel_version}")) + return kernel_params - if kernel_type == "fallback": - version_string = kernel_version + "-fallback" - initrd = "initrd-fallback" - else: - version_string = kernel_version - initrd = "initrd" + +def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, kernel, kernel_version): + """ + Creates systemd-boot configuration files based on given parameters. + + :param installation_root_path: A string containing the absolute path to the root of the installation + :param efi_dir: A string containing the path to the efi dir relative to the root of the installation + :param uuid: A string containing the UUID of the root volume + :param kernel: A string containing the path to the kernel relative to the root of the installation + :param kernel_version: The kernel version string + """ + + # Get the kernel params and write them to /etc/kernel/cmdline + # This file is used by kernel-install + kernel_params = " ".join(get_kernel_params(uuid)) + kernel_cmdline_path = os.path.join(installation_root_path, "etc", "kernel") + os.makedirs(kernel_cmdline_path, exist_ok=True) + with open(os.path.join(kernel_cmdline_path, "cmdline"), "w") as cmdline_file: + cmdline_file.write(kernel_params) + + libcalamares.utils.debug(f"Configuring kernel version {kernel_version}") # get the machine-id with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file: machine_id = machineid_file.read().rstrip('\n') - # Copy kernel to a subdirectory of /efi partition + # Ensure the directory exists machine_dir = os.path.join(installation_root_path + efi_dir, machine_id) os.makedirs(machine_dir, exist_ok=True) - target_efi_files_dir = os.path.join(machine_dir, kernel_version) - os.makedirs(target_efi_files_dir, exist_ok=True) - - kernel_path = os.path.join(installation_root_path, kernel) - kernel_name = os.path.basename(kernel_path) - shutil.copyfile(kernel_path, os.path.join(target_efi_files_dir, "linux")) - - # write the entry - lines = [ - '## Generated by Calamares\n', - '\n', - "title {!s}\n".format(entry), - "version {!s}\n".format(version_string), - "machine-id {!s}\n".format(machine_id), - "linux {!s}\n".format(os.path.join("/", machine_id, kernel_version, "linux")), - ] - - try: - additional_initrd_files = libcalamares.job.configuration["additionalInitrdFiles"] - for initrd_file in additional_initrd_files: - libcalamares.utils.debug("Attempting to handle initrd image " + initrd_file) - if os.path.isfile(os.path.join(installation_root_path, initrd_file.lstrip('/'))): - libcalamares.utils.debug("Found image " + initrd_file) - shutil.copyfile(os.path.join(installation_root_path, initrd_file.lstrip('/')), os.path.join(target_efi_files_dir, os.path.basename(initrd_file))) - lines.append("initrd {!s}\n".format(os.path.join("/", machine_id, kernel_version, os.path.basename(initrd_file)))) - except KeyError: # If the configuration option isn't set, we can just move on - libcalamares.utils.debug("Failed to find key additionalInitrdFiles") - pass - - lines.append("initrd {!s}\n".format(os.path.join("/", machine_id, kernel_version, initrd))) - lines.append("options {!s} rw\n".format(" ".join(kernel_params))) - - conf_path = os.path.join(installation_root_path + efi_dir, - "loader", - "entries", - machine_id + "-" + version_string + ".conf") - - with open(conf_path, 'w') as conf_file: - for line in lines: - conf_file.write(line) + # Call kernel-install for each kernel + libcalamares.utils.target_env_process_output(["kernel-install", + "add", + kernel_version, + os.path.join("/", kernel)]) -def create_loader(loader_path, entry): +def create_loader(loader_path, installation_root_path): """ Writes configuration for loader. - :param loader_path: - :param entry: + :param loader_path: The absolute path to the loader.conf file + :param installation_root_path: The path to the root of the target installation """ - timeout = libcalamares.job.configuration["timeout"] - lines = [ - "timeout {!s}\n".format(timeout), - "default {!s}\n".format(entry), - ] + + # get the machine-id + with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file: + machine_id = machineid_file.read().rstrip('\n') + + try: + loader_entries = libcalamares.job.configuration["loaderEntries"] + except KeyError: + libcalamares.utils.debug("No aditional loader entries found in config") + loader_entries = [] + pass + + lines = [f"default {machine_id}*"] + + lines.extend(loader_entries) with open(loader_path, 'w') as loader_file: for line in lines: - loader_file.write(line) + loader_file.write(line + "\n") -class suffix_iterator(object): +class SuffixIterator(object): """ Wrapper for one of the "generator" classes below to behave like a proper Python iterator. The iterator is initialized with a maximum number of attempts to generate a new suffix. """ + def __init__(self, attempts, generator): self.generator = generator self.attempts = attempts @@ -310,6 +286,7 @@ class serialEfi(object): """ EFI Id generator that appends a serial number to the given name. """ + def __init__(self, name): self.name = name # So the first call to next() will bump it to 0 @@ -354,6 +331,7 @@ class randomEfi(object): """ EFI Id generator that appends a random 4-digit hex number to the given name. """ + def __init__(self, name): self.name = name # So the first call to next() will bump it to 0 @@ -427,7 +405,7 @@ def change_efi_suffix(efi_directory, bootloader_id): """ if bootloader_id.endswith("}") and "${" in bootloader_id: # Do 10 attempts with any suffix generator - g = suffix_iterator(10, get_efi_suffix_generator(bootloader_id)) + g = SuffixIterator(10, get_efi_suffix_generator(bootloader_id)) else: # Just one attempt g = [bootloader_id] @@ -444,7 +422,7 @@ def efi_label(efi_directory): used within @p efi_directory. """ if "efiBootloaderId" in libcalamares.job.configuration: - efi_bootloader_id = change_efi_suffix( efi_directory, libcalamares.job.configuration["efiBootloaderId"] ) + efi_bootloader_id = change_efi_suffix(efi_directory, libcalamares.job.configuration["efiBootloaderId"]) else: branding = libcalamares.globalstorage.value("branding") efi_bootloader_id = branding["bootloaderEntryName"] @@ -482,6 +460,7 @@ def efi_boot_next(): if boot_entry: subprocess.call([boot_mgr, "-n", boot_entry]) + def get_kernels(installation_root_path): """ Gets a list of kernels and associated values for each kernel. This will work as is for many distros. @@ -493,20 +472,32 @@ def get_kernels(installation_root_path): Each 3-tuple contains the kernel, kernel_type and kernel_version """ - kernel_search_path = libcalamares.job.configuration["kernelSearchPath"] - source_kernel_name = libcalamares.job.configuration["kernelName"] + try: + kernel_search_path = libcalamares.job.configuration["kernelSearchPath"] + except KeyError: + libcalamares.utils.warning("No kernel pattern found in configuration, using '/usr/lib/modules'") + kernel_search_path = "/usr/lib/modules" + pass + kernel_list = [] - # find all the installed kernels and generate default and fallback entries for each + try: + kernel_pattern = libcalamares.job.configuration["kernelPattern"] + except KeyError: + libcalamares.utils.warning("No kernel pattern found in configuration, using 'vmlinuz'") + kernel_pattern = "vmlinuz" + pass + + # find all the installed kernels for root, dirs, files in os.walk(os.path.join(installation_root_path, kernel_search_path.lstrip('/'))): for file in files: - if file == source_kernel_name: + if re.search(kernel_pattern, file): rel_root = os.path.relpath(root, installation_root_path) - kernel_list.append((os.path.join(rel_root, file),"default",os.path.basename(root))) - kernel_list.append((os.path.join(rel_root, file),"fallback",os.path.basename(root))) + kernel_list.append((os.path.join(rel_root, file), "default", os.path.basename(root))) return kernel_list + def install_systemd_boot(efi_directory): """ Installs systemd-boot as bootloader for EFI setups. @@ -517,8 +508,6 @@ def install_systemd_boot(efi_directory): installation_root_path = libcalamares.globalstorage.value("rootMountPoint") install_efi_directory = installation_root_path + efi_directory uuid = get_uuid() - distribution = get_bootloader_entry_name() - distribution_translated = distribution.translate(file_name_sanitizer) loader_path = os.path.join(install_efi_directory, "loader", "loader.conf") @@ -528,14 +517,13 @@ def install_systemd_boot(efi_directory): for (kernel, kernel_type, kernel_version) in get_kernels(installation_root_path): create_systemd_boot_conf(installation_root_path, - efi_directory, - uuid, - distribution, - kernel, - kernel_type, - kernel_version) + efi_directory, + uuid, + kernel, + kernel_version) + + create_loader(loader_path, installation_root_path) - create_loader(loader_path, distribution_translated) def get_grub_efi_parameters(): """ @@ -554,15 +542,16 @@ def get_grub_efi_parameters(): if efi_bitness == "32": # Assume all 32-bitters are legacy x86 - return ("i386-efi", "grubia32.efi", "bootia32.efi") + return "i386-efi", "grubia32.efi", "bootia32.efi" elif efi_bitness == "64" and cpu_type == "aarch64": - return ("arm64-efi", "grubaa64.efi", "bootaa64.efi") + return "arm64-efi", "grubaa64.efi", "bootaa64.efi" elif efi_bitness == "64" and cpu_type == "loongarch64": - return ("loongarch64-efi", "grubloongarch64.efi", "bootloongarch64.efi") + return "loongarch64-efi", "grubloongarch64.efi", "bootloongarch64.efi" elif efi_bitness == "64": # If it's not ARM, must by AMD64 - return ("x86_64-efi", "grubx64.efi", "bootx64.efi") - libcalamares.utils.warning("Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type))) + return "x86_64-efi", "grubx64.efi", "bootx64.efi" + libcalamares.utils.warning( + "Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type))) return None @@ -667,8 +656,8 @@ def install_grub(efi_directory, fw_type): # VFAT is weird, see issue CAL-385 install_efi_directory_firmware = (vfat_correct_case( - install_efi_directory, - "EFI")) + install_efi_directory, + "EFI")) if not os.path.exists(install_efi_directory_firmware): os.makedirs(install_efi_directory_firmware) @@ -676,8 +665,8 @@ def install_grub(efi_directory, fw_type): # most usual they are boot, Boot, BOOT install_efi_boot_directory = (vfat_correct_case( - install_efi_directory_firmware, - "boot")) + install_efi_directory_firmware, + "boot")) if not os.path.exists(install_efi_boot_directory): os.makedirs(install_efi_boot_directory) @@ -712,6 +701,9 @@ def install_secureboot(efi_directory): install_efi_bin = "shimx64.efi" elif efi_word_size() == "32": install_efi_bin = "shimia32.efi" + else: + libcalamares.utils.warning(f"Unknown efi word size of {efi_word_size()} found") + return None # Copied, roughly, from openSUSE's install script, # and pythonified. *disk* is something like /dev/sda, @@ -725,7 +717,7 @@ def install_secureboot(efi_directory): libcalamares.job.configuration["grubProbe"], "-t", "disk", "--device-map=", install_efi_directory]).decode("ascii") - efi_drive_partition = efi_drive.replace("(","").replace(")","").split(",")[1] + efi_drive_partition = efi_drive.replace("(", "").replace(")", "").split(",")[1] # Get the first run of digits from the partition efi_partition_number = None c = 0 @@ -765,6 +757,62 @@ def vfat_correct_case(parent, name): return os.path.join(parent, name) +def efi_partitions(efi_boot_path): + """ + The (one) partition mounted on @p efi_boot_path, or an empty list. + """ + return [p for p in libcalamares.globalstorage.value("partitions") if p["mountPoint"] == efi_boot_path] + + +def update_refind_config(efi_directory, installation_root_path): + """ + :param efi_directory: The path to the efi directory relative to the root + :param installation_root_path: The path to the root of the installation + """ + try: + kernel_list = libcalamares.job.configuration["refindKernelList"] + except KeyError: + libcalamares.utils.warning('refindKernelList not set. Skipping updating refind.conf') + return + + # Update the config in the file + for line in fileinput.input(installation_root_path + efi_directory + "/EFI/refind/refind.conf", inplace=True): + line = line.strip() + if line.startswith("#extra_kernel_version_strings") or line.startswith("extra_kernel_version_strings"): + line = line.lstrip("#") + for kernel in kernel_list: + if kernel not in line: + line += "," + kernel + print(line) + + +def install_refind(efi_directory): + try: + installation_root_path = libcalamares.globalstorage.value("rootMountPoint") + except KeyError: + libcalamares.utils.warning('Global storage value "rootMountPoint" missing') + + install_efi_directory = installation_root_path + efi_directory + uuid = get_uuid() + kernel_params = " ".join(get_kernel_params(uuid)) + conf_path = os.path.join(installation_root_path, "boot/refind_linux.conf") + + check_target_env_call(["refind-install"]) + + with open(conf_path, "r") as refind_file: + filedata = [x.strip() for x in refind_file.readlines()] + + with open(conf_path, 'w') as refind_file: + for line in filedata: + if line.startswith('"Boot with standard options"'): + line = f'"Boot with standard options" "{kernel_params}"' + elif line.startswith('"Boot to single-user mode"'): + line = f'"Boot to single-user mode" "{kernel_params}" single' + refind_file.write(line + "\n") + + update_refind_config(efi_directory, installation_root_path) + + def prepare_bootloader(fw_type): """ Prepares bootloader. @@ -774,19 +822,47 @@ def prepare_bootloader(fw_type): :param fw_type: :return: """ - efi_boot_loader = libcalamares.job.configuration["efiBootLoader"] + + # Get the boot loader selection from global storage if it is set in the config file + try: + gs_name = libcalamares.job.configuration["efiBootLoaderVar"] + if libcalamares.globalstorage.contains(gs_name): + efi_boot_loader = libcalamares.globalstorage.value(gs_name) + else: + libcalamares.utils.warning( + f"Specified global storage value not found in global storage") + return None + except KeyError: + # If the conf value for using global storage is not set, use the setting from the config file. + try: + efi_boot_loader = libcalamares.job.configuration["efiBootLoader"] + except KeyError: + if fw_type == "efi": + libcalamares.utils.warning("Configuration missing both efiBootLoader and efiBootLoaderVar on an EFI " + "system, bootloader not installed") + return + else: + pass + + # If the user has selected not to install bootloader, bail out here + if efi_boot_loader.casefold() == "none": + libcalamares.utils.debug("Skipping bootloader installation since no bootloader was selected") + return None + efi_directory = libcalamares.globalstorage.value("efiSystemPartition") if efi_boot_loader == "systemd-boot" and fw_type == "efi": install_systemd_boot(efi_directory) elif efi_boot_loader == "sb-shim" and fw_type == "efi": install_secureboot(efi_directory) + elif efi_boot_loader == "refind" and fw_type == "efi": + install_refind(efi_directory) elif efi_boot_loader == "grub" or fw_type != "efi": install_grub(efi_directory, fw_type) else: - libcalamares.utils.debug( "WARNING: the combination of " - "boot-loader '{!s}' and firmware '{!s}' " - "is not supported.".format(efi_boot_loader, fw_type) ) + libcalamares.utils.debug("WARNING: the combination of " + "boot-loader '{!s}' and firmware '{!s}' " + "is not supported.".format(efi_boot_loader, fw_type)) def run(): @@ -798,16 +874,16 @@ def run(): fw_type = libcalamares.globalstorage.value("firmwareType") - if (libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi"): - libcalamares.utils.warning( "Non-EFI system, and no bootloader is set." ) + if libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi": + libcalamares.utils.warning("Non-EFI system, and no bootloader is set.") return None partitions = libcalamares.globalstorage.value("partitions") if fw_type == "efi": efi_system_partition = libcalamares.globalstorage.value("efiSystemPartition") - esp_found = [ p for p in partitions if p["mountPoint"] == efi_system_partition ] + esp_found = [p for p in partitions if p["mountPoint"] == efi_system_partition] if not esp_found: - libcalamares.utils.warning( "EFI system, but nothing mounted on {!s}".format(efi_system_partition) ) + libcalamares.utils.warning("EFI system, but nothing mounted on {!s}".format(efi_system_partition)) return None try: @@ -817,7 +893,8 @@ def run(): libcalamares.utils.debug("stdout:" + str(e.stdout)) libcalamares.utils.debug("stderr:" + str(e.stderr)) return (_("Bootloader installation error"), - _("The bootloader could not be installed. The installation command
{!s}
returned error code {!s}.") + _("The bootloader could not be installed. The installation command
{!s}
returned error " + "code {!s}.") .format(e.cmd, e.returncode)) return None diff --git a/src/modules/displaymanager/displaymanager.conf b/src/modules/displaymanager/displaymanager.conf index c5c0e0b78..81469bd58 100644 --- a/src/modules/displaymanager/displaymanager.conf +++ b/src/modules/displaymanager/displaymanager.conf @@ -59,3 +59,17 @@ basicSetup: false # *displaymanagers* list (as the only one). # sysconfigSetup: false + +# Some DMs have specific settings. These can be customized here. +# +# greetd has configurable user and group; the user and group is created if it +# does not exist, and the user is set as default-session user. +# +# lightdm has a list of greeters to look for, preferring them in order if +# they are installed (if not, picks the alphabetically first greeter that is installed). +# +greetd: + greeter_user: "tom_bombadil" + greeter_group: "wheel" +lightdm: + preferred_greeters: ["lightdm-greeter.desktop", "slick-greeter.desktop"] diff --git a/src/modules/displaymanager/displaymanager.schema.yaml b/src/modules/displaymanager/displaymanager.schema.yaml index 7e4c5f54c..ab89c0ada 100644 --- a/src/modules/displaymanager/displaymanager.schema.yaml +++ b/src/modules/displaymanager/displaymanager.schema.yaml @@ -20,3 +20,13 @@ properties: required: [ executable, desktopFile ] basicSetup: { type: boolean, default: false } sysconfigSetup: { type: boolean, default: false } + greetd: + type: object + properties: + greeter_user: { type: string } + greeter_group: { type: string } + additionalProperties: false + lightdm: + type: object + properties: + preferred_greeters: { type: array, items: { type: string } } diff --git a/src/modules/displaymanager/main.py b/src/modules/displaymanager/main.py index f908bc441..2a1448e51 100644 --- a/src/modules/displaymanager/main.py +++ b/src/modules/displaymanager/main.py @@ -197,6 +197,8 @@ desktop_environments = [ DesktopEnvironment('/usr/bin/sway', 'sway'), DesktopEnvironment('/usr/bin/ukui-session', 'ukui'), DesktopEnvironment('/usr/bin/cutefish-session', 'cutefish-xsession'), + DesktopEnvironment('/usr/bin/river', 'river'), + DesktopEnvironment('/usr/bin/Hyprland', 'hyprland'), ] @@ -444,7 +446,6 @@ class DMgdm(DisplayManager): userfile.write("Icon=\n") - def basic_setup(self): if libcalamares.utils.target_env_call( ['getent', 'group', 'gdm'] @@ -544,6 +545,11 @@ class DMlightdm(DisplayManager): name = "lightdm" executable = "lightdm" + # Can be overridden in the .conf file. With no value it won't match any + # desktop file in the xgreeters directory and instead we end up picking + # the alphabetically first file there. + preferred_greeters = [] + def set_autologin(self, username, do_autologin, default_desktop_environment): # Systems with LightDM as Desktop Manager # Ideally, we should use configparser for the ini conf file, @@ -593,7 +599,6 @@ class DMlightdm(DisplayManager): _("LightDM config file {!s} does not exist").format(lightdm_conf_path) ) - def basic_setup(self): libcalamares.utils.target_env_call( ['mkdir', '-p', '/run/lightdm'] @@ -633,40 +638,52 @@ class DMlightdm(DisplayManager): ) ) + def find_preferred_greeter(self): + """ + On Debian, lightdm-greeter.desktop is typically a symlink managed + by update-alternatives pointing to /etc/alternatives/lightdm-greeter + which is also a symlink to a real .desktop file back in /usr/share/xgreeters/ + + Returns a path *into the mounted target* of the preferred greeter -- usually + a .desktop file that specifies where the actual executable is. May return + None to indicate nothing-was-found. + """ + greeters_dir = "usr/share/xgreeters" + greeters_target_path = os.path.join(self.root_mount_point, greeters_dir) + available_names = os.listdir(greeters_target_path) + available_names.sort() + desktop_names = [n for n in self.preferred_greeters if n in available_names] # Preferred ones + if desktop_names: + return desktop_names[0] + desktop_names = [n for n in available_names if n.endswith(".desktop")] # .. otherwise any .desktop + if desktop_names: + return desktop_names[0] + return None + def greeter_setup(self): - lightdm_conf_path = os.path.join( - self.root_mount_point, "etc/lightdm/lightdm.conf" - ) + lightdm_conf_path = os.path.join(self.root_mount_point, "etc/lightdm/lightdm.conf") + greeter_name = self.find_preferred_greeter() - # configure lightdm-greeter - greeter_path = os.path.join( - self.root_mount_point, "usr/share/xgreeters" - ) + if greeter_name is not None: + greeter = os.path.basename(greeter_name) # Follow symlinks, hope they are not absolute + if greeter.endswith('.desktop'): + greeter = greeter[:-8] # Remove ".desktop" from end - if (os.path.exists(greeter_path)): - # configure first found lightdm-greeter - for entry in os.listdir(greeter_path): - if entry.endswith('.desktop'): - greeter = entry.split('.')[0] - libcalamares.utils.debug( - "found greeter {!s}".format(greeter) - ) - os.system( - "sed -i -e \"s/^.*greeter-session=.*" - "/greeter-session={!s}/\" {!s}".format( - greeter, - lightdm_conf_path - ) - ) - libcalamares.utils.debug( - "{!s} configured as greeter.".format(greeter) - ) - break - else: - return ( - _("Cannot configure LightDM"), - _("No LightDM greeter installed.") - ) + libcalamares.utils.debug("found greeter {!s}".format(greeter)) + os.system( + "sed -i -e \"s/^.*greeter-session=.*" + "/greeter-session={!s}/\" {!s}".format( + greeter, + lightdm_conf_path + ) + ) + libcalamares.utils.debug("{!s} configured as greeter.".format(greeter)) + else: + libcalamares.utils.error("No greeter found at all, preferred {!s}".format(self.preferred_greeters)) + return ( + _("Cannot configure LightDM"), + _("No LightDM greeter installed.") + ) class DMslim(DisplayManager): @@ -817,7 +834,7 @@ class DMgreetd(DisplayManager): def desktop_environment_setup(self, default_desktop_environment): with open(self.environments_path(), 'w') as envs_file: - envs_file.write(default_desktop_environment.desktop_file) + envs_file.write(default_desktop_environment.executable) envs_file.write("\n") def greeter_setup(self): @@ -828,7 +845,7 @@ class DMgreetd(DisplayManager): de_command = default_desktop_environment.executable if os.path.exists(self.os_path("usr/bin/gtkgreet")) and os.path.exists(self.os_path("usr/bin/cage")): - self.config_data['default_session']['command'] = "cage -s -- gtkgreet" + self.config_data['default_session']['command'] = "cage -d -s -- gtkgreet" elif os.path.exists(self.os_path("usr/bin/tuigreet")): tuigreet_base_cmd = "tuigreet --remember --time --issue --asterisks --cmd " self.config_data['default_session']['command'] = tuigreet_base_cmd + de_command @@ -983,6 +1000,11 @@ def run(): # Do the actual configuration and collect messages dm_setup_message = [] for dm in dm_impl: + dm_specific_configuration = libcalamares.job.configuration.get(dm.name, None) + if dm_specific_configuration and isinstance(dm_specific_configuration, dict): + for k, v in dm_specific_configuration.items(): + if hasattr(dm, k): + setattr(dm, k, v) dm_message = None if enable_basic_setup: dm_message = dm.basic_setup() diff --git a/src/modules/dracut/dracut.conf b/src/modules/dracut/dracut.conf index 98d847d9e..ba1a7b08c 100644 --- a/src/modules/dracut/dracut.conf +++ b/src/modules/dracut/dracut.conf @@ -5,6 +5,6 @@ --- # Dracut defaults to setting initramfs-.img # If you want to specify another filename for the resulting image, -# set a custom kernel name, including the path +# set a custom name, including the path # -# kernelName: /boot/initramfs-linux.img +initramfsName: /boot/initramfs-freebsd.img diff --git a/src/modules/dracut/dracut.schema.yaml b/src/modules/dracut/dracut.schema.yaml index 503b1a4f0..d6008e1bf 100644 --- a/src/modules/dracut/dracut.schema.yaml +++ b/src/modules/dracut/dracut.schema.yaml @@ -6,4 +6,4 @@ $id: https://calamares.io/schemas/dracut additionalProperties: false type: object properties: - kernelName: { type: string } + initramfsName: { type: string } diff --git a/src/modules/dracut/main.py b/src/modules/dracut/main.py index 071406580..85e6f3e7f 100644 --- a/src/modules/dracut/main.py +++ b/src/modules/dracut/main.py @@ -12,9 +12,10 @@ # # Calamares is Free Software: see the License-Identifier above. # +import subprocess import libcalamares -from libcalamares.utils import check_target_env_call +from libcalamares.utils import target_env_process_output import gettext @@ -34,12 +35,20 @@ def run_dracut(): :return: """ - kernelName = libcalamares.job.configuration['kernelName'] + try: + initramfs_name = libcalamares.job.configuration['initramfsName'] + target_env_process_output(['dracut', '-f', initramfs_name]) + except KeyError: + try: + target_env_process_output(['dracut', '-f']) + except subprocess.CalledProcessError as cpe: + libcalamares.utils.warning(f"Dracut failed with output: {cpe.output}") + return cpe.returncode + except subprocess.CalledProcessError as cpe: + libcalamares.utils.warning(f"Dracut failed with output: {cpe.output}") + return cpe.returncode - if not kernelName: - return check_target_env_call(['dracut', '-f']) - else: - return check_target_env_call(['dracut', '-f', '{}'.format(kernelName)]) + return 0 def run(): @@ -50,7 +59,6 @@ def run(): :return: """ return_code = run_dracut() - if return_code != 0: - return (_("Failed to run dracut on the target"), - _("The exit code was {}").format(return_code)) + return (_("Failed to run dracut"), + _(f"Dracut failed to run on the target with return code: {return_code}")) diff --git a/src/modules/grubcfg/grubcfg.conf b/src/modules/grubcfg/grubcfg.conf index 2d58e5dc2..189d4c2a9 100644 --- a/src/modules/grubcfg/grubcfg.conf +++ b/src/modules/grubcfg/grubcfg.conf @@ -46,3 +46,6 @@ defaults: GRUB_DISABLE_SUBMENU: true GRUB_TERMINAL_OUTPUT: "console" GRUB_DISABLE_RECOVERY: true + +# Set to true to force defaults to be used even when not overwriting +always_use_defaults: false diff --git a/src/modules/grubcfg/grubcfg.schema.yaml b/src/modules/grubcfg/grubcfg.schema.yaml index d4d8b5ddb..c04c0b79f 100644 --- a/src/modules/grubcfg/grubcfg.schema.yaml +++ b/src/modules/grubcfg/grubcfg.schema.yaml @@ -19,4 +19,5 @@ properties: GRUB_DISABLE_SUBMENU: { type: boolean, default: true } GRUB_TERMINAL_OUTPUT: { type: string } GRUB_DISABLE_RECOVERY: { type: boolean, default: true } - required: [ GRUB_TIMEOUT, GRUB_DEFAULT, GRUB_TERMINAL_OUTPUT ] + required: [ GRUB_TIMEOUT, GRUB_DEFAULT ] + always_use_defaults: { type: boolean, default: false } diff --git a/src/modules/grubcfg/main.py b/src/modules/grubcfg/main.py index 27f8bc4b6..50f9ca9d1 100644 --- a/src/modules/grubcfg/main.py +++ b/src/modules/grubcfg/main.py @@ -14,6 +14,7 @@ # import libcalamares +import fileinput import os import re @@ -85,6 +86,30 @@ def get_zfs_root(): return None +def update_existing_config(default_grub, grub_config_items): + """ + Updates the existing grub configuration file with any items present in @p grub_config_items + + Items that exist in the file will be updated and new items will be appended to the end + + :param default_grub: The absolute path to the grub config file + :param grub_config_items: A dict holding the key value pairs representing the items + """ + for line in fileinput.input(default_grub, inplace=True): + line = line.strip() + if "=" in line: + # This may be a key, strip the leading comment if it has one + key = line.lstrip("#").split("=")[0].strip() + + # check if this is one of the keys we care about + if key in grub_config_items.keys(): + print(f"{key}={grub_config_items[key]}") + else: + print(line) + else: + print(line) + + def modify_grub_default(partitions, root_mount_point, distributor): """ Configures '/etc/default/grub' for hibernation and plymouth. @@ -103,7 +128,7 @@ def modify_grub_default(partitions, root_mount_point, distributor): :return: """ default_grub = get_grub_config_path(root_mount_point) - distributor_replace = distributor.replace("'", "'\\''") + distributor = distributor.replace("'", "'\\''") dracut_bin = libcalamares.utils.target_env_call( ["sh", "-c", "which dracut"] ) @@ -127,7 +152,7 @@ def modify_grub_default(partitions, root_mount_point, distributor): zfs_root_path = None for partition in partitions: - if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs"): + if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs", "zfs"): no_save_default = True break @@ -197,63 +222,46 @@ def modify_grub_default(partitions, root_mount_point, distributor): if swap_outer_mappername: kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}") - if "overwrite" in libcalamares.job.configuration: - overwrite = libcalamares.job.configuration["overwrite"] - else: - overwrite = False + overwrite = libcalamares.job.configuration.get("overwrite", False) - distributor_line = f"GRUB_DISTRIBUTOR='{distributor_replace}'" - kernel_cmd = f'GRUB_CMDLINE_LINUX_DEFAULT="{" ".join(kernel_params)}"' - have_kernel_cmd = False - have_distributor_line = False + grub_config_items = {} + # read the lines we need from the existing config if os.path.exists(default_grub) and not overwrite: with open(default_grub, 'r') as grub_file: lines = [x.strip() for x in grub_file.readlines()] - for i in range(len(lines)): - if lines[i].startswith("#GRUB_CMDLINE_LINUX_DEFAULT"): - lines[i] = kernel_cmd - have_kernel_cmd = True - elif lines[i].startswith("GRUB_CMDLINE_LINUX_DEFAULT"): - regex = re.compile(r"^GRUB_CMDLINE_LINUX_DEFAULT\s*=\s*") - line = regex.sub("", lines[i]) - line = line.lstrip() - line = line.lstrip("\"") - line = line.lstrip("'") - line = line.rstrip() - line = line.rstrip("\"") - line = line.rstrip("'") - existing_params = line.split() + for line in lines: + if line.startswith("GRUB_CMDLINE_LINUX_DEFAULT"): + existing_params = re.sub(r"^GRUB_CMDLINE_LINUX_DEFAULT\s*=\s*", "", line).strip("\"'").split() for existing_param in existing_params: - existing_param_name = existing_param.split("=")[0] + existing_param_name = existing_param.split("=")[0].strip() - # the only ones we ever add - if existing_param_name not in [ - "quiet", "resume", "splash"]: + # Ensure we aren't adding duplicated params + param_exists = False + for param in kernel_params: + if param.split("=")[0].strip() == existing_param_name: + param_exists = True + break + if not param_exists and existing_param_name not in ["quiet", "resume", "splash"]: kernel_params.append(existing_param) - lines[i] = kernel_cmd - have_kernel_cmd = True - elif (lines[i].startswith("#GRUB_DISTRIBUTOR") - or lines[i].startswith("GRUB_DISTRIBUTOR")): - if libcalamares.job.configuration.get("keep_distributor", False): - lines[i] = distributor_line - have_distributor_line = True - else: - # We're not updating because of *keep_distributor*, but if - # this was a comment line, then it's still not been set. - have_distributor_line = have_distributor_line or not lines[i].startswith("#") - # If btrfs or f2fs is used, don't save default - if no_save_default and lines[i].startswith("GRUB_SAVEDEFAULT="): - lines[i] = "#GRUB_SAVEDEFAULT=\"true\"" - else: - lines = [] + elif line.startswith("GRUB_DISTRIBUTOR") and libcalamares.job.configuration.get("keep_distributor", False): + distributor_parts = line.split("=") + if len(distributor_parts) > 1: + distributor = distributor_parts[1].strip("'\"") + # If a filesystem grub can't write to is used, disable save default + if no_save_default and line.strip().startswith("GRUB_SAVEDEFAULT"): + grub_config_items["GRUB_SAVEDEFAULT"] = "false" + + always_use_defaults = libcalamares.job.configuration.get("always_use_defaults", False) + + # If applicable add the items from defaults to the dict containing the grub config to wirte/modify + if always_use_defaults or overwrite or not os.path.exists(default_grub): if "defaults" in libcalamares.job.configuration: - for key, value in libcalamares.job.configuration[ - "defaults"].items(): - if value.__class__.__name__ == "bool": + for key, value in libcalamares.job.configuration["defaults"].items(): + if isinstance(value, bool): if value: escaped_value = "true" else: @@ -261,19 +269,20 @@ def modify_grub_default(partitions, root_mount_point, distributor): else: escaped_value = str(value).replace("'", "'\\''") - lines.append(f"{key}='{escaped_value}'") + grub_config_items[key] = f"'{escaped_value}'" - if not have_kernel_cmd: - lines.append(kernel_cmd) - - if not have_distributor_line: - lines.append(distributor_line) + grub_config_items['GRUB_CMDLINE_LINUX_DEFAULT'] = f"'{' '.join(kernel_params)}'" + grub_config_items["GRUB_DISTRIBUTOR"] = f"'{distributor}'" if cryptdevice_params and not unencrypted_separate_boot: - lines.append("GRUB_ENABLE_CRYPTODISK=y") + grub_config_items["GRUB_ENABLE_CRYPTODISK"] = "y" - with open(default_grub, 'w') as grub_file: - grub_file.write("\n".join(lines) + "\n") + if overwrite or not os.path.exists(default_grub) or libcalamares.job.configuration.get("prefer_grub_d", False): + with open(default_grub, 'w') as grub_file: + for key, value in grub_config_items.items(): + grub_file.write(f"{key}={value}\n") + else: + update_existing_config(default_grub, grub_config_items) return None diff --git a/src/modules/initcpiocfg/main.py b/src/modules/initcpiocfg/main.py index 744287cf6..31480ecd0 100644 --- a/src/modules/initcpiocfg/main.py +++ b/src/modules/initcpiocfg/main.py @@ -147,6 +147,7 @@ def find_initcpio_features(partitions, root_mount_point): "base", "udev", "autodetect", + "kms", "modconf", "block", "keyboard", @@ -176,8 +177,6 @@ def find_initcpio_features(partitions, root_mount_point): hooks.append("bootsplash-{!s}".format(bootsplash_theme)) for partition in partitions: - hooks.extend(["filesystems"]) - if partition["fs"] == "linuxswap" and not partition.get("claimed", None): # Skip foreign swap continue @@ -225,9 +224,11 @@ def find_initcpio_features(partitions, root_mount_point): hooks.append("zfs") if swap_uuid != "": - hooks.extend(["resume"]) if encrypt_hook and openswap_hook: hooks.extend(["openswap"]) + hooks.extend(["resume", "filesystems"]) + else: + hooks.extend(["filesystems"]) if uses_btrfs: modules.append("crc32c-intel" if cpuinfo().is_intel else "crc32c") diff --git a/src/modules/mount/main.py b/src/modules/mount/main.py index 49ffc9627..235b67060 100644 --- a/src/modules/mount/main.py +++ b/src/modules/mount/main.py @@ -294,13 +294,13 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun # Mount the subvolumes swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap") for s in btrfs_subvolumes: - mount_option = "subvol={}".format(s['subvolume']) if s['subvolume'] == swap_subvol: - mount_option += "," + get_mount_options("btrfs_swap", mount_options, partition) + mount_option_no_subvol = get_mount_options("btrfs_swap", mount_options, partition) else: - mount_option += "," + get_mount_options(fstype, mount_options, partition) + mount_option_no_subvol = get_mount_options(fstype, mount_options, partition) + mount_option = f"subvol={s['subvolume']},{mount_option_no_subvol}" subvolume_mountpoint = mount_point[:-1] + s['mountPoint'] - mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option}) + mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option_no_subvol}) if libcalamares.utils.mount(device, subvolume_mountpoint, fstype, @@ -308,6 +308,14 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun libcalamares.utils.warning("Cannot mount {}".format(device)) +def enable_swap_partition(devices): + try: + for d in devices: + libcalamares.utils.host_env_process_output(["swapon", d]) + except subprocess.CalledProcessError: + libcalamares.utils.warning(f"Failed to enable swap for devices: {devices}") + + def run(): """ Mount all the partitions from GlobalStorage and from the job configuration. @@ -321,6 +329,11 @@ def run(): return (_("Configuration Error"), _("No partitions are defined for
{!s}
to use.").format("mount")) + # Find existing swap partitions that are part of the installation and enable them now + swap_devices = [p['device'] for p in partitions if (p['fs'] == 'linuxswap' and p['claimed'])] + + enable_swap_partition(swap_devices) + root_mount_point = tempfile.mkdtemp(prefix="calamares-root-") # Get the mountOptions, if this is None, that is OK and will be handled later diff --git a/src/modules/netinstall/PackageModel.cpp b/src/modules/netinstall/PackageModel.cpp index d5bae1c89..4e48d3d09 100644 --- a/src/modules/netinstall/PackageModel.cpp +++ b/src/modules/netinstall/PackageModel.cpp @@ -188,7 +188,7 @@ PackageModel::flags( const QModelIndex& index ) const if ( index.column() == NameColumn ) { PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() ); - if ( item->isImmutable() ) + if ( item->isImmutable() || item->isNoncheckable() ) { return QAbstractItemModel::flags( index ); //Qt::NoItemFlags; } diff --git a/src/modules/netinstall/PackageTreeItem.cpp b/src/modules/netinstall/PackageTreeItem.cpp index 5841606c4..d5b680ea0 100644 --- a/src/modules/netinstall/PackageTreeItem.cpp +++ b/src/modules/netinstall/PackageTreeItem.cpp @@ -49,6 +49,7 @@ PackageTreeItem::PackageTreeItem( const QString& packageName, PackageTreeItem* p , m_isGroup( false ) , m_isCritical( parent ? parent->isCritical() : false ) , m_showReadOnly( parent ? parent->isImmutable() : false ) + , m_showNoncheckable( false ) { } @@ -60,6 +61,7 @@ PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, PackageTag&& par , m_isGroup( false ) , m_isCritical( parent.parent ? parent.parent->isCritical() : false ) , m_showReadOnly( parent.parent ? parent.parent->isImmutable() : false ) + , m_showNoncheckable( false ) { } @@ -75,6 +77,7 @@ PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, GroupTag&& paren , m_isCritical( parentCriticality( groupData, parent.parent ) ) , m_isHidden( CalamaresUtils::getBool( groupData, "hidden", false ) ) , m_showReadOnly( CalamaresUtils::getBool( groupData, "immutable", false ) ) + , m_showNoncheckable( CalamaresUtils::getBool( groupData, "noncheckable", false ) ) , m_startExpanded( CalamaresUtils::getBool( groupData, "expanded", false ) ) { } diff --git a/src/modules/netinstall/PackageTreeItem.h b/src/modules/netinstall/PackageTreeItem.h index 2a0fca83e..074bc3dc2 100644 --- a/src/modules/netinstall/PackageTreeItem.h +++ b/src/modules/netinstall/PackageTreeItem.h @@ -109,6 +109,13 @@ public: */ bool isImmutable() const { return m_showReadOnly; } + /** @brief Is this a non-checkable item? + * + * Groups can be non-checkable: then you can't toggle the selected + * state of the group. This does not affect subgroups or packages. + */ + bool isNoncheckable() const { return m_showNoncheckable; } + /** @brief is this item selected? * * Groups may be partially selected; packages are only on or off. @@ -165,6 +172,7 @@ private: bool m_isCritical = false; bool m_isHidden = false; bool m_showReadOnly = false; + bool m_showNoncheckable = false; bool m_startExpanded = false; }; diff --git a/src/modules/netinstall/netinstall.conf b/src/modules/netinstall/netinstall.conf index d38c0b040..f185fc1b0 100644 --- a/src/modules/netinstall/netinstall.conf +++ b/src/modules/netinstall/netinstall.conf @@ -183,6 +183,9 @@ label: # really only makes sense in combination with *selected* set to true, # so that the packages will be installed. (Setting a group to immutable # can be seen as removing it from the user-interface.) +# - *noncheckable*: if true, the entire group cannot be selected or +# deselected by a single click. This does not affect any subgroups +# or child packages # - *expanded*: if true, the group is shown in an expanded form (that is, # not-collapsed) in the treeview on start. This only affects the user- # interface. Only top-level groups are show expanded-initially. diff --git a/src/modules/netinstall/netinstall.schema.yaml b/src/modules/netinstall/netinstall.schema.yaml index 3ec63daed..abc7ae117 100644 --- a/src/modules/netinstall/netinstall.schema.yaml +++ b/src/modules/netinstall/netinstall.schema.yaml @@ -33,6 +33,7 @@ definitions: selected: { type: boolean } critical: { type: boolean, default: false } immutable: { type: boolean } + noncheckable: { type: boolean } expanded: { type: boolean } subgroups: type: array diff --git a/src/modules/packages/main.py b/src/modules/packages/main.py index e373a3443..26ed30a19 100644 --- a/src/modules/packages/main.py +++ b/src/modules/packages/main.py @@ -282,12 +282,12 @@ class PMDnf(PackageManager): backend = "dnf" def install(self, pkgs, from_local=False): - check_target_env_call(["dnf", "-y", "install"] + pkgs) + check_target_env_call(["dnf-3", "-y", "install"] + pkgs) def remove(self, pkgs): # ignore the error code for now because dnf thinks removing a # nonexistent package is an error - target_env_call(["dnf", "--disablerepo=*", "-C", "-y", + target_env_call(["dnf-3", "--disablerepo=*", "-C", "-y", "remove"] + pkgs) def update_db(self): @@ -295,7 +295,7 @@ class PMDnf(PackageManager): pass def update_system(self): - check_target_env_call(["dnf", "-y", "upgrade"]) + check_target_env_call(["dnf-3", "-y", "upgrade"]) class PMDummy(PackageManager): diff --git a/src/modules/partition/CMakeLists.txt b/src/modules/partition/CMakeLists.txt index 743d906bd..27125b030 100644 --- a/src/modules/partition/CMakeLists.txt +++ b/src/modules/partition/CMakeLists.txt @@ -88,7 +88,6 @@ if(KPMcore_FOUND) gui/PartitionSplitterWidget.cpp gui/ResizeVolumeGroupDialog.cpp gui/ScanningDialog.cpp - gui/ReplaceWidget.cpp gui/VolumeGroupBaseDialog.cpp jobs/AutoMountManagementJob.cpp jobs/ChangeFilesystemLabelJob.cpp @@ -113,7 +112,6 @@ if(KPMcore_FOUND) gui/EditExistingPartitionDialog.ui gui/EncryptWidget.ui gui/PartitionPage.ui - gui/ReplaceWidget.ui gui/VolumeGroupBaseDialog.ui LINK_PRIVATE_LIBRARIES calamares::kpmcore diff --git a/src/modules/partition/Config.cpp b/src/modules/partition/Config.cpp index 367c4ee38..4519bd278 100644 --- a/src/modules/partition/Config.cpp +++ b/src/modules/partition/Config.cpp @@ -25,23 +25,52 @@ Config::Config( QObject* parent ) const NamedEnumTable< Config::InstallChoice >& Config::installChoiceNames() { - static const NamedEnumTable< InstallChoice > names { { QStringLiteral( "none" ), InstallChoice::NoChoice }, - { QStringLiteral( "nochoice" ), InstallChoice::NoChoice }, - { QStringLiteral( "alongside" ), InstallChoice::Alongside }, - { QStringLiteral( "erase" ), InstallChoice::Erase }, - { QStringLiteral( "replace" ), InstallChoice::Replace }, - { QStringLiteral( "manual" ), InstallChoice::Manual } }; + // *INDENT-OFF* + // clang-format off + static const NamedEnumTable< InstallChoice > names { + { QStringLiteral( "none" ), InstallChoice::NoChoice }, + { QStringLiteral( "nochoice" ), InstallChoice::NoChoice }, + { QStringLiteral( "alongside" ), InstallChoice::Alongside }, + { QStringLiteral( "erase" ), InstallChoice::Erase }, + { QStringLiteral( "replace" ), InstallChoice::Replace }, + { QStringLiteral( "manual" ), InstallChoice::Manual }, + }; + // clang-format on + // *INDENT-ON* + return names; } const NamedEnumTable< Config::SwapChoice >& Config::swapChoiceNames() { - static const NamedEnumTable< SwapChoice > names { { QStringLiteral( "none" ), SwapChoice::NoSwap }, - { QStringLiteral( "small" ), SwapChoice::SmallSwap }, - { QStringLiteral( "suspend" ), SwapChoice::FullSwap }, - { QStringLiteral( "reuse" ), SwapChoice::ReuseSwap }, - { QStringLiteral( "file" ), SwapChoice::SwapFile } }; + // *INDENT-OFF* + // clang-format off + static const NamedEnumTable< SwapChoice > names { + { QStringLiteral( "none" ), SwapChoice::NoSwap }, + { QStringLiteral( "small" ), SwapChoice::SmallSwap }, + { QStringLiteral( "suspend" ), SwapChoice::FullSwap }, + { QStringLiteral( "reuse" ), SwapChoice::ReuseSwap }, + { QStringLiteral( "file" ), SwapChoice::SwapFile }, + }; + // clang-format on + // *INDENT-ON* + + return names; +} + +const NamedEnumTable< Config::LuksGeneration >& +Config::luksGenerationNames() +{ + // *INDENT-OFF* + // clang-format off + static const NamedEnumTable< LuksGeneration > names { + { QStringLiteral( "luks1" ), LuksGeneration::Luks1 }, + { QStringLiteral( "luks" ), LuksGeneration::Luks1 }, + { QStringLiteral( "luks2" ), LuksGeneration::Luks2 }, + }; + // clang-format on + // *INDENT-ON* return names; } @@ -213,7 +242,7 @@ Config::setSwapChoice( Config::SwapChoice c ) void Config::setEraseFsTypeChoice( const QString& choice ) { - QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr ); + const QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr ); if ( canonicalChoice != m_eraseFsTypeChoice ) { m_eraseFsTypeChoice = canonicalChoice; @@ -221,6 +250,17 @@ Config::setEraseFsTypeChoice( const QString& choice ) } } +void +Config::setReplaceFilesystemChoice( const QString& filesystemName ) +{ + const QString canonicalChoice = PartUtils::canonicalFilesystemName( filesystemName, nullptr ); + if ( canonicalChoice != m_replaceFileSystemChoice ) + { + m_replaceFileSystemChoice = canonicalChoice; + Q_EMIT replaceModeFilesystemChanged( canonicalChoice ); + } +} + bool Config::acceptPartitionTableType( PartitionTable::TableType tableType ) const { @@ -252,8 +292,8 @@ fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configu gs->insert( "efiSystemPartitionSize_i", part_size.toBytes() ); // Assign long long int to long unsigned int to prevent compilation warning - size_t unsigned_part_size = part_size.toBytes(); - if ( unsigned_part_size != PartUtils::efiFilesystemMinimumSize() ) + auto byte_part_size = part_size.toBytes(); + if ( byte_part_size != PartUtils::efiFilesystemMinimumSize() ) { cWarning() << "EFI partition size" << sizeString << "has been adjusted to" << PartUtils::efiFilesystemMinimumSize() << "bytes"; @@ -323,13 +363,26 @@ Config::fillConfigurationFSTypes( const QVariantMap& configurationMap ) } } + // Set LUKS file system based on luksGeneration provided, defaults to 'luks'. + bool nameFound = false; + Config::LuksGeneration luksGeneration + = luksGenerationNames().find( CalamaresUtils::getString( configurationMap, "luksGeneration" ), nameFound ); + if ( !nameFound ) + { + cWarning() << "Partition-module setting *luksGeneration* not found or invalid. Defaulting to luks1."; + luksGeneration = Config::LuksGeneration::Luks1; + } + m_luksFileSystemType = luksGeneration; + gs->insert( "luksFileSystemType", luksGenerationNames().find( luksGeneration ) ); + Q_ASSERT( !m_eraseFsTypes.isEmpty() ); Q_ASSERT( m_eraseFsTypes.contains( fsRealName ) ); m_eraseFsTypeChoice = fsRealName; + m_replaceFileSystemChoice = fsRealName; Q_EMIT eraseModeFilesystemChanged( m_eraseFsTypeChoice ); + Q_EMIT replaceModeFilesystemChanged( m_replaceFileSystemChoice ); } - void Config::setConfigurationMap( const QVariantMap& configurationMap ) { @@ -356,6 +409,8 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) } setSwapChoice( m_initialSwapChoice ); + m_allowZfsEncryption = CalamaresUtils::getBool( configurationMap, "allowZfsEncryption", true ); + m_allowManualPartitioning = CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true ); m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" ); diff --git a/src/modules/partition/Config.h b/src/modules/partition/Config.h index 2ac44b424..0146d1a3e 100644 --- a/src/modules/partition/Config.h +++ b/src/modules/partition/Config.h @@ -31,6 +31,9 @@ class Config : public QObject Q_PROPERTY( QString eraseModeFilesystem READ eraseFsType WRITE setEraseFsTypeChoice NOTIFY eraseModeFilesystemChanged ) + Q_PROPERTY( QString replaceModeFilesystem READ replaceModeFilesystem WRITE setReplaceFilesystemChoice NOTIFY + replaceModeFilesystemChanged ) + Q_PROPERTY( bool allowManualPartitioning READ allowManualPartitioning CONSTANT FINAL ) public: @@ -63,6 +66,15 @@ public: using EraseFsTypesSet = QStringList; + /** @brief Choice of LUKS disk encryption generation */ + enum class LuksGeneration + { + Luks1, // First generation of LUKS + Luks2, // Second generation of LUKS, default since cryptsetup >= 2.1.0 + }; + Q_ENUM( LuksGeneration ) + static const NamedEnumTable< LuksGeneration >& luksGenerationNames(); + void setConfigurationMap( const QVariantMap& ); /** @brief Set GS values where other modules configuration has priority * @@ -122,6 +134,9 @@ public: */ QString eraseFsType() const { return m_eraseFsTypeChoice; } + /// @brief Currently-selected FS type for *replace* mode + QString replaceModeFilesystem() const { return m_replaceFileSystemChoice; } + /** @brief Configured default FS type (for other modes than erase) * * This is not "Unknown" or "Unformatted" @@ -140,33 +155,44 @@ public: /// @brief Returns list of acceptable types. May be empty. QStringList partitionTableTypes() const { return m_requiredPartitionTableType; } + /** @brief The configured LUKS generation (1 or 2) + */ + LuksGeneration luksFileSystemType() const { return m_luksFileSystemType; } + + /// @brief If zfs encryption should be allowed + bool allowZfsEncryption() const { return m_allowZfsEncryption; } + public Q_SLOTS: void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice void setInstallChoice( InstallChoice ); void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice void setSwapChoice( SwapChoice ); void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem + void setReplaceFilesystemChoice( const QString& filesystemName ); Q_SIGNALS: void installChoiceChanged( InstallChoice ); void swapChoiceChanged( SwapChoice ); void eraseModeFilesystemChanged( const QString& ); + void replaceModeFilesystemChanged( const QString& ); private: /** @brief Handle FS-type configuration, for erase and default */ void fillConfigurationFSTypes( const QVariantMap& configurationMap ); EraseFsTypesSet m_eraseFsTypes; QString m_eraseFsTypeChoice; + QString m_replaceFileSystemChoice; FileSystem::Type m_defaultFsType; SwapChoiceSet m_swapChoices; SwapChoice m_initialSwapChoice = NoSwap; SwapChoice m_swapChoice = NoSwap; + LuksGeneration m_luksFileSystemType = LuksGeneration::Luks1; InstallChoice m_initialInstallChoice = NoChoice; InstallChoice m_installChoice = NoChoice; qreal m_requiredStorageGiB = 0.0; // May duplicate setting in the welcome module QStringList m_requiredPartitionTableType; - + bool m_allowZfsEncryption = true; bool m_allowManualPartitioning = true; }; diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index fe77bee3a..a95ed0a9a 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -447,7 +447,6 @@ PartitionViewStep::onActivate() { m_choicePage->applyActionChoice( Config::InstallChoice::Alongside ); // m_choicePage->reset(); - //FIXME: ReplaceWidget should be reset maybe? } } @@ -616,7 +615,9 @@ PartitionViewStep::onLeave() // If the root partition is encrypted, and there's a separate boot // partition which is not encrypted - if ( root_p->fileSystem().type() == FileSystem::Luks && boot_p->fileSystem().type() != FileSystem::Luks ) + if ( ( root_p->fileSystem().type() == FileSystem::Luks && boot_p->fileSystem().type() != FileSystem::Luks ) + || ( root_p->fileSystem().type() == FileSystem::Luks2 + && boot_p->fileSystem().type() != FileSystem::Luks2 ) ) { message = tr( "Boot partition not encrypted" ); description = tr( "A separate boot partition was set up together with " diff --git a/src/modules/partition/core/ColorUtils.cpp b/src/modules/partition/core/ColorUtils.cpp index fb613e240..5368c2f86 100644 --- a/src/modules/partition/core/ColorUtils.cpp +++ b/src/modules/partition/core/ColorUtils.cpp @@ -98,7 +98,7 @@ colorForPartition( Partition* partition ) if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone && !partition->fileSystem().uuid().isEmpty() ) { - if ( partition->fileSystem().type() == FileSystem::Luks ) + if ( partition->fileSystem().type() == FileSystem::Luks || partition->fileSystem().type() == FileSystem::Luks2 ) { FS::luks& luksFs = dynamic_cast< FS::luks& >( partition->fileSystem() ); if ( !luksFs.outerUuid().isEmpty() && s_partitionColorsCache.contains( luksFs.outerUuid() ) ) @@ -146,7 +146,7 @@ colorForPartition( Partition* partition ) if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone && !partition->fileSystem().uuid().isEmpty() ) { - if ( partition->fileSystem().type() == FileSystem::Luks ) + if ( partition->fileSystem().type() == FileSystem::Luks || partition->fileSystem().type() == FileSystem::Luks2 ) { FS::luks& luksFs = dynamic_cast< FS::luks& >( partition->fileSystem() ); if ( !luksFs.outerUuid().isEmpty() ) diff --git a/src/modules/partition/core/KPMHelpers.cpp b/src/modules/partition/core/KPMHelpers.cpp index 102d803cb..44da940fb 100644 --- a/src/modules/partition/core/KPMHelpers.cpp +++ b/src/modules/partition/core/KPMHelpers.cpp @@ -84,6 +84,7 @@ createNewEncryptedPartition( PartitionNode* parent, const QString& fsLabel, qint64 firstSector, qint64 lastSector, + Config::LuksGeneration luksFsType, const QString& passphrase, PartitionTable::Flags flags ) { @@ -93,8 +94,10 @@ createNewEncryptedPartition( PartitionNode* parent, newRoles |= PartitionRole::Luks; } + FileSystem::Type luksType = luksGenerationToFSName( luksFsType ); + FS::luks* fs = dynamic_cast< FS::luks* >( - FileSystemFactory::create( FileSystem::Luks, firstSector, lastSector, device.logicalSize() ) ); + FileSystemFactory::create( luksType, firstSector, lastSector, device.logicalSize() ) ); if ( !fs ) { cError() << "cannot create LUKS filesystem. Giving up."; @@ -296,6 +299,24 @@ cryptVersion( Partition* partition ) return luksVersion; } +FileSystem::Type +luksGenerationToFSName( Config::LuksGeneration luksGeneration ) +{ + // Convert luksGenerationChoice from partition.conf into its + // corresponding file system type from KPMCore. + switch ( luksGeneration ) + { + case Config::LuksGeneration::Luks2: + return FileSystem::Type::Luks2; + case Config::LuksGeneration::Luks1: + return FileSystem::Type::Luks; + default: + cWarning() << "luksGeneration not supported, defaulting to \"luks\""; + return FileSystem::Type::Luks; + } +} + + Calamares::JobResult execute( Operation& operation, const QString& failureMessage ) { diff --git a/src/modules/partition/core/KPMHelpers.h b/src/modules/partition/core/KPMHelpers.h index 2b2da6b14..289520ec5 100644 --- a/src/modules/partition/core/KPMHelpers.h +++ b/src/modules/partition/core/KPMHelpers.h @@ -11,6 +11,7 @@ #ifndef KPMHELPERS_H #define KPMHELPERS_H +#include "Config.h" #include "Job.h" #include @@ -83,6 +84,7 @@ Partition* createNewEncryptedPartition( PartitionNode* parent, const QString& fsLabel, qint64 firstSector, qint64 lastSector, + Config::LuksGeneration luksFsType, const QString& passphrase, PartitionTable::Flags flags ); @@ -119,6 +121,17 @@ bool cryptLabel( Partition* partition, const QString& label ); */ int cryptVersion( Partition* partition ); +/** @brief Convert a luksGeneration into its FS type for KPMCore. + * + * Will convert Luks1 into FileSystem::Type::luks and Luks2 into + * FileSystem::Type::luks2 for KPMCore partitioning functions. + * + * @return The LUKS FS type (default @c luks ) + */ +FileSystem::Type +luksGenerationToFSName( Config::LuksGeneration luksGeneration ); + + /** @brief Return a result for an @p operation * * Executes the operation, and if successful, returns a success result. diff --git a/src/modules/partition/core/PartitionActions.cpp b/src/modules/partition/core/PartitionActions.cpp index 0ceff4324..2287c57bd 100644 --- a/src/modules/partition/core/PartitionActions.cpp +++ b/src/modules/partition/core/PartitionActions.cpp @@ -169,7 +169,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO lastSectorForRoot -= suggestedSwapSizeB / sectorSize + 1; } - core->layoutApply( dev, firstFreeSector, lastSectorForRoot, o.luksPassphrase ); + core->layoutApply( dev, firstFreeSector, lastSectorForRoot, o.luksFsType, o.luksPassphrase ); if ( shouldCreateSwap ) { @@ -194,6 +194,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO QStringLiteral( "swap" ), lastSectorForRoot + 1, dev->totalLogical() - 1, + o.luksFsType, o.luksPassphrase, KPM_PARTITION_FLAG( None ) ); } @@ -216,6 +217,12 @@ doReplacePartition( PartitionCoreModule* core, Device* dev, Partition* partition cDebug() << "doReplacePartition for device" << partition->partitionPath(); + // Looking up the defaultFsType (which should name a filesystem type) + // will log an error and set the type to Unknown if there's something wrong. + FileSystem::Type type = FileSystem::Unknown; + PartUtils::canonicalFilesystemName( o.defaultFsType, &type ); + core->partitionLayout().setDefaultFsType( type == FileSystem::Unknown ? FileSystem::Ext4 : type ); + PartitionRole newRoles( partition->roles() ); if ( partition->roles().has( PartitionRole::Extended ) ) { @@ -244,7 +251,7 @@ doReplacePartition( PartitionCoreModule* core, Device* dev, Partition* partition core->deletePartition( dev, partition ); } - core->layoutApply( dev, firstSector, lastSector, o.luksPassphrase ); + core->layoutApply( dev, firstSector, lastSector, o.luksFsType, o.luksPassphrase ); core->dumpQueue(); } diff --git a/src/modules/partition/core/PartitionActions.h b/src/modules/partition/core/PartitionActions.h index d6b86a6cd..fdf56e56e 100644 --- a/src/modules/partition/core/PartitionActions.h +++ b/src/modules/partition/core/PartitionActions.h @@ -31,12 +31,17 @@ struct ReplacePartitionOptions { QString defaultPartitionTableType; // e.g. "gpt" or "msdos" QString defaultFsType; // e.g. "ext4" or "btrfs" + Config::LuksGeneration luksFsType = Config::LuksGeneration::Luks1; // optional ("luks", "luks2") QString luksPassphrase; // optional - ReplacePartitionOptions( const QString& pt, const QString& fs, const QString& luks ) + ReplacePartitionOptions( const QString& pt, + const QString& fs, + Config::LuksGeneration luksFs, + const QString& luksPassphrase ) : defaultPartitionTableType( pt ) , defaultFsType( fs ) - , luksPassphrase( luks ) + , luksFsType( luksFs ) + , luksPassphrase( luksPassphrase ) { } }; @@ -49,11 +54,12 @@ struct AutoPartitionOptions : ReplacePartitionOptions AutoPartitionOptions( const QString& pt, const QString& fs, - const QString& luks, + Config::LuksGeneration luksFs, + const QString& luksPassphrase, const QString& efi, qint64 requiredBytes, Config::SwapChoice s ) - : ReplacePartitionOptions( pt, fs, luks ) + : ReplacePartitionOptions( pt, fs, luksFs, luksPassphrase ) , efiPartitionMountPoint( efi ) , requiredSpaceB( requiredBytes > 0 ? quint64( requiredBytes ) : 0U ) , swap( s ) diff --git a/src/modules/partition/core/PartitionCoreModule.cpp b/src/modules/partition/core/PartitionCoreModule.cpp index 78af2abf7..8eef01223 100644 --- a/src/modules/partition/core/PartitionCoreModule.cpp +++ b/src/modules/partition/core/PartitionCoreModule.cpp @@ -952,13 +952,14 @@ void PartitionCoreModule::layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, + Config::LuksGeneration luksFsType, QString luksPassphrase, PartitionNode* parent, const PartitionRole& role ) { - bool isEfi = PartUtils::isEfiSystem(); + const bool isEfi = PartUtils::isEfiSystem(); QList< Partition* > partList - = m_partLayout.createPartitions( dev, firstSector, lastSector, luksPassphrase, parent, role ); + = m_partLayout.createPartitions( dev, firstSector, lastSector, luksFsType, luksPassphrase, parent, role ); // Partition::mountPoint() tells us where it is mounted **now**, while // PartitionInfo::mountPoint() says where it will be mounted in the target system. @@ -999,10 +1000,19 @@ PartitionCoreModule::layoutApply( Device* dev, } void -PartitionCoreModule::layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase ) +PartitionCoreModule::layoutApply( Device* dev, + qint64 firstSector, + qint64 lastSector, + Config::LuksGeneration luksFsType, + QString luksPassphrase ) { - layoutApply( - dev, firstSector, lastSector, luksPassphrase, dev->partitionTable(), PartitionRole( PartitionRole::Primary ) ); + layoutApply( dev, + firstSector, + lastSector, + luksFsType, + luksPassphrase, + dev->partitionTable(), + PartitionRole( PartitionRole::Primary ) ); } void diff --git a/src/modules/partition/core/PartitionCoreModule.h b/src/modules/partition/core/PartitionCoreModule.h index 1ed46fdad..8a7140a8c 100644 --- a/src/modules/partition/core/PartitionCoreModule.h +++ b/src/modules/partition/core/PartitionCoreModule.h @@ -12,6 +12,7 @@ #ifndef PARTITIONCOREMODULE_H #define PARTITIONCOREMODULE_H +#include "Config.h" #include "core/KPMHelpers.h" #include "core/PartitionLayout.h" #include "core/PartitionModel.h" @@ -166,10 +167,11 @@ public: */ PartitionLayout& partitionLayout() { return m_partLayout; } - void layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase ); + void layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, Config::LuksGeneration luksFsType, QString luksPassphrase ); void layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, + Config::LuksGeneration luksFsType, QString luksPassphrase, PartitionNode* parent, const PartitionRole& role ); diff --git a/src/modules/partition/core/PartitionLayout.cpp b/src/modules/partition/core/PartitionLayout.cpp index a8aae61ff..61c701042 100644 --- a/src/modules/partition/core/PartitionLayout.cpp +++ b/src/modules/partition/core/PartitionLayout.cpp @@ -204,6 +204,7 @@ QList< Partition* > PartitionLayout::createPartitions( Device* dev, qint64 firstSector, qint64 lastSector, + Config::LuksGeneration luksFsType, QString luksPassphrase, PartitionNode* parent, const PartitionRole& role ) @@ -317,6 +318,7 @@ PartitionLayout::createPartitions( Device* dev, entry.partLabel, currentSector, currentSector + sectors - 1, + luksFsType, luksPassphrase, KPM_PARTITION_FLAG( None ) ); } diff --git a/src/modules/partition/core/PartitionLayout.h b/src/modules/partition/core/PartitionLayout.h index c4c26d9f1..6a3aad6fc 100644 --- a/src/modules/partition/core/PartitionLayout.h +++ b/src/modules/partition/core/PartitionLayout.h @@ -11,9 +11,9 @@ #ifndef PARTITIONLAYOUT_H #define PARTITIONLAYOUT_H -#include "partition/PartitionSize.h" - +#include "Config.h" #include "core/PartUtils.h" +#include "partition/PartitionSize.h" // KPMcore #include @@ -116,6 +116,7 @@ public: QList< Partition* > createPartitions( Device* dev, qint64 firstSector, qint64 lastSector, + Config::LuksGeneration luksFsType, QString luksPassphrase, PartitionNode* parent, const PartitionRole& role ); diff --git a/src/modules/partition/gui/ChoicePage.cpp b/src/modules/partition/gui/ChoicePage.cpp index c5e44aaec..b31d042fd 100644 --- a/src/modules/partition/gui/ChoicePage.cpp +++ b/src/modules/partition/gui/ChoicePage.cpp @@ -4,6 +4,7 @@ * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot * SPDX-FileCopyrightText: 2019 Collabora Ltd * SPDX-FileCopyrightText: 2021 Anubhav Choudhary + * SPDX-FileCopyrightText: 2023 Evan James * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -28,7 +29,6 @@ #include "gui/PartitionBarsView.h" #include "gui/PartitionLabelsView.h" #include "gui/PartitionSplitterWidget.h" -#include "gui/ReplaceWidget.h" #include "gui/ScanningDialog.h" #include "Branding.h" @@ -288,6 +288,16 @@ ChoicePage::setupChoices() m_eraseFsTypesChoiceComboBox, &QComboBox::currentTextChanged, m_config, &Config::setEraseFsTypeChoice ); connect( m_config, &Config::eraseModeFilesystemChanged, this, &ChoicePage::onActionChanged ); m_eraseButton->addOptionsComboBox( m_eraseFsTypesChoiceComboBox ); + + // Also offer it for "replace + m_replaceFsTypesChoiceComboBox = new QComboBox; + m_replaceFsTypesChoiceComboBox->addItems( m_config->eraseFsTypes() ); + connect( m_replaceFsTypesChoiceComboBox, + &QComboBox::currentTextChanged, + m_config, + &Config::setReplaceFilesystemChoice ); + connect( m_config, &Config::replaceModeFilesystemChanged, this, &ChoicePage::onActionChanged ); + m_replaceButton->addOptionsComboBox( m_replaceFsTypesChoiceComboBox ); } m_itemsLayout->addWidget( m_alongsideButton ); @@ -303,13 +313,8 @@ ChoicePage::setupChoices() m_itemsLayout->addStretch(); -#if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) ) - auto buttonSignal = QOverload< int, bool >::of( &QButtonGroup::buttonToggled ); -#else - auto buttonSignal = &QButtonGroup::idToggled; -#endif connect( m_grp, - buttonSignal, + &QButtonGroup::idToggled, this, [ this ]( int id, bool checked ) { @@ -449,7 +454,6 @@ ChoicePage::continueApplyDeviceChoice() if ( m_lastSelectedDeviceIndex != m_drivesCombo->currentIndex() ) { m_lastSelectedDeviceIndex = m_drivesCombo->currentIndex(); - m_lastSelectedActionIndex = -1; m_config->setInstallChoice( m_config->initialInstallChoice() ); checkInstallChoiceRadioButton( m_config->installChoice() ); } @@ -461,6 +465,18 @@ ChoicePage::continueApplyDeviceChoice() void ChoicePage::onActionChanged() { + if ( m_enableEncryptionWidget ) + { + if ( m_config->installChoice() == InstallChoice::Erase && m_eraseFsTypesChoiceComboBox ) + { + m_encryptWidget->setFilesystem( FileSystem::typeForName( m_eraseFsTypesChoiceComboBox->currentText() ) ); + } + else if ( m_config->installChoice() == InstallChoice::Replace && m_replaceFsTypesChoiceComboBox ) + { + m_encryptWidget->setFilesystem( FileSystem::typeForName( m_replaceFsTypesChoiceComboBox->currentText() ) ); + } + } + Device* currd = selectedDevice(); if ( currd ) { @@ -483,9 +499,9 @@ ChoicePage::onEraseSwapChoiceChanged() void ChoicePage::applyActionChoice( InstallChoice choice ) { - cDebug() << "Prev" << m_lastSelectedActionIndex << "InstallChoice" << choice - << Config::installChoiceNames().find( choice ); + cDebug() << "InstallChoice" << choice << Config::installChoiceNames().find( choice ); m_beforePartitionBarsView->selectionModel()->disconnect( SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ) ); + auto priorSelection = m_beforePartitionBarsView->selectionModel()->currentIndex(); m_beforePartitionBarsView->selectionModel()->clearSelection(); m_beforePartitionBarsView->selectionModel()->clearCurrentIndex(); @@ -496,6 +512,7 @@ ChoicePage::applyActionChoice( InstallChoice choice ) auto gs = Calamares::JobQueue::instance()->globalStorage(); PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(), m_config->eraseFsType(), + m_config->luksFileSystemType(), m_encryptWidget->passphrase(), gs->value( "efiSystemPartition" ).toString(), CalamaresUtils::GiBtoBytes( @@ -543,6 +560,12 @@ ChoicePage::applyActionChoice( InstallChoice choice ) this, SLOT( onPartitionToReplaceSelected( QModelIndex, QModelIndex ) ), Qt::UniqueConnection ); + + // Maintain the selection for replace + if ( priorSelection.isValid() ) + { + m_beforePartitionBarsView->selectionModel()->setCurrentIndex( priorSelection, QItemSelectionModel::Select ); + } break; case InstallChoice::Alongside: @@ -752,6 +775,7 @@ ChoicePage::doAlongsideApply() m_core->layoutApply( dev, newLastSector + 2, oldLastSector, + m_config->luksFileSystemType(), m_encryptWidget->passphrase(), candidate->parent(), candidate->roles() ); @@ -826,6 +850,7 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current ) m_core->layoutApply( selectedDevice(), selectedPartition->firstSector(), selectedPartition->lastSector(), + m_config->luksFileSystemType(), m_encryptWidget->passphrase(), newParent, newRoles ); @@ -857,7 +882,8 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current ) selectedDevice(), selectedPartition, { gs->value( "defaultPartitionType" ).toString(), - gs->value( "defaultFileSystemType" ).toString(), + m_config->replaceModeFilesystem(), + m_config->luksFileSystemType(), m_encryptWidget->passphrase() } ); Partition* homePartition = findPartitionByPath( { selectedDevice() }, *homePartitionPath ); @@ -1731,10 +1757,20 @@ ChoicePage::createBootloaderPanel() return panelWidget; } -bool ChoicePage::shouldShowEncryptWidget( Config::InstallChoice choice ) const +bool +ChoicePage::shouldShowEncryptWidget( Config::InstallChoice choice ) const { - // If there are any choices for FS, check it's not ZFS because that doesn't - // support the kind of encryption we enable here. - const bool suitableFS = m_eraseFsTypesChoiceComboBox ? m_eraseFsTypesChoiceComboBox->currentText() != "zfs" : true; - return (choice == InstallChoice::Erase) && m_enableEncryptionWidget && suitableFS; + bool suitableFS = true; + if ( !m_config->allowZfsEncryption() + && ( ( m_eraseFsTypesChoiceComboBox && m_eraseFsTypesChoiceComboBox->isVisible() + && m_eraseFsTypesChoiceComboBox->currentText() == "zfs" ) + || ( m_replaceFsTypesChoiceComboBox && m_replaceFsTypesChoiceComboBox->isVisible() + && m_replaceFsTypesChoiceComboBox->currentText() == "zfs" ) ) ) + { + suitableFS = false; + } + + const bool suitableChoice + = choice == InstallChoice::Erase || choice == InstallChoice::Alongside || choice == InstallChoice::Replace; + return suitableChoice && m_enableEncryptionWidget && suitableFS; } diff --git a/src/modules/partition/gui/ChoicePage.h b/src/modules/partition/gui/ChoicePage.h index 6d77629d4..7deb4dec6 100644 --- a/src/modules/partition/gui/ChoicePage.h +++ b/src/modules/partition/gui/ChoicePage.h @@ -3,6 +3,7 @@ * SPDX-FileCopyrightText: 2014-2016 Teo Mrnjavac * SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot * SPDX-FileCopyrightText: 2019 Collabora Ltd + * SPDX-FileCopyrightText: 2023 Evan James * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -152,6 +153,7 @@ private: Calamares::Widgets::PrettyRadioButton* m_somethingElseButton; QComboBox* m_eraseSwapChoiceComboBox = nullptr; // UI, see also Config's swap choice QComboBox* m_eraseFsTypesChoiceComboBox = nullptr; // UI, see also Config's erase-mode FS + QComboBox* m_replaceFsTypesChoiceComboBox = nullptr; // UI, see also Config's erase-mode FS DeviceInfoWidget* m_deviceInfoWidget; @@ -166,7 +168,6 @@ private: QPointer< QComboBox > m_efiComboBox; int m_lastSelectedDeviceIndex = -1; - int m_lastSelectedActionIndex = -1; bool m_enableEncryptionWidget = false; diff --git a/src/modules/partition/gui/CreatePartitionDialog.cpp b/src/modules/partition/gui/CreatePartitionDialog.cpp index c5b17c69e..8c8116b2e 100644 --- a/src/modules/partition/gui/CreatePartitionDialog.cpp +++ b/src/modules/partition/gui/CreatePartitionDialog.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -223,6 +224,8 @@ CreatePartitionDialog::initGptPartitionTypeUi() Partition* CreatePartitionDialog::getNewlyCreatedPartition() { + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + if ( m_role.roles() == PartitionRole::None ) { m_role = PartitionRole( m_ui->extendedRadioButton->isChecked() ? PartitionRole::Extended @@ -242,12 +245,21 @@ CreatePartitionDialog::getNewlyCreatedPartition() // newFlags() and the consumer (see PartitionPage::onCreateClicked) // does so, to set up the partition for create-and-then-set-flags. Partition* partition = nullptr; + QString luksFsType = gs->value( "luksFileSystemType" ).toString(); QString luksPassphrase = m_ui->encryptWidget->passphrase(); if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty() && fsType != FileSystem::Zfs ) { - partition = KPMHelpers::createNewEncryptedPartition( - m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() ); + partition = KPMHelpers::createNewEncryptedPartition( m_parent, + *m_device, + m_role, + fsType, + fsLabel, + first, + last, + Config::luksGenerationNames().find(luksFsType, Config::LuksGeneration::Luks1), + luksPassphrase, + PartitionTable::Flags() ); } else { @@ -308,6 +320,12 @@ CreatePartitionDialog::updateMountPointUi() m_ui->encryptWidget->show(); m_ui->encryptWidget->reset(); } + else if ( FileSystemFactory::map()[ FileSystem::Type::Luks2 ]->supportCreate() + && FS::luks2::canEncryptType( type ) && !m_role.has( PartitionRole::Extended ) ) + { + m_ui->encryptWidget->show(); + m_ui->encryptWidget->reset(); + } else { m_ui->encryptWidget->reset(); diff --git a/src/modules/partition/gui/EncryptWidget.cpp b/src/modules/partition/gui/EncryptWidget.cpp index 7c17de7af..f2ed1d17e 100644 --- a/src/modules/partition/gui/EncryptWidget.cpp +++ b/src/modules/partition/gui/EncryptWidget.cpp @@ -2,6 +2,7 @@ * * SPDX-FileCopyrightText: 2016 Teo Mrnjavac * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-FileCopyrightText: 2023 Evan James * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -17,6 +18,8 @@ #include "utils/CalamaresUtilsGui.h" #include "utils/Retranslator.h" +constexpr int ZFS_MIN_LENGTH = 8; + /** @brief Does this system support whole-disk encryption? * * Returns @c true if the system is likely to support encryption @@ -143,7 +146,7 @@ applyPixmap( QLabel* label, CalamaresUtils::ImageType pixmap ) } void -EncryptWidget::updateState() +EncryptWidget::updateState( const bool notify ) { if ( m_ui->m_passphraseLineEdit->isVisible() ) { @@ -155,6 +158,11 @@ EncryptWidget::updateState() applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusWarning ); m_ui->m_iconLabel->setToolTip( tr( "Please enter the same passphrase in both boxes." ) ); } + else if ( m_filesystem == FileSystem::Zfs && p1.length() < ZFS_MIN_LENGTH ) + { + applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusError ); + m_ui->m_iconLabel->setToolTip( tr( "Password must be a minimum of %1 characters" ).arg( ZFS_MIN_LENGTH ) ); + } else if ( p1 == p2 ) { applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusOk ); @@ -172,7 +180,10 @@ EncryptWidget::updateState() if ( newState != m_state ) { m_state = newState; - Q_EMIT stateChanged( m_state ); + if ( notify ) + { + Q_EMIT stateChanged( m_state ); + } } } @@ -201,3 +212,13 @@ EncryptWidget::onCheckBoxStateChanged( int checked ) updateState(); } + +void +EncryptWidget::setFilesystem( const FileSystem::Type fs ) +{ + m_filesystem = fs; + if ( m_state != Encryption::Disabled ) + { + updateState( false ); + } +} diff --git a/src/modules/partition/gui/EncryptWidget.h b/src/modules/partition/gui/EncryptWidget.h index 9a3b8ab1f..9669b4d21 100644 --- a/src/modules/partition/gui/EncryptWidget.h +++ b/src/modules/partition/gui/EncryptWidget.h @@ -2,6 +2,7 @@ * * SPDX-FileCopyrightText: 2016 Teo Mrnjavac * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-FileCopyrightText: 2023 Evan James * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -14,6 +15,8 @@ #include +#include + namespace Ui { class EncryptWidget; @@ -38,6 +41,12 @@ public: Encryption state() const; void setText( const QString& text ); + /** + * @brief setFilesystem sets the filesystem name used for password validation + * @param fs A QString containing the name of the filesystem + */ + void setFilesystem( const FileSystem::Type fs ); + QString passphrase() const; void retranslate(); @@ -46,12 +55,14 @@ signals: void stateChanged( Encryption ); private: - void updateState(); + void updateState( const bool notify = true ); void onPassphraseEdited(); void onCheckBoxStateChanged( int checked ); Ui::EncryptWidget* m_ui; Encryption m_state; + + FileSystem::Type m_filesystem; }; #endif // ENCRYPTWIDGET_H diff --git a/src/modules/partition/gui/ReplaceWidget.cpp b/src/modules/partition/gui/ReplaceWidget.cpp deleted file mode 100644 index 76f9ff963..000000000 --- a/src/modules/partition/gui/ReplaceWidget.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* === This file is part of Calamares - === - * - * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac - * SPDX-FileCopyrightText: 2014 Aurélien Gâteau - * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot - * 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 -#include - -#include - -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.
" - "Warning: 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( "%4

" - "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( "%2

" - "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( "%3

" - "%1 will be installed on %2.
" - "Warning: 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( "%3

" - "%1 will be installed on %2.
" - "Warning: 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( "%3

" - "%1 will be installed on %2.
" - "Warning: 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(); -} diff --git a/src/modules/partition/gui/ReplaceWidget.h b/src/modules/partition/gui/ReplaceWidget.h deleted file mode 100644 index fbd19b429..000000000 --- a/src/modules/partition/gui/ReplaceWidget.h +++ /dev/null @@ -1,61 +0,0 @@ -/* === This file is part of Calamares - === - * - * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac - * SPDX-FileCopyrightText: 2014 Aurélien Gâteau - * SPDX-FileCopyrightText: 2018 Adriaan de Groot - * 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 -#include - -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 diff --git a/src/modules/partition/gui/ReplaceWidget.ui b/src/modules/partition/gui/ReplaceWidget.ui deleted file mode 100644 index 14f520f49..000000000 --- a/src/modules/partition/gui/ReplaceWidget.ui +++ /dev/null @@ -1,135 +0,0 @@ - - - -SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org> -SPDX-License-Identifier: GPL-3.0-or-later - - ReplaceWidget - - - - 0 - 0 - 643 - 187 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - QAbstractItemView::NoEditTriggers - - - false - - - true - - - false - - - false - - - - - - - - - - - - - - - Qt::AlignCenter - - - true - - - - - - - - 0 - 0 - - - - - - - - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - - - - - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - diff --git a/src/modules/partition/jobs/FillGlobalStorageJob.cpp b/src/modules/partition/jobs/FillGlobalStorageJob.cpp index d734485f0..f6ea8c772 100644 --- a/src/modules/partition/jobs/FillGlobalStorageJob.cpp +++ b/src/modules/partition/jobs/FillGlobalStorageJob.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -91,11 +92,18 @@ mapForPartition( Partition* partition, const QString& uuid ) map[ "parttype" ] = partition->type(); map[ "partattrs" ] = partition->attributes(); map[ "features" ] = partition->fileSystem().features(); + if ( partition->fileSystem().type() == FileSystem::Luks && dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() ) { map[ "fs" ] = untranslatedFS( dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() ); } + if ( partition->fileSystem().type() == FileSystem::Luks2 + && dynamic_cast< FS::luks2& >( partition->fileSystem() ).innerFS() ) + { + map[ "fs" ] = untranslatedFS( dynamic_cast< FS::luks2& >( partition->fileSystem() ).innerFS() ); + } + map[ "uuid" ] = uuid; map[ "claimed" ] = PartitionInfo::format( partition ); // If we formatted it, it's ours diff --git a/src/modules/partition/partition.conf b/src/modules/partition/partition.conf index d1c5ba255..ecd183ca1 100644 --- a/src/modules/partition/partition.conf +++ b/src/modules/partition/partition.conf @@ -64,6 +64,30 @@ userSwapChoices: # ensureSuspendToDisk: true # neverCreateSwap: false +# This setting specifies the LUKS generation (i.e LUKS1, LUKS2) used internally by +# cryptsetup when creating an encrypted partition. +# +# This option is set to luks1 by default, as grub doesn't support LUKS2 + Argon2id +# currently. On the other hand grub does support LUKS2 with PBKDF2 and could therefore be +# also set to luks2. Also there are some patches for grub and Argon2. +# See: https://aur.archlinux.org/packages/grub-improved-luks2-git +# +# Choices: luks1, luks2 (in addition, "luks" means "luks1") +# +# The default is luks1 +# +luksGeneration: luks1 + +# This setting determines if encryption should be allowed when using zfs. This +# setting has no effect unless zfs support is provided. +# +# This setting is to handle the fact that some bootloaders(such as grub) do not +# support zfs encryption. +# +# The default is true +# +# allowZfsEncryption: true + # Correctly draw nested (e.g. logical) partitions as such. drawNestedPartitions: false diff --git a/src/modules/partition/partition.schema.yaml b/src/modules/partition/partition.schema.yaml index 6c65e8ae7..dafdc5851 100644 --- a/src/modules/partition/partition.schema.yaml +++ b/src/modules/partition/partition.schema.yaml @@ -1,4 +1,5 @@ # SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-FileCopyrightText: 2023 Evan James # SPDX-License-Identifier: GPL-3.0-or-later --- $schema: https://json-schema.org/schema# @@ -14,13 +15,16 @@ properties: # ensureSuspendToDisk: { type: boolean, default: true } # Legacy # neverCreateSwap: { type: boolean, default: false } # Legacy + allowZfsEncryption: { type: boolean, default: true } drawNestedPartitions: { type: boolean, default: false } alwaysShowPartitionLabels: { type: boolean, default: true } defaultFileSystemType: { type: string } availableFileSystemTypes: { type: array, items: { type: string } } + luksGeneration: { type: string, enum: [luks1, luks2] } # Also allows "luks" as alias of "luks1" enableLuksAutomatedPartitioning: { type: boolean, default: false } + allowManualPartitioning: { type: boolean, default: true } partitionLayout: { type: array } # TODO: specify items initialPartitioningChoice: { type: string, enum: [ none, erase, replace, alongside, manual ] } diff --git a/src/modules/partition/tests/CreateLayoutsTests.cpp b/src/modules/partition/tests/CreateLayoutsTests.cpp index b83f71a26..db57f0e9d 100644 --- a/src/modules/partition/tests/CreateLayoutsTests.cpp +++ b/src/modules/partition/tests/CreateLayoutsTests.cpp @@ -63,8 +63,8 @@ CreateLayoutsTests::testFixedSizePartition() QFAIL( qPrintable( "Unable to create / partition" ) ); } - partitions - = layout.createPartitions( static_cast< Device* >( &dev ), 0, dev.totalLogical(), nullptr, nullptr, role ); + partitions = layout.createPartitions( + static_cast< Device* >( &dev ), 0, dev.totalLogical(), Config::LuksGeneration::Luks1, nullptr, nullptr, role ); QCOMPARE( partitions.count(), 1 ); @@ -84,8 +84,8 @@ CreateLayoutsTests::testPercentSizePartition() QFAIL( qPrintable( "Unable to create / partition" ) ); } - partitions - = layout.createPartitions( static_cast< Device* >( &dev ), 0, dev.totalLogical(), nullptr, nullptr, role ); + partitions = layout.createPartitions( + static_cast< Device* >( &dev ), 0, dev.totalLogical(), Config::LuksGeneration::Luks1, nullptr, nullptr, role ); QCOMPARE( partitions.count(), 1 ); @@ -115,8 +115,8 @@ CreateLayoutsTests::testMixedSizePartition() QFAIL( qPrintable( "Unable to create /bkup partition" ) ); } - partitions - = layout.createPartitions( static_cast< Device* >( &dev ), 0, dev.totalLogical(), nullptr, nullptr, role ); + partitions = layout.createPartitions( + static_cast< Device* >( &dev ), 0, dev.totalLogical(), Config::LuksGeneration::Luks1, nullptr, nullptr, role ); QCOMPARE( partitions.count(), 3 ); diff --git a/src/modules/services-systemd/services-systemd.conf b/src/modules/services-systemd/services-systemd.conf index aff5f5518..d13dbf5cd 100644 --- a/src/modules/services-systemd/services-systemd.conf +++ b/src/modules/services-systemd/services-systemd.conf @@ -37,7 +37,8 @@ # # - name: "cups.socket" # action: "disable" -# mandatory: false +# # The property "mandatory" is taken to be false by default here +# # because it is not specified # # - name: "graphical" # action: "enable" diff --git a/src/modules/umount/umount.conf b/src/modules/umount/umount.conf index 9fb922740..5842c8780 100644 --- a/src/modules/umount/umount.conf +++ b/src/modules/umount/umount.conf @@ -11,4 +11,4 @@ --- # Setting emergency to true will make it so this module is still run # when a prior module fails -emergency: false +emergency: true diff --git a/src/modules/zfs/ZfsJob.cpp b/src/modules/zfs/ZfsJob.cpp index 921645f0a..c840da846 100644 --- a/src/modules/zfs/ZfsJob.cpp +++ b/src/modules/zfs/ZfsJob.cpp @@ -241,6 +241,13 @@ ZfsJob::exec() } } + // Generate the zfs hostid file + auto i = system->runCommand( { "zgenhostid" }, std::chrono::seconds( 3 ) ); + if ( i.getExitCode() != 0 ) + { + cWarning() << "Failed to create /etc/hostid"; + } + // Create the zpool ZfsResult zfsResult; if ( encrypt ) diff --git a/src/modules/zfshostid/main.py b/src/modules/zfshostid/main.py new file mode 100644 index 000000000..702ba1ede --- /dev/null +++ b/src/modules/zfshostid/main.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2022 Anke Boersma +# 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 diff --git a/src/modules/zfshostid/module.desc b/src/modules/zfshostid/module.desc new file mode 100644 index 000000000..13fec35be --- /dev/null +++ b/src/modules/zfshostid/module.desc @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "zfshostid" +interface: "python" +script: "main.py" +noconfig: true diff --git a/src/modules/zfshostid/zfshostid.schema.yaml b/src/modules/zfshostid/zfshostid.schema.yaml new file mode 100644 index 000000000..24774d01a --- /dev/null +++ b/src/modules/zfshostid/zfshostid.schema.yaml @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Anke Boersma +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/zfshostid +additionalProperties: false +type: object