Merge branch 'calamares' of https://github.com/calamares/calamares into development

This commit is contained in:
Philip Müller 2020-11-04 23:07:45 +01:00
commit 9d8e9a6c7d
45 changed files with 3666 additions and 508 deletions

16
CHANGES
View File

@ -7,25 +7,35 @@ 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.33 (unreleased) # # 3.2.33 (2020-11-03) #
This release contains contributions from (alphabetically by first name): This release contains contributions from (alphabetically by first name):
- Anke Boersma - Anke Boersma
- Andrius Štikonas - Andrius Štikonas
- Artem Grinev - Artem Grinev
- Gaël PORTAY - Gaël PORTAY
- Matti Hyttinen
- TTran Me - TTran Me
## Core ## ## Core ##
- Calamares now sets the C++ standard for compilation to C++17; this - Calamares now sets the C++ standard for compilation to C++17; this
is for better compatibility and fewer warnings when building with is for better compatibility and fewer warnings when building with
modern KDE Frameworks and KPMcore 4.2.0. modern KDE Frameworks and KPMcore 4.2.0.
- Vietnamese translations have been added. Welcome! - Vietnamese translations have been added. Welcome! (Thanks TTran)
## Modules ## ## Modules ##
- The *initcpiocfg* module should support plymouth with encryption
now. (Thanks Matti)
- The *keyboard* and *keyboardq* modules now share backend code - The *keyboard* and *keyboardq* modules now share backend code
and handle non-ASCII layouts better (for setting passwords and handle non-ASCII layouts better (for setting passwords
and usernames). and usernames). (Thanks Artem)
- Various cleanups and documentation improvements in the *partition*
module, and configurable GPT name for swap. (Thanks Gaël)
- The *users* module now has a more detailed way to specify
user groups -- which may be system groups rather than user-GIDs.
A new option in each group can require that the group already
exists in the target system, allowing for better consistency checks
with the squashfs. #1523
# 3.2.32.1 (2020-10-17) # # 3.2.32.1 (2020-10-17) #

View File

@ -44,7 +44,7 @@ project( CALAMARES
VERSION 3.2.33 VERSION 3.2.33
LANGUAGES C CXX ) LANGUAGES C CXX )
set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development
### OPTIONS ### OPTIONS
# #

View File

@ -42,6 +42,8 @@ function( calamares_add_test )
Qt5::Test Qt5::Test
) )
calamares_automoc( ${TEST_NAME} ) calamares_automoc( ${TEST_NAME} )
# We specifically pass in the source directory of the test-being-
# compiled, so that it can find test-files in that source dir.
target_compile_definitions( ${TEST_NAME} PRIVATE -DBUILD_AS_TEST="${CMAKE_CURRENT_SOURCE_DIR}" ${TEST_DEFINITIONS} ) target_compile_definitions( ${TEST_NAME} PRIVATE -DBUILD_AS_TEST="${CMAKE_CURRENT_SOURCE_DIR}" ${TEST_DEFINITIONS} )
if( TEST_GUI ) if( TEST_GUI )
target_link_libraries( ${TEST_NAME} calamaresui Qt5::Gui ) target_link_libraries( ${TEST_NAME} calamaresui Qt5::Gui )

View File

@ -40,7 +40,7 @@ git clone https://github.com/calamares/calamares.git
``` ```
Calamares is a KDE-Frameworks and Qt-based, C++17, CMake-built application. Calamares is a KDE-Frameworks and Qt-based, C++17, CMake-built application.
The dependencies are explainged in [CONTRIBUTING.md](CONTRIBUTING.md). The dependencies are explained in [CONTRIBUTING.md](CONTRIBUTING.md).
## Contributing to Calamares ## Contributing to Calamares

View File

@ -619,17 +619,17 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir.</transl
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1448"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1448"/>
<source>This storage device already has an operating system on it, but the partition table &lt;strong&gt;%1&lt;/strong&gt; is different from the needed &lt;strong&gt;%2&lt;/strong&gt;.&lt;br/&gt;</source> <source>This storage device already has an operating system on it, but the partition table &lt;strong&gt;%1&lt;/strong&gt; is different from the needed &lt;strong&gt;%2&lt;/strong&gt;.&lt;br/&gt;</source>
<translation type="unfinished"/> <translation>Bu yaddaş qurğusunda artıq əməliyyat sistemi var, lakin, bölmə cədvəli &lt;strong&gt;%1&lt;/strong&gt;, lazım olan &lt;strong&gt;%2&lt;/strong&gt; ilə fərqlidir.&lt;br/&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1471"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1471"/>
<source>This storage device has one of its partitions &lt;strong&gt;mounted&lt;/strong&gt;.</source> <source>This storage device has one of its partitions &lt;strong&gt;mounted&lt;/strong&gt;.</source>
<translation type="unfinished"/> <translation>Bu yaddaş qurğusunda bölmələrdən biri &lt;strong&gt;quraşdırılmışdır&lt;/strong&gt;.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1476"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1476"/>
<source>This storage device is a part of an &lt;strong&gt;inactive RAID&lt;/strong&gt; device.</source> <source>This storage device is a part of an &lt;strong&gt;inactive RAID&lt;/strong&gt; device.</source>
<translation type="unfinished"/> <translation>Bu yaddaş qurğusu &lt;strong&gt;qeyri-aktiv RAİD&lt;/strong&gt; qurğusunun bir hissəsidir.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1603"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1603"/>
@ -2869,7 +2869,7 @@ Output:
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/>
<source>Directory not found</source> <source>Directory not found</source>
<translation type="unfinished"/> <translation>Qovluq tapılmadı</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/>

View File

@ -619,17 +619,17 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir.</transl
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1448"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1448"/>
<source>This storage device already has an operating system on it, but the partition table &lt;strong&gt;%1&lt;/strong&gt; is different from the needed &lt;strong&gt;%2&lt;/strong&gt;.&lt;br/&gt;</source> <source>This storage device already has an operating system on it, but the partition table &lt;strong&gt;%1&lt;/strong&gt; is different from the needed &lt;strong&gt;%2&lt;/strong&gt;.&lt;br/&gt;</source>
<translation type="unfinished"/> <translation>Bu yaddaş qurğusunda artıq əməliyyat sistemi var, lakin, bölmə cədvəli &lt;strong&gt;%1&lt;/strong&gt;, lazım olan &lt;strong&gt;%2&lt;/strong&gt; ilə fərqlidir.&lt;br/&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1471"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1471"/>
<source>This storage device has one of its partitions &lt;strong&gt;mounted&lt;/strong&gt;.</source> <source>This storage device has one of its partitions &lt;strong&gt;mounted&lt;/strong&gt;.</source>
<translation type="unfinished"/> <translation>Bu yaddaş qurğusunda bölmələrdən biri &lt;strong&gt;quraşdırılmışdır&lt;/strong&gt;.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1476"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1476"/>
<source>This storage device is a part of an &lt;strong&gt;inactive RAID&lt;/strong&gt; device.</source> <source>This storage device is a part of an &lt;strong&gt;inactive RAID&lt;/strong&gt; device.</source>
<translation type="unfinished"/> <translation>Bu yaddaş qurğusu &lt;strong&gt;qeyri-aktiv RAİD&lt;/strong&gt; qurğusunun bir hissəsidir.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1603"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1603"/>
@ -2869,7 +2869,7 @@ Output:
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/>
<source>Directory not found</source> <source>Directory not found</source>
<translation type="unfinished"/> <translation>Qovluq tapılmadı</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/>

View File

@ -437,7 +437,7 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/libcalamares/PythonHelper.cpp" line="288"/> <location filename="../src/libcalamares/PythonHelper.cpp" line="288"/>
<source>Unknown exception type</source> <source>Unknown exception type</source>
<translation>טיפוס חריגה אינו מוכר</translation> <translation>סוג חריגה לא מוכר</translation>
</message> </message>
<message> <message>
<location filename="../src/libcalamares/PythonHelper.cpp" line="306"/> <location filename="../src/libcalamares/PythonHelper.cpp" line="306"/>

View File

@ -1620,7 +1620,7 @@ Telepítés nem folytatható. &lt;a href="#details"&gt;Részletek...&lt;/a&gt;</
<message> <message>
<location filename="../src/modules/license/LicensePage.ui" line="26"/> <location filename="../src/modules/license/LicensePage.ui" line="26"/>
<source>&lt;h1&gt;License Agreement&lt;/h1&gt;</source> <source>&lt;h1&gt;License Agreement&lt;/h1&gt;</source>
<translation type="unfinished"/> <translation>&lt;h1&gt;Licenszszerződés&lt;/h1&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/license/LicensePage.cpp" line="136"/> <location filename="../src/modules/license/LicensePage.cpp" line="136"/>

View File

