Merge branch 'calamares' into work-3.3

This commit is contained in:
Adriaan de Groot 2022-02-02 14:41:58 +01:00
commit d1e4740e70
90 changed files with 6934 additions and 1115 deletions

View File

@ -28,8 +28,9 @@ PointerAlignment: Left
ReflowComments: "false"
SortIncludes: "true"
SpaceAfterCStyleCast: "false"
SpaceInEmptyBlock: "false"
SpacesBeforeTrailingComments: "2"
SpacesInAngles: "true"
SpacesInParentheses: "true"
SpacesInSquareBrackets: "true"
Standard: Cpp11
Standard: c++17

View File

@ -1,35 +0,0 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
---
BasedOnStyle: WebKit
AlignAfterOpenBracket: Align
AlignEscapedNewlines: DontAlign
AllowAllParametersOfDeclarationOnNextLine: "false"
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: "false"
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: "false"
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: "false"
BinPackParameters: "false"
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: "true"
BreakConstructorInitializers: BeforeComma
ColumnLimit: 120
Cpp11BracedListStyle: "false"
FixNamespaceComments: "true"
IncludeBlocks: Preserve
IndentWidth: "4"
MaxEmptyLinesToKeep: "2"
NamespaceIndentation: None
PointerAlignment: Left
ReflowComments: "false"
SortIncludes: "true"
SpaceAfterCStyleCast: "false"
SpacesBeforeTrailingComments: "2"
SpacesInAngles: "true"
SpacesInParentheses: "true"
SpacesInSquareBrackets: "true"
Standard: Cpp11

View File

@ -7,22 +7,84 @@ contributors are listed. Note that Calamares does not have a historical
changelog -- this log starts with version 3.2.0. The release notes on the
website will have to do for older versions.
# 3.2.50 (unreleased) #
# 3.2.52 (unreleased) #
This release contains contributions from (alphabetically by first name):
- Erik Dubois
- No external contributors yet
## Core ##
- No core changes yet
## Modules ##
- No module changes yet
# 3.2.51 (2022-02-01) #
This release contains contributions from (alphabetically by first name):
- Evan James
**WARNING** The *umount* module has been rewritten in C++. Check your
configuration if you previously used the copy-a-log functionality.
## Core ##
- Evan has made a start on documenting which Global Storage keys there
are and how they tie modules together. This can be found in the
`src/modules/README.md` documentation.
## Modules ##
- *bootloader* can now be configured to try to generate a unique
suffix for the bootloader-id. #1820
- *grubcfg* now has a configurable default for kernel parameters,
which allows distributions to change the value from `quiet`.
The default, if nothing is set, remains `quiet`. Use an explicitly-
empty list to specify no-arguments-at-all.
- *packagechooser* can now export its choices for use by the *netinstall*
module. This makes it possible to use *packagechooser* for large-scale
choices, followed by *netinstall* for fine-grained control. (Thanks Evan)
- When the *partition* module has a conflicting configuration for the
swap choices, it prints a warning and uses the configured choice, rather
than always using "suspend". #1881
- The *umount* module has been re-written in C++. The copy-a-log-file
functionality has been removed. Use the *preservefiles* module for that.
# 3.2.50 (2022-01-18) #
This release contains contributions from (alphabetically by first name):
- Anke Boersma
- Erik Dubois
- Evan James
- Johannes Kamprad
- Taejun Park (new contributor, welcome!)
**Replacement notice:** The *umount* module will be replaced by a C++
implementation in the next release. The "preserve log file" feature
will be removed in that release. Use the *preservefiles* module instead.
## Core ##
- No core changes yet
## Modules ##
- *initcpiocfg* mentioned a special kernel-name "all", which did not work,
and a special kernel-name "$uname" which cannot work. Fixed the former
and removed the "$uname" special key. (Thanks Evan)
- *luksswaphookcfg* has been converted to a C++ module.
- *networkcfg* could fail to update the NetworkManager configuration
if the SSID or username contained non-ASCII characters **and** the
default Python text-file encoding was set to ASCII. The files are
now read and written in UTF-8, explicitly. #1848
- *partition* always sets *bigtime* option on XFS filesystems, if possible.
Requires sufficiently-recent xfsprogs. #1874
- *preservefiles* was missing some necessary features, needed for it
to replace the deprecated log-file-saving functionality in the *umount*
module. (Thanks Erik and Joe for testing) #1851
- *umount* is now marked as an emergency module in the example configuration,
since it should **probably** be run as a cleanup. (Thanks Evan)
- *welcome* and *locale* could be confusing, together, and configure
the target system with a language that does not match the installer
language, even though the user did not make any explicit choice.
(Thanks Taejun) #1864
# 3.2.49.1 (2021-12-11) #

View File

@ -75,7 +75,7 @@ fi
#
#
BUILDDIR=$(mktemp -d --suffix=-build --tmpdir=.)
KEY_ID="CFDDC96F12B1915C"
KEY_ID="328D742D8807A435"
# Try to make gpg cache the signing key, so we can leave the process
# to run and sign.

View File

@ -19,12 +19,12 @@ BASEDIR=$(dirname $0)
TOPDIR=$( cd $BASEDIR/.. && pwd -P )
test -d "$BASEDIR" || { echo "! Could not determine base for $0" ; exit 1 ; }
test -d "$TOPDIR" || { echo "! Cound not determine top-level source dir" ; exit 1 ; }
test -f "$TOPDIR/.clang-format.base" || { echo "! No .clang-format support files in $TOPDIR" ; exit 1 ; }
test -f "$TOPDIR/.clang-format" || { echo "! No .clang-format support files in $TOPDIR" ; exit 1 ; }
AS=$( which astyle )
# Allow specifying CF_VERSIONS outside already
CF_VERSIONS="$CF_VERSIONS clang-format-8 clang-format80 clang-format90 clang-format-9.0.1 clang-format"
CF_VERSIONS="$CF_VERSIONS clang-format13 clang-format12 clang-format"
for _cf in $CF_VERSIONS
do
# Not an error if this particular clang-format isn't found
@ -40,31 +40,19 @@ test -x "$CF" || { echo "! $CF is not executable."; exit 1 ; }
### CLANG-FORMAT-WRANGLING
#
# Version 7 and earlier doesn't understand all the options we would like
# Version 8 is ok
# Version 9 is ok
# Later versions change some defaults so need extra wrangling.
# .. there are extra files that are appended to the settings, per
# .. clang-format version.
# Version 12 handles lambdas nicely, so use that.
# Version 13 is also ok.
format_version=`"$CF" --version | tr -dc '[^.0-9]' | cut -d . -f 1`
case "$format_version" in
[0-7] )
echo "! Clang-format version 8+ required"
exit 1
;;
[89] )
12|13 )
:
;;
* )
echo "! Clang-format version '$format_version' unsupported."
echo "! Clang-format version '$format_version' unsupported, version 12 required."
exit 1
;;
esac
_fmt="$TOPDIR/.clang-format"
cp "$_fmt.base" "$_fmt"
for f in "$extra_settings" ; do
test -f "$_fmt.$f" && cat "$_fmt.$f" >> "$_fmt"
done
### FILE PROCESSING
@ -98,8 +86,3 @@ if test "x$any_dirs" = "xyes" ; then
else
style_some "$@"
fi
### CLANG-FORMAT-WRANGLING
#
# Restore the original .clang-format
cp "$_fmt.base" "$_fmt"

View File

@ -693,27 +693,27 @@ Instalační program bude ukončen a všechny změny ztraceny.</translation>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
<source>Successfully unmounted %1.</source>
<translation type="unfinished"/>
<translation>Úspěšně odpojeno %1.</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
<source>Successfully disabled swap %1.</source>
<translation type="unfinished"/>
<translation>Úspěšně vypnut swap %1.</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
<source>Successfully cleared swap %1.</source>
<translation type="unfinished"/>
<translation>Úspěšně vyčištěn swap %1.</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
<source>Successfully closed mapper device %1.</source>
<translation type="unfinished"/>
<translation>Úspěšně zavřeno mapper zařízení %1.</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
<source>Successfully disabled volume group %1.</source>
<translation type="unfinished"/>
<translation>Úspěšně vypnuta skupina svazků %1.</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>

File diff suppressed because it is too large Load Diff

4366
lang/calamares_ja-Hira.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2014,7 +2014,7 @@ O instalador será encerrado e todas as alterações serão perdidas.</translati
<source>Please select your preferred location on the map so the installer can suggest the locale
and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging
to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming.</source>
<translation>Por favor selecione o seu local preferido no mapa para que o instalador possa sugerir a localização
<translation>Selecione o seu local preferido no mapa para que o instalador possa sugerir a localização
e fuso horário para si. Pode ajustar as definições sugeridas abaixo. Procure no mapa arrastando
para mover e utilizando os botões +/- para aumentar/diminuir ou utilize a roda do rato para dar zoom.</translation>
</message>
@ -4003,7 +4003,7 @@ Saída de Dados:
<message>
<location filename="../src/modules/welcome/WelcomePage.cpp" line="238"/>
<source>&lt;h1&gt;%1&lt;/h1&gt;&lt;br/&gt;&lt;strong&gt;%2&lt;br/&gt;for %3&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;Copyright 2014-2017 Teo Mrnjavac &amp;lt;teo@kde.org&amp;gt;&lt;br/&gt;Copyright 2017-2020 Adriaan de Groot &amp;lt;groot@kde.org&amp;gt;&lt;br/&gt;Thanks to &lt;a href="https://calamares.io/team/"&gt;the Calamares team&lt;/a&gt; and the &lt;a href="https://www.transifex.com/calamares/calamares/"&gt;Calamares translators team&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;&lt;a href="https://calamares.io/"&gt;Calamares&lt;/a&gt; development is sponsored by &lt;br/&gt;&lt;a href="http://www.blue-systems.com/"&gt;Blue Systems&lt;/a&gt; - Liberating Software.</source>
<translation>&lt;h1&gt;%1&lt;/h1&gt;&lt;br/&gt;&lt;strong&gt;%2&lt;br/&gt;para %3&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;Copyright 2014-2017 Teo Mrnjavac &amp;lt;teo@kde.org&amp;gt;&lt;br/&gt;Copyright 2017-2020 Adriaan de Groot &amp;lt;groot@kde.org&amp;gt;&lt;br/&gt;Obrigado à &lt;a href="https://calamares.io/team/"&gt;equipa Calamares&lt;/a&gt; e à &lt;a href="https://www.transifex.com/calamares/calamares/"&gt;equipa de tradutores do Calamares&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;O desenvolvimento do &lt;a href="https://calamares.io/"&gt;Calamares&lt;/a&gt; é patrocinado pela &lt;br/&gt;&lt;a href="http://www.blue-systems.com/"&gt;Blue Systems&lt;/a&gt; - Liberating Software.</translation>
<translation>&lt;h1&gt;%1&lt;/h1&gt;&lt;br/&gt;&lt;strong&gt;%2&lt;br/&gt;para o %3&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;Copyright 2014-2017 Teo Mrnjavac &amp;lt;teo@kde.org&amp;gt;&lt;br/&gt;Copyright 2017-2020 Adriaan de Groot &amp;lt;groot@kde.org&amp;gt;&lt;br/&gt;Obrigado à &lt;a href="https://calamares.io/team/"&gt;equipa Calamares&lt;/a&gt; e à &lt;a href="https://www.transifex.com/calamares/calamares/"&gt;equipa de tradutores do Calamares&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;O desenvolvimento do &lt;a href="https://calamares.io/"&gt;Calamares&lt;/a&gt; é patrocinado pela &lt;br/&gt;&lt;a href="http://www.blue-systems.com/"&gt;Blue Systems&lt;/a&gt; - Liberating Software.</translation>
</message>
</context>
<context>

View File

@ -104,17 +104,17 @@
<message>
<location filename="../src/calamares/DebugWindow.ui" line="102"/>
<source>Crashes Calamares, so that Dr. Konqui can look at it.</source>
<translation type="unfinished"/>
<translation> crash lui Calamares, pentru ca doctorul Konqui se uite la el.</translation>
</message>
<message>
<location filename="../src/calamares/DebugWindow.ui" line="115"/>
<source>Reloads the stylesheet from the branding directory.</source>
<translation type="unfinished"/>
<translation>Reîncarcă foaia de stil din directorul branding.</translation>
</message>
<message>
<location filename="../src/calamares/DebugWindow.ui" line="141"/>
<source>Uploads the session log to the configured pastebin.</source>
<translation type="unfinished"/>
<translation>Încarcă jurnalul sesiunii pe pastebin-ul configurat.</translation>
</message>
<message>
<location filename="../src/calamares/DebugWindow.ui" line="144"/>
@ -134,7 +134,7 @@
<message>
<location filename="../src/calamares/DebugWindow.ui" line="131"/>
<source>Widget Tree</source>
<translation>Lista widget</translation>
<translation>Arborele de widget</translation>
</message>
<message>
<location filename="../src/calamares/DebugWindow.cpp" line="221"/>

View File

@ -6,7 +6,7 @@
<message>
<location filename="../src/modules/partition/jobs/AutoMountManagementJob.cpp" line="22"/>
<source>Manage auto-mount settings</source>
<translation type="unfinished"/>
<translation>Идора кардани танзимоти васлкунии худкор</translation>
</message>
</context>
<context>

View File

@ -688,27 +688,27 @@ The installer will quit and all changes will be lost.</source>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
<source>Successfully unmounted %1.</source>
<translation type="unfinished"/>
<translation> %1</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
<source>Successfully disabled swap %1.</source>
<translation type="unfinished"/>
<translation> %1</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
<source>Successfully cleared swap %1.</source>
<translation type="unfinished"/>
<translation> %1</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
<source>Successfully closed mapper device %1.</source>
<translation type="unfinished"/>
<translation> %1</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
<source>Successfully disabled volume group %1.</source>
<translation type="unfinished"/>
<translation> %1</translation>
</message>
<message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>

View File

