Merge upstream development
This commit is contained in:
commit
e3bf37c261
37
CHANGES-3.2
37
CHANGES-3.2
@ -7,22 +7,55 @@ 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.51 (unreleased) #
|
||||
|
||||
This release contains contributions from (alphabetically by first name):
|
||||
- Erik Dubois
|
||||
- No external contributors yet
|
||||
|
||||
## Core ##
|
||||
- No core changes yet
|
||||
|
||||
## Modules ##
|
||||
- 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) #
|
||||
|
@ -41,7 +41,7 @@
|
||||
# TODO:3.3: Require CMake 3.12
|
||||
cmake_minimum_required( VERSION 3.3 FATAL_ERROR )
|
||||
project( CALAMARES
|
||||
VERSION 3.2.50
|
||||
VERSION 3.2.51
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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"/>
|
||||
|
4366
lang/calamares_ja-Hira.ts
Normal file
4366
lang/calamares_ja-Hira.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -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><h1>%1</h1><br/><strong>%2<br/>for %3</strong><br/><br/>Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Copyright 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Thanks to <a href="https://calamares.io/team/">the Calamares team</a> and the <a href="https://www.transifex.com/calamares/calamares/">Calamares translators team</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> development is sponsored by <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software.</source>
|
||||
<translation><h1>%1</h1><br/><strong>%2<br/>para %3</strong><br/><br/>Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Copyright 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Obrigado à <a href="https://calamares.io/team/">equipa Calamares</a> e à <a href="https://www.transifex.com/calamares/calamares/">equipa de tradutores do Calamares</a>.<br/><br/>O desenvolvimento do <a href="https://calamares.io/">Calamares</a> é patrocinado pela <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software.</translation>
|
||||
<translation><h1>%1</h1><br/><strong>%2<br/>para o %3</strong><br/><br/>Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Copyright 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Obrigado à <a href="https://calamares.io/team/">equipa Calamares</a> e à <a href="https://www.transifex.com/calamares/calamares/">equipa de tradutores do Calamares</a>.<br/><br/>O desenvolvimento do <a href="https://calamares.io/">Calamares</a> é patrocinado pela <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -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>Dă crash lui Calamares, pentru ca doctorul Konqui să 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"/>
|
||||
|
@ -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>
|
||||
|
@ -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"/>
|
||||
|
@ -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"
|
||||
|
385
lang/python/ja-Hira/LC_MESSAGES/python.po
Normal file
385
lang/python/ja-Hira/LC_MESSAGES/python.po
Normal 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 ""
|
@ -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"
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "partition/Sync.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/String.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QTemporaryDir>
|
||||
@ -92,7 +93,7 @@ struct TemporaryMount::Private
|
||||
|
||||
|
||||
TemporaryMount::TemporaryMount( const QString& devicePath, const QString& filesystemName, const QString& options )
|
||||
: m_d( std::make_unique<Private>() )
|
||||
: m_d( std::make_unique< Private >() )
|
||||
{
|
||||
m_d->m_devicePath = devicePath;
|
||||
m_d->m_mountDir.setAutoRemove( false );
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -46,6 +46,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
|
||||
|
@ -268,10 +268,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, calamares.job.configuration["efiBootloaderId"] )
|
||||
else:
|
||||
branding = libcalamares.globalstorage.value("branding")
|
||||
efi_bootloader_id = branding["bootloaderEntryName"]
|
||||
@ -390,7 +546,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
|
||||
|
||||
@ -407,7 +563,7 @@ 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()
|
||||
efi_bootloader_id = efi_label(efi_directory)
|
||||
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
|
||||
|
||||
if is_zfs:
|
||||
@ -462,7 +618,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()
|
||||
|
||||
@ -506,7 +662,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)
|
||||
|
||||
install_path = libcalamares.globalstorage.value("rootMountPoint")
|
||||
install_efi_directory = install_path + efi_directory
|
||||
|
7
src/modules/bootloader/tests/CMakeTests.txt
Normal file
7
src/modules/bootloader/tests/CMakeTests.txt
Normal 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}
|
||||
)
|
64
src/modules/bootloader/tests/test-bootloader-efiname.py
Normal file
64
src/modules/bootloader/tests/test-bootloader-efiname.py
Normal 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")
|
@ -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 );
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -180,7 +180,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"]:
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
auto newLocale = automaticLocaleConfiguration();
|
||||
if ( !m_selectedLocaleConfiguration.explicit_lang )
|
||||
{
|
||||
setLanguage( newLocale.language() );
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
@ -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
|
||||
@ -392,7 +392,7 @@ class PMPacman(PackageManager):
|
||||
global custom_status_message
|
||||
custom_status_message = "pacman: " + line.strip()
|
||||
libcalamares.job.setprogress(self.progress_fraction)
|
||||
libcalamares.utils.debug(line)
|
||||
libcalamares.utils.debug(line)
|
||||
|
||||
self.in_package_changes = False
|
||||
self.line_cb = line_cb
|
||||
@ -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")
|
||||
|
||||
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() ),
|
||||
tr( "The installer failed to format partition %1 on disk '%2'." )
|
||||
.arg( m_partition->partitionPath(), m_device->name() ) );
|
||||
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;
|
||||
}
|
||||
|
19
src/modules/umount/CMakeLists.txt
Normal file
19
src/modules/umount/CMakeLists.txt
Normal 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
|
||||
)
|
52
src/modules/umount/Tests.cpp
Normal file
52
src/modules/umount/Tests.cpp
Normal 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"
|
158
src/modules/umount/UmountJob.cpp
Normal file
158
src/modules/umount/UmountJob.cpp
Normal 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 >(); )
|
41
src/modules/umount/UmountJob.h
Normal file
41
src/modules/umount/UmountJob.h
Normal 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
|
@ -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
|
@ -1,7 +0,0 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
type: "job"
|
||||
name: "umount"
|
||||
interface: "python"
|
||||
script: "main.py"
|
@ -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
|
||||
|
@ -6,5 +6,4 @@ $id: https://calamares.io/schemas/umount
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
srcLog: { type: string }
|
||||
destLog: { type: string }
|
||||
emergency: { type: boolean }
|
||||
|
@ -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.")
|
||||
|
Loading…
Reference in New Issue
Block a user