@ -304,12 +304,12 @@
<message> <message>
<location filename="../src/libcalamaresui/ViewManager.cpp" line="208"/> <location filename="../src/libcalamaresui/ViewManager.cpp" line="208"/>
<source>Calamares Initialization Failed</source> <source>Calamares Initialization Failed</source>
<translation>Calamares </translation> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/libcalamaresui/ViewManager.cpp" line="209"/> <location filename="../src/libcalamaresui/ViewManager.cpp" line="209"/>
<source>%1 can not be installed. Calamares was unable to load all of the configured modules. This is a problem with the way Calamares is being used by the distribution.</source> <source>%1 can not be installed. Calamares was unable to load all of the configured modules. This is a problem with the way Calamares is being used by the distribution.</source>
<translation>%1 . Calamares . Calamares가 .</translation> <translation>%1 . . .</translation>
</message> </message>
<message> <message>
<location filename="../src/libcalamaresui/ViewManager.cpp" line="215"/> <location filename="../src/libcalamaresui/ViewManager.cpp" line="215"/>
@ -617,17 +617,17 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1448"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1448"/>
<source>This storage device already has an operating system on it, but the partition table &lt;strong&gt;%1&lt;/strong&gt; is different from the needed &lt;strong&gt;%2&lt;/strong&gt;.&lt;br/&gt;</source> <source>This storage device already has an operating system on it, but the partition table &lt;strong&gt;%1&lt;/strong&gt; is different from the needed &lt;strong&gt;%2&lt;/strong&gt;.&lt;br/&gt;</source>
<translation type="unfinished"/> <translation> &lt;strong&gt;%1&lt;/strong&gt; &lt;strong&gt;%2&lt;/strong&gt; .&lt;br/&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1471"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1471"/>
<source>This storage device has one of its partitions &lt;strong&gt;mounted&lt;/strong&gt;.</source> <source>This storage device has one of its partitions &lt;strong&gt;mounted&lt;/strong&gt;.</source>
<translation type="unfinished"/> <translation> &lt;strong&gt;&lt;/strong&gt; .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1476"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1476"/>
<source>This storage device is a part of an &lt;strong&gt;inactive RAID&lt;/strong&gt; device.</source> <source>This storage device is a part of an &lt;strong&gt;inactive RAID&lt;/strong&gt; device.</source>
<translation type="unfinished"/> <translation> &lt;strong&gt; RAID&lt;/strong&gt; .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1603"/> <location filename="../src/modules/partition/gui/ChoicePage.cpp" line="1603"/>
@ -730,7 +730,7 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/modules/locale/Config.cpp" line="334"/> <location filename="../src/modules/locale/Config.cpp" line="334"/>
<source>Set timezone to %1/%2.</source> <source>Set timezone to %1/%2.</source>
<translation type="unfinished"/> <translation> %1/%2 .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/locale/Config.cpp" line="372"/> <location filename="../src/modules/locale/Config.cpp" line="372"/>
@ -790,22 +790,22 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/modules/welcome/Config.cpp" line="244"/> <location filename="../src/modules/welcome/Config.cpp" line="244"/>
<source>&lt;h1&gt;Welcome to the Calamares setup program for %1&lt;/h1&gt;</source> <source>&lt;h1&gt;Welcome to the Calamares setup program for %1&lt;/h1&gt;</source>
<translation type="unfinished"/> <translation>&lt;h1&gt; %1 &lt;/h1&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/welcome/Config.cpp" line="245"/> <location filename="../src/modules/welcome/Config.cpp" line="245"/>
<source>&lt;h1&gt;Welcome to %1 setup&lt;/h1&gt;</source> <source>&lt;h1&gt;Welcome to %1 setup&lt;/h1&gt;</source>
<translation type="unfinished"/> <translation>&lt;h1&gt;%1 &lt;/h1&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/welcome/Config.cpp" line="250"/> <location filename="../src/modules/welcome/Config.cpp" line="250"/>
<source>&lt;h1&gt;Welcome to the Calamares installer for %1&lt;/h1&gt;</source> <source>&lt;h1&gt;Welcome to the Calamares installer for %1&lt;/h1&gt;</source>
<translation type="unfinished"/> <translation>&lt;h1&gt; %1 &lt;/h1&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/welcome/Config.cpp" line="251"/> <location filename="../src/modules/welcome/Config.cpp" line="251"/>
<source>&lt;h1&gt;Welcome to the %1 installer&lt;/h1&gt;</source> <source>&lt;h1&gt;Welcome to the %1 installer&lt;/h1&gt;</source>
<translation type="unfinished"/> <translation>&lt;h1&gt;%1 &lt;/h1&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/users/Config.cpp" line="164"/> <location filename="../src/modules/users/Config.cpp" line="164"/>
@ -815,7 +815,7 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/modules/users/Config.cpp" line="170"/> <location filename="../src/modules/users/Config.cpp" line="170"/>
<source>'%1' is not allowed as username.</source> <source>'%1' is not allowed as username.</source>
<translation type="unfinished"/> <translation>'%1' .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/users/Config.cpp" line="177"/> <location filename="../src/modules/users/Config.cpp" line="177"/>
@ -840,7 +840,7 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/modules/users/Config.cpp" line="237"/> <location filename="../src/modules/users/Config.cpp" line="237"/>
<source>'%1' is not allowed as hostname.</source> <source>'%1' is not allowed as hostname.</source>
<translation type="unfinished"/> <translation>'%1' .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/users/Config.cpp" line="243"/> <location filename="../src/modules/users/Config.cpp" line="243"/>
@ -1813,7 +1813,7 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/modules/localeq/Map.qml" line="243"/> <location filename="../src/modules/localeq/Map.qml" line="243"/>
<source>Timezone: %1</source> <source>Timezone: %1</source>
<translation type="unfinished"/> <translation>: %1</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/localeq/Map.qml" line="264"/> <location filename="../src/modules/localeq/Map.qml" line="264"/>
@ -1945,7 +1945,7 @@ The installer will quit and all changes will be lost.</source>
<message> <message>
<location filename="../src/modules/oemid/OEMPage.ui" line="52"/> <location filename="../src/modules/oemid/OEMPage.ui" line="52"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;h1&gt;OEM Configuration&lt;/h1&gt;&lt;p&gt;Calamares will use OEM settings while configuring the target system.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source> <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;h1&gt;OEM Configuration&lt;/h1&gt;&lt;p&gt;Calamares will use OEM settings while configuring the target system.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;h1&gt;OEM &lt;/h1&gt; &lt;p&gt;Calamares OEM .&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation> <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;h1&gt;OEM &lt;/h1&gt; &lt;p&gt; OEM .&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1973,22 +1973,22 @@ The installer will quit and all changes will be lost.</source>
<location filename="../src/modules/localeq/Offline.qml" line="169"/> <location filename="../src/modules/localeq/Offline.qml" line="169"/>
<location filename="../src/modules/localeq/Offline.qml" line="213"/> <location filename="../src/modules/localeq/Offline.qml" line="213"/>
<source>Timezone: %1</source> <source>Timezone: %1</source>
<translation type="unfinished"/> <translation>: %1</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/localeq/Offline.qml" line="111"/> <location filename="../src/modules/localeq/Offline.qml" line="111"/>
<source>Select your preferred Zone within your Region.</source> <source>Select your preferred Zone within your Region.</source>
<translation type="unfinished"/> <translation> .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/localeq/Offline.qml" line="182"/> <location filename="../src/modules/localeq/Offline.qml" line="182"/>
<source>Zones</source> <source>Zones</source>
<translation type="unfinished"/> <translation></translation>
</message> </message>
<message> <message>
<location filename="../src/modules/localeq/Offline.qml" line="229"/> <location filename="../src/modules/localeq/Offline.qml" line="229"/>
<source>You can fine-tune Language and Locale settings below.</source> <source>You can fine-tune Language and Locale settings below.</source>
<translation type="unfinished"/> <translation> .</translation>
</message> </message>
</context> </context>
<context> <context>
@ -2864,7 +2864,7 @@ Output:
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/>
<source>Directory not found</source> <source>Directory not found</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/>
@ -3044,7 +3044,7 @@ Output:
<message> <message>
<location filename="../src/modules/fsresizer/ResizeFSJob.cpp" line="176"/> <location filename="../src/modules/fsresizer/ResizeFSJob.cpp" line="176"/>
<source>Calamares cannot start KPMCore for the file-system resize job.</source> <source>Calamares cannot start KPMCore for the file-system resize job.</source>
<translation>Calamares KPMCore를 .</translation> <translation> KPMCore를 .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/fsresizer/ResizeFSJob.cpp" line="184"/> <location filename="../src/modules/fsresizer/ResizeFSJob.cpp" line="184"/>
@ -3482,18 +3482,18 @@ Output:
<message> <message>
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="122"/> <location filename="../src/modules/tracking/TrackingJobs.cpp" line="122"/>
<source>KDE user feedback</source> <source>KDE user feedback</source>
<translation type="unfinished"/> <translation>KDE </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="128"/> <location filename="../src/modules/tracking/TrackingJobs.cpp" line="128"/>
<source>Configuring KDE user feedback.</source> <source>Configuring KDE user feedback.</source>
<translation type="unfinished"/> <translation>KDE .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="150"/> <location filename="../src/modules/tracking/TrackingJobs.cpp" line="150"/>
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="156"/> <location filename="../src/modules/tracking/TrackingJobs.cpp" line="156"/>
<source>Error in KDE user feedback configuration.</source> <source>Error in KDE user feedback configuration.</source>
<translation type="unfinished"/> <translation>KDE .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/tracking/TrackingJobs.cpp" line="151"/> <location filename="../src/modules/tracking/TrackingJobs.cpp" line="151"/>
@ -3744,7 +3744,7 @@ Output:
<message> <message>
<location filename="../src/modules/welcome/WelcomePage.cpp" line="216"/> <location filename="../src/modules/welcome/WelcomePage.cpp" line="216"/>
<source>&lt;h1&gt;Welcome to the Calamares setup program for %1.&lt;/h1&gt;</source> <source>&lt;h1&gt;Welcome to the Calamares setup program for %1.&lt;/h1&gt;</source>
<translation>&lt;h1&gt;%1 Calamares .&lt;/h1&gt;</translation> <translation>&lt;h1&gt;%1 .&lt;/h1&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/welcome/WelcomePage.cpp" line="217"/> <location filename="../src/modules/welcome/WelcomePage.cpp" line="217"/>
@ -3754,7 +3754,7 @@ Output:
<message> <message>
<location filename="../src/modules/welcome/WelcomePage.cpp" line="222"/> <location filename="../src/modules/welcome/WelcomePage.cpp" line="222"/>
<source>&lt;h1&gt;Welcome to the Calamares installer for %1.&lt;/h1&gt;</source> <source>&lt;h1&gt;Welcome to the Calamares installer for %1.&lt;/h1&gt;</source>
<translation>&lt;h1&gt;%1 Calamares .&lt;/h1&gt;</translation> <translation>&lt;h1&gt;%1 .&lt;/h1&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/welcome/WelcomePage.cpp" line="223"/> <location filename="../src/modules/welcome/WelcomePage.cpp" line="223"/>
@ -3877,7 +3877,7 @@ Output:
<message> <message>
<location filename="../src/modules/keyboardq/keyboardq.qml" line="276"/> <location filename="../src/modules/keyboardq/keyboardq.qml" line="276"/>
<source>Keyboard Variant</source> <source>Keyboard Variant</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/keyboardq/keyboardq.qml" line="386"/> <location filename="../src/modules/keyboardq/keyboardq.qml" line="386"/>
@ -3890,7 +3890,7 @@ Output:
<message> <message>
<location filename="../src/modules/localeq/localeq.qml" line="81"/> <location filename="../src/modules/localeq/localeq.qml" line="81"/>
<source>Change</source> <source>Change</source>
<translation type="unfinished"/> <translation></translation>
</message> </message>
</context> </context>
<context> <context>
@ -3941,7 +3941,7 @@ Output:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="36"/> <location filename="../src/modules/usersq/usersq.qml" line="36"/>
<source>Pick your user name and credentials to login and perform admin tasks</source> <source>Pick your user name and credentials to login and perform admin tasks</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="52"/> <location filename="../src/modules/usersq/usersq.qml" line="52"/>
@ -3961,7 +3961,7 @@ Output:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="87"/> <location filename="../src/modules/usersq/usersq.qml" line="87"/>
<source>Login Name</source> <source>Login Name</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="103"/> <location filename="../src/modules/usersq/usersq.qml" line="103"/>
@ -4006,7 +4006,7 @@ Output:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="216"/> <location filename="../src/modules/usersq/usersq.qml" line="216"/>
<source>Validate passwords quality</source> <source>Validate passwords quality</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="226"/> <location filename="../src/modules/usersq/usersq.qml" line="226"/>
@ -4016,12 +4016,12 @@ Output:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="234"/> <location filename="../src/modules/usersq/usersq.qml" line="234"/>
<source>Log in automatically without asking for the password</source> <source>Log in automatically without asking for the password</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="243"/> <location filename="../src/modules/usersq/usersq.qml" line="243"/>
<source>Reuse user password as root password</source> <source>Reuse user password as root password</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="253"/> <location filename="../src/modules/usersq/usersq.qml" line="253"/>
@ -4031,22 +4031,22 @@ Output:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="268"/> <location filename="../src/modules/usersq/usersq.qml" line="268"/>
<source>Choose a root password to keep your account safe.</source> <source>Choose a root password to keep your account safe.</source>
<translation type="unfinished"/> <translation> .</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="279"/> <location filename="../src/modules/usersq/usersq.qml" line="279"/>
<source>Root Password</source> <source>Root Password</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="298"/> <location filename="../src/modules/usersq/usersq.qml" line="298"/>
<source>Repeat Root Password</source> <source>Repeat Root Password</source>
<translation type="unfinished"/> <translation> </translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="318"/> <location filename="../src/modules/usersq/usersq.qml" line="318"/>
<source>Enter the same password twice, so that it can be checked for typing errors.</source> <source>Enter the same password twice, so that it can be checked for typing errors.</source>
<translation type="unfinished"/> <translation> .</translation>
</message> </message>
</context> </context>
<context> <context>
@ -4060,7 +4060,7 @@ Output:
<message> <message>
<location filename="../src/modules/welcomeq/welcomeq.qml" line="66"/> <location filename="../src/modules/welcomeq/welcomeq.qml" line="66"/>
<source>About</source> <source>About</source>
<translation>Calamares</translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../src/modules/welcomeq/welcomeq.qml" line="80"/> <location filename="../src/modules/welcomeq/welcomeq.qml" line="80"/>

View File

@ -2870,7 +2870,7 @@ Išvestis:
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="83"/>
<source>Directory not found</source> <source>Directory not found</source>
<translation type="unfinished"/> <translation>Katalogas nerastas</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/> <location filename="../src/modules/machineid/MachineIdJob.cpp" line="84"/>
@ -3885,7 +3885,7 @@ Išvestis:
<message> <message>
<location filename="../src/modules/keyboardq/keyboardq.qml" line="276"/> <location filename="../src/modules/keyboardq/keyboardq.qml" line="276"/>
<source>Keyboard Variant</source> <source>Keyboard Variant</source>
<translation type="unfinished"/> <translation>Klaviatūros variantas</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/keyboardq/keyboardq.qml" line="386"/> <location filename="../src/modules/keyboardq/keyboardq.qml" line="386"/>
@ -3989,7 +3989,7 @@ Išvestis:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="87"/> <location filename="../src/modules/usersq/usersq.qml" line="87"/>
<source>Login Name</source> <source>Login Name</source>
<translation type="unfinished"/> <translation>Prisijungimo vardas</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="103"/> <location filename="../src/modules/usersq/usersq.qml" line="103"/>
@ -4009,7 +4009,7 @@ Išvestis:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="140"/> <location filename="../src/modules/usersq/usersq.qml" line="140"/>
<source>This name will be used if you make the computer visible to others on a network.</source> <source>This name will be used if you make the computer visible to others on a network.</source>
<translation type="unfinished"/> <translation>Šis vardas bus naudojamas, jeigu padarysite savo kompiuterį matomą kitiems naudotojams tinkle.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="155"/> <location filename="../src/modules/usersq/usersq.qml" line="155"/>
@ -4074,7 +4074,7 @@ Išvestis:
<message> <message>
<location filename="../src/modules/usersq/usersq.qml" line="318"/> <location filename="../src/modules/usersq/usersq.qml" line="318"/>
<source>Enter the same password twice, so that it can be checked for typing errors.</source> <source>Enter the same password twice, so that it can be checked for typing errors.</source>
<translation type="unfinished"/> <translation>Norint įsitikinti, kad rašydami slaptažodį nesuklydote, įrašykite patį slaptažodį du kartus.</translation>
</message> </message>
</context> </context>
<context> <context>

View File

@ -206,6 +206,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 ""
"Ekran menecerləri siyahısı həm qlobal yaddaşda, həm də displaymanager.conf-"
"da boşdur və ya təyin olunmamışdır."
#: src/modules/displaymanager/main.py:977 #: src/modules/displaymanager/main.py:977
msgid "Display manager configuration was incomplete" msgid "Display manager configuration was incomplete"

View File

@ -206,6 +206,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 ""
"Ekran menecerləri siyahısı həm qlobal yaddaşda, həm də displaymanager.conf-"
"da boşdur və ya təyin olunmamışdır."
#: src/modules/displaymanager/main.py:977 #: src/modules/displaymanager/main.py:977
msgid "Display manager configuration was incomplete" msgid "Display manager configuration was incomplete"
@ -318,11 +320,11 @@ msgstr "Aparat saatını ayarlamaq."
#: src/modules/mkinitfs/main.py:27 #: src/modules/mkinitfs/main.py:27
msgid "Creating initramfs with mkinitfs." msgid "Creating initramfs with mkinitfs."
msgstr "mkinitfs ilə initramfs yaradılır." msgstr "mkinitfs ilə initramfs yaradılır"
#: 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 "Hədəfdə dracut başladılmadı" msgstr "Hədəfdə mkinitfs başlatmaq baş tutmadı"
#: src/modules/mkinitfs/main.py:50 src/modules/dracut/main.py:50 #: src/modules/mkinitfs/main.py:50 src/modules/dracut/main.py:50
msgid "The exit code was {}" msgid "The exit code was {}"

View File

@ -4,7 +4,7 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# 김지현 <potatogim@potatogim.net>, 2018 # Ji-Hyeon Gim <potatogim@potatogim.net>, 2018
# JungHee Lee <daemul72@gmail.com>, 2020 # JungHee Lee <daemul72@gmail.com>, 2020
# #
#, fuzzy #, fuzzy

2617
lang/tz_vi.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -249,6 +249,7 @@ JobQueue::~JobQueue()
} }
delete m_storage; delete m_storage;
s_instance = nullptr;
} }

View File