@ -8,403 +8,397 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-02 15:45+0100\n"
"POT-Creation-Date: 2022-02-01 17:27+0100\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=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: src/modules/initramfscfg/main.py:32
msgid "Configuring initramfs."
msgstr "Configuring initramfs."
msgstr ""
#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89
#: src/modules/fstab/main.py:355 src/modules/fstab/main.py:361
#: src/modules/fstab/main.py:388 src/modules/networkcfg/main.py:105
#: src/modules/initcpiocfg/main.py:227 src/modules/initcpiocfg/main.py:231
#: src/modules/localecfg/main.py:135 src/modules/mount/main.py:144
#: src/modules/fstab/main.py:360 src/modules/fstab/main.py:366
#: src/modules/fstab/main.py:393 src/modules/networkcfg/main.py:105
#: src/modules/initcpiocfg/main.py:235 src/modules/initcpiocfg/main.py:239
#: src/modules/localecfg/main.py:135 src/modules/mount/main.py:229
#: src/modules/rawfs/main.py:164 src/modules/openrcdmcryptcfg/main.py:72
#: src/modules/openrcdmcryptcfg/main.py:76
#: src/modules/luksopenswaphookcfg/main.py:86
#: src/modules/luksopenswaphookcfg/main.py:90
msgid "Configuration Error"
msgstr "Configuration Error"
msgstr ""
#: src/modules/initramfscfg/main.py:86 src/modules/fstab/main.py:356
#: src/modules/initcpiocfg/main.py:228 src/modules/mount/main.py:145
#: src/modules/initramfscfg/main.py:86 src/modules/fstab/main.py:361
#: src/modules/initcpiocfg/main.py:236 src/modules/mount/main.py:230
#: src/modules/rawfs/main.py:165 src/modules/openrcdmcryptcfg/main.py:73
#: src/modules/luksopenswaphookcfg/main.py:87
msgid "No partitions are defined for <pre>{!s}</pre> to use."
msgstr "No partitions are defined for <pre>{!s}</pre> to use."
msgstr ""
#: src/modules/initramfscfg/main.py:90 src/modules/fstab/main.py:362
#: src/modules/networkcfg/main.py:106 src/modules/initcpiocfg/main.py:232
#: src/modules/initramfscfg/main.py:90 src/modules/fstab/main.py:367
#: src/modules/networkcfg/main.py:106 src/modules/initcpiocfg/main.py:240
#: src/modules/localecfg/main.py:136 src/modules/openrcdmcryptcfg/main.py:77
#: src/modules/luksopenswaphookcfg/main.py:91
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."
msgstr ""
#: src/modules/grubcfg/main.py:28
msgid "Configure GRUB."
msgstr "Configure GRUB."
msgstr ""
#: src/modules/bootloader/main.py:43
msgid "Install bootloader."
msgstr "Install bootloader."
#: src/modules/bootloader/main.py:508
msgid "Bootloader installation error"
msgstr "Bootloader installation error"
#: src/modules/bootloader/main.py:509
msgid ""
"The bootloader could not be installed. The installation command "
"<pre>{!s}</pre> returned error code {!s}."
msgstr ""
"The bootloader could not be installed. The installation command "
"<pre>{!s}</pre> returned error code {!s}."
#: src/modules/bootloader/main.py:612
msgid "Failed to install grub, no partitions defined in global storage"
msgstr ""
#: src/modules/bootloader/main.py:780
msgid "Bootloader installation error"
msgstr ""
#: src/modules/bootloader/main.py:781
msgid ""
"The bootloader could not be installed. The installation command <pre>{!s}</"
"pre> returned error code {!s}."
msgstr ""
#: src/modules/fstab/main.py:29
msgid "Writing fstab."
msgstr "Writing fstab."
msgstr ""
#: src/modules/fstab/main.py:389
#: src/modules/fstab/main.py:394
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."
msgstr ""
#: src/modules/dracut/main.py:27
msgid "Creating initramfs with dracut."
msgstr "Creating initramfs with dracut."
msgstr ""
#: src/modules/dracut/main.py:49
msgid "Failed to run dracut on the target"
msgstr "Failed to run dracut on the target"
msgstr ""
#: src/modules/dracut/main.py:50 src/modules/mkinitfs/main.py:50
msgid "The exit code was {}"
msgstr "The exit code was {}"
msgstr ""
#: src/modules/displaymanager/main.py:526
#: src/modules/displaymanager/main.py:524
msgid "Cannot write KDM configuration file"
msgstr "Cannot write KDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:527
#: src/modules/displaymanager/main.py:525
msgid "KDM config file {!s} does not exist"
msgstr "KDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:588
#: src/modules/displaymanager/main.py:586
msgid "Cannot write LXDM configuration file"
msgstr "Cannot write LXDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:589
#: src/modules/displaymanager/main.py:587
msgid "LXDM config file {!s} does not exist"
msgstr "LXDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:672
#: src/modules/displaymanager/main.py:670
msgid "Cannot write LightDM configuration file"
msgstr "Cannot write LightDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:673
#: src/modules/displaymanager/main.py:671
msgid "LightDM config file {!s} does not exist"
msgstr "LightDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:747
#: src/modules/displaymanager/main.py:745
msgid "Cannot configure LightDM"
msgstr "Cannot configure LightDM"
msgstr ""
#: src/modules/displaymanager/main.py:748
#: src/modules/displaymanager/main.py:746
msgid "No LightDM greeter installed."
msgstr "No LightDM greeter installed."
msgstr ""
#: src/modules/displaymanager/main.py:779
#: src/modules/displaymanager/main.py:777
msgid "Cannot write SLIM configuration file"
msgstr "Cannot write SLIM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:780
#: src/modules/displaymanager/main.py:778
msgid "SLIM config file {!s} does not exist"
msgstr "SLIM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:906
#: src/modules/displaymanager/main.py:991
msgid "No display managers selected for the displaymanager module."
msgstr "No display managers selected for the displaymanager module."
msgstr ""
#: src/modules/displaymanager/main.py:907
#: src/modules/displaymanager/main.py:992
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:989
#: src/modules/displaymanager/main.py:1074
msgid "Display manager configuration was incomplete"
msgstr "Display manager configuration was incomplete"
msgstr ""
#: src/modules/services-openrc/main.py:29
msgid "Configure OpenRC services"
msgstr "Configure OpenRC services"
msgstr ""
#: 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}."
msgstr ""
#: 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}."
msgstr ""
#: 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:93
#: src/modules/services-systemd/main.py:59
msgid "Cannot modify service"
msgstr "Cannot modify service"
msgstr ""
#: src/modules/services-openrc/main.py:94
msgid ""
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
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"
msgstr ""
#: src/modules/services-openrc/main.py:102
msgid ""
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
"exist."
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"
msgstr ""
#: src/modules/services-openrc/main.py:111
msgid ""
"The path for service {name!s} is <code>{path!s}</code>, which does not "
"exist."
"The path for service {name!s} is <code>{path!s}</code>, which does not exist."
msgstr ""
"The path for service {name!s} is <code>{path!s}</code>, which does not "
"exist."
#: src/modules/networkcfg/main.py:29
msgid "Saving network configuration."
msgstr "Saving network configuration."
msgstr ""
#: src/modules/packages/main.py:50 src/modules/packages/main.py:59
#: src/modules/packages/main.py:69
#: 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:57
#: 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:62
#: 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:65
#: 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:638 src/modules/packages/main.py:650
#: src/modules/packages/main.py:678
#: 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:639
#: 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:651
#: src/modules/packages/main.py:738
msgid ""
"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:679
#: 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/plymouthcfg/main.py:27
msgid "Configure Plymouth theme"
msgstr "Configure Plymouth theme"
msgstr ""
#: src/modules/initcpiocfg/main.py:28
msgid "Configuring mkinitcpio."
msgstr "Configuring mkinitcpio."
msgstr ""
#: src/modules/localecfg/main.py:30
msgid "Configuring locales."
msgstr "Configuring locales."
msgstr ""
#: src/modules/mount/main.py:30
#: src/modules/mount/main.py:42
msgid "Mounting partitions."
msgstr "Mounting partitions."
msgstr ""
#: src/modules/mount/main.py:88 src/modules/mount/main.py:124
msgid "Internal error mounting zfs datasets"
msgstr ""
#: src/modules/mount/main.py:100
msgid "Failed to import zpool"
msgstr ""
#: src/modules/mount/main.py:116
msgid "Failed to unlock zpool"
msgstr ""
#: src/modules/mount/main.py:133 src/modules/mount/main.py:138
msgid "Failed to set zfs mountpoint"
msgstr ""
#: src/modules/mount/main.py:253
msgid "zfs mounting error"
msgstr ""
#: src/modules/rawfs/main.py:26
msgid "Installing data."
msgstr "Installing data."
msgstr ""
#: src/modules/dummypython/main.py:35
msgid "Dummy python job."
msgstr "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 "Dummy python step {}"
msgstr ""
#: src/modules/hwclock/main.py:26
msgid "Setting hardware clock."
msgstr "Setting hardware clock."
#: src/modules/umount/main.py:31
msgid "Unmount file systems."
msgstr "Unmount file systems."
msgstr ""
#: src/modules/openrcdmcryptcfg/main.py:26
msgid "Configuring OpenRC dmcrypt service."
msgstr "Configuring OpenRC dmcrypt service."
msgstr ""
#: src/modules/services-systemd/main.py:26
msgid "Configure systemd services"
msgstr "Configure systemd services"
msgstr ""
#: src/modules/services-systemd/main.py:60
msgid ""
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
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>."
msgstr ""
#: 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>."
msgstr ""
#: 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>."
msgstr ""
#: 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>."
msgstr ""
#: 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>."
msgstr ""
#: src/modules/services-systemd/main.py:75
msgid ""
"Unknown systemd commands <code>{command!s}</code> and "
"<code>{suffix!s}</code> for unit {name!s}."
"Unknown systemd commands <code>{command!s}</code> and <code>{suffix!s}</"
"code> for unit {name!s}."
msgstr ""
"Unknown systemd commands <code>{command!s}</code> and "
"<code>{suffix!s}</code> for unit {name!s}."
#: src/modules/mkinitfs/main.py:27
msgid "Creating initramfs with mkinitfs."
msgstr "Creating initramfs with mkinitfs."
msgstr ""
#: src/modules/mkinitfs/main.py:49
msgid "Failed to run mkinitfs on the target"
msgstr "Failed to run mkinitfs on the target"
msgstr ""
#: src/modules/unpackfs/main.py:34
msgid "Filling up filesystems."
msgstr "Filling up filesystems."
msgstr ""
#: src/modules/unpackfs/main.py:254
msgid "rsync failed with error code {}."
msgstr "rsync failed with error code {}."
msgstr ""
#: src/modules/unpackfs/main.py:299
msgid "Unpacking image {}/{}, file {}/{}"
msgstr "Unpacking image {}/{}, file {}/{}"
msgstr ""
#: src/modules/unpackfs/main.py:314
msgid "Starting to unpack {}"
msgstr "Starting to unpack {}"
msgstr ""
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:465
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:467
msgid "Failed to unpack image \"{}\""
msgstr "Failed to unpack image \"{}\""
msgstr ""
#: src/modules/unpackfs/main.py:430
msgid "No mount point for root partition"
msgstr "No mount point for root partition"
msgstr ""
#: src/modules/unpackfs/main.py:431
msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing"
msgstr "globalstorage does not contain a \"rootMountPoint\" key, doing nothing"
msgid "globalstorage does not contain a \"rootMountPoint\" key."
msgstr ""
#: src/modules/unpackfs/main.py:436
#: src/modules/unpackfs/main.py:434
msgid "Bad mount point for root partition"
msgstr "Bad mount point for root partition"
msgstr ""
#: src/modules/unpackfs/main.py:437
msgid "rootMountPoint is \"{}\", which does not exist, doing nothing"
msgstr "rootMountPoint is \"{}\", which does not exist, doing nothing"
#: src/modules/unpackfs/main.py:435
msgid "rootMountPoint is \"{}\", which does not exist."
msgstr ""
#: src/modules/unpackfs/main.py:453 src/modules/unpackfs/main.py:457
#: src/modules/unpackfs/main.py:463 src/modules/unpackfs/main.py:478
msgid "Bad unsquash configuration"
msgstr "Bad unsquash configuration"
#: 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:454
#: 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 "The filesystem for \"{}\" ({}) is not supported by your current kernel"
msgstr ""
#: src/modules/unpackfs/main.py:458
#: src/modules/unpackfs/main.py:460
msgid "The source filesystem \"{}\" does not exist"
msgstr "The source filesystem \"{}\" does not exist"
msgstr ""
#: src/modules/unpackfs/main.py:464
#: src/modules/unpackfs/main.py:466
msgid ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
msgstr ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
#: src/modules/unpackfs/main.py:479
#: 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/luksopenswaphookcfg/main.py:26
msgid "Configuring encrypted swap."
msgstr "Configuring encrypted swap."
msgstr ""

View File

@ -6,7 +6,7 @@
# Translators:
# pavelrz, 2017
# LiberteCzech <martin.kriz.czech@gmail.com>, 2020
# Pavel Borecki <pavel.borecki@gmail.com>, 2021
# Pavel Borecki <pavel.borecki@gmail.com>, 2022
#
#, fuzzy
msgid ""
@ -15,7 +15,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-02 15:45+0100\n"
"PO-Revision-Date: 2017-08-09 10:34+0000\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>, 2021\n"
"Last-Translator: Pavel Borecki <pavel.borecki@gmail.com>, 2022\n"
"Language-Team: Czech (Czech Republic) (https://www.transifex.com/calamares/teams/20061/cs_CZ/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -331,7 +331,7 @@ msgstr "Nedaří se zapnout systemd službu <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:67
msgid "Cannot enable systemd timer <code>{name!s}</code>."
msgstr ""
msgstr "Nedaří se zapnout systemd časovač <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:71
msgid "Cannot disable systemd target <code>{name!s}</code>."
@ -413,6 +413,8 @@ msgid ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
msgstr ""
"Nepodařilo se nalézt nástroj unsquashfs ověřte, že je nainstalovaný "
"balíček squashfs-tools."
#: src/modules/unpackfs/main.py:479
msgid "The destination \"{}\" in the target system is not a directory"

View File

