Merge branch 'calamares' of https://github.com/calamares/calamares into development
This commit is contained in:
commit
fe2d8f3262
67
CHANGES-3.2
67
CHANGES-3.2
@ -7,7 +7,7 @@ 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
|
changelog -- this log starts with version 3.2.0. The release notes on the
|
||||||
website will have to do for older versions.
|
website will have to do for older versions.
|
||||||
|
|
||||||
# 3.2.46 (unreleased) #
|
# 3.2.48 (unreleased) #
|
||||||
|
|
||||||
This release contains contributions from (alphabetically by first name):
|
This release contains contributions from (alphabetically by first name):
|
||||||
- No external contributors yet
|
- No external contributors yet
|
||||||
@ -19,6 +19,71 @@ This release contains contributions from (alphabetically by first name):
|
|||||||
- No module changes yet
|
- No module changes yet
|
||||||
|
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
- Philip Müller
|
||||||
|
|
||||||
|
## Core ##
|
||||||
|
- A new core class `Runner` is now responsible for running commands
|
||||||
|
either in the host or in the target system. This is invisible for
|
||||||
|
end-users, but **does** expand the API available to consumers inside
|
||||||
|
Calamares modules. In particular, Python modules can now easily read
|
||||||
|
and respond to command output. #1740
|
||||||
|
|
||||||
|
## Modules ##
|
||||||
|
- *fstab* writes a slightly different message in `/etc/crypttab`
|
||||||
|
about the root filesystem. Since Calamares itself ignores the
|
||||||
|
(previous wording of) message, it was confusing. #1811
|
||||||
|
- *packages* module has some support for reporting progress while
|
||||||
|
the packages are installed. This depends on the package-manager itself
|
||||||
|
reporting useful progress information **and** the *packages* module having
|
||||||
|
support-code to interpret that progress. A proof-of-concept for `pacman`
|
||||||
|
has been implemented. #1582
|
||||||
|
- *partition* has a number of edge-cases for LVM and LUKS resolved. #1564 #1817
|
||||||
|
- *partition* module once again always offers `/boot` as a mount-point, even
|
||||||
|
when EFI would want `/boot/efi`. (Thanks Phil)
|
||||||
|
- *summary* had a regression and showed some descriptive texts twice.
|
||||||
|
|
||||||
|
|
||||||
# 3.2.45 (2021-10-31) #
|
# 3.2.45 (2021-10-31) #
|
||||||
|
|
||||||
This release contains contributions from (alphabetically by first name):
|
This release contains contributions from (alphabetically by first name):
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
# TODO:3.3: Require CMake 3.12
|
# TODO:3.3: Require CMake 3.12
|
||||||
cmake_minimum_required( VERSION 3.3 FATAL_ERROR )
|
cmake_minimum_required( VERSION 3.3 FATAL_ERROR )
|
||||||
project( CALAMARES
|
project( CALAMARES
|
||||||
VERSION 3.2.46
|
VERSION 3.2.48
|
||||||
LANGUAGES C CXX
|
LANGUAGES C CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -133,12 +133,12 @@ set( CALAMARES_DESCRIPTION_SUMMARY
|
|||||||
# `txstats.py -e`. See also
|
# `txstats.py -e`. See also
|
||||||
#
|
#
|
||||||
# Total 81 languages
|
# Total 81 languages
|
||||||
set( _tx_complete az az_AZ ca cs_CZ fi_FI he hi hr ja ko lt pt_BR
|
set( _tx_complete az az_AZ ca de fa fi_FI he hr ja ko lt pt_PT si
|
||||||
pt_PT sq sv tr_TR uk zh_CN zh_TW )
|
sq tr_TR uk zh_TW )
|
||||||
set( _tx_good as be ca@valencia da de fr fur it_IT ml nl ru sk tg
|
set( _tx_good as be ca@valencia cs_CZ da fr fur hi it_IT ml nl
|
||||||
vi )
|
pt_BR ru sk sv tg vi zh_CN )
|
||||||
set( _tx_ok ar ast bg bn el en_GB es es_MX et eu fa gl hu id is mr
|
set( _tx_ok ar ast bg bn el en_GB es es_MX et eu gl hu id is mr nb
|
||||||
nb pl ro si sl sr sr@latin th )
|
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
|
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 )
|
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
|
Comment[eu]=Calamares - sistema instalatzailea
|
||||||
Name[fa]=نصب سامانه
|
Name[fa]=نصب سامانه
|
||||||
Icon[fa]=کالامارس
|
Icon[fa]=کالامارس
|
||||||
GenericName[fa]=نصبکنندهٔ سامانه
|
GenericName[fa]=نصبکننده سامانه
|
||||||
Comment[fa]=کالامارس — نصبکنندهٔ سامانه
|
Comment[fa]=کالامارس — نصبکننده سامانه
|
||||||
Name[es_PR]=Instalar el sistema
|
Name[es_PR]=Instalar el sistema
|
||||||
Name[fr]=Installer le système
|
Name[fr]=Installer le système
|
||||||
Icon[fr]=calamares
|
Icon[fr]=calamares
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
* Double-check the *CALAMARES_VERSION* value at the top of `CMakeLists.txt`.
|
* Double-check the *CALAMARES_VERSION* value at the top of `CMakeLists.txt`.
|
||||||
* Set *CALAMARES_RELEASE_MODE* to `ON` in `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
|
* Commit both. This is usually done with commit-message
|
||||||
*Changes: pre-release housekeeping*.
|
*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*.
|
* Bump the version number in `CMakeLists.txt` in *CALAMARES_VERSION*.
|
||||||
* Set *CALAMARES_RELEASE_MODE* back to `OFF`.
|
* Set *CALAMARES_RELEASE_MODE* back to `OFF`.
|
||||||
* Add a placeholder entry for the next release in `CHANGES` with date
|
* Add a placeholder entry for the next release in `CHANGES-*` with date
|
||||||
text *not released yet*.
|
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
|
* Commit and push that, usually with the message
|
||||||
*Changes: post-release housekeeping*.
|
*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
|
# Related Material
|
||||||
|
|
||||||
> This section isn't directly related to any specific release,
|
> 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 relevant GitHub profile.
|
||||||
- Upload that public key to the Calamares site.
|
- 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 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
|
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
@ -689,27 +689,27 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir.</transl
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||||
<source>Successfully unmounted %1.</source>
|
<source>Successfully unmounted %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 uğurla ayrıldı.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||||
<source>Successfully disabled swap %1.</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||||
<source>Successfully cleared swap %1.</source>
|
<source>Successfully cleared swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 mübadilə bölməsi uğurla təmizləndi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||||
<source>Successfully closed mapper device %1.</source>
|
<source>Successfully closed mapper device %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Yerləşdirmə cihazı %1 uğurla bağlandı</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||||
<source>Successfully disabled volume group %1.</source>
|
<source>Successfully disabled volume group %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Tutum qrupu %1, uğurla söndürüldü</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||||
<source>Successfully unmounted %1.</source>
|
<source>Successfully unmounted %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 uğurla ayrıldı.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||||
<source>Successfully disabled swap %1.</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||||
<source>Successfully cleared swap %1.</source>
|
<source>Successfully cleared swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 mübadilə bölməsi uğurla təmizləndi</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||||
<source>Successfully closed mapper device %1.</source>
|
<source>Successfully closed mapper device %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Yerləşdirmə cihazı %1 uğurla bağlandı</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||||
<source>Successfully disabled volume group %1.</source>
|
<source>Successfully disabled volume group %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Tutum qrupu %1, uğurla söndürüldü</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||||
<source>Successfully unmounted %1.</source>
|
<source>Successfully unmounted %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>S'ha desmuntat correctament %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||||
<source>Successfully disabled swap %1.</source>
|
<source>Successfully disabled swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>S'ha inhabilitat correctament l'intercanvi %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||||
<source>Successfully cleared swap %1.</source>
|
<source>Successfully cleared swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>S'ha netejat correctament l'intercanvi %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||||
<source>Successfully closed mapper device %1.</source>
|
<source>Successfully closed mapper device %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>El dispositiu de mapatge %1 s'ha tancat correctament.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||||
<source>Successfully disabled volume group %1.</source>
|
<source>Successfully disabled volume group %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>El grup de volums %1 s'ha inhabilitat correctament.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||||
<source>Successfully unmounted %1.</source>
|
<source>Successfully unmounted %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Uspješno demontiran %1. </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||||
<source>Successfully disabled swap %1.</source>
|
<source>Successfully disabled swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Uspješno onemogućen swap %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||||
<source>Successfully cleared swap %1.</source>
|
<source>Successfully cleared swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Uspješno obrisan swap %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||||
<source>Successfully closed mapper device %1.</source>
|
<source>Successfully closed mapper device %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Uspješno zatvoren device mapper %1.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||||
<source>Successfully disabled volume group %1.</source>
|
<source>Successfully disabled volume group %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Volume grupa %1 je uspješno onemogućena.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||||
<source>Successfully unmounted %1.</source>
|
<source>Successfully unmounted %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1을(를) 성공적으로 마운트 해제했습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||||
<source>Successfully disabled swap %1.</source>
|
<source>Successfully disabled swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>스왑% 1을(를) 성공적으로 비활성화했습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||||
<source>Successfully cleared swap %1.</source>
|
<source>Successfully cleared swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>스왑 %1을(를) 성공적으로 지웠습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||||
<source>Successfully closed mapper device %1.</source>
|
<source>Successfully closed mapper device %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>매퍼 장치 %1을(를) 성공적으로 닫았습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||||
<source>Successfully disabled volume group %1.</source>
|
<source>Successfully disabled volume group %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>볼륨 그룹 %1을(를) 성공적으로 비활성화했습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||||
<source>Successfully unmounted %1.</source>
|
<source>Successfully unmounted %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>% 1 desmontado com sucesso.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||||
<source>Successfully disabled swap %1.</source>
|
<source>Successfully disabled swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Swap %1 desativada com sucesso.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||||
<source>Successfully cleared swap %1.</source>
|
<source>Successfully cleared swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Swap % 1 limpa com sucesso.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||||
<source>Successfully closed mapper device %1.</source>
|
<source>Successfully closed mapper device %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Dispositivo mapeador % 1 fechado com sucesso.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||||
<source>Successfully disabled volume group %1.</source>
|
<source>Successfully disabled volume group %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>Grupo de volume % 1 desativado com sucesso.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
<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>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
|
||||||
<source>Successfully unmounted %1.</source>
|
<source>Successfully unmounted %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 bağlantısı başarıyla kaldırıldı.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
|
||||||
<source>Successfully disabled swap %1.</source>
|
<source>Successfully disabled swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 takas alanı başarıyla devre dışı bırakıldı.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
|
||||||
<source>Successfully cleared swap %1.</source>
|
<source>Successfully cleared swap %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 takas alanı başarıyla temizlendi.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
|
||||||
<source>Successfully closed mapper device %1.</source>
|
<source>Successfully closed mapper device %1.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%1 eşleyici aygıtı başarıyla kapatıldı.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
|
||||||
<source>Successfully disabled volume group %1.</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
|
||||||
@ -862,17 +862,17 @@ Kurulum devam edebilir fakat bazı özellikler devre dışı kalabilir.</transla
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="251"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="251"/>
|
||||||
<source><h1>Welcome to %1 setup</h1></source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="255"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="255"/>
|
||||||
<source><h1>Welcome to the Calamares installer for %1</h1></source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/Config.cpp" line="256"/>
|
<location filename="../src/modules/welcome/Config.cpp" line="256"/>
|
||||||
<source><h1>Welcome to the %1 installer</h1></source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/users/Config.cpp" line="217"/>
|
<location filename="../src/modules/users/Config.cpp" line="217"/>
|
||||||
@ -3978,7 +3978,7 @@ Output:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/WelcomePage.cpp" line="217"/>
|
<location filename="../src/modules/welcome/WelcomePage.cpp" line="217"/>
|
||||||
<source><h1>Welcome to %1 setup.</h1></source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/WelcomePage.cpp" line="222"/>
|
<location filename="../src/modules/welcome/WelcomePage.cpp" line="222"/>
|
||||||
@ -4016,7 +4016,7 @@ Output:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcomeq/WelcomeQmlViewStep.cpp" line="40"/>
|
<location filename="../src/modules/welcomeq/WelcomeQmlViewStep.cpp" line="40"/>
|
||||||
<source>Welcome</source>
|
<source>Welcome</source>
|
||||||
<translation>Hoşgeldiniz</translation>
|
<translation>Hoş geldiniz</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -4024,7 +4024,7 @@ Output:
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/modules/welcome/WelcomeViewStep.cpp" line="46"/>
|
<location filename="../src/modules/welcome/WelcomeViewStep.cpp" line="46"/>
|
||||||
<source>Welcome</source>
|
<source>Welcome</source>
|
||||||
<translation>Hoşgeldiniz</translation>
|
<translation>Hoş geldiniz</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -323,7 +323,7 @@ msgstr "<code>{name!s}</code> systemd hədəfi aktiv edilmədi"
|
|||||||
|
|
||||||
#: src/modules/services-systemd/main.py:67
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
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
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Unsquashfs tapılmadı, squashfs-tools paketinin quraşdırıldığına əmin olun."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
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
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Unsquashfs tapılmadı, squashfs-tools paketinin quraşdırıldığına əmin olun."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"No es pot habilitar el temporitzador de systemd <code>{name!s}</code>."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:71
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"No s'ha pogut trobar unsquashfs, assegureu-vos que tingueu instal·lat el "
|
||||||
|
"paquet squashfs-tools."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
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
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Unsquashfs nicht gefunden, stellen Sie sicher, dass das Paket squashfs-tools"
|
||||||
|
" installiert ist."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
msgid "The destination \"{}\" in the target system is not a directory"
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
# Translators:
|
# Translators:
|
||||||
# Danial Behzadi <dani.behzi@ubuntu.com>, 2020
|
# Danial Behzadi <dani.behzi@ubuntu.com>, 2020
|
||||||
# alireza jamshidi <alirezajam98@gmail.com>, 2020
|
# alireza jamshidi <alirezajam98@gmail.com>, 2020
|
||||||
|
# Mahdy Mirzade <me@mahdym.ir>, 2021
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -14,7 +15,7 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-11-02 15:45+0100\n"
|
"POT-Creation-Date: 2021-11-02 15:45+0100\n"
|
||||||
"PO-Revision-Date: 2017-08-09 10:34+0000\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"
|
"Language-Team: Persian (https://www.transifex.com/calamares/teams/20061/fa/)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@ -62,13 +63,15 @@ msgstr "نصب بارکنندهٔ راهاندازی."
|
|||||||
|
|
||||||
#: src/modules/bootloader/main.py:508
|
#: src/modules/bootloader/main.py:508
|
||||||
msgid "Bootloader installation error"
|
msgid "Bootloader installation error"
|
||||||
msgstr ""
|
msgstr "خطای نصب بوت لودر"
|
||||||
|
|
||||||
#: src/modules/bootloader/main.py:509
|
#: src/modules/bootloader/main.py:509
|
||||||
msgid ""
|
msgid ""
|
||||||
"The bootloader could not be installed. The installation command "
|
"The bootloader could not be installed. The installation command "
|
||||||
"<pre>{!s}</pre> returned error code {!s}."
|
"<pre>{!s}</pre> returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"بوت لودر نتوانست نصب شود. دستور <pre>{!s}</pre> برای نصب با خطای {!s} مواجه "
|
||||||
|
"شد."
|
||||||
|
|
||||||
#: src/modules/fstab/main.py:29
|
#: src/modules/fstab/main.py:29
|
||||||
msgid "Writing fstab."
|
msgid "Writing fstab."
|
||||||
@ -77,6 +80,7 @@ msgstr "در حال نوشتن fstab."
|
|||||||
#: src/modules/fstab/main.py:389
|
#: src/modules/fstab/main.py:389
|
||||||
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"هیچ تنظیمات <pre>{!s}</pre> برای استفاده برای <pre>{!s}</pre> داده نشده است."
|
||||||
|
|
||||||
#: src/modules/dracut/main.py:27
|
#: src/modules/dracut/main.py:27
|
||||||
msgid "Creating initramfs with dracut."
|
msgid "Creating initramfs with dracut."
|
||||||
@ -139,6 +143,8 @@ msgid ""
|
|||||||
"The displaymanagers list is empty or undefined in both globalstorage and "
|
"The displaymanagers list is empty or undefined in both globalstorage and "
|
||||||
"displaymanager.conf."
|
"displaymanager.conf."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"فهرست مدیریت صفحه نمایش ها خالی بوده یا در محل ذخیره داده و "
|
||||||
|
"displaymanager.conf تعریف نشده است."
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:989
|
#: src/modules/displaymanager/main.py:989
|
||||||
msgid "Display manager configuration was incomplete"
|
msgid "Display manager configuration was incomplete"
|
||||||
@ -161,6 +167,8 @@ msgid ""
|
|||||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||||
"level {level!s}."
|
"level {level!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"دستور سرویس <code>{arg!s}</code> برای سرویس {name!s} در سطح اجرای {level!s}"
|
||||||
|
" ناشناخته است."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:93
|
#: src/modules/services-openrc/main.py:93
|
||||||
#: src/modules/services-systemd/main.py:59
|
#: src/modules/services-systemd/main.py:59
|
||||||
@ -171,6 +179,8 @@ msgstr "نمیتوان خدمت را دستکاری کرد"
|
|||||||
msgid ""
|
msgid ""
|
||||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"فراخوانی <code>rc-update {arg!s}</code> در chroot کد خطای {num!s} را "
|
||||||
|
"برگرداند."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:101
|
#: src/modules/services-openrc/main.py:101
|
||||||
msgid "Target runlevel does not exist"
|
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 "
|
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||||
"exist."
|
"exist."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"مسیر برای سطح اجرای {level!s} برابر <code>{path!s}</code> است، که وجود "
|
||||||
|
"ندارد."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:110
|
#: src/modules/services-openrc/main.py:110
|
||||||
msgid "Target service does not exist"
|
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 "
|
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
||||||
"exist."
|
"exist."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"مسیر برای سرویس {name!s} برابر <code>{path!s}</code> است، که وجود ندارد."
|
||||||
|
|
||||||
#: src/modules/networkcfg/main.py:29
|
#: src/modules/networkcfg/main.py:29
|
||||||
msgid "Saving network configuration."
|
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:638 src/modules/packages/main.py:650
|
||||||
#: src/modules/packages/main.py:678
|
#: src/modules/packages/main.py:678
|
||||||
msgid "Package Manager error"
|
msgid "Package Manager error"
|
||||||
msgstr ""
|
msgstr "خطای مدیر بسته"
|
||||||
|
|
||||||
#: src/modules/packages/main.py:639
|
#: src/modules/packages/main.py:639
|
||||||
msgid ""
|
msgid ""
|
||||||
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
|
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
|
||||||
"returned error code {!s}."
|
"returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"مدیر بسته نتوانست برای بروزرسانی ها آماده شود، دستور <pre>{!s}</pre> با خطای"
|
||||||
|
" {!s} مواجه شد."
|
||||||
|
|
||||||
#: src/modules/packages/main.py:651
|
#: src/modules/packages/main.py:651
|
||||||
msgid ""
|
msgid ""
|
||||||
"The package manager could not update the system. The command <pre>{!s}</pre>"
|
"The package manager could not update the system. The command <pre>{!s}</pre>"
|
||||||
" returned error code {!s}."
|
" returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"مدیر بسته نتوانست سامانه را بروز کند. دستور <pre>{!s}</pre> با خطای {!s} "
|
||||||
|
"مواجه شد."
|
||||||
|
|
||||||
#: src/modules/packages/main.py:679
|
#: src/modules/packages/main.py:679
|
||||||
msgid ""
|
msgid ""
|
||||||
"The package manager could not make changes to the installed system. The "
|
"The package manager could not make changes to the installed system. The "
|
||||||
"command <pre>{!s}</pre> returned error code {!s}."
|
"command <pre>{!s}</pre> returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"مدیر بسته نتوانست تغییرات را برای نصب سامانه انجام دهد. دستور "
|
||||||
|
"<pre>{!s}</pre> با خطای {!s} مواجه شد."
|
||||||
|
|
||||||
#: src/modules/plymouthcfg/main.py:27
|
#: src/modules/plymouthcfg/main.py:27
|
||||||
msgid "Configure Plymouth theme"
|
msgid "Configure Plymouth theme"
|
||||||
@ -306,7 +325,7 @@ msgstr "نمیتوان هدف سیستمدی <code>{name!s}</code> را ب
|
|||||||
|
|
||||||
#: src/modules/services-systemd/main.py:67
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr "نمیتوان تایمر سیستمدی <code>{name!s}</code> را به کار انداخت."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:71
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||||
@ -326,11 +345,11 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/modules/mkinitfs/main.py:27
|
#: src/modules/mkinitfs/main.py:27
|
||||||
msgid "Creating initramfs with mkinitfs."
|
msgid "Creating initramfs with mkinitfs."
|
||||||
msgstr ""
|
msgstr "درحال ایجاد initramfs با mkinitfs."
|
||||||
|
|
||||||
#: src/modules/mkinitfs/main.py:49
|
#: src/modules/mkinitfs/main.py:49
|
||||||
msgid "Failed to run mkinitfs on the target"
|
msgid "Failed to run mkinitfs on the target"
|
||||||
msgstr ""
|
msgstr "شکست در اجرا mkinitfs روی هدف"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:34
|
#: src/modules/unpackfs/main.py:34
|
||||||
msgid "Filling up filesystems."
|
msgid "Filling up filesystems."
|
||||||
@ -385,7 +404,7 @@ msgstr "سامانهٔ پروندهٔ مبدأ {} وجود ندارد"
|
|||||||
msgid ""
|
msgid ""
|
||||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr "شکست در یافتن unsquashfs. مطمئن شوید بسته squashfs-tools نصب است."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
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
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Neuspješno pronalaženje unsquashfs, provjerite imate li instaliran paket "
|
||||||
|
"squashfs-tools."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr "시스템 타이머 <code>{name!s}</code>를 활성화할 수 없습니다."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:71
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||||
@ -386,7 +386,7 @@ msgstr "\"{}\" 소스 파일시스템은 존재하지 않습니다."
|
|||||||
msgid ""
|
msgid ""
|
||||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr "unsquashfs를 찾지 못했습니다. squashfs-tools 패키지가 설치되어 있는지 확인하십시오."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
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
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Falha ao localizar o unsquashfs, certifique-se de que tem o pacote squashfs-"
|
||||||
|
"tools instalado."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
msgid "The destination \"{}\" in the target system is not a directory"
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#
|
#
|
||||||
# Translators:
|
# Translators:
|
||||||
# හෙළබස, 2021
|
# හෙළබස, 2021
|
||||||
|
# Sandaruwan Samaraweera, 2021
|
||||||
#
|
#
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
@ -13,7 +14,7 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-11-02 15:45+0100\n"
|
"POT-Creation-Date: 2021-11-02 15:45+0100\n"
|
||||||
"PO-Revision-Date: 2017-08-09 10:34+0000\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"
|
"Language-Team: Sinhala (https://www.transifex.com/calamares/teams/20061/si/)\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
@ -23,7 +24,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: src/modules/initramfscfg/main.py:32
|
#: src/modules/initramfscfg/main.py:32
|
||||||
msgid "Configuring initramfs."
|
msgid "Configuring initramfs."
|
||||||
msgstr ""
|
msgstr "initramfs වින්යාස කිරීම."
|
||||||
|
|
||||||
#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89
|
#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89
|
||||||
#: src/modules/fstab/main.py:355 src/modules/fstab/main.py:361
|
#: src/modules/fstab/main.py:355 src/modules/fstab/main.py:361
|
||||||
@ -35,161 +36,169 @@ msgstr ""
|
|||||||
#: src/modules/luksopenswaphookcfg/main.py:86
|
#: src/modules/luksopenswaphookcfg/main.py:86
|
||||||
#: src/modules/luksopenswaphookcfg/main.py:90
|
#: src/modules/luksopenswaphookcfg/main.py:90
|
||||||
msgid "Configuration Error"
|
msgid "Configuration Error"
|
||||||
msgstr ""
|
msgstr "වින්යාස දෝෂය"
|
||||||
|
|
||||||
#: src/modules/initramfscfg/main.py:86 src/modules/fstab/main.py:356
|
#: 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/initcpiocfg/main.py:228 src/modules/mount/main.py:145
|
||||||
#: src/modules/rawfs/main.py:165 src/modules/openrcdmcryptcfg/main.py:73
|
#: src/modules/rawfs/main.py:165 src/modules/openrcdmcryptcfg/main.py:73
|
||||||
#: src/modules/luksopenswaphookcfg/main.py:87
|
#: src/modules/luksopenswaphookcfg/main.py:87
|
||||||
msgid "No partitions are defined for <pre>{!s}</pre> to use."
|
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/initramfscfg/main.py:90 src/modules/fstab/main.py:362
|
||||||
#: src/modules/networkcfg/main.py:106 src/modules/initcpiocfg/main.py:232
|
#: 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/localecfg/main.py:136 src/modules/openrcdmcryptcfg/main.py:77
|
||||||
#: src/modules/luksopenswaphookcfg/main.py:91
|
#: src/modules/luksopenswaphookcfg/main.py:91
|
||||||
msgid "No root mount point is given for <pre>{!s}</pre> to use."
|
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
|
#: src/modules/grubcfg/main.py:28
|
||||||
msgid "Configure GRUB."
|
msgid "Configure GRUB."
|
||||||
msgstr ""
|
msgstr "GRUB වින්යාස කරන්න."
|
||||||
|
|
||||||
#: src/modules/bootloader/main.py:43
|
#: src/modules/bootloader/main.py:43
|
||||||
msgid "Install bootloader."
|
msgid "Install bootloader."
|
||||||
msgstr ""
|
msgstr "bootloader ස්ථාපනය කරන්න."
|
||||||
|
|
||||||
#: src/modules/bootloader/main.py:508
|
#: src/modules/bootloader/main.py:508
|
||||||
msgid "Bootloader installation error"
|
msgid "Bootloader installation error"
|
||||||
msgstr ""
|
msgstr "Bootloader ස්ථාපනය කිරීමේ දෝෂයකි"
|
||||||
|
|
||||||
#: src/modules/bootloader/main.py:509
|
#: src/modules/bootloader/main.py:509
|
||||||
msgid ""
|
msgid ""
|
||||||
"The bootloader could not be installed. The installation command "
|
"The bootloader could not be installed. The installation command "
|
||||||
"<pre>{!s}</pre> returned error code {!s}."
|
"<pre>{!s}</pre> returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"ඇරඹුම් කාරකය ස්ථාපනය කල නොහැක. ස්ථාපන විධානය <pre>{!s}</pre> දෝෂ කේතය {!s} "
|
||||||
|
"ලබා දුන්නේය."
|
||||||
|
|
||||||
#: src/modules/fstab/main.py:29
|
#: src/modules/fstab/main.py:29
|
||||||
msgid "Writing fstab."
|
msgid "Writing fstab."
|
||||||
msgstr ""
|
msgstr "fstab ලියමින්."
|
||||||
|
|
||||||
#: src/modules/fstab/main.py:389
|
#: src/modules/fstab/main.py:389
|
||||||
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
msgid "No <pre>{!s}</pre> configuration is given for <pre>{!s}</pre> to use."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"භාවිතා කිරීමට <pre>{!s}</pre> සඳහා <pre>{!s}</pre> වින්යාසයක් ලබා දී නොමැත."
|
||||||
|
|
||||||
#: src/modules/dracut/main.py:27
|
#: src/modules/dracut/main.py:27
|
||||||
msgid "Creating initramfs with dracut."
|
msgid "Creating initramfs with dracut."
|
||||||
msgstr ""
|
msgstr "dracut සමඟ initramfs නිර්මාණය කිරීම."
|
||||||
|
|
||||||
#: src/modules/dracut/main.py:49
|
#: src/modules/dracut/main.py:49
|
||||||
msgid "Failed to run dracut on the target"
|
msgid "Failed to run dracut on the target"
|
||||||
msgstr ""
|
msgstr "ඉලක්කය මත ඩ්රැකට් ධාවනය කිරීමට අපොහොසත් විය"
|
||||||
|
|
||||||
#: src/modules/dracut/main.py:50 src/modules/mkinitfs/main.py:50
|
#: src/modules/dracut/main.py:50 src/modules/mkinitfs/main.py:50
|
||||||
msgid "The exit code was {}"
|
msgid "The exit code was {}"
|
||||||
msgstr ""
|
msgstr "පිටවීමේ කේතය වූයේ {}"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:526
|
#: src/modules/displaymanager/main.py:526
|
||||||
msgid "Cannot write KDM configuration file"
|
msgid "Cannot write KDM configuration file"
|
||||||
msgstr ""
|
msgstr "KDM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:527
|
#: src/modules/displaymanager/main.py:527
|
||||||
msgid "KDM config file {!s} does not exist"
|
msgid "KDM config file {!s} does not exist"
|
||||||
msgstr ""
|
msgstr "KDM වින්යාස ගොනුව {!s} නොපවතී"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:588
|
#: src/modules/displaymanager/main.py:588
|
||||||
msgid "Cannot write LXDM configuration file"
|
msgid "Cannot write LXDM configuration file"
|
||||||
msgstr ""
|
msgstr "LXDM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:589
|
#: src/modules/displaymanager/main.py:589
|
||||||
msgid "LXDM config file {!s} does not exist"
|
msgid "LXDM config file {!s} does not exist"
|
||||||
msgstr ""
|
msgstr "LXDM වින්යාස ගොනුව {!s} නොපවතී"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:672
|
#: src/modules/displaymanager/main.py:672
|
||||||
msgid "Cannot write LightDM configuration file"
|
msgid "Cannot write LightDM configuration file"
|
||||||
msgstr ""
|
msgstr "LightDM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:673
|
#: src/modules/displaymanager/main.py:673
|
||||||
msgid "LightDM config file {!s} does not exist"
|
msgid "LightDM config file {!s} does not exist"
|
||||||
msgstr ""
|
msgstr "LightDM වින්යාස ගොනුව {!s} නොපවතී"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:747
|
#: src/modules/displaymanager/main.py:747
|
||||||
msgid "Cannot configure LightDM"
|
msgid "Cannot configure LightDM"
|
||||||
msgstr ""
|
msgstr "LightDM වින්යාස කළ නොහැක"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:748
|
#: src/modules/displaymanager/main.py:748
|
||||||
msgid "No LightDM greeter installed."
|
msgid "No LightDM greeter installed."
|
||||||
msgstr ""
|
msgstr "LightDM ග්රීටර් ස්ථාපනය කර නැත."
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:779
|
#: src/modules/displaymanager/main.py:779
|
||||||
msgid "Cannot write SLIM configuration file"
|
msgid "Cannot write SLIM configuration file"
|
||||||
msgstr ""
|
msgstr "SLIM වින්යාස ගොනුව ලිවිය නොහැක"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:780
|
#: src/modules/displaymanager/main.py:780
|
||||||
msgid "SLIM config file {!s} does not exist"
|
msgid "SLIM config file {!s} does not exist"
|
||||||
msgstr ""
|
msgstr "SLIM වින්යාස ගොනුව {!s} නොපවතී"
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:906
|
#: src/modules/displaymanager/main.py:906
|
||||||
msgid "No display managers selected for the displaymanager module."
|
msgid "No display managers selected for the displaymanager module."
|
||||||
msgstr ""
|
msgstr "සංදර්ශක කළමනාකරු මොඩියුලය සඳහා සංදර්ශක කළමනාකරුවන් තෝරාගෙන නොමැත."
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:907
|
#: src/modules/displaymanager/main.py:907
|
||||||
msgid ""
|
msgid ""
|
||||||
"The displaymanagers list is empty or undefined in both globalstorage and "
|
"The displaymanagers list is empty or undefined in both globalstorage and "
|
||||||
"displaymanager.conf."
|
"displaymanager.conf."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"ගෝලීය ගබඩාව සහ displaymanager.conf යන දෙකෙහිම සංදර්ශක කළමනාකරු ලැයිස්තුව "
|
||||||
|
"හිස් හෝ අර්ථ දක්වා නොමැත."
|
||||||
|
|
||||||
#: src/modules/displaymanager/main.py:989
|
#: src/modules/displaymanager/main.py:989
|
||||||
msgid "Display manager configuration was incomplete"
|
msgid "Display manager configuration was incomplete"
|
||||||
msgstr ""
|
msgstr "සංදර්ශක කළමනාකරු වින්යාසය අසම්පූර්ණ විය"
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:29
|
#: src/modules/services-openrc/main.py:29
|
||||||
msgid "Configure OpenRC services"
|
msgid "Configure OpenRC services"
|
||||||
msgstr ""
|
msgstr "OpenRC සේවා වින්යාස කරන්න"
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:57
|
#: src/modules/services-openrc/main.py:57
|
||||||
msgid "Cannot add service {name!s} to run-level {level!s}."
|
msgid "Cannot add service {name!s} to run-level {level!s}."
|
||||||
msgstr ""
|
msgstr "ධාවන මට්ටම {level!s} වෙත සේවාව {name!s} එක් කළ නොහැක."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:59
|
#: src/modules/services-openrc/main.py:59
|
||||||
msgid "Cannot remove service {name!s} from run-level {level!s}."
|
msgid "Cannot remove service {name!s} from run-level {level!s}."
|
||||||
msgstr ""
|
msgstr "ධාවන මට්ටමේ {level!s} වෙතින් සේවාව {name!s} ඉවත් කළ නොහැක."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:61
|
#: src/modules/services-openrc/main.py:61
|
||||||
msgid ""
|
msgid ""
|
||||||
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
"Unknown service-action <code>{arg!s}</code> for service {name!s} in run-"
|
||||||
"level {level!s}."
|
"level {level!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"{name!s} සේවාව සඳහා නොදන්නා සේවා-ක්රියාව <code>{arg!s}</code> ධාවන මට්ටමේ "
|
||||||
|
"{level!s}."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:93
|
#: src/modules/services-openrc/main.py:93
|
||||||
#: src/modules/services-systemd/main.py:59
|
#: src/modules/services-systemd/main.py:59
|
||||||
msgid "Cannot modify service"
|
msgid "Cannot modify service"
|
||||||
msgstr ""
|
msgstr "සේවාව වෙනස් කළ නොහැක"
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:94
|
#: src/modules/services-openrc/main.py:94
|
||||||
msgid ""
|
msgid ""
|
||||||
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
"<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"<code>rc-update {arg!s}</code> chroot හි ඇමතුම {num!s} දෝෂ කේතය ලබා දුන්නේය."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:101
|
#: src/modules/services-openrc/main.py:101
|
||||||
msgid "Target runlevel does not exist"
|
msgid "Target runlevel does not exist"
|
||||||
msgstr ""
|
msgstr "ඉලක්ක ධාවන මට්ටම නොපවතී"
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:102
|
#: src/modules/services-openrc/main.py:102
|
||||||
msgid ""
|
msgid ""
|
||||||
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
"The path for runlevel {level!s} is <code>{path!s}</code>, which does not "
|
||||||
"exist."
|
"exist."
|
||||||
msgstr ""
|
msgstr "ධාවන මට්ටම {level!s} සඳහා මාර්ගය <code>{path!s}</code>, එය නොපවතී."
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:110
|
#: src/modules/services-openrc/main.py:110
|
||||||
msgid "Target service does not exist"
|
msgid "Target service does not exist"
|
||||||
msgstr ""
|
msgstr "ඉලක්ක සේවාව නොපවතී"
|
||||||
|
|
||||||
#: src/modules/services-openrc/main.py:111
|
#: src/modules/services-openrc/main.py:111
|
||||||
msgid ""
|
msgid ""
|
||||||
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
"The path for service {name!s} is <code>{path!s}</code>, which does not "
|
||||||
"exist."
|
"exist."
|
||||||
msgstr ""
|
msgstr "සේවාව සඳහා {name!s} මාර්ගය <code>{path!s}</code>, එය නොපවතී."
|
||||||
|
|
||||||
#: src/modules/networkcfg/main.py:29
|
#: src/modules/networkcfg/main.py:29
|
||||||
msgid "Saving network configuration."
|
msgid "Saving network configuration."
|
||||||
@ -203,7 +212,7 @@ msgstr "ඇසුරුම් ස්ථාපනය කරන්න."
|
|||||||
#: src/modules/packages/main.py:57
|
#: src/modules/packages/main.py:57
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Processing packages (%(count)d / %(total)d)"
|
msgid "Processing packages (%(count)d / %(total)d)"
|
||||||
msgstr ""
|
msgstr "පැකේජ සැකසීම (%(count)d / %(total)d)"
|
||||||
|
|
||||||
#: src/modules/packages/main.py:62
|
#: src/modules/packages/main.py:62
|
||||||
#, python-format
|
#, 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:638 src/modules/packages/main.py:650
|
||||||
#: src/modules/packages/main.py:678
|
#: src/modules/packages/main.py:678
|
||||||
msgid "Package Manager error"
|
msgid "Package Manager error"
|
||||||
msgstr ""
|
msgstr "පැකේජ කළමනාකරු දෝෂයකි"
|
||||||
|
|
||||||
#: src/modules/packages/main.py:639
|
#: src/modules/packages/main.py:639
|
||||||
msgid ""
|
msgid ""
|
||||||
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
|
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
|
||||||
"returned error code {!s}."
|
"returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"පැකේජ කළමනාකරුට යාවත්කාලීන සකස් කිරීමට නොහැකි විය. විධානය <pre>{!s}</pre> "
|
||||||
|
"දෝෂ කේතය {!s} ලබා දුන්නේය."
|
||||||
|
|
||||||
#: src/modules/packages/main.py:651
|
#: src/modules/packages/main.py:651
|
||||||
msgid ""
|
msgid ""
|
||||||
"The package manager could not update the system. The command <pre>{!s}</pre>"
|
"The package manager could not update the system. The command <pre>{!s}</pre>"
|
||||||
" returned error code {!s}."
|
" returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"පැකේජ කළමනාකරුට පද්ධතිය යාවත්කාලීන කළ නොහැකි විය. විධානය <pre>{!s}</pre> දෝෂ"
|
||||||
|
" කේතය {!s} ලබා දුන්නේය."
|
||||||
|
|
||||||
#: src/modules/packages/main.py:679
|
#: src/modules/packages/main.py:679
|
||||||
msgid ""
|
msgid ""
|
||||||
"The package manager could not make changes to the installed system. The "
|
"The package manager could not make changes to the installed system. The "
|
||||||
"command <pre>{!s}</pre> returned error code {!s}."
|
"command <pre>{!s}</pre> returned error code {!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"පැකේජ කළමනාකරුට ස්ථාපිත පද්ධතියට වෙනස්කම් සිදු කළ නොහැක. විධානය "
|
||||||
|
"<pre>{!s}</pre> දෝෂ කේතය {!s} ලබා දුන්නේය."
|
||||||
|
|
||||||
#: src/modules/plymouthcfg/main.py:27
|
#: src/modules/plymouthcfg/main.py:27
|
||||||
msgid "Configure Plymouth theme"
|
msgid "Configure Plymouth theme"
|
||||||
msgstr ""
|
msgstr "Plymouth තේමාව වින්යාස කරන්න"
|
||||||
|
|
||||||
#: src/modules/initcpiocfg/main.py:28
|
#: src/modules/initcpiocfg/main.py:28
|
||||||
msgid "Configuring mkinitcpio."
|
msgid "Configuring mkinitcpio."
|
||||||
msgstr ""
|
msgstr "mkinitcpio වින්යාස කරමින්."
|
||||||
|
|
||||||
#: src/modules/localecfg/main.py:30
|
#: src/modules/localecfg/main.py:30
|
||||||
msgid "Configuring locales."
|
msgid "Configuring locales."
|
||||||
msgstr ""
|
msgstr "ස්ථාන වින්යාස කිරීම."
|
||||||
|
|
||||||
#: src/modules/mount/main.py:30
|
#: src/modules/mount/main.py:30
|
||||||
msgid "Mounting partitions."
|
msgid "Mounting partitions."
|
||||||
msgstr ""
|
msgstr "කොටස් සවි කිරීම."
|
||||||
|
|
||||||
#: src/modules/rawfs/main.py:26
|
#: src/modules/rawfs/main.py:26
|
||||||
msgid "Installing data."
|
msgid "Installing data."
|
||||||
@ -264,12 +279,12 @@ msgstr "දත්ත ස්ථාපනය වෙමින්."
|
|||||||
|
|
||||||
#: src/modules/dummypython/main.py:35
|
#: src/modules/dummypython/main.py:35
|
||||||
msgid "Dummy python job."
|
msgid "Dummy python job."
|
||||||
msgstr ""
|
msgstr "ඩමි python වැඩසටහන."
|
||||||
|
|
||||||
#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
|
#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
|
||||||
#: src/modules/dummypython/main.py:94
|
#: src/modules/dummypython/main.py:94
|
||||||
msgid "Dummy python step {}"
|
msgid "Dummy python step {}"
|
||||||
msgstr ""
|
msgstr "ව්යාජ python පියවර {}"
|
||||||
|
|
||||||
#: src/modules/hwclock/main.py:26
|
#: src/modules/hwclock/main.py:26
|
||||||
msgid "Setting hardware clock."
|
msgid "Setting hardware clock."
|
||||||
@ -277,115 +292,120 @@ msgstr "දෘඩාංග ඔරලෝසුව සැකසෙමින්."
|
|||||||
|
|
||||||
#: src/modules/umount/main.py:31
|
#: src/modules/umount/main.py:31
|
||||||
msgid "Unmount file systems."
|
msgid "Unmount file systems."
|
||||||
msgstr ""
|
msgstr "ගොනු පද්ධති ඉවත් කරන්න."
|
||||||
|
|
||||||
#: src/modules/openrcdmcryptcfg/main.py:26
|
#: src/modules/openrcdmcryptcfg/main.py:26
|
||||||
msgid "Configuring OpenRC dmcrypt service."
|
msgid "Configuring OpenRC dmcrypt service."
|
||||||
msgstr ""
|
msgstr "OpenRC dmcrypt සේවාව වින්යාස කරමින්."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:26
|
#: src/modules/services-systemd/main.py:26
|
||||||
msgid "Configure systemd services"
|
msgid "Configure systemd services"
|
||||||
msgstr ""
|
msgstr "systemd සේවා වින්යාස කරන්න"
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:60
|
#: src/modules/services-systemd/main.py:60
|
||||||
msgid ""
|
msgid ""
|
||||||
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
|
"<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"<code>systemctl {arg!s}</code> chroot වෙත ඇමතුමක් ලබා දුන් දෝෂ කේතය {num!s}."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:63
|
#: src/modules/services-systemd/main.py:63
|
||||||
#: src/modules/services-systemd/main.py:69
|
#: src/modules/services-systemd/main.py:69
|
||||||
msgid "Cannot enable systemd service <code>{name!s}</code>."
|
msgid "Cannot enable systemd service <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr "systemd සේවාව <code>{name!s}</code> සබල කළ නොහැක."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:65
|
#: src/modules/services-systemd/main.py:65
|
||||||
msgid "Cannot enable systemd target <code>{name!s}</code>."
|
msgid "Cannot enable systemd target <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr "systemd ඉලක්කය <code>{name!s}</code> සබල කළ නොහැක."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:67
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr "systemd ටයිමරය <code>{name!s}</code> සබල කළ නොහැක."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:71
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr "systemd ඉලක්කය <code>{name!s}</code> අක්රිය කළ නොහැක."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:73
|
#: src/modules/services-systemd/main.py:73
|
||||||
msgid "Cannot mask systemd unit <code>{name!s}</code>."
|
msgid "Cannot mask systemd unit <code>{name!s}</code>."
|
||||||
msgstr ""
|
msgstr "systemd ඒකකය <code>{name!s}</code> වසන් කළ නොහැක."
|
||||||
|
|
||||||
#: src/modules/services-systemd/main.py:75
|
#: src/modules/services-systemd/main.py:75
|
||||||
msgid ""
|
msgid ""
|
||||||
"Unknown systemd commands <code>{command!s}</code> and "
|
"Unknown systemd commands <code>{command!s}</code> and "
|
||||||
"<code>{suffix!s}</code> for unit {name!s}."
|
"<code>{suffix!s}</code> for unit {name!s}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"{name!s} ඒකකය සඳහා නොදන්නා systemd විධාන <code>{command!s}</code> සහ "
|
||||||
|
"<code>{suffix!s}</code>."
|
||||||
|
|
||||||
#: src/modules/mkinitfs/main.py:27
|
#: src/modules/mkinitfs/main.py:27
|
||||||
msgid "Creating initramfs with mkinitfs."
|
msgid "Creating initramfs with mkinitfs."
|
||||||
msgstr ""
|
msgstr "mkinitfs සමඟ initramfs නිර්මාණය කිරීම."
|
||||||
|
|
||||||
#: src/modules/mkinitfs/main.py:49
|
#: src/modules/mkinitfs/main.py:49
|
||||||
msgid "Failed to run mkinitfs on the target"
|
msgid "Failed to run mkinitfs on the target"
|
||||||
msgstr ""
|
msgstr "ඉලක්කය මත mkinitfs ධාවනය කිරීමට අසමත් විය"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:34
|
#: src/modules/unpackfs/main.py:34
|
||||||
msgid "Filling up filesystems."
|
msgid "Filling up filesystems."
|
||||||
msgstr ""
|
msgstr "ගොනු පද්ධති පිරවීම."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:254
|
#: src/modules/unpackfs/main.py:254
|
||||||
msgid "rsync failed with error code {}."
|
msgid "rsync failed with error code {}."
|
||||||
msgstr ""
|
msgstr "දෝෂ කේතය {} සමඟ rsync අසාර්ථක විය."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:299
|
#: src/modules/unpackfs/main.py:299
|
||||||
msgid "Unpacking image {}/{}, file {}/{}"
|
msgid "Unpacking image {}/{}, file {}/{}"
|
||||||
msgstr ""
|
msgstr "රූපය {}/{}, ගොනුව {}/{} අසුරමින්"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:314
|
#: src/modules/unpackfs/main.py:314
|
||||||
msgid "Starting to unpack {}"
|
msgid "Starting to unpack {}"
|
||||||
msgstr ""
|
msgstr "ඉවත් කිරීමට පටන් ගනියි {}"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:465
|
#: src/modules/unpackfs/main.py:323 src/modules/unpackfs/main.py:465
|
||||||
msgid "Failed to unpack image \"{}\""
|
msgid "Failed to unpack image \"{}\""
|
||||||
msgstr ""
|
msgstr "\"{}\" රූපය ඉවත් කිරීමට අසමත් විය"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:430
|
#: src/modules/unpackfs/main.py:430
|
||||||
msgid "No mount point for root partition"
|
msgid "No mount point for root partition"
|
||||||
msgstr ""
|
msgstr "root කොටස සඳහා සවි කිරීමේ ලක්ෂ්යයක් නොමැත"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:431
|
#: src/modules/unpackfs/main.py:431
|
||||||
msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing"
|
msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing"
|
||||||
msgstr ""
|
msgstr "ගෝලීය ගබඩාවේ \"rootMountPoint\" යතුරක් අඩංගු නොවේ, කිසිවක් නොකරයි"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:436
|
#: src/modules/unpackfs/main.py:436
|
||||||
msgid "Bad mount point for root partition"
|
msgid "Bad mount point for root partition"
|
||||||
msgstr ""
|
msgstr "මූල කොටස සඳහා නරක සවි කිරීමේ ලක්ෂ්යය"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:437
|
#: src/modules/unpackfs/main.py:437
|
||||||
msgid "rootMountPoint is \"{}\", which does not exist, doing nothing"
|
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:453 src/modules/unpackfs/main.py:457
|
||||||
#: src/modules/unpackfs/main.py:463 src/modules/unpackfs/main.py:478
|
#: src/modules/unpackfs/main.py:463 src/modules/unpackfs/main.py:478
|
||||||
msgid "Bad unsquash configuration"
|
msgid "Bad unsquash configuration"
|
||||||
msgstr ""
|
msgstr "නරක unsquash වින්යාසය"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:454
|
#: src/modules/unpackfs/main.py:454
|
||||||
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel"
|
||||||
msgstr ""
|
msgstr "\"{}\" ({}) සඳහා ගොනු පද්ධතිය ඔබගේ වත්මන් කර්නලයෙන් සහය නොදක්වයි"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:458
|
#: src/modules/unpackfs/main.py:458
|
||||||
msgid "The source filesystem \"{}\" does not exist"
|
msgid "The source filesystem \"{}\" does not exist"
|
||||||
msgstr ""
|
msgstr "මූලාශ්ර ගොනු පද්ධතිය \"{}\" නොපවතී"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:464
|
#: src/modules/unpackfs/main.py:464
|
||||||
msgid ""
|
msgid ""
|
||||||
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Unsquashfs සොයා ගැනීමට අපොහොසත් විය, ඔබ squashfs-tools පැකේජය ස්ථාපනය කර ඇති"
|
||||||
|
" බවට වග බලා ගන්න."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
msgid "The destination \"{}\" in the target system is not a directory"
|
||||||
msgstr ""
|
msgstr "ඉලක්ක පද්ධතියේ \"{}\" ගමනාන්තය නාමාවලියක් නොවේ"
|
||||||
|
|
||||||
#: src/modules/luksopenswaphookcfg/main.py:26
|
#: src/modules/luksopenswaphookcfg/main.py:26
|
||||||
msgid "Configuring encrypted swap."
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
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
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Kunde inte hitta unsquashfs, se till att du har paketet squashfs-tools "
|
||||||
|
"installerat"
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
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
|
#: src/modules/services-systemd/main.py:67
|
||||||
msgid "Cannot enable systemd timer <code>{name!s}</code>."
|
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
|
#: src/modules/services-systemd/main.py:71
|
||||||
msgid "Cannot disable systemd target <code>{name!s}</code>."
|
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 "
|
"Failed to find unsquashfs, make sure you have the squashfs-tools package "
|
||||||
"installed."
|
"installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Unsquashfs bulunamadı, squashfs-tools paketinin kurulu olduğundan emin olun."
|
||||||
|
|
||||||
#: src/modules/unpackfs/main.py:479
|
#: src/modules/unpackfs/main.py:479
|
||||||
msgid "The destination \"{}\" in the target system is not a directory"
|
msgid "The destination \"{}\" in the target system is not a directory"
|
||||||
|
@ -122,6 +122,7 @@ sequence:
|
|||||||
- summary
|
- summary
|
||||||
- exec:
|
- exec:
|
||||||
- partition
|
- partition
|
||||||
|
# - zfs
|
||||||
- mount
|
- mount
|
||||||
- unpackfs
|
- unpackfs
|
||||||
- machineid
|
- machineid
|
||||||
|
@ -132,6 +132,11 @@ public:
|
|||||||
void setEmergency( bool e ) { m_emergency = e; }
|
void setEmergency( bool e ) { m_emergency = e; }
|
||||||
|
|
||||||
signals:
|
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 );
|
void progress( qreal percent );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QTextStream>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
@ -229,7 +231,7 @@ toString( const QVariant& v )
|
|||||||
}
|
}
|
||||||
|
|
||||||
QDebug&
|
QDebug&
|
||||||
operator<<( QDebug& s, const Redacted& l )
|
operator<<( QDebug& s, const RedactedCommand& l )
|
||||||
{
|
{
|
||||||
// Special case logging: don't log the (encrypted) password.
|
// Special case logging: don't log the (encrypted) password.
|
||||||
if ( l.list.contains( "usermod" ) )
|
if ( l.list.contains( "usermod" ) )
|
||||||
@ -252,4 +254,33 @@ operator<<( QDebug& s, const Redacted& l )
|
|||||||
return s;
|
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
|
} // namespace Logger
|
||||||
|
@ -145,8 +145,8 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const T& first;
|
const T first;
|
||||||
const U& second;
|
const U second;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,9 +214,9 @@ public:
|
|||||||
* since the log may get posted to bug reports, or stored in
|
* since the log may get posted to bug reports, or stored in
|
||||||
* the target system.
|
* the target system.
|
||||||
*/
|
*/
|
||||||
struct Redacted
|
struct RedactedCommand
|
||||||
{
|
{
|
||||||
Redacted( const QStringList& l )
|
RedactedCommand( const QStringList& l )
|
||||||
: list( l )
|
: list( l )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -224,7 +224,30 @@ struct Redacted
|
|||||||
const QStringList& list;
|
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
|
* @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();
|
process.start();
|
||||||
if ( !process.waitForStarted() )
|
if ( !process.waitForStarted() )
|
||||||
{
|
{
|
||||||
@ -225,13 +225,13 @@ Calamares::Utils::Runner::run()
|
|||||||
{
|
{
|
||||||
if ( !output.isEmpty() )
|
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"
|
<< "output:\n"
|
||||||
<< Logger::NoQuote << output;
|
<< Logger::NoQuote << output;
|
||||||
}
|
}
|
||||||
else
|
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)";
|
<< "(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
|
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.
|
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.
|
The *timeout* is in seconds, with 0 (or a negative number) treated as no-timeout.
|
||||||
|
@ -92,6 +92,50 @@ def get_kernel_line(kernel_type):
|
|||||||
return ""
|
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(install_path, efi_dir, uuid, entry, entry_name, kernel_type):
|
def create_systemd_boot_conf(install_path, efi_dir, uuid, entry, entry_name, kernel_type):
|
||||||
"""
|
"""
|
||||||
Creates systemd-boot configuration files based on given parameters.
|
Creates systemd-boot configuration files based on given parameters.
|
||||||
@ -133,11 +177,24 @@ def create_systemd_boot_conf(install_path, efi_dir, uuid, entry, entry_name, ker
|
|||||||
"root=/dev/mapper/"
|
"root=/dev/mapper/"
|
||||||
+ partition["luksMapperName"]]
|
+ partition["luksMapperName"]]
|
||||||
|
|
||||||
# systemd-boot with a BTRFS root filesystem needs to be told
|
|
||||||
# about the root subvolume.
|
|
||||||
for partition in partitions:
|
for partition in partitions:
|
||||||
if partition["mountPoint"] == "/" and partition["fs"] == "btrfs":
|
# systemd-boot with a BTRFS root filesystem needs to be told abouut the root subvolume.
|
||||||
kernel_params.append("rootflags=subvol=@")
|
# 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:
|
if cryptdevice_params:
|
||||||
kernel_params.extend(cryptdevice_params)
|
kernel_params.extend(cryptdevice_params)
|
||||||
@ -314,6 +371,76 @@ def get_grub_efi_parameters():
|
|||||||
return None
|
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):
|
def install_grub(efi_directory, fw_type):
|
||||||
"""
|
"""
|
||||||
Installs grub as bootloader, either in pc or efi mode.
|
Installs grub as bootloader, either in pc or efi mode.
|
||||||
@ -321,6 +448,12 @@ def install_grub(efi_directory, fw_type):
|
|||||||
:param efi_directory:
|
:param efi_directory:
|
||||||
:param fw_type:
|
: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":
|
if fw_type == "efi":
|
||||||
libcalamares.utils.debug("Bootloader: grub (efi)")
|
libcalamares.utils.debug("Bootloader: grub (efi)")
|
||||||
install_path = libcalamares.globalstorage.value("rootMountPoint")
|
install_path = libcalamares.globalstorage.value("rootMountPoint")
|
||||||
@ -333,11 +466,7 @@ def install_grub(efi_directory, fw_type):
|
|||||||
|
|
||||||
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
|
efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
|
||||||
|
|
||||||
check_target_env_call([libcalamares.job.configuration["grubInstall"],
|
run_grub_install(fw_type, partitions, efi_directory)
|
||||||
"--target=" + efi_target,
|
|
||||||
"--efi-directory=" + efi_directory,
|
|
||||||
"--bootloader-id=" + efi_bootloader_id,
|
|
||||||
"--force"])
|
|
||||||
|
|
||||||
# VFAT is weird, see issue CAL-385
|
# VFAT is weird, see issue CAL-385
|
||||||
install_efi_directory_firmware = (vfat_correct_case(
|
install_efi_directory_firmware = (vfat_correct_case(
|
||||||
@ -356,36 +485,21 @@ def install_grub(efi_directory, fw_type):
|
|||||||
os.makedirs(install_efi_boot_directory)
|
os.makedirs(install_efi_boot_directory)
|
||||||
|
|
||||||
# Workaround for some UEFI firmwares
|
# Workaround for some UEFI firmwares
|
||||||
FALLBACK = "installEFIFallback"
|
fallback = "installEFIFallback"
|
||||||
libcalamares.utils.debug("UEFI Fallback: " + str(libcalamares.job.configuration.get(FALLBACK, "<unset>")))
|
libcalamares.utils.debug("UEFI Fallback: " + str(libcalamares.job.configuration.get(fallback, "<unset>")))
|
||||||
if libcalamares.job.configuration.get(FALLBACK, True):
|
if libcalamares.job.configuration.get(fallback, True):
|
||||||
libcalamares.utils.debug(" .. installing '{!s}' fallback firmware".format(efi_boot_file))
|
libcalamares.utils.debug(" .. installing '{!s}' fallback firmware".format(efi_boot_file))
|
||||||
efi_file_source = os.path.join(install_efi_directory_firmware,
|
efi_file_source = os.path.join(install_efi_directory_firmware,
|
||||||
efi_bootloader_id,
|
efi_bootloader_id,
|
||||||
efi_grub_file)
|
efi_grub_file)
|
||||||
efi_file_target = os.path.join(install_efi_boot_directory,
|
efi_file_target = os.path.join(install_efi_boot_directory, efi_boot_file)
|
||||||
efi_boot_file)
|
|
||||||
|
|
||||||
shutil.copy2(efi_file_source, efi_file_target)
|
shutil.copy2(efi_file_source, efi_file_target)
|
||||||
else:
|
else:
|
||||||
libcalamares.utils.debug("Bootloader: grub (bios)")
|
libcalamares.utils.debug("Bootloader: grub (bios)")
|
||||||
if libcalamares.globalstorage.value("bootLoader") is None:
|
run_grub_install(fw_type, partitions)
|
||||||
return
|
|
||||||
|
|
||||||
boot_loader = libcalamares.globalstorage.value("bootLoader")
|
run_grub_mkconfig(partitions, libcalamares.job.configuration["grubCfg"])
|
||||||
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"]])
|
|
||||||
|
|
||||||
|
|
||||||
def install_secureboot(efi_directory):
|
def install_secureboot(efi_directory):
|
||||||
|
@ -23,6 +23,7 @@ displaymanagers:
|
|||||||
- mdm
|
- mdm
|
||||||
- lxdm
|
- lxdm
|
||||||
- kdm
|
- kdm
|
||||||
|
- greetd
|
||||||
|
|
||||||
# Enable the following settings to force a desktop environment
|
# Enable the following settings to force a desktop environment
|
||||||
# in your displaymanager configuration file. This will attempt
|
# in your displaymanager configuration file. This will attempt
|
||||||
|
@ -10,7 +10,7 @@ properties:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
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
|
minItems: 1 # Must be non-empty, if present at all
|
||||||
defaultDesktopEnvironment:
|
defaultDesktopEnvironment:
|
||||||
type: object
|
type: object
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
|
|
||||||
import abc
|
import abc
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import libcalamares
|
import libcalamares
|
||||||
import configparser
|
|
||||||
|
|
||||||
from libcalamares.utils import gettext_path, gettext_languages
|
from libcalamares.utils import gettext_path, gettext_languages
|
||||||
|
|
||||||
@ -796,6 +794,8 @@ class DMsddm(DisplayManager):
|
|||||||
executable = "sddm"
|
executable = "sddm"
|
||||||
|
|
||||||
def set_autologin(self, username, do_autologin, default_desktop_environment):
|
def set_autologin(self, username, do_autologin, default_desktop_environment):
|
||||||
|
import configparser
|
||||||
|
|
||||||
# Systems with Sddm as Desktop Manager
|
# Systems with Sddm as Desktop Manager
|
||||||
sddm_conf_path = os.path.join(self.root_mount_point, "etc/sddm.conf")
|
sddm_conf_path = os.path.join(self.root_mount_point, "etc/sddm.conf")
|
||||||
|
|
||||||
@ -835,6 +835,91 @@ class DMsddm(DisplayManager):
|
|||||||
pass
|
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):
|
class DMsysconfig(DisplayManager):
|
||||||
name = "sysconfig"
|
name = "sysconfig"
|
||||||
executable = None
|
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,13 @@
|
|||||||
# Mount options to use for all filesystems. If a specific filesystem
|
# Mount options to use for all filesystems. If a specific filesystem
|
||||||
# is listed here, use those options, otherwise use the *default*
|
# is listed here, use those options, otherwise use the *default*
|
||||||
# options from this mapping.
|
# 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.
|
||||||
mountOptions:
|
mountOptions:
|
||||||
default: defaults,noatime
|
default: defaults,noatime
|
||||||
btrfs: defaults,noatime,space_cache,autodefrag,compress=zstd
|
btrfs: defaults,noatime,autodefrag,compress=zstd
|
||||||
|
|
||||||
# Mount options to use for the EFI System Partition. If not defined, the
|
# 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,
|
# *mountOptions* for *vfat* are used, or if that is not set either,
|
||||||
|
@ -196,7 +196,7 @@ class FstabGenerator(object):
|
|||||||
dct = self.generate_fstab_line_info(mount_entry)
|
dct = self.generate_fstab_line_info(mount_entry)
|
||||||
if dct:
|
if dct:
|
||||||
self.print_fstab_line(dct, file=fstab_file)
|
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)
|
dct = self.generate_fstab_line_info(partition)
|
||||||
if dct:
|
if dct:
|
||||||
self.print_fstab_line(dct, file=fstab_file)
|
self.print_fstab_line(dct, file=fstab_file)
|
||||||
|
@ -55,6 +55,32 @@ def get_grub_config_path(root_mount_point):
|
|||||||
return os.path.join(default_dir, default_config_file)
|
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):
|
def modify_grub_default(partitions, root_mount_point, distributor):
|
||||||
"""
|
"""
|
||||||
Configures '/etc/default/grub' for hibernation and plymouth.
|
Configures '/etc/default/grub' for hibernation and plymouth.
|
||||||
@ -93,6 +119,8 @@ def modify_grub_default(partitions, root_mount_point, distributor):
|
|||||||
swap_outer_mappername = None
|
swap_outer_mappername = None
|
||||||
no_save_default = False
|
no_save_default = False
|
||||||
unencrypted_separate_boot = any(p["mountPoint"] == "/boot" and "luksMapperName" not in p for p in partitions)
|
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:
|
for partition in partitions:
|
||||||
if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs"):
|
if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs"):
|
||||||
@ -143,8 +171,15 @@ def modify_grub_default(partitions, root_mount_point, distributor):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if partition["fs"] == "zfs" and partition["mountPoint"] == "/":
|
||||||
|
zfs_root_path = get_zfs_root()
|
||||||
|
|
||||||
kernel_params = ["quiet"]
|
kernel_params = ["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:
|
if cryptdevice_params:
|
||||||
kernel_params.extend(cryptdevice_params)
|
kernel_params.extend(cryptdevice_params)
|
||||||
|
|
||||||
|
@ -150,6 +150,7 @@ def find_initcpio_features(partitions, root_mount_point):
|
|||||||
|
|
||||||
swap_uuid = ""
|
swap_uuid = ""
|
||||||
uses_btrfs = False
|
uses_btrfs = False
|
||||||
|
uses_zfs = False
|
||||||
uses_lvm2 = False
|
uses_lvm2 = False
|
||||||
encrypt_hook = False
|
encrypt_hook = False
|
||||||
openswap_hook = False
|
openswap_hook = False
|
||||||
@ -179,6 +180,9 @@ def find_initcpio_features(partitions, root_mount_point):
|
|||||||
if partition["fs"] == "btrfs":
|
if partition["fs"] == "btrfs":
|
||||||
uses_btrfs = True
|
uses_btrfs = True
|
||||||
|
|
||||||
|
if partition["fs"] == "zfs":
|
||||||
|
uses_zfs = True
|
||||||
|
|
||||||
if "lvm2" in partition["fs"]:
|
if "lvm2" in partition["fs"]:
|
||||||
uses_lvm2 = True
|
uses_lvm2 = True
|
||||||
|
|
||||||
@ -205,6 +209,9 @@ def find_initcpio_features(partitions, root_mount_point):
|
|||||||
if uses_lvm2:
|
if uses_lvm2:
|
||||||
hooks.append("lvm2")
|
hooks.append("lvm2")
|
||||||
|
|
||||||
|
if uses_zfs:
|
||||||
|
hooks.append("zfs")
|
||||||
|
|
||||||
if swap_uuid != "":
|
if swap_uuid != "":
|
||||||
if encrypt_hook and openswap_hook:
|
if encrypt_hook and openswap_hook:
|
||||||
hooks.extend(["openswap"])
|
hooks.extend(["openswap"])
|
||||||
|
@ -20,12 +20,24 @@ import os
|
|||||||
import libcalamares
|
import libcalamares
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
|
|
||||||
_ = gettext.translation("calamares-python",
|
_ = gettext.translation("calamares-python",
|
||||||
localedir=libcalamares.utils.gettext_path(),
|
localedir=libcalamares.utils.gettext_path(),
|
||||||
languages=libcalamares.utils.gettext_languages(),
|
languages=libcalamares.utils.gettext_languages(),
|
||||||
fallback=True).gettext
|
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():
|
def pretty_name():
|
||||||
return _("Mounting partitions.")
|
return _("Mounting partitions.")
|
||||||
|
|
||||||
@ -47,20 +59,85 @@ def get_btrfs_subvolumes(partitions):
|
|||||||
if btrfs_subvolumes is None:
|
if btrfs_subvolumes is None:
|
||||||
libcalamares.utils.warning("No configuration for btrfsSubvolumes")
|
libcalamares.utils.warning("No configuration for btrfsSubvolumes")
|
||||||
if not btrfs_subvolumes:
|
if not btrfs_subvolumes:
|
||||||
btrfs_subvolumes = [ dict(mountPoint="/", subvolume="/@"), dict(mountPoint="/home", subvolume="/@home") ]
|
btrfs_subvolumes = [dict(mountPoint="/", subvolume="/@"), dict(mountPoint="/home", subvolume="/@home")]
|
||||||
|
|
||||||
# Filter out the subvolumes which have a dedicated partition
|
# 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
|
||||||
btrfs_subvolumes = list(filter(lambda s : s["mountPoint"] not in non_root_partition_mounts, btrfs_subvolumes))
|
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.
|
# If we have a swap **file**, give it a separate subvolume.
|
||||||
swap_choice = libcalamares.globalstorage.value( "partitionChoices" )
|
swap_choice = libcalamares.globalstorage.value("partitionChoices")
|
||||||
if swap_choice and swap_choice.get( "swap", None ) == "file":
|
if swap_choice and swap_choice.get("swap", None) == "file":
|
||||||
btrfs_subvolumes.append({'mountPoint': '/swap', 'subvolume': '/@swap'})
|
btrfs_subvolumes.append({'mountPoint': '/swap', 'subvolume': '/@swap'})
|
||||||
|
|
||||||
return btrfs_subvolumes
|
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):
|
def mount_partition(root_mount_point, partition, partitions):
|
||||||
"""
|
"""
|
||||||
Do a single mount of @p partition inside @p root_mount_point.
|
Do a single mount of @p partition inside @p root_mount_point.
|
||||||
@ -96,11 +173,14 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
if "luksMapperName" in partition:
|
if "luksMapperName" in partition:
|
||||||
device = os.path.join("/dev/mapper", partition["luksMapperName"])
|
device = os.path.join("/dev/mapper", partition["luksMapperName"])
|
||||||
|
|
||||||
if libcalamares.utils.mount(device,
|
if fstype == "zfs":
|
||||||
mount_point,
|
mount_zfs(root_mount_point, partition)
|
||||||
fstype,
|
else: # fstype == "zfs"
|
||||||
partition.get("options", "")) != 0:
|
if libcalamares.utils.mount(device,
|
||||||
libcalamares.utils.warning("Cannot mount {}".format(device))
|
mount_point,
|
||||||
|
fstype,
|
||||||
|
partition.get("options", "")) != 0:
|
||||||
|
libcalamares.utils.warning("Cannot mount {}".format(device))
|
||||||
|
|
||||||
# Special handling for btrfs subvolumes. Create the subvolumes listed in mount.conf
|
# Special handling for btrfs subvolumes. Create the subvolumes listed in mount.conf
|
||||||
if fstype == "btrfs" and partition["mountPoint"] == '/':
|
if fstype == "btrfs" and partition["mountPoint"] == '/':
|
||||||
@ -111,9 +191,11 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
libcalamares.globalstorage.insert("btrfsSubvolumes", btrfs_subvolumes)
|
libcalamares.globalstorage.insert("btrfsSubvolumes", btrfs_subvolumes)
|
||||||
# Create the subvolumes that are in the completed list
|
# Create the subvolumes that are in the completed list
|
||||||
for s in btrfs_subvolumes:
|
for s in btrfs_subvolumes:
|
||||||
subprocess.check_call(['btrfs', 'subvolume', 'create',
|
subprocess.check_call(["btrfs", "subvolume", "create",
|
||||||
root_mount_point + s['subvolume']])
|
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])
|
subprocess.check_call(["umount", "-v", root_mount_point])
|
||||||
|
|
||||||
device = partition["device"]
|
device = partition["device"]
|
||||||
@ -126,9 +208,9 @@ def mount_partition(root_mount_point, partition, partitions):
|
|||||||
mount_option = "subvol={}".format(s['subvolume'])
|
mount_option = "subvol={}".format(s['subvolume'])
|
||||||
subvolume_mountpoint = mount_point[:-1] + s['mountPoint']
|
subvolume_mountpoint = mount_point[:-1] + s['mountPoint']
|
||||||
if libcalamares.utils.mount(device,
|
if libcalamares.utils.mount(device,
|
||||||
subvolume_mountpoint,
|
subvolume_mountpoint,
|
||||||
fstype,
|
fstype,
|
||||||
",".join([mount_option, partition.get("options", "")])) != 0:
|
",".join([mount_option, partition.get("options", "")])) != 0:
|
||||||
libcalamares.utils.warning("Cannot mount {}".format(device))
|
libcalamares.utils.warning("Cannot mount {}".format(device))
|
||||||
|
|
||||||
|
|
||||||
@ -142,7 +224,7 @@ def run():
|
|||||||
if not partitions:
|
if not partitions:
|
||||||
libcalamares.utils.warning("partitions is empty, {!s}".format(partitions))
|
libcalamares.utils.warning("partitions is empty, {!s}".format(partitions))
|
||||||
return (_("Configuration Error"),
|
return (_("Configuration Error"),
|
||||||
_("No partitions are defined for <pre>{!s}</pre> to use." ).format("mount"))
|
_("No partitions are defined for <pre>{!s}</pre> to use.").format("mount"))
|
||||||
|
|
||||||
root_mount_point = tempfile.mkdtemp(prefix="calamares-root-")
|
root_mount_point = tempfile.mkdtemp(prefix="calamares-root-")
|
||||||
|
|
||||||
@ -159,10 +241,13 @@ def run():
|
|||||||
# This way, we ensure / is mounted before the rest, and every mount point
|
# This way, we ensure / is mounted before the rest, and every mount point
|
||||||
# is created on the right partition (e.g. if a partition is to be mounted
|
# is created on the right partition (e.g. if a partition is to be mounted
|
||||||
# under /tmp, we make sure /tmp is mounted before the partition)
|
# 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 = [p for p in partitions + extra_mounts if "mountPoint" in p and p["mountPoint"]]
|
||||||
mountable_partitions.sort(key=lambda x: x["mountPoint"])
|
mountable_partitions.sort(key=lambda x: x["mountPoint"])
|
||||||
for partition in mountable_partitions:
|
try:
|
||||||
mount_partition(root_mount_point, partition, partitions)
|
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)
|
libcalamares.globalstorage.insert("rootMountPoint", root_mount_point)
|
||||||
|
|
||||||
|
@ -35,6 +35,10 @@ total_packages = 0 # For the entire job
|
|||||||
completed_packages = 0 # Done so far for this job
|
completed_packages = 0 # Done so far for this job
|
||||||
group_packages = 0 # One group of packages from an -install or -remove entry
|
group_packages = 0 # One group of packages from an -install or -remove entry
|
||||||
|
|
||||||
|
# A PM object may set this to a string (take care of translations!)
|
||||||
|
# to override the string produced by pretty_status_message()
|
||||||
|
custom_status_message = None
|
||||||
|
|
||||||
INSTALL = object()
|
INSTALL = object()
|
||||||
REMOVE = object()
|
REMOVE = object()
|
||||||
mode_packages = None # Changes to INSTALL or REMOVE
|
mode_packages = None # Changes to INSTALL or REMOVE
|
||||||
@ -51,6 +55,8 @@ def pretty_name():
|
|||||||
|
|
||||||
|
|
||||||
def pretty_status_message():
|
def pretty_status_message():
|
||||||
|
if custom_status_message is not None:
|
||||||
|
return custom_status_message
|
||||||
if not group_packages:
|
if not group_packages:
|
||||||
if (total_packages > 0):
|
if (total_packages > 0):
|
||||||
# Outside the context of an operation
|
# Outside the context of an operation
|
||||||
@ -370,17 +376,44 @@ class PMPackageKit(PackageManager):
|
|||||||
class PMPacman(PackageManager):
|
class PMPacman(PackageManager):
|
||||||
backend = "pacman"
|
backend = "pacman"
|
||||||
|
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
if self.in_package_changes and line.endswith("...\n"):
|
||||||
|
# Update the message, untranslated; do not change the
|
||||||
|
# progress percentage, since there may be more "installing..."
|
||||||
|
# lines in the output for the group, than packages listed
|
||||||
|
# explicitly. We don't know how to calculate proper progress.
|
||||||
|
global custom_status_message
|
||||||
|
custom_status_message = "pacman: " + line.strip()
|
||||||
|
libcalamares.job.setprogress(self.progress_fraction)
|
||||||
|
libcalamares.utils.debug(line)
|
||||||
|
|
||||||
|
self.in_package_changes = False
|
||||||
|
self.line_cb = line_cb
|
||||||
|
|
||||||
|
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):
|
def install(self, pkgs, from_local=False):
|
||||||
if from_local:
|
if from_local:
|
||||||
pacman_flags = "-U"
|
pacman_flags = "-U"
|
||||||
else:
|
else:
|
||||||
pacman_flags = "-S"
|
pacman_flags = "-S"
|
||||||
|
|
||||||
check_target_env_call(["pacman", pacman_flags,
|
self.reset_progress()
|
||||||
"--noconfirm"] + pkgs)
|
libcalamares.utils.target_env_process_output(["pacman", pacman_flags,
|
||||||
|
"--noconfirm"] + pkgs, self.line_cb)
|
||||||
|
|
||||||
def remove(self, pkgs):
|
def remove(self, pkgs):
|
||||||
check_target_env_call(["pacman", "-Rs", "--noconfirm"] + pkgs)
|
self.reset_progress()
|
||||||
|
libcalamares.utils.target_env_process_output(["pacman", "-Rs", "--noconfirm"] + pkgs, self.line_cb)
|
||||||
|
|
||||||
def update_db(self):
|
def update_db(self):
|
||||||
check_target_env_call(["pacman", "-Sy"])
|
check_target_env_call(["pacman", "-Sy"])
|
||||||
|
@ -221,6 +221,14 @@ Config::setEraseFsTypeChoice( const QString& choice )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Config::acceptPartitionTableType( PartitionTable::TableType tableType ) const
|
||||||
|
{
|
||||||
|
return m_requiredPartitionTableType.empty()
|
||||||
|
|| m_requiredPartitionTableType.contains( PartitionTable::tableTypeToName( tableType ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configurationMap )
|
fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configurationMap )
|
||||||
{
|
{
|
||||||
@ -235,18 +243,18 @@ fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configu
|
|||||||
if ( configurationMap.contains( "efiSystemPartitionSize" ) )
|
if ( configurationMap.contains( "efiSystemPartitionSize" ) )
|
||||||
{
|
{
|
||||||
const QString sizeString = CalamaresUtils::getString( configurationMap, "efiSystemPartitionSize" );
|
const QString sizeString = CalamaresUtils::getString( configurationMap, "efiSystemPartitionSize" );
|
||||||
CalamaresUtils::Partition::PartitionSize part_size
|
CalamaresUtils::Partition::PartitionSize part_size = CalamaresUtils::Partition::PartitionSize( sizeString );
|
||||||
= CalamaresUtils::Partition::PartitionSize( sizeString );
|
if ( part_size.isValid() )
|
||||||
if (part_size.isValid())
|
|
||||||
{
|
{
|
||||||
// Insert once as string, once as a size-in-bytes;
|
// Insert once as string, once as a size-in-bytes;
|
||||||
// changes to these keys should be synchronized with PartUtils.cpp
|
// changes to these keys should be synchronized with PartUtils.cpp
|
||||||
gs->insert( "efiSystemPartitionSize", sizeString );
|
gs->insert( "efiSystemPartitionSize", sizeString );
|
||||||
gs->insert( "efiSystemPartitionSize_i", part_size.toBytes());
|
gs->insert( "efiSystemPartitionSize_i", part_size.toBytes() );
|
||||||
|
|
||||||
if (part_size.toBytes() != PartUtils::efiFilesystemMinimumSize())
|
if ( part_size.toBytes() != PartUtils::efiFilesystemMinimumSize() )
|
||||||
{
|
{
|
||||||
cWarning() << "EFI partition size" << sizeString << "has been adjusted to" << PartUtils::efiFilesystemMinimumSize() << "bytes";
|
cWarning() << "EFI partition size" << sizeString << "has been adjusted to"
|
||||||
|
<< PartUtils::efiFilesystemMinimumSize() << "bytes";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -342,11 +350,9 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
|
|||||||
setSwapChoice( m_initialSwapChoice );
|
setSwapChoice( m_initialSwapChoice );
|
||||||
|
|
||||||
m_allowManualPartitioning = CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true );
|
m_allowManualPartitioning = CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true );
|
||||||
|
m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" );
|
||||||
|
|
||||||
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||||
m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" );
|
|
||||||
gs->insert( "requiredPartitionTableType", m_requiredPartitionTableType );
|
|
||||||
|
|
||||||
fillGSConfigurationEFI( gs, configurationMap );
|
fillGSConfigurationEFI( gs, configurationMap );
|
||||||
fillConfigurationFSTypes( configurationMap );
|
fillConfigurationFSTypes( configurationMap );
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "utils/NamedEnum.h"
|
#include "utils/NamedEnum.h"
|
||||||
|
|
||||||
|
#include <kpmcore/core/partition.h>
|
||||||
#include <kpmcore/fs/filesystem.h>
|
#include <kpmcore/fs/filesystem.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@ -127,9 +128,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
FileSystem::Type defaultFsType() const { return m_defaultFsType; }
|
FileSystem::Type defaultFsType() const { return m_defaultFsType; }
|
||||||
|
|
||||||
///@brief Is manual partitioning allowed (not explicitly disabled in the config file)?
|
/// @brief Is manual partitioning allowed (not explicitly disabled in the config file)?
|
||||||
bool allowManualPartitioning() const { return m_allowManualPartitioning; }
|
bool allowManualPartitioning() const { return m_allowManualPartitioning; }
|
||||||
|
|
||||||
|
/** @brief Will @p tableType be ok?
|
||||||
|
*
|
||||||
|
* If no required types are specified, it's ok, otherwise the
|
||||||
|
* type must be named in the list of required types.
|
||||||
|
*/
|
||||||
|
bool acceptPartitionTableType( PartitionTable::TableType tableType ) const;
|
||||||
|
/// @brief Returns list of acceptable types. May be empty.
|
||||||
|
QStringList partitionTableTypes() const { return m_requiredPartitionTableType; }
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice
|
void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice
|
||||||
void setInstallChoice( InstallChoice );
|
void setInstallChoice( InstallChoice );
|
||||||
|
@ -257,14 +257,16 @@ PartitionCoreModule::doInit()
|
|||||||
cDebug() << Logger::SubEntry << "node\tcapacity\tname\tprettyName";
|
cDebug() << Logger::SubEntry << "node\tcapacity\tname\tprettyName";
|
||||||
for ( auto device : devices )
|
for ( auto device : devices )
|
||||||
{
|
{
|
||||||
cDebug() << Logger::SubEntry << Logger::Pointer( device );
|
|
||||||
if ( device )
|
if ( device )
|
||||||
{
|
{
|
||||||
// Gives ownership of the Device* to the DeviceInfo object
|
// Gives ownership of the Device* to the DeviceInfo object
|
||||||
auto deviceInfo = new DeviceInfo( device );
|
auto deviceInfo = new DeviceInfo( device );
|
||||||
m_deviceInfos << deviceInfo;
|
m_deviceInfos << deviceInfo;
|
||||||
cDebug() << Logger::SubEntry << device->deviceNode() << device->capacity() << device->name()
|
cDebug() << Logger::SubEntry
|
||||||
<< device->prettyName();
|
<< device->deviceNode()
|
||||||
|
<< device->capacity()
|
||||||
|
<< Logger::RedactedName( "DevName", device->name() )
|
||||||
|
<< Logger::RedactedName( "DevNamePretty", device->prettyName() );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -707,10 +709,10 @@ PartitionCoreModule::dumpQueue() const
|
|||||||
cDebug() << "# Queue:";
|
cDebug() << "# Queue:";
|
||||||
for ( auto info : m_deviceInfos )
|
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() )
|
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;
|
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,
|
part = KPMHelpers::createNewPartition( parent,
|
||||||
*dev,
|
*dev,
|
||||||
@ -319,6 +321,24 @@ PartitionLayout::createPartitions( Device* dev,
|
|||||||
luksPassphrase,
|
luksPassphrase,
|
||||||
KPM_PARTITION_FLAG( None ) );
|
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::setFormat( part, true );
|
||||||
PartitionInfo::setMountPoint( part, entry.partMountPoint );
|
PartitionInfo::setMountPoint( part, entry.partMountPoint );
|
||||||
if ( !entry.partLabel.isEmpty() )
|
if ( !entry.partLabel.isEmpty() )
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include "widgets/PrettyRadioButton.h"
|
#include "widgets/PrettyRadioButton.h"
|
||||||
|
|
||||||
#include <kpmcore/core/device.h>
|
#include <kpmcore/core/device.h>
|
||||||
#include <kpmcore/core/partition.h>
|
|
||||||
#ifdef WITH_KPMCORE4API
|
#ifdef WITH_KPMCORE4API
|
||||||
#include <kpmcore/core/softwareraid.h>
|
#include <kpmcore/core/softwareraid.h>
|
||||||
#endif
|
#endif
|
||||||
@ -90,7 +89,6 @@ ChoicePage::ChoicePage( Config* config, QWidget* parent )
|
|||||||
|
|
||||||
auto gs = Calamares::JobQueue::instance()->globalStorage();
|
auto gs = Calamares::JobQueue::instance()->globalStorage();
|
||||||
|
|
||||||
m_requiredPartitionTableType = gs->value( "requiredPartitionTableType" ).toStringList();
|
|
||||||
m_enableEncryptionWidget = gs->value( "enableLuksAutomatedPartitioning" ).toBool();
|
m_enableEncryptionWidget = gs->value( "enableLuksAutomatedPartitioning" ).toBool();
|
||||||
|
|
||||||
// Set up drives combo
|
// Set up drives combo
|
||||||
@ -1252,6 +1250,28 @@ operator<<( QDebug& s, PartitionIterator& it )
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
describePartitionTypes( const QStringList& types )
|
||||||
|
{
|
||||||
|
if ( types.empty() )
|
||||||
|
{
|
||||||
|
return QCoreApplication::translate(
|
||||||
|
ChoicePage::staticMetaObject.className(), "any", "any partition-table type" );
|
||||||
|
}
|
||||||
|
if ( types.size() == 1 )
|
||||||
|
{
|
||||||
|
return types.first();
|
||||||
|
}
|
||||||
|
if ( types.size() == 2 )
|
||||||
|
{
|
||||||
|
return QCoreApplication::translate(
|
||||||
|
ChoicePage::staticMetaObject.className(), "%1 or %2", "partition-table types" )
|
||||||
|
.arg( types.at( 0 ), types.at( 1 ) );
|
||||||
|
}
|
||||||
|
// More than two, rather unlikely
|
||||||
|
return types.join( ", " );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief ChoicePage::setupActions happens every time a new Device* is selected in the
|
* @brief ChoicePage::setupActions happens every time a new Device* is selected in the
|
||||||
* device picker. Sets up the text and visibility of the partitioning actions based
|
* device picker. Sets up the text and visibility of the partitioning actions based
|
||||||
@ -1305,8 +1325,7 @@ ChoicePage::setupActions()
|
|||||||
if ( currentDevice->partitionTable() )
|
if ( currentDevice->partitionTable() )
|
||||||
{
|
{
|
||||||
tableType = currentDevice->partitionTable()->type();
|
tableType = currentDevice->partitionTable()->type();
|
||||||
matchTableType = m_requiredPartitionTableType.size() == 0
|
matchTableType = m_config->acceptPartitionTableType( tableType );
|
||||||
|| m_requiredPartitionTableType.contains( PartitionTable::tableTypeToName( tableType ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( auto it = PartitionIterator::begin( currentDevice ); it != PartitionIterator::end( currentDevice ); ++it )
|
for ( auto it = PartitionIterator::begin( currentDevice ); it != PartitionIterator::end( currentDevice ); ++it )
|
||||||
@ -1487,11 +1506,11 @@ ChoicePage::setupActions()
|
|||||||
"but the partition table <strong>%1</strong> is different from the "
|
"but the partition table <strong>%1</strong> is different from the "
|
||||||
"needed <strong>%2</strong>.<br/>" )
|
"needed <strong>%2</strong>.<br/>" )
|
||||||
.arg( PartitionTable::tableTypeToName( tableType ) )
|
.arg( PartitionTable::tableTypeToName( tableType ) )
|
||||||
.arg( m_requiredPartitionTableType.join( " or " ) ) );
|
.arg( describePartitionTypes( m_config->partitionTableTypes() ) ) );
|
||||||
m_messageLabel->show();
|
m_messageLabel->show();
|
||||||
|
|
||||||
cWarning() << "Partition table" << PartitionTable::tableTypeToName( tableType )
|
cWarning() << "Partition table" << PartitionTable::tableTypeToName( tableType )
|
||||||
<< "does not match the requirement " << m_requiredPartitionTableType.join( " or " )
|
<< "does not match the requirement " << m_config->partitionTableTypes().join( ',' )
|
||||||
<< ", ENABLING erase feature and DISABLING alongside, replace and manual features.";
|
<< ", ENABLING erase feature and DISABLING alongside, replace and manual features.";
|
||||||
m_eraseButton->show();
|
m_eraseButton->show();
|
||||||
m_alongsideButton->hide();
|
m_alongsideButton->hide();
|
||||||
@ -1642,7 +1661,8 @@ ChoicePage::updateSwapChoicesTr()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cWarning() << "Box item" << index << m_eraseSwapChoiceComboBox->itemText( index ) << "has non-integer role.";
|
cWarning() << "Box item" << index << m_eraseSwapChoiceComboBox->itemText( index )
|
||||||
|
<< "has non-integer role.";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SwapChoice::ReuseSwap:
|
case SwapChoice::ReuseSwap:
|
||||||
|
@ -159,7 +159,6 @@ private:
|
|||||||
int m_lastSelectedDeviceIndex = -1;
|
int m_lastSelectedDeviceIndex = -1;
|
||||||
int m_lastSelectedActionIndex = -1;
|
int m_lastSelectedActionIndex = -1;
|
||||||
|
|
||||||
QStringList m_requiredPartitionTableType;
|
|
||||||
bool m_enableEncryptionWidget;
|
bool m_enableEncryptionWidget;
|
||||||
|
|
||||||
QMutex m_coreMutex;
|
QMutex m_coreMutex;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
#include "GlobalStorage.h"
|
#include "GlobalStorage.h"
|
||||||
#include "JobQueue.h"
|
#include "JobQueue.h"
|
||||||
|
#include "Settings.h"
|
||||||
#include "partition/FileSystem.h"
|
#include "partition/FileSystem.h"
|
||||||
#include "partition/PartitionQuery.h"
|
#include "partition/PartitionQuery.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
@ -104,7 +105,9 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
|
|||||||
QStringList fsNames;
|
QStringList fsNames;
|
||||||
for ( auto fs : FileSystemFactory::map() )
|
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
|
fsNames << userVisibleFS( fs ); // This is put into the combobox
|
||||||
if ( fs->type() == defaultFSType )
|
if ( fs->type() == defaultFSType )
|
||||||
@ -240,7 +243,8 @@ CreatePartitionDialog::getNewlyCreatedPartition()
|
|||||||
// does so, to set up the partition for create-and-then-set-flags.
|
// does so, to set up the partition for create-and-then-set-flags.
|
||||||
Partition* partition = nullptr;
|
Partition* partition = nullptr;
|
||||||
QString luksPassphrase = m_ui->encryptWidget->passphrase();
|
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(
|
partition = KPMHelpers::createNewEncryptedPartition(
|
||||||
m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() );
|
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() );
|
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 )
|
if ( m_device->type() == Device::Type::LVM_Device )
|
||||||
{
|
{
|
||||||
partition->setPartitionPath( m_device->deviceNode() + QStringLiteral( "/" )
|
partition->setPartitionPath( m_device->deviceNode() + QStringLiteral( "/" )
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "GlobalStorage.h"
|
#include "GlobalStorage.h"
|
||||||
#include "JobQueue.h"
|
#include "JobQueue.h"
|
||||||
|
#include "Settings.h"
|
||||||
#include "partition/FileSystem.h"
|
#include "partition/FileSystem.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
@ -89,7 +90,9 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
|
|||||||
QStringList fsNames;
|
QStringList fsNames;
|
||||||
for ( auto fs : FileSystemFactory::map() )
|
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
|
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->fileSystemLabel->setEnabled( m_ui->formatRadioButton->isChecked() );
|
||||||
m_ui->fileSystemComboBox->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 ) );
|
setFlagList( *( m_ui->m_listFlags ), m_partition->availableFlags(), PartitionInfo::flags( m_partition ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2016 Teo Mrnjavac <teo@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2018-2021 Adriaan de Groot <groot@kde.org>
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*
|
*
|
||||||
* Calamares is Free Software: see the License-Identifier above.
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
@ -215,6 +215,12 @@ getPVGroups( const QString& deviceName )
|
|||||||
* meant **only** for debugging and is not displayed to the user,
|
* meant **only** for debugging and is not displayed to the user,
|
||||||
* which is why no translation is applied.
|
* 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
|
class MessageAndPath
|
||||||
@ -237,8 +243,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
const char* const m_message = nullptr;
|
||||||
QString const m_path;
|
QString const m_path;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
STATICTEST inline QDebug&
|
STATICTEST inline QDebug&
|
||||||
|
@ -11,8 +11,10 @@
|
|||||||
|
|
||||||
#include "CreatePartitionJob.h"
|
#include "CreatePartitionJob.h"
|
||||||
|
|
||||||
|
#include "core/PartitionInfo.h"
|
||||||
#include "partition/FileSystem.h"
|
#include "partition/FileSystem.h"
|
||||||
#include "partition/PartitionQuery.h"
|
#include "partition/PartitionQuery.h"
|
||||||
|
#include "utils/CalamaresUtilsSystem.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Units.h"
|
#include "utils/Units.h"
|
||||||
|
|
||||||
@ -24,9 +26,79 @@
|
|||||||
#include <kpmcore/ops/newoperation.h>
|
#include <kpmcore/ops/newoperation.h>
|
||||||
#include <kpmcore/util/report.h>
|
#include <kpmcore/util/report.h>
|
||||||
|
|
||||||
|
#include <qcoreapplication.h>
|
||||||
|
#include <qregularexpression.h>
|
||||||
|
|
||||||
using CalamaresUtils::Partition::untranslatedFS;
|
using CalamaresUtils::Partition::untranslatedFS;
|
||||||
using CalamaresUtils::Partition::userVisibleFS;
|
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 )
|
CreatePartitionJob::CreatePartitionJob( Device* device, Partition* partition )
|
||||||
: PartitionJob( partition )
|
: PartitionJob( partition )
|
||||||
, m_device( device )
|
, m_device( device )
|
||||||
@ -194,6 +266,13 @@ CreatePartitionJob::prettyStatusMessage() const
|
|||||||
Calamares::JobResult
|
Calamares::JobResult
|
||||||
CreatePartitionJob::exec()
|
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 );
|
Report report( nullptr );
|
||||||
NewOperation op( *m_device, m_partition );
|
NewOperation op( *m_device, m_partition );
|
||||||
op.setStatus( Operation::StatusRunning );
|
op.setStatus( Operation::StatusRunning );
|
||||||
|
@ -104,14 +104,19 @@ mapForPartition( Partition* partition, const QString& uuid )
|
|||||||
// Debugging for inside the loop in createPartitionList(),
|
// Debugging for inside the loop in createPartitionList(),
|
||||||
// so indent a bit
|
// so indent a bit
|
||||||
Logger::CDebug deb;
|
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()
|
deb << Logger::SubEntry << "mapping for" << partition->partitionPath() << partition->deviceNode()
|
||||||
<< TR( "partlabel", map[ "partlabel" ].toString() ) << TR( "partuuid", map[ "partuuid" ].toString() )
|
<< TR( "partlabel", map[ "partlabel" ].toString() )
|
||||||
<< TR( "parttype", map[ "parttype" ].toString() ) << TR( "partattrs", map[ "partattrs" ].toString() )
|
<< TR( "partition-uuid (partuuid)", Logger::RedactedName( "PartUUID", map[ "partuuid" ].toString() ) )
|
||||||
<< TR( "mountPoint:", PartitionInfo::mountPoint( partition ) ) << TR( "fs:", map[ "fs" ].toString() )
|
<< TR( "parttype", map[ "parttype" ].toString() )
|
||||||
<< TR( "fsName", map[ "fsName" ].toString() ) << TR( "uuid", uuid )
|
<< 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() );
|
<< TR( "claimed", map[ "claimed" ].toString() );
|
||||||
|
// clang-format on
|
||||||
if ( partition->roles().has( PartitionRole::Luks ) )
|
if ( partition->roles().has( PartitionRole::Luks ) )
|
||||||
{
|
{
|
||||||
const FileSystem& fsRef = partition->fileSystem();
|
const FileSystem& fsRef = partition->fileSystem();
|
||||||
|
@ -19,6 +19,14 @@ QTEST_GUILESS_MAIN( ClearMountsJobTests )
|
|||||||
/* Not exactly public API */
|
/* Not exactly public API */
|
||||||
QStringList getPartitionsForDevice( const QString& deviceName );
|
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
|
QStringList
|
||||||
getPartitionsForDevice_other( const QString& deviceName )
|
getPartitionsForDevice_other( const QString& deviceName )
|
||||||
{
|
{
|
||||||
@ -26,7 +34,7 @@ getPartitionsForDevice_other( const QString& deviceName )
|
|||||||
process.setProgram( "sh" );
|
process.setProgram( "sh" );
|
||||||
process.setArguments(
|
process.setArguments(
|
||||||
{ "-c",
|
{ "-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 ) } );
|
.arg( deviceName ) } );
|
||||||
process.start();
|
process.start();
|
||||||
process.waitForFinished();
|
process.waitForFinished();
|
||||||
|
@ -49,6 +49,27 @@ def list_mounts(root_mount_point):
|
|||||||
return lst
|
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():
|
def run():
|
||||||
""" Unmounts given mountpoints in decreasing order.
|
""" Unmounts given mountpoints in decreasing order.
|
||||||
|
|
||||||
@ -94,6 +115,8 @@ def run():
|
|||||||
# in the exception object.
|
# in the exception object.
|
||||||
subprocess.check_output(["umount", "-lv", mount_point], stderr=subprocess.STDOUT)
|
subprocess.check_output(["umount", "-lv", mount_point], stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
export_zpools(root_mount_point)
|
||||||
|
|
||||||
os.rmdir(root_mount_point)
|
os.rmdir(root_mount_point)
|
||||||
|
|
||||||
return None
|
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
|
||||||
|
)
|
||||||
|
|
18
src/modules/zfs/README.md
Normal file
18
src/modules/zfs/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
## 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
|
||||||
|
* Support for zfs in the partition module is conditional on the zfs module being enabled
|
||||||
|
* 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
|
38
src/modules/zfs/zfs.conf
Normal file
38
src/modules/zfs/zfs.conf
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 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
|
||||||
|
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 /
|
||||||
|
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