@ -161,7 +161,10 @@ def modify_mkinitcpio_conf(partitions, root_mount_point):
hooks.append("usr") hooks.append("usr")
if encrypt_hook: if encrypt_hook:
hooks.append("encrypt") if detect_plymouth():
hooks.append("plymouth-encrypt")
else:
hooks.append("encrypt")
if not unencrypted_separate_boot and \ if not unencrypted_separate_boot and \
os.path.isfile( os.path.isfile(
os.path.join(root_mount_point, "crypto_keyfile.bin") os.path.join(root_mount_point, "crypto_keyfile.bin")

View File

@ -179,6 +179,8 @@ SetKeyboardLayoutJob::findLegacyKeymap() const
bool bool
SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const QString& convertedKeymapPath ) const SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const QString& convertedKeymapPath ) const
{ {
cDebug() << "Writing vconsole data to" << vconsoleConfPath;
QString keymap = findConvertedKeymap( convertedKeymapPath ); QString keymap = findConvertedKeymap( convertedKeymapPath );
if ( keymap.isEmpty() ) if ( keymap.isEmpty() )
{ {
@ -205,15 +207,20 @@ SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const
file.close(); file.close();
if ( stream.status() != QTextStream::Ok ) if ( stream.status() != QTextStream::Ok )
{ {
cError() << "Could not read lines from" << file.fileName();
return false; return false;
} }
} }
// Write out the existing lines and replace the KEYMAP= line // Write out the existing lines and replace the KEYMAP= line
file.open( QIODevice::WriteOnly | QIODevice::Text ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
cError() << "Could not open" << file.fileName() << "for writing.";
return false;
}
QTextStream stream( &file ); QTextStream stream( &file );
bool found = false; bool found = false;
foreach ( const QString& existingLine, existingLines ) for ( const QString& existingLine : qAsConst( existingLines ) )
{ {
if ( existingLine.trimmed().startsWith( "KEYMAP=" ) ) if ( existingLine.trimmed().startsWith( "KEYMAP=" ) )
{ {
@ -233,7 +240,7 @@ SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const
stream.flush(); stream.flush();
file.close(); file.close();
cDebug() << "Written KEYMAP=" << keymap << "to vconsole.conf"; cDebug() << Logger::SubEntry << "Written KEYMAP=" << keymap << "to vconsole.conf" << stream.status();
return ( stream.status() == QTextStream::Ok ); return ( stream.status() == QTextStream::Ok );
} }
@ -242,8 +249,14 @@ SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const
bool bool
SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const
{ {
cDebug() << "Writing X11 configuration to" << keyboardConfPath;
QFile file( keyboardConfPath ); QFile file( keyboardConfPath );
file.open( QIODevice::WriteOnly | QIODevice::Text ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
cError() << "Could not open" << file.fileName() << "for writing.";
return false;
}
QTextStream stream( &file ); QTextStream stream( &file );
stream << "# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" stream << "# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
@ -287,8 +300,8 @@ SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const
file.close(); file.close();
cDebug() << "Written XkbLayout" << m_layout << "; XkbModel" << m_model << "; XkbVariant" << m_variant cDebug() << Logger::SubEntry << "Written XkbLayout" << m_layout << "; XkbModel" << m_model << "; XkbVariant"
<< "to X.org file" << keyboardConfPath; << m_variant << "to X.org file" << keyboardConfPath << stream.status();
return ( stream.status() == QTextStream::Ok ); return ( stream.status() == QTextStream::Ok );
} }
@ -297,8 +310,14 @@ SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const
bool bool
SetKeyboardLayoutJob::writeDefaultKeyboardData( const QString& defaultKeyboardPath ) const SetKeyboardLayoutJob::writeDefaultKeyboardData( const QString& defaultKeyboardPath ) const
{ {
cDebug() << "Writing default keyboard data to" << defaultKeyboardPath;
QFile file( defaultKeyboardPath ); QFile file( defaultKeyboardPath );
file.open( QIODevice::WriteOnly | QIODevice::Text ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
cError() << "Could not open" << defaultKeyboardPath << "for writing";
return false;
}
QTextStream stream( &file ); QTextStream stream( &file );
stream << "# KEYBOARD CONFIGURATION FILE\n\n" stream << "# KEYBOARD CONFIGURATION FILE\n\n"
@ -313,8 +332,8 @@ SetKeyboardLayoutJob::writeDefaultKeyboardData( const QString& defaultKeyboardPa
file.close(); file.close();
cDebug() << "Written XKBMODEL" << m_model << "; XKBLAYOUT" << m_layout << "; XKBVARIANT" << m_variant cDebug() << Logger::SubEntry << "Written XKBMODEL" << m_model << "; XKBLAYOUT" << m_layout << "; XKBVARIANT"
<< "to /etc/default/keyboard file" << defaultKeyboardPath; << m_variant << "to /etc/default/keyboard file" << defaultKeyboardPath << stream.status();
return ( stream.status() == QTextStream::Ok ); return ( stream.status() == QTextStream::Ok );
} }
@ -329,60 +348,72 @@ SetKeyboardLayoutJob::exec()
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
QDir destDir( gs->value( "rootMountPoint" ).toString() ); QDir destDir( gs->value( "rootMountPoint" ).toString() );
// Get the path to the destination's /etc/vconsole.conf
QString vconsoleConfPath = destDir.absoluteFilePath( "etc/vconsole.conf" );
// Get the path to the destination's /etc/X11/xorg.conf.d/00-keyboard.conf
QString xorgConfDPath;
QString keyboardConfPath;
if ( QDir::isAbsolutePath( m_xOrgConfFileName ) )
{ {
keyboardConfPath = m_xOrgConfFileName; // Get the path to the destination's /etc/vconsole.conf
while ( keyboardConfPath.startsWith( '/' ) ) QString vconsoleConfPath = destDir.absoluteFilePath( "etc/vconsole.conf" );
// Get the path to the destination's path to the converted key mappings
QString convertedKeymapPath = m_convertedKeymapPath;
if ( !convertedKeymapPath.isEmpty() )
{ {
keyboardConfPath.remove( 0, 1 ); while ( convertedKeymapPath.startsWith( '/' ) )
{
convertedKeymapPath.remove( 0, 1 );
}
convertedKeymapPath = destDir.absoluteFilePath( convertedKeymapPath );
} }
keyboardConfPath = destDir.absoluteFilePath( keyboardConfPath );
xorgConfDPath = QFileInfo( keyboardConfPath ).path();
}
else
{
xorgConfDPath = destDir.absoluteFilePath( "etc/X11/xorg.conf.d" );
keyboardConfPath = QDir( xorgConfDPath ).absoluteFilePath( m_xOrgConfFileName );
}
destDir.mkpath( xorgConfDPath );
QString defaultKeyboardPath; if ( !writeVConsoleData( vconsoleConfPath, convertedKeymapPath ) )
if ( QDir( destDir.absoluteFilePath( "etc/default" ) ).exists() )
{
defaultKeyboardPath = destDir.absoluteFilePath( "etc/default/keyboard" );
}
// Get the path to the destination's path to the converted key mappings
QString convertedKeymapPath = m_convertedKeymapPath;
if ( !convertedKeymapPath.isEmpty() )
{
while ( convertedKeymapPath.startsWith( '/' ) )
{ {
convertedKeymapPath.remove( 0, 1 ); return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for the virtual console." ),
tr( "Failed to write to %1" ).arg( vconsoleConfPath ) );
} }
convertedKeymapPath = destDir.absoluteFilePath( convertedKeymapPath );
} }
if ( !writeVConsoleData( vconsoleConfPath, convertedKeymapPath ) )
return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for the virtual console." ),
tr( "Failed to write to %1" ).arg( vconsoleConfPath ) );
if ( !writeX11Data( keyboardConfPath ) )
return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for X11." ),
tr( "Failed to write to %1" ).arg( keyboardConfPath ) );
if ( !defaultKeyboardPath.isEmpty() && m_writeEtcDefaultKeyboard )
{ {
if ( !writeDefaultKeyboardData( defaultKeyboardPath ) ) // Get the path to the destination's /etc/X11/xorg.conf.d/00-keyboard.conf
return Calamares::JobResult::error( QString xorgConfDPath;
tr( "Failed to write keyboard configuration to existing /etc/default directory." ), QString keyboardConfPath;
tr( "Failed to write to %1" ).arg( keyboardConfPath ) ); if ( QDir::isAbsolutePath( m_xOrgConfFileName ) )
{
keyboardConfPath = m_xOrgConfFileName;
while ( keyboardConfPath.startsWith( '/' ) )
{
keyboardConfPath.remove( 0, 1 );
}
keyboardConfPath = destDir.absoluteFilePath( keyboardConfPath );
xorgConfDPath = QFileInfo( keyboardConfPath ).path();
}
else
{
xorgConfDPath = destDir.absoluteFilePath( "etc/X11/xorg.conf.d" );
keyboardConfPath = QDir( xorgConfDPath ).absoluteFilePath( m_xOrgConfFileName );
}
destDir.mkpath( xorgConfDPath );
if ( !writeX11Data( keyboardConfPath ) )
{
return Calamares::JobResult::error( tr( "Failed to write keyboard configuration for X11." ),
tr( "Failed to write to %1" ).arg( keyboardConfPath ) );
}
}
{
QString defaultKeyboardPath;
if ( QDir( destDir.absoluteFilePath( "etc/default" ) ).exists() )
{
defaultKeyboardPath = destDir.absoluteFilePath( "etc/default/keyboard" );
}
if ( !defaultKeyboardPath.isEmpty() && m_writeEtcDefaultKeyboard )
{
if ( !writeDefaultKeyboardData( defaultKeyboardPath ) )
{
return Calamares::JobResult::error(
tr( "Failed to write keyboard configuration to existing /etc/default directory." ),
tr( "Failed to write to %1" ).arg( defaultKeyboardPath ) );
}
}
} }
return Calamares::JobResult::ok(); return Calamares::JobResult::ok();

View File

@ -7,6 +7,7 @@
# SPDX-FileCopyrightText: 2017 Alf Gaida <agaida@siduction.org> # SPDX-FileCopyrightText: 2017 Alf Gaida <agaida@siduction.org>
# SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> # SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
# SPDX-FileCopyrightText: 2019 Kevin Kofler <kevin.kofler@chello.at> # SPDX-FileCopyrightText: 2019 Kevin Kofler <kevin.kofler@chello.at>
# SPDX-FileCopyrightText: 2019-2020 Collabora Ltd
# 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.
@ -38,6 +39,9 @@ def mount_partition(root_mount_point, partition, partitions):
# Create mount point with `+` rather than `os.path.join()` because # Create mount point with `+` rather than `os.path.join()` because
# `partition["mountPoint"]` starts with a '/'. # `partition["mountPoint"]` starts with a '/'.
raw_mount_point = partition["mountPoint"] raw_mount_point = partition["mountPoint"]
if not raw_mount_point:
return
mount_point = root_mount_point + raw_mount_point mount_point = root_mount_point + raw_mount_point
# Ensure that the created directory has the correct SELinux context on # Ensure that the created directory has the correct SELinux context on
@ -52,26 +56,22 @@ def mount_partition(root_mount_point, partition, partitions):
raise raise
fstype = partition.get("fs", "").lower() fstype = partition.get("fs", "").lower()
if not fstype or fstype == "unformatted":
return
if fstype == "fat16" or fstype == "fat32": if fstype == "fat16" or fstype == "fat32":
fstype = "vfat" fstype = "vfat"
if "luksMapperName" in partition: device = partition["device"]
libcalamares.utils.debug(
"about to mount {!s}".format(partition["luksMapperName"]))
libcalamares.utils.mount(
"/dev/mapper/{!s}".format(partition["luksMapperName"]),
mount_point,
fstype,
partition.get("options", ""),
)
else: if "luksMapperName" in partition:
libcalamares.utils.mount(partition["device"], device = os.path.join("/dev/mapper", partition["luksMapperName"])
mount_point,
fstype, if libcalamares.utils.mount(device,
partition.get("options", ""), mount_point,
) fstype,
partition.get("options", "")) != 0:
libcalamares.utils.warning("Cannot mount {}".format(device))
# If the root partition is btrfs, we create a subvolume "@" # If the root partition is btrfs, we create a subvolume "@"
# for the root mount point. # for the root mount point.
@ -96,37 +96,23 @@ def mount_partition(root_mount_point, partition, partitions):
subprocess.check_call(["umount", "-v", root_mount_point]) subprocess.check_call(["umount", "-v", root_mount_point])
device = partition["device"]
if "luksMapperName" in partition: if "luksMapperName" in partition:
libcalamares.utils.mount( device = os.path.join("/dev/mapper", partition["luksMapperName"])
"/dev/mapper/{!s}".format(partition["luksMapperName"]),
mount_point, if libcalamares.utils.mount(device,
fstype, mount_point,
",".join( fstype,
["subvol=@", partition.get("options", "")]), ",".join(["subvol=@", partition.get("options", "")])) != 0:
) libcalamares.utils.warning("Cannot mount {}".format(device))
if not has_home_mount_point:
libcalamares.utils.mount( if not has_home_mount_point:
"/dev/mapper/{!s}".format(partition["luksMapperName"]), if libcalamares.utils.mount(device,
root_mount_point + "/home", root_mount_point + "/home",
fstype, fstype,
",".join( ",".join(["subvol=@home", partition.get("options", "")])) != 0:
["subvol=@home", partition.get("options", "")]), libcalamares.utils.warning("Cannot mount {}".format(device))
)
else:
libcalamares.utils.mount(
partition["device"],
mount_point,
fstype,
",".join(["subvol=@", partition.get("options", "")]),
)
if not has_home_mount_point:
libcalamares.utils.mount(
partition["device"],
root_mount_point + "/home",
fstype,
",".join(
["subvol=@home", partition.get("options", "")]),
)
def run(): def run():

View File

@ -439,11 +439,14 @@ isEfiSystem()
bool bool
isEfiBootable( const Partition* candidate ) isEfiBootable( const Partition* candidate )
{ {
cDebug() << "Check EFI bootable" << convenienceName( candidate ) << candidate->devicePath(); const auto flags = PartitionInfo::flags( candidate );
cDebug() << Logger::SubEntry << "flags" << candidate->activeFlags();
auto flags = PartitionInfo::flags( candidate );
// TODO: with KPMCore 4, this comment is wrong: the flags
// are remapped, and the ESP flag is the same as Boot.
#if defined( WITH_KPMCORE4API )
static_assert( KPM_PARTITION_FLAG_ESP == KPM_PARTITION_FLAG( Boot ), "KPMCore API enum changed" );
return flags.testFlag( KPM_PARTITION_FLAG_ESP );
#else
/* If bit 17 is set, old-style Esp flag, it's OK */ /* If bit 17 is set, old-style Esp flag, it's OK */
if ( flags.testFlag( KPM_PARTITION_FLAG_ESP ) ) if ( flags.testFlag( KPM_PARTITION_FLAG_ESP ) )
{ {
@ -455,19 +458,28 @@ isEfiBootable( const Partition* candidate )
while ( root && !root->isRoot() ) while ( root && !root->isRoot() )
{ {
root = root->parent(); root = root->parent();
cDebug() << Logger::SubEntry << "moved towards root" << Logger::Pointer( root );
} }
// Strange case: no root found, no partition table node? // Strange case: no root found, no partition table node?
if ( !root ) if ( !root )
{ {
cWarning() << "No root of partition table found.";
return false; return false;
} }
const PartitionTable* table = dynamic_cast< const PartitionTable* >( root ); const PartitionTable* table = dynamic_cast< const PartitionTable* >( root );
cDebug() << Logger::SubEntry << "partition table" << Logger::Pointer( table ) << "type" if ( !table )
<< ( table ? table->type() : PartitionTable::TableType::unknownTableType ); {
return table && ( table->type() == PartitionTable::TableType::gpt ) && flags.testFlag( KPM_PARTITION_FLAG( Boot ) ); cWarning() << "Root of partition table is not a PartitionTable object";
return false;
}
if ( table->type() == PartitionTable::TableType::gpt )
{
const auto bootFlag = KPM_PARTITION_FLAG( Boot );
return flags.testFlag( bootFlag );
}
return false;
#endif
} }
QString QString

View File

@ -97,21 +97,6 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
// empty and a EFI boot partition, while BIOS starts at // empty and a EFI boot partition, while BIOS starts at
// the 1MiB boundary (usually sector 2048). // the 1MiB boundary (usually sector 2048).
int empty_space_sizeB = isEfi ? 2_MiB : 1_MiB; int empty_space_sizeB = isEfi ? 2_MiB : 1_MiB;
int uefisys_part_sizeB = 0_MiB;
if ( isEfi )
{
if ( gs->contains( "efiSystemPartitionSize" ) )
{
CalamaresUtils::Partition::PartitionSize part_size
= CalamaresUtils::Partition::PartitionSize( gs->value( "efiSystemPartitionSize" ).toString() );
uefisys_part_sizeB = part_size.toBytes( dev->capacity() );
}
else
{
uefisys_part_sizeB = 300_MiB;
}
}
// Since sectors count from 0, if the space is 2048 sectors in size, // Since sectors count from 0, if the space is 2048 sectors in size,
// the first free sector has number 2048 (and there are 2048 sectors // the first free sector has number 2048 (and there are 2048 sectors
@ -128,6 +113,14 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
if ( isEfi ) if ( isEfi )
{ {
int uefisys_part_sizeB = 300_MiB;
if ( gs->contains( "efiSystemPartitionSize" ) )
{
CalamaresUtils::Partition::PartitionSize part_size
= CalamaresUtils::Partition::PartitionSize( gs->value( "efiSystemPartitionSize" ).toString() );
uefisys_part_sizeB = part_size.toBytes( dev->capacity() );
}
qint64 efiSectorCount = CalamaresUtils::bytesToSectors( uefisys_part_sizeB, dev->logicalSize() ); qint64 efiSectorCount = CalamaresUtils::bytesToSectors( uefisys_part_sizeB, dev->logicalSize() );
Q_ASSERT( efiSectorCount > 0 ); Q_ASSERT( efiSectorCount > 0 );
@ -203,6 +196,10 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
KPM_PARTITION_FLAG( None ) ); KPM_PARTITION_FLAG( None ) );
} }
PartitionInfo::setFormat( swapPartition, true ); PartitionInfo::setFormat( swapPartition, true );
if ( gs->contains( "swapPartitionName" ))
{
swapPartition->setLabel( gs->value( "swapPartitionName" ).toString() );
}
core->createPartition( dev, swapPartition ); core->createPartition( dev, swapPartition );
} }