@ -0,0 +1,385 @@
# SOME DESCRIPTIVE TITLE.
# 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: 2021-11-02 15:45+0100\n"
"PO-Revision-Date: 2017-08-09 10:34+0000\n"
"Language-Team: Japanese (Hiragana) (https://www.transifex.com/calamares/teams/20061/ja-Hira/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ja-Hira\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: src/modules/initramfscfg/main.py:32
msgid "Configuring initramfs."
msgstr ""
#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89
#: src/modules/fstab/main.py:355 src/modules/fstab/main.py:361
#: src/modules/fstab/main.py:388 src/modules/networkcfg/main.py:105
#: src/modules/initcpiocfg/main.py:227 src/modules/initcpiocfg/main.py:231
#: src/modules/localecfg/main.py:135 src/modules/mount/main.py:144
#: src/modules/rawfs/main.py:164 src/modules/openrcdmcryptcfg/main.py:72
#: src/modules/openrcdmcryptcfg/main.py:76
#: src/modules/luksopenswaphookcfg/main.py:86
#: src/modules/luksopenswaphookcfg/main.py:90
msgid "Configuration Error"
msgstr ""
#: src/modules/initramfscfg/main.py:86 src/modules/fstab/main.py:356
#: src/modules/initcpiocfg/main.py:228 src/modules/mount/main.py:145
#: src/modules/rawfs/main.py:165 src/modules/openrcdmcryptcfg/main.py:73
#: src/modules/luksopenswaphookcfg/main.py:87
msgid "No partitions are defined for <pre>{!s}</pre> to use."
msgstr ""
#: src/modules/initramfscfg/main.py:90 src/modules/fstab/main.py:362
#: src/modules/networkcfg/main.py:106 src/modules/initcpiocfg/main.py:232
#: src/modules/localecfg/main.py:136 src/modules/openrcdmcryptcfg/main.py:77
#: src/modules/luksopenswaphookcfg/main.py:91
msgid "No root mount point is given for <pre>{!s}</pre> to use."
msgstr ""
#: src/modules/grubcfg/main.py:28
msgid "Configure GRUB."
msgstr ""
#: src/modules/bootloader/main.py:43
msgid "Install bootloader."
msgstr ""
#: src/modules/bootloader/main.py:508
msgid "Bootloader installation error"
msgstr ""
#: src/modules/bootloader/main.py:509
msgid ""
"The bootloader could not be installed. The installation command "
"<pre>{!s}</pre> returned error code {!s}."
msgstr ""
#: src/modules/fstab/main.py:29
msgid "Writing fstab."
msgstr ""
#: src/modules/fstab/main.py:389
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
msgstr ""
#: src/modules/dracut/main.py:27
msgid "Creating initramfs with dracut."
msgstr ""
#: src/modules/dracut/main.py:49
msgid "Failed to run dracut on the target"
msgstr ""
#: src/modules/dracut/main.py:50 src/modules/mkinitfs/main.py:50
msgid "The exit code was {}"
msgstr ""
#: src/modules/displaymanager/main.py:526
msgid "Cannot write KDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:527
msgid "KDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:588
msgid "Cannot write LXDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:589
msgid "LXDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:672
msgid "Cannot write LightDM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:673
msgid "LightDM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:747
msgid "Cannot configure LightDM"
msgstr ""
#: src/modules/displaymanager/main.py:748
msgid "No LightDM greeter installed."
msgstr ""
#: src/modules/displaymanager/main.py:779
msgid "Cannot write SLIM configuration file"
msgstr ""
#: src/modules/displaymanager/main.py:780
msgid "SLIM config file {!s} does not exist"
msgstr ""
#: src/modules/displaymanager/main.py:906
msgid "No display managers selected for the displaymanager module."
msgstr ""
#: src/modules/displaymanager/main.py:907
msgid ""
"The displaymanagers list is empty or undefined in both globalstorage and "
"displaymanager.conf."
msgstr ""
#: src/modules/displaymanager/main.py:989
msgid "Display manager configuration was incomplete"
msgstr ""
#: src/modules/services-openrc/main.py:29
msgid "Configure OpenRC services"
msgstr ""
#: src/modules/services-openrc/main.py:57
msgid "Cannot add service {name!s} to run-level {level!s}."
msgstr ""
#: src/modules/services-openrc/main.py:59
msgid "Cannot remove service {name!s} from run-level {level!s}."
msgstr ""
#: 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/services-openrc/main.py:93
#: src/modules/services-systemd/main.py:59
msgid "Cannot modify service"
msgstr ""
#: 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/services-openrc/main.py:101
msgid "Target runlevel does not exist"
msgstr ""
#: 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/services-openrc/main.py:110
msgid "Target service does not exist"
msgstr ""
#: 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/networkcfg/main.py:29
msgid "Saving network configuration."
msgstr ""
#: src/modules/packages/main.py:50 src/modules/packages/main.py:59
#: src/modules/packages/main.py:69
msgid "Install packages."
msgstr ""
#: src/modules/packages/main.py:57
#, python-format
msgid "Processing packages (%(count)d / %(total)d)"
msgstr ""
#: src/modules/packages/main.py:62
#, python-format
msgid "Installing one package."
msgid_plural "Installing %(num)d packages."
msgstr[0] ""
#: src/modules/packages/main.py:65
#, python-format
msgid "Removing one package."
msgid_plural "Removing %(num)d packages."
msgstr[0] ""
#: src/modules/packages/main.py:638 src/modules/packages/main.py:650
#: src/modules/packages/main.py:678
msgid "Package Manager error"
msgstr ""
#: src/modules/packages/main.py:639
msgid ""
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
"returned error code {!s}."
msgstr ""
#: src/modules/packages/main.py:651
msgid ""
"The package manager could not update the system. The command <pre>{!s}</pre>"
" returned error code {!s}."
msgstr ""
#: src/modules/packages/main.py:679
msgid ""
"The package manager could not make changes to the installed system. The "
"command <pre>{!s}</pre> returned error code {!s}."
msgstr ""
#: src/modules/plymouthcfg/main.py:27
msgid "Configure Plymouth theme"
msgstr ""
#: src/modules/initcpiocfg/main.py:28
msgid "Configuring mkinitcpio."
msgstr ""
#: src/modules/localecfg/main.py:30
msgid "Configuring locales."
msgstr ""
#: src/modules/mount/main.py:30
msgid "Mounting partitions."
msgstr ""
#: src/modules/rawfs/main.py:26
msgid "Installing data."
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/hwclock/main.py:26
msgid "Setting hardware clock."
msgstr ""
#: src/modules/umount/main.py:31
msgid "Unmount file systems."
msgstr ""
#: src/modules/openrcdmcryptcfg/main.py:26
msgid "Configuring OpenRC dmcrypt service."
msgstr ""
#: src/modules/services-systemd/main.py:26
msgid "Configure systemd services"
msgstr ""
#: src/modules/services-systemd/main.py:60
msgid ""
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
msgstr ""
#: src/modules/services-systemd/main.py:63
#: src/modules/services-systemd/main.py:69
msgid "Cannot enable systemd service <code>{name!s}</code>."
msgstr ""
#: src/modules/services-systemd/main.py:65
msgid "Cannot enable systemd target <code>{name!s}</code>."
msgstr ""
#: src/modules/services-systemd/main.py:67
msgid "Cannot enable systemd timer <code>{name!s}</code>."
msgstr ""
#: src/modules/services-systemd/main.py:71
msgid "Cannot disable systemd target <code>{name!s}</code>."
msgstr ""
#: src/modules/services-systemd/main.py:73
msgid "Cannot mask systemd unit <code>{name!s}</code>."
msgstr ""
#: src/modules/services-systemd/main.py:75
msgid ""
"Unknown systemd commands <code>{command!s}</code> and "
"<code>{suffix!s}</code> for unit {name!s}."
msgstr ""
#: 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/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:465
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, doing nothing"
msgstr ""
#: src/modules/unpackfs/main.py:436
msgid "Bad mount point for root partition"
msgstr ""
#: src/modules/unpackfs/main.py:437
msgid "rootMountPoint is \"{}\", which does not exist, doing nothing"
msgstr ""
#: src/modules/unpackfs/main.py:453 src/modules/unpackfs/main.py:457
#: src/modules/unpackfs/main.py:463 src/modules/unpackfs/main.py:478
msgid "Bad unsquash configuration"
msgstr ""
#: src/modules/unpackfs/main.py:454
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
msgstr ""
#: src/modules/unpackfs/main.py:458
msgid "The source filesystem \"{}\" does not exist"
msgstr ""
#: src/modules/unpackfs/main.py:464
msgid ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
msgstr ""
#: src/modules/unpackfs/main.py:479
msgid "The destination \"{}\" in the target system is not a directory"
msgstr ""
#: src/modules/luksopenswaphookcfg/main.py:26
msgid "Configuring encrypted swap."
msgstr ""

View File

@ -9,6 +9,7 @@
# Feng Chao <chaofeng111@qq.com>, 2020
# Bobby Rong <admin@bobby285271.top>, 2020
# 玉堂白鹤 <yjwork@qq.com>, 2021
# Giovanni Schiano-Moriello, 2022
#
#, fuzzy
msgid ""
@ -17,7 +18,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-02 15:45+0100\n"
"PO-Revision-Date: 2017-08-09 10:34+0000\n"
"Last-Translator: 玉堂白鹤 <yjwork@qq.com>, 2021\n"
"Last-Translator: Giovanni Schiano-Moriello, 2022\n"
"Language-Team: Chinese (China) (https://www.transifex.com/calamares/teams/20061/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -305,7 +306,7 @@ msgstr "无法启用 systemd 目标 <code>{name!s}</code>."
#: src/modules/services-systemd/main.py:67
msgid "Cannot enable systemd timer <code>{name!s}</code>."
msgstr ""
msgstr "无法启用 systemd 计时器 <code>{name!s}</code>。"
#: src/modules/services-systemd/main.py:71
msgid "Cannot disable systemd target <code>{name!s}</code>."
@ -384,7 +385,7 @@ msgstr "源文件系统 \"{}\" 不存在"
msgid ""
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
"installed."
msgstr ""
msgstr "寻找 unsquashfs 失败,请确定您已安装 squashfs-tools 软体包。"
#: src/modules/unpackfs/main.py:479
msgid "The destination \"{}\" in the target system is not a directory"

View File

@ -485,7 +485,7 @@ main( int argc, char* argv[] )
return 1;
}
cDebug() << Logger::SubEntry << " .. got" << m->name() << m->typeString() << m->interfaceString();
cDebug() << Logger::SubEntry << "got" << m->name() << m->typeString() << m->interfaceString();
if ( m->type() == Calamares::Module::Type::View )
{
// If we forgot the --ui, any ViewModule will core dump as it
@ -535,7 +535,7 @@ main( int argc, char* argv[] )
using TR = Logger::DebugRow< const char*, const QString >;
cDebug() << "Module metadata" << TR( "name", m->name() ) << TR( "type", m->typeString() )
cDebug() << Logger::SubEntry << "Module metadata" << TR( "name", m->name() ) << TR( "type", m->typeString() )
<< TR( "interface", m->interfaceString() );
Calamares::JobList jobList = m->jobs();
@ -543,6 +543,8 @@ main( int argc, char* argv[] )
unsigned int count = 1;
for ( const auto& p : jobList )
{
// This doesn't get a SubEntry because the jobs may log a bunch of
// things; print the function-header to make clear that we're back in main.
cDebug() << "Job #" << count << "name" << p->prettyName();
Calamares::JobResult r = p->exec();
if ( !r )

View File

@ -47,7 +47,7 @@ public:
/** @brief Is this JobResult a success?
*
* Equivalent to errorCode() == 0, might be named isValid().
* Equivalent to errorCode() == 0, see succeeded().
*/
virtual operator bool() const;
@ -58,6 +58,11 @@ public:
virtual void setDetails( const QString& details );
int errorCode() const { return m_number; }
/** @brief Is this JobResult a success?
*
* Equivalent to errorCode() == 0.
*/
bool succeeded() const { return this->operator bool(); }
/// @brief an "ok status" result
static JobResult ok();

View File

@ -122,8 +122,9 @@ public:
}
else
{
cDebug() << o << "Starting" << ( failureEncountered ? "EMERGENCY JOB" : "job" ) << jobitem.job->prettyName()
<< '(' << ( m_jobIndex + 1 ) << '/' << m_runningJobs->count() << ')';
cDebug() << o << "Starting" << ( failureEncountered ? "EMERGENCY JOB" : "job" )
<< jobitem.job->prettyName() << '(' << ( m_jobIndex + 1 ) << '/' << m_runningJobs->count()
<< ')';
o.refresh(); // So next time it shows the function header again
emitProgress( 0.0 ); // 0% for *this job*
connect( jobitem.job.data(), &Job::progress, this, &JobThread::emitProgress );

View File

@ -100,7 +100,7 @@ BOOST_PYTHON_MODULE( libcalamares )
bp::args( "s" ),
"Writes the given string to the Calamares warning stream." );
bp::def(
"error", &CalamaresPython::warning, bp::args( "s" ), "Writes the given string to the Calamares error stream." );
"error", &CalamaresPython::error, bp::args( "s" ), "Writes the given string to the Calamares error stream." );
// .. YAML functions

View File

@ -14,6 +14,7 @@
#include "partition/Sync.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include "utils/String.h"
#include <QDir>
#include <QTemporaryDir>
@ -123,5 +124,32 @@ TemporaryMount::path() const
return m_d ? m_d->m_mountDir.path() : QString();
}
QList< MtabInfo >
MtabInfo::fromMtabFilteredByPrefix( const QString& mountPrefix, const QString& mtabPath )
{
QFile f( mtabPath.isEmpty() ? "/etc/mtab" : mtabPath );
if ( !f.open( QIODevice::ReadOnly ) )
{
return {};
}
QTextStream in( &f );
QList< MtabInfo > l;
while ( !f.atEnd() )
{
QStringList line = in.readLine().split( ' ', SplitSkipEmptyParts );
if ( line.length() == 3 && !line[ 0 ].startsWith( '#' ) )
{
// Lines have format: <device> <mountpoint> <options>, so check
// the mountpoint field. Everything starts with an empty string.
if ( line[ 1 ].startsWith( mountPrefix ) )
{
l.append( { line[ 0 ], line[ 1 ] } );
}
}
}
return l;
}
} // namespace Partition
} // namespace CalamaresUtils

View File

@ -14,6 +14,7 @@
#include "DllMacro.h"
#include <QList>
#include <QString>
#include <QStringList>
@ -50,6 +51,13 @@ DLLEXPORT int mount( const QString& devicePath,
*/
DLLEXPORT int unmount( const QString& path, const QStringList& options = QStringList() );
/** @brief Mount and automatically unmount a device
*
* The TemporaryMount object mounts a filesystem, and is like calling
* the mount() function, above. When the object is destroyed, unmount()
* is called with suitable options to undo the original mount.
*/
class DLLEXPORT TemporaryMount
{
public:
@ -68,6 +76,36 @@ private:
std::unique_ptr< Private > m_d;
};
/** @brief Information about a mount point from /etc/mtab
*
* Entries in /etc/mtab are of the form: <device> <mountpoint> <other>
* This struct only stores device and mountpoint.
*
* The main way of getting these structs is to call fromMtab() to read
* an /etc/mtab-like file and storing all of the entries from it.
*/
struct DLLEXPORT MtabInfo
{
QString device;
QString mountPoint;
/** @brief Reads an mtab-like file and returns the entries from it
*
* When @p mtabPath is given, that file is read. If the given name is
* empty (e.g. the default) then /etc/mtab is read, instead.
*
* If @p mountPrefix is given, then only entries that have a mount point
* that starts with that prefix are returned.
*/
static QList< MtabInfo > fromMtabFilteredByPrefix( const QString& mountPrefix = QString(),
const QString& mtabPath = QString() );
/// @brief Predicate to sort MtabInfo objects by device-name
static bool deviceOrder( const MtabInfo& a, const MtabInfo& b ) { return a.device > b.device; }
/// @brief Predicate to sort MtabInfo objects by mount-point
static bool mountPointOrder( const MtabInfo& a, const MtabInfo& b ) { return a.mountPoint > b.mountPoint; }
};
} // namespace Partition
} // namespace CalamaresUtils

View File

@ -259,7 +259,8 @@ operator<<( QDebug& s, const RedactedCommand& l )
* Identical strings with the same context will be hashed the same,
* so that they can be logged and still recognized as the-same.
*/
static uint insertRedactedName( const QString& context, const QString& s )
static uint
insertRedactedName( const QString& context, const QString& s )
{
static uint salt = QRandomGenerator::global()->generate(); // Just once
@ -268,8 +269,8 @@ static uint insertRedactedName( const QString& context, const QString& s )
}
RedactedName::RedactedName( const QString& context, const QString& s )
: m_id( insertRedactedName(context, s) ),
m_context(context)
: m_id( insertRedactedName( context, s ) )
, m_context( context )
{
}

View File

@ -70,9 +70,12 @@ private:
inline CDebug&
operator<<( CDebug&& s, const FuncSuppressor& f )
{
if ( s.m_funcinfo )
{
s.m_funcinfo = nullptr;
s << f.m_s;
s.m_msg = QString( f.m_s );
}
return s;
}
@ -244,7 +247,8 @@ private:
const QString m_context;
};
inline QDebug& operator<<( QDebug& s, const RedactedName& n )
inline QDebug&
operator<<( QDebug& s, const RedactedName& n )
{
return s << NoQuote << QString( n ) << Quote;
}

View File

