[merge] with upstream
This commit is contained in:
commit
448ebd639c
14
.tx/config
14
.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_<lang>.ts
|
||||
source_file = lang/calamares_en.ts
|
||||
source_lang = en
|
||||
type = QT
|
||||
type = QT
|
||||
|
||||
[calamares.fdo]
|
||||
[o:calamares:p:calamares:r:fdo]
|
||||
file_filter = lang/desktop_<lang>.desktop
|
||||
source_file = calamares.desktop
|
||||
source_lang = en
|
||||
type = DESKTOP
|
||||
type = DESKTOP
|
||||
|
||||
[calamares.python]
|
||||
[o:calamares:p:calamares:r:python]
|
||||
file_filter = lang/python/<lang>/LC_MESSAGES/python.po
|
||||
source_file = lang/python.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
type = PO
|
||||
|
||||
|
26
CHANGES-3.3
26
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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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( "" )
|
||||
|
@ -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) |
|
||||
|:--:|:--:|:--:|:--:|:--:|:--:|
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
27
ci/txpush.sh
27
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 "<!DOCTYPE TS>" ; cat - ; } | $XMLLINT --format --encode utf-8 -o "$TS_FILE".new - && mv "$TS_FILE".new "$TS_FILE"
|
||||
fi
|
||||
|
||||
tx push --source --no-interactive -r calamares.calamares
|
||||
tx push --source --no-interactive -r calamares.fdo
|
||||
tx push --source -r calamares.calamares || exit 1
|
||||
tx push --source -r calamares.fdo || exit 1
|
||||
|
||||
|
||||
### PYTHON MODULES
|
||||
@ -132,9 +145,8 @@ for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d | sort) ; d
|
||||
${PYGETTEXT} -p ${MODULE_DIR}/lang -d ${MODULE_NAME} -o ${MODULE_NAME}.pot ${MODULE_DIR}/*.py
|
||||
POTFILE="${MODULE_DIR}/lang/${MODULE_NAME}.pot"
|
||||
if [ -f "$POTFILE" ]; then
|
||||
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
|
||||
tx set -r calamares.${MODULE_NAME} --source -l en "$POTFILE"
|
||||
tx push --source --no-interactive -r calamares.${MODULE_NAME}
|
||||
reinplace '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
|
||||
tx push --source -r calamares.${MODULE_NAME}
|
||||
fi
|
||||
else
|
||||
SHARED_PYTHON="$SHARED_PYTHON $FILES"
|
||||
@ -145,9 +157,8 @@ done
|
||||
if test -n "$SHARED_PYTHON" ; then
|
||||
${PYGETTEXT} -p lang -d python -o python.pot $SHARED_PYTHON
|
||||
POTFILE="lang/python.pot"
|
||||
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
|
||||
tx set -r calamares.python --source -l en "$POTFILE"
|
||||
tx push --source --no-interactive -r calamares.python
|
||||
reinplace '/^"Content-Type/s/CHARSET/UTF-8/' "$POTFILE"
|
||||
tx push --source -r calamares.python
|
||||
fi
|
||||
|
||||
txtag
|
||||
|
@ -53,7 +53,7 @@ class TransifexGetter(object):
|
||||
parser = configparser.ConfigParser()
|
||||
parser.read_file(f)
|
||||
|
||||
return parser.get("https://www.transifex.com", "password")
|
||||
return parser.get("https://app.transifex.com", "password")
|
||||
except IOError as e:
|
||||
return None
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
593
lang/python.pot
593
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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-05-29 16:17+0200\n"
|
||||
"POT-Creation-Date: 2023-04-24 23:48+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: \n"
|
||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||
|
||||
#: src/modules/grubcfg/main.py:28
|
||||
msgid "Configure GRUB."
|
||||
msgstr "Configure GRUB."
|
||||
|
||||
#: src/modules/mount/main.py:42
|
||||
msgid "Mounting partitions."
|
||||
msgstr "Mounting partitions."
|
||||
|
||||
#: src/modules/mount/main.py:88 src/modules/mount/main.py:124
|
||||
msgid "Internal error mounting zfs datasets"
|
||||
msgstr "Internal error mounting zfs datasets"
|
||||
|
||||
#: src/modules/mount/main.py:100
|
||||
msgid "Failed to import zpool"
|
||||
msgstr "Failed to import zpool"
|
||||
|
||||
#: src/modules/mount/main.py:116
|
||||
msgid "Failed to unlock zpool"
|
||||
msgstr "Failed to unlock zpool"
|
||||
|
||||
#: src/modules/mount/main.py:133 src/modules/mount/main.py:138
|
||||
msgid "Failed to set zfs mountpoint"
|
||||
msgstr "Failed to set zfs mountpoint"
|
||||
|
||||
#: src/modules/mount/main.py:229 src/modules/initcpiocfg/main.py:235
|
||||
#: src/modules/initcpiocfg/main.py:239 src/modules/rawfs/main.py:164
|
||||
#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89
|
||||
#: src/modules/openrcdmcryptcfg/main.py:72
|
||||
#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/fstab/main.py:394
|
||||
#: src/modules/fstab/main.py:400 src/modules/fstab/main.py:428
|
||||
#: src/modules/localecfg/main.py:140 src/modules/networkcfg/main.py:105
|
||||
msgid "Configuration Error"
|
||||
msgstr "Configuration Error"
|
||||
|
||||
#: src/modules/mount/main.py:230 src/modules/initcpiocfg/main.py:236
|
||||
#: src/modules/rawfs/main.py:165 src/modules/initramfscfg/main.py:86
|
||||
#: src/modules/openrcdmcryptcfg/main.py:73 src/modules/fstab/main.py:395
|
||||
msgid "No partitions are defined for <pre>{!s}</pre> to use."
|
||||
msgstr "No partitions are defined for <pre>{!s}</pre> to use."
|
||||
|
||||
#: src/modules/mount/main.py:253
|
||||
msgid "zfs mounting error"
|
||||
msgstr "zfs mounting error"
|
||||
|
||||
#: src/modules/services-systemd/main.py:26
|
||||
msgid "Configure systemd services"
|
||||
msgstr "Configure systemd services"
|
||||
|
||||
#: src/modules/services-systemd/main.py:59
|
||||
#: src/modules/services-openrc/main.py:93
|
||||
msgid "Cannot modify service"
|
||||
msgstr "Cannot modify service"
|
||||
|
||||
#: src/modules/services-systemd/main.py:60
|
||||
msgid ""
|
||||
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
#: src/modules/bootloader/main.py:46
|
||||
msgid "Install bootloader."
|
||||
msgstr ""
|
||||
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
|
||||
#: src/modules/services-systemd/main.py:63
|
||||
#: src/modules/services-systemd/main.py:69
|
||||
msgid "Cannot enable systemd service <code>{name!s}</code>."
|
||||
msgstr "Cannot enable systemd service <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:65
|
||||
msgid "Cannot enable systemd target <code>{name!s}</code>."
|
||||
msgstr "Cannot enable systemd target <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
msgstr "Cannot disable systemd target <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:73
|
||||
msgid "Cannot mask systemd unit <code>{name!s}</code>."
|
||||
msgstr "Cannot mask systemd unit <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:75
|
||||
msgid ""
|
||||
"Unknown systemd commands <code>{command!s}</code> and "
|
||||
"<code>{suffix!s}</code> for unit {name!s}."
|
||||
#: src/modules/bootloader/main.py:640
|
||||
msgid "Failed to install grub, no partitions defined in global storage"
|
||||
msgstr ""
|
||||
"Unknown systemd commands <code>{command!s}</code> and "
|
||||
"<code>{suffix!s}</code> for unit {name!s}."
|
||||
|
||||
#: src/modules/unpackfs/main.py:34
|
||||
msgid "Filling up filesystems."
|
||||
msgstr "Filling up filesystems."
|
||||
|
||||
#: src/modules/unpackfs/main.py:254
|
||||
msgid "rsync failed with error code {}."
|
||||
msgstr "rsync failed with error code {}."
|
||||
|
||||
#: src/modules/unpackfs/main.py:299
|
||||
msgid "Unpacking image {}/{}, file {}/{}"
|
||||
msgstr "Unpacking image {}/{}, file {}/{}"
|
||||
|
||||
#: src/modules/unpackfs/main.py:314
|
||||
msgid "Starting to unpack {}"
|
||||
msgstr "Starting to unpack {}"
|
||||
|
||||
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:467
|
||||
msgid "Failed to unpack image \"{}\""
|
||||
msgstr "Failed to unpack image \"{}\""
|
||||
|
||||
#: src/modules/unpackfs/main.py:430
|
||||
msgid "No mount point for root partition"
|
||||
msgstr "No mount point for root partition"
|
||||
|
||||
#: src/modules/unpackfs/main.py:431
|
||||
msgid "globalstorage does not contain a \"rootMountPoint\" key."
|
||||
msgstr "globalstorage does not contain a \"rootMountPoint\" key."
|
||||
|
||||
#: src/modules/unpackfs/main.py:434
|
||||
msgid "Bad mount point for root partition"
|
||||
msgstr "Bad mount point for root partition"
|
||||
|
||||
#: src/modules/unpackfs/main.py:435
|
||||
msgid "rootMountPoint is \"{}\", which does not exist."
|
||||
msgstr "rootMountPoint is \"{}\", which does not exist."
|
||||
|
||||
#: src/modules/unpackfs/main.py:439 src/modules/unpackfs/main.py:455
|
||||
#: src/modules/unpackfs/main.py:459 src/modules/unpackfs/main.py:465
|
||||
#: src/modules/unpackfs/main.py:480
|
||||
msgid "Bad unpackfs configuration"
|
||||
msgstr "Bad unpackfs configuration"
|
||||
|
||||
#: src/modules/unpackfs/main.py:440
|
||||
msgid "There is no configuration information."
|
||||
msgstr "There is no configuration information."
|
||||
|
||||
#: src/modules/unpackfs/main.py:456
|
||||
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
||||
msgstr "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
||||
|
||||
#: src/modules/unpackfs/main.py:460
|
||||
msgid "The source filesystem \"{}\" does not exist"
|
||||
msgstr "The source filesystem \"{}\" does not exist"
|
||||
|
||||
#: src/modules/unpackfs/main.py:466
|
||||
msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
#: src/modules/bootloader/main.py:895
|
||||
msgid "Bootloader installation error"
|
||||
msgstr ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
|
||||
#: src/modules/unpackfs/main.py:481
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
msgstr "The destination \"{}\" in the target system is not a directory"
|
||||
#: src/modules/bootloader/main.py:896
|
||||
msgid ""
|
||||
"The bootloader could not be installed. The installation command <pre>{!s}</"
|
||||
"pre> returned error code {!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:524
|
||||
msgid "Cannot write KDM configuration file"
|
||||
msgstr "Cannot write KDM configuration file"
|
||||
|
||||
#: src/modules/displaymanager/main.py:525
|
||||
msgid "KDM config file {!s} does not exist"
|
||||
msgstr "KDM config file {!s} does not exist"
|
||||
|
||||
#: src/modules/displaymanager/main.py:586
|
||||
#: src/modules/displaymanager/main.py:507
|
||||
msgid "Cannot write LXDM configuration file"
|
||||
msgstr "Cannot write LXDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:587
|
||||
#: src/modules/displaymanager/main.py:508
|
||||
msgid "LXDM config file {!s} does not exist"
|
||||
msgstr "LXDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:670
|
||||
#: src/modules/displaymanager/main.py:596
|
||||
msgid "Cannot write LightDM configuration file"
|
||||
msgstr "Cannot write LightDM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:671
|
||||
#: src/modules/displaymanager/main.py:597
|
||||
msgid "LightDM config file {!s} does not exist"
|
||||
msgstr "LightDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:745
|
||||
#: src/modules/displaymanager/main.py:682
|
||||
msgid "Cannot configure LightDM"
|
||||
msgstr "Cannot configure LightDM"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:746
|
||||
#: src/modules/displaymanager/main.py:683
|
||||
msgid "No LightDM greeter installed."
|
||||
msgstr "No LightDM greeter installed."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:777
|
||||
#: src/modules/displaymanager/main.py:714
|
||||
msgid "Cannot write SLIM configuration file"
|
||||
msgstr "Cannot write SLIM configuration file"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:778
|
||||
#: src/modules/displaymanager/main.py:715
|
||||
msgid "SLIM config file {!s} does not exist"
|
||||
msgstr "SLIM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:992
|
||||
#: src/modules/displaymanager/main.py:933
|
||||
msgid "No display managers selected for the displaymanager module."
|
||||
msgstr "No display managers selected for the displaymanager module."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/displaymanager/main.py:993
|
||||
#: src/modules/displaymanager/main.py:934
|
||||
msgid ""
|
||||
"The displaymanagers list is empty or undefined in both globalstorage and "
|
||||
"displaymanager.conf."
|
||||
msgstr ""
|
||||
"The displaymanagers list is empty or undefined in both globalstorage and "
|
||||
"displaymanager.conf."
|
||||
|
||||
#: src/modules/displaymanager/main.py:1075
|
||||
#: src/modules/displaymanager/main.py:1021
|
||||
msgid "Display manager configuration was incomplete"
|
||||
msgstr "Display manager configuration was incomplete"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:28
|
||||
msgid "Configuring mkinitcpio."
|
||||
msgstr "Configuring mkinitcpio."
|
||||
#: src/modules/dracut/main.py:29
|
||||
msgid "Creating initramfs with dracut."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:240 src/modules/initramfscfg/main.py:90
|
||||
#: src/modules/openrcdmcryptcfg/main.py:77 src/modules/fstab/main.py:401
|
||||
#: src/modules/dracut/main.py:63
|
||||
msgid "Failed to run dracut"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:64
|
||||
#, python-brace-format
|
||||
msgid "Dracut failed to run on the target with return code: {return_code}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:35
|
||||
msgid "Dummy python job."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
|
||||
#: src/modules/dummypython/main.py:94
|
||||
msgid "Dummy python step {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:28
|
||||
msgid "Writing fstab."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:377 src/modules/fstab/main.py:383
|
||||
#: src/modules/fstab/main.py:411 src/modules/initcpiocfg/main.py:245
|
||||
#: src/modules/initcpiocfg/main.py:249 src/modules/initramfscfg/main.py:85
|
||||
#: src/modules/initramfscfg/main.py:89 src/modules/localecfg/main.py:140
|
||||
#: src/modules/mount/main.py:329 src/modules/networkcfg/main.py:105
|
||||
#: src/modules/openrcdmcryptcfg/main.py:72
|
||||
#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/rawfs/main.py:164
|
||||
msgid "Configuration Error"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:378 src/modules/initramfscfg/main.py:86
|
||||
#: src/modules/mount/main.py:330 src/modules/openrcdmcryptcfg/main.py:73
|
||||
#: src/modules/rawfs/main.py:165
|
||||
msgid "No partitions are defined for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:384 src/modules/initramfscfg/main.py:90
|
||||
#: src/modules/localecfg/main.py:141 src/modules/networkcfg/main.py:106
|
||||
#: src/modules/openrcdmcryptcfg/main.py:77
|
||||
msgid "No root mount point is given for <pre>{!s}</pre> to use."
|
||||
msgstr "No root mount point is given for <pre>{!s}</pre> to use."
|
||||
|
||||
#: src/modules/rawfs/main.py:26
|
||||
msgid "Installing data."
|
||||
msgstr "Installing data."
|
||||
|
||||
#: src/modules/services-openrc/main.py:29
|
||||
msgid "Configure OpenRC services"
|
||||
msgstr "Configure OpenRC services"
|
||||
|
||||
#: src/modules/services-openrc/main.py:57
|
||||
msgid "Cannot add service {name!s} to run-level {level!s}."
|
||||
msgstr "Cannot add service {name!s} to run-level {level!s}."
|
||||
|
||||
#: src/modules/services-openrc/main.py:59
|
||||
msgid "Cannot remove service {name!s} from run-level {level!s}."
|
||||
msgstr "Cannot remove service {name!s} from run-level {level!s}."
|
||||
|
||||
#: src/modules/services-openrc/main.py:61
|
||||
msgid ""
|
||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||
"level {level!s}."
|
||||
msgstr ""
|
||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||
"level {level!s}."
|
||||
|
||||
#: src/modules/services-openrc/main.py:94
|
||||
msgid ""
|
||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
#: src/modules/fstab/main.py:412
|
||||
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
|
||||
#: src/modules/services-openrc/main.py:101
|
||||
msgid "Target runlevel does not exist"
|
||||
msgstr "Target runlevel does not exist"
|
||||
|
||||
#: src/modules/services-openrc/main.py:102
|
||||
msgid ""
|
||||
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
#: src/modules/grubcfg/main.py:29
|
||||
msgid "Configure GRUB."
|
||||
msgstr ""
|
||||
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
|
||||
#: src/modules/services-openrc/main.py:110
|
||||
msgid "Target service does not exist"
|
||||
msgstr "Target service does not exist"
|
||||
|
||||
#: src/modules/services-openrc/main.py:111
|
||||
msgid ""
|
||||
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
#: src/modules/hwclock/main.py:26
|
||||
msgid "Setting hardware clock."
|
||||
msgstr ""
|
||||
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
|
||||
#: src/modules/plymouthcfg/main.py:27
|
||||
msgid "Configure Plymouth theme"
|
||||
msgstr "Configure Plymouth theme"
|
||||
#: src/modules/initcpiocfg/main.py:27
|
||||
msgid "Configuring mkinitcpio."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:246
|
||||
msgid "No partitions are defined for <pre>initcpiocfg</pre>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:250
|
||||
msgid "No root mount point for <pre>initcpiocfg</pre>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initramfscfg/main.py:32
|
||||
msgid "Configuring initramfs."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/localecfg/main.py:31
|
||||
msgid "Configuring locales."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mkinitfs/main.py:27
|
||||
msgid "Creating initramfs with mkinitfs."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mkinitfs/main.py:49
|
||||
msgid "Failed to run mkinitfs on the target"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mkinitfs/main.py:50
|
||||
msgid "The exit code was {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:43
|
||||
msgid "Mounting partitions."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:164 src/modules/mount/main.py:200
|
||||
msgid "Internal error mounting zfs datasets"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:176
|
||||
msgid "Failed to import zpool"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:192
|
||||
msgid "Failed to unlock zpool"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:209 src/modules/mount/main.py:214
|
||||
msgid "Failed to set zfs mountpoint"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mount/main.py:365
|
||||
msgid "zfs mounting error"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/networkcfg/main.py:29
|
||||
msgid "Saving network configuration."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/openrcdmcryptcfg/main.py:26
|
||||
msgid "Configuring OpenRC dmcrypt service."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:54 src/modules/packages/main.py:65
|
||||
#: src/modules/packages/main.py:75
|
||||
msgid "Install packages."
|
||||
msgstr "Install packages."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:63
|
||||
#, python-format
|
||||
msgid "Processing packages (%(count)d / %(total)d)"
|
||||
msgstr "Processing packages (%(count)d / %(total)d)"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:68
|
||||
#, python-format
|
||||
msgid "Installing one package."
|
||||
msgid_plural "Installing %(num)d packages."
|
||||
msgstr[0] "Installing one package."
|
||||
msgstr[1] "Installing %(num)d packages."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/packages/main.py:71
|
||||
#, python-format
|
||||
msgid "Removing one package."
|
||||
msgid_plural "Removing %(num)d packages."
|
||||
msgstr[0] "Removing one package."
|
||||
msgstr[1] "Removing %(num)d packages."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/modules/packages/main.py:725 src/modules/packages/main.py:737
|
||||
#: src/modules/packages/main.py:765
|
||||
msgid "Package Manager error"
|
||||
msgstr "Package Manager error"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/packages/main.py:726
|
||||
msgid ""
|
||||
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
|
||||
"returned error code {!s}."
|
||||
msgstr ""
|
||||
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
|
||||
"returned error code {!s}."
|
||||
|
||||
#: src/modules/packages/main.py:738
|
||||
msgid ""
|
||||
"The package manager could not update the system. The command <pre>{!s}</pre>"
|
||||
" returned error code {!s}."
|
||||
"The package manager could not update the system. The command <pre>{!s}</pre> "
|
||||
"returned error code {!s}."
|
||||
msgstr ""
|
||||
"The package manager could not update the system. The command <pre>{!s}</pre>"
|
||||
" returned error code {!s}."
|
||||
|
||||
#: src/modules/packages/main.py:766
|
||||
msgid ""
|
||||
"The package manager could not make changes to the installed system. The "
|
||||
"command <pre>{!s}</pre> returned error code {!s}."
|
||||
msgstr ""
|
||||
"The package manager could not make changes to the installed system. The "
|
||||
"command <pre>{!s}</pre> returned error code {!s}."
|
||||
|
||||
#: src/modules/bootloader/main.py:43
|
||||
msgid "Install bootloader."
|
||||
msgstr "Install bootloader."
|
||||
|
||||
#: src/modules/bootloader/main.py:614
|
||||
msgid "Failed to install grub, no partitions defined in global storage"
|
||||
msgstr "Failed to install grub, no partitions defined in global storage"
|
||||
|
||||
#: src/modules/bootloader/main.py:782
|
||||
msgid "Bootloader installation error"
|
||||
msgstr "Bootloader installation error"
|
||||
|
||||
#: src/modules/bootloader/main.py:783
|
||||
msgid ""
|
||||
"The bootloader could not be installed. The installation command "
|
||||
"<pre>{!s}</pre> returned error code {!s}."
|
||||
#: src/modules/plymouthcfg/main.py:27
|
||||
msgid "Configure Plymouth theme"
|
||||
msgstr ""
|
||||
"The bootloader could not be installed. The installation command "
|
||||
"<pre>{!s}</pre> returned error code {!s}."
|
||||
|
||||
#: src/modules/hwclock/main.py:26
|
||||
msgid "Setting hardware clock."
|
||||
msgstr "Setting hardware clock."
|
||||
#: src/modules/rawfs/main.py:26
|
||||
msgid "Installing data."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mkinitfs/main.py:27
|
||||
msgid "Creating initramfs with mkinitfs."
|
||||
msgstr "Creating initramfs with mkinitfs."
|
||||
#: src/modules/services-openrc/main.py:29
|
||||
msgid "Configure OpenRC services"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mkinitfs/main.py:49
|
||||
msgid "Failed to run mkinitfs on the target"
|
||||
msgstr "Failed to run mkinitfs on the target"
|
||||
#: src/modules/services-openrc/main.py:57
|
||||
msgid "Cannot add service {name!s} to run-level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/mkinitfs/main.py:50 src/modules/dracut/main.py:50
|
||||
msgid "The exit code was {}"
|
||||
msgstr "The exit code was {}"
|
||||
#: src/modules/services-openrc/main.py:59
|
||||
msgid "Cannot remove service {name!s} from run-level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:27
|
||||
msgid "Creating initramfs with dracut."
|
||||
msgstr "Creating initramfs with dracut."
|
||||
#: src/modules/services-openrc/main.py:61
|
||||
msgid ""
|
||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||
"level {level!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dracut/main.py:49
|
||||
msgid "Failed to run dracut on the target"
|
||||
msgstr "Failed to run dracut on the target"
|
||||
#: src/modules/services-openrc/main.py:93
|
||||
msgid "Cannot modify service"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/initramfscfg/main.py:32
|
||||
msgid "Configuring initramfs."
|
||||
msgstr "Configuring initramfs."
|
||||
#: src/modules/services-openrc/main.py:94
|
||||
msgid ""
|
||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/openrcdmcryptcfg/main.py:26
|
||||
msgid "Configuring OpenRC dmcrypt service."
|
||||
msgstr "Configuring OpenRC dmcrypt service."
|
||||
#: src/modules/services-openrc/main.py:101
|
||||
msgid "Target runlevel does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:28
|
||||
msgid "Writing fstab."
|
||||
msgstr "Writing fstab."
|
||||
#: src/modules/services-openrc/main.py:102
|
||||
msgid ""
|
||||
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/fstab/main.py:429
|
||||
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
||||
msgstr "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
||||
#: src/modules/services-openrc/main.py:110
|
||||
msgid "Target service does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:35
|
||||
msgid "Dummy python job."
|
||||
msgstr "Dummy python job."
|
||||
#: src/modules/services-openrc/main.py:111
|
||||
msgid ""
|
||||
"The path for service {name!s} is <code>{path!s}</code>, which does not exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
|
||||
#: src/modules/dummypython/main.py:94
|
||||
msgid "Dummy python step {}"
|
||||
msgstr "Dummy python step {}"
|
||||
#: src/modules/services-systemd/main.py:26
|
||||
msgid "Configure systemd units"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/localecfg/main.py:31
|
||||
msgid "Configuring locales."
|
||||
msgstr "Configuring locales."
|
||||
#: src/modules/services-systemd/main.py:64
|
||||
msgid "Cannot modify unit"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/networkcfg/main.py:29
|
||||
msgid "Saving network configuration."
|
||||
msgstr "Saving network configuration."
|
||||
#: src/modules/services-systemd/main.py:65
|
||||
msgid ""
|
||||
"<code>systemctl {_action!s}</code> call in chroot returned error code "
|
||||
"{_exit_code!s}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/services-systemd/main.py:66
|
||||
msgid "Cannot {_action!s} systemd unit <code>{_name!s}</code>."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:34
|
||||
msgid "Filling up filesystems."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:254
|
||||
msgid "rsync failed with error code {}."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:299
|
||||
msgid "Unpacking image {}/{}, file {}/{}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:314
|
||||
msgid "Starting to unpack {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:467
|
||||
msgid "Failed to unpack image \"{}\""
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:430
|
||||
msgid "No mount point for root partition"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:431
|
||||
msgid "globalstorage does not contain a \"rootMountPoint\" key."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:434
|
||||
msgid "Bad mount point for root partition"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:435
|
||||
msgid "rootMountPoint is \"{}\", which does not exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:439 src/modules/unpackfs/main.py:455
|
||||
#: src/modules/unpackfs/main.py:459 src/modules/unpackfs/main.py:465
|
||||
#: src/modules/unpackfs/main.py:480
|
||||
msgid "Bad unpackfs configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:440
|
||||
msgid "There is no configuration information."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:456
|
||||
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:460
|
||||
msgid "The source filesystem \"{}\" does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:466
|
||||
msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/unpackfs/main.py:481
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
msgstr ""
|
||||
|
||||
#: src/modules/zfshostid/main.py:27
|
||||
msgid "Copying zfs generated hostid."
|
||||
msgstr ""
|
||||
|
@ -132,9 +132,11 @@ sequence:
|
||||
- locale
|
||||
- keyboard
|
||||
- localecfg
|
||||
- luksopenswaphookcfg
|
||||
- luksbootkeyfile
|
||||
- luksopenswaphookcfg
|
||||
# - dracutlukscfg
|
||||
- plymouthcfg
|
||||
# - zfshostid
|
||||
- initcpiocfg
|
||||
- initcpio
|
||||
- users
|
||||
|
@ -19,7 +19,7 @@ static const char s_header[]
|
||||
static const char s_footer[]
|
||||
= QT_TRANSLATE_NOOP( "AboutData",
|
||||
"Thanks to <a href=\"https://calamares.io/team/\">the Calamares team</a> "
|
||||
"and the <a href=\"https://www.transifex.com/calamares/calamares/\">Calamares "
|
||||
"and the <a href=\"https://app.transifex.com/calamares/calamares/\">Calamares "
|
||||
"translators team</a>.<br/><br/>"
|
||||
"<a href=\"https://calamares.io/\">Calamares</a> "
|
||||
"development is sponsored by <br/>"
|
||||
|
@ -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:
|
||||
|
@ -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 );
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -20,7 +20,9 @@
|
||||
# Calamares is Free Software: see the License-Identifier above.
|
||||
#
|
||||
|
||||
import fileinput
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
@ -28,8 +30,8 @@ import libcalamares
|
||||
|
||||
from libcalamares.utils import check_target_env_call
|
||||
|
||||
|
||||
import gettext
|
||||
|
||||
_ = gettext.translation("calamares-python",
|
||||
localedir=libcalamares.utils.gettext_path(),
|
||||
languages=libcalamares.utils.gettext_languages(),
|
||||
@ -39,6 +41,7 @@ _ = gettext.translation("calamares-python",
|
||||
# to make identifiers (or to clean up names to make filenames).
|
||||
file_name_sanitizer = str.maketrans(" /()", "_-__")
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("Install bootloader.")
|
||||
|
||||
@ -59,20 +62,6 @@ def get_uuid():
|
||||
return ""
|
||||
|
||||
|
||||
def get_bootloader_entry_name():
|
||||
"""
|
||||
Passes 'bootloader_entry_name' to other routine based
|
||||
on configuration file.
|
||||
|
||||
:return:
|
||||
"""
|
||||
if "bootloaderEntryName" in libcalamares.job.configuration:
|
||||
return libcalamares.job.configuration["bootloaderEntryName"]
|
||||
else:
|
||||
branding = libcalamares.globalstorage.value("branding")
|
||||
return branding["bootloaderEntryName"]
|
||||
|
||||
|
||||
def get_kernel_line(kernel_type):
|
||||
"""
|
||||
Passes 'kernel_line' to other routine based on configuration file.
|
||||
@ -136,49 +125,43 @@ def is_zfs_root(partition):
|
||||
return partition["mountPoint"] == "/" and partition["fs"] == "zfs"
|
||||
|
||||
|
||||
def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kernel, kernel_type, kernel_version):
|
||||
"""
|
||||
Creates systemd-boot configuration files based on given parameters.
|
||||
|
||||
:param installation_root_path: A string containing the absolute path to the root of the installation
|
||||
:param efi_dir: A string containing the path to the efi dir relative to the root of the installation
|
||||
:param uuid: A string containing the UUID of the root volume
|
||||
:param entry: A string containing the name of the entry as it will be displayed on boot
|
||||
:param kernel: A string containing the path to the kernel relative to the root of the installation
|
||||
:param kernel_type: A string which should be set if there is a special version of the entry, for example "fallback"
|
||||
:param kernel_version: The kernel version string
|
||||
"""
|
||||
kernel_params = ["quiet"]
|
||||
def get_kernel_params(uuid):
|
||||
kernel_params = libcalamares.job.configuration.get("kernelParams", ["quiet"])
|
||||
kernel_params.append("rw")
|
||||
|
||||
partitions = libcalamares.globalstorage.value("partitions")
|
||||
swap_uuid = ""
|
||||
swap_outer_mappername = None
|
||||
swap_outer_uuid = None
|
||||
|
||||
cryptdevice_params = []
|
||||
|
||||
have_dracut = libcalamares.utils.target_env_call(["sh", "-c", "which dracut"]) == 0
|
||||
|
||||
# Take over swap settings:
|
||||
# - unencrypted swap partition sets swap_uuid
|
||||
# - encrypted root sets cryptdevice_params
|
||||
for partition in partitions:
|
||||
if partition["fs"] == "linuxswap" and not partition.get("claimed", None):
|
||||
# Skip foreign swap
|
||||
continue
|
||||
has_luks = "luksMapperName" in partition
|
||||
if partition["fs"] == "linuxswap" and not has_luks:
|
||||
swap_uuid = partition["uuid"]
|
||||
|
||||
if (partition["fs"] == "linuxswap" and has_luks):
|
||||
if partition["fs"] == "linuxswap" and has_luks:
|
||||
swap_outer_mappername = partition["luksMapperName"]
|
||||
swap_outer_uuid = partition["luksUuid"]
|
||||
|
||||
if partition["mountPoint"] == "/" and has_luks:
|
||||
cryptdevice_params = ["cryptdevice=UUID="
|
||||
+ partition["luksUuid"]
|
||||
+ ":"
|
||||
+ partition["luksMapperName"],
|
||||
"root=/dev/mapper/"
|
||||
+ partition["luksMapperName"]]
|
||||
if have_dracut:
|
||||
cryptdevice_params = [f"rd.luks.uuid={partition['luksUuid']}"]
|
||||
else:
|
||||
cryptdevice_params = [f"cryptdevice=UUID={partition['luksUuid']}:{partition['luksMapperName']}"]
|
||||
cryptdevice_params.append(f"root=/dev/mapper/{partition['luksMapperName']}")
|
||||
|
||||
# btrfs and zfs handling
|
||||
for partition in partitions:
|
||||
# systemd-boot with a BTRFS root filesystem needs to be told abouut the root subvolume.
|
||||
# If a btrfs root subvolume wasn't set, it means the root is directly on the partition
|
||||
# and this option isn't needed
|
||||
if is_btrfs_root(partition):
|
||||
@ -190,7 +173,7 @@ def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kerne
|
||||
if is_zfs_root(partition):
|
||||
zfs_root_path = get_zfs_root()
|
||||
if zfs_root_path is not None:
|
||||
kernel_params.append("zfs=" + zfs_root_path)
|
||||
kernel_params.append("root=ZFS=" + zfs_root_path)
|
||||
else:
|
||||
# Something is really broken if we get to this point
|
||||
libcalamares.utils.warning("Internal error handling zfs dataset")
|
||||
@ -204,93 +187,86 @@ def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kerne
|
||||
if swap_uuid:
|
||||
kernel_params.append("resume=UUID={!s}".format(swap_uuid))
|
||||
|
||||
if have_dracut and swap_outer_uuid:
|
||||
kernel_params.append(f"rd.luks.uuid={swap_outer_uuid}")
|
||||
|
||||
if swap_outer_mappername:
|
||||
kernel_params.append("resume=/dev/mapper/{!s}".format(
|
||||
swap_outer_mappername))
|
||||
kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}")
|
||||
|
||||
libcalamares.utils.debug("Configure: \"{!s}\"".format(f"{entry} {kernel_version}"))
|
||||
return kernel_params
|
||||
|
||||
if kernel_type == "fallback":
|
||||
version_string = kernel_version + "-fallback"
|
||||
initrd = "initrd-fallback"
|
||||
else:
|
||||
version_string = kernel_version
|
||||
initrd = "initrd"
|
||||
|
||||
def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, kernel, kernel_version):
|
||||
"""
|
||||
Creates systemd-boot configuration files based on given parameters.
|
||||
|
||||
:param installation_root_path: A string containing the absolute path to the root of the installation
|
||||
:param efi_dir: A string containing the path to the efi dir relative to the root of the installation
|
||||
:param uuid: A string containing the UUID of the root volume
|
||||
:param kernel: A string containing the path to the kernel relative to the root of the installation
|
||||
:param kernel_version: The kernel version string
|
||||
"""
|
||||
|
||||
# Get the kernel params and write them to /etc/kernel/cmdline
|
||||
# This file is used by kernel-install
|
||||
kernel_params = " ".join(get_kernel_params(uuid))
|
||||
kernel_cmdline_path = os.path.join(installation_root_path, "etc", "kernel")
|
||||
os.makedirs(kernel_cmdline_path, exist_ok=True)
|
||||
with open(os.path.join(kernel_cmdline_path, "cmdline"), "w") as cmdline_file:
|
||||
cmdline_file.write(kernel_params)
|
||||
|
||||
libcalamares.utils.debug(f"Configuring kernel version {kernel_version}")
|
||||
|
||||
# get the machine-id
|
||||
with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file:
|
||||
machine_id = machineid_file.read().rstrip('\n')
|
||||
|
||||
# Copy kernel to a subdirectory of /efi partition
|
||||
# Ensure the directory exists
|
||||
machine_dir = os.path.join(installation_root_path + efi_dir, machine_id)
|
||||
os.makedirs(machine_dir, exist_ok=True)
|
||||
|
||||
target_efi_files_dir = os.path.join(machine_dir, kernel_version)
|
||||
os.makedirs(target_efi_files_dir, exist_ok=True)
|
||||
|
||||
kernel_path = os.path.join(installation_root_path, kernel)
|
||||
kernel_name = os.path.basename(kernel_path)
|
||||
shutil.copyfile(kernel_path, os.path.join(target_efi_files_dir, "linux"))
|
||||
|
||||
# write the entry
|
||||
lines = [
|
||||
'## Generated by Calamares\n',
|
||||
'\n',
|
||||
"title {!s}\n".format(entry),
|
||||
"version {!s}\n".format(version_string),
|
||||
"machine-id {!s}\n".format(machine_id),
|
||||
"linux {!s}\n".format(os.path.join("/", machine_id, kernel_version, "linux")),
|
||||
]
|
||||
|
||||
try:
|
||||
additional_initrd_files = libcalamares.job.configuration["additionalInitrdFiles"]
|
||||
for initrd_file in additional_initrd_files:
|
||||
libcalamares.utils.debug("Attempting to handle initrd image " + initrd_file)
|
||||
if os.path.isfile(os.path.join(installation_root_path, initrd_file.lstrip('/'))):
|
||||
libcalamares.utils.debug("Found image " + initrd_file)
|
||||
shutil.copyfile(os.path.join(installation_root_path, initrd_file.lstrip('/')), os.path.join(target_efi_files_dir, os.path.basename(initrd_file)))
|
||||
lines.append("initrd {!s}\n".format(os.path.join("/", machine_id, kernel_version, os.path.basename(initrd_file))))
|
||||
except KeyError: # If the configuration option isn't set, we can just move on
|
||||
libcalamares.utils.debug("Failed to find key additionalInitrdFiles")
|
||||
pass
|
||||
|
||||
lines.append("initrd {!s}\n".format(os.path.join("/", machine_id, kernel_version, initrd)))
|
||||
lines.append("options {!s} rw\n".format(" ".join(kernel_params)))
|
||||
|
||||
conf_path = os.path.join(installation_root_path + efi_dir,
|
||||
"loader",
|
||||
"entries",
|
||||
machine_id + "-" + version_string + ".conf")
|
||||
|
||||
with open(conf_path, 'w') as conf_file:
|
||||
for line in lines:
|
||||
conf_file.write(line)
|
||||
# Call kernel-install for each kernel
|
||||
libcalamares.utils.target_env_process_output(["kernel-install",
|
||||
"add",
|
||||
kernel_version,
|
||||
os.path.join("/", kernel)])
|
||||
|
||||
|
||||
def create_loader(loader_path, entry):
|
||||
def create_loader(loader_path, installation_root_path):
|
||||
"""
|
||||
Writes configuration for loader.
|
||||
|
||||
:param loader_path:
|
||||
:param entry:
|
||||
:param loader_path: The absolute path to the loader.conf file
|
||||
:param installation_root_path: The path to the root of the target installation
|
||||
"""
|
||||
timeout = libcalamares.job.configuration["timeout"]
|
||||
lines = [
|
||||
"timeout {!s}\n".format(timeout),
|
||||
"default {!s}\n".format(entry),
|
||||
]
|
||||
|
||||
# get the machine-id
|
||||
with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file:
|
||||
machine_id = machineid_file.read().rstrip('\n')
|
||||
|
||||
try:
|
||||
loader_entries = libcalamares.job.configuration["loaderEntries"]
|
||||
except KeyError:
|
||||
libcalamares.utils.debug("No aditional loader entries found in config")
|
||||
loader_entries = []
|
||||
pass
|
||||
|
||||
lines = [f"default {machine_id}*"]
|
||||
|
||||
lines.extend(loader_entries)
|
||||
|
||||
with open(loader_path, 'w') as loader_file:
|
||||
for line in lines:
|
||||
loader_file.write(line)
|
||||
loader_file.write(line + "\n")
|
||||
|
||||
|
||||
class suffix_iterator(object):
|
||||
class SuffixIterator(object):
|
||||
"""
|
||||
Wrapper for one of the "generator" classes below to behave like
|
||||
a proper Python iterator. The iterator is initialized with a
|
||||
maximum number of attempts to generate a new suffix.
|
||||
"""
|
||||
|
||||
def __init__(self, attempts, generator):
|
||||
self.generator = generator
|
||||
self.attempts = attempts
|
||||
@ -310,6 +286,7 @@ class serialEfi(object):
|
||||
"""
|
||||
EFI Id generator that appends a serial number to the given name.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
# So the first call to next() will bump it to 0
|
||||
@ -354,6 +331,7 @@ class randomEfi(object):
|
||||
"""
|
||||
EFI Id generator that appends a random 4-digit hex number to the given name.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
# So the first call to next() will bump it to 0
|
||||
@ -427,7 +405,7 @@ def change_efi_suffix(efi_directory, bootloader_id):
|
||||
"""
|
||||
if bootloader_id.endswith("}") and "${" in bootloader_id:
|
||||
# Do 10 attempts with any suffix generator
|
||||
g = suffix_iterator(10, get_efi_suffix_generator(bootloader_id))
|
||||
g = SuffixIterator(10, get_efi_suffix_generator(bootloader_id))
|
||||
else:
|
||||
# Just one attempt
|
||||
g = [bootloader_id]
|
||||
@ -444,7 +422,7 @@ def efi_label(efi_directory):
|
||||
used within @p efi_directory.
|
||||
"""
|
||||
if "efiBootloaderId" in libcalamares.job.configuration:
|
||||
efi_bootloader_id = change_efi_suffix( efi_directory, libcalamares.job.configuration["efiBootloaderId"] )
|
||||
efi_bootloader_id = change_efi_suffix(efi_directory, libcalamares.job.configuration["efiBootloaderId"])
|
||||
else:
|
||||
branding = libcalamares.globalstorage.value("branding")
|
||||
efi_bootloader_id = branding["bootloaderEntryName"]
|
||||
@ -482,6 +460,7 @@ def efi_boot_next():
|
||||
if boot_entry:
|
||||
subprocess.call([boot_mgr, "-n", boot_entry])
|
||||
|
||||
|
||||
def get_kernels(installation_root_path):
|
||||
"""
|
||||
Gets a list of kernels and associated values for each kernel. This will work as is for many distros.
|
||||
@ -493,20 +472,32 @@ def get_kernels(installation_root_path):
|
||||
|
||||
Each 3-tuple contains the kernel, kernel_type and kernel_version
|
||||
"""
|
||||
kernel_search_path = libcalamares.job.configuration["kernelSearchPath"]
|
||||
source_kernel_name = libcalamares.job.configuration["kernelName"]
|
||||
try:
|
||||
kernel_search_path = libcalamares.job.configuration["kernelSearchPath"]
|
||||
except KeyError:
|
||||
libcalamares.utils.warning("No kernel pattern found in configuration, using '/usr/lib/modules'")
|
||||
kernel_search_path = "/usr/lib/modules"
|
||||
pass
|
||||
|
||||
kernel_list = []
|
||||
|
||||
# find all the installed kernels and generate default and fallback entries for each
|
||||
try:
|
||||
kernel_pattern = libcalamares.job.configuration["kernelPattern"]
|
||||
except KeyError:
|
||||
libcalamares.utils.warning("No kernel pattern found in configuration, using 'vmlinuz'")
|
||||
kernel_pattern = "vmlinuz"
|
||||
pass
|
||||
|
||||
# find all the installed kernels
|
||||
for root, dirs, files in os.walk(os.path.join(installation_root_path, kernel_search_path.lstrip('/'))):
|
||||
for file in files:
|
||||
if file == source_kernel_name:
|
||||
if re.search(kernel_pattern, file):
|
||||
rel_root = os.path.relpath(root, installation_root_path)
|
||||
kernel_list.append((os.path.join(rel_root, file),"default",os.path.basename(root)))
|
||||
kernel_list.append((os.path.join(rel_root, file),"fallback",os.path.basename(root)))
|
||||
kernel_list.append((os.path.join(rel_root, file), "default", os.path.basename(root)))
|
||||
|
||||
return kernel_list
|
||||
|
||||
|
||||
def install_systemd_boot(efi_directory):
|
||||
"""
|
||||
Installs systemd-boot as bootloader for EFI setups.
|
||||
@ -517,8 +508,6 @@ def install_systemd_boot(efi_directory):
|
||||
installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
|
||||
install_efi_directory = installation_root_path + efi_directory
|
||||
uuid = get_uuid()
|
||||
distribution = get_bootloader_entry_name()
|
||||
distribution_translated = distribution.translate(file_name_sanitizer)
|
||||
loader_path = os.path.join(install_efi_directory,
|
||||
"loader",
|
||||
"loader.conf")
|
||||
@ -528,14 +517,13 @@ def install_systemd_boot(efi_directory):
|
||||
|
||||
for (kernel, kernel_type, kernel_version) in get_kernels(installation_root_path):
|
||||
create_systemd_boot_conf(installation_root_path,
|
||||
efi_directory,
|
||||
uuid,
|
||||
distribution,
|
||||
kernel,
|
||||
kernel_type,
|
||||
kernel_version)
|
||||
efi_directory,
|
||||
uuid,
|
||||
kernel,
|
||||
kernel_version)
|
||||
|
||||
create_loader(loader_path, installation_root_path)
|
||||
|
||||
create_loader(loader_path, distribution_translated)
|
||||
|
||||
def get_grub_efi_parameters():
|
||||
"""
|
||||
@ -554,15 +542,16 @@ def get_grub_efi_parameters():
|
||||
|
||||
if efi_bitness == "32":
|
||||
# Assume all 32-bitters are legacy x86
|
||||
return ("i386-efi", "grubia32.efi", "bootia32.efi")
|
||||
return "i386-efi", "grubia32.efi", "bootia32.efi"
|
||||
elif efi_bitness == "64" and cpu_type == "aarch64":
|
||||
return ("arm64-efi", "grubaa64.efi", "bootaa64.efi")
|
||||
return "arm64-efi", "grubaa64.efi", "bootaa64.efi"
|
||||
elif efi_bitness == "64" and cpu_type == "loongarch64":
|
||||
return ("loongarch64-efi", "grubloongarch64.efi", "bootloongarch64.efi")
|
||||
return "loongarch64-efi", "grubloongarch64.efi", "bootloongarch64.efi"
|
||||
elif efi_bitness == "64":
|
||||
# If it's not ARM, must by AMD64
|
||||
return ("x86_64-efi", "grubx64.efi", "bootx64.efi")
|
||||
libcalamares.utils.warning("Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type)))
|
||||
return "x86_64-efi", "grubx64.efi", "bootx64.efi"
|
||||
libcalamares.utils.warning(
|
||||
"Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type)))
|
||||
return None
|
||||
|
||||
|
||||
@ -667,8 +656,8 @@ def install_grub(efi_directory, fw_type):
|
||||
|
||||
# VFAT is weird, see issue CAL-385
|
||||
install_efi_directory_firmware = (vfat_correct_case(
|
||||
install_efi_directory,
|
||||
"EFI"))
|
||||
install_efi_directory,
|
||||
"EFI"))
|
||||
if not os.path.exists(install_efi_directory_firmware):
|
||||
os.makedirs(install_efi_directory_firmware)
|
||||
|
||||
@ -676,8 +665,8 @@ def install_grub(efi_directory, fw_type):
|
||||
# most usual they are boot, Boot, BOOT
|
||||
|
||||
install_efi_boot_directory = (vfat_correct_case(
|
||||
install_efi_directory_firmware,
|
||||
"boot"))
|
||||
install_efi_directory_firmware,
|
||||
"boot"))
|
||||
if not os.path.exists(install_efi_boot_directory):
|
||||
os.makedirs(install_efi_boot_directory)
|
||||
|
||||
@ -712,6 +701,9 @@ def install_secureboot(efi_directory):
|
||||
install_efi_bin = "shimx64.efi"
|
||||
elif efi_word_size() == "32":
|
||||
install_efi_bin = "shimia32.efi"
|
||||
else:
|
||||
libcalamares.utils.warning(f"Unknown efi word size of {efi_word_size()} found")
|
||||
return None
|
||||
|
||||
# Copied, roughly, from openSUSE's install script,
|
||||
# and pythonified. *disk* is something like /dev/sda,
|
||||
@ -725,7 +717,7 @@ def install_secureboot(efi_directory):
|
||||
libcalamares.job.configuration["grubProbe"],
|
||||
"-t", "disk", "--device-map=", install_efi_directory]).decode("ascii")
|
||||
|
||||
efi_drive_partition = efi_drive.replace("(","").replace(")","").split(",")[1]
|
||||
efi_drive_partition = efi_drive.replace("(", "").replace(")", "").split(",")[1]
|
||||
# Get the first run of digits from the partition
|
||||
efi_partition_number = None
|
||||
c = 0
|
||||
@ -765,6 +757,62 @@ def vfat_correct_case(parent, name):
|
||||
return os.path.join(parent, name)
|
||||
|
||||
|
||||
def efi_partitions(efi_boot_path):
|
||||
"""
|
||||
The (one) partition mounted on @p efi_boot_path, or an empty list.
|
||||
"""
|
||||
return [p for p in libcalamares.globalstorage.value("partitions") if p["mountPoint"] == efi_boot_path]
|
||||
|
||||
|
||||
def update_refind_config(efi_directory, installation_root_path):
|
||||
"""
|
||||
:param efi_directory: The path to the efi directory relative to the root
|
||||
:param installation_root_path: The path to the root of the installation
|
||||
"""
|
||||
try:
|
||||
kernel_list = libcalamares.job.configuration["refindKernelList"]
|
||||
except KeyError:
|
||||
libcalamares.utils.warning('refindKernelList not set. Skipping updating refind.conf')
|
||||
return
|
||||
|
||||
# Update the config in the file
|
||||
for line in fileinput.input(installation_root_path + efi_directory + "/EFI/refind/refind.conf", inplace=True):
|
||||
line = line.strip()
|
||||
if line.startswith("#extra_kernel_version_strings") or line.startswith("extra_kernel_version_strings"):
|
||||
line = line.lstrip("#")
|
||||
for kernel in kernel_list:
|
||||
if kernel not in line:
|
||||
line += "," + kernel
|
||||
print(line)
|
||||
|
||||
|
||||
def install_refind(efi_directory):
|
||||
try:
|
||||
installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
|
||||
except KeyError:
|
||||
libcalamares.utils.warning('Global storage value "rootMountPoint" missing')
|
||||
|
||||
install_efi_directory = installation_root_path + efi_directory
|
||||
uuid = get_uuid()
|
||||
kernel_params = " ".join(get_kernel_params(uuid))
|
||||
conf_path = os.path.join(installation_root_path, "boot/refind_linux.conf")
|
||||
|
||||
check_target_env_call(["refind-install"])
|
||||
|
||||
with open(conf_path, "r") as refind_file:
|
||||
filedata = [x.strip() for x in refind_file.readlines()]
|
||||
|
||||
with open(conf_path, 'w') as refind_file:
|
||||
for line in filedata:
|
||||
if line.startswith('"Boot with standard options"'):
|
||||
line = f'"Boot with standard options" "{kernel_params}"'
|
||||
elif line.startswith('"Boot to single-user mode"'):
|
||||
line = f'"Boot to single-user mode" "{kernel_params}" single'
|
||||
refind_file.write(line + "\n")
|
||||
|
||||
update_refind_config(efi_directory, installation_root_path)
|
||||
|
||||
|
||||
def prepare_bootloader(fw_type):
|
||||
"""
|
||||
Prepares bootloader.
|
||||
@ -774,19 +822,47 @@ def prepare_bootloader(fw_type):
|
||||
:param fw_type:
|
||||
:return:
|
||||
"""
|
||||
efi_boot_loader = libcalamares.job.configuration["efiBootLoader"]
|
||||
|
||||
# Get the boot loader selection from global storage if it is set in the config file
|
||||
try:
|
||||
gs_name = libcalamares.job.configuration["efiBootLoaderVar"]
|
||||
if libcalamares.globalstorage.contains(gs_name):
|
||||
efi_boot_loader = libcalamares.globalstorage.value(gs_name)
|
||||
else:
|
||||
libcalamares.utils.warning(
|
||||
f"Specified global storage value not found in global storage")
|
||||
return None
|
||||
except KeyError:
|
||||
# If the conf value for using global storage is not set, use the setting from the config file.
|
||||
try:
|
||||
efi_boot_loader = libcalamares.job.configuration["efiBootLoader"]
|
||||
except KeyError:
|
||||
if fw_type == "efi":
|
||||
libcalamares.utils.warning("Configuration missing both efiBootLoader and efiBootLoaderVar on an EFI "
|
||||
"system, bootloader not installed")
|
||||
return
|
||||
else:
|
||||
pass
|
||||
|
||||
# If the user has selected not to install bootloader, bail out here
|
||||
if efi_boot_loader.casefold() == "none":
|
||||
libcalamares.utils.debug("Skipping bootloader installation since no bootloader was selected")
|
||||
return None
|
||||
|
||||
efi_directory = libcalamares.globalstorage.value("efiSystemPartition")
|
||||
|
||||
if efi_boot_loader == "systemd-boot" and fw_type == "efi":
|
||||
install_systemd_boot(efi_directory)
|
||||
elif efi_boot_loader == "sb-shim" and fw_type == "efi":
|
||||
install_secureboot(efi_directory)
|
||||
elif efi_boot_loader == "refind" and fw_type == "efi":
|
||||
install_refind(efi_directory)
|
||||
elif efi_boot_loader == "grub" or fw_type != "efi":
|
||||
install_grub(efi_directory, fw_type)
|
||||
else:
|
||||
libcalamares.utils.debug( "WARNING: the combination of "
|
||||
"boot-loader '{!s}' and firmware '{!s}' "
|
||||
"is not supported.".format(efi_boot_loader, fw_type) )
|
||||
libcalamares.utils.debug("WARNING: the combination of "
|
||||
"boot-loader '{!s}' and firmware '{!s}' "
|
||||
"is not supported.".format(efi_boot_loader, fw_type))
|
||||
|
||||
|
||||
def run():
|
||||
@ -798,16 +874,16 @@ def run():
|
||||
|
||||
fw_type = libcalamares.globalstorage.value("firmwareType")
|
||||
|
||||
if (libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi"):
|
||||
libcalamares.utils.warning( "Non-EFI system, and no bootloader is set." )
|
||||
if libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi":
|
||||
libcalamares.utils.warning("Non-EFI system, and no bootloader is set.")
|
||||
return None
|
||||
|
||||
partitions = libcalamares.globalstorage.value("partitions")
|
||||
if fw_type == "efi":
|
||||
efi_system_partition = libcalamares.globalstorage.value("efiSystemPartition")
|
||||
esp_found = [ p for p in partitions if p["mountPoint"] == efi_system_partition ]
|
||||
esp_found = [p for p in partitions if p["mountPoint"] == efi_system_partition]
|
||||
if not esp_found:
|
||||
libcalamares.utils.warning( "EFI system, but nothing mounted on {!s}".format(efi_system_partition) )
|
||||
libcalamares.utils.warning("EFI system, but nothing mounted on {!s}".format(efi_system_partition))
|
||||
return None
|
||||
|
||||
try:
|
||||
@ -817,7 +893,8 @@ def run():
|
||||
libcalamares.utils.debug("stdout:" + str(e.stdout))
|
||||
libcalamares.utils.debug("stderr:" + str(e.stderr))
|
||||
return (_("Bootloader installation error"),
|
||||
_("The bootloader could not be installed. The installation command <pre>{!s}</pre> returned error code {!s}.")
|
||||
_("The bootloader could not be installed. The installation command <pre>{!s}</pre> returned error "
|
||||
"code {!s}.")
|
||||
.format(e.cmd, e.returncode))
|
||||
|
||||
return None
|
||||
|
@ -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"]
|
||||
|
@ -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 } }
|
||||
|
@ -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()
|
||||
|
@ -5,6 +5,6 @@
|
||||
---
|
||||
# Dracut defaults to setting initramfs-<kernel-version>.img
|
||||
# If you want to specify another filename for the resulting image,
|
||||
# set a custom kernel name, including the path
|
||||
# set a custom name, including the path
|
||||
#
|
||||
# kernelName: /boot/initramfs-linux.img
|
||||
initramfsName: /boot/initramfs-freebsd.img
|
||||
|
@ -6,4 +6,4 @@ $id: https://calamares.io/schemas/dracut
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
kernelName: { type: string }
|
||||
initramfsName: { type: string }
|
||||
|
@ -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}"))
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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")
|
||||
|
@ -294,13 +294,13 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun
|
||||
# Mount the subvolumes
|
||||
swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap")
|
||||
for s in btrfs_subvolumes:
|
||||
mount_option = "subvol={}".format(s['subvolume'])
|
||||
if s['subvolume'] == swap_subvol:
|
||||
mount_option += "," + get_mount_options("btrfs_swap", mount_options, partition)
|
||||
mount_option_no_subvol = get_mount_options("btrfs_swap", mount_options, partition)
|
||||
else:
|
||||
mount_option += "," + get_mount_options(fstype, mount_options, partition)
|
||||
mount_option_no_subvol = get_mount_options(fstype, mount_options, partition)
|
||||
mount_option = f"subvol={s['subvolume']},{mount_option_no_subvol}"
|
||||
subvolume_mountpoint = mount_point[:-1] + s['mountPoint']
|
||||
mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option})
|
||||
mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option_no_subvol})
|
||||
if libcalamares.utils.mount(device,
|
||||
subvolume_mountpoint,
|
||||
fstype,
|
||||
@ -308,6 +308,14 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun
|
||||
libcalamares.utils.warning("Cannot mount {}".format(device))
|
||||
|
||||
|
||||
def enable_swap_partition(devices):
|
||||
try:
|
||||
for d in devices:
|
||||
libcalamares.utils.host_env_process_output(["swapon", d])
|
||||
except subprocess.CalledProcessError:
|
||||
libcalamares.utils.warning(f"Failed to enable swap for devices: {devices}")
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Mount all the partitions from GlobalStorage and from the job configuration.
|
||||
@ -321,6 +329,11 @@ def run():
|
||||
return (_("Configuration Error"),
|
||||
_("No partitions are defined for <pre>{!s}</pre> to use.").format("mount"))
|
||||
|
||||
# Find existing swap partitions that are part of the installation and enable them now
|
||||
swap_devices = [p['device'] for p in partitions if (p['fs'] == 'linuxswap' and p['claimed'])]
|
||||
|
||||
enable_swap_partition(swap_devices)
|
||||
|
||||
root_mount_point = tempfile.mkdtemp(prefix="calamares-root-")
|
||||
|
||||
# Get the mountOptions, if this is None, that is OK and will be handled later
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ) )
|
||||
{
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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" );
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 "
|
||||
|
@ -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() )
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -11,6 +11,7 @@
|
||||
#ifndef KPMHELPERS_H
|
||||
#define KPMHELPERS_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Job.h"
|
||||
|
||||
#include <kpmcore/core/partitiontable.h>
|
||||
@ -83,6 +84,7 @@ Partition* createNewEncryptedPartition( PartitionNode* parent,
|
||||
const QString& fsLabel,
|
||||
qint64 firstSector,
|
||||
qint64 lastSector,
|
||||
Config::LuksGeneration luksFsType,
|
||||
const QString& passphrase,
|
||||
PartitionTable::Flags flags );
|
||||
|
||||
@ -119,6 +121,17 @@ bool cryptLabel( Partition* partition, const QString& label );
|
||||
*/
|
||||
int cryptVersion( Partition* partition );
|
||||
|
||||
/** @brief Convert a luksGeneration into its FS type for KPMCore.
|
||||
*
|
||||
* Will convert Luks1 into FileSystem::Type::luks and Luks2 into
|
||||
* FileSystem::Type::luks2 for KPMCore partitioning functions.
|
||||
*
|
||||
* @return The LUKS FS type (default @c luks )
|
||||
*/
|
||||
FileSystem::Type
|
||||
luksGenerationToFSName( Config::LuksGeneration luksGeneration );
|
||||
|
||||
|
||||
/** @brief Return a result for an @p operation
|
||||
*
|
||||
* Executes the operation, and if successful, returns a success result.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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 ) );
|
||||
}
|
||||
|
@ -11,9 +11,9 @@
|
||||
#ifndef PARTITIONLAYOUT_H
|
||||
#define PARTITIONLAYOUT_H
|
||||
|
||||
#include "partition/PartitionSize.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "core/PartUtils.h"
|
||||
#include "partition/PartitionSize.h"
|
||||
|
||||
// KPMcore
|
||||
#include <kpmcore/core/partitiontable.h>
|
||||
@ -116,6 +116,7 @@ public:
|
||||
QList< Partition* > createPartitions( Device* dev,
|
||||
qint64 firstSector,
|
||||
qint64 lastSector,
|
||||
Config::LuksGeneration luksFsType,
|
||||
QString luksPassphrase,
|
||||
PartitionNode* parent,
|
||||
const PartitionRole& role );
|
||||
|
@ -4,6 +4,7 @@
|
||||
* SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2019 Collabora Ltd
|
||||
* SPDX-FileCopyrightText: 2021 Anubhav Choudhary <ac.10edu@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
@ -28,7 +29,6 @@
|
||||
#include "gui/PartitionBarsView.h"
|
||||
#include "gui/PartitionLabelsView.h"
|
||||
#include "gui/PartitionSplitterWidget.h"
|
||||
#include "gui/ReplaceWidget.h"
|
||||
#include "gui/ScanningDialog.h"
|
||||
|
||||
#include "Branding.h"
|
||||
@ -288,6 +288,16 @@ ChoicePage::setupChoices()
|
||||
m_eraseFsTypesChoiceComboBox, &QComboBox::currentTextChanged, m_config, &Config::setEraseFsTypeChoice );
|
||||
connect( m_config, &Config::eraseModeFilesystemChanged, this, &ChoicePage::onActionChanged );
|
||||
m_eraseButton->addOptionsComboBox( m_eraseFsTypesChoiceComboBox );
|
||||
|
||||
// Also offer it for "replace
|
||||
m_replaceFsTypesChoiceComboBox = new QComboBox;
|
||||
m_replaceFsTypesChoiceComboBox->addItems( m_config->eraseFsTypes() );
|
||||
connect( m_replaceFsTypesChoiceComboBox,
|
||||
&QComboBox::currentTextChanged,
|
||||
m_config,
|
||||
&Config::setReplaceFilesystemChoice );
|
||||
connect( m_config, &Config::replaceModeFilesystemChanged, this, &ChoicePage::onActionChanged );
|
||||
m_replaceButton->addOptionsComboBox( m_replaceFsTypesChoiceComboBox );
|
||||
}
|
||||
|
||||
m_itemsLayout->addWidget( m_alongsideButton );
|
||||
@ -303,13 +313,8 @@ ChoicePage::setupChoices()
|
||||
|
||||
m_itemsLayout->addStretch();
|
||||
|
||||
#if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) )
|
||||
auto buttonSignal = QOverload< int, bool >::of( &QButtonGroup::buttonToggled );
|
||||
#else
|
||||
auto buttonSignal = &QButtonGroup::idToggled;
|
||||
#endif
|
||||
connect( m_grp,
|
||||
buttonSignal,
|
||||
&QButtonGroup::idToggled,
|
||||
this,
|
||||
[ this ]( int id, bool checked )
|
||||
{
|
||||
@ -449,7 +454,6 @@ ChoicePage::continueApplyDeviceChoice()
|
||||
if ( m_lastSelectedDeviceIndex != m_drivesCombo->currentIndex() )
|
||||
{
|
||||
m_lastSelectedDeviceIndex = m_drivesCombo->currentIndex();
|
||||
m_lastSelectedActionIndex = -1;
|
||||
m_config->setInstallChoice( m_config->initialInstallChoice() );
|
||||
checkInstallChoiceRadioButton( m_config->installChoice() );
|
||||
}
|
||||
@ -461,6 +465,18 @@ ChoicePage::continueApplyDeviceChoice()
|
||||
void
|
||||
ChoicePage::onActionChanged()
|
||||
{
|
||||
if ( m_enableEncryptionWidget )
|
||||
{
|
||||
if ( m_config->installChoice() == InstallChoice::Erase && m_eraseFsTypesChoiceComboBox )
|
||||
{
|
||||
m_encryptWidget->setFilesystem( FileSystem::typeForName( m_eraseFsTypesChoiceComboBox->currentText() ) );
|
||||
}
|
||||
else if ( m_config->installChoice() == InstallChoice::Replace && m_replaceFsTypesChoiceComboBox )
|
||||
{
|
||||
m_encryptWidget->setFilesystem( FileSystem::typeForName( m_replaceFsTypesChoiceComboBox->currentText() ) );
|
||||
}
|
||||
}
|
||||
|
||||
Device* currd = selectedDevice();
|
||||
if ( currd )
|
||||
{
|
||||
@ -483,9 +499,9 @@ ChoicePage::onEraseSwapChoiceChanged()
|
||||
void
|
||||
ChoicePage::applyActionChoice( InstallChoice choice )
|
||||
{
|
||||
cDebug() << "Prev" << m_lastSelectedActionIndex << "InstallChoice" << choice
|
||||
<< Config::installChoiceNames().find( choice );
|
||||
cDebug() << "InstallChoice" << choice << Config::installChoiceNames().find( choice );
|
||||
m_beforePartitionBarsView->selectionModel()->disconnect( SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ) );
|
||||
auto priorSelection = m_beforePartitionBarsView->selectionModel()->currentIndex();
|
||||
m_beforePartitionBarsView->selectionModel()->clearSelection();
|
||||
m_beforePartitionBarsView->selectionModel()->clearCurrentIndex();
|
||||
|
||||
@ -496,6 +512,7 @@ ChoicePage::applyActionChoice( InstallChoice choice )
|
||||
auto gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(),
|
||||
m_config->eraseFsType(),
|
||||
m_config->luksFileSystemType(),
|
||||
m_encryptWidget->passphrase(),
|
||||
gs->value( "efiSystemPartition" ).toString(),
|
||||
CalamaresUtils::GiBtoBytes(
|
||||
@ -543,6 +560,12 @@ ChoicePage::applyActionChoice( InstallChoice choice )
|
||||
this,
|
||||
SLOT( onPartitionToReplaceSelected( QModelIndex, QModelIndex ) ),
|
||||
Qt::UniqueConnection );
|
||||
|
||||
// Maintain the selection for replace
|
||||
if ( priorSelection.isValid() )
|
||||
{
|
||||
m_beforePartitionBarsView->selectionModel()->setCurrentIndex( priorSelection, QItemSelectionModel::Select );
|
||||
}
|
||||
break;
|
||||
|
||||
case InstallChoice::Alongside:
|
||||
@ -752,6 +775,7 @@ ChoicePage::doAlongsideApply()
|
||||
m_core->layoutApply( dev,
|
||||
newLastSector + 2,
|
||||
oldLastSector,
|
||||
m_config->luksFileSystemType(),
|
||||
m_encryptWidget->passphrase(),
|
||||
candidate->parent(),
|
||||
candidate->roles() );
|
||||
@ -826,6 +850,7 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
|
||||
m_core->layoutApply( selectedDevice(),
|
||||
selectedPartition->firstSector(),
|
||||
selectedPartition->lastSector(),
|
||||
m_config->luksFileSystemType(),
|
||||
m_encryptWidget->passphrase(),
|
||||
newParent,
|
||||
newRoles );
|
||||
@ -857,7 +882,8 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
|
||||
selectedDevice(),
|
||||
selectedPartition,
|
||||
{ gs->value( "defaultPartitionType" ).toString(),
|
||||
gs->value( "defaultFileSystemType" ).toString(),
|
||||
m_config->replaceModeFilesystem(),
|
||||
m_config->luksFileSystemType(),
|
||||
m_encryptWidget->passphrase() } );
|
||||
Partition* homePartition = findPartitionByPath( { selectedDevice() }, *homePartitionPath );
|
||||
|
||||
@ -1731,10 +1757,20 @@ ChoicePage::createBootloaderPanel()
|
||||
return panelWidget;
|
||||
}
|
||||
|
||||
bool ChoicePage::shouldShowEncryptWidget( Config::InstallChoice choice ) const
|
||||
bool
|
||||
ChoicePage::shouldShowEncryptWidget( Config::InstallChoice choice ) const
|
||||
{
|
||||
// If there are any choices for FS, check it's not ZFS because that doesn't
|
||||
// support the kind of encryption we enable here.
|
||||
const bool suitableFS = m_eraseFsTypesChoiceComboBox ? m_eraseFsTypesChoiceComboBox->currentText() != "zfs" : true;
|
||||
return (choice == InstallChoice::Erase) && m_enableEncryptionWidget && suitableFS;
|
||||
bool suitableFS = true;
|
||||
if ( !m_config->allowZfsEncryption()
|
||||
&& ( ( m_eraseFsTypesChoiceComboBox && m_eraseFsTypesChoiceComboBox->isVisible()
|
||||
&& m_eraseFsTypesChoiceComboBox->currentText() == "zfs" )
|
||||
|| ( m_replaceFsTypesChoiceComboBox && m_replaceFsTypesChoiceComboBox->isVisible()
|
||||
&& m_replaceFsTypesChoiceComboBox->currentText() == "zfs" ) ) )
|
||||
{
|
||||
suitableFS = false;
|
||||
}
|
||||
|
||||
const bool suitableChoice
|
||||
= choice == InstallChoice::Erase || choice == InstallChoice::Alongside || choice == InstallChoice::Replace;
|
||||
return suitableChoice && m_enableEncryptionWidget && suitableFS;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
* SPDX-FileCopyrightText: 2014-2016 Teo Mrnjavac <teo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2019 Collabora Ltd
|
||||
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
@ -152,6 +153,7 @@ private:
|
||||
Calamares::Widgets::PrettyRadioButton* m_somethingElseButton;
|
||||
QComboBox* m_eraseSwapChoiceComboBox = nullptr; // UI, see also Config's swap choice
|
||||
QComboBox* m_eraseFsTypesChoiceComboBox = nullptr; // UI, see also Config's erase-mode FS
|
||||
QComboBox* m_replaceFsTypesChoiceComboBox = nullptr; // UI, see also Config's erase-mode FS
|
||||
|
||||
|
||||
DeviceInfoWidget* m_deviceInfoWidget;
|
||||
@ -166,7 +168,6 @@ private:
|
||||
QPointer< QComboBox > m_efiComboBox;
|
||||
|
||||
int m_lastSelectedDeviceIndex = -1;
|
||||
int m_lastSelectedActionIndex = -1;
|
||||
|
||||
bool m_enableEncryptionWidget = false;
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <kpmcore/fs/filesystem.h>
|
||||
#include <kpmcore/fs/filesystemfactory.h>
|
||||
#include <kpmcore/fs/luks.h>
|
||||
#include <kpmcore/fs/luks2.h>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QDir>
|
||||
@ -223,6 +224,8 @@ CreatePartitionDialog::initGptPartitionTypeUi()
|
||||
Partition*
|
||||
CreatePartitionDialog::getNewlyCreatedPartition()
|
||||
{
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
|
||||
if ( m_role.roles() == PartitionRole::None )
|
||||
{
|
||||
m_role = PartitionRole( m_ui->extendedRadioButton->isChecked() ? PartitionRole::Extended
|
||||
@ -242,12 +245,21 @@ CreatePartitionDialog::getNewlyCreatedPartition()
|
||||
// newFlags() and the consumer (see PartitionPage::onCreateClicked)
|
||||
// does so, to set up the partition for create-and-then-set-flags.
|
||||
Partition* partition = nullptr;
|
||||
QString luksFsType = gs->value( "luksFileSystemType" ).toString();
|
||||
QString luksPassphrase = m_ui->encryptWidget->passphrase();
|
||||
if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty()
|
||||
&& fsType != FileSystem::Zfs )
|
||||
{
|
||||
partition = KPMHelpers::createNewEncryptedPartition(
|
||||
m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() );
|
||||
partition = KPMHelpers::createNewEncryptedPartition( m_parent,
|
||||
*m_device,
|
||||
m_role,
|
||||
fsType,
|
||||
fsLabel,
|
||||
first,
|
||||
last,
|
||||
Config::luksGenerationNames().find(luksFsType, Config::LuksGeneration::Luks1),
|
||||
luksPassphrase,
|
||||
PartitionTable::Flags() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -308,6 +320,12 @@ CreatePartitionDialog::updateMountPointUi()
|
||||
m_ui->encryptWidget->show();
|
||||
m_ui->encryptWidget->reset();
|
||||
}
|
||||
else if ( FileSystemFactory::map()[ FileSystem::Type::Luks2 ]->supportCreate()
|
||||
&& FS::luks2::canEncryptType( type ) && !m_role.has( PartitionRole::Extended ) )
|
||||
{
|
||||
m_ui->encryptWidget->show();
|
||||
m_ui->encryptWidget->reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->encryptWidget->reset();
|
||||
|
@ -2,6 +2,7 @@
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
@ -17,6 +18,8 @@
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
#include "utils/Retranslator.h"
|
||||
|
||||
constexpr int ZFS_MIN_LENGTH = 8;
|
||||
|
||||
/** @brief Does this system support whole-disk encryption?
|
||||
*
|
||||
* Returns @c true if the system is likely to support encryption
|
||||
@ -143,7 +146,7 @@ applyPixmap( QLabel* label, CalamaresUtils::ImageType pixmap )
|
||||
}
|
||||
|
||||
void
|
||||
EncryptWidget::updateState()
|
||||
EncryptWidget::updateState( const bool notify )
|
||||
{
|
||||
if ( m_ui->m_passphraseLineEdit->isVisible() )
|
||||
{
|
||||
@ -155,6 +158,11 @@ EncryptWidget::updateState()
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusWarning );
|
||||
m_ui->m_iconLabel->setToolTip( tr( "Please enter the same passphrase in both boxes." ) );
|
||||
}
|
||||
else if ( m_filesystem == FileSystem::Zfs && p1.length() < ZFS_MIN_LENGTH )
|
||||
{
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusError );
|
||||
m_ui->m_iconLabel->setToolTip( tr( "Password must be a minimum of %1 characters" ).arg( ZFS_MIN_LENGTH ) );
|
||||
}
|
||||
else if ( p1 == p2 )
|
||||
{
|
||||
applyPixmap( m_ui->m_iconLabel, CalamaresUtils::StatusOk );
|
||||
@ -172,7 +180,10 @@ EncryptWidget::updateState()
|
||||
if ( newState != m_state )
|
||||
{
|
||||
m_state = newState;
|
||||
Q_EMIT stateChanged( m_state );
|
||||
if ( notify )
|
||||
{
|
||||
Q_EMIT stateChanged( m_state );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,3 +212,13 @@ EncryptWidget::onCheckBoxStateChanged( int checked )
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
void
|
||||
EncryptWidget::setFilesystem( const FileSystem::Type fs )
|
||||
{
|
||||
m_filesystem = fs;
|
||||
if ( m_state != Encryption::Disabled )
|
||||
{
|
||||
updateState( false );
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
@ -14,6 +15,8 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <kpmcore/fs/filesystem.h>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class EncryptWidget;
|
||||
@ -38,6 +41,12 @@ public:
|
||||
Encryption state() const;
|
||||
void setText( const QString& text );
|
||||
|
||||
/**
|
||||
* @brief setFilesystem sets the filesystem name used for password validation
|
||||
* @param fs A QString containing the name of the filesystem
|
||||
*/
|
||||
void setFilesystem( const FileSystem::Type fs );
|
||||
|
||||
QString passphrase() const;
|
||||
|
||||
void retranslate();
|
||||
@ -46,12 +55,14 @@ signals:
|
||||
void stateChanged( Encryption );
|
||||
|
||||
private:
|
||||
void updateState();
|
||||
void updateState( const bool notify = true );
|
||||
void onPassphraseEdited();
|
||||
void onCheckBoxStateChanged( int checked );
|
||||
|
||||
Ui::EncryptWidget* m_ui;
|
||||
Encryption m_state;
|
||||
|
||||
FileSystem::Type m_filesystem;
|
||||
};
|
||||
|
||||
#endif // ENCRYPTWIDGET_H
|
||||
|
@ -1,393 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
||||
* SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ReplaceWidget.h"
|
||||
#include "ui_ReplaceWidget.h"
|
||||
|
||||
#include "core/DeviceModel.h"
|
||||
#include "core/PartitionActions.h"
|
||||
#include "core/PartitionCoreModule.h"
|
||||
#include "core/PartitionInfo.h"
|
||||
|
||||
#include "Branding.h"
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "partition/FileSystem.h"
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
#include "utils/Retranslator.h"
|
||||
|
||||
#include <kpmcore/core/partition.h>
|
||||
#include <kpmcore/fs/filesystem.h>
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
using CalamaresUtils::Partition::untranslatedFS;
|
||||
using CalamaresUtils::Partition::userVisibleFS;
|
||||
|
||||
ReplaceWidget::ReplaceWidget( PartitionCoreModule* core, QComboBox* devicesComboBox, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, m_ui( new Ui_ReplaceWidget )
|
||||
, m_core( core )
|
||||
, m_isEfi( false )
|
||||
{
|
||||
m_ui->setupUi( this );
|
||||
|
||||
m_ui->bootComboBox->hide();
|
||||
m_ui->bootComboBox->clear();
|
||||
m_ui->bootStatusLabel->hide();
|
||||
m_ui->bootStatusLabel->clear();
|
||||
|
||||
updateFromCurrentDevice( devicesComboBox );
|
||||
connect( devicesComboBox,
|
||||
&QComboBox::currentTextChanged,
|
||||
this,
|
||||
[ = ]( const QString& /* text */ ) { updateFromCurrentDevice( devicesComboBox ); } );
|
||||
|
||||
CALAMARES_RETRANSLATE( onPartitionSelected(); );
|
||||
}
|
||||
|
||||
|
||||
ReplaceWidget::~ReplaceWidget() {}
|
||||
|
||||
|
||||
bool
|
||||
ReplaceWidget::isNextEnabled() const
|
||||
{
|
||||
return m_nextEnabled;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::reset()
|
||||
{
|
||||
//moo;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::applyChanges()
|
||||
{
|
||||
auto gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
|
||||
PartitionModel* model = qobject_cast< PartitionModel* >( m_ui->partitionTreeView->model() );
|
||||
if ( model )
|
||||
{
|
||||
Partition* partition = model->partitionForIndex( m_ui->partitionTreeView->currentIndex() );
|
||||
if ( partition )
|
||||
{
|
||||
Device* dev = model->device();
|
||||
|
||||
PartitionActions::doReplacePartition( m_core,
|
||||
dev,
|
||||
partition,
|
||||
{ gs->value( "defaultPartitionTableType" ).toString(),
|
||||
gs->value( "defaultFileSystemType" ).toString(),
|
||||
QString() } );
|
||||
|
||||
if ( m_isEfi )
|
||||
{
|
||||
QList< Partition* > efiSystemPartitions = m_core->efiSystemPartitions();
|
||||
if ( efiSystemPartitions.count() == 1 )
|
||||
{
|
||||
PartitionInfo::setMountPoint( efiSystemPartitions.first(),
|
||||
gs->value( "efiSystemPartition" ).toString() );
|
||||
}
|
||||
else if ( efiSystemPartitions.count() > 1 )
|
||||
{
|
||||
PartitionInfo::setMountPoint( efiSystemPartitions.at( m_ui->bootComboBox->currentIndex() ),
|
||||
gs->value( "efiSystemPartition" ).toString() );
|
||||
}
|
||||
}
|
||||
|
||||
m_core->dumpQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::onPartitionSelected()
|
||||
{
|
||||
if ( Calamares::JobQueue::instance()->globalStorage()->value( "firmwareType" ) == "efi" )
|
||||
{
|
||||
m_isEfi = true;
|
||||
}
|
||||
|
||||
const auto* branding = Calamares::Branding::instance();
|
||||
if ( m_ui->partitionTreeView->currentIndex() == QModelIndex() )
|
||||
{
|
||||
updateStatus( CalamaresUtils::PartitionPartition,
|
||||
tr( "Select where to install %1.<br/>"
|
||||
"<font color=\"red\">Warning: </font>this will delete all files "
|
||||
"on the selected partition." )
|
||||
.arg( branding->versionedName() ) );
|
||||
setNextEnabled( false );
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
double requiredSpaceB
|
||||
= Calamares::JobQueue::instance()->globalStorage()->value( "requiredStorageGiB" ).toDouble( &ok ) * 1024 * 1024
|
||||
* 1024;
|
||||
|
||||
PartitionModel* model = qobject_cast< PartitionModel* >( m_ui->partitionTreeView->model() );
|
||||
if ( model && ok )
|
||||
{
|
||||
const QStringList osproberLines
|
||||
= Calamares::JobQueue::instance()->globalStorage()->value( "osproberLines" ).toStringList();
|
||||
|
||||
Partition* partition = model->partitionForIndex( m_ui->partitionTreeView->currentIndex() );
|
||||
if ( !partition || partition->state() != KPM_PARTITION_STATE( None ) )
|
||||
{
|
||||
updateStatus( CalamaresUtils::Fail, tr( "The selected item does not appear to be a valid partition." ) );
|
||||
setNextEnabled( false );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( partition->roles().has( PartitionRole::Unallocated ) )
|
||||
{
|
||||
updateStatus( CalamaresUtils::Fail,
|
||||
tr( "%1 cannot be installed on empty space. Please select an "
|
||||
"existing partition." )
|
||||
.arg( branding->versionedName() ) );
|
||||
setNextEnabled( false );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( partition->roles().has( PartitionRole::Extended ) )
|
||||
{
|
||||
updateStatus( CalamaresUtils::Fail,
|
||||
tr( "%1 cannot be installed on an extended partition. Please select an "
|
||||
"existing primary or logical partition." )
|
||||
.arg( branding->versionedName() ) );
|
||||
setNextEnabled( false );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( partition->partitionPath().isEmpty() )
|
||||
{
|
||||
updateStatus( CalamaresUtils::Fail,
|
||||
tr( "%1 cannot be installed on this partition." ).arg( branding->versionedName() ) );
|
||||
setNextEnabled( false );
|
||||
return;
|
||||
}
|
||||
|
||||
QString fsNameForUser = userVisibleFS( partition->fileSystem() );
|
||||
QString prettyName = tr( "Data partition (%1)" ).arg( fsNameForUser );
|
||||
for ( const auto& line : osproberLines )
|
||||
{
|
||||
QStringList lineColumns = line.split( ':' );
|
||||
|
||||
QString path = lineColumns.value( 0 ).simplified();
|
||||
if ( path == partition->partitionPath() )
|
||||
{
|
||||
QString osName;
|
||||
if ( !lineColumns.value( 1 ).simplified().isEmpty() )
|
||||
{
|
||||
osName = lineColumns.value( 1 ).simplified();
|
||||
}
|
||||
else if ( !lineColumns.value( 2 ).simplified().isEmpty() )
|
||||
{
|
||||
osName = lineColumns.value( 2 ).simplified();
|
||||
}
|
||||
|
||||
if ( osName.isEmpty() )
|
||||
{
|
||||
prettyName = tr( "Unknown system partition (%1)" ).arg( fsNameForUser );
|
||||
}
|
||||
else
|
||||
{
|
||||
prettyName = tr( "%1 system partition (%2)" )
|
||||
.arg( osName.replace( 0, 1, osName.at( 0 ).toUpper() ) )
|
||||
.arg( fsNameForUser );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The loss of precision is ok; we're not going to fall over from a single byte
|
||||
if ( static_cast< double >( partition->capacity() ) < requiredSpaceB )
|
||||
{
|
||||
updateStatus( CalamaresUtils::Fail,
|
||||
tr( "<strong>%4</strong><br/><br/>"
|
||||
"The partition %1 is too small for %2. Please select a partition "
|
||||
"with capacity at least %3 GiB." )
|
||||
.arg( partition->partitionPath() )
|
||||
.arg( branding->versionedName() )
|
||||
.arg( requiredSpaceB / ( 1024. * 1024. * 1024. ), 0, 'f', 1 )
|
||||
.arg( prettyName ) );
|
||||
setNextEnabled( false );
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui->bootComboBox->hide();
|
||||
m_ui->bootComboBox->clear();
|
||||
m_ui->bootStatusLabel->hide();
|
||||
m_ui->bootStatusLabel->clear();
|
||||
|
||||
if ( m_isEfi )
|
||||
{
|
||||
QList< Partition* > efiSystemPartitions = m_core->efiSystemPartitions();
|
||||
if ( efiSystemPartitions.count() == 0 )
|
||||
{
|
||||
updateStatus( CalamaresUtils::Fail,
|
||||
tr( "<strong>%2</strong><br/><br/>"
|
||||
"An EFI system partition cannot be found anywhere "
|
||||
"on this system. Please go back and use manual "
|
||||
"partitioning to set up %1." )
|
||||
.arg( branding->shortProductName() )
|
||||
.arg( prettyName ) );
|
||||
setNextEnabled( false );
|
||||
}
|
||||
else if ( efiSystemPartitions.count() == 1 )
|
||||
{
|
||||
updateStatus( CalamaresUtils::PartitionPartition,
|
||||
tr( "<strong>%3</strong><br/><br/>"
|
||||
"%1 will be installed on %2.<br/>"
|
||||
"<font color=\"red\">Warning: </font>all data on partition "
|
||||
"%2 will be lost." )
|
||||
.arg( branding->versionedName() )
|
||||
.arg( partition->partitionPath() )
|
||||
.arg( prettyName ) );
|
||||
m_ui->bootStatusLabel->show();
|
||||
m_ui->bootStatusLabel->setText( tr( "The EFI system partition at %1 will be used for starting %2." )
|
||||
.arg( efiSystemPartitions.first()->partitionPath() )
|
||||
.arg( branding->shortProductName() ) );
|
||||
setNextEnabled( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
updateStatus( CalamaresUtils::PartitionPartition,
|
||||
tr( "<strong>%3</strong><br/><br/>"
|
||||
"%1 will be installed on %2.<br/>"
|
||||
"<font color=\"red\">Warning: </font>all data on partition "
|
||||
"%2 will be lost." )
|
||||
.arg( branding->versionedName() )
|
||||
.arg( partition->partitionPath() )
|
||||
.arg( prettyName ) );
|
||||
m_ui->bootStatusLabel->show();
|
||||
m_ui->bootStatusLabel->setText( tr( "EFI system partition:" ) );
|
||||
m_ui->bootComboBox->show();
|
||||
for ( int i = 0; i < efiSystemPartitions.count(); ++i )
|
||||
{
|
||||
Partition* efiPartition = efiSystemPartitions.at( i );
|
||||
m_ui->bootComboBox->addItem( efiPartition->partitionPath(), i );
|
||||
if ( efiPartition->devicePath() == partition->devicePath() && efiPartition->number() == 1 )
|
||||
{
|
||||
m_ui->bootComboBox->setCurrentIndex( i );
|
||||
}
|
||||
}
|
||||
setNextEnabled( true );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
updateStatus( CalamaresUtils::PartitionPartition,
|
||||
tr( "<strong>%3</strong><br/><br/>"
|
||||
"%1 will be installed on %2.<br/>"
|
||||
"<font color=\"red\">Warning: </font>all data on partition "
|
||||
"%2 will be lost." )
|
||||
.arg( branding->versionedName() )
|
||||
.arg( partition->partitionPath() )
|
||||
.arg( prettyName ) );
|
||||
setNextEnabled( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::setNextEnabled( bool enabled )
|
||||
{
|
||||
if ( enabled == m_nextEnabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_nextEnabled = enabled;
|
||||
Q_EMIT nextStatusChanged( enabled );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::updateStatus( CalamaresUtils::ImageType imageType, const QString& text )
|
||||
{
|
||||
int iconSize = CalamaresUtils::defaultFontHeight() * 6;
|
||||
m_ui->selectedIconLabel->setPixmap(
|
||||
CalamaresUtils::defaultPixmap( imageType, CalamaresUtils::Original, QSize( iconSize, iconSize ) ) );
|
||||
m_ui->selectedIconLabel->setFixedHeight( iconSize );
|
||||
m_ui->selectedStatusLabel->setText( text );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::updateFromCurrentDevice( QComboBox* devicesComboBox )
|
||||
{
|
||||
QModelIndex index = m_core->deviceModel()->index( devicesComboBox->currentIndex(), 0 );
|
||||
if ( !index.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Device* device = m_core->deviceModel()->deviceForIndex( index );
|
||||
|
||||
QAbstractItemModel* oldModel = m_ui->partitionTreeView->model();
|
||||
if ( oldModel )
|
||||
{
|
||||
disconnect( oldModel, nullptr, this, nullptr );
|
||||
}
|
||||
|
||||
PartitionModel* model = m_core->partitionModelForDevice( device );
|
||||
m_ui->partitionTreeView->setModel( model );
|
||||
m_ui->partitionTreeView->expandAll();
|
||||
|
||||
// Must be done here because we need to have a model set to define
|
||||
// individual column resize mode
|
||||
QHeaderView* header = m_ui->partitionTreeView->header();
|
||||
header->setSectionResizeMode( QHeaderView::ResizeToContents );
|
||||
header->setSectionResizeMode( 0, QHeaderView::Stretch );
|
||||
|
||||
//updateButtons();
|
||||
// Establish connection here because selection model is destroyed when
|
||||
// model changes
|
||||
connect( m_ui->partitionTreeView->selectionModel(),
|
||||
&QItemSelectionModel::currentRowChanged,
|
||||
this,
|
||||
&ReplaceWidget::onPartitionViewActivated );
|
||||
|
||||
connect( model, &QAbstractItemModel::modelReset, this, &ReplaceWidget::onPartitionModelReset );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::onPartitionViewActivated()
|
||||
{
|
||||
QModelIndex index = m_ui->partitionTreeView->currentIndex();
|
||||
if ( !index.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const PartitionModel* model = static_cast< const PartitionModel* >( index.model() );
|
||||
Q_ASSERT( model );
|
||||
Partition* partition = model->partitionForIndex( index );
|
||||
Q_ASSERT( partition );
|
||||
|
||||
onPartitionSelected();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ReplaceWidget::onPartitionModelReset()
|
||||
{
|
||||
m_ui->partitionTreeView->expandAll();
|
||||
onPartitionSelected();
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
|
||||
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
||||
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef REPLACEWIDGET_H
|
||||
#define REPLACEWIDGET_H
|
||||
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
|
||||
#include <QScopedPointer>
|
||||
#include <QWidget>
|
||||
|
||||
class Ui_ReplaceWidget;
|
||||
class QComboBox;
|
||||
class PartitionCoreModule;
|
||||
class Partition;
|
||||
|
||||
class ReplaceWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ReplaceWidget( PartitionCoreModule* core, QComboBox* devicesComboBox, QWidget* parent = nullptr );
|
||||
virtual ~ReplaceWidget() override;
|
||||
|
||||
bool isNextEnabled() const;
|
||||
|
||||
void reset();
|
||||
|
||||
void applyChanges();
|
||||
|
||||
signals:
|
||||
void nextStatusChanged( bool );
|
||||
|
||||
private slots:
|
||||
void onPartitionSelected();
|
||||
|
||||
private:
|
||||
QScopedPointer< Ui_ReplaceWidget > m_ui;
|
||||
void setNextEnabled( bool enabled );
|
||||
|
||||
void updateStatus( CalamaresUtils::ImageType imageType, const QString& text );
|
||||
|
||||
PartitionCoreModule* m_core;
|
||||
|
||||
bool m_nextEnabled;
|
||||
|
||||
bool m_isEfi;
|
||||
|
||||
void updateFromCurrentDevice( QComboBox* devicesComboBox );
|
||||
void onPartitionViewActivated();
|
||||
void onPartitionModelReset();
|
||||
};
|
||||
|
||||
#endif // REPLACEWIDGET_H
|
@ -1,135 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<author>
|
||||
SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</author>
|
||||
<class>ReplaceWidget</class>
|
||||
<widget class="QWidget" name="ReplaceWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>643</width>
|
||||
<height>187</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="partitionTreeView">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="selectedIconLabel">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="selectedStatusLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="bootStatusLabel">
|
||||
<property name="toolTip">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="bootComboBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -26,6 +26,7 @@
|
||||
#include <kpmcore/core/partition.h>
|
||||
#include <kpmcore/fs/filesystem.h>
|
||||
#include <kpmcore/fs/luks.h>
|
||||
#include <kpmcore/fs/luks2.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
@ -91,11 +92,18 @@ mapForPartition( Partition* partition, const QString& uuid )
|
||||
map[ "parttype" ] = partition->type();
|
||||
map[ "partattrs" ] = partition->attributes();
|
||||
map[ "features" ] = partition->fileSystem().features();
|
||||
|
||||
if ( partition->fileSystem().type() == FileSystem::Luks
|
||||
&& dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() )
|
||||
{
|
||||
map[ "fs" ] = untranslatedFS( dynamic_cast< FS::luks& >( partition->fileSystem() ).innerFS() );
|
||||
}
|
||||
if ( partition->fileSystem().type() == FileSystem::Luks2
|
||||
&& dynamic_cast< FS::luks2& >( partition->fileSystem() ).innerFS() )
|
||||
{
|
||||
map[ "fs" ] = untranslatedFS( dynamic_cast< FS::luks2& >( partition->fileSystem() ).innerFS() );
|
||||
}
|
||||
|
||||
map[ "uuid" ] = uuid;
|
||||
map[ "claimed" ] = PartitionInfo::format( partition ); // If we formatted it, it's ours
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-FileCopyrightText: 2023 Evan James <dalto@fastmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
---
|
||||
$schema: https://json-schema.org/schema#
|
||||
@ -14,13 +15,16 @@ properties:
|
||||
# ensureSuspendToDisk: { type: boolean, default: true } # Legacy
|
||||
# neverCreateSwap: { type: boolean, default: false } # Legacy
|
||||
|
||||
allowZfsEncryption: { type: boolean, default: true }
|
||||
drawNestedPartitions: { type: boolean, default: false }
|
||||
alwaysShowPartitionLabels: { type: boolean, default: true }
|
||||
|
||||
defaultFileSystemType: { type: string }
|
||||
availableFileSystemTypes: { type: array, items: { type: string } }
|
||||
|
||||
luksGeneration: { type: string, enum: [luks1, luks2] } # Also allows "luks" as alias of "luks1"
|
||||
enableLuksAutomatedPartitioning: { type: boolean, default: false }
|
||||
|
||||
allowManualPartitioning: { type: boolean, default: true }
|
||||
partitionLayout: { type: array } # TODO: specify items
|
||||
initialPartitioningChoice: { type: string, enum: [ none, erase, replace, alongside, manual ] }
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
|
44
src/modules/zfshostid/main.py
Normal file
44
src/modules/zfshostid/main.py
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# === This file is part of Calamares - <https://calamares.io> ===
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2022 Anke Boersma <demm@kaosx.us>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Calamares is Free Software: see the License-Identifier above.
|
||||
#
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import libcalamares
|
||||
from libcalamares.utils import gettext_path, gettext_languages
|
||||
|
||||
|
||||
import gettext
|
||||
_ = gettext.translation("calamares-python",
|
||||
localedir=libcalamares.utils.gettext_path(),
|
||||
languages=libcalamares.utils.gettext_languages(),
|
||||
fallback=True).gettext
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("Copying zfs generated hostid.")
|
||||
|
||||
|
||||
def run():
|
||||
|
||||
zfs = libcalamares.globalstorage.value("zfsDatasets")
|
||||
root_mount_point = libcalamares.globalstorage.value("rootMountPoint")
|
||||
|
||||
if zfs:
|
||||
hostid_source = '/etc/hostid'
|
||||
hostid_destination = '{!s}/etc/hostid'.format(root_mount_point)
|
||||
|
||||
try:
|
||||
shutil.copy2(hostid_source, hostid_destination)
|
||||
except Exception as e:
|
||||
libcalamares.utils.warning("Could not copy hostid")
|
||||
|
||||
return None
|
8
src/modules/zfshostid/module.desc
Normal file
8
src/modules/zfshostid/module.desc
Normal file
@ -0,0 +1,8 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
type: "job"
|
||||
name: "zfshostid"
|
||||
interface: "python"
|
||||
script: "main.py"
|
||||
noconfig: true
|
7
src/modules/zfshostid/zfshostid.schema.yaml
Normal file
7
src/modules/zfshostid/zfshostid.schema.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2022 Anke Boersma <demm@kaosx.us>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
---
|
||||
$schema: https://json-schema.org/schema#
|
||||
$id: https://calamares.io/schemas/zfshostid
|
||||
additionalProperties: false
|
||||
type: object
|
Loading…
Reference in New Issue
Block a user