View File

@ -716,6 +716,8 @@ PartitionCoreModule::updateIsDirty()
void void
PartitionCoreModule::scanForEfiSystemPartitions() PartitionCoreModule::scanForEfiSystemPartitions()
{ {
const bool wasEmpty = m_efiSystemPartitions.isEmpty();
m_efiSystemPartitions.clear(); m_efiSystemPartitions.clear();
QList< Device* > devices; QList< Device* > devices;
@ -732,6 +734,11 @@ PartitionCoreModule::scanForEfiSystemPartitions()
{ {
cWarning() << "system is EFI but no EFI system partitions found."; cWarning() << "system is EFI but no EFI system partitions found.";
} }
else if ( wasEmpty )
{
// But it isn't empty anymore, so whatever problem has been solved
cDebug() << "system is EFI and new EFI system partition has been found.";
}
m_efiSystemPartitions = efiSystemPartitions; m_efiSystemPartitions = efiSystemPartitions;
} }
@ -861,9 +868,9 @@ PartitionCoreModule::setBootLoaderInstallPath( const QString& path )
} }
void void
PartitionCoreModule::initLayout( const QVariantList& config ) PartitionCoreModule::initLayout( FileSystem::Type defaultFsType, const QVariantList& config )
{ {
m_partLayout.init( config ); m_partLayout.init( defaultFsType, config );
} }
void void
@ -875,7 +882,8 @@ PartitionCoreModule::layoutApply( Device* dev,
const PartitionRole& role ) const PartitionRole& role )
{ {
bool isEfi = PartUtils::isEfiSystem(); bool isEfi = PartUtils::isEfiSystem();
QList< Partition* > partList = m_partLayout.createPartitions( dev, firstSector, lastSector, luksPassphrase, parent, role ); QList< Partition* > partList
= m_partLayout.createPartitions( dev, firstSector, lastSector, luksPassphrase, parent, role );
// Partition::mountPoint() tells us where it is mounted **now**, while // Partition::mountPoint() tells us where it is mounted **now**, while
// PartitionInfo::mountPoint() says where it will be mounted in the target system. // PartitionInfo::mountPoint() says where it will be mounted in the target system.

View File

@ -156,7 +156,11 @@ public:
/// @brief Set the path where the bootloader will be installed /// @brief Set the path where the bootloader will be installed
void setBootLoaderInstallPath( const QString& path ); void setBootLoaderInstallPath( const QString& path );
void initLayout( const QVariantList& config = QVariantList() ); /** @brief Initialize the default layout that will be applied
*
* See PartitionLayout::init()
*/
void initLayout( FileSystem::Type defaultFsType, const QVariantList& config = QVariantList() );
void layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase ); void layoutApply( Device* dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase );
void layoutApply( Device* dev, void layoutApply( Device* dev,

View File

@ -52,7 +52,15 @@ PartitionTable::Flags
flags( const Partition* partition ) flags( const Partition* partition )
{ {
auto v = partition->property( FLAGS_PROPERTY ); auto v = partition->property( FLAGS_PROPERTY );
if ( v.type() == QVariant::Int ) if ( !v.isValid() )
{
return partition->activeFlags();
}
// The underlying type of PartitionTable::Flags can be int or uint
// (see qflags.h) and so setting those flags can create a QVariant
// of those types; we don't just want to check QVariant::canConvert()
// here because that will also accept QByteArray and some other things.
if ( v.type() == QVariant::Int || v.type() == QVariant::UInt )
{ {
return static_cast< PartitionTable::Flags >( v.toInt() ); return static_cast< PartitionTable::Flags >( v.toInt() );
} }

View File

@ -27,32 +27,10 @@
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
#include <kpmcore/fs/filesystem.h> #include <kpmcore/fs/filesystem.h>
static FileSystem::Type PartitionLayout::PartitionLayout() {}
getDefaultFileSystemType()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
FileSystem::Type defaultFS = FileSystem::Ext4;
if ( gs->contains( "defaultFileSystemType" ) )
{
PartUtils::findFS( gs->value( "defaultFileSystemType" ).toString(), &defaultFS );
if ( defaultFS == FileSystem::Unknown )
{
defaultFS = FileSystem::Ext4;
}
}
return defaultFS;
}
PartitionLayout::PartitionLayout()
: m_defaultFsType( getDefaultFileSystemType() )
{
}
PartitionLayout::PartitionLayout( const PartitionLayout& layout ) PartitionLayout::PartitionLayout( const PartitionLayout& layout )
: m_defaultFsType( layout.m_defaultFsType ) : m_partLayout( layout.m_partLayout )
, m_partLayout( layout.m_partLayout )
{ {
} }
@ -63,9 +41,14 @@ PartitionLayout::PartitionEntry::PartitionEntry()
{ {
} }
PartitionLayout::PartitionEntry::PartitionEntry( const QString& mountPoint, const QString& size, const QString& minSize, const QString& maxSize ) PartitionLayout::PartitionEntry::PartitionEntry( FileSystem::Type fs,
const QString& mountPoint,
const QString& size,
const QString& minSize,
const QString& maxSize )
: partAttributes( 0 ) : partAttributes( 0 )
, partMountPoint( mountPoint ) , partMountPoint( mountPoint )
, partFileSystem( fs )
, partSize( size ) , partSize( size )
, partMinSize( minSize ) , partMinSize( minSize )
, partMaxSize( maxSize ) , partMaxSize( maxSize )
@ -95,20 +78,6 @@ PartitionLayout::PartitionEntry::PartitionEntry( const QString& label,
PartUtils::findFS( fs, &partFileSystem ); PartUtils::findFS( fs, &partFileSystem );
} }
PartitionLayout::PartitionEntry::PartitionEntry( const PartitionEntry& e )
: partLabel( e.partLabel )
, partUUID( e.partUUID )
, partType( e.partType )
, partAttributes( e.partAttributes )
, partMountPoint( e.partMountPoint )
, partFileSystem( e.partFileSystem )
, partFeatures( e.partFeatures )
, partSize( e.partSize )
, partMinSize( e.partMinSize )
, partMaxSize( e.partMaxSize )
{
}
bool bool
PartitionLayout::addEntry( const PartitionEntry& entry ) PartitionLayout::addEntry( const PartitionEntry& entry )
@ -124,7 +93,7 @@ PartitionLayout::addEntry( const PartitionEntry& entry )
} }
void void
PartitionLayout::init( const QVariantList& config ) PartitionLayout::init( FileSystem::Type defaultFsType, const QVariantList& config )
{ {
bool ok; bool ok;
@ -134,8 +103,7 @@ PartitionLayout::init( const QVariantList& config )
{ {
QVariantMap pentry = r.toMap(); QVariantMap pentry = r.toMap();
if ( !pentry.contains( "name" ) || !pentry.contains( "mountPoint" ) || !pentry.contains( "filesystem" ) if ( !pentry.contains( "name" ) || !pentry.contains( "size" ) )
|| !pentry.contains( "size" ) )
{ {
cError() << "Partition layout entry #" << config.indexOf( r ) cError() << "Partition layout entry #" << config.indexOf( r )
<< "lacks mandatory attributes, switching to default layout."; << "lacks mandatory attributes, switching to default layout.";
@ -148,7 +116,7 @@ PartitionLayout::init( const QVariantList& config )
CalamaresUtils::getString( pentry, "type" ), CalamaresUtils::getString( pentry, "type" ),
CalamaresUtils::getUnsignedInteger( pentry, "attributes", 0 ), CalamaresUtils::getUnsignedInteger( pentry, "attributes", 0 ),
CalamaresUtils::getString( pentry, "mountPoint" ), CalamaresUtils::getString( pentry, "mountPoint" ),
CalamaresUtils::getString( pentry, "filesystem" ), CalamaresUtils::getString( pentry, "filesystem", "unformatted" ),
CalamaresUtils::getSubMap( pentry, "features", ok ), CalamaresUtils::getSubMap( pentry, "features", ok ),
CalamaresUtils::getString( pentry, "size", QStringLiteral( "0" ) ), CalamaresUtils::getString( pentry, "size", QStringLiteral( "0" ) ),
CalamaresUtils::getString( pentry, "minSize", QStringLiteral( "0" ) ), CalamaresUtils::getString( pentry, "minSize", QStringLiteral( "0" ) ),
@ -162,7 +130,7 @@ PartitionLayout::init( const QVariantList& config )
if ( !m_partLayout.count() ) if ( !m_partLayout.count() )
{ {
addEntry( { QString( "/" ), QString( "100%" ) } ); addEntry( { defaultFsType, QString( "/" ), QString( "100%" ) } );
} }
} }
@ -228,8 +196,8 @@ PartitionLayout::createPartitions( Device* dev,
{ {
if ( entry.partSize.unit() == CalamaresUtils::Partition::SizeUnit::Percent ) if ( entry.partSize.unit() == CalamaresUtils::Partition::SizeUnit::Percent )
{ {
qint64 sectors = entry.partSize.toSectors( availableSectors + partSectorsMap.value( &entry ), qint64 sectors
dev->logicalSize() ); = entry.partSize.toSectors( availableSectors + partSectorsMap.value( &entry ), dev->logicalSize() );
if ( entry.partMinSize.isValid() ) if ( entry.partMinSize.isValid() )
{ {
sectors = std::max( sectors, entry.partMinSize.toSectors( totalSectors, dev->logicalSize() ) ); sectors = std::max( sectors, entry.partMinSize.toSectors( totalSectors, dev->logicalSize() ) );
@ -258,13 +226,24 @@ PartitionLayout::createPartitions( Device* dev,
Partition* part = nullptr; Partition* part = nullptr;
if ( luksPassphrase.isEmpty() ) if ( luksPassphrase.isEmpty() )
{ {
part = KPMHelpers::createNewPartition( part = KPMHelpers::createNewPartition( parent,
parent, *dev, role, entry.partFileSystem, currentSector, currentSector + sectors - 1, KPM_PARTITION_FLAG( None ) ); *dev,
role,
entry.partFileSystem,
currentSector,
currentSector + sectors - 1,
KPM_PARTITION_FLAG( None ) );
} }
else else
{ {
part = KPMHelpers::createNewEncryptedPartition( part = KPMHelpers::createNewEncryptedPartition( parent,
parent, *dev, role, entry.partFileSystem, currentSector, currentSector + sectors - 1, luksPassphrase, KPM_PARTITION_FLAG( None ) ); *dev,
role,
entry.partFileSystem,
currentSector,
currentSector + sectors - 1,
luksPassphrase,
KPM_PARTITION_FLAG( None ) );
} }
PartitionInfo::setFormat( part, true ); PartitionInfo::setFormat( part, true );
PartitionInfo::setMountPoint( part, entry.partMountPoint ); PartitionInfo::setMountPoint( part, entry.partMountPoint );

View File

@ -34,7 +34,7 @@ public:
QString partLabel; QString partLabel;
QString partUUID; QString partUUID;
QString partType; QString partType;
quint64 partAttributes; quint64 partAttributes = 0;
QString partMountPoint; QString partMountPoint;
FileSystem::Type partFileSystem = FileSystem::Unknown; FileSystem::Type partFileSystem = FileSystem::Unknown;
QVariantMap partFeatures; QVariantMap partFeatures;
@ -44,8 +44,13 @@ public:
/// @brief All-zeroes PartitionEntry /// @brief All-zeroes PartitionEntry
PartitionEntry(); PartitionEntry();
/// @brief Parse @p mountPoint, @p size, @p minSize and @p maxSize to their respective member variables /** @brief Parse @p mountPoint, @p size, @p minSize and @p maxSize to their respective member variables
PartitionEntry( const QString& mountPoint, *
* Sets a specific FS type (not parsed from string like the other
* constructor).
*/
PartitionEntry( FileSystem::Type fs,
const QString& mountPoint,
const QString& size, const QString& size,
const QString& minSize = QString(), const QString& minSize = QString(),
const QString& maxSize = QString() ); const QString& maxSize = QString() );
@ -61,7 +66,7 @@ public:
const QString& minSize = QString(), const QString& minSize = QString(),
const QString& maxSize = QString() ); const QString& maxSize = QString() );
/// @brief Copy PartitionEntry /// @brief Copy PartitionEntry
PartitionEntry( const PartitionEntry& e ); PartitionEntry( const PartitionEntry& e ) = default;
bool isValid() const bool isValid() const
{ {
@ -78,7 +83,13 @@ public:
PartitionLayout( const PartitionLayout& layout ); PartitionLayout( const PartitionLayout& layout );
~PartitionLayout(); ~PartitionLayout();
void init( const QVariantList& config ); /** @brief create the configuration from @p config
*
* @p config is a list of partition entries (in QVariant form,
* read from YAML). If no entries are given, then a single
* partition is created with the given @p defaultFsType
*/
void init( FileSystem::Type defaultFsType, const QVariantList& config );
bool addEntry( const PartitionEntry& entry ); bool addEntry( const PartitionEntry& entry );
/** /**
@ -93,7 +104,6 @@ public:
const PartitionRole& role ); const PartitionRole& role );
private: private:
FileSystem::Type m_defaultFsType;
QList< PartitionEntry > m_partLayout; QList< PartitionEntry > m_partLayout;
}; };

View File

@ -136,8 +136,8 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
bool partResizedMoved = newFirstSector != m_partition->firstSector() || newLastSector != m_partition->lastSector(); bool partResizedMoved = newFirstSector != m_partition->firstSector() || newLastSector != m_partition->lastSector();
cDebug() << "old boundaries:" << m_partition->firstSector() << m_partition->lastSector() << m_partition->length(); cDebug() << "old boundaries:" << m_partition->firstSector() << m_partition->lastSector() << m_partition->length();
cDebug() << "new boundaries:" << newFirstSector << newLastSector; cDebug() << Logger::SubEntry << "new boundaries:" << newFirstSector << newLastSector;
cDebug() << "dirty status:" << m_partitionSizeController->isDirty(); cDebug() << Logger::SubEntry << "dirty status:" << m_partitionSizeController->isDirty();
FileSystem::Type fsType = FileSystem::Unknown; FileSystem::Type fsType = FileSystem::Unknown;
if ( m_ui->formatRadioButton->isChecked() ) if ( m_ui->formatRadioButton->isChecked() )
@ -147,6 +147,9 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
: FileSystem::typeForName( m_ui->fileSystemComboBox->currentText() ); : FileSystem::typeForName( m_ui->fileSystemComboBox->currentText() );
} }
const auto resultFlags = newFlags();
const auto currentFlags = PartitionInfo::flags( m_partition );
if ( partResizedMoved ) if ( partResizedMoved )
{ {
if ( m_ui->formatRadioButton->isChecked() ) if ( m_ui->formatRadioButton->isChecked() )
@ -157,20 +160,20 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
fsType, fsType,
newFirstSector, newFirstSector,
newLastSector, newLastSector,
newFlags() ); resultFlags );
PartitionInfo::setMountPoint( newPartition, PartitionInfo::mountPoint( m_partition ) ); PartitionInfo::setMountPoint( newPartition, PartitionInfo::mountPoint( m_partition ) );
PartitionInfo::setFormat( newPartition, true ); PartitionInfo::setFormat( newPartition, true );
core->deletePartition( m_device, m_partition ); core->deletePartition( m_device, m_partition );
core->createPartition( m_device, newPartition ); core->createPartition( m_device, newPartition );
core->setPartitionFlags( m_device, newPartition, newFlags() ); core->setPartitionFlags( m_device, newPartition, resultFlags );
} }
else else
{ {
core->resizePartition( m_device, m_partition, newFirstSector, newLastSector ); core->resizePartition( m_device, m_partition, newFirstSector, newLastSector );
if ( m_partition->activeFlags() != newFlags() ) if ( currentFlags != resultFlags )
{ {
core->setPartitionFlags( m_device, m_partition, newFlags() ); core->setPartitionFlags( m_device, m_partition, resultFlags );
} }
} }
} }
@ -183,9 +186,9 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
if ( m_partition->fileSystem().type() == fsType ) if ( m_partition->fileSystem().type() == fsType )
{ {
core->formatPartition( m_device, m_partition ); core->formatPartition( m_device, m_partition );
if ( m_partition->activeFlags() != newFlags() ) if ( currentFlags != resultFlags )
{ {
core->setPartitionFlags( m_device, m_partition, newFlags() ); core->setPartitionFlags( m_device, m_partition, resultFlags );
} }
} }
else // otherwise, we delete and recreate the partition with new fs type else // otherwise, we delete and recreate the partition with new fs type
@ -196,22 +199,22 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
fsType, fsType,
m_partition->firstSector(), m_partition->firstSector(),
m_partition->lastSector(), m_partition->lastSector(),
newFlags() ); resultFlags );
PartitionInfo::setMountPoint( newPartition, PartitionInfo::mountPoint( m_partition ) ); PartitionInfo::setMountPoint( newPartition, PartitionInfo::mountPoint( m_partition ) );
PartitionInfo::setFormat( newPartition, true ); PartitionInfo::setFormat( newPartition, true );
core->deletePartition( m_device, m_partition ); core->deletePartition( m_device, m_partition );
core->createPartition( m_device, newPartition ); core->createPartition( m_device, newPartition );
core->setPartitionFlags( m_device, newPartition, newFlags() ); core->setPartitionFlags( m_device, newPartition, resultFlags );
} }
} }
else else
{ {
core->refreshPartition( m_device, m_partition ); if ( currentFlags != resultFlags )
if ( m_partition->activeFlags() != newFlags() )
{ {
core->setPartitionFlags( m_device, m_partition, newFlags() ); core->setPartitionFlags( m_device, m_partition, resultFlags );
} }
core->refreshPartition( m_device, m_partition );
} }
} }
} }

View File

@ -543,6 +543,12 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
gs->insert( "efiSystemPartitionName", CalamaresUtils::getString( configurationMap, "efiSystemPartitionName" ) ); gs->insert( "efiSystemPartitionName", CalamaresUtils::getString( configurationMap, "efiSystemPartitionName" ) );
} }
// Read and parse key swapPartitionName
if ( configurationMap.contains( "swapPartitionName" ) )
{
gs->insert( "swapPartitionName", CalamaresUtils::getString( configurationMap, "swapPartitionName" ) );
}
// OTHER SETTINGS // OTHER SETTINGS
// //
gs->insert( "drawNestedPartitions", CalamaresUtils::getBool( configurationMap, "drawNestedPartitions", false ) ); gs->insert( "drawNestedPartitions", CalamaresUtils::getBool( configurationMap, "drawNestedPartitions", false ) );
@ -596,7 +602,8 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
QFuture< void > future = QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule ); QFuture< void > future = QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule );
m_future->setFuture( future ); m_future->setFuture( future );
m_core->initLayout( configurationMap.value( "partitionLayout" ).toList() ); m_core->initLayout( fsType == FileSystem::Unknown ? FileSystem::Ext4 : fsType,
configurationMap.value( "partitionLayout" ).toList() );
} }

View File

@ -105,7 +105,7 @@ mapForPartition( Partition* partition, const QString& uuid )
using TR = Logger::DebugRow< const char* const, const QString& >; using TR = Logger::DebugRow< const char* const, const QString& >;
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( "partuuid", map[ "partuuid" ].toString() )
<< TR( "parttype", map[ "partype" ].toString() ) << TR( "partattrs", map[ "partattrs" ].toString() ) << TR( "parttype", map[ "parttype" ].toString() ) << TR( "partattrs", map[ "partattrs" ].toString() )
<< TR( "mountPoint:", PartitionInfo::mountPoint( partition ) ) << TR( "fs:", map[ "fs" ].toString() ) << TR( "mountPoint:", PartitionInfo::mountPoint( partition ) ) << TR( "fs:", map[ "fs" ].toString() )
<< TR( "fsName", map[ "fsName" ].toString() ) << TR( "uuid", uuid ) << TR( "fsName", map[ "fsName" ].toString() ) << TR( "uuid", uuid )
<< TR( "claimed", map[ "claimed" ].toString() ); << TR( "claimed", map[ "claimed" ].toString() );
@ -153,7 +153,7 @@ FillGlobalStorageJob::prettyDescription() const
QString path = partitionMap.value( "device" ).toString(); QString path = partitionMap.value( "device" ).toString();
QString mountPoint = partitionMap.value( "mountPoint" ).toString(); QString mountPoint = partitionMap.value( "mountPoint" ).toString();
QString fsType = partitionMap.value( "fs" ).toString(); QString fsType = partitionMap.value( "fs" ).toString();
if ( mountPoint.isEmpty() || fsType.isEmpty() ) if ( mountPoint.isEmpty() || fsType.isEmpty() || fsType == QString( "unformatted" ) )
{ {
continue; continue;
} }

View File

@ -12,7 +12,8 @@ efiSystemPartition: "/boot/efi"
# If nothing is specified, the default size of 300MiB will be used. # If nothing is specified, the default size of 300MiB will be used.
# efiSystemPartitionSize: 300M # efiSystemPartitionSize: 300M
# This optional setting specifies the name of the EFI system partition. # This optional setting specifies the name of the EFI system partition (see
# PARTLABEL; gpt only; requires KPMCore >= 4.2.0).
# If nothing is specified, the partition name is left unset. # If nothing is specified, the partition name is left unset.
# efiSystemPartitionName: EFI # efiSystemPartitionName: EFI
@ -48,6 +49,11 @@ userSwapChoices:
# - reuse # Re-use existing swap, but don't create any (unsupported right now) # - reuse # Re-use existing swap, but don't create any (unsupported right now)
- file # To swap file instead of partition - file # To swap file instead of partition
# This optional setting specifies the name of the swap partition (see
# PARTLABEL; gpt only; requires KPMCore >= 4.2.0).
# If nothing is specified, the partition name is left unset.
# swapPartitionName: swap
# LEGACY SETTINGS (these will generate a warning) # LEGACY SETTINGS (these will generate a warning)
# ensureSuspendToDisk: true # ensureSuspendToDisk: true
# neverCreateSwap: false # neverCreateSwap: false
@ -201,8 +207,8 @@ defaultFileSystemType: "ext4"
# - uuid: partition uuid (optional parameter; gpt only; requires KPMCore >= 4.2.0) # - uuid: partition uuid (optional parameter; gpt only; requires KPMCore >= 4.2.0)
# - type: partition type (optional parameter; gpt only; requires KPMCore >= 4.2.0) # - type: partition type (optional parameter; gpt only; requires KPMCore >= 4.2.0)
# - attributes: partition attributes (optional parameter; gpt only; requires KPMCore >= 4.2.0) # - attributes: partition attributes (optional parameter; gpt only; requires KPMCore >= 4.2.0)
# - filesystem: filesystem type # - filesystem: filesystem type (optional parameter; fs not created if "unformatted" or unset)
# - mountPoint: partition mount point # - mountPoint: partition mount point (optional parameter; not mounted if unset)
# - size: partition size in bytes (append 'K', 'M' or 'G' for KiB, MiB or GiB) # - size: partition size in bytes (append 'K', 'M' or 'G' for KiB, MiB or GiB)
# or # or
# % of the available drive space if a '%' is appended to the value # % of the available drive space if a '%' is appended to the value

View File

@ -25,6 +25,7 @@ class SmartStatus;
QTEST_GUILESS_MAIN( CreateLayoutsTests ) QTEST_GUILESS_MAIN( CreateLayoutsTests )
static CalamaresUtils::Partition::KPMManager* kpmcore = nullptr; static CalamaresUtils::Partition::KPMManager* kpmcore = nullptr;
static Calamares::JobQueue* jobqueue = nullptr;
using CalamaresUtils::operator""_MiB; using CalamaresUtils::operator""_MiB;
using CalamaresUtils::operator""_GiB; using CalamaresUtils::operator""_GiB;
@ -39,7 +40,7 @@ CreateLayoutsTests::CreateLayoutsTests()
void void
CreateLayoutsTests::init() CreateLayoutsTests::init()
{ {
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) ); jobqueue = new Calamares::JobQueue( nullptr );
kpmcore = new CalamaresUtils::Partition::KPMManager(); kpmcore = new CalamaresUtils::Partition::KPMManager();
} }
@ -47,6 +48,7 @@ void
CreateLayoutsTests::cleanup() CreateLayoutsTests::cleanup()
{ {
delete kpmcore; delete kpmcore;
delete jobqueue;
} }
void void
@ -57,7 +59,7 @@ CreateLayoutsTests::testFixedSizePartition()
PartitionRole role( PartitionRole::Role::Any ); PartitionRole role( PartitionRole::Role::Any );
QList< Partition* > partitions; QList< Partition* > partitions;
if ( !layout.addEntry( { QString( "/" ), QString( "5MiB" ) } ) ) if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/" ), QString( "5MiB" ) } ) )
{ {
QFAIL( qPrintable( "Unable to create / partition" ) ); QFAIL( qPrintable( "Unable to create / partition" ) );
} }
@ -77,7 +79,7 @@ CreateLayoutsTests::testPercentSizePartition()
PartitionRole role( PartitionRole::Role::Any ); PartitionRole role( PartitionRole::Role::Any );
QList< Partition* > partitions; QList< Partition* > partitions;
if ( !layout.addEntry( { QString( "/" ), QString( "50%" ) } ) ) if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/" ), QString( "50%" ) } ) )
{ {
QFAIL( qPrintable( "Unable to create / partition" ) ); QFAIL( qPrintable( "Unable to create / partition" ) );
} }
@ -97,17 +99,17 @@ CreateLayoutsTests::testMixedSizePartition()
PartitionRole role( PartitionRole::Role::Any ); PartitionRole role( PartitionRole::Role::Any );
QList< Partition* > partitions; QList< Partition* > partitions;
if ( !layout.addEntry( { QString( "/" ), QString( "5MiB" ) } ) ) if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/" ), QString( "5MiB" ) } ) )
{ {
QFAIL( qPrintable( "Unable to create / partition" ) ); QFAIL( qPrintable( "Unable to create / partition" ) );
} }
if ( !layout.addEntry( { QString( "/home" ), QString( "50%" ) } ) ) if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/home" ), QString( "50%" ) } ) )
{ {
QFAIL( qPrintable( "Unable to create /home partition" ) ); QFAIL( qPrintable( "Unable to create /home partition" ) );
} }
if ( !layout.addEntry( { QString( "/bkup" ), QString( "50%" ) } ) ) if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/bkup" ), QString( "50%" ) } ) )
{ {
QFAIL( qPrintable( "Unable to create /bkup partition" ) ); QFAIL( qPrintable( "Unable to create /bkup partition" ) );
} }
@ -122,7 +124,7 @@ CreateLayoutsTests::testMixedSizePartition()
} }
#ifdef WITH_KPMCORE4API #ifdef WITH_KPMCORE4API
// TODO: Get a clean way to instanciate a test Device from KPMCore // TODO: Get a clean way to instantiate a test Device from KPMCore
class DevicePrivate class DevicePrivate
{ {
public: public:

View File

@ -39,10 +39,11 @@ def list_mounts(root_mount_point):
""" """
lst = [] lst = []
root_mount_point = os.path.normpath(root_mount_point)
for line in open("/etc/mtab").readlines(): for line in open("/etc/mtab").readlines():
device, mount_point, _ = line.split(" ", 2) device, mount_point, _ = line.split(" ", 2)
if mount_point.startswith(root_mount_point): if os.path.commonprefix([root_mount_point, mount_point]) == root_mount_point:
lst.append((device, mount_point)) lst.append((device, mount_point))
return lst return lst

View File

@ -23,33 +23,45 @@ endif()
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ) include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
set( JOB_SRC set( _users_src
# Jobs
CreateUserJob.cpp CreateUserJob.cpp
MiscJobs.cpp
SetPasswordJob.cpp SetPasswordJob.cpp
SetHostNameJob.cpp SetHostNameJob.cpp
) # Configuration
set( CONFIG_SRC
CheckPWQuality.cpp CheckPWQuality.cpp
Config.cpp Config.cpp
) )
calamares_add_library(
users_internal
EXPORT_MACRO PLUGINDLLEXPORT_PRO
TARGET_TYPE STATIC
NO_INSTALL
NO_VERSION
SOURCES
${_users_src}
LINK_LIBRARIES
Qt5::DBus
${CRYPT_LIBRARIES}
)
calamares_add_plugin( users calamares_add_plugin( users
TYPE viewmodule TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES SOURCES
UsersViewStep.cpp UsersViewStep.cpp
UsersPage.cpp UsersPage.cpp
${JOB_SRC}
${CONFIG_SRC}
UI UI
page_usersetup.ui page_usersetup.ui
RESOURCES RESOURCES
users.qrc users.qrc
LINK_PRIVATE_LIBRARIES LINK_PRIVATE_LIBRARIES
users_internal
calamaresui calamaresui
${CRYPT_LIBRARIES} ${CRYPT_LIBRARIES}
${USER_EXTRA_LIB} ${USER_EXTRA_LIB}
Qt5::DBus
SHARED_LIB SHARED_LIB
) )
@ -63,10 +75,14 @@ calamares_add_test(
) )
calamares_add_test( calamares_add_test(
userscreatetest usersgroupstest
SOURCES SOURCES
TestCreateUserJob.cpp TestGroupInformation.cpp
CreateUserJob.cpp ${_users_src} # Build again with test-visibility
LIBRARIES
Qt5::DBus # HostName job can use DBus to systemd
${CRYPT_LIBRARIES} # SetPassword job uses crypt()
${USER_EXTRA_LIB}
) )
calamares_add_test( calamares_add_test(
@ -82,8 +98,7 @@ calamares_add_test(
userstest userstest
SOURCES SOURCES
Tests.cpp Tests.cpp
${JOB_SRC} ${_users_src} # Build again with test-visibility
${CONFIG_SRC}
LIBRARIES LIBRARIES
Qt5::DBus # HostName job can use DBus to systemd Qt5::DBus # HostName job can use DBus to systemd
${CRYPT_LIBRARIES} # SetPassword job uses crypt() ${CRYPT_LIBRARIES} # SetPassword job uses crypt()

View File

@ -10,6 +10,7 @@
#include "Config.h" #include "Config.h"
#include "CreateUserJob.h" #include "CreateUserJob.h"
#include "MiscJobs.h"
#include "SetHostNameJob.h" #include "SetHostNameJob.h"
#include "SetPasswordJob.h" #include "SetPasswordJob.h"
@ -34,6 +35,11 @@ static void
updateGSAutoLogin( bool doAutoLogin, const QString& login ) updateGSAutoLogin( bool doAutoLogin, const QString& login )
{ {
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( !gs )
{
cWarning() << "No Global Storage available";
return;
}
if ( doAutoLogin && !login.isEmpty() ) if ( doAutoLogin && !login.isEmpty() )
{ {
@ -95,11 +101,16 @@ Config::setUserShell( const QString& shell )
cWarning() << "User shell" << shell << "is not an absolute path."; cWarning() << "User shell" << shell << "is not an absolute path.";
return; return;
} }
// The shell is put into GS because the CreateUser job expects it there if ( shell != m_userShell )
auto* gs = Calamares::JobQueue::instance()->globalStorage();
if ( gs )
{ {
gs->insert( "userShell", shell ); m_userShell = shell;
emit userShellChanged( shell );
// The shell is put into GS as well.
auto* gs = Calamares::JobQueue::instance()->globalStorage();
if ( gs )
{
gs->insert( "userShell", shell );
}
} }
} }
@ -117,15 +128,41 @@ insertInGlobalStorage( const QString& key, const QString& group )
void void
Config::setAutologinGroup( const QString& group ) Config::setAutologinGroup( const QString& group )
{ {
insertInGlobalStorage( QStringLiteral( "autologinGroup" ), group ); if ( group != m_autologinGroup )
emit autologinGroupChanged( group ); {
m_autologinGroup = group;
insertInGlobalStorage( QStringLiteral( "autologinGroup" ), group );
emit autologinGroupChanged( group );
}
}
QStringList
Config::groupsForThisUser() const
{
QStringList l;
l.reserve( defaultGroups().size() + 1 );
for ( const auto& g : defaultGroups() )
{
l << g.name();
}
if ( doAutoLogin() && !autologinGroup().isEmpty() )
{
l << autologinGroup();
}
return l;
} }
void void
Config::setSudoersGroup( const QString& group ) Config::setSudoersGroup( const QString& group )
{ {
insertInGlobalStorage( QStringLiteral( "sudoersGroup" ), group ); if ( group != m_sudoersGroup )
emit sudoersGroupChanged( group ); {
m_sudoersGroup = group;
insertInGlobalStorage( QStringLiteral( "sudoersGroup" ), group );
emit sudoersGroupChanged( group );
}
} }
@ -134,10 +171,9 @@ Config::setLoginName( const QString& login )
{ {
if ( login != m_loginName ) if ( login != m_loginName )
{ {
updateGSAutoLogin( doAutoLogin(), login );
m_customLoginName = !login.isEmpty(); m_customLoginName = !login.isEmpty();
m_loginName = login; m_loginName = login;
updateGSAutoLogin( doAutoLogin(), login );
emit loginNameChanged( login ); emit loginNameChanged( login );
emit loginNameStatusChanged( loginNameStatus() ); emit loginNameStatusChanged( loginNameStatus() );
} }
@ -189,6 +225,8 @@ Config::setHostName( const QString& host )
{ {
if ( host != m_hostName ) if ( host != m_hostName )
{ {
m_customHostName = !host.isEmpty();
m_hostName = host;
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( host.isEmpty() ) if ( host.isEmpty() )
{ {
@ -198,9 +236,6 @@ Config::setHostName( const QString& host )
{ {
gs->insert( "hostname", host ); gs->insert( "hostname", host );
} }
m_customHostName = !host.isEmpty();
m_hostName = host;
emit hostNameChanged( host ); emit hostNameChanged( host );
emit hostNameStatusChanged( hostNameStatus() ); emit hostNameStatusChanged( hostNameStatus() );
} }
@ -369,8 +404,8 @@ Config::setAutoLogin( bool b )
{ {
if ( b != m_doAutoLogin ) if ( b != m_doAutoLogin )
{ {
updateGSAutoLogin( b, loginName() );
m_doAutoLogin = b; m_doAutoLogin = b;
updateGSAutoLogin( b, loginName() );
emit autoLoginChanged( b ); emit autoLoginChanged( b );
} }
} }
@ -590,16 +625,59 @@ Config::checkReady()
STATICTEST void STATICTEST void
setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups ) setConfigurationDefaultGroups( const QVariantMap& map, QList< GroupDescription >& defaultGroups )
{ {
// '#' is not a valid group name; use that to distinguish an empty-list defaultGroups.clear();
// in the configuration (which is a legitimate, if unusual, choice)
// from a bad or missing configuration value. const QString key( "defaultGroups" );
defaultGroups = CalamaresUtils::getStringList( map, QStringLiteral( "defaultGroups" ), QStringList { "#" } ); auto groupsFromConfig = map.value( key ).toList();
if ( defaultGroups.contains( QStringLiteral( "#" ) ) ) if ( groupsFromConfig.isEmpty() )
{ {
cWarning() << "Using fallback groups. Please check *defaultGroups* in users.conf"; if ( map.contains( key ) && map.value( key ).isValid() && map.value( key ).canConvert( QVariant::List ) )
defaultGroups = QStringList { "lp", "video", "network", "storage", "wheel", "audio" }; {
// Explicitly set, but empty: this is valid, but unusual.
cDebug() << key << "has explicit empty value.";
}
else
{
// By default give the user a handful of "traditional" groups, if
// none are specified at all. These are system (GID < 1000) groups.
cWarning() << "Using fallback groups. Please check *defaultGroups* value in users.conf";
for ( const auto& s : { "lp", "video", "network", "storage", "wheel", "audio" } )
{
defaultGroups.append(
GroupDescription( s, GroupDescription::CreateIfNeeded {}, GroupDescription::SystemGroup {} ) );
}
}
}
else
{
for ( const auto& v : groupsFromConfig )
{
if ( v.type() == QVariant::String )
{
defaultGroups.append( GroupDescription( v.toString() ) );
}
else if ( v.type() == QVariant::Map )
{
const auto innermap = v.toMap();
QString name = CalamaresUtils::getString( innermap, "name" );
if ( !name.isEmpty() )
{
defaultGroups.append( GroupDescription( name,
CalamaresUtils::getBool( innermap, "must_exist", false ),
CalamaresUtils::getBool( innermap, "system", false ) ) );
}
else
{
cWarning() << "Ignoring *defaultGroups* entry without a name" << v;
}
}
else
{
cWarning() << "Unknown *defaultGroups* entry" << v;
}
}
} }
} }
@ -737,8 +815,16 @@ Config::createJobs() const
Calamares::Job* j; Calamares::Job* j;
j = new CreateUserJob( if ( m_sudoersGroup.isEmpty() )
loginName(), fullName().isEmpty() ? loginName() : fullName(), doAutoLogin(), defaultGroups() ); {
j = new SetupSudoJob( m_sudoersGroup );
jobs.append( Calamares::job_ptr( j ) );
}
j = new SetupGroupsJob( this );
jobs.append( Calamares::job_ptr( j ) );
j = new CreateUserJob( this );
jobs.append( Calamares::job_ptr( j ) ); jobs.append( Calamares::job_ptr( j ) );
j = new SetPasswordJob( loginName(), userPassword() ); j = new SetPasswordJob( loginName(), userPassword() );

View File

@ -15,6 +15,7 @@
#include "Job.h" #include "Job.h"
#include "utils/NamedEnum.h" #include "utils/NamedEnum.h"
#include <QList>
#include <QObject> #include <QObject>
#include <QVariantMap> #include <QVariantMap>
@ -30,7 +31,61 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( HostNameActions )
const NamedEnumTable< HostNameAction >& hostNameActionNames(); const NamedEnumTable< HostNameAction >& hostNameActionNames();
class Config : public QObject /** @brief Settings for a single group
*
* The list of defaultgroups from the configuration can be
* set up in a fine-grained way, with both user- and system-
* level groups; this class stores a configuration for each.
*/
class GroupDescription
{
public:
// TODO: still too-weakly typed, add a macro to define strongly-typed bools
class MustExist : public std::true_type
{
};
class CreateIfNeeded : public std::false_type
{
};
class SystemGroup : public std::true_type
{
};
class UserGroup : public std::false_type
{
};
///@brief An invalid, empty group
GroupDescription() {}
///@brief A group with full details
GroupDescription( const QString& name, bool mustExistAlready = CreateIfNeeded {}, bool isSystem = UserGroup {} )
: m_name( name )
, m_isValid( !name.isEmpty() )
, m_mustAlreadyExist( mustExistAlready )
, m_isSystem( isSystem )
{
}
bool isValid() const { return m_isValid; }
bool isSystemGroup() const { return m_isSystem; }
bool mustAlreadyExist() const { return m_mustAlreadyExist; }
QString name() const { return m_name; }
///@brief Equality of groups depends only on name and kind
bool operator==( const GroupDescription& rhs ) const
{
return rhs.name() == name() && rhs.isSystemGroup() == isSystemGroup();
}
private:
QString m_name;
bool m_isValid = false;
bool m_mustAlreadyExist = false;
bool m_isSystem = false;
};
class PLUGINDLLEXPORT Config : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -158,7 +213,12 @@ public:
/// Current setting for "require strong password"? /// Current setting for "require strong password"?
bool requireStrongPasswords() const { return m_requireStrongPasswords; } bool requireStrongPasswords() const { return m_requireStrongPasswords; }
const QStringList& defaultGroups() const { return m_defaultGroups; } const QList< GroupDescription >& defaultGroups() const { return m_defaultGroups; }
/** @brief the names of all the groups for the current user
*
* Takes into account defaultGroups and autologin behavior.
*/
QStringList groupsForThisUser() const;
// The user enters a password (and again in a separate UI element) // The user enters a password (and again in a separate UI element)
QString userPassword() const { return m_userPassword; } QString userPassword() const { return m_userPassword; }
@ -242,7 +302,7 @@ private:
PasswordStatus passwordStatus( const QString&, const QString& ) const; PasswordStatus passwordStatus( const QString&, const QString& ) const;
void checkReady(); void checkReady();
QStringList m_defaultGroups; QList< GroupDescription > m_defaultGroups;
QString m_userShell; QString m_userShell;
QString m_autologinGroup; QString m_autologinGroup;
QString m_sudoersGroup; QString m_sudoersGroup;

View File

@ -7,6 +7,8 @@
#include "CreateUserJob.h" #include "CreateUserJob.h"
#include "Config.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/CalamaresUtilsSystem.h"
@ -21,15 +23,9 @@
#include <QTextStream> #include <QTextStream>
CreateUserJob::CreateUserJob( const QString& userName, CreateUserJob::CreateUserJob( const Config* config )
const QString& fullName,
bool autologin,
const QStringList& defaultGroups )
: Calamares::Job() : Calamares::Job()
, m_userName( userName ) , m_config( config )
, m_fullName( fullName )
, m_autologin( autologin )
, m_defaultGroups( defaultGroups )
{ {
} }
@ -37,68 +33,21 @@ CreateUserJob::CreateUserJob( const QString& userName,
QString QString
CreateUserJob::prettyName() const CreateUserJob::prettyName() const
{ {
return tr( "Create user %1" ).arg( m_userName ); return tr( "Create user %1" ).arg( m_config->loginName() );
} }
QString QString
CreateUserJob::prettyDescription() const CreateUserJob::prettyDescription() const
{ {
return tr( "Create user <strong>%1</strong>." ).arg( m_userName ); return tr( "Create user <strong>%1</strong>." ).arg( m_config->loginName() );
} }
QString QString
CreateUserJob::prettyStatusMessage() const CreateUserJob::prettyStatusMessage() const
{ {
return tr( "Creating user %1." ).arg( m_userName ); return m_status.isEmpty() ? tr( "Creating user %1." ).arg( m_config->loginName() ) : m_status;
}
STATICTEST QStringList
groupsInTargetSystem( const QDir& targetRoot )
{
QFileInfo groupsFi( targetRoot.absoluteFilePath( "etc/group" ) );
QFile groupsFile( groupsFi.absoluteFilePath() );
if ( !groupsFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
{
return QStringList();
}
QString groupsData = QString::fromLocal8Bit( groupsFile.readAll() );
QStringList groupsLines = groupsData.split( '\n' );
QStringList::iterator it = groupsLines.begin();
while ( it != groupsLines.end() )
{
if ( it->startsWith( '#' ) )
{
it = groupsLines.erase( it );
continue;
}
int indexOfFirstToDrop = it->indexOf( ':' );
if ( indexOfFirstToDrop < 1 )
{
it = groupsLines.erase( it );
continue;
}
it->truncate( indexOfFirstToDrop );
++it;
}
return groupsLines;
}
static void
ensureGroupsExistInTarget( const QStringList& wantedGroups, const QStringList& availableGroups )
{
for ( const QString& group : wantedGroups )
{
if ( !availableGroups.contains( group ) )
{
#ifdef __FreeBSD__
CalamaresUtils::System::instance()->targetEnvCall( { "pw", "groupadd", "-n", group } );
#else
CalamaresUtils::System::instance()->targetEnvCall( { "groupadd", group } );
#endif
}
}
} }
static Calamares::JobResult static Calamares::JobResult
@ -161,47 +110,22 @@ setUserGroups( const QString& loginName, const QStringList& groups )
Calamares::JobResult Calamares::JobResult
CreateUserJob::exec() CreateUserJob::exec()
{ {
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); QDir destDir;
QDir destDir( gs->value( "rootMountPoint" ).toString() ); bool reuseHome = false;
if ( gs->contains( "sudoersGroup" ) && !gs->value( "sudoersGroup" ).toString().isEmpty() )
{ {
cDebug() << "[CREATEUSER]: preparing sudoers"; Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
destDir = QDir( gs->value( "rootMountPoint" ).toString() );
QString sudoersLine = QString( "%%1 ALL=(ALL) ALL\n" ).arg( gs->value( "sudoersGroup" ).toString() ); reuseHome = gs->value( "reuseHome" ).toBool();
auto fileResult
= CalamaresUtils::System::instance()->createTargetFile( QStringLiteral( "/etc/sudoers.d/10-installer" ),
sudoersLine.toUtf8().constData(),
CalamaresUtils::System::WriteMode::Overwrite );
if ( fileResult )
{
if ( !CalamaresUtils::Permissions::apply( fileResult.path(), 0440 ) )
{
return Calamares::JobResult::error( tr( "Cannot chmod sudoers file." ) );
}
}
else
{
return Calamares::JobResult::error( tr( "Cannot create sudoers file for writing." ) );
}
} }
cDebug() << "[CREATEUSER]: preparing groups";
QStringList availableGroups = groupsInTargetSystem( destDir );
QStringList groupsForThisUser = m_defaultGroups;
if ( m_autologin && gs->contains( "autologinGroup" ) && !gs->value( "autologinGroup" ).toString().isEmpty() )
{
groupsForThisUser << gs->value( "autologinGroup" ).toString();
}
ensureGroupsExistInTarget( groupsForThisUser, availableGroups );
// If we're looking to reuse the contents of an existing /home. // If we're looking to reuse the contents of an existing /home.
// This GS setting comes from the **partitioning** module. // This GS setting comes from the **partitioning** module.
if ( gs->value( "reuseHome" ).toBool() ) if ( reuseHome )
{ {
QString shellFriendlyHome = "/home/" + m_userName; m_status = tr( "Preserving home directory" );
emit progress( 0.2 );
QString shellFriendlyHome = "/home/" + m_config->loginName();
QDir existingHome( destDir.absolutePath() + shellFriendlyHome ); QDir existingHome( destDir.absolutePath() + shellFriendlyHome );
if ( existingHome.exists() ) if ( existingHome.exists() )
{ {
@ -216,20 +140,26 @@ CreateUserJob::exec()
cDebug() << "[CREATEUSER]: creating user"; cDebug() << "[CREATEUSER]: creating user";
auto useraddResult = createUser( m_userName, m_fullName, gs->value( "userShell" ).toString() ); m_status = tr( "Creating user %1" ).arg( m_config->loginName() );
emit progress( 0.5 );
auto useraddResult = createUser( m_config->loginName(), m_config->fullName(), m_config->userShell() );
if ( !useraddResult ) if ( !useraddResult )
{ {
return useraddResult; return useraddResult;
} }
auto usergroupsResult = setUserGroups( m_userName, groupsForThisUser ); m_status = tr( "Configuring user %1" ).arg( m_config->loginName() );
emit progress( 0.8 );
auto usergroupsResult = setUserGroups( m_config->loginName(), m_config->groupsForThisUser() );
if ( !usergroupsResult ) if ( !usergroupsResult )
{ {
return usergroupsResult; return usergroupsResult;
} }
QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName ); m_status = tr( "Setting file permissions" );
QString homeDir = QString( "/home/%1" ).arg( m_userName ); emit progress( 0.9 );
QString userGroup = QString( "%1:%2" ).arg( m_config->loginName() ).arg( m_config->loginName() );
QString homeDir = QString( "/home/%1" ).arg( m_config->loginName() );
auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( { "chown", "-R", userGroup, homeDir } ); auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( { "chown", "-R", userGroup, homeDir } );
if ( commandResult.getExitCode() ) if ( commandResult.getExitCode() )
{ {

View File

@ -12,23 +12,21 @@
#include "Job.h" #include "Job.h"
#include <QStringList> class Config;
class CreateUserJob : public Calamares::Job class CreateUserJob : public Calamares::Job
{ {
Q_OBJECT Q_OBJECT
public: public:
CreateUserJob( const QString& userName, const QString& fullName, bool autologin, const QStringList& defaultGroups ); CreateUserJob( const Config* config );
QString prettyName() const override; QString prettyName() const override;
QString prettyDescription() const override; QString prettyDescription() const override;
QString prettyStatusMessage() const override; QString prettyStatusMessage() const override;
Calamares::JobResult exec() override; Calamares::JobResult exec() override;
private: private:
QString m_userName; const Config* m_config;
QString m_fullName; QString m_status;
bool m_autologin;
QStringList m_defaultGroups;
}; };
#endif /* CREATEUSERJOB_H */ #endif /* CREATEUSERJOB_H */

View File

@ -0,0 +1,194 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "MiscJobs.h"
#include "Config.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include "utils/Permissions.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
SetupSudoJob::SetupSudoJob( const QString& group )
: m_sudoGroup( group )
{
}
QString
SetupSudoJob::prettyName() const
{
return tr( "Configure <pre>sudo</pre> users." );
}
Calamares::JobResult
SetupSudoJob::exec()
{
if ( m_sudoGroup.isEmpty() )
{
return Calamares::JobResult::ok();
}
QString sudoersLine = QString( "%%1 ALL=(ALL) ALL\n" ).arg( m_sudoGroup );
auto fileResult
= CalamaresUtils::System::instance()->createTargetFile( QStringLiteral( "/etc/sudoers.d/10-installer" ),
sudoersLine.toUtf8().constData(),
CalamaresUtils::System::WriteMode::Overwrite );
if ( fileResult )
{
if ( !CalamaresUtils::Permissions::apply( fileResult.path(), 0440 ) )
{
return Calamares::JobResult::error( tr( "Cannot chmod sudoers file." ) );
}
}
else
{
return Calamares::JobResult::error( tr( "Cannot create sudoers file for writing." ) );
}
return Calamares::JobResult::ok();
}
STATICTEST QStringList
groupsInTargetSystem()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( !gs )
{
return QStringList();
}
QDir targetRoot( gs->value( "rootMountPoint" ).toString() );
QFileInfo groupsFi( targetRoot.absoluteFilePath( "etc/group" ) );
QFile groupsFile( groupsFi.absoluteFilePath() );
if ( !groupsFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
{
return QStringList();
}
QString groupsData = QString::fromLocal8Bit( groupsFile.readAll() );
QStringList groupsLines = groupsData.split( '\n' );
QStringList::iterator it = groupsLines.begin();
while ( it != groupsLines.end() )
{
if ( it->startsWith( '#' ) )
{
it = groupsLines.erase( it );
continue;
}
int indexOfFirstToDrop = it->indexOf( ':' );
if ( indexOfFirstToDrop < 1 )
{
it = groupsLines.erase( it );
continue;
}
it->truncate( indexOfFirstToDrop );
++it;
}
return groupsLines;
}
/** @brief Create groups in target system as needed
*
* Given a list of groups that already exist, @p availableGroups,
* go through the @p wantedGroups and create each of them. Groups that
* fail, or which should have already been there, are added to
* @p missingGroups by name.
*/
static bool
ensureGroupsExistInTarget( const QList< GroupDescription >& wantedGroups,
const QStringList& availableGroups,
QStringList& missingGroups )
{
int failureCount = 0;
for ( const auto& group : wantedGroups )
{
if ( group.isValid() && !availableGroups.contains( group.name() ) )
{
if ( group.mustAlreadyExist() )
{
// Should have been there already: don't create it
missingGroups.append( group.name() );
continue;
}
QStringList cmd;
#ifdef __FreeBSD__
if ( group.isSystemGroup() )
{
cWarning() << "Ignoring must-be-a-system group for" << group.name() << "on FreeBSD";
}
cmd = QStringList { "pw", "groupadd", "-n", group.name() };
#else
cmd << QStringLiteral( "groupadd" );
if ( group.isSystemGroup() )
{
cmd << "--system";
}
cmd << group.name();
#endif
if ( CalamaresUtils::System::instance()->targetEnvCall( cmd ) )
{
failureCount++;
missingGroups.append( group.name() + QChar( '*' ) );
}
}
}
if ( !missingGroups.isEmpty() )
{
cWarning() << "Missing groups in target system (* for groupadd failure):" << Logger::DebugList( missingGroups );
}
return failureCount == 0;
}
SetupGroupsJob::SetupGroupsJob( const Config* config )
: m_config( config )
{
}
QString
SetupGroupsJob::prettyName() const
{
return tr( "Preparing groups." );
}
Calamares::JobResult
SetupGroupsJob::exec()
{
const auto& defaultGroups = m_config->defaultGroups();
QStringList availableGroups = groupsInTargetSystem();
QStringList missingGroups;
if ( !ensureGroupsExistInTarget( defaultGroups, availableGroups, missingGroups ) )
{
return Calamares::JobResult::error( tr( "Could not create groups in target system" ) );
}
if ( !missingGroups.isEmpty() )
{
return Calamares::JobResult::error(
tr( "Could not create groups in target system" ),
tr( "These groups are missing in the target system: %1" ).arg( missingGroups.join( ',' ) ) );
}
if ( m_config->doAutoLogin() && !m_config->autologinGroup().isEmpty() )
{
const QString autologinGroup = m_config->autologinGroup();
(void)ensureGroupsExistInTarget(
QList< GroupDescription >() << GroupDescription( autologinGroup ), availableGroups, missingGroups );
}
return Calamares::JobResult::ok();
}

View File

@ -0,0 +1,49 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
/**@file Various small jobs
*
* This file collects miscellaneous jobs that need to be run to prepare
* the system for the user-creation job.
*/
#ifndef USERS_MISCJOBS_H
#define USERS_MISCJOBS_H
#include "Job.h"
class Config;
class SetupSudoJob : public Calamares::Job
{
Q_OBJECT
public:
SetupSudoJob( const QString& group );
QString prettyName() const override;
Calamares::JobResult exec() override;
public:
QString m_sudoGroup;
};
class SetupGroupsJob : public Calamares::Job
{
Q_OBJECT
public:
SetupGroupsJob( const Config* config );
QString prettyName() const override;
Calamares::JobResult exec() override;
public:
const Config* m_config;
};
#endif

View File

@ -1,72 +0,0 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "CreateUserJob.h"
#include "utils/Logger.h"
#include <QDir>
#include <QtTest/QtTest>
// Implementation details
extern QStringList groupsInTargetSystem( const QDir& targetRoot ); // CreateUserJob
class CreateUserTests : public QObject
{
Q_OBJECT
public:
CreateUserTests();
~CreateUserTests() override {}
private Q_SLOTS:
void initTestCase();
void testReadGroup();
};
CreateUserTests::CreateUserTests() {}
void
CreateUserTests::initTestCase()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << "Users test started.";
}
void
CreateUserTests::testReadGroup()
{
QDir root( "/" );
QVERIFY( root.exists() );
// Get the groups in the host system
QStringList groups = groupsInTargetSystem( root );
QVERIFY( groups.count() > 2 );
#ifdef __FreeBSD__
QVERIFY( groups.contains( QStringLiteral( "wheel" ) ) );
#else
QVERIFY( groups.contains( QStringLiteral( "root" ) ) );
#endif
// openSUSE doesn't have "sys"
// QVERIFY( groups.contains( QStringLiteral( "sys" ) ) );
QVERIFY( groups.contains( QStringLiteral( "nogroup" ) ) );
QVERIFY( groups.contains( QStringLiteral( "tty" ) ) );
for ( const QString& s : groups )
{
QVERIFY( !s.isEmpty() );
QVERIFY( !s.contains( '#' ) );
}
}
QTEST_GUILESS_MAIN( CreateUserTests )
#include "utils/moc-warnings.h"
#include "TestCreateUserJob.moc"

View File

@ -0,0 +1,106 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "Config.h"
#include "CreateUserJob.h"
#include "MiscJobs.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/Logger.h"
#include "utils/Yaml.h"
#include <QDir>
#include <QtTest/QtTest>
// Implementation details
extern QStringList groupsInTargetSystem(); // CreateUserJob
class GroupTests : public QObject
{
Q_OBJECT
public:
GroupTests();
~GroupTests() override {}
private Q_SLOTS:
void initTestCase();
void testReadGroup();
void testCreateGroup();
};
GroupTests::GroupTests() {}
void
GroupTests::initTestCase()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << "Users test started.";
if ( !Calamares::JobQueue::instance() )
{
(void)new Calamares::JobQueue();
}
Calamares::JobQueue::instance()->globalStorage()->insert( "rootMountPoint", "/" );
}
void
GroupTests::testReadGroup()
{
// Get the groups in the host system
QStringList groups = groupsInTargetSystem();
QVERIFY( groups.count() > 2 );
#ifdef __FreeBSD__
QVERIFY( groups.contains( QStringLiteral( "wheel" ) ) );
#else
QVERIFY( groups.contains( QStringLiteral( "root" ) ) );
#endif
// openSUSE doesn't have "sys"
// QVERIFY( groups.contains( QStringLiteral( "sys" ) ) );
QVERIFY( groups.contains( QStringLiteral( "nogroup" ) ) );
QVERIFY( groups.contains( QStringLiteral( "tty" ) ) );
for ( const QString& s : groups )
{
QVERIFY( !s.isEmpty() );
QVERIFY( !s.contains( '#' ) );
}
}
void GroupTests::testCreateGroup()
{
// BUILD_AS_TEST is the source-directory path
QFile fi( QString( "%1/tests/5-issue-1523.conf" ).arg( BUILD_AS_TEST ) );
QVERIFY( fi.exists() );
bool ok = false;
const auto map = CalamaresUtils::loadYaml( fi, &ok );
QVERIFY( ok );
QVERIFY( map.count() > 0 ); // Just that it loaded, one key *defaultGroups*
Config c;
c.setConfigurationMap( map );
QCOMPARE( c.defaultGroups().count(), 4 );
QVERIFY( c.defaultGroups().contains( QStringLiteral( "adm" ) ) );
QVERIFY( c.defaultGroups().contains( QStringLiteral( "bar" ) ) );
Calamares::JobQueue::instance()->globalStorage()->insert( "rootMountPoint", "/" );
SetupGroupsJob j(&c);
QVERIFY( !j.exec() ); // running as regular user this should fail
}
QTEST_GUILESS_MAIN( GroupTests )
#include "utils/moc-warnings.h"
#include "TestGroupInformation.moc"

View File

@ -16,7 +16,7 @@
#include <QtTest/QtTest> #include <QtTest/QtTest>
// Implementation details // Implementation details
extern void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups ); extern void setConfigurationDefaultGroups( const QVariantMap& map, QList< GroupDescription >& defaultGroups );
extern HostNameActions getHostNameActions( const QVariantMap& configurationMap ); extern HostNameActions getHostNameActions( const QVariantMap& configurationMap );
extern bool addPasswordCheck( const QString& key, const QVariant& value, PasswordCheckList& passwordChecks ); extern bool addPasswordCheck( const QString& key, const QVariant& value, PasswordCheckList& passwordChecks );
@ -33,6 +33,9 @@ public:
private Q_SLOTS: private Q_SLOTS:
void initTestCase(); void initTestCase();
// Derpy test for getting and setting regular values
void testGetSet();
void testDefaultGroups(); void testDefaultGroups();
void testDefaultGroupsYAML_data(); void testDefaultGroupsYAML_data();
void testDefaultGroupsYAML(); void testDefaultGroupsYAML();
@ -50,35 +53,97 @@ UserTests::initTestCase()
{ {
Logger::setupLogLevel( Logger::LOGDEBUG ); Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << "Users test started."; cDebug() << "Users test started.";
if ( !Calamares::JobQueue::instance() )
{
(void)new Calamares::JobQueue();
}
} }
void
UserTests::testGetSet()
{
Config c;
{
const QString sh( "/bin/sh" );
QCOMPARE( c.userShell(), QString() );
c.setUserShell( sh );
QCOMPARE( c.userShell(), sh );
c.setUserShell( sh + sh );
QCOMPARE( c.userShell(), sh + sh );
const QString badsh( "bash" ); // Not absolute, that's bad
c.setUserShell( badsh ); // .. so unchanged
QCOMPARE( c.userShell(), sh + sh ); // what was set previously
// Explicit set to empty is ok
c.setUserShell( QString() );
QCOMPARE( c.userShell(), QString() );
}
{
const QString al( "autolg" );
QCOMPARE( c.autologinGroup(), QString() );
c.setAutologinGroup( al );
QCOMPARE( c.autologinGroup(), al );
QVERIFY( !c.doAutoLogin() );
c.setAutoLogin( true );
QVERIFY( c.doAutoLogin() );
QCOMPARE( c.autologinGroup(), al );
}
{
const QString su( "sudogrp" );
QCOMPARE( c.sudoersGroup(), QString() );
c.setSudoersGroup( su );
QCOMPARE( c.sudoersGroup(), su );
}
{
const QString ful( "Jan-Jaap Karel Kees" );
const QString lg( "jjkk" );
QCOMPARE( c.fullName(), QString() );
QCOMPARE( c.loginName(), QString() );
QVERIFY( c.loginNameStatus().isEmpty() ); // empty login name is ok
c.setLoginName( lg );
c.setFullName( ful );
QVERIFY( c.loginNameStatus().isEmpty() ); // now it's still ok
QCOMPARE( c.loginName(), lg );
QCOMPARE( c.fullName(), ful );
c.setLoginName( "root" );
QVERIFY( !c.loginNameStatus().isEmpty() ); // can't be root
}
}
void void
UserTests::testDefaultGroups() UserTests::testDefaultGroups()
{ {
{ {
QStringList groups; QList< GroupDescription > groups;
QVariantMap hweelGroup; QVariantMap hweelGroup;
QVERIFY( groups.isEmpty() ); QVERIFY( groups.isEmpty() );
hweelGroup.insert( "defaultGroups", QStringList { "hweel" } ); hweelGroup.insert( "defaultGroups", QStringList { "hweel" } );
setConfigurationDefaultGroups( hweelGroup, groups ); setConfigurationDefaultGroups( hweelGroup, groups );
QCOMPARE( groups.count(), 1 ); QCOMPARE( groups.count(), 1 );
QVERIFY( groups.contains( "hweel" ) ); QVERIFY( groups.contains( GroupDescription( "hweel" ) ) );
} }
{ {
QStringList desired { "wheel", "root", "operator" }; QStringList desired { "wheel", "root", "operator" };
QStringList groups; QList< GroupDescription > groups;
QVariantMap threeGroup; QVariantMap threeGroup;
QVERIFY( groups.isEmpty() ); QVERIFY( groups.isEmpty() );
threeGroup.insert( "defaultGroups", desired ); threeGroup.insert( "defaultGroups", desired );
setConfigurationDefaultGroups( threeGroup, groups ); setConfigurationDefaultGroups( threeGroup, groups );
QCOMPARE( groups.count(), 3 ); QCOMPARE( groups.count(), 3 );
QVERIFY( !groups.contains( "hweel" ) ); QVERIFY( !groups.contains( GroupDescription( "hweel" ) ) );
QCOMPARE( groups, desired ); for ( const auto& s : desired )
{
QVERIFY( groups.contains( GroupDescription( s ) ) );
}
} }
{ {
QStringList groups; QList< GroupDescription > groups;
QVariantMap explicitEmpty; QVariantMap explicitEmpty;
QVERIFY( groups.isEmpty() ); QVERIFY( groups.isEmpty() );
explicitEmpty.insert( "defaultGroups", QStringList() ); explicitEmpty.insert( "defaultGroups", QStringList() );
@ -87,22 +152,22 @@ UserTests::testDefaultGroups()
} }
{ {
QStringList groups; QList< GroupDescription > groups;
QVariantMap missing; QVariantMap missing;
QVERIFY( groups.isEmpty() ); QVERIFY( groups.isEmpty() );
setConfigurationDefaultGroups( missing, groups ); setConfigurationDefaultGroups( missing, groups );
QCOMPARE( groups.count(), 6 ); // because of fallback! QCOMPARE( groups.count(), 6 ); // because of fallback!
QVERIFY( groups.contains( "lp" ) ); QVERIFY( groups.contains( GroupDescription( "lp", false, GroupDescription::SystemGroup {} ) ) );
} }
{ {
QStringList groups; QList< GroupDescription > groups;
QVariantMap typeMismatch; QVariantMap typeMismatch;
QVERIFY( groups.isEmpty() ); QVERIFY( groups.isEmpty() );
typeMismatch.insert( "defaultGroups", 1 ); typeMismatch.insert( "defaultGroups", 1 );
setConfigurationDefaultGroups( typeMismatch, groups ); setConfigurationDefaultGroups( typeMismatch, groups );
QCOMPARE( groups.count(), 6 ); // because of fallback! QCOMPARE( groups.count(), 6 ); // because of fallback!
QVERIFY( groups.contains( "lp" ) ); QVERIFY( groups.contains( GroupDescription( "lp", false, GroupDescription::SystemGroup {} ) ) );
} }
} }
@ -116,6 +181,7 @@ UserTests::testDefaultGroupsYAML_data()
QTest::newRow( "users.conf" ) << "users.conf" << 7 << "video"; QTest::newRow( "users.conf" ) << "users.conf" << 7 << "video";
QTest::newRow( "dashed list" ) << "tests/4-audio.conf" << 4 << "audio"; QTest::newRow( "dashed list" ) << "tests/4-audio.conf" << 4 << "audio";
QTest::newRow( "blocked list" ) << "tests/3-wing.conf" << 3 << "wing"; QTest::newRow( "blocked list" ) << "tests/3-wing.conf" << 3 << "wing";
QTest::newRow( "issue 1523" ) << "tests/5-issue-1523.conf" << 4 << "foobar";
} }
void void
@ -130,6 +196,7 @@ UserTests::testDefaultGroupsYAML()
QFETCH( int, count ); QFETCH( int, count );
QFETCH( QString, group ); QFETCH( QString, group );
// BUILD_AS_TEST is the source-directory path
QFile fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) ); QFile fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) );
QVERIFY( fi.exists() ); QVERIFY( fi.exists() );

View File

@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
defaultGroups:
- adm
- name: foo
must_exist: false
system: true
- name: bar
must_exist: true
- name: foobar
must_exist: false
system: false

View File

@ -3,26 +3,42 @@
# #
# Configuration for the one-user-system user module. # Configuration for the one-user-system user module.
# #
# Besides these settings, the user module also places the following # Besides these settings, the users module also places the following
# keys into the globalconfig area, based on user input in the view step. # keys into the Global Storage area, based on user input in the view step.
# #
# - hostname # - hostname
# - username # - username
# - password (obscured) # - password (obscured)
# - autologinUser (if enabled, set to username) # - autologinUser (if enabled, set to username)
# #
# These globalconfig keys are set when the jobs for this module # These Global Storage keys are set when the configuration for this module
# are created. # is read and when they are modified in the UI.
--- ---
# Used as default groups for the created user. # Used as default groups for the created user.
# Adjust to your Distribution defaults. # Adjust to your Distribution defaults.
#
# Each entry in the *defaultGroups* list is either:
# - a string, naming a group; this is a **non**-system group
# which does not need to exist in the target system; if it
# does not exist, it will be created.
# - an entry with subkeys *name*, *must_exist* and *system*;
# if the group *must_exist* and does not, an error is thrown
# and the installation fails.
#
# The group is created if it does not exist, and it is
# created as a system group (GID < 1000) or user group
# (GID >= 1000) depending on the value of *system*.
defaultGroups: defaultGroups:
- users - name: users
must_exist: true
system: true
- lp - lp
- video - video
- network - network
- storage - storage
- wheel - name: wheel
must_exist: false
system: true
- audio - audio
# Some Distributions require a 'autologin' group for the user. # Some Distributions require a 'autologin' group for the user.

View File

@ -11,7 +11,16 @@ properties:
# Group settings # Group settings
defaultGroups: defaultGroups:
type: array type: array
items: { type: string } items:
oneOf:
- type: string
- type: object
properties:
name: { type: string }
must_exist: { type: boolean, default: false }
system: { type: boolean, default: false }
additionalProperties: false
required: [ name ]
autologinGroup: { type: string } autologinGroup: { type: string }
sudoersGroup: { type: string } sudoersGroup: { type: string }
# Skip login (depends on displaymanager support) # Skip login (depends on displaymanager support)

View File

@ -13,9 +13,8 @@ find_package( Crypt REQUIRED )
# Add optional libraries here # Add optional libraries here
set( USER_EXTRA_LIB ) set( USER_EXTRA_LIB )
set( _users ${CMAKE_CURRENT_SOURCE_DIR}/../users )
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ${CMAKE_CURRENT_SOURCE_DIR}/../../libcalamares ${_users} ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../users )
find_package( LibPWQuality ) find_package( LibPWQuality )
set_package_properties( set_package_properties(
@ -33,15 +32,11 @@ calamares_add_plugin( usersq
TYPE viewmodule TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES SOURCES
${_users}/Config.cpp
${_users}/CreateUserJob.cpp
${_users}/SetPasswordJob.cpp
UsersQmlViewStep.cpp UsersQmlViewStep.cpp
${_users}/SetHostNameJob.cpp
${_users}/CheckPWQuality.cpp
RESOURCES RESOURCES
usersq.qrc usersq.qrc
LINK_PRIVATE_LIBRARIES LINK_PRIVATE_LIBRARIES
users_internal
calamaresui calamaresui
${CRYPT_LIBRARIES} ${CRYPT_LIBRARIES}
${USER_EXTRA_LIB} ${USER_EXTRA_LIB}