@ -29,7 +29,8 @@ namespace CalamaresUtils
* @param locale the new locale (names as defined by Calamares).
* @param brandingTranslationsPrefix the branding path prefix, from Calamares::Branding.
*/
DLLEXPORT void installTranslator( const CalamaresUtils::Locale::Translation::Id& locale, const QString& brandingTranslationsPrefix );
DLLEXPORT void installTranslator( const CalamaresUtils::Locale::Translation::Id& locale,
const QString& brandingTranslationsPrefix );
/** @brief Initializes the translations with the current system settings
*/
@ -56,7 +57,8 @@ DLLEXPORT CalamaresUtils::Locale::Translation::Id translatorLocaleName();
*
* @returns @c true on success
*/
DLLEXPORT bool loadTranslator( const CalamaresUtils::Locale::Translation::Id& locale, const QString& prefix, QTranslator* translator );
DLLEXPORT bool
loadTranslator( const CalamaresUtils::Locale::Translation::Id& locale, const QString& prefix, QTranslator* translator );
/** @brief Set @p allow to true to load translations from current dir.
*
@ -138,7 +140,10 @@ private:
#define CALAMARES_RETRANSLATE_SLOT( slotfunc ) \
do \
{ \
connect( CalamaresUtils::Retranslator::instance(), &CalamaresUtils::Retranslator::languageChanged, this, slotfunc ); \
connect( CalamaresUtils::Retranslator::instance(), \
&CalamaresUtils::Retranslator::languageChanged, \
this, \
slotfunc ); \
( this->*slotfunc )(); \
} while ( false )

View File

@ -67,7 +67,7 @@ Note that process modules are not recommended.
Module descriptors **may** have the following keys:
- *emergency* (a boolean value, set to true to mark the module
as an emergency module)
as an emergency module; see the section *Emergency Modules*, below)
- *noconfig* (a boolean value, set to true to state that the module
has no configuration file; defaults to false)
- *requiredModules* (a list of modules which are required for this module
@ -102,8 +102,14 @@ to generate a suitable `module.desc`. For Python modules, manually add
A module that is marked as an emergency module in its module.desc
must **also** set the *emergency* key to *true* in its configuration file
(see below). If it does not, the module is not considered to be an emergency
module after all (this is so that you can have modules that have several
instances, only some of which are actually needed for emergencies).
module after all. This is so that you can have modules that have several
instances, only some of which are actually needed for emergencies.
In summary:
- in `module.desc`, write `emergency: true` to make it **possible** to
run the module in emergency mode,
- in `<modulename>.conf`, write `emergency: true` to make that specific
module run in emergency mode.
### Module-specific configuration
@ -112,6 +118,10 @@ named `<modulename>.conf`. If such a file is present in the
module's directory, it can be shipped as a *default* configuration file.
This only happens if the CMake-time option `INSTALL_CONFIG` is on.
The name of the configuration file for a given module can be
influenced by the `settings.conf` of the overall Calamares configuration.
By default, though, the module's own name is used.
Modules that have *noconfig* set to true will not attempt to
read a configuration file, and will not warn that one is missing;
conversely if *noconfig* is set to false (or is missing, since
@ -169,6 +179,26 @@ it is possible to take the whole installation-process into account
for determining the relative weights there.
## Global storage keys
Some modules place values in global storage so that they can be referenced later by other modules or even other parts of the same module. The following table represents a partial list of the values available as well as where they originate from and which module consume them.
Key|Source|Consumers|Description
---|---|---|---
btrfsSubvolumes|mount|fstab|List of maps containing the mountpoint and btrtfs subvolume
btrfsRootSubvolume|mount|bootloader, luksopenswaphook|String containing the subvolume mounted at root
efiSystemPartition|partition|bootloader, fstab|String containing the path to the ESP relative to the installed system
extraMounts|mount|unpackfs|List of maps holding metadata for the temporary mountpoints used by the installer
hostname|users||A string containing the hostname of the new system
netinstallAdd|packagechooser|netinstall|Data to add to netinstall tree. Same format as netinstall.yaml
netinstallSelect|packagechooser|netinstall|List of group names to select in the netinstall tree
partitions|partition, rawfs|numerous modules|List of maps of metadata about each partition
rootMountPoint|mount|numerous modules|A string with the absolute path to the root mountpoint
username|users|networkcfg, plasmainf, preservefiles|A string containing the username of the new user
zfsDatasets|zfs|bootloader, grubcfg, mount|List of maps of zfs datasets including the name and mount information
zfsInfo|partition|mount, zfs|List of encrypted zfs partitions and the encription info
zfsPoolInfo|zfs|mount, umount|List of maps of zfs pool info including the name and mountpoint
## C++ modules
> Type: viewmodule, jobmodule

View File

@ -48,6 +48,16 @@ efiBootMgr: "efibootmgr"
# setting the option here, keep in mind that the name is sanitized
# (problematic characters, see above, are replaced).
#
# There are some special words possible at the end of *efiBootloaderId*:
# @@SERIAL@@ can be used to obtain a uniquely-numbered suffix
# that is added to the Id (yielding, e.g., `dirname1` or `dirname72`)
# @@RANDOM@@ can be used to obtain a unique 4-digit hex suffix
# @@PHRASE@@ can be used to obtain a unique 1-to-3-word suffix
# from a dictionary of space-themed words
# Note that these must be at the **end** of the *efiBootloaderId* value.
# There must also be at most one of them. If there is none, no suffix-
# processing is done and the *efiBootloaderId* is used unchanged.
#
# efiBootloaderId: "dirname"
# Optionally install a copy of the GRUB EFI bootloader as the EFI

View File

@ -285,10 +285,166 @@ def create_loader(loader_path, entry):
loader_file.write(line)
def efi_label():
class suffix_iterator(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
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter <= self.attempts:
return self.generator.next()
raise StopIteration
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
self.counter = -1
def next(self):
self.counter += 1
if self.counter > 0:
return "{!s}{!s}".format(self.name, self.counter)
else:
return self.name
def render_in_base(value, base_values, length=-1):
"""
Renders @p value in base-N, where N is the number of
items in @p base_values. When rendering, use the items
of @p base_values (e.g. use "0123456789" to get regular decimal
rendering, or "ABCDEFGHIJ" for letters-as-numbers 'encoding').
If length is positive, pads out to at least that long with
leading "zeroes", whatever base_values[0] is.
"""
if value < 0:
raise ValueError("Cannot render negative values")
if len(base_values) < 2:
raise ValueError("Insufficient items for base-N rendering")
if length < 1:
length = 1
digits = []
base = len(base_values)
while value > 0:
place = value % base
value = value // base
digits.append(base_values[place])
while len(digits) < length:
digits.append(base_values[0])
return "".join(reversed(digits))
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
self.counter = -1
def next(self):
self.counter += 1
if self.counter > 0:
import random
v = random.randint(0, 65535) # 16 bits
return "{!s}{!s}".format(self.name, render_in_base(v, "0123456789ABCDEF", 4))
else:
return self.name
class phraseEfi(object):
"""
EFI Id generator that appends a random phrase to the given name.
"""
words = ("Sun", "Moon", "Mars", "Soyuz", "Falcon", "Kuaizhou", "Gaganyaan")
def __init__(self, name):
self.name = name
# So the first call to next() will bump it to 0
self.counter = -1
def next(self):
self.counter += 1
if self.counter > 0:
import random
desired_length = 1 + self.counter // 5
v = random.randint(0, len(self.words) ** desired_length)
return "{!s}{!s}".format(self.name, render_in_base(v, self.words))
else:
return self.name
def get_efi_suffix_generator(name):
"""
Handle EFI bootloader Ids with @@<something>@@ for suffix-processing.
"""
if "@@" not in name:
raise ValueError("Misplaced call to get_efi_suffix_generator, no @@")
parts = name.split("@@")
if len(parts) != 3:
raise ValueError("EFI Id {!r} is malformed".format(name))
if parts[2]:
# Supposed to be empty because the string ends with "@@"
raise ValueError("EFI Id {!r} is malformed".format(name))
if parts[1] not in ("SERIAL", "RANDOM", "PHRASE"):
raise ValueError("EFI suffix {!r} is unknown".format(parts[1]))
generator = None
if parts[1] == "SERIAL":
generator = serialEfi(parts[0])
elif parts[1] == "RANDOM":
generator = randomEfi(parts[0])
elif parts[1] == "PHRASE":
generator = phraseEfi(parts[0])
if generator is None:
raise ValueError("EFI suffix {!r} is unsupported".format(parts[1]))
return generator
def change_efi_suffix(efi_directory, bootloader_id):
"""
Returns a label based on @p bootloader_id that is usable within
@p efi_directory. If there is a @@<something>@@ suffix marker
in the given id, tries to generate a unique label.
"""
if bootloader_id.endswith("@@"):
# Do 10 attempts with any suffix generator
g = suffix_iterator(10, get_efi_suffix_generator(bootloader_id))
else:
# Just one attempt
g = [bootloader_id]
for candidate_name in g:
if not os.path.exists(os.path.join(efi_directory, candidate_name)):
return candidate_name
return bootloader_id
def efi_label(efi_directory):
"""
Returns a sanitized label, possibly unique, that can be
used within @p efi_directory.
"""
if "efiBootloaderId" in libcalamares.job.configuration:
efi_bootloader_id = 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"]
@ -427,7 +583,7 @@ def run_grub_mkconfig(partitions, output_file):
check_target_env_call([libcalamares.job.configuration["grubMkconfig"], "-o", output_file])
def run_grub_install(fw_type, partitions, efi_directory=None):
def run_grub_install(fw_type, partitions, efi_directory):
"""
Runs grub-install in the target environment
@ -444,7 +600,8 @@ def run_grub_install(fw_type, partitions, efi_directory=None):
check_target_env_call(["sh", "-c", "echo ZPOOL_VDEV_NAME_PATH=1 >> /etc/environment"])
if fw_type == "efi":
efi_bootloader_id = efi_label()
assert efi_directory is not None
efi_bootloader_id = efi_label(efi_directory)
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
if is_zfs:
@ -458,6 +615,7 @@ def run_grub_install(fw_type, partitions, efi_directory=None):
"--bootloader-id=" + efi_bootloader_id,
"--force"])
else:
assert efi_directory is None
if libcalamares.globalstorage.value("bootLoader") is None:
return
@ -499,7 +657,7 @@ def install_grub(efi_directory, fw_type):
if not os.path.isdir(install_efi_directory):
os.makedirs(install_efi_directory)
efi_bootloader_id = efi_label()
efi_bootloader_id = efi_label(efi_directory)
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
@ -534,7 +692,7 @@ def install_grub(efi_directory, fw_type):
shutil.copy2(efi_file_source, efi_file_target)
else:
libcalamares.utils.debug("Bootloader: grub (bios)")
run_grub_install(fw_type, partitions)
run_grub_install(fw_type, partitions, None)
run_grub_mkconfig(partitions, libcalamares.job.configuration["grubCfg"])
@ -543,7 +701,7 @@ def install_secureboot(efi_directory):
"""
Installs the secureboot shim in the system by calling efibootmgr.
"""
efi_bootloader_id = efi_label()
efi_bootloader_id = efi_label(efi_directory)
installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
install_efi_directory = installation_root_path + efi_directory

View File

@ -0,0 +1,7 @@
# We have tests to exercise some of the module internals.
# Those tests conventionally live in Python files here in the tests/ directory. Add them.
add_test(
NAME test-bootloader-efiname
COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-bootloader-efiname.py
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View File

@ -0,0 +1,64 @@
# Calamares Boilerplate
import libcalamares
libcalamares.globalstorage = libcalamares.GlobalStorage(None)
libcalamares.globalstorage.insert("testing", True)
# Module prep-work
from src.modules.bootloader import main
# Specific Bootloader test
g = main.get_efi_suffix_generator("derp@@SERIAL@@")
assert g is not None
assert g.next() == "derp" # First time, no suffix
for n in range(9):
print(g.next())
# We called next() 10 times in total, starting from 0
assert g.next() == "derp10"
g = main.get_efi_suffix_generator("derp@@RANDOM@@")
assert g is not None
for n in range(10):
print(g.next())
# it's random, nothing to assert
g = main.get_efi_suffix_generator("derp@@PHRASE@@")
assert g is not None
for n in range(10):
print(g.next())
# it's random, nothing to assert
# Check invalid things
try:
g = main.get_efi_suffix_generator("derp")
raise TypeError("Shouldn't get generator (no indicator)")
except ValueError as e:
pass
try:
g = main.get_efi_suffix_generator("derp@@HEX@@")
raise TypeError("Shouldn't get generator (unknown indicator)")
except ValueError as e:
pass
try:
g = main.get_efi_suffix_generator("derp@@SERIAL@@x")
raise TypeError("Shouldn't get generator (trailing garbage)")
except ValueError as e:
pass
try:
g = main.get_efi_suffix_generator("derp@@SERIAL@@@@RANDOM@@")
raise TypeError("Shouldn't get generator (multiple indicators)")
except ValueError as e:
pass
# Try the generator (assuming no calamares- test files exist in /tmp)
import os
assert "calamares-single" == main.change_efi_suffix("/tmp", "calamares-single")
assert "calamares-serial" == main.change_efi_suffix("/tmp", "calamares-serial@@SERIAL@@")
try:
os.makedirs("/tmp/calamares-serial", exist_ok=True)
assert "calamares-serial1" == main.change_efi_suffix("/tmp", "calamares-serial@@SERIAL@@")
finally:
os.rmdir("/tmp/calamares-serial")

View File

@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
# This test shows how btrfs root would work
rootMountPoint: /tmp/mount
partitions:
- device: /dev/sda1
fs: btrfs
mountPoint: /
uuid: 2a00f1d5-1217-49a7-bedd-b55c85764732
- device: /dev/sda2
fs: swap
uuid: 59406569-446f-4730-a874-9f6b4b44fee3
mountPoint:
- device: /dev/sdb1
fs: btrfs
mountPoint: /home
uuid: 59406569-abcd-1234-a874-9f6b4b44fee3
btrfsSubvolumes:
- mountPoint: /
subvolume: "@ROOT"
- mountPoint: /var
subvolume: "@var"
- mountPoint: /usr/local
subvolume: "@local"

View File

@ -29,8 +29,16 @@ prefer_grub_d: false
# kept, not updated to the *bootloaderEntryName* from the branding file.
# Use this if the GRUB_DISTRIBUTOR setting in the file is "smart" in
# some way (e.g. uses shell-command substitution).
#
# TODO:3.3:snake-case this key
keepDistributor: false
# The default kernel params that should always be applied.
# This is an array of strings. If it is unset, the default is
# `["quiet"]`. To avoid the default, explicitly set this key
# to an empty list, `[]`.
kernel_params: [ "quiet" ]
# Default entries to write to /etc/default/grub if it does not exist yet or if
# we are overwriting it.
#

View File

@ -7,8 +7,10 @@ additionalProperties: false
type: object
properties:
overwrite: { type: boolean, default: false }
# TODO:3.3:snake-case this key
keepDistributor: { type: boolean, default: false }
prefer_grub_d: { type: boolean, default: false }
kernel_params: { type: array, items: { type: string } }
defaults:
type: object
additionalProperties: true # Other fields are acceptable

View File

@ -170,7 +170,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
if partition["fs"] == "zfs" and partition["mountPoint"] == "/":
zfs_root_path = get_zfs_root()
kernel_params = ["quiet"]
kernel_params = libcalamares.job.configuration.get("kernel_params", ["quiet"])
# Currently, grub doesn't detect this properly so it must be set manually
if zfs_root_path:

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2022 Evan James <dalto@fastmail.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
@ -31,15 +32,22 @@ InitcpioJob::prettyName() const
return tr( "Creating initramfs with mkinitcpio." );
}
/** @brief Sets secure permissions on each initramfs
*
* Iterates over each initramfs contained directly in the directory @p d.
* For each initramfs found, the permissions are set to owner read/write only.
*
*/
void
fixPermissions( const QDir& d )
{
for ( const auto& fi : d.entryInfoList( { "initramfs*" }, QDir::Files ) )
const auto initramList = d.entryInfoList( { "initramfs*" }, QDir::Files );
for ( const auto& fi : initramList )
{
QFile f( fi.absoluteFilePath() );
if ( f.exists() )
{
cDebug() << "initcpio fixing permissions for" << f.fileName();
cDebug() << "initcpio setting permissions for" << f.fileName();
f.setPermissions( QFileDevice::ReadOwner | QFileDevice::WriteOwner );
}
}
@ -63,9 +71,19 @@ InitcpioJob::exec()
}
}
// If the kernel option isn't set to a specific kernel, run mkinitcpio on all kernels
QStringList command = { "mkinitcpio" };
if ( m_kernel.isEmpty() || m_kernel == "all" )
{
command.append( "-P" );
}
else
{
command.append( { "-p", m_kernel } );
}
cDebug() << "Updating initramfs with kernel" << m_kernel;
auto r = CalamaresUtils::System::instance()->targetEnvCommand(
{ "mkinitcpio", "-p", m_kernel }, QString(), QString() /* no timeout , 0 */ );
auto r = CalamaresUtils::System::instance()->targetEnvCommand( command, QString(), QString() /* no timeout , 0 */ );
return r.explainProcess( "mkinitcpio", std::chrono::seconds( 10 ) /* fake timeout */ );
}
@ -73,28 +91,6 @@ void
InitcpioJob::setConfigurationMap( const QVariantMap& configurationMap )
{
m_kernel = CalamaresUtils::getString( configurationMap, "kernel" );
if ( m_kernel.isEmpty() )
{
m_kernel = QStringLiteral( "all" );
}
else if ( m_kernel == "$uname" )
{
auto r = CalamaresUtils::System::runCommand( CalamaresUtils::System::RunLocation::RunInHost,
{ "/bin/uname", "-r" },
QString(),
QString(),
std::chrono::seconds( 3 ) );
if ( r.getExitCode() == 0 )
{
m_kernel = r.getOutput();
cDebug() << "*initcpio* using running kernel" << m_kernel;
}
else
{
cWarning() << "*initcpio* could not determine running kernel, using 'all'." << Logger::Continuation
<< r.getExitCode() << r.getOutput();
}
}
m_unsafe = CalamaresUtils::getBool( configurationMap, "be_unsafe", false );
}

