Merge branch 'calamares' into work-3.3
This commit is contained in:
commit
ce23efae99
61
CHANGES-3.2
61
CHANGES-3.2
@ -7,6 +7,67 @@ 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.48 (unreleased) #
|
||||
|
||||
This release contains contributions from (alphabetically by first name):
|
||||
- Evan James
|
||||
|
||||
## Core ##
|
||||
- Python modules now have `warn()` and `error()` methods they can call,
|
||||
alongside the existing `debug()` and `warning()` (all live in the
|
||||
*libcalamares.utils* module).
|
||||
- Python modules can load YAML files via `libcalamares.utils.load_yaml()`.
|
||||
This may be the most useful for test-scripts.
|
||||
|
||||
## Modules ##
|
||||
- *fstab* now has a separate, special, flags-setting for swap subvolumes
|
||||
on btrfs. A swap subvolume is created if a swap **file** (not a separate
|
||||
partition) is selected in the auto-partitioning page. (Thanks Evan)
|
||||
- The *packages* module now has some special settings for the `pacman`
|
||||
package manager (generally used on Arch-derivatives). This allows
|
||||
tweaking of the installation process, if downloads are slow or
|
||||
packages may fail to install. See the `packages.conf` file for
|
||||
details. (Thanks Evan)
|
||||
|
||||
|
||||
# 3.2.47 (2021-11-19) #
|
||||
|
||||
This release contains contributions from (alphabetically by first name):
|
||||
- Evan James
|
||||
- Jonas Strassel
|
||||
|
||||
## Core ##
|
||||
- The translation for Sinhala (`si`) has reached 100%. Thank you to
|
||||
හෙළබස and Sandaruwan, translators for Sinhala, for special effort
|
||||
in completing that translation.
|
||||
- Logging now supports Redacted names. This reduces the scope for
|
||||
leaking names or other private information through the logs
|
||||
(if they are posted to a pastebin). A name is redacted consistently
|
||||
within one run of Calamares, but differently each time.
|
||||
|
||||
## Modules ##
|
||||
- *bootloader* with systemd-boot now handles root subvolumes better
|
||||
(Thanks Evan)
|
||||
- *displaymanager* supports the *greetd* display manager, which is a
|
||||
kind of meta-DM itself, supporting multiple greeters. (Thanks Jonas)
|
||||
- *finishedq* now has an extra example QML file that builds the UI in
|
||||
a different fashion, demonstrating how a mobile-OS customization of
|
||||
Calamares would present the "all done" message.
|
||||
- *fstab* has an example configuration file that mentioned `space_cache`
|
||||
as an option. Since 2014 there was only one possible value, so this
|
||||
option matched the default-and-only value. Newer kernels with newer
|
||||
btrfs versions have a `v2` option value as well. Remove the example
|
||||
option, since the kernel automatically picks the right value, while
|
||||
setting it to the wrong one may prevent the system from booting.
|
||||
(Thanks Evan)
|
||||
- The *partition* module no longer logs recognizable disk names or
|
||||
UUIDs. These are redacted in the logs. #1593
|
||||
- The *partition* module, together with the new *zfs* module and changes
|
||||
in *mount* and *bootloader* can install to ZFS **if** the distribution
|
||||
kernel supports it. ZFS tools are required, as well as the relevant
|
||||
kernel modules. See the `README.md` in the *zfs* module. (Thanks Evan)
|
||||
|
||||
|
||||
# 3.2.46 (2021-11-09) #
|
||||
|
||||
This release contains contributions from (alphabetically by first name):
|
||||
|
@ -55,6 +55,10 @@ project( CALAMARES
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
if( CALAMARES_RELEASE_MODE AND CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
|
||||
message( FATAL_ERROR "Do not build development versions in the source-directory." )
|
||||
endif()
|
||||
|
||||
### OPTIONS
|
||||
#
|
||||
option( INSTALL_CONFIG "Install configuration files" OFF )
|
||||
@ -139,12 +143,12 @@ set( CALAMARES_DESCRIPTION_SUMMARY
|
||||
# `txstats.py -e`. See also
|
||||
#
|
||||
# Total 81 languages
|
||||
set( _tx_complete az az_AZ ca cs_CZ fi_FI he hi hr ja ko lt pt_BR
|
||||
pt_PT sq sv tr_TR uk zh_CN zh_TW )
|
||||
set( _tx_good as be ca@valencia da de fr fur it_IT ml nl ru sk tg
|
||||
vi )
|
||||
set( _tx_ok ar ast bg bn el en_GB es es_MX et eu fa gl hu id is mr
|
||||
nb pl ro si sl sr sr@latin th )
|
||||
set( _tx_complete az az_AZ ca de fa fi_FI he hr ja ko lt pt_PT si
|
||||
sq tr_TR uk zh_TW )
|
||||
set( _tx_good as be ca@valencia cs_CZ da fr fur hi it_IT ml nl
|
||||
pt_BR ru sk sv tg vi zh_CN )
|
||||
set( _tx_ok ar ast bg bn el en_GB es es_MX et eu gl hu id is mr nb
|
||||
pl ro sl sr sr@latin th )
|
||||
set( _tx_incomplete en_HK en_IN eo es_PE es_PR fr_CH gu hi_IN id_ID
|
||||
ie kk kn ko_KR lo lv mk ne ne_NP te te_IN ur zh zh_HK )
|
||||
|
||||
|
@ -71,8 +71,8 @@ GenericName[eu]=Sistema instalatzailea
|
||||
Comment[eu]=Calamares - sistema instalatzailea
|
||||
Name[fa]=نصب سامانه
|
||||
Icon[fa]=کالامارس
|
||||
GenericName[fa]=نصبکنندهٔ سامانه
|
||||
Comment[fa]=کالامارس — نصبکنندهٔ سامانه
|
||||
GenericName[fa]=نصبکننده سامانه
|
||||
Comment[fa]=کالامارس — نصبکننده سامانه
|
||||
Name[es_PR]=Instalar el sistema
|
||||
Name[fr]=Installer le système
|
||||
Icon[fr]=calamares
|
||||
|
@ -29,7 +29,8 @@
|
||||
|
||||
* Double-check the *CALAMARES_VERSION* value at the top of `CMakeLists.txt`.
|
||||
* Set *CALAMARES_RELEASE_MODE* to `ON` in `CMakeLists.txt`.
|
||||
* Edit `CHANGES` and set the date of the release.
|
||||
* Edit `CHANGES-*` and set the date of the release. Pick the right
|
||||
file for the release-stream.
|
||||
* Commit both. This is usually done with commit-message
|
||||
*Changes: pre-release housekeeping*.
|
||||
|
||||
@ -81,24 +82,12 @@ Follow the instructions printed by the release script.
|
||||
|
||||
* Bump the version number in `CMakeLists.txt` in *CALAMARES_VERSION*.
|
||||
* Set *CALAMARES_RELEASE_MODE* back to `OFF`.
|
||||
* Add a placeholder entry for the next release in `CHANGES` with date
|
||||
text *not released yet*.
|
||||
* Add a placeholder entry for the next release in `CHANGES-*` with date
|
||||
text *not released yet*. See the text below, "Placeholder Release".
|
||||
Add the placeholder to the right file for the release-stream.
|
||||
* Commit and push that, usually with the message
|
||||
*Changes: post-release housekeeping*.
|
||||
|
||||
```
|
||||
# 3.2.XX (unreleased) #
|
||||
|
||||
This release contains contributions from (alphabetically by first name):
|
||||
- No external contributors yet
|
||||
|
||||
## Core ##
|
||||
- No core changes yet
|
||||
|
||||
## Modules ##
|
||||
- No module changes yet
|
||||
```
|
||||
|
||||
# Related Material
|
||||
|
||||
> This section isn't directly related to any specific release,
|
||||
@ -139,3 +128,18 @@ ssb rsa3072/0xCFDDC96F12B1915C
|
||||
- Upload that public key to the relevant GitHub profile.
|
||||
- Upload that public key to the Calamares site.
|
||||
|
||||
## Placeholder Release Notes
|
||||
|
||||
```
|
||||
# 3.2.XX (unreleased) #
|
||||
|
||||
This release contains contributions from (alphabetically by first name):
|
||||
- No external contributors yet
|
||||
|
||||
## Core ##
|
||||
- No core changes yet
|
||||
|
||||
## Modules ##
|
||||
- No module changes yet
|
||||
```
|
||||
|
||||
|
0
ci/configvalidator.py
Normal file → Executable file
0
ci/configvalidator.py
Normal file → Executable file
@ -20,4 +20,8 @@ def target_env_call(_): return 0
|
||||
|
||||
def check_target_env_call(_): pass
|
||||
|
||||
def target_env_process_output(cmd, *args): return 0
|
||||
|
||||
def host_env_process_output(cmd, *args): return 0
|
||||
|
||||
def mount(device, mountpoint, fstype, options): return 0
|
||||
|
0
ci/txcheck.sh
Normal file → Executable file
0
ci/txcheck.sh
Normal file → Executable file
0
ci/txreduce.py
Normal file → Executable file
0
ci/txreduce.py
Normal file → Executable file
@ -1,37 +0,0 @@
|
||||
# $FreeBSD$
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <adridg@FreeBSD.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
PORTNAME= calamares
|
||||
DISTVERSION= 3.2.25
|
||||
CATEGORIES= sysutils
|
||||
MASTER_SITES= https://github.com/${PORTNAME}/${PORTNAME}/releases/download/v${DISTVERSION}/
|
||||
|
||||
MAINTAINER= adridg@FreeBSD.org
|
||||
COMMENT= GUI System installer and OEM configurator
|
||||
|
||||
LICENSE= GPLv3
|
||||
LICENSE_FILE= ${WRKSRC}/LICENSE
|
||||
|
||||
LIB_DEPENDS= libyaml-cpp.so:devel/yaml-cpp \
|
||||
libpwquality.so:security/libpwquality \
|
||||
libboost_python${PYTHON_SUFFIX}.so:devel/boost-python-libs
|
||||
|
||||
USES= cmake compiler:c++17-lang gettext kde:5 pkgconfig \
|
||||
python:3.3+ qt:5
|
||||
USE_QT= concurrent core dbus declarative gui \
|
||||
network quickcontrols2 svg widgets xml \
|
||||
buildtools_build linguist_build qmake_build
|
||||
USE_KDE= coreaddons dbusaddons parts service \
|
||||
ecm_build
|
||||
USE_LDCONFIG= yes
|
||||
|
||||
CMAKE_OFF= WITH_KF5Crash \
|
||||
INSTALL_CONFIG \
|
||||
INSTALL_COMPLETION \
|
||||
INSTALL_POLKIT
|
||||
CMAKE_ON= CMAKE_DISABLE_FIND_PACKAGE_KPMcore
|
||||
CMAKE_ARGS= -DSKIP_MODULES="webview"
|
||||
|
||||
.include <bsd.port.mk>
|
@ -1,3 +0,0 @@
|
||||
TIMESTAMP = 1592339404
|
||||
SHA256 (calamares-3.2.25.tar.gz) = 797ce33db7d4e4c06bbccef95f6c4023f7628e91bd142896695565fed4ae8c4b
|
||||
SIZE (calamares-3.2.25.tar.gz) = 3580197
|
@ -1,14 +0,0 @@
|
||||
Calamares is an installer framework. By design it is very customizable,
|
||||
in order to satisfy a wide variety of needs and use cases.
|
||||
|
||||
Calamares aims to be easy, usable, beautiful, pragmatic, inclusive and
|
||||
distribution-agnostic.
|
||||
|
||||
Got a Linux distribution but no system installer? Grab Calamares, mix
|
||||
and match any number of Calamares modules (or write your own in Python
|
||||
or C++), throw together some branding, package it up and you are ready
|
||||
to ship!
|
||||
|
||||
(The above applies to FreeBSD as well)
|
||||
|
||||
WWW: https://calamares.io/
|
@ -1,224 +0,0 @@
|
||||
bin/calamares
|
||||
include/libcalamares/CalamaresConfig.h
|
||||
include/libcalamares/CppJob.h
|
||||
include/libcalamares/DllMacro.h
|
||||
include/libcalamares/GlobalStorage.h
|
||||
include/libcalamares/Job.h
|
||||
include/libcalamares/JobExample.h
|
||||
include/libcalamares/JobQueue.h
|
||||
include/libcalamares/ProcessJob.h
|
||||
include/libcalamares/PythonHelper.h
|
||||
include/libcalamares/PythonJob.h
|
||||
include/libcalamares/PythonJobApi.h
|
||||
include/libcalamares/Settings.h
|
||||
include/libcalamares/utils/BoostPython.h
|
||||
include/libcalamares/utils/CalamaresUtilsSystem.h
|
||||
include/libcalamares/utils/CommandList.h
|
||||
include/libcalamares/utils/Dirs.h
|
||||
include/libcalamares/utils/Entropy.h
|
||||
include/libcalamares/utils/Logger.h
|
||||
include/libcalamares/utils/NamedEnum.h
|
||||
include/libcalamares/utils/NamedSuffix.h
|
||||
include/libcalamares/utils/PluginFactory.h
|
||||
include/libcalamares/utils/RAII.h
|
||||
include/libcalamares/utils/Retranslator.h
|
||||
include/libcalamares/utils/String.h
|
||||
include/libcalamares/utils/Tests.h
|
||||
include/libcalamares/utils/UMask.h
|
||||
include/libcalamares/utils/Units.h
|
||||
include/libcalamares/utils/Variant.h
|
||||
include/libcalamares/utils/Yaml.h
|
||||
include/libcalamares/utils/moc-warnings.h
|
||||
lib/calamares/libcalamares.so
|
||||
lib/calamares/modules/bootloader/main.py
|
||||
lib/calamares/modules/bootloader/module.desc
|
||||
lib/calamares/modules/bootloader/test.yaml
|
||||
lib/calamares/modules/contextualprocess/libcalamares_job_contextualprocess.so
|
||||
lib/calamares/modules/contextualprocess/module.desc
|
||||
lib/calamares/modules/displaymanager/main.py
|
||||
lib/calamares/modules/displaymanager/module.desc
|
||||
lib/calamares/modules/dracut/main.py
|
||||
lib/calamares/modules/dracut/module.desc
|
||||
lib/calamares/modules/dracutlukscfg/libcalamares_job_dracutlukscfg.so
|
||||
lib/calamares/modules/dracutlukscfg/module.desc
|
||||
lib/calamares/modules/dummycpp/libcalamares_job_dummycpp.so
|
||||
lib/calamares/modules/dummycpp/module.desc
|
||||
lib/calamares/modules/dummyprocess/module.desc
|
||||
lib/calamares/modules/dummypython/main.py
|
||||
lib/calamares/modules/dummypython/module.desc
|
||||
lib/calamares/modules/finished/libcalamares_viewmodule_finished.so
|
||||
lib/calamares/modules/finished/module.desc
|
||||
lib/calamares/modules/fstab/main.py
|
||||
lib/calamares/modules/fstab/module.desc
|
||||
lib/calamares/modules/fstab/test.yaml
|
||||
lib/calamares/modules/grubcfg/main.py
|
||||
lib/calamares/modules/grubcfg/module.desc
|
||||
lib/calamares/modules/hostinfo/libcalamares_job_hostinfo.so
|
||||
lib/calamares/modules/hostinfo/module.desc
|
||||
lib/calamares/modules/hwclock/main.py
|
||||
lib/calamares/modules/hwclock/module.desc
|
||||
lib/calamares/modules/initcpio/libcalamares_job_initcpio.so
|
||||
lib/calamares/modules/initcpio/module.desc
|
||||
lib/calamares/modules/initcpiocfg/main.py
|
||||
lib/calamares/modules/initcpiocfg/module.desc
|
||||
lib/calamares/modules/initramfs/libcalamares_job_initramfs.so
|
||||
lib/calamares/modules/initramfs/module.desc
|
||||
lib/calamares/modules/initramfscfg/encrypt_hook
|
||||
lib/calamares/modules/initramfscfg/encrypt_hook_nokey
|
||||
lib/calamares/modules/initramfscfg/main.py
|
||||
lib/calamares/modules/initramfscfg/module.desc
|
||||
lib/calamares/modules/interactiveterminal/libcalamares_viewmodule_interactiveterminal.so
|
||||
lib/calamares/modules/interactiveterminal/module.desc
|
||||
lib/calamares/modules/keyboard/libcalamares_viewmodule_keyboard.so
|
||||
lib/calamares/modules/keyboard/module.desc
|
||||
lib/calamares/modules/keyboardq/libcalamares_viewmodule_keyboardq.so
|
||||
lib/calamares/modules/keyboardq/module.desc
|
||||
lib/calamares/modules/license/libcalamares_viewmodule_license.so
|
||||
lib/calamares/modules/license/module.desc
|
||||
lib/calamares/modules/locale/libcalamares_viewmodule_locale.so
|
||||
lib/calamares/modules/locale/module.desc
|
||||
lib/calamares/modules/localecfg/main.py
|
||||
lib/calamares/modules/localecfg/module.desc
|
||||
lib/calamares/modules/localeq/libcalamares_viewmodule_localeq.so
|
||||
lib/calamares/modules/localeq/module.desc
|
||||
lib/calamares/modules/luksbootkeyfile/libcalamares_job_luksbootkeyfile.so
|
||||
lib/calamares/modules/luksbootkeyfile/module.desc
|
||||
lib/calamares/modules/luksopenswaphookcfg/main.py
|
||||
lib/calamares/modules/luksopenswaphookcfg/module.desc
|
||||
lib/calamares/modules/machineid/libcalamares_job_machineid.so
|
||||
lib/calamares/modules/machineid/module.desc
|
||||
lib/calamares/modules/mount/main.py
|
||||
lib/calamares/modules/mount/module.desc
|
||||
lib/calamares/modules/mount/test.yaml
|
||||
lib/calamares/modules/netinstall/libcalamares_viewmodule_netinstall.so
|
||||
lib/calamares/modules/netinstall/module.desc
|
||||
lib/calamares/modules/networkcfg/main.py
|
||||
lib/calamares/modules/networkcfg/module.desc
|
||||
lib/calamares/modules/notesqml/libcalamares_viewmodule_notesqml.so
|
||||
lib/calamares/modules/notesqml/module.desc
|
||||
lib/calamares/modules/oemid/libcalamares_viewmodule_oemid.so
|
||||
lib/calamares/modules/oemid/module.desc
|
||||
lib/calamares/modules/openrcdmcryptcfg/main.py
|
||||
lib/calamares/modules/openrcdmcryptcfg/module.desc
|
||||
lib/calamares/modules/packagechooser/libcalamares_viewmodule_packagechooser.so
|
||||
lib/calamares/modules/packagechooser/module.desc
|
||||
lib/calamares/modules/packages/main.py
|
||||
lib/calamares/modules/packages/module.desc
|
||||
lib/calamares/modules/packages/test.yaml
|
||||
lib/calamares/modules/plymouthcfg/main.py
|
||||
lib/calamares/modules/plymouthcfg/module.desc
|
||||
lib/calamares/modules/preservefiles/libcalamares_job_preservefiles.so
|
||||
lib/calamares/modules/preservefiles/module.desc
|
||||
lib/calamares/modules/rawfs/main.py
|
||||
lib/calamares/modules/rawfs/module.desc
|
||||
lib/calamares/modules/removeuser/libcalamares_job_removeuser.so
|
||||
lib/calamares/modules/removeuser/module.desc
|
||||
lib/calamares/modules/services-openrc/main.py
|
||||
lib/calamares/modules/services-openrc/module.desc
|
||||
lib/calamares/modules/services-systemd/main.py
|
||||
lib/calamares/modules/services-systemd/module.desc
|
||||
lib/calamares/modules/shellprocess/libcalamares_job_shellprocess.so
|
||||
lib/calamares/modules/shellprocess/module.desc
|
||||
lib/calamares/modules/summary/libcalamares_viewmodule_summary.so
|
||||
lib/calamares/modules/summary/module.desc
|
||||
lib/calamares/modules/tracking/libcalamares_viewmodule_tracking.so
|
||||
lib/calamares/modules/tracking/module.desc
|
||||
lib/calamares/modules/umount/main.py
|
||||
lib/calamares/modules/umount/module.desc
|
||||
lib/calamares/modules/unpackfs/main.py
|
||||
lib/calamares/modules/unpackfs/module.desc
|
||||
lib/calamares/modules/unpackfs/runtests.sh
|
||||
lib/calamares/modules/users/libcalamares_viewmodule_users.so
|
||||
lib/calamares/modules/users/module.desc
|
||||
lib/calamares/modules/welcome/libcalamares_viewmodule_welcome.so
|
||||
lib/calamares/modules/welcome/module.desc
|
||||
lib/calamares/modules/welcomeq/libcalamares_viewmodule_welcomeq.so
|
||||
lib/calamares/modules/welcomeq/module.desc
|
||||
lib/cmake/Calamares/CMakeColors.cmake
|
||||
lib/cmake/Calamares/CalamaresAddBrandingSubdirectory.cmake
|
||||
lib/cmake/Calamares/CalamaresAddLibrary.cmake
|
||||
lib/cmake/Calamares/CalamaresAddModuleSubdirectory.cmake
|
||||
lib/cmake/Calamares/CalamaresAddPlugin.cmake
|
||||
lib/cmake/Calamares/CalamaresAddTest.cmake
|
||||
lib/cmake/Calamares/CalamaresAddTranslations.cmake
|
||||
lib/cmake/Calamares/CalamaresAutomoc.cmake
|
||||
lib/cmake/Calamares/CalamaresConfig.cmake
|
||||
lib/cmake/Calamares/CalamaresConfigVersion.cmake
|
||||
lib/cmake/Calamares/CalamaresLibraryDepends-%%CMAKE_BUILD_TYPE%%.cmake
|
||||
lib/cmake/Calamares/CalamaresLibraryDepends.cmake
|
||||
lib/cmake/Calamares/CalamaresUse.cmake
|
||||
lib/libcalamares.so
|
||||
lib/libcalamares.so.3.2.25
|
||||
lib/libcalamaresui.so
|
||||
lib/libcalamaresui.so.3.2.25
|
||||
man/man8/calamares.8.gz
|
||||
share/applications/calamares.desktop
|
||||
%%DATADIR%%/branding/default/banner.png
|
||||
%%DATADIR%%/branding/default/branding.desc
|
||||
%%DATADIR%%/branding/default/lang/calamares-default_ar.qm
|
||||
%%DATADIR%%/branding/default/lang/calamares-default_en.qm
|
||||
%%DATADIR%%/branding/default/lang/calamares-default_eo.qm
|
||||
%%DATADIR%%/branding/default/lang/calamares-default_fr.qm
|
||||
%%DATADIR%%/branding/default/lang/calamares-default_nl.qm
|
||||
%%DATADIR%%/branding/default/languages.png
|
||||
%%DATADIR%%/branding/default/show.qml
|
||||
%%DATADIR%%/branding/default/squid.png
|
||||
%%DATADIR%%/branding/default/stylesheet.qss
|
||||
%%DATADIR%%/qml/calamares/slideshow/BackButton.qml
|
||||
%%DATADIR%%/qml/calamares/slideshow/ForwardButton.qml
|
||||
%%DATADIR%%/qml/calamares/slideshow/NavButton.qml
|
||||
%%DATADIR%%/qml/calamares/slideshow/Presentation.qml
|
||||
%%DATADIR%%/qml/calamares/slideshow/Slide.qml
|
||||
%%DATADIR%%/qml/calamares/slideshow/SlideCounter.qml
|
||||
%%DATADIR%%/qml/calamares/slideshow/qmldir
|
||||
share/icons/hicolor/scalable/apps/calamares.svg
|
||||
share/locale/ar/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/as/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/ast/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/be/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/bg/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/ca/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/cs_CZ/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/da/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/de/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/el/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/en_GB/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/eo/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/es/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/es_MX/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/es_PR/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/et/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/eu/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/fi_FI/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/fr/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/gl/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/he/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/hi/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/hr/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/hu/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/id/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/is/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/it_IT/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/ja/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/ko/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/lt/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/ml/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/mr/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/nb/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/nl/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/pl/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/pt_BR/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/pt_PT/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/ro/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/ru/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/sk/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/sl/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/sq/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/sr/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/sr@latin/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/sv/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/th/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/tr_TR/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/uk/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/zh_CN/LC_MESSAGES/calamares-python.mo
|
||||
share/locale/zh_TW/LC_MESSAGES/calamares-python.mo
|
@ -689,27 +689,27 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir.</transl
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||
<source>Successfully unmounted %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 uğurla ayrıldı.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||
<source>Successfully disabled swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 mübadilə bölməsi uğurla söndürüldü.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||
<source>Successfully cleared swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 mübadilə bölməsi uğurla təmizləndi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||
<source>Successfully closed mapper device %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Yerləşdirmə cihazı %1 uğurla bağlandı</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||
<source>Successfully disabled volume group %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tutum qrupu %1, uğurla söndürüldü</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
||||
|
@ -689,27 +689,27 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir.</transl
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||
<source>Successfully unmounted %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 uğurla ayrıldı.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||
<source>Successfully disabled swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 mübadilə bölməsi uğurla söndürüldü.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||
<source>Successfully cleared swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 mübadilə bölməsi uğurla təmizləndi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||
<source>Successfully closed mapper device %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Yerləşdirmə cihazı %1 uğurla bağlandı</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||
<source>Successfully disabled volume group %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Tutum qrupu %1, uğurla söndürüldü</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
||||
|
@ -689,27 +689,27 @@ L'instal·lador es tancarà i tots els canvis es perdran.</translation>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||
<source>Successfully unmounted %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>S'ha desmuntat correctament %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||
<source>Successfully disabled swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>S'ha inhabilitat correctament l'intercanvi %1.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||
<source>Successfully cleared swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>S'ha netejat correctament l'intercanvi %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>El dispositiu de mapatge %1 s'ha tancat correctament.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||
<source>Successfully disabled volume group %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>El grup de volums %1 s'ha inhabilitat correctament.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -691,27 +691,27 @@ Instalacijski program će izaći i sve promjene će biti izgubljene.</translatio
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||
<source>Successfully unmounted %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Uspješno demontiran %1. </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||
<source>Successfully disabled swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Uspješno onemogućen 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>Uspješno obrisan 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>Uspješno zatvoren device mapper %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>Volume grupa %1 je uspješno onemogućena.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
||||
|
@ -687,27 +687,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"/>
|
||||
|
@ -689,27 +689,27 @@ O instalador será encerrado e todas as alterações serão perdidas.</translati
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||
<source>Successfully unmounted %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>% 1 desmontado com sucesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||
<source>Successfully disabled swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Swap %1 desativada com sucesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||
<source>Successfully cleared swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Swap % 1 limpa com sucesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||
<source>Successfully closed mapper device %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Dispositivo mapeador % 1 fechado com sucesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||
<source>Successfully disabled volume group %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Grupo de volume % 1 desativado com sucesso.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
||||
|
1028
lang/calamares_si.ts
1028
lang/calamares_si.ts
File diff suppressed because it is too large
Load Diff
@ -690,27 +690,27 @@ Yükleyiciden çıkınca tüm değişiklikler kaybedilecek.</translation>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||
<source>Successfully unmounted %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 bağlantısı başarıyla kaldırıldı.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||
<source>Successfully disabled swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 takas alanı başarıyla devre dışı bırakıldı.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||
<source>Successfully cleared swap %1.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>%1 takas alanı başarıyla temizlendi.</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 eşleyici aygıtı başarıyla kapatıldı.</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 birim grubu başarıyla devre dışı bırakıldı.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
||||
@ -867,12 +867,12 @@ Kurulum devam edebilir fakat bazı özellikler devre dışı kalabilir.</transla
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="255"/>
|
||||
<source><h1>Welcome to the Calamares installer for %1</h1></source>
|
||||
<translation><h1>%1 Calamares Sistem Yükleyiciye Hoşgeldiniz</h1></translation>
|
||||
<translation><h1>%1 Calamares Sistem Yükleyiciye Hoş Geldiniz</h1></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/Config.cpp" line="256"/>
|
||||
<source><h1>Welcome to the %1 installer</h1></source>
|
||||
<translation><h1>%1 Sistem Yükleyiciye Hoşgeldiniz</h1></translation>
|
||||
<translation><h1>%1 Sistem Yükleyiciye Hoş Geldiniz</h1></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/users/Config.cpp" line="217"/>
|
||||
@ -3978,7 +3978,7 @@ Output:
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/WelcomePage.cpp" line="217"/>
|
||||
<source><h1>Welcome to %1 setup.</h1></source>
|
||||
<translation><h1>%1 Kurulumuna Hoşgeldiniz.</h1></translation>
|
||||
<translation><h1>%1 Kurulumuna Hoş Geldiniz.</h1></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/modules/welcome/WelcomePage.cpp" line="222"/>
|
||||
|
@ -323,7 +323,7 @@ msgstr "<code>{name!s}</code> systemd hədəfi aktiv edilmədi"
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "<code>{name!s}</code> systemd taymeri aktiv edilə bilmir."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -407,6 +407,7 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Unsquashfs tapılmadı, squashfs-tools paketinin quraşdırıldığına əmin olun."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -323,7 +323,7 @@ msgstr "<code>{name!s}</code> systemd hədəfi aktiv edilmədi"
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "<code>{name!s}</code> systemd taymeri aktiv edilə bilmir."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -407,6 +407,7 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Unsquashfs tapılmadı, squashfs-tools paketinin quraşdırıldığına əmin olun."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -327,6 +327,7 @@ msgstr "No es pot habilitar la destinació de systemd <code>{name!s}</code>."
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
"No es pot habilitar el temporitzador de systemd <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -406,6 +407,8 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"No s'ha pogut trobar unsquashfs, assegureu-vos que tingueu instal·lat el "
|
||||
"paquet squashfs-tools."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -329,7 +329,7 @@ msgstr "Das systemd-Ziel <code>{name!s}</code> kann nicht aktiviert werden."
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "Systemd-Timer <code>{name!s}</code> kann nicht aktiviert werden."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -412,6 +412,8 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Unsquashfs nicht gefunden, stellen Sie sicher, dass das Paket squashfs-tools"
|
||||
" installiert ist."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -6,6 +6,7 @@
|
||||
# Translators:
|
||||
# Danial Behzadi <dani.behzi@ubuntu.com>, 2020
|
||||
# alireza jamshidi <alirezajam98@gmail.com>, 2020
|
||||
# Mahdy Mirzade <me@mahdym.ir>, 2021
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
@ -14,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: alireza jamshidi <alirezajam98@gmail.com>, 2020\n"
|
||||
"Last-Translator: Mahdy Mirzade <me@mahdym.ir>, 2021\n"
|
||||
"Language-Team: Persian (https://www.transifex.com/calamares/teams/20061/fa/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -62,13 +63,15 @@ msgstr "نصب بارکنندهٔ راهاندازی."
|
||||
|
||||
#: src/modules/bootloader/main.py:508
|
||||
msgid "Bootloader installation error"
|
||||
msgstr ""
|
||||
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 ""
|
||||
"بوت لودر نتوانست نصب شود. دستور <pre>{!s}</pre> برای نصب با خطای {!s} مواجه "
|
||||
"شد."
|
||||
|
||||
#: src/modules/fstab/main.py:29
|
||||
msgid "Writing fstab."
|
||||
@ -77,6 +80,7 @@ msgstr "در حال نوشتن fstab."
|
||||
#: src/modules/fstab/main.py:389
|
||||
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
"هیچ تنظیمات <pre>{!s}</pre> برای استفاده برای <pre>{!s}</pre> داده نشده است."
|
||||
|
||||
#: src/modules/dracut/main.py:27
|
||||
msgid "Creating initramfs with dracut."
|
||||
@ -139,6 +143,8 @@ msgid ""
|
||||
"The displaymanagers list is empty or undefined in both globalstorage and "
|
||||
"displaymanager.conf."
|
||||
msgstr ""
|
||||
"فهرست مدیریت صفحه نمایش ها خالی بوده یا در محل ذخیره داده و "
|
||||
"displaymanager.conf تعریف نشده است."
|
||||
|
||||
#: src/modules/displaymanager/main.py:989
|
||||
msgid "Display manager configuration was incomplete"
|
||||
@ -161,6 +167,8 @@ msgid ""
|
||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||
"level {level!s}."
|
||||
msgstr ""
|
||||
"دستور سرویس <code>{arg!s}</code> برای سرویس {name!s} در سطح اجرای {level!s}"
|
||||
" ناشناخته است."
|
||||
|
||||
#: src/modules/services-openrc/main.py:93
|
||||
#: src/modules/services-systemd/main.py:59
|
||||
@ -171,6 +179,8 @@ msgstr "نمیتوان خدمت را دستکاری کرد"
|
||||
msgid ""
|
||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||
msgstr ""
|
||||
"فراخوانی <code>rc-update {arg!s}</code> در chroot کد خطای {num!s} را "
|
||||
"برگرداند."
|
||||
|
||||
#: src/modules/services-openrc/main.py:101
|
||||
msgid "Target runlevel does not exist"
|
||||
@ -181,6 +191,8 @@ msgid ""
|
||||
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
msgstr ""
|
||||
"مسیر برای سطح اجرای {level!s} برابر <code>{path!s}</code> است، که وجود "
|
||||
"ندارد."
|
||||
|
||||
#: src/modules/services-openrc/main.py:110
|
||||
msgid "Target service does not exist"
|
||||
@ -191,6 +203,7 @@ msgid ""
|
||||
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
||||
"exist."
|
||||
msgstr ""
|
||||
"مسیر برای سرویس {name!s} برابر <code>{path!s}</code> است، که وجود ندارد."
|
||||
|
||||
#: src/modules/networkcfg/main.py:29
|
||||
msgid "Saving network configuration."
|
||||
@ -223,25 +236,31 @@ msgstr[1] "در حال برداشتن %(num)d بسته."
|
||||
#: src/modules/packages/main.py:638 src/modules/packages/main.py:650
|
||||
#: src/modules/packages/main.py:678
|
||||
msgid "Package Manager error"
|
||||
msgstr ""
|
||||
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 ""
|
||||
"مدیر بسته نتوانست برای بروزرسانی ها آماده شود، دستور <pre>{!s}</pre> با خطای"
|
||||
" {!s} مواجه شد."
|
||||
|
||||
#: 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 ""
|
||||
"مدیر بسته نتوانست سامانه را بروز کند. دستور <pre>{!s}</pre> با خطای {!s} "
|
||||
"مواجه شد."
|
||||
|
||||
#: 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 ""
|
||||
"مدیر بسته نتوانست تغییرات را برای نصب سامانه انجام دهد. دستور "
|
||||
"<pre>{!s}</pre> با خطای {!s} مواجه شد."
|
||||
|
||||
#: src/modules/plymouthcfg/main.py:27
|
||||
msgid "Configure Plymouth theme"
|
||||
@ -306,7 +325,7 @@ msgstr "نمیتوان هدف سیستمدی <code>{name!s}</code> را ب
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "نمیتوان تایمر سیستمدی <code>{name!s}</code> را به کار انداخت."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -326,11 +345,11 @@ msgstr ""
|
||||
|
||||
#: src/modules/mkinitfs/main.py:27
|
||||
msgid "Creating initramfs with mkinitfs."
|
||||
msgstr ""
|
||||
msgstr "درحال ایجاد initramfs با mkinitfs."
|
||||
|
||||
#: src/modules/mkinitfs/main.py:49
|
||||
msgid "Failed to run mkinitfs on the target"
|
||||
msgstr ""
|
||||
msgstr "شکست در اجرا mkinitfs روی هدف"
|
||||
|
||||
#: src/modules/unpackfs/main.py:34
|
||||
msgid "Filling up filesystems."
|
||||
@ -385,7 +404,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"
|
||||
|
@ -325,7 +325,7 @@ msgstr "Ne mogu omogućiti systemd cilj <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "Nije moguće omogućiti systemd timer <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -405,6 +405,8 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Neuspješno pronalaženje unsquashfs, provjerite imate li instaliran paket "
|
||||
"squashfs-tools."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -307,7 +307,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 "시스템 타이머 <code>{name!s}</code>를 활성화할 수 없습니다."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -386,7 +386,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"
|
||||
|
@ -329,7 +329,7 @@ msgstr "Não é possível ativar o destino do systemd <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "Não é possível ativar o temporizador systemd <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -411,6 +411,8 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Falha ao localizar o unsquashfs, certifique-se de que tem o pacote squashfs-"
|
||||
"tools instalado."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -5,6 +5,7 @@
|
||||
#
|
||||
# Translators:
|
||||
# හෙළබස, 2021
|
||||
# Sandaruwan Samaraweera, 2021
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
@ -13,7 +14,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: හෙළබස, 2021\n"
|
||||
"Last-Translator: Sandaruwan Samaraweera, 2021\n"
|
||||
"Language-Team: Sinhala (https://www.transifex.com/calamares/teams/20061/si/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -23,7 +24,7 @@ msgstr ""
|
||||
|
||||
#: src/modules/initramfscfg/main.py:32
|
||||
msgid "Configuring initramfs."
|
||||
msgstr ""
|
||||
msgstr "initramfs වින්යාස කිරීම."
|
||||
|
||||
#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89
|
||||
#: src/modules/fstab/main.py:355 src/modules/fstab/main.py:361
|
||||
@ -35,161 +36,169 @@ msgstr ""
|
||||
#: src/modules/luksopenswaphookcfg/main.py:86
|
||||
#: src/modules/luksopenswaphookcfg/main.py:90
|
||||
msgid "Configuration Error"
|
||||
msgstr ""
|
||||
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 ""
|
||||
msgstr "{!s} සඳහා භාවිතා කිරීමට කිසිදු කොටස් නිර්වචනය කර නොමැත."
|
||||
|
||||
#: 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 ""
|
||||
msgstr "{!s} සඳහා භාවිතා කිරීමට root mount point ලබා දී නොමැත."
|
||||
|
||||
#: src/modules/grubcfg/main.py:28
|
||||
msgid "Configure GRUB."
|
||||
msgstr ""
|
||||
msgstr "GRUB වින්යාස කරන්න."
|
||||
|
||||
#: src/modules/bootloader/main.py:43
|
||||
msgid "Install bootloader."
|
||||
msgstr ""
|
||||
msgstr "bootloader ස්ථාපනය කරන්න."
|
||||
|
||||
#: src/modules/bootloader/main.py:508
|
||||
msgid "Bootloader installation error"
|
||||
msgstr ""
|
||||
msgstr "Bootloader ස්ථාපනය කිරීමේ දෝෂයකි"
|
||||
|
||||
#: src/modules/bootloader/main.py:509
|
||||
msgid ""
|
||||
"The bootloader could not be installed. The installation command "
|
||||
"<pre>{!s}</pre> returned error code {!s}."
|
||||
msgstr ""
|
||||
"ඇරඹුම් කාරකය ස්ථාපනය කල නොහැක. ස්ථාපන විධානය <pre>{!s}</pre> දෝෂ කේතය {!s} "
|
||||
"ලබා දුන්නේය."
|
||||
|
||||
#: src/modules/fstab/main.py:29
|
||||
msgid "Writing fstab."
|
||||
msgstr ""
|
||||
msgstr "fstab ලියමින්."
|
||||
|
||||
#: src/modules/fstab/main.py:389
|
||||
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
||||
msgstr ""
|
||||
"භාවිතා කිරීමට <pre>{!s}</pre> සඳහා <pre>{!s}</pre> වින්යාසයක් ලබා දී නොමැත."
|
||||
|
||||
#: src/modules/dracut/main.py:27
|
||||
msgid "Creating initramfs with dracut."
|
||||
msgstr ""
|
||||
msgstr "dracut සමඟ initramfs නිර්මාණය කිරීම."
|
||||
|
||||
#: src/modules/dracut/main.py:49
|
||||
msgid "Failed to run dracut on the target"
|
||||
msgstr ""
|
||||
msgstr "ඉලක්කය මත ඩ්රැකට් ධාවනය කිරීමට අපොහොසත් විය"
|
||||
|
||||
#: src/modules/dracut/main.py:50 src/modules/mkinitfs/main.py:50
|
||||
msgid "The exit code was {}"
|
||||
msgstr ""
|
||||
msgstr "පිටවීමේ කේතය වූයේ {}"
|
||||
|
||||
#: src/modules/displaymanager/main.py:526
|
||||
msgid "Cannot write KDM configuration file"
|
||||
msgstr ""
|
||||
msgstr "KDM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||
|
||||
#: src/modules/displaymanager/main.py:527
|
||||
msgid "KDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
msgstr "KDM වින්යාස ගොනුව {!s} නොපවතී"
|
||||
|
||||
#: src/modules/displaymanager/main.py:588
|
||||
msgid "Cannot write LXDM configuration file"
|
||||
msgstr ""
|
||||
msgstr "LXDM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||
|
||||
#: src/modules/displaymanager/main.py:589
|
||||
msgid "LXDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
msgstr "LXDM වින්යාස ගොනුව {!s} නොපවතී"
|
||||
|
||||
#: src/modules/displaymanager/main.py:672
|
||||
msgid "Cannot write LightDM configuration file"
|
||||
msgstr ""
|
||||
msgstr "LightDM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||
|
||||
#: src/modules/displaymanager/main.py:673
|
||||
msgid "LightDM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
msgstr "LightDM වින්යාස ගොනුව {!s} නොපවතී"
|
||||
|
||||
#: src/modules/displaymanager/main.py:747
|
||||
msgid "Cannot configure LightDM"
|
||||
msgstr ""
|
||||
msgstr "LightDM වින්යාස කළ නොහැක"
|
||||
|
||||
#: src/modules/displaymanager/main.py:748
|
||||
msgid "No LightDM greeter installed."
|
||||
msgstr ""
|
||||
msgstr "LightDM ග්රීටර් ස්ථාපනය කර නැත."
|
||||
|
||||
#: src/modules/displaymanager/main.py:779
|
||||
msgid "Cannot write SLIM configuration file"
|
||||
msgstr ""
|
||||
msgstr "SLIM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||
|
||||
#: src/modules/displaymanager/main.py:780
|
||||
msgid "SLIM config file {!s} does not exist"
|
||||
msgstr ""
|
||||
msgstr "SLIM වින්යාස ගොනුව {!s} නොපවතී"
|
||||
|
||||
#: src/modules/displaymanager/main.py:906
|
||||
msgid "No display managers selected for the displaymanager module."
|
||||
msgstr ""
|
||||
msgstr "සංදර්ශක කළමනාකරු මොඩියුලය සඳහා සංදර්ශක කළමනාකරුවන් තෝරාගෙන නොමැත."
|
||||
|
||||
#: src/modules/displaymanager/main.py:907
|
||||
msgid ""
|
||||
"The displaymanagers list is empty or undefined in both globalstorage and "
|
||||
"displaymanager.conf."
|
||||
msgstr ""
|
||||
"ගෝලීය ගබඩාව සහ displaymanager.conf යන දෙකෙහිම සංදර්ශක කළමනාකරු ලැයිස්තුව "
|
||||
"හිස් හෝ අර්ථ දක්වා නොමැත."
|
||||
|
||||
#: src/modules/displaymanager/main.py:989
|
||||
msgid "Display manager configuration was incomplete"
|
||||
msgstr ""
|
||||
msgstr "සංදර්ශක කළමනාකරු වින්යාසය අසම්පූර්ණ විය"
|
||||
|
||||
#: src/modules/services-openrc/main.py:29
|
||||
msgid "Configure OpenRC services"
|
||||
msgstr ""
|
||||
msgstr "OpenRC සේවා වින්යාස කරන්න"
|
||||
|
||||
#: src/modules/services-openrc/main.py:57
|
||||
msgid "Cannot add service {name!s} to run-level {level!s}."
|
||||
msgstr ""
|
||||
msgstr "ධාවන මට්ටම {level!s} වෙත සේවාව {name!s} එක් කළ නොහැක."
|
||||
|
||||
#: src/modules/services-openrc/main.py:59
|
||||
msgid "Cannot remove service {name!s} from run-level {level!s}."
|
||||
msgstr ""
|
||||
msgstr "ධාවන මට්ටමේ {level!s} වෙතින් සේවාව {name!s} ඉවත් කළ නොහැක."
|
||||
|
||||
#: src/modules/services-openrc/main.py:61
|
||||
msgid ""
|
||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||
"level {level!s}."
|
||||
msgstr ""
|
||||
"{name!s} සේවාව සඳහා නොදන්නා සේවා-ක්රියාව <code>{arg!s}</code> ධාවන මට්ටමේ "
|
||||
"{level!s}."
|
||||
|
||||
#: src/modules/services-openrc/main.py:93
|
||||
#: src/modules/services-systemd/main.py:59
|
||||
msgid "Cannot modify service"
|
||||
msgstr ""
|
||||
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> chroot හි ඇමතුම {num!s} දෝෂ කේතය ලබා දුන්නේය."
|
||||
|
||||
#: src/modules/services-openrc/main.py:101
|
||||
msgid "Target runlevel does not exist"
|
||||
msgstr ""
|
||||
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 ""
|
||||
msgstr "ධාවන මට්ටම {level!s} සඳහා මාර්ගය <code>{path!s}</code>, එය නොපවතී."
|
||||
|
||||
#: src/modules/services-openrc/main.py:110
|
||||
msgid "Target service does not exist"
|
||||
msgstr ""
|
||||
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 ""
|
||||
msgstr "සේවාව සඳහා {name!s} මාර්ගය <code>{path!s}</code>, එය නොපවතී."
|
||||
|
||||
#: src/modules/networkcfg/main.py:29
|
||||
msgid "Saving network configuration."
|
||||
@ -203,7 +212,7 @@ msgstr "ඇසුරුම් ස්ථාපනය කරන්න."
|
||||
#: src/modules/packages/main.py:57
|
||||
#, python-format
|
||||
msgid "Processing packages (%(count)d / %(total)d)"
|
||||
msgstr ""
|
||||
msgstr "පැකේජ සැකසීම (%(count)d / %(total)d)"
|
||||
|
||||
#: src/modules/packages/main.py:62
|
||||
#, python-format
|
||||
@ -222,41 +231,47 @@ msgstr[1] "ඇසුරුම් %(num)d ක් ඉවත් වෙමින්.
|
||||
#: src/modules/packages/main.py:638 src/modules/packages/main.py:650
|
||||
#: src/modules/packages/main.py:678
|
||||
msgid "Package Manager error"
|
||||
msgstr ""
|
||||
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 ""
|
||||
"පැකේජ කළමනාකරුට යාවත්කාලීන සකස් කිරීමට නොහැකි විය. විධානය <pre>{!s}</pre> "
|
||||
"දෝෂ කේතය {!s} ලබා දුන්නේය."
|
||||
|
||||
#: 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 ""
|
||||
"පැකේජ කළමනාකරුට පද්ධතිය යාවත්කාලීන කළ නොහැකි විය. විධානය <pre>{!s}</pre> දෝෂ"
|
||||
" කේතය {!s} ලබා දුන්නේය."
|
||||
|
||||
#: 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 ""
|
||||
"පැකේජ කළමනාකරුට ස්ථාපිත පද්ධතියට වෙනස්කම් සිදු කළ නොහැක. විධානය "
|
||||
"<pre>{!s}</pre> දෝෂ කේතය {!s} ලබා දුන්නේය."
|
||||
|
||||
#: src/modules/plymouthcfg/main.py:27
|
||||
msgid "Configure Plymouth theme"
|
||||
msgstr ""
|
||||
msgstr "Plymouth තේමාව වින්යාස කරන්න"
|
||||
|
||||
#: src/modules/initcpiocfg/main.py:28
|
||||
msgid "Configuring mkinitcpio."
|
||||
msgstr ""
|
||||
msgstr "mkinitcpio වින්යාස කරමින්."
|
||||
|
||||
#: src/modules/localecfg/main.py:30
|
||||
msgid "Configuring locales."
|
||||
msgstr ""
|
||||
msgstr "ස්ථාන වින්යාස කිරීම."
|
||||
|
||||
#: src/modules/mount/main.py:30
|
||||
msgid "Mounting partitions."
|
||||
msgstr ""
|
||||
msgstr "කොටස් සවි කිරීම."
|
||||
|
||||
#: src/modules/rawfs/main.py:26
|
||||
msgid "Installing data."
|
||||
@ -264,12 +279,12 @@ msgstr "දත්ත ස්ථාපනය වෙමින්."
|
||||
|
||||
#: src/modules/dummypython/main.py:35
|
||||
msgid "Dummy python job."
|
||||
msgstr ""
|
||||
msgstr "ඩමි python වැඩසටහන."
|
||||
|
||||
#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
|
||||
#: src/modules/dummypython/main.py:94
|
||||
msgid "Dummy python step {}"
|
||||
msgstr ""
|
||||
msgstr "ව්යාජ python පියවර {}"
|
||||
|
||||
#: src/modules/hwclock/main.py:26
|
||||
msgid "Setting hardware clock."
|
||||
@ -277,115 +292,120 @@ msgstr "දෘඩාංග ඔරලෝසුව සැකසෙමින්."
|
||||
|
||||
#: src/modules/umount/main.py:31
|
||||
msgid "Unmount file systems."
|
||||
msgstr ""
|
||||
msgstr "ගොනු පද්ධති ඉවත් කරන්න."
|
||||
|
||||
#: src/modules/openrcdmcryptcfg/main.py:26
|
||||
msgid "Configuring OpenRC dmcrypt service."
|
||||
msgstr ""
|
||||
msgstr "OpenRC dmcrypt සේවාව වින්යාස කරමින්."
|
||||
|
||||
#: src/modules/services-systemd/main.py:26
|
||||
msgid "Configure systemd services"
|
||||
msgstr ""
|
||||
msgstr "systemd සේවා වින්යාස කරන්න"
|
||||
|
||||
#: 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> chroot වෙත ඇමතුමක් ලබා දුන් දෝෂ කේතය {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 ""
|
||||
msgstr "systemd සේවාව <code>{name!s}</code> සබල කළ නොහැක."
|
||||
|
||||
#: src/modules/services-systemd/main.py:65
|
||||
msgid "Cannot enable systemd target <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
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>."
|
||||
msgstr ""
|
||||
msgstr "systemd ඉලක්කය <code>{name!s}</code> අක්රිය කළ නොහැක."
|
||||
|
||||
#: src/modules/services-systemd/main.py:73
|
||||
msgid "Cannot mask systemd unit <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "systemd ඒකකය <code>{name!s}</code> වසන් කළ නොහැක."
|
||||
|
||||
#: src/modules/services-systemd/main.py:75
|
||||
msgid ""
|
||||
"Unknown systemd commands <code>{command!s}</code> and "
|
||||
"<code>{suffix!s}</code> for unit {name!s}."
|
||||
msgstr ""
|
||||
"{name!s} ඒකකය සඳහා නොදන්නා systemd විධාන <code>{command!s}</code> සහ "
|
||||
"<code>{suffix!s}</code>."
|
||||
|
||||
#: src/modules/mkinitfs/main.py:27
|
||||
msgid "Creating initramfs with mkinitfs."
|
||||
msgstr ""
|
||||
msgstr "mkinitfs සමඟ initramfs නිර්මාණය කිරීම."
|
||||
|
||||
#: src/modules/mkinitfs/main.py:49
|
||||
msgid "Failed to run mkinitfs on the target"
|
||||
msgstr ""
|
||||
msgstr "ඉලක්කය මත mkinitfs ධාවනය කිරීමට අසමත් විය"
|
||||
|
||||
#: src/modules/unpackfs/main.py:34
|
||||
msgid "Filling up filesystems."
|
||||
msgstr ""
|
||||
msgstr "ගොනු පද්ධති පිරවීම."
|
||||
|
||||
#: src/modules/unpackfs/main.py:254
|
||||
msgid "rsync failed with error code {}."
|
||||
msgstr ""
|
||||
msgstr "දෝෂ කේතය {} සමඟ rsync අසාර්ථක විය."
|
||||
|
||||
#: src/modules/unpackfs/main.py:299
|
||||
msgid "Unpacking image {}/{}, file {}/{}"
|
||||
msgstr ""
|
||||
msgstr "රූපය {}/{}, ගොනුව {}/{} අසුරමින්"
|
||||
|
||||
#: src/modules/unpackfs/main.py:314
|
||||
msgid "Starting to unpack {}"
|
||||
msgstr ""
|
||||
msgstr "ඉවත් කිරීමට පටන් ගනියි {}"
|
||||
|
||||
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:465
|
||||
msgid "Failed to unpack image \"{}\""
|
||||
msgstr ""
|
||||
msgstr "\"{}\" රූපය ඉවත් කිරීමට අසමත් විය"
|
||||
|
||||
#: src/modules/unpackfs/main.py:430
|
||||
msgid "No mount point for root partition"
|
||||
msgstr ""
|
||||
msgstr "root කොටස සඳහා සවි කිරීමේ ලක්ෂ්යයක් නොමැත"
|
||||
|
||||
#: src/modules/unpackfs/main.py:431
|
||||
msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing"
|
||||
msgstr ""
|
||||
msgstr "ගෝලීය ගබඩාවේ \"rootMountPoint\" යතුරක් අඩංගු නොවේ, කිසිවක් නොකරයි"
|
||||
|
||||
#: src/modules/unpackfs/main.py:436
|
||||
msgid "Bad mount point for root partition"
|
||||
msgstr ""
|
||||
msgstr "මූල කොටස සඳහා නරක සවි කිරීමේ ලක්ෂ්යය"
|
||||
|
||||
#: src/modules/unpackfs/main.py:437
|
||||
msgid "rootMountPoint is \"{}\", which does not exist, doing nothing"
|
||||
msgstr ""
|
||||
msgstr "rootMountPoint යනු \"{}\", එය නොපවතින, කිසිවක් නොකරයි"
|
||||
|
||||
#: 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 ""
|
||||
msgstr "නරක unsquash වින්යාසය"
|
||||
|
||||
#: src/modules/unpackfs/main.py:454
|
||||
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
||||
msgstr ""
|
||||
msgstr "\"{}\" ({}) සඳහා ගොනු පද්ධතිය ඔබගේ වත්මන් කර්නලයෙන් සහය නොදක්වයි"
|
||||
|
||||
#: src/modules/unpackfs/main.py:458
|
||||
msgid "The source filesystem \"{}\" does not exist"
|
||||
msgstr ""
|
||||
msgstr "මූලාශ්ර ගොනු පද්ධතිය \"{}\" නොපවතී"
|
||||
|
||||
#: src/modules/unpackfs/main.py:464
|
||||
msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Unsquashfs සොයා ගැනීමට අපොහොසත් විය, ඔබ squashfs-tools පැකේජය ස්ථාපනය කර ඇති"
|
||||
" බවට වග බලා ගන්න."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
msgstr ""
|
||||
msgstr "ඉලක්ක පද්ධතියේ \"{}\" ගමනාන්තය නාමාවලියක් නොවේ"
|
||||
|
||||
#: src/modules/luksopenswaphookcfg/main.py:26
|
||||
msgid "Configuring encrypted swap."
|
||||
msgstr ""
|
||||
msgstr "සංකේතාත්මක swap වින්යාස කිරීම."
|
||||
|
@ -327,7 +327,7 @@ msgstr "Kunde inte aktivera systemd målsystem <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "Kunde inte aktivera systemd timer <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -407,6 +407,8 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Kunde inte hitta unsquashfs, se till att du har paketet squashfs-tools "
|
||||
"installerat"
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -322,7 +322,7 @@ msgstr "Systemd hedefi etkinleştirilemiyor <code>{name!s}</code>."
|
||||
|
||||
#: src/modules/services-systemd/main.py:67
|
||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||
msgstr ""
|
||||
msgstr "<code>{name!s}</code> sistem zamanlayıcısı etkinleştirilemiyor."
|
||||
|
||||
#: src/modules/services-systemd/main.py:71
|
||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||
@ -403,6 +403,7 @@ msgid ""
|
||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||
"installed."
|
||||
msgstr ""
|
||||
"Unsquashfs bulunamadı, squashfs-tools paketinin kurulu olduğundan emin olun."
|
||||
|
||||
#: src/modules/unpackfs/main.py:479
|
||||
msgid "The destination \"{}\" in the target system is not a directory"
|
||||
|
@ -127,6 +127,7 @@ sequence:
|
||||
# - dummyprocess
|
||||
# - dummypython
|
||||
- partition
|
||||
# - zfs
|
||||
- mount
|
||||
- unpackfs
|
||||
- machineid
|
||||
|
@ -132,6 +132,11 @@ public:
|
||||
void setEmergency( bool e ) { m_emergency = e; }
|
||||
|
||||
signals:
|
||||
/** @brief Signals that the job has made progress
|
||||
*
|
||||
* The parameter @p percent should be between 0 (0%) and 1 (100%).
|
||||
* Values outside of this range will be clamped.
|
||||
*/
|
||||
void progress( qreal percent );
|
||||
|
||||
private:
|
||||
|
@ -79,13 +79,25 @@ BOOST_PYTHON_MODULE( libcalamares )
|
||||
bp::scope utilsScope = utilsModule;
|
||||
Q_UNUSED( utilsScope )
|
||||
|
||||
// .. Logging functions
|
||||
bp::def(
|
||||
"debug", &CalamaresPython::debug, bp::args( "s" ), "Writes the given string to the Calamares debug stream." );
|
||||
bp::def( "warning",
|
||||
&CalamaresPython::warning,
|
||||
bp::args( "s" ),
|
||||
"Writes the given string to the Calamares warning stream." );
|
||||
bp::def( "warn",
|
||||
&CalamaresPython::warning,
|
||||
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." );
|
||||
|
||||
|
||||
// .. YAML functions
|
||||
bp::def( "load_yaml", &CalamaresPython::load_yaml, bp::args( "path" ), "Loads YAML from a file." );
|
||||
|
||||
// .. Filesystem functions
|
||||
bp::def( "mount",
|
||||
&CalamaresPython::mount,
|
||||
mount_overloads( bp::args( "device_path", "mount_point", "filesystem_name", "options" ),
|
||||
@ -94,6 +106,8 @@ BOOST_PYTHON_MODULE( libcalamares )
|
||||
"-1 = QProcess crash\n"
|
||||
"-2 = QProcess cannot start\n"
|
||||
"-3 = bad arguments" ) );
|
||||
|
||||
// .. Process functions
|
||||
bp::def(
|
||||
"target_env_call",
|
||||
static_cast< int ( * )( const std::string&, const std::string&, int ) >( &CalamaresPython::target_env_call ),
|
||||
@ -152,6 +166,7 @@ BOOST_PYTHON_MODULE( libcalamares )
|
||||
host_env_process_output_overloads( bp::args( "command", "callback", "stdin", "timeout" ),
|
||||
"Runs the specified command in the host system." ) );
|
||||
|
||||
// .. String functions
|
||||
bp::def( "obscure",
|
||||
&CalamaresPython::obscure,
|
||||
bp::args( "s" ),
|
||||
@ -160,7 +175,7 @@ BOOST_PYTHON_MODULE( libcalamares )
|
||||
"Applying the function to a string obscured by this function will result "
|
||||
"in the original string." );
|
||||
|
||||
|
||||
// .. Translation functions
|
||||
bp::def( "gettext_languages",
|
||||
&CalamaresPython::gettext_languages,
|
||||
"Returns list of languages (most to least-specific) for gettext." );
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "utils/RAII.h"
|
||||
#include "utils/Runner.h"
|
||||
#include "utils/String.h"
|
||||
#include "utils/Yaml.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
@ -139,19 +140,44 @@ check_target_env_output( const bp::list& args, const std::string& stdin, int tim
|
||||
}
|
||||
|
||||
static const char output_prefix[] = "[PYTHON JOB]:";
|
||||
static inline void
|
||||
log_action( unsigned int level, const std::string& s )
|
||||
{
|
||||
Logger::CDebug( level ) << output_prefix << QString::fromStdString( s );
|
||||
}
|
||||
|
||||
void
|
||||
debug( const std::string& s )
|
||||
{
|
||||
Logger::CDebug( Logger::LOGDEBUG ) << output_prefix << QString::fromStdString( s );
|
||||
log_action( Logger::LOGDEBUG, s );
|
||||
}
|
||||
|
||||
void
|
||||
warning( const std::string& s )
|
||||
{
|
||||
Logger::CDebug( Logger::LOGWARNING ) << output_prefix << QString::fromStdString( s );
|
||||
log_action( Logger::LOGWARNING, s );
|
||||
}
|
||||
|
||||
void
|
||||
error( const std::string& s )
|
||||
{
|
||||
log_action( Logger::LOGERROR, s );
|
||||
}
|
||||
|
||||
boost::python::dict
|
||||
load_yaml( const std::string& path )
|
||||
{
|
||||
const QString filePath = QString::fromStdString( path );
|
||||
bool ok = false;
|
||||
auto map = CalamaresUtils::loadYaml( filePath, &ok );
|
||||
if ( !ok )
|
||||
{
|
||||
cWarning() << "Loading YAML from" << filePath << "failed.";
|
||||
}
|
||||
return variantMapToPyDict( map );
|
||||
}
|
||||
|
||||
|
||||
PythonJobInterface::PythonJobInterface( Calamares::PythonJob* parent )
|
||||
: m_parent( parent )
|
||||
{
|
||||
|
@ -60,6 +60,12 @@ boost::python::list gettext_languages();
|
||||
|
||||
void debug( const std::string& s );
|
||||
void warning( const std::string& s );
|
||||
void error( const std::string& s );
|
||||
|
||||
/** @brief Loads YAML and returns (nested) dicts representing it
|
||||
*
|
||||
*/
|
||||
boost::python::dict load_yaml( const std::string& path );
|
||||
|
||||
class PythonJobInterface
|
||||
{
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QMutex>
|
||||
#include <QRandomGenerator>
|
||||
#include <QTextStream>
|
||||
#include <QTime>
|
||||
#include <QVariant>
|
||||
|
||||
@ -229,7 +231,7 @@ toString( const QVariant& v )
|
||||
}
|
||||
|
||||
QDebug&
|
||||
operator<<( QDebug& s, const Redacted& l )
|
||||
operator<<( QDebug& s, const RedactedCommand& l )
|
||||
{
|
||||
// Special case logging: don't log the (encrypted) password.
|
||||
if ( l.list.contains( "usermod" ) )
|
||||
@ -252,4 +254,33 @@ operator<<( QDebug& s, const Redacted& l )
|
||||
return s;
|
||||
}
|
||||
|
||||
/** @brief Returns a stable-but-private hash of @p context and @p s
|
||||
*
|
||||
* 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 salt = QRandomGenerator::global()->generate(); // Just once
|
||||
|
||||
uint val = qHash(context, salt);
|
||||
return qHash(s, val);
|
||||
}
|
||||
|
||||
RedactedName::RedactedName( const QString& context, const QString& s )
|
||||
: m_id( insertRedactedName(context, s) ),
|
||||
m_context(context)
|
||||
{
|
||||
}
|
||||
|
||||
RedactedName::RedactedName(const char *context, const QString& s )
|
||||
: RedactedName( QString::fromLatin1( context ), s )
|
||||
{
|
||||
}
|
||||
|
||||
RedactedName::operator QString() const
|
||||
{
|
||||
return QString( m_context + '$' + QString::number( m_id, 16 ) );
|
||||
}
|
||||
|
||||
} // namespace Logger
|
||||
|
@ -145,8 +145,8 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
const T& first;
|
||||
const U& second;
|
||||
const T first;
|
||||
const U second;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -214,9 +214,9 @@ public:
|
||||
* since the log may get posted to bug reports, or stored in
|
||||
* the target system.
|
||||
*/
|
||||
struct Redacted
|
||||
struct RedactedCommand
|
||||
{
|
||||
Redacted( const QStringList& l )
|
||||
RedactedCommand( const QStringList& l )
|
||||
: list( l )
|
||||
{
|
||||
}
|
||||
@ -224,7 +224,30 @@ struct Redacted
|
||||
const QStringList& list;
|
||||
};
|
||||
|
||||
QDebug& operator<<( QDebug& s, const Redacted& l );
|
||||
QDebug& operator<<( QDebug& s, const RedactedCommand& l );
|
||||
|
||||
/** @brief When logging "private" identifiers, keep them consistent but private
|
||||
*
|
||||
* Send a string to a logger in such a way that each time it is logged,
|
||||
* it logs the same way, but without revealing the actual contents.
|
||||
* This can be applied to user names, UUIDs, etc.
|
||||
*/
|
||||
struct RedactedName
|
||||
{
|
||||
RedactedName( const char* context, const QString& s );
|
||||
RedactedName( const QString& context, const QString& s );
|
||||
|
||||
operator QString() const;
|
||||
|
||||
private:
|
||||
const uint m_id;
|
||||
const QString m_context;
|
||||
};
|
||||
|
||||
inline QDebug& operator<<( QDebug& s, const RedactedName& n )
|
||||
{
|
||||
return s << NoQuote << QString( n ) << Quote;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Formatted logging of a pointer
|
||||
|
@ -163,7 +163,7 @@ Calamares::Utils::Runner::run()
|
||||
} );
|
||||
}
|
||||
|
||||
cDebug() << Logger::SubEntry << "Running" << Logger::Redacted( m_command );
|
||||
cDebug() << Logger::SubEntry << "Running" << Logger::RedactedCommand( m_command );
|
||||
process.start();
|
||||
if ( !process.waitForStarted() )
|
||||
{
|
||||
@ -225,13 +225,13 @@ Calamares::Utils::Runner::run()
|
||||
{
|
||||
if ( !output.isEmpty() )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "Target cmd:" << Logger::Redacted( m_command ) << "Exit code:" << r
|
||||
cDebug() << Logger::SubEntry << "Target cmd:" << Logger::RedactedCommand( m_command ) << "Exit code:" << r
|
||||
<< "output:\n"
|
||||
<< Logger::NoQuote << output;
|
||||
}
|
||||
else
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "Target cmd:" << Logger::Redacted( m_command ) << "Exit code:" << r
|
||||
cDebug() << Logger::SubEntry << "Target cmd:" << Logger::RedactedCommand( m_command ) << "Exit code:" << r
|
||||
<< "(no output)";
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +398,9 @@ target_env_process_output(["ls"])
|
||||
```
|
||||
|
||||
The functions return 0. If the exit code of *command* is not 0, an exception
|
||||
is raised instead of returning 0.
|
||||
is raised instead of returning 0. The exception is `subprocess.CalledProcessError`
|
||||
(as if the *subprocess* module had been used), and the `returncode` member
|
||||
of the exception object can be used to determine the exit code.
|
||||
|
||||
Parameter *stdin* may be a string which is fed to the command as standard input.
|
||||
The *timeout* is in seconds, with 0 (or a negative number) treated as no-timeout.
|
||||
|
@ -73,6 +73,69 @@ def get_bootloader_entry_name():
|
||||
return branding["bootloaderEntryName"]
|
||||
|
||||
|
||||
def get_kernel_line(kernel_type):
|
||||
"""
|
||||
Passes 'kernel_line' to other routine based on configuration file.
|
||||
|
||||
:param kernel_type:
|
||||
:return:
|
||||
"""
|
||||
if kernel_type == "fallback":
|
||||
if "fallbackKernelLine" in libcalamares.job.configuration:
|
||||
return libcalamares.job.configuration["fallbackKernelLine"]
|
||||
else:
|
||||
return " (fallback)"
|
||||
else:
|
||||
if "kernelLine" in libcalamares.job.configuration:
|
||||
return libcalamares.job.configuration["kernelLine"]
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def get_zfs_root():
|
||||
"""
|
||||
Looks in global storage to find the zfs root
|
||||
|
||||
:return: A string containing the path to the zfs root or None if it is not found
|
||||
"""
|
||||
|
||||
zfs = libcalamares.globalstorage.value("zfsDatasets")
|
||||
|
||||
if not zfs:
|
||||
libcalamares.utils.warning("Failed to locate zfs dataset list")
|
||||
return None
|
||||
|
||||
# Find the root dataset
|
||||
for dataset in zfs:
|
||||
try:
|
||||
if dataset["mountpoint"] == "/":
|
||||
return dataset["zpool"] + "/" + dataset["dsName"]
|
||||
except KeyError:
|
||||
# This should be impossible
|
||||
libcalamares.utils.warning("Internal error handling zfs dataset")
|
||||
raise
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_btrfs_root(partition):
|
||||
""" Returns True if the partition object refers to a btrfs root filesystem
|
||||
|
||||
:param partition: A partition map from global storage
|
||||
:return: True if btrfs and root, False otherwise
|
||||
"""
|
||||
return partition["mountPoint"] == "/" and partition["fs"] == "btrfs"
|
||||
|
||||
|
||||
def is_zfs_root(partition):
|
||||
""" Returns True if the partition object refers to a zfs root filesystem
|
||||
|
||||
:param partition: A partition map from global storage
|
||||
:return: True if zfs and root, False otherwise
|
||||
"""
|
||||
return partition["mountPoint"] == "/" and partition["fs"] == "zfs"
|
||||
|
||||
|
||||
def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kernel, kernel_type, kernel_version):
|
||||
"""
|
||||
Creates systemd-boot configuration files based on given parameters.
|
||||
@ -114,11 +177,24 @@ def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, entry, kerne
|
||||
"root=/dev/mapper/"
|
||||
+ partition["luksMapperName"]]
|
||||
|
||||
# systemd-boot with a BTRFS root filesystem needs to be told
|
||||
# about the root subvolume.
|
||||
for partition in partitions:
|
||||
if partition["mountPoint"] == "/" and partition["fs"] == "btrfs":
|
||||
kernel_params.append("rootflags=subvol=@")
|
||||
# systemd-boot with a BTRFS root filesystem needs to be told abouut the root subvolume.
|
||||
# If a btrfs root subvolume wasn't set, it means the root is directly on the partition
|
||||
# and this option isn't needed
|
||||
if is_btrfs_root(partition):
|
||||
btrfs_root_subvolume = libcalamares.globalstorage.value("btrfsRootSubvolume")
|
||||
if btrfs_root_subvolume:
|
||||
kernel_params.append("rootflags=subvol=" + btrfs_root_subvolume)
|
||||
|
||||
# zfs needs to be told the location of the root dataset
|
||||
if is_zfs_root(partition):
|
||||
zfs_root_path = get_zfs_root()
|
||||
if zfs_root_path is not None:
|
||||
kernel_params.append("zfs=" + zfs_root_path)
|
||||
else:
|
||||
# Something is really broken if we get to this point
|
||||
libcalamares.utils.warning("Internal error handling zfs dataset")
|
||||
raise Exception("Internal zfs data missing, please contact your distribution")
|
||||
|
||||
if cryptdevice_params:
|
||||
kernel_params.extend(cryptdevice_params)
|
||||
@ -332,6 +408,76 @@ def get_grub_efi_parameters():
|
||||
return None
|
||||
|
||||
|
||||
def run_grub_mkconfig(partitions, output_file):
|
||||
"""
|
||||
Runs grub-mkconfig in the target environment
|
||||
|
||||
:param partitions: The partitions list from global storage
|
||||
:param output_file: A string containing the path to the generating grub config file
|
||||
:return:
|
||||
"""
|
||||
|
||||
# zfs needs an environment variable set for grub-mkconfig
|
||||
if any([is_zfs_root(partition) for partition in partitions]):
|
||||
check_target_env_call(["sh", "-c", "ZPOOL_VDEV_NAME_PATH=1 " +
|
||||
libcalamares.job.configuration["grubMkconfig"] + " -o " + output_file])
|
||||
else:
|
||||
# The input file /etc/default/grub should already be filled out by the
|
||||
# grubcfg job module.
|
||||
check_target_env_call([libcalamares.job.configuration["grubMkconfig"], "-o", output_file])
|
||||
|
||||
|
||||
def run_grub_install(fw_type, partitions, efi_directory=None):
|
||||
"""
|
||||
Runs grub-install in the target environment
|
||||
|
||||
:param fw_type: A string which is "efi" for UEFI installs. Any other value results in a BIOS install
|
||||
:param partitions: The partitions list from global storage
|
||||
:param efi_directory: The path of the efi directory relative to the root of the install
|
||||
:return:
|
||||
"""
|
||||
|
||||
is_zfs = any([is_zfs_root(partition) for partition in partitions])
|
||||
|
||||
# zfs needs an environment variable set for grub
|
||||
if is_zfs:
|
||||
check_target_env_call(["sh", "-c", "echo ZPOOL_VDEV_NAME_PATH=1 >> /etc/environment"])
|
||||
|
||||
if fw_type == "efi":
|
||||
efi_bootloader_id = efi_label()
|
||||
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
|
||||
|
||||
if is_zfs:
|
||||
check_target_env_call(["sh", "-c", "ZPOOL_VDEV_NAME_PATH=1 " + libcalamares.job.configuration["grubInstall"]
|
||||
+ " --target=" + efi_target + " --efi-directory=" + efi_directory
|
||||
+ " --bootloader-id=" + efi_bootloader_id + " --force"])
|
||||
else:
|
||||
check_target_env_call([libcalamares.job.configuration["grubInstall"],
|
||||
"--target=" + efi_target,
|
||||
"--efi-directory=" + efi_directory,
|
||||
"--bootloader-id=" + efi_bootloader_id,
|
||||
"--force"])
|
||||
else:
|
||||
if libcalamares.globalstorage.value("bootLoader") is None:
|
||||
return
|
||||
|
||||
boot_loader = libcalamares.globalstorage.value("bootLoader")
|
||||
if boot_loader["installPath"] is None:
|
||||
return
|
||||
|
||||
if is_zfs:
|
||||
check_target_env_call(["sh", "-c", "ZPOOL_VDEV_NAME_PATH=1 "
|
||||
+ libcalamares.job.configuration["grubInstall"]
|
||||
+ " --target=i386-pc --recheck --force "
|
||||
+ boot_loader["installPath"]])
|
||||
else:
|
||||
check_target_env_call([libcalamares.job.configuration["grubInstall"],
|
||||
"--target=i386-pc",
|
||||
"--recheck",
|
||||
"--force",
|
||||
boot_loader["installPath"]])
|
||||
|
||||
|
||||
def install_grub(efi_directory, fw_type):
|
||||
"""
|
||||
Installs grub as bootloader, either in pc or efi mode.
|
||||
@ -339,6 +485,12 @@ def install_grub(efi_directory, fw_type):
|
||||
:param efi_directory:
|
||||
:param fw_type:
|
||||
"""
|
||||
# get the partition from global storage
|
||||
partitions = libcalamares.globalstorage.value("partitions")
|
||||
if not partitions:
|
||||
libcalamares.utils.warning(_("Failed to install grub, no partitions defined in global storage"))
|
||||
return
|
||||
|
||||
if fw_type == "efi":
|
||||
libcalamares.utils.debug("Bootloader: grub (efi)")
|
||||
installation_root_path = libcalamares.globalstorage.value("rootMountPoint")
|
||||
@ -351,11 +503,7 @@ def install_grub(efi_directory, fw_type):
|
||||
|
||||
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
|
||||
|
||||
check_target_env_call([libcalamares.job.configuration["grubInstall"],
|
||||
"--target=" + efi_target,
|
||||
"--efi-directory=" + efi_directory,
|
||||
"--bootloader-id=" + efi_bootloader_id,
|
||||
"--force"])
|
||||
run_grub_install(fw_type, partitions, efi_directory)
|
||||
|
||||
# VFAT is weird, see issue CAL-385
|
||||
install_efi_directory_firmware = (vfat_correct_case(
|
||||
@ -374,36 +522,21 @@ def install_grub(efi_directory, fw_type):
|
||||
os.makedirs(install_efi_boot_directory)
|
||||
|
||||
# Workaround for some UEFI firmwares
|
||||
FALLBACK = "installEFIFallback"
|
||||
libcalamares.utils.debug("UEFI Fallback: " + str(libcalamares.job.configuration.get(FALLBACK, "<unset>")))
|
||||
if libcalamares.job.configuration.get(FALLBACK, True):
|
||||
fallback = "installEFIFallback"
|
||||
libcalamares.utils.debug("UEFI Fallback: " + str(libcalamares.job.configuration.get(fallback, "<unset>")))
|
||||
if libcalamares.job.configuration.get(fallback, True):
|
||||
libcalamares.utils.debug(" .. installing '{!s}' fallback firmware".format(efi_boot_file))
|
||||
efi_file_source = os.path.join(install_efi_directory_firmware,
|
||||
efi_bootloader_id,
|
||||
efi_grub_file)
|
||||
efi_file_target = os.path.join(install_efi_boot_directory,
|
||||
efi_boot_file)
|
||||
efi_file_target = os.path.join(install_efi_boot_directory, efi_boot_file)
|
||||
|
||||
shutil.copy2(efi_file_source, efi_file_target)
|
||||
else:
|
||||
libcalamares.utils.debug("Bootloader: grub (bios)")
|
||||
if libcalamares.globalstorage.value("bootLoader") is None:
|
||||
return
|
||||
run_grub_install(fw_type, partitions)
|
||||
|
||||
boot_loader = libcalamares.globalstorage.value("bootLoader")
|
||||
if boot_loader["installPath"] is None:
|
||||
return
|
||||
|
||||
check_target_env_call([libcalamares.job.configuration["grubInstall"],
|
||||
"--target=i386-pc",
|
||||
"--recheck",
|
||||
"--force",
|
||||
boot_loader["installPath"]])
|
||||
|
||||
# The input file /etc/default/grub should already be filled out by the
|
||||
# grubcfg job module.
|
||||
check_target_env_call([libcalamares.job.configuration["grubMkconfig"],
|
||||
"-o", libcalamares.job.configuration["grubCfg"]])
|
||||
run_grub_mkconfig(partitions, libcalamares.job.configuration["grubCfg"])
|
||||
|
||||
|
||||
def install_secureboot(efi_directory):
|
||||
|
@ -23,6 +23,7 @@ displaymanagers:
|
||||
- mdm
|
||||
- lxdm
|
||||
- kdm
|
||||
- greetd
|
||||
|
||||
# Enable the following settings to force a desktop environment
|
||||
# in your displaymanager configuration file. This will attempt
|
||||
|
@ -10,7 +10,7 @@ properties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum: [slim, sddm, lightdm, gdm, mdm, lxdm, kdm]
|
||||
enum: [slim, sddm, lightdm, gdm, mdm, lxdm, kdm, greetd]
|
||||
minItems: 1 # Must be non-empty, if present at all
|
||||
defaultDesktopEnvironment:
|
||||
type: object
|
||||
|
@ -17,9 +17,7 @@
|
||||
|
||||
import abc
|
||||
import os
|
||||
import re
|
||||
import libcalamares
|
||||
import configparser
|
||||
|
||||
from libcalamares.utils import gettext_path, gettext_languages
|
||||
|
||||
@ -796,6 +794,8 @@ class DMsddm(DisplayManager):
|
||||
executable = "sddm"
|
||||
|
||||
def set_autologin(self, username, do_autologin, default_desktop_environment):
|
||||
import configparser
|
||||
|
||||
# Systems with Sddm as Desktop Manager
|
||||
sddm_conf_path = os.path.join(self.root_mount_point, "etc/sddm.conf")
|
||||
|
||||
@ -835,6 +835,91 @@ class DMsddm(DisplayManager):
|
||||
pass
|
||||
|
||||
|
||||
class DMgreetd(DisplayManager):
|
||||
name = "greetd"
|
||||
executable = "greetd"
|
||||
greeter_user = "greeter"
|
||||
greeter_group = "greetd"
|
||||
config_data = {}
|
||||
|
||||
def os_path(self, path):
|
||||
return os.path.join(self.root_mount_point, path)
|
||||
|
||||
def config_path(self):
|
||||
return self.os_path("etc/greetd/config.toml")
|
||||
|
||||
def environments_path(self):
|
||||
return self.os_path("etc/greetd/environments")
|
||||
|
||||
def config_load(self):
|
||||
import toml
|
||||
|
||||
if (os.path.exists(self.config_path())):
|
||||
with open(self.config_path(), "r") as f:
|
||||
self.config_data = toml.load(f)
|
||||
|
||||
self.config_data['terminal'] = dict(vt = "next")
|
||||
|
||||
default_session_group = self.config_data.get('default_session', None)
|
||||
if not default_session_group:
|
||||
self.config_data['default_session'] = {}
|
||||
|
||||
self.config_data['default_session']['user'] = self.greeter_user
|
||||
|
||||
return self.config_data
|
||||
|
||||
def config_write(self):
|
||||
import toml
|
||||
with open(self.config_path(), "w") as f:
|
||||
toml.dump(self.config_data, f)
|
||||
|
||||
def basic_setup(self):
|
||||
if libcalamares.utils.target_env_call(
|
||||
['getent', 'group', self.greeter_group]
|
||||
) != 0:
|
||||
libcalamares.utils.target_env_call(
|
||||
['groupadd', self.greeter_group]
|
||||
)
|
||||
|
||||
if libcalamares.utils.target_env_call(
|
||||
['getent', 'passwd', self.greeter_user]
|
||||
) != 0:
|
||||
libcalamares.utils.target_env_call(
|
||||
['useradd',
|
||||
'-c', '"Greeter User"',
|
||||
'-g', self.greeter_group,
|
||||
'-s', '/bin/bash',
|
||||
self.greeter_user
|
||||
]
|
||||
)
|
||||
|
||||
def desktop_environment_setup(self, default_desktop_environment):
|
||||
with open(self.environments_path(), 'w') as envs_file:
|
||||
envs_file.write(default_desktop_environment)
|
||||
|
||||
def greeter_setup(self):
|
||||
pass
|
||||
|
||||
def set_autologin(self, username, do_autologin, default_desktop_environment):
|
||||
self.config_load()
|
||||
|
||||
de_command = default_desktop_environment.executable
|
||||
if os.path.exists(self.os_path("usr/bin/gtkgreed")) and os.path.exists(self.os_path("usr/bin/cage")):
|
||||
self.config_data['default_session']['command'] = "cage -s -- gtkgreet"
|
||||
elif os.path.exists(self.os_path("usr/bin/tuigreet")):
|
||||
tuigreet_base_cmd = "tuigreet --remember --time --issue --asterisks --cmd "
|
||||
self.config_data['default_session']['command'] = tuigreet_base_cmd + de_command
|
||||
elif os.path.exists(self.os_path("usr/bin/ddlm")):
|
||||
self.config_data['default_session']['command'] = "ddlm --target " + de_command
|
||||
else:
|
||||
self.config_data['default_session']['command'] = "agreety --cmd " + de_command
|
||||
|
||||
if do_autologin == True:
|
||||
self.config_data['initial_session'] = dict(command = de_command, user = username)
|
||||
|
||||
self.config_write()
|
||||
|
||||
|
||||
class DMsysconfig(DisplayManager):
|
||||
name = "sysconfig"
|
||||
executable = None
|
||||
|
3
src/modules/displaymanager/tests/1.global
Normal file
3
src/modules/displaymanager/tests/1.global
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
rootMountPoint: /tmp
|
13
src/modules/displaymanager/tests/CMakeTests.txt
Normal file
13
src/modules/displaymanager/tests/CMakeTests.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# We have tests to load (some) of the DMs specifically, to test their
|
||||
# configuration code. Those tests conventionally live in Python
|
||||
# files here in the tests/ directory. Add them.
|
||||
foreach(_dmname greetd sddm)
|
||||
add_test(
|
||||
NAME configure-displaymanager-${_dmname}
|
||||
COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-dm-${_dmname}.py
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
endforeach()
|
25
src/modules/displaymanager/tests/test-dm-greetd.py
Normal file
25
src/modules/displaymanager/tests/test-dm-greetd.py
Normal file
@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Calamares Boilerplate
|
||||
import libcalamares
|
||||
libcalamares.globalstorage = libcalamares.GlobalStorage(None)
|
||||
libcalamares.globalstorage.insert("testing", True)
|
||||
|
||||
# Module prep-work
|
||||
from src.modules.displaymanager import main
|
||||
default_desktop_environment = main.DesktopEnvironment("startplasma-x11", "kde-plasma.desktop")
|
||||
|
||||
import os
|
||||
os.makedirs("/tmp/etc/greetd/", exist_ok=True)
|
||||
try:
|
||||
os.remove("/tmp/etc/greetd/config.toml")
|
||||
except FileNotFoundError as e:
|
||||
pass
|
||||
|
||||
# Specific DM test
|
||||
d = main.DMgreetd("/tmp")
|
||||
d.set_autologin("d", True, default_desktop_environment)
|
||||
# .. and again (this time checks load/save)
|
||||
d.set_autologin("d", True, default_desktop_environment)
|
||||
d.set_autologin("d", True, default_desktop_environment)
|
18
src/modules/displaymanager/tests/test-dm-sddm.py
Normal file
18
src/modules/displaymanager/tests/test-dm-sddm.py
Normal file
@ -0,0 +1,18 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Calamares Boilerplate
|
||||
import libcalamares
|
||||
libcalamares.globalstorage = libcalamares.GlobalStorage(None)
|
||||
libcalamares.globalstorage.insert("testing", True)
|
||||
|
||||
# Module prep-work
|
||||
from src.modules.displaymanager import main
|
||||
default_desktop_environment = main.DesktopEnvironment("startplasma-x11", "kde-plasma.desktop")
|
||||
|
||||
# Specific DM test
|
||||
d = main.DMsddm("/tmp")
|
||||
d.set_autologin("d", True, default_desktop_environment)
|
||||
# .. and again (this time checks load/save)
|
||||
d.set_autologin("d", True, default_desktop_environment)
|
||||
d.set_autologin("d", True, default_desktop_environment)
|
121
src/modules/finishedq/finishedq@mobile.qml
Normal file
121
src/modules/finishedq/finishedq@mobile.qml
Normal file
@ -0,0 +1,121 @@
|
||||
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 Anke Boersma <demm@kaosx.us>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* License-Filename: LICENSE
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
|
||||
Page {
|
||||
|
||||
id: finished
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
header: Kirigami.Heading {
|
||||
width: parent.width
|
||||
height: 100
|
||||
id: header
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
color: Kirigami.Theme.textColor
|
||||
level: 1
|
||||
text: qsTr("Installation Completed")
|
||||
|
||||
Text {
|
||||
anchors.top: header.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: 12
|
||||
text: qsTr("%1 has been installed on your computer.<br/>
|
||||
You may now restart your device.").arg(Branding.string(Branding.ProductName))
|
||||
}
|
||||
|
||||
Image {
|
||||
source: "seedling.svg"
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 80
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 64
|
||||
height: 64
|
||||
mipmap: true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight|Qt.AlignVCenter
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
Button {
|
||||
id: button
|
||||
text: qsTr("Close")
|
||||
icon.name: "application-exit"
|
||||
onClicked: { ViewManager.quit(); }
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Restart")
|
||||
icon.name: "system-reboot"
|
||||
onClicked: { config.doRestart(true); }
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin : 100
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
ProgressBar {
|
||||
id: autoRestartBar
|
||||
value: 1.0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autoRestartTimer
|
||||
// This is in milliseconds and should be less than 1000 (because of logic in onTriggered)
|
||||
interval: 100
|
||||
repeat: true
|
||||
running: false
|
||||
// Whenever the timer fires (1000 / interval times a second) count the progress bar down
|
||||
// by 1%. When the bar is empty, try to restart normally; as a backup, when the bar
|
||||
// is empty change settings and schedule it to quit 1000 milliseconds (1s) later.
|
||||
onTriggered: {
|
||||
autoRestartBar.value -= 0.01;
|
||||
if (autoRestartBar.value <= 0.0) {
|
||||
// First time through here, set the interval to 1000 so that the
|
||||
// second time (1 second later) goes to quit().
|
||||
if ( interval > 999) { ViewManager.quit(); }
|
||||
else { config.doRestart(true); running = false; interval = 1000; repeat = false; start(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onActivate()
|
||||
{
|
||||
autoRestartTimer.running = true
|
||||
}
|
||||
|
||||
function onLeave()
|
||||
{
|
||||
}
|
||||
}
|
@ -11,9 +11,17 @@
|
||||
# Mount options to use for all filesystems. If a specific filesystem
|
||||
# is listed here, use those options, otherwise use the *default*
|
||||
# options from this mapping.
|
||||
#
|
||||
# With kernels 5.15 and newer be cautious of adding the option space_cache
|
||||
# to the btrfs mount options. The default in 5.15 changed to space_cache=v2.
|
||||
# If space_cache or space_cache=v1 are specified, it may fail to remount.
|
||||
#
|
||||
# btrfs_swap options are used when a swapfile is chosen with a btrfs root
|
||||
# the options are applied to the subvolume which holds the swap partition
|
||||
mountOptions:
|
||||
default: defaults,noatime
|
||||
btrfs: defaults,noatime,space_cache,autodefrag,compress=zstd
|
||||
btrfs: defaults,noatime,autodefrag,compress=zstd
|
||||
btrfs_swap: defaults,noatime
|
||||
|
||||
# Mount options to use for the EFI System Partition. If not defined, the
|
||||
# *mountOptions* for *vfat* are used, or if that is not set either,
|
||||
|
@ -22,6 +22,7 @@ properties:
|
||||
xfs: { type: string }
|
||||
swap: { type: string }
|
||||
btrfs: { type: string }
|
||||
btrfs_swap: { type: string }
|
||||
efiMountOptions: { type: string }
|
||||
crypttabOptions: { type: string }
|
||||
required: [ mountOptions ]
|
||||
|
@ -196,7 +196,7 @@ class FstabGenerator(object):
|
||||
dct = self.generate_fstab_line_info(mount_entry)
|
||||
if dct:
|
||||
self.print_fstab_line(dct, file=fstab_file)
|
||||
else:
|
||||
elif partition["fs"] != "zfs": # zfs partitions don't need an entry in fstab
|
||||
dct = self.generate_fstab_line_info(partition)
|
||||
if dct:
|
||||
self.print_fstab_line(dct, file=fstab_file)
|
||||
@ -236,6 +236,10 @@ class FstabGenerator(object):
|
||||
libcalamares.utils.debug("Ignoring foreign swap {!s} {!s}".format(disk_name, partition.get("uuid", None)))
|
||||
return None
|
||||
|
||||
# If this is btrfs subvol a dedicated to a swapfile, use different options than a normal btrfs subvol
|
||||
if filesystem == "btrfs" and partition["subvol"] == "/@swap":
|
||||
options = self.get_mount_options("btrfs_swap", mount_point)
|
||||
else:
|
||||
options = self.get_mount_options(filesystem, mount_point)
|
||||
|
||||
if is_ssd:
|
||||
|
@ -59,6 +59,32 @@ def get_grub_config_path(root_mount_point):
|
||||
return os.path.join(default_dir, default_config_file)
|
||||
|
||||
|
||||
def get_zfs_root():
|
||||
"""
|
||||
Looks in global storage to find the zfs root
|
||||
|
||||
:return: A string containing the path to the zfs root or None if it is not found
|
||||
"""
|
||||
|
||||
zfs = libcalamares.globalstorage.value("zfsDatasets")
|
||||
|
||||
if not zfs:
|
||||
libcalamares.utils.warning("Failed to locate zfs dataset list")
|
||||
return None
|
||||
|
||||
# Find the root dataset
|
||||
for dataset in zfs:
|
||||
try:
|
||||
if dataset["mountpoint"] == "/":
|
||||
return dataset["zpool"] + "/" + dataset["dsName"]
|
||||
except KeyError:
|
||||
# This should be impossible
|
||||
libcalamares.utils.warning("Internal error handling zfs dataset")
|
||||
raise
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def modify_grub_default(partitions, root_mount_point, distributor):
|
||||
"""
|
||||
Configures '/etc/default/grub' for hibernation and plymouth.
|
||||
@ -95,6 +121,8 @@ def modify_grub_default(partitions, root_mount_point, distributor):
|
||||
swap_outer_mappername = None
|
||||
no_save_default = False
|
||||
unencrypted_separate_boot = any(p["mountPoint"] == "/boot" and "luksMapperName" not in p for p in partitions)
|
||||
# If there is no dracut, and the root partition is ZFS, this gets set below
|
||||
zfs_root_path = None
|
||||
|
||||
for partition in partitions:
|
||||
if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs"):
|
||||
@ -139,7 +167,15 @@ def modify_grub_default(partitions, root_mount_point, distributor):
|
||||
f"root=/dev/mapper/{partition['luksMapperName']}"
|
||||
]
|
||||
|
||||
if partition["fs"] == "zfs" and partition["mountPoint"] == "/":
|
||||
zfs_root_path = get_zfs_root()
|
||||
|
||||
kernel_params = ["quiet"]
|
||||
|
||||
# Currently, grub doesn't detect this properly so it must be set manually
|
||||
if zfs_root_path:
|
||||
kernel_params.insert(0, "zfs=" + zfs_root_path)
|
||||
|
||||
if cryptdevice_params:
|
||||
kernel_params.extend(cryptdevice_params)
|
||||
|
||||
|
@ -158,6 +158,7 @@ def find_initcpio_features(partitions, root_mount_point):
|
||||
|
||||
swap_uuid = ""
|
||||
uses_btrfs = False
|
||||
uses_zfs = False
|
||||
uses_lvm2 = False
|
||||
encrypt_hook = False
|
||||
openswap_hook = False
|
||||
@ -182,6 +183,9 @@ def find_initcpio_features(partitions, root_mount_point):
|
||||
if partition["fs"] == "btrfs":
|
||||
uses_btrfs = True
|
||||
|
||||
if partition["fs"] == "zfs":
|
||||
uses_zfs = True
|
||||
|
||||
if "lvm2" in partition["fs"]:
|
||||
uses_lvm2 = True
|
||||
|
||||
@ -209,6 +213,9 @@ def find_initcpio_features(partitions, root_mount_point):
|
||||
if uses_lvm2:
|
||||
hooks.append("lvm2")
|
||||
|
||||
if uses_zfs:
|
||||
hooks.append("zfs")
|
||||
|
||||
if swap_uuid != "":
|
||||
hooks.extend(["resume"])
|
||||
if encrypt_hook and openswap_hook:
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
@ -20,12 +20,24 @@ import os
|
||||
import libcalamares
|
||||
|
||||
import gettext
|
||||
|
||||
_ = gettext.translation("calamares-python",
|
||||
localedir=libcalamares.utils.gettext_path(),
|
||||
languages=libcalamares.utils.gettext_languages(),
|
||||
fallback=True).gettext
|
||||
|
||||
|
||||
class ZfsException(Exception):
|
||||
"""Exception raised when there is a problem with zfs
|
||||
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
|
||||
def pretty_name():
|
||||
return _("Mounting partitions.")
|
||||
|
||||
@ -50,7 +62,8 @@ def get_btrfs_subvolumes(partitions):
|
||||
btrfs_subvolumes = [dict(mountPoint="/", subvolume="/@"), dict(mountPoint="/home", subvolume="/@home")]
|
||||
|
||||
# Filter out the subvolumes which have a dedicated partition
|
||||
non_root_partition_mounts = [ m for m in [ p.get("mountPoint", None) for p in partitions ] if m is not None and m != '/' ]
|
||||
non_root_partition_mounts = [m for m in [p.get("mountPoint", None) for p in partitions] if
|
||||
m is not None and m != '/']
|
||||
btrfs_subvolumes = list(filter(lambda s: s["mountPoint"] not in non_root_partition_mounts, btrfs_subvolumes))
|
||||
|
||||
# If we have a swap **file**, give it a separate subvolume.
|
||||
@ -61,6 +74,70 @@ def get_btrfs_subvolumes(partitions):
|
||||
return btrfs_subvolumes
|
||||
|
||||
|
||||
def mount_zfs(root_mount_point, partition):
|
||||
""" Mounts a zfs partition at @p root_mount_point
|
||||
|
||||
:param root_mount_point: The absolute path to the root of the install
|
||||
:param partition: The partition map from global storage for this partition
|
||||
:return:
|
||||
"""
|
||||
# Get the list of zpools from global storage
|
||||
zfs_pool_list = libcalamares.globalstorage.value("zfsPoolInfo")
|
||||
if not zfs_pool_list:
|
||||
libcalamares.utils.warning("Failed to locate zfsPoolInfo data in global storage")
|
||||
raise ZfsException(_("Internal error mounting zfs datasets"))
|
||||
|
||||
# Find the zpool matching this partition
|
||||
for zfs_pool in zfs_pool_list:
|
||||
if zfs_pool["mountpoint"] == partition["mountPoint"]:
|
||||
pool_name = zfs_pool["poolName"]
|
||||
ds_name = zfs_pool["dsName"]
|
||||
|
||||
# import the zpool
|
||||
try:
|
||||
libcalamares.utils.host_env_process_output(["zpool", "import", "-N", "-R", root_mount_point, pool_name], None)
|
||||
except subprocess.CalledProcessError:
|
||||
raise ZfsException(_("Failed to import zpool"))
|
||||
|
||||
# Get the encrpytion information from global storage
|
||||
zfs_info_list = libcalamares.globalstorage.value("zfsInfo")
|
||||
encrypt = False
|
||||
if zfs_info_list:
|
||||
for zfs_info in zfs_info_list:
|
||||
if zfs_info["mountpoint"] == partition["mountPoint"] and zfs_info["encrypted"] is True:
|
||||
encrypt = True
|
||||
passphrase = zfs_info["passphrase"]
|
||||
|
||||
if encrypt is True:
|
||||
# The zpool is encrypted, we need to unlock it
|
||||
try:
|
||||
libcalamares.utils.host_env_process_output(["zfs", "load-key", pool_name], None, passphrase)
|
||||
except subprocess.CalledProcessError:
|
||||
raise ZfsException(_("Failed to unlock zpool"))
|
||||
|
||||
if partition["mountPoint"] == '/':
|
||||
# Get the zfs dataset list from global storage
|
||||
zfs = libcalamares.globalstorage.value("zfsDatasets")
|
||||
|
||||
if not zfs:
|
||||
libcalamares.utils.warning("Failed to locate zfs dataset list")
|
||||
raise ZfsException(_("Internal error mounting zfs datasets"))
|
||||
|
||||
zfs.sort(key=lambda x: x["mountpoint"])
|
||||
for dataset in zfs:
|
||||
try:
|
||||
if dataset["canMount"] == "noauto" or dataset["canMount"] is True:
|
||||
libcalamares.utils.host_env_process_output(["zfs", "mount",
|
||||
dataset["zpool"] + '/' + dataset["dsName"]])
|
||||
except subprocess.CalledProcessError:
|
||||
raise ZfsException(_("Failed to set zfs mountpoint"))
|
||||
else:
|
||||
try:
|
||||
libcalamares.utils.host_env_process_output(["zfs", "mount", pool_name + '/' + ds_name])
|
||||
except subprocess.CalledProcessError:
|
||||
raise ZfsException(_("Failed to set zfs mountpoint"))
|
||||
|
||||
|
||||
def mount_partition(root_mount_point, partition, partitions):
|
||||
"""
|
||||
Do a single mount of @p partition inside @p root_mount_point.
|
||||
@ -96,6 +173,9 @@ def mount_partition(root_mount_point, partition, partitions):
|
||||
if "luksMapperName" in partition:
|
||||
device = os.path.join("/dev/mapper", partition["luksMapperName"])
|
||||
|
||||
if fstype == "zfs":
|
||||
mount_zfs(root_mount_point, partition)
|
||||
else: # fstype == "zfs"
|
||||
if libcalamares.utils.mount(device,
|
||||
mount_point,
|
||||
fstype,
|
||||
@ -111,9 +191,11 @@ def mount_partition(root_mount_point, partition, partitions):
|
||||
libcalamares.globalstorage.insert("btrfsSubvolumes", btrfs_subvolumes)
|
||||
# Create the subvolumes that are in the completed list
|
||||
for s in btrfs_subvolumes:
|
||||
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
||||
root_mount_point + s['subvolume']])
|
||||
|
||||
subprocess.check_call(["btrfs", "subvolume", "create",
|
||||
root_mount_point + s["subvolume"]])
|
||||
if s["mountPoint"] == "/":
|
||||
# insert the root subvolume into global storage
|
||||
libcalamares.globalstorage.insert("btrfsRootSubvolume", s["subvolume"])
|
||||
subprocess.check_call(["umount", "-v", root_mount_point])
|
||||
|
||||
device = partition["device"]
|
||||
@ -161,8 +243,11 @@ def run():
|
||||
# under /tmp, we make sure /tmp is mounted before the partition)
|
||||
mountable_partitions = [p for p in partitions + extra_mounts if "mountPoint" in p and p["mountPoint"]]
|
||||
mountable_partitions.sort(key=lambda x: x["mountPoint"])
|
||||
try:
|
||||
for partition in mountable_partitions:
|
||||
mount_partition(root_mount_point, partition, partitions)
|
||||
except ZfsException as ze:
|
||||
return _("zfs mounting error"), ze.message
|
||||
|
||||
libcalamares.globalstorage.insert("rootMountPoint", root_mount_point)
|
||||
|
||||
|
@ -379,6 +379,7 @@ class PMPacman(PackageManager):
|
||||
def __init__(self):
|
||||
import re
|
||||
progress_match = re.compile("^\\((\\d+)/(\\d+)\\)")
|
||||
|
||||
def line_cb(line):
|
||||
if line.startswith(":: "):
|
||||
self.in_package_changes = "package changes" in line
|
||||
@ -396,30 +397,79 @@ class PMPacman(PackageManager):
|
||||
self.in_package_changes = False
|
||||
self.line_cb = line_cb
|
||||
|
||||
pacman = libcalamares.job.configuration.get("pacman", None)
|
||||
if pacman is None:
|
||||
pacman = dict()
|
||||
if type(pacman) is not dict:
|
||||
libcalamares.utils.warning("Job configuration *pacman* will be ignored.")
|
||||
pacman = dict()
|
||||
self.pacman_num_retries = pacman.get("num_retries", 0)
|
||||
self.pacman_disable_timeout = pacman.get("disable_download_timeout", False)
|
||||
self.pacman_needed_only = pacman.get("needed_only", False)
|
||||
|
||||
def reset_progress(self):
|
||||
self.in_package_changes = False
|
||||
# These are globals
|
||||
self.progress_fraction = (completed_packages * 1.0 / total_packages)
|
||||
|
||||
def install(self, pkgs, from_local=False):
|
||||
if from_local:
|
||||
pacman_flags = "-U"
|
||||
def run_pacman(self, command, callback=False):
|
||||
"""
|
||||
Call pacman in a loop until it is successful or the number of retries is exceeded
|
||||
:param command: The pacman command to run
|
||||
:param callback: An optional boolean that indicates if this pacman run should use the callback
|
||||
:return:
|
||||
"""
|
||||
|
||||
pacman_count = 0
|
||||
while pacman_count <= self.pacman_num_retries:
|
||||
pacman_count += 1
|
||||
try:
|
||||
if callback is True:
|
||||
libcalamares.utils.target_env_process_output(command, self.line_cb)
|
||||
else:
|
||||
pacman_flags = "-S"
|
||||
libcalamares.utils.target_env_process_output(command)
|
||||
|
||||
return
|
||||
except subprocess.CalledProcessError:
|
||||
if pacman_count <= self.pacman_num_retries:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
def install(self, pkgs, from_local=False):
|
||||
command = ["pacman"]
|
||||
|
||||
if from_local:
|
||||
command.append("-U")
|
||||
else:
|
||||
command.append("-S")
|
||||
|
||||
command.append("--noconfirm")
|
||||
|
||||
if self.pacman_needed_only is True:
|
||||
command.append("--needed")
|
||||
|
||||
if self.pacman_disable_timeout is True:
|
||||
command.append("--disable-download-timeout")
|
||||
|
||||
command += pkgs
|
||||
|
||||
self.reset_progress()
|
||||
libcalamares.utils.target_env_process_output(["pacman", pacman_flags,
|
||||
"--noconfirm"] + pkgs, self.line_cb)
|
||||
self.run_pacman(command, True)
|
||||
|
||||
def remove(self, pkgs):
|
||||
self.reset_progress()
|
||||
libcalamares.utils.target_env_process_output(["pacman", "-Rs", "--noconfirm"] + pkgs, self.line_cb)
|
||||
self.run_pacman(["pacman", "-Rs", "--noconfirm"] + pkgs, True)
|
||||
|
||||
def update_db(self):
|
||||
check_target_env_call(["pacman", "-Sy"])
|
||||
self.run_pacman(["pacman", "-Sy"])
|
||||
|
||||
def update_system(self):
|
||||
check_target_env_call(["pacman", "-Su", "--noconfirm"])
|
||||
command = ["pacman", "-Su", "--noconfirm"]
|
||||
if self.pacman_disable_timeout is True:
|
||||
command.append("--disable-download-timeout")
|
||||
|
||||
self.run_pacman(command)
|
||||
|
||||
|
||||
class PMPamac(PackageManager):
|
||||
|
@ -62,6 +62,23 @@ skip_if_no_internet: false
|
||||
update_db: true
|
||||
update_system: false
|
||||
|
||||
# pacman specific options
|
||||
#
|
||||
# *num_retries* should be a positive integer which specifies the
|
||||
# number of times the call to pacman will be retried in the event of a
|
||||
# failure. If it is missing, it will be set to 0.
|
||||
#
|
||||
# *disable_download_timeout* is a boolean that, when true, includes
|
||||
# the flag --disable-download-timeout on calls to pacman. When missing,
|
||||
# false is assumed.
|
||||
#
|
||||
# *needed_only* is a boolean that includes the pacman argument --needed
|
||||
# when set to true. If missing, false is assumed.
|
||||
pacman:
|
||||
num_retries: 0
|
||||
disable_download_timeout: false
|
||||
needed_only: false
|
||||
|
||||
#
|
||||
# List of maps with package operations such as install or remove.
|
||||
# Distro developers can provide a list of packages to remove
|
||||
|
@ -26,6 +26,14 @@ properties:
|
||||
update_system: { type: boolean, default: false }
|
||||
skip_if_no_internet: { type: boolean, default: false }
|
||||
|
||||
pacman:
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
num_retries: { type: integer, default: 0 }
|
||||
disable_download_timeout: { type: boolean, default: false }
|
||||
needed_only: { type: boolean, default: false }
|
||||
|
||||
operations:
|
||||
type: array
|
||||
items:
|
||||
|
3
src/modules/packages/tests/1.global
Normal file
3
src/modules/packages/tests/1.global
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
rootMountPoint: /tmp
|
@ -1,7 +1,6 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
backend: dummy
|
||||
rootMountPoint: /tmp/mount
|
||||
operations:
|
||||
- install:
|
||||
- pre-script: touch /tmp/foo
|
42
src/modules/packages/tests/CMakeTests.txt
Normal file
42
src/modules/packages/tests/CMakeTests.txt
Normal file
@ -0,0 +1,42 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# We have tests to load (some) of the package-managers specifically, to
|
||||
# test their configuration code and implementation. Those tests conventionally
|
||||
# live in Python files here in the tests/ directory. Add them.
|
||||
|
||||
# Pacman (Arch) tests
|
||||
set(_pm pacman)
|
||||
add_test(
|
||||
NAME configure-packages-${_pm}
|
||||
COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-pm-${_pm}.py
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_test(
|
||||
NAME configure-packages-${_pm}-ops-1
|
||||
COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-pm-${_pm}.py ${CMAKE_CURRENT_LIST_DIR}/pm-pacman-1.yaml 4 1 1
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_test(
|
||||
NAME configure-packages-${_pm}-ops-2
|
||||
COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-pm-${_pm}.py ${CMAKE_CURRENT_LIST_DIR}/pm-pacman-2.yaml 3 0 0
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
if ( BUILD_TESTING AND BUILD_SCHEMA_TESTING AND PYTHONINTERP_FOUND AND PYTHON_EXECUTABLE )
|
||||
set( _module packages )
|
||||
set( _schema_file "${CMAKE_CURRENT_SOURCE_DIR}/${_module}/${_module}.schema.yaml" )
|
||||
message(STATUS "Schema ${_schema_file}")
|
||||
foreach( _cf pm-pacman-1.yaml pm-pacman-2.yaml )
|
||||
set( _conf_file "${CMAKE_CURRENT_SOURCE_DIR}/${_module}/tests/${_cf}" )
|
||||
if ( EXISTS "${_schema_file}" AND EXISTS "${_conf_file}" )
|
||||
add_test(
|
||||
NAME validate-packages-${_cf}
|
||||
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_SOURCE_DIR}/ci/configvalidator.py" "${_schema_file}" "${_conf_file}"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Missing ${_conf_file}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
10
src/modules/packages/tests/pm-pacman-1.yaml
Normal file
10
src/modules/packages/tests/pm-pacman-1.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
backend: pacman
|
||||
operations: []
|
||||
|
||||
pacman:
|
||||
num_retries: 4
|
||||
disable_download_timeout: yes
|
||||
needed_only: true
|
||||
|
9
src/modules/packages/tests/pm-pacman-2.yaml
Normal file
9
src/modules/packages/tests/pm-pacman-2.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
backend: pacman
|
||||
operations: []
|
||||
|
||||
# Leave some things unspecified
|
||||
pacman:
|
||||
num_retries: 3
|
||||
|
36
src/modules/packages/tests/test-pm-pacman.py
Normal file
36
src/modules/packages/tests/test-pm-pacman.py
Normal file
@ -0,0 +1,36 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Calamares Boilerplate
|
||||
import libcalamares
|
||||
libcalamares.globalstorage = libcalamares.GlobalStorage(None)
|
||||
libcalamares.globalstorage.insert("testing", True)
|
||||
|
||||
# Module prep-work
|
||||
from src.modules.packages import main
|
||||
|
||||
# .. we don't have a job in this test, so fake one
|
||||
class Job(object):
|
||||
def __init__(self, filename):
|
||||
self.configuration = libcalamares.utils.load_yaml(filename) if filename is not None else dict()
|
||||
|
||||
import sys
|
||||
if len(sys.argv) > 4:
|
||||
filename = sys.argv[1]
|
||||
retry = int(sys.argv[2])
|
||||
timeout = bool(int(sys.argv[3]))
|
||||
needed = bool(int(sys.argv[4]))
|
||||
else:
|
||||
filename = None
|
||||
retry = 0
|
||||
timeout = False
|
||||
needed = False
|
||||
|
||||
libcalamares.utils.warning("Expecting {!s} retry={!s} timeout={!s} needed={!s}".format(filename, retry, timeout, needed))
|
||||
|
||||
# Specific PM test
|
||||
libcalamares.job = Job(filename)
|
||||
p = main.PMPacman()
|
||||
assert p.pacman_num_retries == retry, "{!r} vs {!r}".format(p.pacman_num_retries, retry)
|
||||
assert p.pacman_disable_timeout == timeout, "{!r} vs {!r}".format(p.pacman_disable_timeout, timeout)
|
||||
assert p.pacman_needed_only == needed, "{!r} vs {!r}".format(p.pacman_needed_only, needed)
|
@ -257,14 +257,16 @@ PartitionCoreModule::doInit()
|
||||
cDebug() << Logger::SubEntry << "node\tcapacity\tname\tprettyName";
|
||||
for ( auto device : devices )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << Logger::Pointer( device );
|
||||
if ( device )
|
||||
{
|
||||
// Gives ownership of the Device* to the DeviceInfo object
|
||||
auto deviceInfo = new DeviceInfo( device );
|
||||
m_deviceInfos << deviceInfo;
|
||||
cDebug() << Logger::SubEntry << device->deviceNode() << device->capacity() << device->name()
|
||||
<< device->prettyName();
|
||||
cDebug() << Logger::SubEntry
|
||||
<< device->deviceNode()
|
||||
<< device->capacity()
|
||||
<< Logger::RedactedName( "DevName", device->name() )
|
||||
<< Logger::RedactedName( "DevNamePretty", device->prettyName() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -707,10 +709,10 @@ PartitionCoreModule::dumpQueue() const
|
||||
cDebug() << "# Queue:";
|
||||
for ( auto info : m_deviceInfos )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "## Device:" << info->device->name();
|
||||
cDebug() << Logger::SubEntry << "## Device:" << info->device->deviceNode();
|
||||
for ( const auto& job : info->jobs() )
|
||||
{
|
||||
cDebug() << Logger::SubEntry << "-" << job->prettyName();
|
||||
cDebug() << Logger::SubEntry << "-" << job->metaObject()->className();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +296,9 @@ PartitionLayout::createPartitions( Device* dev,
|
||||
}
|
||||
|
||||
Partition* part = nullptr;
|
||||
if ( luksPassphrase.isEmpty() )
|
||||
|
||||
// Encryption for zfs is handled in the zfs module
|
||||
if ( luksPassphrase.isEmpty() || correctFS( entry.partFileSystem ) == FileSystem::Zfs )
|
||||
{
|
||||
part = KPMHelpers::createNewPartition( parent,
|
||||
*dev,
|
||||
@ -319,6 +321,24 @@ PartitionLayout::createPartitions( Device* dev,
|
||||
luksPassphrase,
|
||||
KPM_PARTITION_FLAG( None ) );
|
||||
}
|
||||
|
||||
// For zfs, we need to make the passphrase available to later modules
|
||||
if ( correctFS( entry.partFileSystem ) == FileSystem::Zfs )
|
||||
{
|
||||
Calamares::GlobalStorage* storage = Calamares::JobQueue::instance()->globalStorage();
|
||||
QList< QVariant > zfsInfoList;
|
||||
QVariantMap zfsInfo;
|
||||
|
||||
// Save the information subsequent modules will need
|
||||
zfsInfo[ "encrypted" ] = !luksPassphrase.isEmpty();
|
||||
zfsInfo[ "passphrase" ] = luksPassphrase;
|
||||
zfsInfo[ "mountpoint" ] = entry.partMountPoint;
|
||||
|
||||
// Add it to the list and insert it into global storage
|
||||
zfsInfoList.append( zfsInfo );
|
||||
storage->insert( "zfsInfo", zfsInfoList );
|
||||
}
|
||||
|
||||
PartitionInfo::setFormat( part, true );
|
||||
PartitionInfo::setMountPoint( part, entry.partMountPoint );
|
||||
if ( !entry.partLabel.isEmpty() )
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
#include "partition/FileSystem.h"
|
||||
#include "partition/PartitionQuery.h"
|
||||
#include "utils/Logger.h"
|
||||
@ -104,7 +105,9 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
|
||||
QStringList fsNames;
|
||||
for ( auto fs : FileSystemFactory::map() )
|
||||
{
|
||||
if ( fs->supportCreate() != FileSystem::cmdSupportNone && fs->type() != FileSystem::Extended )
|
||||
// We need to ensure zfs is added to the list if the zfs module is enabled
|
||||
if ( ( fs->type() == FileSystem::Type::Zfs && Calamares::Settings::instance()->isModuleEnabled( "zfs" ) )
|
||||
|| ( fs->supportCreate() != FileSystem::cmdSupportNone && fs->type() != FileSystem::Extended ) )
|
||||
{
|
||||
fsNames << userVisibleFS( fs ); // This is put into the combobox
|
||||
if ( fs->type() == defaultFSType )
|
||||
@ -240,7 +243,8 @@ CreatePartitionDialog::getNewlyCreatedPartition()
|
||||
// does so, to set up the partition for create-and-then-set-flags.
|
||||
Partition* partition = nullptr;
|
||||
QString luksPassphrase = m_ui->encryptWidget->passphrase();
|
||||
if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty() )
|
||||
if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty()
|
||||
&& fsType != FileSystem::Zfs )
|
||||
{
|
||||
partition = KPMHelpers::createNewEncryptedPartition(
|
||||
m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() );
|
||||
@ -251,6 +255,31 @@ CreatePartitionDialog::getNewlyCreatedPartition()
|
||||
m_parent, *m_device, m_role, fsType, fsLabel, first, last, PartitionTable::Flags() );
|
||||
}
|
||||
|
||||
// For zfs, we let the zfs module handle the encryption but we need to make the passphrase available to later modules
|
||||
if ( fsType == FileSystem::Zfs )
|
||||
{
|
||||
Calamares::GlobalStorage* storage = Calamares::JobQueue::instance()->globalStorage();
|
||||
QList< QVariant > zfsInfoList;
|
||||
QVariantMap zfsInfo;
|
||||
|
||||
// If this is not the first encrypted zfs partition, get the old list first
|
||||
if ( storage->contains( "zfsInfo" ) )
|
||||
{
|
||||
zfsInfoList = storage->value( "zfsInfo" ).toList();
|
||||
storage->remove( "zfsInfo" );
|
||||
}
|
||||
|
||||
// Save the information subsequent modules will need
|
||||
zfsInfo[ "encrypted" ]
|
||||
= m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty();
|
||||
zfsInfo[ "passphrase" ] = luksPassphrase;
|
||||
zfsInfo[ "mountpoint" ] = selectedMountPoint( m_ui->mountPointComboBox );
|
||||
|
||||
// Add it to the list and insert it into global storage
|
||||
zfsInfoList.append( zfsInfo );
|
||||
storage->insert( "zfsInfo", zfsInfoList );
|
||||
}
|
||||
|
||||
if ( m_device->type() == Device::Type::LVM_Device )
|
||||
{
|
||||
partition->setPartitionPath( m_device->deviceNode() + QStringLiteral( "/" )
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
#include "partition/FileSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
@ -89,7 +90,9 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
|
||||
QStringList fsNames;
|
||||
for ( auto fs : FileSystemFactory::map() )
|
||||
{
|
||||
if ( fs->supportCreate() != FileSystem::cmdSupportNone && fs->type() != FileSystem::Extended )
|
||||
// We need to ensure zfs is added to the list if the zfs module is enabled
|
||||
if ( ( fs->type() == FileSystem::Type::Zfs && Calamares::Settings::instance()->isModuleEnabled( "zfs" ) )
|
||||
|| ( fs->supportCreate() != FileSystem::cmdSupportNone && fs->type() != FileSystem::Extended ) )
|
||||
{
|
||||
fsNames << userVisibleFS( fs ); // For the combo box
|
||||
}
|
||||
@ -117,6 +120,12 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
|
||||
m_ui->fileSystemLabel->setEnabled( m_ui->formatRadioButton->isChecked() );
|
||||
m_ui->fileSystemComboBox->setEnabled( m_ui->formatRadioButton->isChecked() );
|
||||
|
||||
// Force a format if the existing device is a zfs device since reusing a zpool isn't currently supported
|
||||
m_ui->formatRadioButton->setChecked( m_partition->fileSystem().type() == FileSystem::Type::Zfs );
|
||||
m_ui->formatRadioButton->setEnabled( !( m_partition->fileSystem().type() == FileSystem::Type::Zfs ) );
|
||||
m_ui->keepRadioButton->setChecked( !( m_partition->fileSystem().type() == FileSystem::Type::Zfs ) );
|
||||
m_ui->keepRadioButton->setEnabled( !( m_partition->fileSystem().type() == FileSystem::Type::Zfs ) );
|
||||
|
||||
setFlagList( *( m_ui->m_listFlags ), m_partition->availableFlags(), PartitionInfo::flags( m_partition ) );
|
||||
}
|
||||
|
||||
|
@ -215,6 +215,12 @@ getPVGroups( const QString& deviceName )
|
||||
* meant **only** for debugging and is not displayed to the user,
|
||||
* which is why no translation is applied.
|
||||
*
|
||||
* The MessageAndPath class stores a C-style pointer to a character
|
||||
* array -- from QT_TRANSLATE_NOOP() -- and a path to substitute into it.
|
||||
*
|
||||
* When the tryX() functions return an "empty string", it is an
|
||||
* empty MessageAndPath which acts like an empty string (in particular,
|
||||
* isEmpty() is true).
|
||||
*/
|
||||
|
||||
class MessageAndPath
|
||||
@ -237,8 +243,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
#if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) )
|
||||
// TODO: 3.3 remove because newer Qt does support constness
|
||||
const char* m_message = nullptr;
|
||||
QString m_path;
|
||||
#else
|
||||
const char* const m_message = nullptr;
|
||||
QString const m_path;
|
||||
#endif
|
||||
};
|
||||
|
||||
STATICTEST inline QDebug&
|
||||
|
@ -11,8 +11,10 @@
|
||||
|
||||
#include "CreatePartitionJob.h"
|
||||
|
||||
#include "core/PartitionInfo.h"
|
||||
#include "partition/FileSystem.h"
|
||||
#include "partition/PartitionQuery.h"
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Units.h"
|
||||
|
||||
@ -24,9 +26,79 @@
|
||||
#include <kpmcore/ops/newoperation.h>
|
||||
#include <kpmcore/util/report.h>
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qregularexpression.h>
|
||||
|
||||
using CalamaresUtils::Partition::untranslatedFS;
|
||||
using CalamaresUtils::Partition::userVisibleFS;
|
||||
|
||||
/** @brief Create
|
||||
*
|
||||
* Uses sfdisk to remove @p partition. This should only be used in cases
|
||||
* where using kpmcore to remove the partition would not be appropriate
|
||||
*
|
||||
*/
|
||||
static Calamares::JobResult
|
||||
createZfs( Partition* partition, Device* device )
|
||||
{
|
||||
auto r = CalamaresUtils::System::instance()->runCommand(
|
||||
{ "sh",
|
||||
"-c",
|
||||
"echo start=" + QString::number( partition->firstSector() ) + " size="
|
||||
+ QString::number( partition->length() ) + " | sfdisk --append --force " + partition->devicePath() },
|
||||
std::chrono::seconds( 5 ) );
|
||||
if ( r.getExitCode() != 0 )
|
||||
{
|
||||
return Calamares::JobResult::error(
|
||||
QCoreApplication::translate( CreatePartitionJob::staticMetaObject.className(),
|
||||
"Failed to create partition" ),
|
||||
QCoreApplication::translate( CreatePartitionJob::staticMetaObject.className(),
|
||||
"Failed to create zfs partition with output: "
|
||||
+ r.getOutput().toLocal8Bit() ) );
|
||||
}
|
||||
|
||||
// Now we need to do some things that would normally be done by kpmcore
|
||||
|
||||
// First we get the device node from the output and set it as the partition path
|
||||
QRegularExpression re( QStringLiteral( "Created a new partition (\\d+)" ) );
|
||||
QRegularExpressionMatch rem = re.match( r.getOutput() );
|
||||
|
||||
QString deviceNode;
|
||||
if ( rem.hasMatch() )
|
||||
{
|
||||
if ( partition->devicePath().back().isDigit() )
|
||||
{
|
||||
deviceNode = partition->devicePath() + QLatin1Char( 'p' ) + rem.captured( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceNode = partition->devicePath() + rem.captured( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
partition->setPartitionPath( deviceNode );
|
||||
|
||||
// If it is a gpt device, set the partition UUID
|
||||
if ( device->partitionTable()->type() == PartitionTable::gpt && partition->uuid().isEmpty() )
|
||||
{
|
||||
r = CalamaresUtils::System::instance()->runCommand(
|
||||
{ "sfdisk", "--list", "--output", "Device,UUID", partition->devicePath() }, std::chrono::seconds( 5 ) );
|
||||
if ( r.getExitCode() == 0 )
|
||||
{
|
||||
QRegularExpression re( deviceNode + QStringLiteral( " +(.+)" ) );
|
||||
QRegularExpressionMatch rem = re.match( r.getOutput() );
|
||||
|
||||
if ( rem.hasMatch() )
|
||||
{
|
||||
partition->setUUID( rem.captured( 1 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Calamares::JobResult::ok();
|
||||
}
|
||||
|
||||
|
||||
CreatePartitionJob::CreatePartitionJob( Device* device, Partition* partition )
|
||||
: PartitionJob( partition )
|
||||
, m_device( device )
|
||||
@ -194,6 +266,13 @@ CreatePartitionJob::prettyStatusMessage() const
|
||||
Calamares::JobResult
|
||||
CreatePartitionJob::exec()
|
||||
{
|
||||
// kpmcore doesn't currently handle this case properly so for now, we manually create the partion
|
||||
// The zfs module can later deal with creating a zpool in the partition
|
||||
if ( m_partition->fileSystem().type() == FileSystem::Type::Zfs )
|
||||
{
|
||||
return createZfs( m_partition, m_device );
|
||||
}
|
||||
|
||||
Report report( nullptr );
|
||||
NewOperation op( *m_device, m_partition );
|
||||
op.setStatus( Operation::StatusRunning );
|
||||
|
@ -104,14 +104,19 @@ mapForPartition( Partition* partition, const QString& uuid )
|
||||
// Debugging for inside the loop in createPartitionList(),
|
||||
// so indent a bit
|
||||
Logger::CDebug deb;
|
||||
using TR = Logger::DebugRow< const char* const, const QString& >;
|
||||
using TR = Logger::DebugRow< const char* const, const QString >;
|
||||
// clang-format off
|
||||
deb << Logger::SubEntry << "mapping for" << partition->partitionPath() << partition->deviceNode()
|
||||
<< TR( "partlabel", map[ "partlabel" ].toString() ) << TR( "partuuid", map[ "partuuid" ].toString() )
|
||||
<< TR( "parttype", map[ "parttype" ].toString() ) << TR( "partattrs", map[ "partattrs" ].toString() )
|
||||
<< TR( "mountPoint:", PartitionInfo::mountPoint( partition ) ) << TR( "fs:", map[ "fs" ].toString() )
|
||||
<< TR( "fsName", map[ "fsName" ].toString() ) << TR( "uuid", uuid )
|
||||
<< TR( "partlabel", map[ "partlabel" ].toString() )
|
||||
<< TR( "partition-uuid (partuuid)", Logger::RedactedName( "PartUUID", map[ "partuuid" ].toString() ) )
|
||||
<< TR( "parttype", map[ "parttype" ].toString() )
|
||||
<< TR( "partattrs", map[ "partattrs" ].toString() )
|
||||
<< TR( "mountPoint:", PartitionInfo::mountPoint( partition ) )
|
||||
<< TR( "fs:", map[ "fs" ].toString() )
|
||||
<< TR( "fsName", map[ "fsName" ].toString() )
|
||||
<< TR( "filesystem-uuid (uuid)", Logger::RedactedName( "FSUUID", uuid ) )
|
||||
<< TR( "claimed", map[ "claimed" ].toString() );
|
||||
|
||||
// clang-format on
|
||||
if ( partition->roles().has( PartitionRole::Luks ) )
|
||||
{
|
||||
const FileSystem& fsRef = partition->fileSystem();
|
||||
|
@ -19,6 +19,14 @@ QTEST_GUILESS_MAIN( ClearMountsJobTests )
|
||||
/* Not exactly public API */
|
||||
QStringList getPartitionsForDevice( const QString& deviceName );
|
||||
|
||||
/* At one point, the partitions-list was read from /proc/partitions by
|
||||
* running awk and grep, as below. Check that the current implementation
|
||||
* matches that crufty one.
|
||||
*
|
||||
* Update 2021-11-02: the newer implementation prepends /dev/ to the
|
||||
* names of the partitions, for simplicity elsewhere, so that needs
|
||||
* to be added in to the awk(1) program, too.
|
||||
*/
|
||||
QStringList
|
||||
getPartitionsForDevice_other( const QString& deviceName )
|
||||
{
|
||||
@ -26,7 +34,7 @@ getPartitionsForDevice_other( const QString& deviceName )
|
||||
process.setProgram( "sh" );
|
||||
process.setArguments(
|
||||
{ "-c",
|
||||
QString( "echo $(awk '{print $4}' /proc/partitions | sed -e '/name/d' -e '/^$/d' -e '/[1-9]/!d' | grep %1)" )
|
||||
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();
|
||||
|
@ -49,6 +49,27 @@ def list_mounts(root_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.
|
||||
|
||||
@ -94,6 +115,8 @@ def run():
|
||||
# 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
|
||||
|
13
src/modules/zfs/CMakeLists.txt
Normal file
13
src/modules/zfs/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# === This file is part of Calamares - <https://calamares.io> ===
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
calamares_add_plugin( zfs
|
||||
TYPE job
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
ZfsJob.cpp
|
||||
SHARED_LIB
|
||||
)
|
||||
|
21
src/modules/zfs/README.md
Normal file
21
src/modules/zfs/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
## zfs Module Notes
|
||||
|
||||
<!-- SPDX-FileCopyrightText: 2021 Evan James <dalto@fastmail.com>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
There are a few considerations to be aware of when enabling the zfs module
|
||||
* You must provide zfs kernel modules or kernel support on the ISO for the zfs module to function
|
||||
* The zfs kernel module must be loaded prior to the partition module running
|
||||
* One way to achieve this is by running `modprobe zfs`
|
||||
* Support for zfs in the partition module is conditional on the zfs module being enabled
|
||||
* The config for the default pools and datasets is configured and described in modules/zfs.conf
|
||||
* If you use grub with zfs, you must have `ZPOOL_VDEV_NAME_PATH=1` in your environment when running grub-install or grub-mkconfig.
|
||||
* Calamares will ensure this happens during the bootloader module.
|
||||
* It will also add it to `/etc/environment` so it will be available in the installation
|
||||
* If you have an scripts or other processes that trigger grub-mkconfig during the install process, be sure to add that to the environment
|
||||
* In most cases, you will need to enable services for zfs support appropriate to your distro. For example, when testing on Arch the following services were enabled:
|
||||
* zfs.target
|
||||
* zfs-import-cache
|
||||
* zfs-mount
|
||||
* zfs-import.target
|
365
src/modules/zfs/ZfsJob.cpp
Normal file
365
src/modules/zfs/ZfsJob.cpp
Normal file
@ -0,0 +1,365 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 Evan James <dalto@fastmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ZfsJob.h"
|
||||
|
||||
#include "utils/CalamaresUtilsSystem.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/** @brief Returns the alphanumeric portion of a string
|
||||
*
|
||||
* @p input is the input string
|
||||
*
|
||||
*/
|
||||
static QString
|
||||
alphaNumeric( QString input )
|
||||
{
|
||||
return input.remove( QRegExp( "[^a-zA-Z\\d\\s]" ) );
|
||||
}
|
||||
|
||||
/** @brief Returns the best available device for zpool creation
|
||||
*
|
||||
* zfs partitions generally don't have UUID until the zpool is created. Generally,
|
||||
* they are formed using either the id or the partuuid. The id isn't stored by kpmcore
|
||||
* so this function checks to see if we have a partuuid. If so, it forms a device path
|
||||
* for it. As a backup, it uses the device name i.e. /dev/sdax.
|
||||
*
|
||||
* The function returns a fully qualified path to the device or an empty string if no device
|
||||
* is found
|
||||
*
|
||||
* @p pMap is the partition map from global storage
|
||||
*
|
||||
*/
|
||||
static QString
|
||||
findBestZfsDevice( QVariantMap pMap )
|
||||
{
|
||||
// Find the best device identifier, if one isn't available, skip this partition
|
||||
QString deviceName;
|
||||
if ( pMap[ "partuuid" ].toString() != "" )
|
||||
{
|
||||
return "/dev/disk/by-partuuid/" + pMap[ "partuuid" ].toString().toLower();
|
||||
}
|
||||
else if ( pMap[ "device" ].toString() != "" )
|
||||
{
|
||||
return pMap[ "device" ].toString().toLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Converts the value in a QVariant to a string which is a valid option for canmount
|
||||
*
|
||||
* Storing "on" and "off" in QVariant results in a conversion to boolean. This function takes
|
||||
* the Qvariant in @p canMount and converts it to a QString holding "on", "off" or the string
|
||||
* value in the QVariant.
|
||||
*
|
||||
*/
|
||||
static QString
|
||||
convertCanMount( QVariant canMount )
|
||||
{
|
||||
if ( canMount == true )
|
||||
{
|
||||
return "on";
|
||||
}
|
||||
else if ( canMount == false )
|
||||
{
|
||||
return "off";
|
||||
}
|
||||
else
|
||||
{
|
||||
return canMount.toString();
|
||||
}
|
||||
}
|
||||
|
||||
ZfsJob::ZfsJob( QObject* parent )
|
||||
: Calamares::CppJob( parent )
|
||||
{
|
||||
}
|
||||
|
||||
ZfsJob::~ZfsJob() {}
|
||||
|
||||
QString
|
||||
ZfsJob::prettyName() const
|
||||
{
|
||||
return tr( "Create ZFS pools and datasets" );
|
||||
}
|
||||
|
||||
void
|
||||
ZfsJob::collectMountpoints( const QVariantList& partitions )
|
||||
{
|
||||
m_mountpoints.empty();
|
||||
for ( const QVariant& partition : partitions )
|
||||
{
|
||||
if ( partition.canConvert( QVariant::Map ) )
|
||||
{
|
||||
QString mountpoint = partition.toMap().value( "mountPoint" ).toString();
|
||||
if ( !mountpoint.isEmpty() )
|
||||
{
|
||||
m_mountpoints.append( mountpoint );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ZfsJob::isMountpointOverlapping( const QString& targetMountpoint ) const
|
||||
{
|
||||
for ( const QString& mountpoint : m_mountpoints )
|
||||
{
|
||||
if ( mountpoint != '/' && targetMountpoint.startsWith( mountpoint ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ZfsResult
|
||||
ZfsJob::createZpool( QString deviceName, QString poolName, QString poolOptions, bool encrypt, QString passphrase ) const
|
||||
{
|
||||
// zfs doesn't wait for the devices so pause for 2 seconds to ensure we give time for the device files to be created
|
||||
sleep( 2 );
|
||||
|
||||
QStringList command;
|
||||
if ( encrypt )
|
||||
{
|
||||
command = QStringList() << "zpool"
|
||||
<< "create" << poolOptions.split( ' ' ) << "-O"
|
||||
<< "encryption=aes-256-gcm"
|
||||
<< "-O"
|
||||
<< "keyformat=passphrase" << poolName << deviceName;
|
||||
}
|
||||
else
|
||||
{
|
||||
command = QStringList() << "zpool"
|
||||
<< "create" << poolOptions.split( ' ' ) << poolName << deviceName;
|
||||
}
|
||||
|
||||
auto r = CalamaresUtils::System::instance()->runCommand(
|
||||
CalamaresUtils::System::RunLocation::RunInHost, command, QString(), passphrase, std::chrono::seconds( 10 ) );
|
||||
|
||||
if ( r.getExitCode() != 0 )
|
||||
{
|
||||
cWarning() << "Failed to run zpool create. The output was: " + r.getOutput();
|
||||
return { false, tr( "Failed to create zpool on " ) + deviceName };
|
||||
}
|
||||
|
||||
return { true, QString() };
|
||||
}
|
||||
|
||||
Calamares::JobResult
|
||||
ZfsJob::exec()
|
||||
{
|
||||
QVariantList partitions;
|
||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||
if ( gs && gs->contains( "partitions" ) && gs->value( "partitions" ).canConvert( QVariant::List ) )
|
||||
{
|
||||
partitions = gs->value( "partitions" ).toList();
|
||||
}
|
||||
else
|
||||
{
|
||||
cWarning() << "No *partitions* defined.";
|
||||
return Calamares::JobResult::internalError( tr( "Configuration Error" ),
|
||||
tr( "No partitions are available for Zfs." ),
|
||||
Calamares::JobResult::InvalidConfiguration );
|
||||
}
|
||||
|
||||
const CalamaresUtils::System* system = CalamaresUtils::System::instance();
|
||||
|
||||
QVariantList poolNames;
|
||||
|
||||
// Check to ensure the list of zfs info from the partition module is available and convert it to a list
|
||||
if ( !gs->contains( "zfsInfo" ) && gs->value( "zfsInfo" ).canConvert( QVariant::List ) )
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "Internal data missing" ), tr( "Failed to create zpool" ) );
|
||||
}
|
||||
QVariantList zfsInfoList = gs->value( "zfsInfo" ).toList();
|
||||
|
||||
for ( auto& partition : qAsConst( partitions ) )
|
||||
{
|
||||
QVariantMap pMap;
|
||||
if ( partition.canConvert( QVariant::Map ) )
|
||||
{
|
||||
pMap = partition.toMap();
|
||||
}
|
||||
|
||||
// If it isn't a zfs partition, ignore it
|
||||
if ( pMap[ "fsName" ] != "zfs" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the best device identifier, if one isn't available, skip this partition
|
||||
QString deviceName = findBestZfsDevice( pMap );
|
||||
if ( deviceName.isEmpty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the partition doesn't have a mountpoint, skip it
|
||||
QString mountpoint = pMap[ "mountPoint" ].toString();
|
||||
if ( mountpoint.isEmpty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build a poolname off config pool name and the mountpoint, this is not ideal but should work until there is UI built for zfs
|
||||
QString poolName = m_poolName;
|
||||
if ( mountpoint != '/' )
|
||||
{
|
||||
poolName += alphaNumeric( mountpoint );
|
||||
}
|
||||
|
||||
// Look in the zfs info list to see if this partition should be encrypted
|
||||
bool encrypt = false;
|
||||
QString passphrase;
|
||||
for ( const QVariant& zfsInfo : qAsConst( zfsInfoList ) )
|
||||
{
|
||||
if ( zfsInfo.canConvert( QVariant::Map ) && zfsInfo.toMap().value( "encrypted" ).toBool()
|
||||
&& mountpoint == zfsInfo.toMap().value( "mountpoint" ) )
|
||||
{
|
||||
encrypt = true;
|
||||
passphrase = zfsInfo.toMap().value( "passphrase" ).toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Create the zpool
|
||||
ZfsResult zfsResult;
|
||||
if ( encrypt )
|
||||
{
|
||||
zfsResult = createZpool( deviceName, poolName, m_poolOptions, true, passphrase );
|
||||
}
|
||||
else
|
||||
{
|
||||
zfsResult = createZpool( deviceName, poolName, m_poolOptions, false );
|
||||
}
|
||||
|
||||
if ( !zfsResult.success )
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "Failed to create zpool" ), zfsResult.failureMessage );
|
||||
}
|
||||
|
||||
// Save the poolname, dataset name and mountpoint. It will later be added to a list and placed in global storage.
|
||||
// This will be used by later modules including mount and umount
|
||||
QVariantMap poolNameEntry;
|
||||
poolNameEntry[ "poolName" ] = poolName;
|
||||
poolNameEntry[ "mountpoint" ] = mountpoint;
|
||||
poolNameEntry[ "dsName" ] = "none";
|
||||
|
||||
// If the mountpoint is /, create datasets per the config file. If not, create a single dataset mounted at the partitions mountpoint
|
||||
if ( mountpoint == '/' )
|
||||
{
|
||||
collectMountpoints( partitions );
|
||||
QVariantList datasetList;
|
||||
for ( const auto& dataset : qAsConst( m_datasets ) )
|
||||
{
|
||||
QVariantMap datasetMap = dataset.toMap();
|
||||
|
||||
// Make sure all values are valid
|
||||
if ( datasetMap[ "dsName" ].toString().isEmpty() || datasetMap[ "mountpoint" ].toString().isEmpty()
|
||||
|| datasetMap[ "canMount" ].toString().isEmpty() )
|
||||
{
|
||||
cWarning() << "Bad dataset entry";
|
||||
continue;
|
||||
}
|
||||
|
||||
// We should skip this dataset if it conflicts with a permanent mountpoint
|
||||
if ( isMountpointOverlapping( datasetMap[ "mountpoint" ].toString() ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QString canMount = convertCanMount( datasetMap[ "canMount" ].toString() );
|
||||
|
||||
// Create the dataset
|
||||
auto r = system->runCommand( { QStringList() << "zfs"
|
||||
<< "create" << m_datasetOptions.split( ' ' ) << "-o"
|
||||
<< "canmount=" + canMount << "-o"
|
||||
<< "mountpoint=" + datasetMap[ "mountpoint" ].toString()
|
||||
<< poolName + "/" + datasetMap[ "dsName" ].toString() },
|
||||
std::chrono::seconds( 10 ) );
|
||||
if ( r.getExitCode() != 0 )
|
||||
{
|
||||
cWarning() << "Failed to create dataset" << datasetMap[ "dsName" ].toString();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the dataset to the list for global storage this information is used later to properly set
|
||||
// the mount options on each dataset
|
||||
datasetMap[ "zpool" ] = m_poolName;
|
||||
datasetList.append( datasetMap );
|
||||
}
|
||||
|
||||
// If the list isn't empty, add it to global storage
|
||||
if ( !datasetList.isEmpty() )
|
||||
{
|
||||
gs->insert( "zfsDatasets", datasetList );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QString dsName = mountpoint;
|
||||
dsName = alphaNumeric( mountpoint );
|
||||
auto r = system->runCommand( { QStringList() << "zfs"
|
||||
<< "create" << m_datasetOptions.split( ' ' ) << "-o"
|
||||
<< "canmount=on"
|
||||
<< "-o"
|
||||
<< "mountpoint=" + mountpoint << poolName + "/" + dsName },
|
||||
std::chrono::seconds( 10 ) );
|
||||
if ( r.getExitCode() != 0 )
|
||||
{
|
||||
return Calamares::JobResult::error( tr( "Failed to create dataset" ),
|
||||
tr( "The output was: " ) + r.getOutput() );
|
||||
}
|
||||
poolNameEntry[ "dsName" ] = dsName;
|
||||
}
|
||||
|
||||
poolNames.append( poolNameEntry );
|
||||
|
||||
// Export the zpool so it can be reimported at the correct location later
|
||||
auto r = system->runCommand( { "zpool", "export", poolName }, std::chrono::seconds( 10 ) );
|
||||
if ( r.getExitCode() != 0 )
|
||||
{
|
||||
cWarning() << "Failed to export pool" << m_poolName;
|
||||
}
|
||||
}
|
||||
|
||||
// Put the list of zpools into global storage
|
||||
if ( !poolNames.isEmpty() )
|
||||
{
|
||||
gs->insert( "zfsPoolInfo", poolNames );
|
||||
}
|
||||
|
||||
return Calamares::JobResult::ok();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ZfsJob::setConfigurationMap( const QVariantMap& map )
|
||||
{
|
||||
m_poolName = CalamaresUtils::getString( map, "poolName" );
|
||||
m_poolOptions = CalamaresUtils::getString( map, "poolOptions" );
|
||||
m_datasetOptions = CalamaresUtils::getString( map, "datasetOptions" );
|
||||
|
||||
m_datasets = CalamaresUtils::getList( map, "datasets" );
|
||||
}
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( ZfsJobFactory, registerPlugin< ZfsJob >(); )
|
89
src/modules/zfs/ZfsJob.h
Normal file
89
src/modules/zfs/ZfsJob.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021 Evan James <dalto@fastmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Calamares is Free Software: see the License-Identifier above.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZFSJOB_H
|
||||
#define ZFSJOB_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "CppJob.h"
|
||||
|
||||
#include "utils/PluginFactory.h"
|
||||
|
||||
#include "DllMacro.h"
|
||||
|
||||
struct ZfsResult
|
||||
{
|
||||
bool success;
|
||||
QString failureMessage; // This message is displayed to the user and should be translated at the time of population
|
||||
};
|
||||
|
||||
/** @brief Create zpools and zfs datasets
|
||||
*
|
||||
*/
|
||||
class PLUGINDLLEXPORT ZfsJob : public Calamares::CppJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ZfsJob( QObject* parent = nullptr );
|
||||
~ZfsJob() override;
|
||||
|
||||
QString prettyName() const override;
|
||||
|
||||
Calamares::JobResult exec() override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
|
||||
private:
|
||||
QString m_poolName;
|
||||
QString m_poolOptions;
|
||||
QString m_datasetOptions;
|
||||
QStringList m_mountpoints;
|
||||
|
||||
QList< QVariant > m_datasets;
|
||||
|
||||
/** @brief Creates a zpool based on the provided arguments
|
||||
*
|
||||
* @p deviceName is a full path to the device the zpool should be created on
|
||||
* @p poolName is a string containing the name of the pool to create
|
||||
* @p poolOptions are the options to pass to zpool create
|
||||
* @p encrypt is a boolean which determines if the pool should be encrypted
|
||||
* @p passphrase is a string continaing the passphrase
|
||||
*
|
||||
*/
|
||||
ZfsResult createZpool( QString deviceName,
|
||||
QString poolName,
|
||||
QString poolOptions,
|
||||
bool encrypt,
|
||||
QString passphrase = QString() ) const;
|
||||
|
||||
/** @brief Collects all the mountpoints from the partitions
|
||||
*
|
||||
* Iterates over @p partitions to gather each mountpoint present
|
||||
* in the list of maps and populates m_mountpoints
|
||||
*
|
||||
*/
|
||||
void collectMountpoints( const QVariantList& partitions );
|
||||
|
||||
/** @brief Check to see if a given mountpoint overlaps with one of the defined moutnpoints
|
||||
*
|
||||
* Iterates over m_partitions and checks if @p targetMountpoint overlaps with them by comparing
|
||||
* the beginning of targetMountpoint with all the values in m_mountpoints. Of course, / is excluded
|
||||
* since all the mountpoints would begin with /
|
||||
*
|
||||
*/
|
||||
bool isMountpointOverlapping( const QString& targetMountpoint ) const;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( ZfsJobFactory )
|
||||
|
||||
#endif // ZFSJOB_H
|
45
src/modules/zfs/zfs.conf
Normal file
45
src/modules/zfs/zfs.conf
Normal file
@ -0,0 +1,45 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# The zfs module creates the zfs pools and datasets
|
||||
#
|
||||
#
|
||||
#
|
||||
---
|
||||
# The name to be used for the zpool
|
||||
poolName: zpcala
|
||||
|
||||
# A list of options that will be passed to zpool create
|
||||
#
|
||||
# Encryption options should generally not be added here since they will be added by
|
||||
# selecting the encrypt disk option in the partition module
|
||||
poolOptions: "-f -o ashift=12 -O mountpoint=none -O acltype=posixacl -O relatime=on"
|
||||
|
||||
# A list of options that will be passed to zfs create when creating each dataset
|
||||
# Do not include "canmount" or "mountpoint" as those are set below in the datasets array
|
||||
datasetOptions: "-o compression=lz4 -o atime=off -o xattr=sa"
|
||||
|
||||
# An array of datasets that will be created on the zpool mounted at /
|
||||
#
|
||||
# This default configuration is commonly used when support for booting more than one distro
|
||||
# out of a single zpool is desired. If you decide to keep this default configuration,
|
||||
# you should replace "distro" with an identifier that represents your distro.
|
||||
datasets:
|
||||
- dsName: ROOT
|
||||
mountpoint: none
|
||||
canMount: off
|
||||
- dsName: ROOT/distro
|
||||
mountpoint: none
|
||||
canMount: off
|
||||
- dsName: ROOT/distro/root
|
||||
mountpoint: /
|
||||
canMount: noauto
|
||||
- dsName: ROOT/distro/home
|
||||
mountpoint: /home
|
||||
canMount: on
|
||||
- dsName: ROOT/distro/varcache
|
||||
mountpoint: /var/cache
|
||||
canMount: on
|
||||
- dsName: ROOT/distro/varlog
|
||||
mountpoint: /var/log
|
||||
canMount: on
|
24
src/modules/zfs/zfs.schema.yaml
Normal file
24
src/modules/zfs/zfs.schema.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
---
|
||||
$schema: https://json-schema.org/schema#
|
||||
$id: https://calamares.io/schemas/zfs
|
||||
additionalProperties: false
|
||||
type: object
|
||||
properties:
|
||||
poolName: { type: string }
|
||||
poolOptions: { type: string }
|
||||
datasetOptions: { type: string }
|
||||
datasets:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
properties:
|
||||
dsName: { type: string }
|
||||
mountpoint: { type: string }
|
||||
# Nominally a string, but "on" and "off" are valid and get
|
||||
# turned into a boolean in the YAML parser.
|
||||
canMount: { anyOf: [ { type: string }, { type: boolean } ] }
|
||||
required: [ dsName, mountpoint, canMount ]
|
||||
required: [ poolName, datasets ]
|
Loading…
Reference in New Issue
Block a user