View File

@ -5,21 +5,22 @@
---
# This key defines the kernel to be loaded.
# It can have the following values:
# - empty or unset, interpreted as "all"
# - the literal string "$uname" (without quotes, with dollar),
# which will use the output of `uname -r` to determine the
# running kernel, and use that.
# - any other string.
# - the name of a single mkinitcpio preset
# - empty or unset
# - the literal string "all"
#
# Whatever is set, that string is passed as *preset* argument to the
# `-p` option of *mkinitcpio*. Take care that both "$uname" operates
# in the host system, and might not be correct if the target system is
# updated (to a newer kernel) as part of the installation.
# If kernel is set to "all" or empty/unset then mkinitpio is called for all
# kernels. Otherwise it is called with a single preset with the value
# contained in kernel.
#
# Note that "all" is probably not a good preset to use either.
kernel: linux312
kernel: linux
# Set this to true to turn off mitigations for lax file
# permissions on initramfs (which, in turn, can compromise
# your LUKS encryption keys, CVS-2019-13179).
#
# If your initramfs are stored in the EFI partition or another non-POSIX
# filesystem, this has no effect as the file permissions cannot be changed.
# In this case, ensure the partition is mounted securely.
#
be_unsafe: false

View File

@ -183,7 +183,8 @@ def find_initcpio_features(partitions, root_mount_point):
if partition["fs"] == "btrfs":
uses_btrfs = True
if partition["fs"] == "zfs":
# In addition to checking the filesystem, check to ensure that zfs is enabled
if partition["fs"] == "zfs" and libcalamares.globalstorage.contains("zfsPoolInfo"):
uses_zfs = True
if "lvm2" in partition["fs"]:

View File

@ -81,6 +81,7 @@ InitramfsJob::setConfigurationMap( const QVariantMap& configurationMap )
}
else
{
m_kernel = QStringLiteral( "all" );
cWarning() << "*initramfs* could not determine running kernel, using 'all'." << Logger::Continuation
<< r.getExitCode() << r.getOutput();
}

View File

@ -221,6 +221,11 @@ Config::setCurrentLocation()
{
setCurrentLocation( m_startingTimezone.first, m_startingTimezone.second );
}
if ( !m_selectedLocaleConfiguration.explicit_lang )
{
auto newLocale = automaticLocaleConfiguration();
setLanguage( newLocale.language() );
}
}
void
@ -252,15 +257,21 @@ Config::setCurrentLocation( const QString& regionName, const QString& zoneName )
void
Config::setCurrentLocation( const CalamaresUtils::Locale::TimeZoneData* location )
{
if ( location != m_currentLocation )
const bool updateLocation = ( location != m_currentLocation );
if ( updateLocation )
{
m_currentLocation = location;
// Overwrite those settings that have not been made explicit.
}
// lang should be always be updated
auto newLocale = automaticLocaleConfiguration();
if ( !m_selectedLocaleConfiguration.explicit_lang )
{
setLanguage( newLocale.language() );
}
if ( updateLocation )
{
if ( !m_selectedLocaleConfiguration.explicit_lc )
{
m_selectedLocaleConfiguration.lc_numeric = newLocale.lc_numeric;

View File

@ -6,7 +6,7 @@
# Because LUKS Open Swap Hook (Job) is such a mouthful, we'll
# use LOSH all over the place as a shorthand.
calamares_add_plugin( luksopenswaphook
calamares_add_plugin( luksopenswaphookcfg
TYPE job
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES

View File

@ -15,6 +15,7 @@
#include "PackageModel.h"
#include "ui_page_netinst.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "network/Manager.h"
@ -62,4 +63,19 @@ void
NetInstallPage::onActivate()
{
ui->groupswidget->setFocus();
// The netinstallSelect global storage value can be used to make additional items selected by default
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
const QStringList selectNames = gs->value( "netinstallSelect" ).toStringList();
if ( !selectNames.isEmpty() )
{
m_config->model()->setSelections( selectNames );
}
// If NetInstallAdd is found in global storage, add those items to the tree
const QVariantList groups = gs->value( "netinstallAdd" ).toList();
if ( !groups.isEmpty() )
{
m_config->model()->appendModelData( groups );
}
}

View File

@ -14,6 +14,43 @@
#include "utils/Variant.h"
#include "utils/Yaml.h"
/// Recursive helper for setSelections()
static void
setSelections( const QStringList& selectNames, PackageTreeItem* item )
{
for ( int i = 0; i < item->childCount(); i++ )
{
auto* child = item->child( i );
setSelections( selectNames, child );
}
if ( item->isGroup() && selectNames.contains( item->name() ) )
{
item->setSelected( Qt::CheckState::Checked );
}
}
/** @brief Collects all the "source" values from @p groupList
*
* Iterates over @p groupList and returns all nonempty "source"
* values from the maps.
*
*/
static QStringList
collectSources( const QVariantList& groupList )
{
QStringList sources;
for ( const QVariant& group : groupList )
{
QVariantMap groupMap = group.toMap();
if ( !groupMap[ "source" ].toString().isEmpty() )
{
sources.append( groupMap[ "source" ].toString() );
}
}
return sources;
}
PackageModel::PackageModel( QObject* parent )
: QAbstractItemModel( parent )
{
@ -170,6 +207,15 @@ PackageModel::headerData( int section, Qt::Orientation orientation, int role ) c
return QVariant();
}
void
PackageModel::setSelections( const QStringList& selectNames )
{
if ( m_rootItem )
{
::setSelections( selectNames, m_rootItem );
}
}
PackageTreeItem::List
PackageModel::getPackages() const
{
@ -303,9 +349,43 @@ PackageModel::setupModelData( const QVariantList& groupList, PackageTreeItem* pa
void
PackageModel::setupModelData( const QVariantList& l )
{
emit beginResetModel();
Q_EMIT beginResetModel();
delete m_rootItem;
m_rootItem = new PackageTreeItem();
setupModelData( l, m_rootItem );
emit endResetModel();
Q_EMIT endResetModel();
}
void
PackageModel::appendModelData( const QVariantList& groupList )
{
if ( m_rootItem )
{
Q_EMIT beginResetModel();
const QStringList sources = collectSources( groupList );
if ( !sources.isEmpty() )
{
// Prune any existing data from the same source
QList< int > removeList;
for ( int i = 0; i < m_rootItem->childCount(); i++ )
{
PackageTreeItem* child = m_rootItem->child( i );
if ( sources.contains( child->source() ) )
{
removeList.insert( 0, i );
}
}
for ( const int& item : qAsConst( removeList ) )
{
m_rootItem->removeChild( item );
}
}
// Add the new data to the model
setupModelData( groupList, m_rootItem );
Q_EMIT endResetModel();
}
}

View File

@ -54,9 +54,33 @@ public:
int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
int columnCount( const QModelIndex& parent = QModelIndex() ) const override;
/** @brief Sets the checked flag on matching groups in the tree
*
* Recursively traverses the tree pointed to by m_rootItem and
* checks if a group name matches any of the items in @p selectNames.
* If a match is found, set check the box for that group and it's children.
*
* Individual packages will not be matched.
*
*/
void setSelections( const QStringList& selectNames );
PackageTreeItem::List getPackages() const;
PackageTreeItem::List getItemPackages( PackageTreeItem* item ) const;
/** @brief Appends groups to the tree
*
* Uses the data from @p groupList to add elements to the
* existing tree that m_rootItem points to. If m_rootItem
* is not valid, it does nothing
*
* Before adding anything to the model, it ensures that there
* is no existing data from the same source. If there is, that
* data is pruned first
*
*/
void appendModelData( const QVariantList& groupList );
private:
friend class ItemTests;

View File

@ -70,6 +70,7 @@ PackageTreeItem::PackageTreeItem( const QVariantMap& groupData, GroupTag&& paren
, m_description( CalamaresUtils::getString( groupData, "description" ) )
, m_preScript( CalamaresUtils::getString( groupData, "pre-install" ) )
, m_postScript( CalamaresUtils::getString( groupData, "post-install" ) )
, m_source( CalamaresUtils::getString( groupData, "source" ) )
, m_isGroup( true )
, m_isCritical( parentCriticality( groupData, parent.parent ) )
, m_isHidden( CalamaresUtils::getBool( groupData, "hidden", false ) )
@ -248,6 +249,19 @@ PackageTreeItem::setChildrenSelected( Qt::CheckState isSelected )
}
}
void
PackageTreeItem::removeChild( int row )
{
if ( 0 <= row && row < m_childItems.count() )
{
m_childItems.removeAt( row );
}
else
{
cWarning() << "Attempt to remove invalid child in removeChild() at row " << row;
}
}
int
PackageTreeItem::type() const
{

View File

@ -56,6 +56,7 @@ public:
QString description() const { return m_description; }
QString preScript() const { return m_preScript; }
QString postScript() const { return m_postScript; }
QString source() const { return m_source; }
/** @brief Is this item a group-item?
*
@ -124,6 +125,8 @@ public:
void setSelected( Qt::CheckState isSelected );
void setChildrenSelected( Qt::CheckState isSelected );
void removeChild( int row );
/** @brief Update selectedness based on the children's states
*
* This only makes sense for groups, which might have packages
@ -157,6 +160,7 @@ private:
QString m_description;
QString m_preScript;
QString m_postScript;
QString m_source;
bool m_isGroup = false;
bool m_isCritical = false;
bool m_isHidden = false;

View File

@ -27,6 +27,29 @@
#include "utils/Logger.h"
#include "utils/Variant.h"
/** @brief This removes any values from @p groups that match @p source
*
* This is used to remove duplicates from the netinstallAdd structure
* It iterates over @p groups and for each map in the list, if the
* "source" element matches @p source, it is removed from the returned
* list.
*/
static QVariantList
pruneNetinstallAdd( const QString& source, const QVariant& groups )
{
QVariantList newGroupList;
const QVariantList groupList = groups.toList();
for ( const QVariant& group : groupList )
{
QVariantMap groupMap = group.toMap();
if ( groupMap.value( "source", "" ).toString() != source )
{
newGroupList.append( groupMap );
}
}
return newGroupList;
}
const NamedEnumTable< PackageChooserMode >&
packageChooserModeNames()
{
@ -55,6 +78,8 @@ PackageChooserMethodNames()
{ "custom", PackageChooserMethod::Legacy },
{ "contextualprocess", PackageChooserMethod::Legacy },
{ "packages", PackageChooserMethod::Packages },
{ "netinstall-add", PackageChooserMethod::NetAdd },
{ "netinstall-select", PackageChooserMethod::NetSelect },
};
return names;
}
@ -121,6 +146,47 @@ Config::updateGlobalStorage( const QStringList& selected ) const
CalamaresUtils::Packages::setGSPackageAdditions(
Calamares::JobQueue::instance()->globalStorage(), m_defaultId, packageNames );
}
else if ( m_method == PackageChooserMethod::NetAdd )
{
QVariantList netinstallDataList = m_model->getNetinstallDataForNames( selected );
if ( netinstallDataList.isEmpty() )
{
cWarning() << "No netinstall information found for " << selected;
}
else
{
// If an earlier packagechooser instance added this data to global storage, combine them
auto* gs = Calamares::JobQueue::instance()->globalStorage();
if ( gs->contains( "netinstallAdd" ) )
{
netinstallDataList
+= pruneNetinstallAdd( QStringLiteral( "packageChooser" ), gs->value( "netinstallAdd" ) );
}
gs->insert( "netinstallAdd", netinstallDataList );
}
}
else if ( m_method == PackageChooserMethod::NetSelect )
{
cDebug() << m_defaultId << "groups to select in netinstall" << selected;
QStringList newSelected = selected;
auto* gs = Calamares::JobQueue::instance()->globalStorage();
// If an earlier packagechooser instance added this data to global storage, combine them
if ( gs->contains( "netinstallSelect" ) )
{
auto selectedOrig = gs->value( "netinstallSelect" );
if ( selectedOrig.canConvert( QVariant::StringList ) )
{
newSelected += selectedOrig.toStringList();
}
else
{
cWarning() << "Invalid NetinstallSelect data in global storage. Earlier selections purged";
}
gs->remove( "netinstallSelect" );
}
gs->insert( "netinstallSelect", newSelected );
}
else
{
cWarning() << "Unknown packagechooser method" << smash( m_method );

View File

@ -33,6 +33,8 @@ enum class PackageChooserMethod
{
Legacy, // use contextualprocess or other custom
Packages, // use the packages module
NetAdd, // adds packages to the netinstall module
NetSelect, // makes selections in the netinstall module
};
const NamedEnumTable< PackageChooserMethod >& PackageChooserMethodNames();

View File

@ -15,6 +15,16 @@
#include <QFileInfo>
/** @brief A wrapper for CalamaresUtils::getSubMap that excludes the success param
*/
static QVariantMap
getSubMap( const QVariantMap& map, const QString& key )
{
bool success;
return CalamaresUtils::getSubMap( map, key, success );
}
static QPixmap
loadScreenshot( const QString& path )
{
@ -51,12 +61,13 @@ PackageItem::PackageItem( const QString& a_id,
{
}
PackageItem::PackageItem::PackageItem( const QVariantMap& item_map )
PackageItem::PackageItem( const QVariantMap& item_map )
: id( CalamaresUtils::getString( item_map, "id" ) )
, name( CalamaresUtils::Locale::TranslatedString( item_map, "name" ) )
, description( CalamaresUtils::Locale::TranslatedString( item_map, "description" ) )
, screenshot( loadScreenshot( CalamaresUtils::getString( item_map, "screenshot" ) ) )
, packageNames( CalamaresUtils::getStringList( item_map, "packages" ) )
, netinstallData( getSubMap( item_map, "netinstall" ) )
{
if ( name.isEmpty() && id.isEmpty() )
{
@ -125,6 +136,25 @@ PackageListModel::getInstallPackagesForNames( const QStringList& ids ) const
return l;
}
QVariantList
PackageListModel::getNetinstallDataForNames( const QStringList& ids ) const
{
QVariantList l;
for ( auto& p : m_packages )
{
if ( ids.contains( p.id ) )
{
if ( !p.netinstallData.isEmpty() )
{
QVariantMap newData = p.netinstallData;
newData[ "source" ] = QStringLiteral( "packageChooser" );
l.append( newData );
}
}
}
return l;
}
int
PackageListModel::rowCount( const QModelIndex& index ) const
{

View File

@ -26,6 +26,7 @@ struct PackageItem
CalamaresUtils::Locale::TranslatedString description;
QPixmap screenshot;
QStringList packageNames;
QVariantMap netinstallData;
/// @brief Create blank PackageItem
PackageItem();
@ -111,6 +112,14 @@ public:
*/
QStringList getInstallPackagesForNames( const QStringList& ids ) const;
/** @brief Does a name lookup (based on id) and returns the netinstall data
*
* If there is a package with an id in @p ids, returns their netinstall data
*
* returns a list of netinstall data or an emply list if none is found
*/
QVariantList getNetinstallDataForNames( const QStringList& ids ) const;
enum Roles : int
{
NameRole = Qt::DisplayRole,

View File

@ -33,6 +33,15 @@ mode: required
# in the `exec` section. These package settings will then be handed
# off to whatever package manager is configured there.
#
# - "netinstall-select"
# When this is set, the id(s) selected are passed to the netinstall module.
# Any id that matches a group name in that module is set to checked
#
# - "netinstall-add"
# With this method, the packagechooser module is used to add groups to the
# netinstall module. For this to hav=e any effect. You must set netinstall,
# which is described below.
#
# There is no need to put this module in the `exec` section. There
# are no jobs that this module provides. You should put **other**
# modules, either *contextualprocess* or *packages* or some custom
@ -101,13 +110,19 @@ labels:
# an additional attempt is made to load the image from the **branding**
# directory.
#
# The following field is **optional** for an item:
# The following fields are **optional** for an item:
#
# - *packages* :
# List of package names for the product. If using the *method*
# "packages", consider this item mandatory (because otherwise
# selecting the item would install no packages).
#
# - *netinstall* :
# The data in this field should follow the format of a group
# from the netinstall module documented in
# src/modules/netinstall/netinstall.conf. This is only used
# when method is set to "netinstall-add"
#
# # AppData Items #
#
# For data provided by AppData XML: the item has an *appdata*

View File

@ -382,7 +382,7 @@ class PMPacman(PackageManager):
def line_cb(line):
if line.startswith(":: "):
self.in_package_changes = "package changes" in line
self.in_package_changes = "package" in line or "hooks" in line
else:
if self.in_package_changes and line.endswith("...\n"):
# Update the message, untranslated; do not change the
@ -444,8 +444,12 @@ class PMPacman(PackageManager):
else:
command.append("-S")
# Don't ask for user intervention, take the default action
command.append("--noconfirm")
# Don't report download progress for each file
command.append("--noprogressbar")
if self.pacman_needed_only is True:
command.append("--needed")

View File

@ -345,6 +345,11 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
if ( !m_swapChoices.contains( m_initialSwapChoice ) )
{
cWarning() << "Configuration for *initialSwapChoice* is not one of the *userSwapChoices*";
if ( nameFound )
{
cWarning() << Logger::SubEntry << "Choice" << swapChoiceNames().find( m_initialSwapChoice ) << "added.";
m_swapChoices.insert( m_initialSwapChoice );
}
m_initialSwapChoice = pickOne( m_swapChoices );
}
setSwapChoice( m_initialSwapChoice );

View File

@ -675,7 +675,11 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
// because it could take a while. Then when it's done, we can set up the widgets
// and remove the spinner.
m_future = new QFutureWatcher< void >();
connect( m_future, &QFutureWatcher< void >::finished, this, [this] {
connect( m_future,
&QFutureWatcher< void >::finished,
this,
[ this ]
{
continueLoading();
this->m_future->deleteLater();
this->m_future = nullptr;

View File

@ -41,6 +41,10 @@ hasRootPartition( Device* device )
return false;
}
/** @brief Check if @p path holds an iso9660 filesystem
*
* The @p path should point to a device; blkid is used to check the FS type.
*/
static bool
blkIdCheckIso9660( const QString& path )
{
@ -49,6 +53,18 @@ blkIdCheckIso9660( const QString& path )
return r.getOutput().contains( "iso9660" );
}
/// @brief Convenience to check if @p partition holds an iso9660 filesystem
static bool
blkIdCheckIso9660P( const Partition* partition )
{
return blkIdCheckIso9660( partition->partitionPath() );
}
/** @brief Check if the @p device is an iso9660 device
*
* An iso9660 device is **probably** a CD-ROM. If the device holds an
* iso9660 FS, or any of its partitions do, then we call it an iso9660 device.
*/
static bool
isIso9660( const Device* device )
{
@ -64,13 +80,8 @@ isIso9660( const Device* device )
if ( device->partitionTable() && !device->partitionTable()->children().isEmpty() )
{
for ( const Partition* partition : device->partitionTable()->children() )
{
if ( blkIdCheckIso9660( partition->partitionPath() ) )
{
return true;
}
}
const auto& p = device->partitionTable()->children();
return std::any_of( p.cbegin(), p.cend(), blkIdCheckIso9660P );
}
return false;
}

View File

@ -28,9 +28,9 @@
static void
sortDevices( DeviceModel::DeviceList& l )
{
std::sort( l.begin(), l.end(), []( const Device* dev1, const Device* dev2 ) {
return dev1->deviceNode() < dev2->deviceNode();
} );
std::sort( l.begin(),
l.end(),
[]( const Device* dev1, const Device* dev2 ) { return dev1->deviceNode() < dev2->deviceNode(); } );
}
DeviceModel::DeviceModel( QObject* parent )

View File

@ -262,9 +262,7 @@ PartitionCoreModule::doInit()
// Gives ownership of the Device* to the DeviceInfo object
auto deviceInfo = new DeviceInfo( device );
m_deviceInfos << deviceInfo;
cDebug() << Logger::SubEntry
<< device->deviceNode()
<< device->capacity()
cDebug() << Logger::SubEntry << device->deviceNode() << device->capacity()
<< Logger::RedactedName( "DevName", device->name() )
<< Logger::RedactedName( "DevNamePretty", device->prettyName() );
}
@ -685,9 +683,8 @@ PartitionCoreModule::lvmPVs() const
bool
PartitionCoreModule::hasVGwithThisName( const QString& name ) const
{
auto condition = [name]( DeviceInfo* d ) {
return dynamic_cast< LvmDevice* >( d->device.data() ) && d->device.data()->name() == name;
};
auto condition = [ name ]( DeviceInfo* d )
{ return dynamic_cast< LvmDevice* >( d->device.data() ) && d->device.data()->name() == name; };
return std::find_if( m_deviceInfos.begin(), m_deviceInfos.end(), condition ) != m_deviceInfos.end();
}
@ -695,7 +692,8 @@ PartitionCoreModule::hasVGwithThisName( const QString& name ) const
bool
PartitionCoreModule::isInVG( const Partition* partition ) const
{
auto condition = [partition]( DeviceInfo* d ) {
auto condition = [ partition ]( DeviceInfo* d )
{
LvmDevice* vg = dynamic_cast< LvmDevice* >( d->device.data() );
return vg && vg->physicalVolumes().contains( partition );
};
@ -1089,7 +1087,11 @@ void
PartitionCoreModule::asyncRevertDevice( Device* dev, std::function< void() > callback )
{
QFutureWatcher< void >* watcher = new QFutureWatcher< void >();
connect( watcher, &QFutureWatcher< void >::finished, this, [watcher, callback] {
connect( watcher,
&QFutureWatcher< void >::finished,
this,
[ watcher, callback ]
{
callback();
watcher->deleteLater();
} );

View File

@ -19,7 +19,8 @@
* to bother with one-byte accuracy (and anyway, a double has at least 50 bits
* at which point we're printing giga (or gibi) bytes).
*/
static inline QString formatByteSize( qint64 sizeValue )
static inline QString
formatByteSize( qint64 sizeValue )
{
return Capacity::formatByteSize( static_cast< double >( sizeValue ) );
}

View File

@ -176,7 +176,11 @@ ChoicePage::init( PartitionCoreModule* core )
// We need to do this because a PCM revert invalidates the deviceModel.
connect( core, &PartitionCoreModule::reverted, this, [=] {
connect( core,
&PartitionCoreModule::reverted,
this,
[ = ]
{
setModelToComboBox( m_drivesCombo, core->deviceModel() );
m_drivesCombo->setCurrentIndex( m_lastSelectedDeviceIndex );
} );
@ -303,7 +307,11 @@ ChoicePage::setupChoices()
#else
auto buttonSignal = &QButtonGroup::idToggled;
#endif
connect( m_grp, buttonSignal, this, [this]( int id, bool checked ) {
connect( m_grp,
buttonSignal,
this,
[ this ]( int id, bool checked )
{
if ( checked ) // An action was picked.
{
m_config->setInstallChoice( id );
@ -401,7 +409,9 @@ ChoicePage::applyDeviceChoice()
if ( m_core->isDirty() )
{
ScanningDialog::run(
QtConcurrent::run( [=] {
QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertAllDevices();
} ),
@ -493,11 +503,14 @@ ChoicePage::applyActionChoice( InstallChoice choice )
if ( m_core->isDirty() )
{
ScanningDialog::run(
QtConcurrent::run( [=] {
QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertDevice( selectedDevice() );
} ),
[=] {
[ = ]
{
PartitionActions::doAutopartition( m_core, selectedDevice(), options );
Q_EMIT deviceChosen();
},
@ -514,7 +527,9 @@ ChoicePage::applyActionChoice( InstallChoice choice )
if ( m_core->isDirty() )
{
ScanningDialog::run(
QtConcurrent::run( [=] {
QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertDevice( selectedDevice() );
} ),
@ -532,11 +547,14 @@ ChoicePage::applyActionChoice( InstallChoice choice )
if ( m_core->isDirty() )
{
ScanningDialog::run(
QtConcurrent::run( [=] {
QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertDevice( selectedDevice() );
} ),
[this] {
[ this ]
{
// We need to reupdate after reverting because the splitter widget is
// not a true view.
updateActionChoicePreview( m_config->installChoice() );
@ -772,7 +790,8 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
ScanningDialog::run(
QtConcurrent::run(
[this, current, homePartitionPath]( bool doReuseHomePartition ) {
[ this, current, homePartitionPath ]( bool doReuseHomePartition )
{
QMutexLocker locker( &m_coreMutex );
if ( m_core->isDirty() )
@ -853,7 +872,8 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
}
},
m_reuseHomeCheckBox->isChecked() ),
[this, homePartitionPath] {
[ this, homePartitionPath ]
{
m_reuseHomeCheckBox->setVisible( !homePartitionPath->isEmpty() );
if ( !homePartitionPath->isEmpty() )
m_reuseHomeCheckBox->setText( tr( "Reuse %1 as home partition for %2." )
@ -1006,7 +1026,8 @@ ChoicePage::updateActionChoicePreview( InstallChoice choice )
connect( m_afterPartitionSplitterWidget,
&PartitionSplitterWidget::partitionResized,
this,
[this, sizeLabel]( const QString& path, qint64 size, qint64 sizeNext ) {
[ this, sizeLabel ]( const QString& path, qint64 size, qint64 sizeNext )
{
Q_UNUSED( path )
sizeLabel->setText(
tr( "%1 will be shrunk to %2MiB and a new "
@ -1020,7 +1041,8 @@ ChoicePage::updateActionChoicePreview( InstallChoice choice )
m_previewAfterFrame->show();
m_previewAfterLabel->show();
SelectionFilter filter = []( const QModelIndex& index ) {
SelectionFilter filter = []( const QModelIndex& index )
{
return PartUtils::canBeResized(
static_cast< Partition* >( index.data( PartitionModel::PartitionPtrRole ).value< void* >() ),
Logger::Once() );
@ -1069,17 +1091,22 @@ ChoicePage::updateActionChoicePreview( InstallChoice choice )
eraseBootloaderLabel->setText( tr( "Boot loader location:" ) );
m_bootloaderComboBox = createBootloaderComboBox( eraseWidget );
connect( m_core->bootLoaderModel(), &QAbstractItemModel::modelReset, [this]() {
connect( m_core->bootLoaderModel(),
&QAbstractItemModel::modelReset,
[ this ]()
{
if ( !m_bootloaderComboBox.isNull() )
{
Calamares::restoreSelectedBootLoader( *m_bootloaderComboBox, m_core->bootLoaderInstallPath() );
Calamares::restoreSelectedBootLoader( *m_bootloaderComboBox,
m_core->bootLoaderInstallPath() );
}
} );
connect(
m_core,
&PartitionCoreModule::deviceReverted,
this,
[this]( Device* dev ) {
[ this ]( Device* dev )
{
Q_UNUSED( dev )
if ( !m_bootloaderComboBox.isNull() )
{
@ -1110,7 +1137,8 @@ ChoicePage::updateActionChoicePreview( InstallChoice choice )
}
else
{
SelectionFilter filter = []( const QModelIndex& index ) {
SelectionFilter filter = []( const QModelIndex& index )
{
return PartUtils::canBeReplaced(
static_cast< Partition* >( index.data( PartitionModel::PartitionPtrRole ).value< void* >() ),
Logger::Once() );
@ -1217,7 +1245,11 @@ ChoicePage::createBootloaderComboBox( QWidget* parent )
comboForBootloader->setModel( m_core->bootLoaderModel() );
// When the chosen bootloader device changes, we update the choice in the PCM
connect( comboForBootloader, QOverload< int >::of( &QComboBox::currentIndexChanged ), this, [this]( int newIndex ) {
connect( comboForBootloader,
QOverload< int >::of( &QComboBox::currentIndexChanged ),
this,
[ this ]( int newIndex )
{
QComboBox* bootloaderCombo = qobject_cast< QComboBox* >( sender() );
if ( bootloaderCombo )
{

View File

@ -325,16 +325,10 @@ CreatePartitionDialog::updateMountPointUi()
void
CreatePartitionDialog::checkMountPointSelection()
{
if ( m_usedMountPoints.contains( selectedMountPoint( m_ui->mountPointComboBox ) ) )
{
m_ui->labelMountPoint->setText( tr( "Mountpoint already in use. Please select another one." ) );
m_ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
}
else
{
m_ui->labelMountPoint->setText( QString() );
m_ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( true );
}
validateMountPoint( selectedMountPoint( m_ui->mountPointComboBox ),
m_usedMountPoints,
m_ui->mountPointExplanation,
m_ui->buttonBox->button( QDialogButtonBox::Ok ) );
}
void

View File

@ -68,7 +68,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<item>
<widget class="QRadioButton" name="primaryRadioButton">
<property name="text">
<string>&amp;Primary</string>
<string>Primar&amp;y</string>
</property>
<property name="checked">
<bool>true</bool>
@ -171,6 +171,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
</item>
<item row="8" column="1">
<widget class="QComboBox" name="mountPointComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
@ -179,21 +185,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="labelMountPoint">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="11" column="0">
<item row="12" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Flags:</string>
</property>
</widget>
</item>
<item row="11" column="1">
<item row="12" column="1">
<widget class="QListWidget" name="m_listFlags">
<property name="alternatingRowColors">
<bool>true</bool>
@ -206,7 +205,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
</property>
</widget>
</item>
<item row="12" column="0">
<item row="13" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -219,14 +218,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
</property>
</spacer>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QLineEdit" name="filesystemLabelEdit">
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Label for the filesystem</string>
</property>
@ -235,13 +228,20 @@ SPDX-License-Identifier: GPL-3.0-or-later
</property>
</widget>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>FS Label:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="mountPointExplanation">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -69,7 +69,10 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
replacePartResizerWidget();
connect( m_ui->formatRadioButton, &QAbstractButton::toggled, [this]( bool doFormat ) {
connect( m_ui->formatRadioButton,
&QAbstractButton::toggled,
[ this ]( bool doFormat )
{
replacePartResizerWidget();
m_ui->fileSystemLabel->setEnabled( doFormat );
@ -295,14 +298,8 @@ EditExistingPartitionDialog::updateMountPointPicker()
void
EditExistingPartitionDialog::checkMountPointSelection()
{
if ( m_usedMountPoints.contains( selectedMountPoint( m_ui->mountPointComboBox ) ) )
{
m_ui->labelMountPoint->setText( tr( "Mountpoint already in use. Please select another one." ) );
m_ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
}
else
{
m_ui->labelMountPoint->setText( QString() );
m_ui->buttonBox->button( QDialogButtonBox::Ok )->setEnabled( true );
}
validateMountPoint( selectedMountPoint( m_ui->mountPointComboBox ),
m_usedMountPoints,
m_ui->mountPointExplanation,
m_ui->buttonBox->button( QDialogButtonBox::Ok ) );
}

View File

@ -51,7 +51,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Content:</string>
<string>Con&amp;tent:</string>
</property>
<property name="buddy">
<cstring>keepRadioButton</cstring>
@ -109,6 +109,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
</item>
<item row="6" column="1">
<widget class="QComboBox" name="mountPointComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
@ -147,14 +153,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
<item row="5" column="1">
<widget class="QComboBox" name="fileSystemComboBox"/>
</item>
<item row="9" column="0">
<item row="10" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Flags:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QListWidget" name="m_listFlags">
<property name="alternatingRowColors">
<bool>true</bool>
@ -168,20 +174,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="labelMountPoint">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="fileSystemLabelEdit">
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Label for the filesystem</string>
</property>
@ -190,13 +183,20 @@ SPDX-License-Identifier: GPL-3.0-or-later
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="fileSystemLabelLabel">
<property name="text">
<string>FS Label:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="mountPointExplanation">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -13,9 +13,27 @@
#include "ui_EncryptWidget.h"
#include "Branding.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h"
/** @brief Does this system support whole-disk encryption?
*
* Returns @c true if the system is likely to support encryption
* with sufficient performance to be usable. A machine that can't
* doe hardware-assisted AES is **probably** too slow, so we could
* warn the user that ticking the "encrypt system" box is a bad
* idea.
*
* Since we don't have an oracle that can answer that question,
* just pretend every system can do it.
*/
static inline bool
systemSupportsEncryptionAcceptably()
{
return true;
}
EncryptWidget::EncryptWidget( QWidget* parent )
: QWidget( parent )
, m_ui( new Ui::EncryptWidget )
@ -27,6 +45,18 @@ EncryptWidget::EncryptWidget( QWidget* parent )
m_ui->m_passphraseLineEdit->hide();
m_ui->m_confirmLineEdit->hide();
m_ui->m_iconLabel->hide();
// TODO: this deserves better rendering, an icon or something, but that will
// depend on having a non-bogus implementation of systemSupportsEncryptionAcceptably
if ( systemSupportsEncryptionAcceptably() )
{
m_ui->m_encryptionUnsupportedLabel->hide();
}
else
{
// This is really ugly, but the character is unicode "unlocked"
m_ui->m_encryptionUnsupportedLabel->setText( QStringLiteral( "🔓" ) );
m_ui->m_encryptionUnsupportedLabel->show();
}
connect( m_ui->m_encryptCheckBox, &QCheckBox::stateChanged, this, &EncryptWidget::onCheckBoxStateChanged );
connect( m_ui->m_passphraseLineEdit, &QLineEdit::textEdited, this, &EncryptWidget::onPassphraseEdited );

View File

@ -37,6 +37,19 @@ SPDX-License-Identifier: GPL-3.0-or-later
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_encryptionUnsupportedLabel">
<property name="toolTip">
<string>Your system does not seem to support encryption well enough to encrypt the entire system. You may enable encryption, but performance may suffer.</string>
</property>
<property name="text">
<string notr="true">🔓</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="m_passphraseLineEdit">
<property name="echoMode">
@ -57,6 +70,19 @@ SPDX-License-Identifier: GPL-3.0-or-later
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="m_iconLabel">
<property name="text">

View File

@ -54,9 +54,10 @@ PartitionBarsView::PartitionBarsView( QWidget* parent )
setSelectionMode( QAbstractItemView::SingleSelection );
// Debug
connect( this, &PartitionBarsView::clicked, this, [=]( const QModelIndex& index ) {
cDebug() << "Clicked row" << index.row();
} );
connect( this,
&PartitionBarsView::clicked,
this,
[ = ]( const QModelIndex& index ) { cDebug() << "Clicked row" << index.row(); } );
setMouseTracking( true );
}
@ -410,7 +411,8 @@ PartitionBarsView::setSelectionFilter( std::function< bool( const QModelIndex& )
}
QModelIndex PartitionBarsView::moveCursor( CursorAction, Qt::KeyboardModifiers )
QModelIndex
PartitionBarsView::moveCursor( CursorAction, Qt::KeyboardModifiers )
{
return QModelIndex();
}

View File

@ -12,13 +12,17 @@
#include "PartitionDialogHelpers.h"
#include "core/PartUtils.h"
#include "gui/CreatePartitionDialog.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/Logger.h"
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
QStringList
standardMountPoints()
@ -37,7 +41,7 @@ void
standardMountPoints( QComboBox& combo )
{
combo.clear();
combo.addItem( QObject::tr( "(no mount point)" ) );
combo.lineEdit()->setPlaceholderText( QObject::tr( "(no mount point)" ) );
combo.addItems( standardMountPoints() );
}
@ -51,10 +55,6 @@ standardMountPoints( QComboBox& combo, const QString& selected )
QString
selectedMountPoint( QComboBox& combo )
{
if ( combo.currentIndex() == 0 )
{
return QString();
}
return combo.currentText();
}
@ -63,7 +63,7 @@ setSelectedMountPoint( QComboBox& combo, const QString& selected )
{
if ( selected.isEmpty() )
{
combo.setCurrentIndex( 0 ); // (no mount point)
combo.setCurrentIndex( -1 ); // (no mount point)
}
else
{
@ -80,6 +80,34 @@ setSelectedMountPoint( QComboBox& combo, const QString& selected )
}
}
bool
validateMountPoint( const QString& mountPoint, const QStringList& inUse, QLabel* label, QPushButton* button )
{
QString msg;
bool ok = true;
if ( inUse.contains( mountPoint ) )
{
msg = CreatePartitionDialog::tr( "Mountpoint already in use. Please select another one." );
ok = false;
}
else if ( !mountPoint.isEmpty() && !mountPoint.startsWith( '/' ) )
{
msg = CreatePartitionDialog::tr( "Mountpoint must start with a <tt>/</tt>." );
ok = false;
}
if ( label )
{
label->setText( msg );
}
if ( button )
{
button->setEnabled( ok );
}
return ok;
}
PartitionTable::Flags
flagsFromList( const QListWidget& list )

View File

@ -16,7 +16,9 @@
#include <QStringList>
class QPushButton;
class QComboBox;
class QLabel;
class QListWidget;
/**
@ -58,6 +60,16 @@ setSelectedMountPoint( QComboBox* combo, const QString& selected )
setSelectedMountPoint( *combo, selected );
}
/** @brief Validate a @p mountPoint and adjust the UI
*
* If @p mountPoint is valid -- unused and starts with a /, for instance --
* then the button is enabled, label is cleared, and returns @c true.
*
* If it is not valid, returns @c false and sets the UI
* to explain why.
*/
bool validateMountPoint( const QString& mountPoint, const QStringList& inUse, QLabel* label, QPushButton* button );
/**
* Get the flags that have been checked in the list widget.
*/

View File

@ -451,7 +451,9 @@ void
PartitionPage::onRevertClicked()
{
ScanningDialog::run(
QtConcurrent::run( [this] {
QtConcurrent::run(
[ this ]
{
QMutexLocker locker( &m_revertMutex );
int oldIndex = m_ui->deviceComboBox->currentIndex();
@ -459,7 +461,8 @@ PartitionPage::onRevertClicked()
m_ui->deviceComboBox->setCurrentIndex( ( oldIndex < 0 ) ? 0 : oldIndex );
updateFromCurrentDevice();
} ),
[this] {
[ this ]
{
m_lastSelectedBootLoaderIndex = -1;
if ( m_ui->bootLoaderComboBox->currentIndex() < 0 )
{
@ -606,7 +609,8 @@ PartitionPage::updateFromCurrentDevice()
m_ui->partitionBarsView->selectionModel(),
&QItemSelectionModel::currentChanged,
this,
[=] {
[ = ]
{
QModelIndex selectedIndex = m_ui->partitionBarsView->selectionModel()->currentIndex();
selectedIndex = selectedIndex.sibling( selectedIndex.row(), 0 );
m_ui->partitionBarsView->setCurrentIndex( selectedIndex );

View File

@ -159,7 +159,9 @@ PartitionSplitterWidget::setSplitPartition( const QString& path, qint64 minSize,
m_itemToResizePath.clear();
}
PartitionSplitterItem itemToResize = _findItem( m_items, [path]( PartitionSplitterItem& item ) -> bool {
PartitionSplitterItem itemToResize = _findItem( m_items,
[ path ]( PartitionSplitterItem& item ) -> bool
{
if ( path == item.itemPath )
{
item.status = PartitionSplitterItem::Resizing;
@ -184,7 +186,9 @@ PartitionSplitterWidget::setSplitPartition( const QString& path, qint64 minSize,
qint64 newSize = m_itemToResize.size - preferredSize;
m_itemToResize.size = preferredSize;
int opCount = _eachItem( m_items, [preferredSize]( PartitionSplitterItem& item ) -> bool {
int opCount = _eachItem( m_items,
[ preferredSize ]( PartitionSplitterItem& item ) -> bool
{
if ( item.status == PartitionSplitterItem::Resizing )
{
item.size = preferredSize;
@ -358,7 +362,9 @@ PartitionSplitterWidget::mouseMoveEvent( QMouseEvent* event )
m_itemToResize.size = qRound64( span * percent );
m_itemToResizeNext.size -= m_itemToResize.size - oldsize;
_eachItem( m_items, [this]( PartitionSplitterItem& item ) -> bool {
_eachItem( m_items,
[ this ]( PartitionSplitterItem& item ) -> bool
{
if ( item.status == PartitionSplitterItem::Resizing )
{
item.size = m_itemToResize.size;

View File

@ -46,9 +46,10 @@ ReplaceWidget::ReplaceWidget( PartitionCoreModule* core, QComboBox* devicesCombo
m_ui->bootStatusLabel->clear();
updateFromCurrentDevice( devicesComboBox );
connect( devicesComboBox, &QComboBox::currentTextChanged, this, [=]( const QString& /* text */ ) {
updateFromCurrentDevice( devicesComboBox );
} );
connect( devicesComboBox,
&QComboBox::currentTextChanged,
this,
[ = ]( const QString& /* text */ ) { updateFromCurrentDevice( devicesComboBox ); } );
CALAMARES_RETRANSLATE( onPartitionSelected(); );
}

View File

@ -47,7 +47,11 @@ ScanningDialog::run( const QFuture< void >& future,
theDialog->show();
QFutureWatcher< void >* watcher = new QFutureWatcher< void >();
connect( watcher, &QFutureWatcher< void >::finished, theDialog, [watcher, theDialog, callback] {
connect( watcher,
&QFutureWatcher< void >::finished,
theDialog,
[ watcher, theDialog, callback ]
{
watcher->deleteLater();
theDialog->hide();
theDialog->deleteLater();

View File

@ -45,12 +45,20 @@ VolumeGroupBaseDialog::VolumeGroupBaseDialog( QString& vgName, QVector< const Pa
updateOkButton();
updateTotalSize();
connect( ui->pvList, &QListWidget::itemChanged, this, [&]( QListWidgetItem* ) {
connect( ui->pvList,
&QListWidget::itemChanged,
this,
[ & ]( QListWidgetItem* )
{
updateTotalSize();
updateOkButton();
} );
connect( ui->peSize, qOverload< int >( &QSpinBox::valueChanged ), this, [&]( int ) {
connect( ui->peSize,
qOverload< int >( &QSpinBox::valueChanged ),
this,
[ & ]( int )
{
updateTotalSectors();
updateOkButton();
} );

View File

@ -26,7 +26,9 @@ Calamares::JobResult
AutoMountManagementJob::exec()
{
cVerbose() << "this" << Logger::Pointer( this ) << "value" << Logger::Pointer( m_stored )
<< ( m_stored ? "restore" : m_disable ? "disable" : "enable" );
<< ( m_stored ? "restore"
: m_disable ? "disable"
: "enable" );
if ( m_stored )
{
CalamaresUtils::Partition::automountRestore( m_stored );

View File

@ -157,9 +157,11 @@ getLVMVolumes()
QStringList lvscanLines = QString::fromLocal8Bit( process.readAllStandardOutput() ).split( '\n' );
// Get the second column (`value(1)`) sinec that is the device name,
// remove quoting.
std::transform( lvscanLines.begin(), lvscanLines.end(), lvscanLines.begin(), []( const QString& lvscanLine ) {
return lvscanLine.simplified().split( ' ' ).value( 1 ).replace( '\'', "" );
} );
std::transform( lvscanLines.begin(),
lvscanLines.end(),
lvscanLines.begin(),
[]( const QString& lvscanLine )
{ return lvscanLine.simplified().split( ' ' ).value( 1 ).replace( '\'', "" ); } );
return lvscanLines;
}
else

View File

@ -9,6 +9,7 @@
#include "ClearTempMountsJob.h"
#include "partition/Mount.h"
#include "utils/Logger.h"
#include "utils/String.h"
@ -45,51 +46,23 @@ ClearTempMountsJob::exec()
{
Logger::Once o;
// Fetch a list of current mounts to Calamares temporary directories.
QList< QPair< QString, QString > > lst;
QFile mtab( "/etc/mtab" );
if ( !mtab.open( QFile::ReadOnly | QFile::Text ) )
{
return Calamares::JobResult::error( tr( "Cannot get list of temporary mounts." ) );
}
using MtabInfo = CalamaresUtils::Partition::MtabInfo;
auto targetMounts = MtabInfo::fromMtabFilteredByPrefix( QStringLiteral( "/tmp/calamares-" ) );
cVerbose() << o << "Opened mtab. Lines:";
QTextStream in( &mtab );
QString lineIn = in.readLine();
while ( !lineIn.isNull() )
{
QStringList line = lineIn.split( ' ', SplitSkipEmptyParts );
cVerbose() << o << line.join( ' ' );
QString device = line.at( 0 );
QString mountPoint = line.at( 1 );
if ( mountPoint.startsWith( "/tmp/calamares-" ) )
{
lst.append( qMakePair( device, mountPoint ) );
}
lineIn = in.readLine();
}
if ( lst.empty() )
if ( targetMounts.isEmpty() )
{
return Calamares::JobResult::ok();
}
std::sort(
lst.begin(), lst.end(), []( const QPair< QString, QString >& a, const QPair< QString, QString >& b ) -> bool {
return a.first > b.first;
} );
std::sort( targetMounts.begin(), targetMounts.end(), MtabInfo::mountPointOrder );
QStringList goodNews;
QProcess process;
for ( const auto& line : qAsConst( lst ) )
for ( const auto& m : qAsConst( targetMounts ) )
{
QString partPath = line.second;
cDebug() << o << "Will try to umount path" << partPath;
process.start( "umount", { "-lv", partPath } );
process.waitForFinished();
if ( process.exitCode() == 0 )
cDebug() << o << "Will try to umount path" << m.mountPoint;
if ( CalamaresUtils::Partition::unmount( m.mountPoint, { "-lv" } ) == 0 )
{
goodNews.append( QString( "Successfully unmounted %1." ).arg( partPath ) );
// Returns the program's exit code, so 0 is success
goodNews.append( QString( "Successfully unmounted %1." ).arg( m.mountPoint ) );
}
}

View File

@ -14,6 +14,7 @@
#include "core/KPMHelpers.h"
#include "partition/FileSystem.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include <kpmcore/core/device.h>
@ -67,7 +68,18 @@ FormatPartitionJob::prettyStatusMessage() const
Calamares::JobResult
FormatPartitionJob::exec()
{
return KPMHelpers::execute( CreateFileSystemOperation( *m_device, *m_partition, m_partition->fileSystem().type() ),
const auto fsType = m_partition->fileSystem().type();
auto r = KPMHelpers::execute( CreateFileSystemOperation( *m_device, *m_partition, fsType ),
tr( "The installer failed to format partition %1 on disk '%2'." )
.arg( m_partition->partitionPath(), m_device->name() ) );
if ( fsType == FileSystem::Xfs && r.succeeded() )
{
// We are going to try to set modern timestamps for the filesystem,
// (ignoring whether this succeeds). Requires a sufficiently-new
// xfs_admin and xfs_repair and might be made obsolete by newer
// kpmcore releases.
CalamaresUtils::System::runCommand( { "xfs_admin", "-O", "bigtime=1", m_partition->partitionPath() },
std::chrono::seconds( 60 ) );
}
return r;
}

View File

@ -32,9 +32,9 @@ getPartitionsForDevice_other( const QString& deviceName )
{
QProcess process;
process.setProgram( "sh" );
process.setArguments(
{ "-c",
QString( "echo $(awk '{print \"/dev/\"$4}' /proc/partitions | sed -e '/name/d' -e '/^$/d' -e '/[1-9]/!d' | grep %1)" )
process.setArguments( { "-c",
QString( "echo $(awk '{print \"/dev/\"$4}' /proc/partitions | sed -e '/name/d' -e '/^$/d' "
"-e '/[1-9]/!d' | grep %1)" )
.arg( deviceName ) } );
process.start();
process.waitForFinished();

View File

@ -0,0 +1,19 @@
# === This file is part of Calamares - <https://calamares.io> ===
#
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
#
calamares_add_plugin( umount
TYPE job
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
UmountJob.cpp
SHARED_LIB
EMERGENCY
)
calamares_add_test(
umounttest
SOURCES
Tests.cpp
)

View File

@ -0,0 +1,52 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "UmountJob.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include <QDir>
#include <QFile>
#include <QtTest/QtTest>
// Internals of UmountJob.cpp
// Actual tests
class UmountTests : public QObject
{
Q_OBJECT
public:
UmountTests() {}
~UmountTests() override {}
private Q_SLOTS:
void initTestCase();
void testTrue();
};
void
UmountTests::initTestCase()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
}
void
UmountTests::testTrue()
{
QVERIFY( true );
}
QTEST_GUILESS_MAIN( UmountTests )
#include "utils/moc-warnings.h"
#include "Tests.moc"

View File

@ -0,0 +1,158 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* Tags from the Python version of this module:
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
* SPDX-FileCopyrightText: 2016 Anke Boersma <demm@kaosx.us>
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
* Tags for the C++ version of this module:
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "UmountJob.h"
#include "partition/Mount.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include <QCoreApplication>
#include <QDir>
#include <QList>
UmountJob::UmountJob( QObject* parent )
: Calamares::CppJob( parent )
{
}
UmountJob::~UmountJob() {}
QString
UmountJob::prettyName() const
{
return tr( "Unmount file systems." );
}
static Calamares::JobResult
unmountTargetMounts( const QString& rootMountPoint )
{
QDir targetMount( rootMountPoint );
if ( !targetMount.exists() )
{
return Calamares::JobResult::internalError(
QCoreApplication::translate( UmountJob::staticMetaObject.className(), "Could not unmount target system." ),
QCoreApplication::translate( UmountJob::staticMetaObject.className(),
"The target system is not mounted at '%1'." )
.arg( rootMountPoint ),
Calamares::JobResult::GenericError );
}
QString targetMountPath = targetMount.absolutePath();
if ( !targetMountPath.endsWith( '/' ) )
{
targetMountPath.append( '/' );
}
using MtabInfo = CalamaresUtils::Partition::MtabInfo;
auto targetMounts = MtabInfo::fromMtabFilteredByPrefix( targetMountPath );
std::sort( targetMounts.begin(), targetMounts.end(), MtabInfo::mountPointOrder );
for ( const auto& m : qAsConst( targetMounts ) )
{
if ( CalamaresUtils::Partition::unmount( m.mountPoint, { "-lv" } ) )
{
// Returns the program's exit code, so 0 is success
return Calamares::JobResult::error(
QCoreApplication::translate( UmountJob::staticMetaObject.className(),
"Could not unmount target system." ),
QCoreApplication::translate( UmountJob::staticMetaObject.className(),
"The device '%1' is mounted in the target system. It is mounted at '%2'. "
"The device could not be unmounted." )
.arg( m.device, m.mountPoint ) );
}
}
return Calamares::JobResult::ok();
}
static Calamares::JobResult
exportZFSPools( const QString& rootMountPoint )
{
auto* gs = Calamares::JobQueue::instance()->globalStorage();
QStringList poolNames;
{
// The pools are dictionaries / VariantMaps
auto zfs_pool_list = gs->value( "zfsPoolInfo" ).toList();
for ( const auto& v : zfs_pool_list )
{
auto m = v.toMap();
QString poolName = m.value( "poolName" ).toString();
if ( !poolName.isEmpty() )
{
poolNames.append( poolName );
}
}
poolNames.sort();
}
for ( const auto& poolName : poolNames )
{
auto result = CalamaresUtils::System::runCommand( { "zpool", "export", poolName }, std::chrono::seconds( 30 ) );
if ( result.getExitCode() )
{
cWarning() << "Failed to export pool" << result.getOutput();
}
}
// Exporting ZFS pools does not cause the install to fail
return Calamares::JobResult::ok();
}
Calamares::JobResult
UmountJob::exec()
{
const auto* sys = CalamaresUtils::System::instance();
if ( !sys )
{
return Calamares::JobResult::internalError(
"UMount", tr( "No target system available." ), Calamares::JobResult::InvalidConfiguration );
}
Calamares::GlobalStorage* gs
= Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr;
if ( !gs || gs->value( "rootMountPoint" ).toString().isEmpty() )
{
return Calamares::JobResult::internalError(
"UMount", tr( "No rootMountPoint is set." ), Calamares::JobResult::InvalidConfiguration );
}
// Do the unmounting of target-system filesystems
{
auto r = unmountTargetMounts( gs->value( "rootMountPoint" ).toString() );
if ( !r )
{
return r;
}
}
// For ZFS systems, export the pools
{
auto r = exportZFSPools( gs->value( "rootMountPoint" ).toString() );
if ( !r )
{
return r;
}
}
return Calamares::JobResult::ok();
}
void
UmountJob::setConfigurationMap( const QVariantMap& map )
{
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( UmountJobFactory, registerPlugin< UmountJob >(); )

View File

@ -0,0 +1,41 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef UMOUNTJOB_H
#define UMOUNTJOB_H
#include "CppJob.h"
#include "DllMacro.h"
#include "utils/PluginFactory.h"
#include <QObject>
#include <QStringList>
#include <QVariantMap>
/** @brief Write 'random' data: machine id, entropy, UUIDs
*
*/
class PLUGINDLLEXPORT UmountJob : public Calamares::CppJob
{
Q_OBJECT
public:
explicit UmountJob( QObject* parent = nullptr );
~UmountJob() override;
QString prettyName() const override;
Calamares::JobResult exec() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( UmountJobFactory )
#endif // UMOUNTJOB_H

View File

@ -1,123 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# === This file is part of Calamares - <https://calamares.io> ===
#
# SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
# SPDX-FileCopyrightText: 2016 Anke Boersma <demm@kaosx.us>
# 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.
#
import os
import subprocess
import shutil
import libcalamares
from libcalamares.utils import gettext_path, gettext_languages
import gettext
_translation = gettext.translation("calamares-python",
localedir=gettext_path(),
languages=gettext_languages(),
fallback=True)
_ = _translation.gettext
_n = _translation.ngettext
def pretty_name():
return _( "Unmount file systems." )
def list_mounts(root_mount_point):
""" List mount points.
:param root_mount_point:
:return:
"""
lst = []
root_mount_point = os.path.normpath(root_mount_point)
for line in open("/etc/mtab").readlines():
device, mount_point, _ = line.split(" ", 2)
if os.path.commonprefix([root_mount_point, mount_point]) == root_mount_point:
lst.append((device, mount_point))
return lst
def export_zpools(root_mount_point):
""" Exports the zpools if defined in global storage
:param root_mount_point: The absolute path to the root of the install
:return:
"""
try:
zfs_pool_list = libcalamares.globalstorage.value("zfsPoolInfo")
zfs_pool_list.sort(reverse=True, key=lambda x: x["poolName"])
if zfs_pool_list:
for zfs_pool in zfs_pool_list:
try:
libcalamares.utils.host_env_process_output(['zpool', 'export', zfs_pool["poolName"]])
except subprocess.CalledProcessError:
libcalamares.utils.warning("Failed to export zpool")
except Exception as e:
# If this fails it shouldn't cause the installation to fail
libcalamares.utils.warning("Received exception while exporting zpools: " + format(e))
pass
def run():
""" Unmounts given mountpoints in decreasing order.
:return:
"""
root_mount_point = libcalamares.globalstorage.value("rootMountPoint")
if(libcalamares.job.configuration and
"srcLog" in libcalamares.job.configuration and
"destLog" in libcalamares.job.configuration):
libcalamares.utils.warning("Log-file preserving is **deprecated** in the *umount* module")
log_source = libcalamares.job.configuration["srcLog"]
log_destination = libcalamares.job.configuration["destLog"]
# Relocate log_destination into target system
log_destination = '{!s}/{!s}'.format(root_mount_point, log_destination)
# Make sure source is a string
log_source = '{!s}'.format(log_source)
# copy installation log before umount
if os.path.exists(log_source):
try:
shutil.copy2(log_source, log_destination)
except Exception as e:
libcalamares.utils.warning("Could not preserve file {!s}, "
"error {!s}".format(log_source, e))
if not root_mount_point:
return ("No mount point for root partition in globalstorage",
"globalstorage does not contain a \"rootMountPoint\" key, "
"doing nothing")
if not os.path.exists(root_mount_point):
return ("Bad mount point for root partition in globalstorage",
"globalstorage[\"rootMountPoint\"] is \"{}\", which does not "
"exist, doing nothing".format(root_mount_point))
lst = list_mounts(root_mount_point)
# Sort the list by mount point in decreasing order. This way we can be sure
# we unmount deeper dirs first.
lst.sort(key=lambda x: x[1], reverse=True)
for device, mount_point in lst:
# On success, no output; if the command fails, its output is
# in the exception object.
subprocess.check_output(["umount", "-lv", mount_point], stderr=subprocess.STDOUT)
export_zpools(root_mount_point)
os.rmdir(root_mount_point)
return None

View File

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

View File

@ -4,27 +4,11 @@
### Umount Module
#
# This module represents the last part of the installation, the unmounting
# of partitions used for the install. It is also the last place where it
# is possible to copy files to the target system.
#
# The "copy log files" functionality is deprecated; use the *preservefiles*
# module instead, which is more flexible.
#
# of partitions used for the install. After this, there is no regular way
# to modify the target system anymore.
#
---
# This is a **deprecated** example. Use the *preservefiles* module
# instead, where the equivalent configuration is this:
#
# files:
# - from: log
# dest: /var/log/installation.log
#
# Note that the "equivalent configuration" always finds the log,
# and is not dependent on specific user names or the vagaries of
# polkit configuration -- so it is a **better** "equivalent".
#
# example when using a log created by `sudo calamares -d`:
#srcLog: "/home/live/installation.log"
#destLog: "/var/log/installation.log"
srcLog: "/bogus/just/do/not/use/this/anymore.txt"
# Setting emergency to true will make it so this module is still run
# when a prior module fails
emergency: false

View File

@ -6,5 +6,4 @@ $id: https://calamares.io/schemas/umount
additionalProperties: false
type: object
properties:
srcLog: { type: string }
destLog: { type: string }
emergency: { type: boolean }

View File

@ -428,14 +428,16 @@ def run():
if not root_mount_point:
libcalamares.utils.warning("No mount point for root partition")
return (_("No mount point for root partition"),
_("globalstorage does not contain a \"rootMountPoint\" key, "
"doing nothing"))
_("globalstorage does not contain a \"rootMountPoint\" key."))
if not os.path.exists(root_mount_point):
libcalamares.utils.warning("Bad root mount point \"{}\"".format(root_mount_point))
return (_("Bad mount point for root partition"),
_("rootMountPoint is \"{}\", which does not "
"exist, doing nothing").format(root_mount_point))
_("rootMountPoint is \"{}\", which does not exist.".format(root_mount_point)))
if libcalamares.job.configuration.get("unpack", None) is None:
libcalamares.utils.warning("No *unpack* key in job configuration.")
return (_("Bad unpackfs configuration"),
_("There is no configuration information."))
supported_filesystems = get_supported_filesystems()
@ -450,17 +452,17 @@ def run():
if sourcefs not in supported_filesystems:
libcalamares.utils.warning("The filesystem for \"{}\" ({}) is not supported by your current kernel".format(source, sourcefs))
libcalamares.utils.warning(" ... modprobe {} may solve the problem".format(sourcefs))
return (_("Bad unsquash configuration"),
return (_("Bad unpackfs configuration"),
_("The filesystem for \"{}\" ({}) is not supported by your current kernel").format(source, sourcefs))
if not os.path.exists(source):
libcalamares.utils.warning("The source filesystem \"{}\" does not exist".format(source))
return (_("Bad unsquash configuration"),
return (_("Bad unpackfs configuration"),
_("The source filesystem \"{}\" does not exist").format(source))
if sourcefs == "squashfs":
if shutil.which("unsquashfs") is None:
libcalamares.utils.warning("Failed to find unsquashfs")
return (_("Bad unsquash configuration"),
return (_("Bad unpackfs configuration"),
_("Failed to find unsquashfs, make sure you have the squashfs-tools package installed.") +
" " + _("Failed to unpack image \"{}\"").format(source))
@ -475,7 +477,7 @@ def run():
if not os.path.isdir(destination) and sourcefs != "file":
libcalamares.utils.warning(("The destination \"{}\" in the target system is not a directory").format(destination))
if is_first:
return (_("Bad unsquash configuration"),
return (_("Bad unpackfs configuration"),
_("The destination \"{}\" in the target system is not a directory").format(destination))
else:
libcalamares.utils.debug(".. assuming that the previous targets will create that directory.")