diff --git a/CHANGES b/CHANGES
index 73c99cf9b..98db4d483 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
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):
- Anke Boersma
- Andrius Štikonas
- Artem Grinev
- Gaël PORTAY
+ - Matti Hyttinen
- TTran Me
## Core ##
- Calamares now sets the C++ standard for compilation to C++17; this
is for better compatibility and fewer warnings when building with
modern KDE Frameworks and KPMcore 4.2.0.
- - Vietnamese translations have been added. Welcome!
+ - Vietnamese translations have been added. Welcome! (Thanks TTran)
## Modules ##
+ - The *initcpiocfg* module should support plymouth with encryption
+ now. (Thanks Matti)
- The *keyboard* and *keyboardq* modules now share backend code
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) #
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b20ce50d9..d8744afb4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,7 +44,7 @@ project( CALAMARES
VERSION 3.2.33
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
#
diff --git a/CMakeModules/CalamaresAddTest.cmake b/CMakeModules/CalamaresAddTest.cmake
index 56a45d7dc..228d7cbc0 100644
--- a/CMakeModules/CalamaresAddTest.cmake
+++ b/CMakeModules/CalamaresAddTest.cmake
@@ -42,6 +42,8 @@ function( calamares_add_test )
Qt5::Test
)
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} )
if( TEST_GUI )
target_link_libraries( ${TEST_NAME} calamaresui Qt5::Gui )
diff --git a/README.md b/README.md
index a2cb426b7..d3db39089 100644
--- a/README.md
+++ b/README.md
@@ -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.
-The dependencies are explainged in [CONTRIBUTING.md](CONTRIBUTING.md).
+The dependencies are explained in [CONTRIBUTING.md](CONTRIBUTING.md).
## Contributing to Calamares
diff --git a/lang/calamares_az.ts b/lang/calamares_az.ts
index e73b01885..cad678b40 100644
--- a/lang/calamares_az.ts
+++ b/lang/calamares_az.ts
@@ -619,17 +619,17 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir.
-
+ Bu yaddaş qurğusunda artıq əməliyyat sistemi var, lakin, bölmə cədvəli <strong>%1</strong>, lazım olan <strong>%2</strong> ilə fərqlidir.<br/>
-
+ Bu yaddaş qurğusunda bölmələrdən biri <strong>quraşdırılmışdır</strong>.
-
+ Bu yaddaş qurğusu <strong>qeyri-aktiv RAİD</strong> qurğusunun bir hissəsidir.
@@ -2869,7 +2869,7 @@ Output:
-
+ Qovluq tapılmadı
diff --git a/lang/calamares_az_AZ.ts b/lang/calamares_az_AZ.ts
index 51ef10496..161a04990 100644
--- a/lang/calamares_az_AZ.ts
+++ b/lang/calamares_az_AZ.ts
@@ -619,17 +619,17 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir.
-
+ Bu yaddaş qurğusunda artıq əməliyyat sistemi var, lakin, bölmə cədvəli <strong>%1</strong>, lazım olan <strong>%2</strong> ilə fərqlidir.<br/>
-
+ Bu yaddaş qurğusunda bölmələrdən biri <strong>quraşdırılmışdır</strong>.
-
+ Bu yaddaş qurğusu <strong>qeyri-aktiv RAİD</strong> qurğusunun bir hissəsidir.
@@ -2869,7 +2869,7 @@ Output:
-
+ Qovluq tapılmadı
diff --git a/lang/calamares_he.ts b/lang/calamares_he.ts
index e87f6bb19..5ed81a899 100644
--- a/lang/calamares_he.ts
+++ b/lang/calamares_he.ts
@@ -437,7 +437,7 @@ The installer will quit and all changes will be lost.
- טיפוס חריגה אינו מוכר
+ סוג חריגה לא מוכר
diff --git a/lang/calamares_hu.ts b/lang/calamares_hu.ts
index f7651406c..e0ca6cd41 100644
--- a/lang/calamares_hu.ts
+++ b/lang/calamares_hu.ts
@@ -1620,7 +1620,7 @@ Telepítés nem folytatható. <a href="#details">Részletek...</a>
-
+ <h1>Licenszszerződés</h1>
diff --git a/lang/calamares_ko.ts b/lang/calamares_ko.ts
index 481637f00..3a5820569 100644
--- a/lang/calamares_ko.ts
+++ b/lang/calamares_ko.ts
@@ -304,12 +304,12 @@
- Calamares 초기화 실패
+ 깔라마레스 초기화 실패
- %1 가 설치될 수 없습니다. Calamares가 모든 구성된 모듈을 불러올 수 없었습니다. 이것은 Calamares가 분포에 의해 사용되는 방식에서 비롯된 문제입니다.
+ %1 가 설치될 수 없습니다. 깔라마레스가 모든 구성된 모듈을 불러올 수 없었습니다. 이것은 깔라마레스가 배포판에서 사용되는 방식에서 발생한 문제입니다.
@@ -617,17 +617,17 @@ The installer will quit and all changes will be lost.
-
+ 이 스토리지 장치에는 이미 운영 체제가 설치되어 있으나 <strong>%1</strong> 파티션 테이블이 필요로 하는 <strong>%2</strong>와 다릅니다.<br/>
-
+ 이 스토리지 장치는 하나 이상의 <strong>마운트된</strong> 파티션을 갖고 있습니다.
-
+ 이 스토리지 장치는 <strong>비활성화된 RAID</strong> 장치의 일부입니다.
@@ -730,7 +730,7 @@ The installer will quit and all changes will be lost.
-
+ 표준시간대를 %1/%2로 설정합니다.
@@ -790,22 +790,22 @@ The installer will quit and all changes will be lost.
-
+ <h1> 깔라마레스 설치 프로그램 %1에 오신 것을 환영합니다</h1>
-
+ <h1>%1 설치에 오신 것을 환영합니다</h1>
-
+ <h1>깔라마레스 인스톨러 %1에 오신 것을 환영합니다</h1>
-
+ <h1>%1 인스톨러에 오신 것을 환영합니다</h1>
@@ -815,7 +815,7 @@ The installer will quit and all changes will be lost.
-
+ '%1'은 사용자 이름으로 허용되지 않습니다.
@@ -840,7 +840,7 @@ The installer will quit and all changes will be lost.
-
+ '%1'은 호스트 이름으로 허용되지 않습니다.
@@ -1813,7 +1813,7 @@ The installer will quit and all changes will be lost.
-
+ 표준시간대: %1
@@ -1945,7 +1945,7 @@ The installer will quit and all changes will be lost.
- <html><head/><body><h1>OEM 구성</h1> <p>Calamares는 대상 시스템을 구성하는 동안 OEM 설정을 사용합니다.</p></body></html>
+ <html><head/><body><h1>OEM 구성</h1> <p>깔라마레스는 대상 시스템을 구성하는 동안 OEM 설정을 사용합니다.</p></body></html>
@@ -1973,22 +1973,22 @@ The installer will quit and all changes will be lost.
-
+ 표준시간대: %1
-
+ 선호하는 표준시간대와 지역을 선택하세요.
-
+ 표준시간대
-
+ 아래에서 언어 및 로케일을 상세하게 설정할 수 있습니다.
@@ -2864,7 +2864,7 @@ Output:
-
+ 디렉터리를 찾을 수 없습니다
@@ -3044,7 +3044,7 @@ Output:
- Calamares는 파일 시스템 크기 조정 작업을 위해 KPMCore를 시작할 수 없습니다.
+ 깔라마레스는 파일 시스템 크기 조정 작업을 위해 KPMCore를 시작할 수 없습니다.
@@ -3482,18 +3482,18 @@ Output:
-
+ KDE 사용자 의견
-
+ KDE 사용자 의견을 설정하는 중입니다.
-
+ KDE 사용자 의견 설정 중에 오류가 발생했습니다.
@@ -3744,7 +3744,7 @@ Output:
- <h1>%1에 대한 Calamares 설정 프로그램에 오신 것을 환영합니다.</h1>
+ <h1>%1에 대한 깔라마레스 설정 프로그램에 오신 것을 환영합니다.</h1>
@@ -3754,7 +3754,7 @@ Output:
- <h1>%1을 위한 Calamares 설치 관리자에 오신 것을 환영합니다.</h1>
+ <h1>%1을 위한 깔라마레스 설치 관리자에 오신 것을 환영합니다.</h1>
@@ -3877,7 +3877,7 @@ Output:
-
+ 키보드 유형
@@ -3890,7 +3890,7 @@ Output:
-
+ 변경
@@ -3941,7 +3941,7 @@ Output:
-
+ 로그인 및 관리자 작업을 수행하려면 사용자 이름과 자격 증명을 선택하세요
@@ -3961,7 +3961,7 @@ Output:
-
+ 로그인 이름
@@ -4006,7 +4006,7 @@ Output:
-
+ 패스워드 품질 검증
@@ -4016,12 +4016,12 @@ Output:
-
+ 패스워드를 묻지 않고 자동으로 로그인합니다
-
+ 사용자 패스워드를 루트 패스워드로 재사용합니다
@@ -4031,22 +4031,22 @@ Output:
-
+ 당신의 계정을 안전하게 보호하기 위해서 루트 패스워드를 선택하세요.
-
+ 루트 패스워드
-
+ 루트 패스워드 확인
-
+ 입력 오류를 확인하기 위해서 동일한 패스워드를 두번 입력해주세요.
@@ -4060,7 +4060,7 @@ Output:
- Calamares에 대하여
+ 깔라마레스에 대하여
diff --git a/lang/calamares_lt.ts b/lang/calamares_lt.ts
index 676603ca3..0adb509ac 100644
--- a/lang/calamares_lt.ts
+++ b/lang/calamares_lt.ts
@@ -2870,7 +2870,7 @@ Išvestis:
-
+ Katalogas nerastas
@@ -3885,7 +3885,7 @@ Išvestis:
-
+ Klaviatūros variantas
@@ -3989,7 +3989,7 @@ Išvestis:
-
+ Prisijungimo vardas
@@ -4009,7 +4009,7 @@ Išvestis:
-
+ Šis vardas bus naudojamas, jeigu padarysite savo kompiuterį matomą kitiems naudotojams tinkle.
@@ -4074,7 +4074,7 @@ Išvestis:
-
+ Norint įsitikinti, kad rašydami slaptažodį nesuklydote, įrašykite tą patį slaptažodį du kartus.
diff --git a/lang/python/az/LC_MESSAGES/python.po b/lang/python/az/LC_MESSAGES/python.po
index 516429c2d..8637bfe7e 100644
--- a/lang/python/az/LC_MESSAGES/python.po
+++ b/lang/python/az/LC_MESSAGES/python.po
@@ -206,6 +206,8 @@ msgid ""
"The displaymanagers list is empty or undefined in both globalstorage and "
"displaymanager.conf."
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
msgid "Display manager configuration was incomplete"
diff --git a/lang/python/az_AZ/LC_MESSAGES/python.po b/lang/python/az_AZ/LC_MESSAGES/python.po
index 5fdf99797..4fb1f949b 100644
--- a/lang/python/az_AZ/LC_MESSAGES/python.po
+++ b/lang/python/az_AZ/LC_MESSAGES/python.po
@@ -206,6 +206,8 @@ msgid ""
"The displaymanagers list is empty or undefined in both globalstorage and "
"displaymanager.conf."
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
msgid "Display manager configuration was incomplete"
@@ -318,11 +320,11 @@ msgstr "Aparat saatını ayarlamaq."
#: src/modules/mkinitfs/main.py:27
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
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
msgid "The exit code was {}"
diff --git a/lang/python/ko/LC_MESSAGES/python.po b/lang/python/ko/LC_MESSAGES/python.po
index 7108fb0e0..8b8a891e8 100644
--- a/lang/python/ko/LC_MESSAGES/python.po
+++ b/lang/python/ko/LC_MESSAGES/python.po
@@ -4,7 +4,7 @@
# FIRST AUTHOR , YEAR.
#
# Translators:
-# 김지현 , 2018
+# Ji-Hyeon Gim , 2018
# JungHee Lee , 2020
#
#, fuzzy
diff --git a/lang/tz_vi.ts b/lang/tz_vi.ts
new file mode 100644
index 000000000..b7149efa9
--- /dev/null
+++ b/lang/tz_vi.ts
@@ -0,0 +1,2617 @@
+
+
+
+
+ QObject
+
+
+
+ tz_regions
+ Châu phi
+
+
+
+
+ tz_regions
+ Châu Mỹ
+
+
+
+
+ tz_regions
+ Nam Cực
+
+
+
+
+ tz_regions
+ Bắc cực
+
+
+
+
+ tz_regions
+ Châu Á
+
+
+
+
+ tz_regions
+ Đại Tây Dương
+
+
+
+
+ tz_regions
+ Châu Úc
+
+
+
+
+ tz_regions
+ Châu Âu
+
+
+
+
+ tz_regions
+ Ấn Độ
+
+
+
+
+ tz_regions
+ Thái Bình Dương
+
+
+
+
+ tz_names
+ Abidjan
+
+
+
+
+ tz_names
+ Accra
+
+
+
+
+ tz_names
+ Adak
+
+
+
+
+ tz_names
+ A-đi A-ba-ba
+
+
+
+
+ tz_names
+ Adelaide
+
+
+
+
+ tz_names
+ Aden
+
+
+
+
+ tz_names
+ Algiers
+
+
+
+
+ tz_names
+ Almaty
+
+
+
+
+ tz_names
+ Amman
+
+
+
+
+ tz_names
+ Am-xtéc-đam
+
+
+
+
+ tz_names
+ Anadyr
+
+
+
+
+ tz_names
+ Anchorage
+
+
+
+
+ tz_names
+ Andorra
+
+
+
+
+ tz_names
+ An-gui-la
+
+
+
+
+ tz_names
+ An-ta-na-na-ri-vô
+
+
+
+
+ tz_names
+ Antigua
+
+
+
+
+ tz_names
+ A-pi-a
+
+
+
+
+ tz_names
+ Aqtau
+
+
+
+
+ tz_names
+ Aqtobe
+
+
+
+
+ tz_names
+ Araguaina
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Buenos Aires
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Catamarca
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Cordoba
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Jujuy
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/La Rioja
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Mendoza
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Rio Gallegos
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Salta
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/San Juan
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/San Luis
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Tucuman
+
+
+
+
+ tz_names
+ Ác-hen-ti-na/Ushuaia
+
+
+
+
+ tz_names
+ A-ru-ba
+
+
+
+
+ tz_names
+ A-sơ-kha0bast
+
+
+
+
+ tz_names
+ Át-ma-ra
+
+
+
+
+ tz_names
+ Astrakhan
+
+
+
+
+ tz_names
+ A-sun-sân
+
+
+
+
+ tz_names
+ A-ten
+
+
+
+
+ tz_names
+ Atikokan
+
+
+
+
+ tz_names
+ Atyrau
+
+
+
+
+ tz_names
+ Auckland
+
+
+
+
+ tz_names
+ Azores
+
+
+
+
+ tz_names
+ Bát-đa
+
+
+
+
+ tz_names
+ Bahia
+
+
+
+
+ tz_names
+ Bahia Banderas
+
+
+
+
+ tz_names
+ Ba-ranh
+
+
+
+
+ tz_names
+ Ba-cu
+
+
+
+
+ tz_names
+ Bamako
+
+
+
+
+ tz_names
+ Băng Cốc
+
+
+
+
+ tz_names
+ Ban-gui
+
+
+
+
+ tz_names
+ Ban-giun
+
+
+
+
+ tz_names
+ Bác-ba-đốt
+
+
+
+
+ tz_names
+ Barnaul
+
+
+
+
+ tz_names
+ Bê-rút
+
+
+
+
+ tz_names
+ Belem
+
+
+
+
+ tz_names
+ Bê-ô-grát
+
+
+
+
+ tz_names
+ Bê-li-xê
+
+
+
+
+ tz_names
+ Béc Lin
+
+
+
+
+ tz_names
+ Béc-mu-đa
+
+
+
+
+ tz_names
+ Bi-skec
+
+
+
+
+ tz_names
+ Bit-xao
+
+
+
+
+ tz_names
+ Blanc-Sablon
+
+
+
+
+ tz_names
+ Blantyre
+
+
+
+
+ tz_names
+ Boa Vista
+
+
+
+
+ tz_names
+ Bô-gô-ta
+
+
+
+
+ tz_names
+ Boise
+
+
+
+
+ tz_names
+ Bougainville
+
+
+
+
+ tz_names
+ Bra-tít-xla-va
+
+
+
+
+ tz_names
+ Brazzaville
+
+
+
+
+ tz_names
+ Brisbane
+
+
+
+
+ tz_names
+ Đồi Broken
+
+
+
+
+ tz_names
+ Bru-nây
+
+
+
+
+ tz_names
+ Brúc-xen
+
+
+
+
+ tz_names
+ Bu-ca-rét
+
+
+
+
+ tz_names
+ Bu-đa-pét
+
+
+
+
+ tz_names
+ Bu-gium-bu-ra
+
+
+
+
+ tz_names
+ Busingen
+
+
+
+
+ tz_names
+ Cai Rô
+
+
+
+
+ tz_names
+ Vịnh Cambridge
+
+
+
+
+ tz_names
+ Campo Grande
+
+
+
+
+ tz_names
+ Canary
+
+
+
+
+ tz_names
+ Cancun
+
+
+
+
+ tz_names
+ Cáp-ve
+
+
+
+
+ tz_names
+ Ca-ra-cát
+
+
+
+
+ tz_names
+ Casablanca
+
+
+
+
+ tz_names
+ Casey
+
+
+
+
+ tz_names
+ Cayenne
+
+
+
+
+ tz_names
+ Quần đảo Cay-man
+
+
+
+
+ tz_names
+ Ceuta
+
+
+
+
+ tz_names
+ Chagos
+
+
+
+
+ tz_names
+ Chatham
+
+
+
+
+ tz_names
+ Chi Ca Gô
+
+
+
+
+ tz_names
+ Chihuahua
+
+
+
+
+ tz_names
+ Ki-si-nhốp
+
+
+
+
+ tz_names
+ Chita
+
+
+
+
+ tz_names
+ Choibalsan
+
+
+
+
+ tz_names
+ Christmas
+
+
+
+
+ tz_names
+ Chuuk
+
+
+
+
+ tz_names
+ Cocos
+
+
+
+
+ tz_names
+ Cô Lôm Bô
+
+
+
+
+ tz_names
+ (Liên bang) Cô-mo
+
+
+
+
+ tz_names
+ Cô-na-cri
+
+
+
+
+ tz_names
+ Cô-pen-ha-gen
+
+
+
+
+ tz_names
+ Cốt-xta-ri-ca
+
+
+
+
+ tz_names
+ Creston
+
+
+
+
+ tz_names
+ Cuiaba
+
+
+
+
+ tz_names
+ Cu-ra-xao
+
+
+
+
+ tz_names
+ Currie
+
+
+
+
+ tz_names
+ Dakar
+
+
+
+
+ tz_names
+ Đa-mát
+
+
+
+
+ tz_names
+ Danmarkshavn
+
+
+
+
+ tz_names
+ Dar es Salaam
+
+
+
+
+ tz_names
+ Darwin
+
+
+
+
+ tz_names
+ Davis
+
+
+
+
+ tz_names
+ Dawson
+
+
+
+
+ tz_names
+ Dawson Creek
+
+
+
+
+ tz_names
+ Denver
+
+
+
+
+ tz_names
+ Detroit
+
+
+
+
+ tz_names
+ Đắc-ca
+
+
+
+
+ tz_names
+ Đi-li
+
+
+
+
+ tz_names
+ Cộng hòa Gi-bu-ti
+
+
+
+
+ tz_names
+ Đô-mi-ni-ca-na
+
+
+
+
+ tz_names
+ Douala
+
+
+
+
+ tz_names
+ Đu Bai
+
+
+
+
+ tz_names
+ Đu-blin
+
+
+
+
+ tz_names
+ DumontDUrville
+
+
+
+
+ tz_names
+ Đu-san-be
+
+
+
+
+ tz_names
+ Đảo Phục Sinh
+
+
+
+
+ tz_names
+ Edmonton
+
+
+
+
+ tz_names
+ Efate
+
+
+
+
+ tz_names
+ Eirunepe
+
+
+
+
+ tz_names
+ El Aaiun
+
+
+
+
+ tz_names
+ En Xan-va-đo
+
+
+
+
+ tz_names
+ Đảo Enderbury
+
+
+
+
+ tz_names
+ Eucla
+
+
+
+
+ tz_names
+ Fakaofo
+
+
+
+
+ tz_names
+ Famagusta
+
+
+
+
+ tz_names
+ Quần đảo Fa-rô
+
+
+
+
+ tz_names
+ Phi-gi
+
+
+
+
+ tz_names
+ Fort Nelson
+
+
+
+
+ tz_names
+ Fortaleza
+
+
+
+
+ tz_names
+ Phritao
+
+
+
+
+ tz_names
+ Funafuti
+
+
+
+
+ tz_names
+ Ga-bô-rô-nê
+
+
+
+
+ tz_names
+ Quần đảo Galapagos
+
+
+
+
+ tz_names
+ Gambier
+
+
+
+
+ tz_names
+ Dải Gaza
+
+
+
+
+ tz_names
+ Gibraltar
+
+
+
+
+ tz_names
+ Vịnh Glace
+
+
+
+
+ tz_names
+ Nu-úc
+
+
+
+
+ tz_names
+ Vịnh Goose
+
+
+
+
+ tz_names
+ Đảo Grand Turk
+
+
+
+
+ tz_names
+ Grê-na-đa
+
+
+
+
+ tz_names
+ Guadalcanal
+
+
+
+
+ tz_names
+ Goa-đê-lốp
+
+
+
+
+ tz_names
+ Guam
+
+
+
+
+ tz_names
+ Goa-tê-ma-la
+
+
+
+
+ tz_names
+ Guayaquil
+
+
+
+
+ tz_names
+ Guernsey
+
+
+
+
+ tz_names
+ Guy-a-na
+
+
+
+
+ tz_names
+ Halifa
+
+
+
+
+ tz_names
+ Ha-ra-rê
+
+
+
+
+ tz_names
+ Ha Va Na
+
+
+
+
+ tz_names
+ Hebron
+
+
+
+
+ tz_names
+ Hen-sin-ki
+
+
+
+
+ tz_names
+ Hermosillo
+
+
+
+
+ tz_names
+ Hồ Chí Minh
+
+
+
+
+ tz_names
+ Hobart
+
+
+
+
+ tz_names
+ Hồng Công
+
+
+
+
+ tz_names
+ Honolulu
+
+
+
+
+ tz_names
+ Khovd
+
+
+
+
+ tz_names
+ Indiana/Indianapolis
+
+
+
+
+ tz_names
+ Indiana/Knox
+
+
+
+
+ tz_names
+ Indiana/Marengo
+
+
+
+
+ tz_names
+ Indiana/Petersburg
+
+
+
+
+ tz_names
+ Indiana/Tell City
+
+
+
+
+ tz_names
+ Indiana/Vevay
+
+
+
+
+ tz_names
+ Indiana/Vincennes
+
+
+
+
+ tz_names
+ Indiana/Winamac
+
+
+
+
+ tz_names
+ Inuvik
+
+
+
+
+ tz_names
+ Iqaluit
+
+
+
+
+ tz_names
+ Irkutsk
+
+
+
+
+ tz_names
+ Đảo Man
+
+
+
+
+ tz_names
+ Istanbul
+
+
+
+
+ tz_names
+ Gia Các Ta
+
+
+
+
+ tz_names
+ Gia-mai-ca
+
+
+
+
+ tz_names
+ Jayapura
+
+
+
+
+ tz_names
+ Jersey
+
+
+
+
+ tz_names
+ Giê-ru-xa-lem
+
+
+
+
+ tz_names
+ Johannesburg
+
+
+
+
+ tz_names
+ Juba
+
+
+
+
+ tz_names
+ Juneau
+
+
+
+
+ tz_names
+ Ka-bun
+
+
+
+
+ tz_names
+ Kaliningrad
+
+
+
+
+ tz_names
+ Bán đảo Kamchatka
+
+
+
+
+ tz_names
+ Campala
+
+
+
+
+ tz_names
+ Karachi
+
+
+
+
+ tz_names
+ Cát-man-đu
+
+
+
+
+ tz_names
+ Ken túc ky/Louisville
+
+
+
+
+ tz_names
+ Ken túc ky/Monticello
+
+
+
+
+ tz_names
+ Đảo Kerguelen
+
+
+
+
+ tz_names
+ Khandyga
+
+
+
+
+ tz_names
+ Khác-tum
+
+
+
+
+ tz_names
+ Kyiv
+
+
+
+
+ tz_names
+ Ki-ga-li
+
+
+
+
+ tz_names
+ Kin-xa-sa
+
+
+
+
+ tz_names
+ Kiritimati
+
+
+
+
+ tz_names
+ Kirov
+
+
+
+
+ tz_names
+ Kolkata
+
+
+
+
+ tz_names
+ Kosrae
+
+
+
+
+ tz_names
+ Kralendijk
+
+
+
+
+ tz_names
+ Krasnoyarsk
+
+
+
+
+ tz_names
+ Kuala Lam Pơ
+
+
+
+
+ tz_names
+ Kuching
+
+
+
+
+ tz_names
+ Kuwait
+
+
+
+
+ tz_names
+ Kwajalein
+
+
+
+
+ tz_names
+ La Pa-xơ
+
+
+
+
+ tz_names
+ Lagos
+
+
+
+
+ tz_names
+ Li-brơ-vin
+
+
+
+
+ tz_names
+ Lima
+
+
+
+
+ tz_names
+ Hồ Lindeman
+
+
+
+
+ tz_names
+ Lít Bon
+
+
+
+
+ tz_names
+ Liu-bli-an-na
+
+
+
+
+ tz_names
+ Lô-mê
+
+
+
+
+ tz_names
+ Luân Đôn
+
+
+
+
+ tz_names
+ Longyearbyen
+
+
+
+
+ tz_names
+ Đảo Lord Howe
+
+
+
+
+ tz_names
+ Lốt Ăng Giơ Lét
+
+
+
+
+ tz_names
+ Lower Princes
+
+
+
+
+ tz_names
+ Lu-an-đa
+
+
+
+
+ tz_names
+ Lubumbashi
+
+
+
+
+ tz_names
+ Lu-xa-ca
+
+
+
+
+ tz_names
+ Lúc-xăm-bua
+
+
+
+
+ tz_names
+ Ma Cao
+
+
+
+
+ tz_names
+ Maceio
+
+
+
+
+ tz_names
+ Macquarie
+
+
+
+
+ tz_names
+ Madeira
+
+
+
+
+ tz_names
+ Ma Rích
+
+
+
+
+ tz_names
+ Magadan
+
+
+
+
+ tz_names
+ Mahe
+
+
+
+
+ tz_names
+ Majuro
+
+
+
+
+ tz_names
+ Makassar
+
+
+
+
+ tz_names
+ Malabo
+
+
+
+
+ tz_names
+ Man-đi-vơ
+
+
+
+
+ tz_names
+ Man-Man-tata
+
+
+
+
+ tz_names
+ Ma-na-goa
+
+
+
+
+ tz_names
+ Manaus
+
+
+
+
+ tz_names
+ Manila
+
+
+
+
+ tz_names
+ Maputo
+
+
+
+
+ tz_names
+ Mariehamn
+
+
+
+
+ tz_names
+ Marigot
+
+
+
+
+ tz_names
+ Quần đảo Marquesas
+
+
+
+
+ tz_names
+ Martinique
+
+
+
+
+ tz_names
+ Ma-xê-ru
+
+
+
+
+ tz_names
+ Matamoros
+
+
+
+
+ tz_names
+ Mô-ri-xơ
+
+
+
+
+ tz_names
+ Mawson
+
+
+
+
+ tz_names
+ Mayotte
+
+
+
+
+ tz_names
+ Mazatlan
+
+
+
+
+ tz_names
+ Mbabane
+
+
+
+
+ tz_names
+ McMurdo
+
+
+
+
+ tz_names
+ Melbourne
+
+
+
+
+ tz_names
+ Menominee
+
+
+
+
+ tz_names
+ Merida
+
+
+
+
+ tz_names
+ Metlakatla
+
+
+
+
+ tz_names
+ Thành phố Mê Xi Cô
+
+
+
+
+ tz_names
+ Midway
+
+
+
+
+ tz_names
+ Min-xcơ
+
+
+
+
+ tz_names
+ Mi-kê-lân
+
+
+
+
+ tz_names
+ Mô-ga-đi-su
+
+
+
+
+ tz_names
+ Mô-na-cô
+
+
+
+
+ tz_names
+ Moncton
+
+
+
+
+ tz_names
+ Monrovia
+
+
+
+
+ tz_names
+ Monterrey
+
+
+
+
+ tz_names
+ Mông-tơ-vi-di-ô
+
+
+
+
+ tz_names
+ Montserrat
+
+
+
+
+ tz_names
+ Mát Cơ Va
+
+
+
+
+ tz_names
+ Mu Cát
+
+
+
+
+ tz_names
+ Nai-rô-bi
+
+
+
+
+ tz_names
+ Nát-xô
+
+
+
+
+ tz_names
+ Na-u-ru
+
+
+
+
+ tz_names
+ Gia-mê-na
+
+
+
+
+ tz_names
+ Nữu Ước
+
+
+
+
+ tz_names
+ Ni-a-mây
+
+
+
+
+ tz_names
+ Ni-cô-xi-a
+
+
+
+
+ tz_names
+ Nipigon
+
+
+
+
+ tz_names
+ Ni-u-ê
+
+
+
+
+ tz_names
+ Nome
+
+
+
+
+ tz_names
+ Norfolk
+
+
+
+
+ tz_names
+ Noronha
+
+
+
+
+ tz_names
+ Bắc Dakota/Beulah
+
+
+
+
+ tz_names
+ Bắc Dakota/Center
+
+
+
+
+ tz_names
+ Bắc Dakota/New Salem
+
+
+
+
+ tz_names
+ Nu-ác-sốt
+
+
+
+
+ tz_names
+ No-mi
+
+
+
+
+ tz_names
+ Novokuznetsk
+
+
+
+
+ tz_names
+ Novosibirsk
+
+
+
+
+ tz_names
+ Ojinaga
+
+
+
+
+ tz_names
+ Omsk
+
+
+
+
+ tz_names
+ Oral
+
+
+
+
+ tz_names
+ Ốt-xlô
+
+
+
+
+ tz_names
+ U-a-ga-đu-gu
+
+
+
+
+ tz_names
+ Pago Pago
+
+
+
+
+ tz_names
+ Pa-lau
+
+
+
+
+ tz_names
+ Palmer
+
+
+
+
+ tz_names
+ Pa-na-ma
+
+
+
+
+ tz_names
+ Pangnirtung
+
+
+
+
+ tz_names
+ Pa-ra-ma-ri-bô
+
+
+
+
+ tz_names
+ Pa Ri
+
+
+
+
+ tz_names
+ Perth
+
+
+
+
+ tz_names
+ Phnôm Pênh
+
+
+
+
+ tz_names
+ Phoenix
+
+
+
+
+ tz_names
+ Quần đảo Pít-cơn
+
+
+
+
+ tz_names
+ Pốt-gô-ri-xa
+
+
+
+
+ tz_names
+ Pohnpei
+
+
+
+
+ tz_names
+ Pontianak
+
+
+
+
+ tz_names
+ Cảng Moresby
+
+
+
+
+ tz_names
+ Cảng Tây Ban Nha
+
+
+
+
+ tz_names
+ Cảng au Prince
+
+
+
+
+ tz_names
+ Cảng Velho
+
+
+
+
+ tz_names
+ Cảng-Novo
+
+
+
+
+ tz_names
+ Praha
+
+
+
+
+ tz_names
+ Cảng Rico
+
+
+
+
+ tz_names
+ Đấu trường Punta
+
+
+
+
+ tz_names
+ Bình Nhưỡng
+
+
+
+
+ tz_names
+ Ca-ta
+
+
+
+
+ tz_names
+ Kostanay
+
+
+
+
+ tz_names
+ Kyzylorda
+
+
+
+
+ tz_names
+ Rainy River
+
+
+
+
+ tz_names
+ Rankin Inlet
+
+
+
+
+ tz_names
+ Rarotonga
+
+
+
+
+ tz_names
+ Recife
+
+
+
+
+ tz_names
+ Regina
+
+
+
+
+ tz_names
+ Resolute
+
+
+
+
+ tz_names
+ Rê-u-niên
+
+
+
+
+ tz_names
+ Rây-ki-a-vích
+
+
+
+
+ tz_names
+ Ri-ga
+
+
+
+
+ tz_names
+ Rio Branco
+
+
+
+
+ tz_names
+ Ri-át
+
+
+
+
+ tz_names
+ Rô Ma
+
+
+
+
+ tz_names
+ Rothera
+
+
+
+
+ tz_names
+ Saipan
+
+
+
+
+ tz_names
+ Sakhalin
+
+
+
+
+ tz_names
+ Samara
+
+
+
+
+ tz_names
+ Samarkand
+
+
+
+
+ tz_names
+ San Marino
+
+
+
+
+ tz_names
+ Santarem
+
+
+
+
+ tz_names
+ Santiago
+
+
+
+
+ tz_names
+ Xan-tô Đô-min-gô
+
+
+
+
+ tz_names
+ Sao Paolo
+
+
+
+
+ tz_names
+ Sao Tô-mê
+
+
+
+
+ tz_names
+ Xa-ra-ê-vô
+
+
+
+
+ tz_names
+ Saratov
+
+
+
+
+ tz_names
+ Scoresby Sund
+
+
+
+
+ tz_names
+ Xê un
+
+
+
+
+ tz_names
+ Thượng Hải
+
+
+
+
+ tz_names
+ Simferopol
+
+
+
+
+ tz_names
+ Singapo
+
+
+
+
+ tz_names
+ Sitka
+
+
+
+
+ tz_names
+ Xcốp-pi-ê
+
+
+
+
+ tz_names
+ Sofia
+
+
+
+
+ tz_names
+ Nam Georgia
+
+
+
+
+ tz_names
+ Srednekolymsk
+
+
+
+
+ tz_names
+ St Barthelemy
+
+
+
+
+ tz_names
+ St Helena
+
+
+
+
+ tz_names
+ St Johns
+
+
+
+
+ tz_names
+ St Kitts
+
+
+
+
+ tz_names
+ St Lucia
+
+
+
+
+ tz_names
+ St Thomas
+
+
+
+
+ tz_names
+ St Vincent
+
+
+
+
+ tz_names
+ Stanley
+
+
+
+
+ tz_names
+ Xtốc-khôm
+
+
+
+
+ tz_names
+ Swift Current
+
+
+
+
+ tz_names
+ Xít ni
+
+
+
+
+ tz_names
+ Syowa
+
+
+
+
+ tz_names
+ Tahiti
+
+
+
+
+ tz_names
+ Đài Bắc
+
+
+
+
+ tz_names
+ Ta-lin
+
+
+
+
+ tz_names
+ Tarawa
+
+
+
+
+ tz_names
+ Ta-sơ-ken
+
+
+
+
+ tz_names
+ Tbi-li-xi
+
+
+
+
+ tz_names
+ Te-gu-xi-gan-pa
+
+
+
+
+ tz_names
+ Tê-hê-ran
+
+
+
+
+ tz_names
+ Thim-bu
+
+
+
+
+ tz_names
+ Thule
+
+
+
+
+ tz_names
+ Vịnh Thunder
+
+
+
+
+ tz_names
+ Tijuana
+
+
+
+
+ tz_names
+ Ti-ra-na
+
+
+
+
+ tz_names
+ Tô Ky Ôs
+
+
+
+
+ tz_names
+ Tomsk
+
+
+
+
+ tz_names
+ Tongatapu
+
+
+
+
+ tz_names
+ Tô Rôn Tô
+
+
+
+
+ tz_names
+ Tortola
+
+
+
+
+ tz_names
+ Tri Pô Li
+
+
+
+
+ tz_names
+ Troll
+
+
+
+
+ tz_names
+ Tuy-nít
+
+
+
+
+ tz_names
+ Ulan Bato
+
+
+
+
+ tz_names
+ Ulyanovsk
+
+
+
+
+ tz_names
+ Urumqi
+
+
+
+
+ tz_names
+ Ust-Nera
+
+
+
+
+ tz_names
+ Uzhgorod
+
+
+
+
+ tz_names
+ Vaduz
+
+
+
+
+ tz_names
+ Van Cô Vơ
+
+
+
+
+ tz_names
+ Va Ti Can
+
+
+
+
+ tz_names
+ Viên
+
+
+
+
+ tz_names
+ Viêng Chăn
+
+
+
+
+ tz_names
+ Vin-ni-út
+
+
+
+
+ tz_names
+ Vladivostok
+
+
+
+
+ tz_names
+ Volgograd
+
+
+
+
+ tz_names
+ Vostok
+
+
+
+
+ tz_names
+ Wake
+
+
+
+
+ tz_names
+ Wa Li
+
+
+
+
+ tz_names
+ Vác-sa-va
+
+
+
+
+ tz_names
+ Whitehorse
+
+
+
+
+ tz_names
+ Windhoek
+
+
+
+
+ tz_names
+ Winnipeg
+
+
+
+
+ tz_names
+ Yakutat
+
+
+
+
+ tz_names
+ Yakutsk
+
+
+
+
+ tz_names
+ Ragoon
+
+
+
+
+ tz_names
+ Yekaterinburg
+
+
+
+
+ tz_names
+ Yellowknife
+
+
+
+
+ tz_names
+ Ê-rê-van
+
+
+
+
+ tz_names
+ Da-gờ-rép
+
+
+
+
+ tz_names
+ Zaporizhia
+
+
+
+
+ tz_names
+ Zurich
+
+
+
diff --git a/src/libcalamares/JobQueue.cpp b/src/libcalamares/JobQueue.cpp
index 1637f0719..b66be7ebe 100644
--- a/src/libcalamares/JobQueue.cpp
+++ b/src/libcalamares/JobQueue.cpp
@@ -249,6 +249,7 @@ JobQueue::~JobQueue()
}
delete m_storage;
+ s_instance = nullptr;
}
diff --git a/src/modules/initcpiocfg/main.py b/src/modules/initcpiocfg/main.py
index 7a94a8acc..c3bc0b95a 100644
--- a/src/modules/initcpiocfg/main.py
+++ b/src/modules/initcpiocfg/main.py
@@ -161,7 +161,10 @@ def modify_mkinitcpio_conf(partitions, root_mount_point):
hooks.append("usr")
if encrypt_hook:
- hooks.append("encrypt")
+ if detect_plymouth():
+ hooks.append("plymouth-encrypt")
+ else:
+ hooks.append("encrypt")
if not unencrypted_separate_boot and \
os.path.isfile(
os.path.join(root_mount_point, "crypto_keyfile.bin")
diff --git a/src/modules/keyboard/SetKeyboardLayoutJob.cpp b/src/modules/keyboard/SetKeyboardLayoutJob.cpp
index 419d6f3fc..c80d84e7d 100644
--- a/src/modules/keyboard/SetKeyboardLayoutJob.cpp
+++ b/src/modules/keyboard/SetKeyboardLayoutJob.cpp
@@ -179,6 +179,8 @@ SetKeyboardLayoutJob::findLegacyKeymap() const
bool
SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const QString& convertedKeymapPath ) const
{
+ cDebug() << "Writing vconsole data to" << vconsoleConfPath;
+
QString keymap = findConvertedKeymap( convertedKeymapPath );
if ( keymap.isEmpty() )
{
@@ -205,15 +207,20 @@ SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const
file.close();
if ( stream.status() != QTextStream::Ok )
{
+ cError() << "Could not read lines from" << file.fileName();
return false;
}
}
// 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 );
bool found = false;
- foreach ( const QString& existingLine, existingLines )
+ for ( const QString& existingLine : qAsConst( existingLines ) )
{
if ( existingLine.trimmed().startsWith( "KEYMAP=" ) )
{
@@ -233,7 +240,7 @@ SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const
stream.flush();
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 );
}
@@ -242,8 +249,14 @@ SetKeyboardLayoutJob::writeVConsoleData( const QString& vconsoleConfPath, const
bool
SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const
{
+ cDebug() << "Writing X11 configuration to" << 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 );
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();
- cDebug() << "Written XkbLayout" << m_layout << "; XkbModel" << m_model << "; XkbVariant" << m_variant
- << "to X.org file" << keyboardConfPath;
+ cDebug() << Logger::SubEntry << "Written XkbLayout" << m_layout << "; XkbModel" << m_model << "; XkbVariant"
+ << m_variant << "to X.org file" << keyboardConfPath << stream.status();
return ( stream.status() == QTextStream::Ok );
}
@@ -297,8 +310,14 @@ SetKeyboardLayoutJob::writeX11Data( const QString& keyboardConfPath ) const
bool
SetKeyboardLayoutJob::writeDefaultKeyboardData( const QString& defaultKeyboardPath ) const
{
+ cDebug() << "Writing default keyboard data to" << 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 );
stream << "# KEYBOARD CONFIGURATION FILE\n\n"
@@ -313,8 +332,8 @@ SetKeyboardLayoutJob::writeDefaultKeyboardData( const QString& defaultKeyboardPa
file.close();
- cDebug() << "Written XKBMODEL" << m_model << "; XKBLAYOUT" << m_layout << "; XKBVARIANT" << m_variant
- << "to /etc/default/keyboard file" << defaultKeyboardPath;
+ cDebug() << Logger::SubEntry << "Written XKBMODEL" << m_model << "; XKBLAYOUT" << m_layout << "; XKBVARIANT"
+ << m_variant << "to /etc/default/keyboard file" << defaultKeyboardPath << stream.status();
return ( stream.status() == QTextStream::Ok );
}
@@ -329,60 +348,72 @@ SetKeyboardLayoutJob::exec()
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
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;
- while ( keyboardConfPath.startsWith( '/' ) )
+ // Get the path to the destination's /etc/vconsole.conf
+ 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 ( 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( '/' ) )
+ if ( !writeVConsoleData( vconsoleConfPath, convertedKeymapPath ) )
{
- 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 ) )
- return Calamares::JobResult::error(
- tr( "Failed to write keyboard configuration to existing /etc/default directory." ),
- tr( "Failed to write to %1" ).arg( keyboardConfPath ) );
+ // 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;
+ 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();
diff --git a/src/modules/mount/main.py b/src/modules/mount/main.py
index 01c0650bf..71599a6de 100644
--- a/src/modules/mount/main.py
+++ b/src/modules/mount/main.py
@@ -7,6 +7,7 @@
# SPDX-FileCopyrightText: 2017 Alf Gaida
# SPDX-FileCopyrightText: 2019 Adriaan de Groot
# SPDX-FileCopyrightText: 2019 Kevin Kofler
+# SPDX-FileCopyrightText: 2019-2020 Collabora Ltd
# SPDX-License-Identifier: GPL-3.0-or-later
#
# 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
# `partition["mountPoint"]` starts with a '/'.
raw_mount_point = partition["mountPoint"]
+ if not raw_mount_point:
+ return
+
mount_point = root_mount_point + raw_mount_point
# Ensure that the created directory has the correct SELinux context on
@@ -52,26 +56,22 @@ def mount_partition(root_mount_point, partition, partitions):
raise
fstype = partition.get("fs", "").lower()
+ if not fstype or fstype == "unformatted":
+ return
if fstype == "fat16" or fstype == "fat32":
fstype = "vfat"
- if "luksMapperName" in partition:
- 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", ""),
- )
+ device = partition["device"]
- else:
- libcalamares.utils.mount(partition["device"],
- mount_point,
- fstype,
- partition.get("options", ""),
- )
+ if "luksMapperName" in partition:
+ device = os.path.join("/dev/mapper", partition["luksMapperName"])
+
+ if libcalamares.utils.mount(device,
+ mount_point,
+ fstype,
+ partition.get("options", "")) != 0:
+ libcalamares.utils.warning("Cannot mount {}".format(device))
# If the root partition is btrfs, we create a subvolume "@"
# 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])
+ device = partition["device"]
+
if "luksMapperName" in partition:
- libcalamares.utils.mount(
- "/dev/mapper/{!s}".format(partition["luksMapperName"]),
- mount_point,
- fstype,
- ",".join(
- ["subvol=@", partition.get("options", "")]),
- )
- if not has_home_mount_point:
- libcalamares.utils.mount(
- "/dev/mapper/{!s}".format(partition["luksMapperName"]),
- root_mount_point + "/home",
- fstype,
- ",".join(
- ["subvol=@home", partition.get("options", "")]),
- )
- 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", "")]),
- )
+ device = os.path.join("/dev/mapper", partition["luksMapperName"])
+
+ if libcalamares.utils.mount(device,
+ mount_point,
+ fstype,
+ ",".join(["subvol=@", partition.get("options", "")])) != 0:
+ libcalamares.utils.warning("Cannot mount {}".format(device))
+
+ if not has_home_mount_point:
+ if libcalamares.utils.mount(device,
+ root_mount_point + "/home",
+ fstype,
+ ",".join(["subvol=@home", partition.get("options", "")])) != 0:
+ libcalamares.utils.warning("Cannot mount {}".format(device))
def run():
diff --git a/src/modules/partition/core/PartUtils.cpp b/src/modules/partition/core/PartUtils.cpp
index 07ab5acc1..92bcbace0 100644
--- a/src/modules/partition/core/PartUtils.cpp
+++ b/src/modules/partition/core/PartUtils.cpp
@@ -439,11 +439,14 @@ isEfiSystem()
bool
isEfiBootable( const Partition* candidate )
{
- cDebug() << "Check EFI bootable" << convenienceName( candidate ) << candidate->devicePath();
- cDebug() << Logger::SubEntry << "flags" << candidate->activeFlags();
-
- auto flags = PartitionInfo::flags( candidate );
+ const 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 ( flags.testFlag( KPM_PARTITION_FLAG_ESP ) )
{
@@ -455,19 +458,28 @@ isEfiBootable( const Partition* candidate )
while ( root && !root->isRoot() )
{
root = root->parent();
- cDebug() << Logger::SubEntry << "moved towards root" << Logger::Pointer( root );
}
// Strange case: no root found, no partition table node?
if ( !root )
{
+ cWarning() << "No root of partition table found.";
return false;
}
const PartitionTable* table = dynamic_cast< const PartitionTable* >( root );
- cDebug() << Logger::SubEntry << "partition table" << Logger::Pointer( table ) << "type"
- << ( table ? table->type() : PartitionTable::TableType::unknownTableType );
- return table && ( table->type() == PartitionTable::TableType::gpt ) && flags.testFlag( KPM_PARTITION_FLAG( Boot ) );
+ if ( !table )
+ {
+ 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
diff --git a/src/modules/partition/core/PartitionActions.cpp b/src/modules/partition/core/PartitionActions.cpp
index a78e8ff53..5d92d9b92 100644
--- a/src/modules/partition/core/PartitionActions.cpp
+++ b/src/modules/partition/core/PartitionActions.cpp
@@ -97,21 +97,6 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
// empty and a EFI boot partition, while BIOS starts at
// the 1MiB boundary (usually sector 2048).
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,
// 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 )
{
+ 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() );
Q_ASSERT( efiSectorCount > 0 );
@@ -203,6 +196,10 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
KPM_PARTITION_FLAG( None ) );
}
PartitionInfo::setFormat( swapPartition, true );
+ if ( gs->contains( "swapPartitionName" ))
+ {
+ swapPartition->setLabel( gs->value( "swapPartitionName" ).toString() );
+ }
core->createPartition( dev, swapPartition );
}
diff --git a/src/modules/partition/core/PartitionCoreModule.cpp b/src/modules/partition/core/PartitionCoreModule.cpp
index 706f99017..254d007c1 100644
--- a/src/modules/partition/core/PartitionCoreModule.cpp
+++ b/src/modules/partition/core/PartitionCoreModule.cpp
@@ -716,6 +716,8 @@ PartitionCoreModule::updateIsDirty()
void
PartitionCoreModule::scanForEfiSystemPartitions()
{
+ const bool wasEmpty = m_efiSystemPartitions.isEmpty();
+
m_efiSystemPartitions.clear();
QList< Device* > devices;
@@ -732,6 +734,11 @@ PartitionCoreModule::scanForEfiSystemPartitions()
{
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;
}
@@ -861,9 +868,9 @@ PartitionCoreModule::setBootLoaderInstallPath( const QString& path )
}
void
-PartitionCoreModule::initLayout( const QVariantList& config )
+PartitionCoreModule::initLayout( FileSystem::Type defaultFsType, const QVariantList& config )
{
- m_partLayout.init( config );
+ m_partLayout.init( defaultFsType, config );
}
void
@@ -875,7 +882,8 @@ PartitionCoreModule::layoutApply( Device* dev,
const PartitionRole& role )
{
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
// PartitionInfo::mountPoint() says where it will be mounted in the target system.
diff --git a/src/modules/partition/core/PartitionCoreModule.h b/src/modules/partition/core/PartitionCoreModule.h
index 46feb5f94..46604b97c 100644
--- a/src/modules/partition/core/PartitionCoreModule.h
+++ b/src/modules/partition/core/PartitionCoreModule.h
@@ -156,7 +156,11 @@ public:
/// @brief Set the path where the bootloader will be installed
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,
diff --git a/src/modules/partition/core/PartitionInfo.cpp b/src/modules/partition/core/PartitionInfo.cpp
index 87aa03b66..c4d239db8 100644
--- a/src/modules/partition/core/PartitionInfo.cpp
+++ b/src/modules/partition/core/PartitionInfo.cpp
@@ -52,7 +52,15 @@ PartitionTable::Flags
flags( const Partition* partition )
{
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() );
}
diff --git a/src/modules/partition/core/PartitionLayout.cpp b/src/modules/partition/core/PartitionLayout.cpp
index 15b18da93..d6f817af7 100644
--- a/src/modules/partition/core/PartitionLayout.cpp
+++ b/src/modules/partition/core/PartitionLayout.cpp
@@ -27,32 +27,10 @@
#include
#include
-static FileSystem::Type
-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() {}
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 )
, partMountPoint( mountPoint )
+ , partFileSystem( fs )
, partSize( size )
, partMinSize( minSize )
, partMaxSize( maxSize )
@@ -95,20 +78,6 @@ PartitionLayout::PartitionEntry::PartitionEntry( const QString& label,
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
PartitionLayout::addEntry( const PartitionEntry& entry )
@@ -124,7 +93,7 @@ PartitionLayout::addEntry( const PartitionEntry& entry )
}
void
-PartitionLayout::init( const QVariantList& config )
+PartitionLayout::init( FileSystem::Type defaultFsType, const QVariantList& config )
{
bool ok;
@@ -134,8 +103,7 @@ PartitionLayout::init( const QVariantList& config )
{
QVariantMap pentry = r.toMap();
- if ( !pentry.contains( "name" ) || !pentry.contains( "mountPoint" ) || !pentry.contains( "filesystem" )
- || !pentry.contains( "size" ) )
+ if ( !pentry.contains( "name" ) || !pentry.contains( "size" ) )
{
cError() << "Partition layout entry #" << config.indexOf( r )
<< "lacks mandatory attributes, switching to default layout.";
@@ -148,7 +116,7 @@ PartitionLayout::init( const QVariantList& config )
CalamaresUtils::getString( pentry, "type" ),
CalamaresUtils::getUnsignedInteger( pentry, "attributes", 0 ),
CalamaresUtils::getString( pentry, "mountPoint" ),
- CalamaresUtils::getString( pentry, "filesystem" ),
+ CalamaresUtils::getString( pentry, "filesystem", "unformatted" ),
CalamaresUtils::getSubMap( pentry, "features", ok ),
CalamaresUtils::getString( pentry, "size", QStringLiteral( "0" ) ),
CalamaresUtils::getString( pentry, "minSize", QStringLiteral( "0" ) ),
@@ -162,7 +130,7 @@ PartitionLayout::init( const QVariantList& config )
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 )
{
- qint64 sectors = entry.partSize.toSectors( availableSectors + partSectorsMap.value( &entry ),
- dev->logicalSize() );
+ qint64 sectors
+ = entry.partSize.toSectors( availableSectors + partSectorsMap.value( &entry ), dev->logicalSize() );
if ( entry.partMinSize.isValid() )
{
sectors = std::max( sectors, entry.partMinSize.toSectors( totalSectors, dev->logicalSize() ) );
@@ -258,13 +226,24 @@ PartitionLayout::createPartitions( Device* dev,
Partition* part = nullptr;
if ( luksPassphrase.isEmpty() )
{
- part = KPMHelpers::createNewPartition(
- parent, *dev, role, entry.partFileSystem, currentSector, currentSector + sectors - 1, KPM_PARTITION_FLAG( None ) );
+ part = KPMHelpers::createNewPartition( parent,
+ *dev,
+ role,
+ entry.partFileSystem,
+ currentSector,
+ currentSector + sectors - 1,
+ KPM_PARTITION_FLAG( None ) );
}
else
{
- part = KPMHelpers::createNewEncryptedPartition(
- parent, *dev, role, entry.partFileSystem, currentSector, currentSector + sectors - 1, luksPassphrase, KPM_PARTITION_FLAG( None ) );
+ part = KPMHelpers::createNewEncryptedPartition( parent,
+ *dev,
+ role,
+ entry.partFileSystem,
+ currentSector,
+ currentSector + sectors - 1,
+ luksPassphrase,
+ KPM_PARTITION_FLAG( None ) );
}
PartitionInfo::setFormat( part, true );
PartitionInfo::setMountPoint( part, entry.partMountPoint );
diff --git a/src/modules/partition/core/PartitionLayout.h b/src/modules/partition/core/PartitionLayout.h
index 9720ab765..6e0c73f8f 100644
--- a/src/modules/partition/core/PartitionLayout.h
+++ b/src/modules/partition/core/PartitionLayout.h
@@ -34,7 +34,7 @@ public:
QString partLabel;
QString partUUID;
QString partType;
- quint64 partAttributes;
+ quint64 partAttributes = 0;
QString partMountPoint;
FileSystem::Type partFileSystem = FileSystem::Unknown;
QVariantMap partFeatures;
@@ -44,8 +44,13 @@ public:
/// @brief All-zeroes PartitionEntry
PartitionEntry();
- /// @brief Parse @p mountPoint, @p size, @p minSize and @p maxSize to their respective member variables
- PartitionEntry( const QString& mountPoint,
+ /** @brief Parse @p mountPoint, @p size, @p minSize and @p maxSize to their respective member variables
+ *
+ * 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& minSize = QString(),
const QString& maxSize = QString() );
@@ -61,7 +66,7 @@ public:
const QString& minSize = QString(),
const QString& maxSize = QString() );
/// @brief Copy PartitionEntry
- PartitionEntry( const PartitionEntry& e );
+ PartitionEntry( const PartitionEntry& e ) = default;
bool isValid() const
{
@@ -78,7 +83,13 @@ public:
PartitionLayout( const PartitionLayout& layout );
~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 );
/**
@@ -93,7 +104,6 @@ public:
const PartitionRole& role );
private:
- FileSystem::Type m_defaultFsType;
QList< PartitionEntry > m_partLayout;
};
diff --git a/src/modules/partition/gui/EditExistingPartitionDialog.cpp b/src/modules/partition/gui/EditExistingPartitionDialog.cpp
index 1e66c539c..3de6e0c4c 100644
--- a/src/modules/partition/gui/EditExistingPartitionDialog.cpp
+++ b/src/modules/partition/gui/EditExistingPartitionDialog.cpp
@@ -136,8 +136,8 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
bool partResizedMoved = newFirstSector != m_partition->firstSector() || newLastSector != m_partition->lastSector();
cDebug() << "old boundaries:" << m_partition->firstSector() << m_partition->lastSector() << m_partition->length();
- cDebug() << "new boundaries:" << newFirstSector << newLastSector;
- cDebug() << "dirty status:" << m_partitionSizeController->isDirty();
+ cDebug() << Logger::SubEntry << "new boundaries:" << newFirstSector << newLastSector;
+ cDebug() << Logger::SubEntry << "dirty status:" << m_partitionSizeController->isDirty();
FileSystem::Type fsType = FileSystem::Unknown;
if ( m_ui->formatRadioButton->isChecked() )
@@ -147,6 +147,9 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
: FileSystem::typeForName( m_ui->fileSystemComboBox->currentText() );
}
+ const auto resultFlags = newFlags();
+ const auto currentFlags = PartitionInfo::flags( m_partition );
+
if ( partResizedMoved )
{
if ( m_ui->formatRadioButton->isChecked() )
@@ -157,20 +160,20 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
fsType,
newFirstSector,
newLastSector,
- newFlags() );
+ resultFlags );
PartitionInfo::setMountPoint( newPartition, PartitionInfo::mountPoint( m_partition ) );
PartitionInfo::setFormat( newPartition, true );
core->deletePartition( m_device, m_partition );
core->createPartition( m_device, newPartition );
- core->setPartitionFlags( m_device, newPartition, newFlags() );
+ core->setPartitionFlags( m_device, newPartition, resultFlags );
}
else
{
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 )
{
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
@@ -196,22 +199,22 @@ EditExistingPartitionDialog::applyChanges( PartitionCoreModule* core )
fsType,
m_partition->firstSector(),
m_partition->lastSector(),
- newFlags() );
+ resultFlags );
PartitionInfo::setMountPoint( newPartition, PartitionInfo::mountPoint( m_partition ) );
PartitionInfo::setFormat( newPartition, true );
core->deletePartition( m_device, m_partition );
core->createPartition( m_device, newPartition );
- core->setPartitionFlags( m_device, newPartition, newFlags() );
+ core->setPartitionFlags( m_device, newPartition, resultFlags );
}
}
else
{
- core->refreshPartition( 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 );
}
+ core->refreshPartition( m_device, m_partition );
}
}
}
diff --git a/src/modules/partition/gui/PartitionViewStep.cpp b/src/modules/partition/gui/PartitionViewStep.cpp
index 0431e1107..561fdaba7 100644
--- a/src/modules/partition/gui/PartitionViewStep.cpp
+++ b/src/modules/partition/gui/PartitionViewStep.cpp
@@ -543,6 +543,12 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
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
//
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 );
m_future->setFuture( future );
- m_core->initLayout( configurationMap.value( "partitionLayout" ).toList() );
+ m_core->initLayout( fsType == FileSystem::Unknown ? FileSystem::Ext4 : fsType,
+ configurationMap.value( "partitionLayout" ).toList() );
}
diff --git a/src/modules/partition/jobs/FillGlobalStorageJob.cpp b/src/modules/partition/jobs/FillGlobalStorageJob.cpp
index 8e7958e83..5384cf243 100644
--- a/src/modules/partition/jobs/FillGlobalStorageJob.cpp
+++ b/src/modules/partition/jobs/FillGlobalStorageJob.cpp
@@ -105,7 +105,7 @@ mapForPartition( Partition* partition, const QString& uuid )
using TR = Logger::DebugRow< const char* const, const QString& >;
deb << Logger::SubEntry << "mapping for" << partition->partitionPath() << partition->deviceNode()
<< 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( "fsName", map[ "fsName" ].toString() ) << TR( "uuid", uuid )
<< TR( "claimed", map[ "claimed" ].toString() );
@@ -153,7 +153,7 @@ FillGlobalStorageJob::prettyDescription() const
QString path = partitionMap.value( "device" ).toString();
QString mountPoint = partitionMap.value( "mountPoint" ).toString();
QString fsType = partitionMap.value( "fs" ).toString();
- if ( mountPoint.isEmpty() || fsType.isEmpty() )
+ if ( mountPoint.isEmpty() || fsType.isEmpty() || fsType == QString( "unformatted" ) )
{
continue;
}
diff --git a/src/modules/partition/partition.conf b/src/modules/partition/partition.conf
index 0f8764bfd..e5de69659 100644
--- a/src/modules/partition/partition.conf
+++ b/src/modules/partition/partition.conf
@@ -12,7 +12,8 @@ efiSystemPartition: "/boot/efi"
# If nothing is specified, the default size of 300MiB will be used.
# 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.
# efiSystemPartitionName: EFI
@@ -48,6 +49,11 @@ userSwapChoices:
# - reuse # Re-use existing swap, but don't create any (unsupported right now)
- 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)
# ensureSuspendToDisk: true
# neverCreateSwap: false
@@ -201,8 +207,8 @@ defaultFileSystemType: "ext4"
# - uuid: partition uuid (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)
-# - filesystem: filesystem type
-# - mountPoint: partition mount point
+# - filesystem: filesystem type (optional parameter; fs not created if "unformatted" or unset)
+# - 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)
# or
# % of the available drive space if a '%' is appended to the value
diff --git a/src/modules/partition/tests/CreateLayoutsTests.cpp b/src/modules/partition/tests/CreateLayoutsTests.cpp
index fb991fc82..04a06b2b3 100644
--- a/src/modules/partition/tests/CreateLayoutsTests.cpp
+++ b/src/modules/partition/tests/CreateLayoutsTests.cpp
@@ -25,6 +25,7 @@ class SmartStatus;
QTEST_GUILESS_MAIN( CreateLayoutsTests )
static CalamaresUtils::Partition::KPMManager* kpmcore = nullptr;
+static Calamares::JobQueue* jobqueue = nullptr;
using CalamaresUtils::operator""_MiB;
using CalamaresUtils::operator""_GiB;
@@ -39,7 +40,7 @@ CreateLayoutsTests::CreateLayoutsTests()
void
CreateLayoutsTests::init()
{
- std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
+ jobqueue = new Calamares::JobQueue( nullptr );
kpmcore = new CalamaresUtils::Partition::KPMManager();
}
@@ -47,6 +48,7 @@ void
CreateLayoutsTests::cleanup()
{
delete kpmcore;
+ delete jobqueue;
}
void
@@ -57,7 +59,7 @@ CreateLayoutsTests::testFixedSizePartition()
PartitionRole role( PartitionRole::Role::Any );
QList< Partition* > partitions;
- if ( !layout.addEntry( { QString( "/" ), QString( "5MiB" ) } ) )
+ if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/" ), QString( "5MiB" ) } ) )
{
QFAIL( qPrintable( "Unable to create / partition" ) );
}
@@ -77,7 +79,7 @@ CreateLayoutsTests::testPercentSizePartition()
PartitionRole role( PartitionRole::Role::Any );
QList< Partition* > partitions;
- if ( !layout.addEntry( { QString( "/" ), QString( "50%" ) } ) )
+ if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/" ), QString( "50%" ) } ) )
{
QFAIL( qPrintable( "Unable to create / partition" ) );
}
@@ -97,17 +99,17 @@ CreateLayoutsTests::testMixedSizePartition()
PartitionRole role( PartitionRole::Role::Any );
QList< Partition* > partitions;
- if ( !layout.addEntry( { QString( "/" ), QString( "5MiB" ) } ) )
+ if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/" ), QString( "5MiB" ) } ) )
{
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" ) );
}
- if ( !layout.addEntry( { QString( "/bkup" ), QString( "50%" ) } ) )
+ if ( !layout.addEntry( { FileSystem::Type::Ext4, QString( "/bkup" ), QString( "50%" ) } ) )
{
QFAIL( qPrintable( "Unable to create /bkup partition" ) );
}
@@ -122,7 +124,7 @@ CreateLayoutsTests::testMixedSizePartition()
}
#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
{
public:
diff --git a/src/modules/umount/main.py b/src/modules/umount/main.py
index 86ab1599a..0035a6b0f 100644
--- a/src/modules/umount/main.py
+++ b/src/modules/umount/main.py
@@ -39,10 +39,11 @@ def list_mounts(root_mount_point):
"""
lst = []
+ root_mount_point = os.path.normpath(root_mount_point)
for line in open("/etc/mtab").readlines():
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))
return lst
diff --git a/src/modules/users/CMakeLists.txt b/src/modules/users/CMakeLists.txt
index fdae38440..92a9e03e7 100644
--- a/src/modules/users/CMakeLists.txt
+++ b/src/modules/users/CMakeLists.txt
@@ -23,33 +23,45 @@ endif()
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
-set( JOB_SRC
+set( _users_src
+ # Jobs
CreateUserJob.cpp
+ MiscJobs.cpp
SetPasswordJob.cpp
SetHostNameJob.cpp
-)
-set( CONFIG_SRC
+ # Configuration
CheckPWQuality.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
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
UsersViewStep.cpp
UsersPage.cpp
- ${JOB_SRC}
- ${CONFIG_SRC}
UI
page_usersetup.ui
RESOURCES
users.qrc
LINK_PRIVATE_LIBRARIES
+ users_internal
calamaresui
${CRYPT_LIBRARIES}
${USER_EXTRA_LIB}
- Qt5::DBus
SHARED_LIB
)
@@ -63,10 +75,14 @@ calamares_add_test(
)
calamares_add_test(
- userscreatetest
+ usersgroupstest
SOURCES
- TestCreateUserJob.cpp
- CreateUserJob.cpp
+ TestGroupInformation.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(
@@ -82,8 +98,7 @@ calamares_add_test(
userstest
SOURCES
Tests.cpp
- ${JOB_SRC}
- ${CONFIG_SRC}
+ ${_users_src} # Build again with test-visibility
LIBRARIES
Qt5::DBus # HostName job can use DBus to systemd
${CRYPT_LIBRARIES} # SetPassword job uses crypt()
diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp
index f8904b9d4..0e70cc215 100644
--- a/src/modules/users/Config.cpp
+++ b/src/modules/users/Config.cpp
@@ -10,6 +10,7 @@
#include "Config.h"
#include "CreateUserJob.h"
+#include "MiscJobs.h"
#include "SetHostNameJob.h"
#include "SetPasswordJob.h"
@@ -34,6 +35,11 @@ static void
updateGSAutoLogin( bool doAutoLogin, const QString& login )
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
+ if ( !gs )
+ {
+ cWarning() << "No Global Storage available";
+ return;
+ }
if ( doAutoLogin && !login.isEmpty() )
{
@@ -95,11 +101,16 @@ Config::setUserShell( const QString& shell )
cWarning() << "User shell" << shell << "is not an absolute path.";
return;
}
- // The shell is put into GS because the CreateUser job expects it there
- auto* gs = Calamares::JobQueue::instance()->globalStorage();
- if ( gs )
+ if ( shell != m_userShell )
{
- 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
Config::setAutologinGroup( const QString& group )
{
- insertInGlobalStorage( QStringLiteral( "autologinGroup" ), group );
- emit autologinGroupChanged( group );
+ if ( group != m_autologinGroup )
+ {
+ 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
Config::setSudoersGroup( const QString& group )
{
- insertInGlobalStorage( QStringLiteral( "sudoersGroup" ), group );
- emit sudoersGroupChanged( group );
+ if ( group != m_sudoersGroup )
+ {
+ m_sudoersGroup = group;
+ insertInGlobalStorage( QStringLiteral( "sudoersGroup" ), group );
+ emit sudoersGroupChanged( group );
+ }
}
@@ -134,10 +171,9 @@ Config::setLoginName( const QString& login )
{
if ( login != m_loginName )
{
- updateGSAutoLogin( doAutoLogin(), login );
-
m_customLoginName = !login.isEmpty();
m_loginName = login;
+ updateGSAutoLogin( doAutoLogin(), login );
emit loginNameChanged( login );
emit loginNameStatusChanged( loginNameStatus() );
}
@@ -189,6 +225,8 @@ Config::setHostName( const QString& host )
{
if ( host != m_hostName )
{
+ m_customHostName = !host.isEmpty();
+ m_hostName = host;
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( host.isEmpty() )
{
@@ -198,9 +236,6 @@ Config::setHostName( const QString& host )
{
gs->insert( "hostname", host );
}
-
- m_customHostName = !host.isEmpty();
- m_hostName = host;
emit hostNameChanged( host );
emit hostNameStatusChanged( hostNameStatus() );
}
@@ -369,8 +404,8 @@ Config::setAutoLogin( bool b )
{
if ( b != m_doAutoLogin )
{
- updateGSAutoLogin( b, loginName() );
m_doAutoLogin = b;
+ updateGSAutoLogin( b, loginName() );
emit autoLoginChanged( b );
}
}
@@ -590,16 +625,59 @@ Config::checkReady()
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
- // in the configuration (which is a legitimate, if unusual, choice)
- // from a bad or missing configuration value.
- defaultGroups = CalamaresUtils::getStringList( map, QStringLiteral( "defaultGroups" ), QStringList { "#" } );
- if ( defaultGroups.contains( QStringLiteral( "#" ) ) )
+ defaultGroups.clear();
+
+ const QString key( "defaultGroups" );
+ auto groupsFromConfig = map.value( key ).toList();
+ if ( groupsFromConfig.isEmpty() )
{
- cWarning() << "Using fallback groups. Please check *defaultGroups* in users.conf";
- defaultGroups = QStringList { "lp", "video", "network", "storage", "wheel", "audio" };
+ if ( map.contains( key ) && map.value( key ).isValid() && map.value( key ).canConvert( QVariant::List ) )
+ {
+ // 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;
- j = new CreateUserJob(
- loginName(), fullName().isEmpty() ? loginName() : fullName(), doAutoLogin(), defaultGroups() );
+ if ( m_sudoersGroup.isEmpty() )
+ {
+ 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 ) );
j = new SetPasswordJob( loginName(), userPassword() );
diff --git a/src/modules/users/Config.h b/src/modules/users/Config.h
index e4057941c..d4bfee4a4 100644
--- a/src/modules/users/Config.h
+++ b/src/modules/users/Config.h
@@ -15,6 +15,7 @@
#include "Job.h"
#include "utils/NamedEnum.h"
+#include
#include
#include
@@ -30,7 +31,61 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( HostNameActions )
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
@@ -158,7 +213,12 @@ public:
/// Current setting for "require strong password"?
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)
QString userPassword() const { return m_userPassword; }
@@ -242,7 +302,7 @@ private:
PasswordStatus passwordStatus( const QString&, const QString& ) const;
void checkReady();
- QStringList m_defaultGroups;
+ QList< GroupDescription > m_defaultGroups;
QString m_userShell;
QString m_autologinGroup;
QString m_sudoersGroup;
diff --git a/src/modules/users/CreateUserJob.cpp b/src/modules/users/CreateUserJob.cpp
index 24b0f36d1..e08108a46 100644
--- a/src/modules/users/CreateUserJob.cpp
+++ b/src/modules/users/CreateUserJob.cpp
@@ -7,6 +7,8 @@
#include "CreateUserJob.h"
+#include "Config.h"
+
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
@@ -21,15 +23,9 @@
#include
-CreateUserJob::CreateUserJob( const QString& userName,
- const QString& fullName,
- bool autologin,
- const QStringList& defaultGroups )
+CreateUserJob::CreateUserJob( const Config* config )
: Calamares::Job()
- , m_userName( userName )
- , m_fullName( fullName )
- , m_autologin( autologin )
- , m_defaultGroups( defaultGroups )
+ , m_config( config )
{
}
@@ -37,68 +33,21 @@ CreateUserJob::CreateUserJob( const QString& userName,
QString
CreateUserJob::prettyName() const
{
- return tr( "Create user %1" ).arg( m_userName );
+ return tr( "Create user %1" ).arg( m_config->loginName() );
}
QString
CreateUserJob::prettyDescription() const
{
- return tr( "Create user %1." ).arg( m_userName );
+ return tr( "Create user %1." ).arg( m_config->loginName() );
}
QString
CreateUserJob::prettyStatusMessage() const
{
- return tr( "Creating user %1." ).arg( m_userName );
-}
-
-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
- }
- }
+ return m_status.isEmpty() ? tr( "Creating user %1." ).arg( m_config->loginName() ) : m_status;
}
static Calamares::JobResult
@@ -161,47 +110,22 @@ setUserGroups( const QString& loginName, const QStringList& groups )
Calamares::JobResult
CreateUserJob::exec()
{
- Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
- QDir destDir( gs->value( "rootMountPoint" ).toString() );
+ QDir destDir;
+ bool reuseHome = false;
- if ( gs->contains( "sudoersGroup" ) && !gs->value( "sudoersGroup" ).toString().isEmpty() )
{
- cDebug() << "[CREATEUSER]: preparing sudoers";
-
- QString sudoersLine = QString( "%%1 ALL=(ALL) ALL\n" ).arg( gs->value( "sudoersGroup" ).toString() );
- 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." ) );
- }
+ Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
+ destDir = QDir( gs->value( "rootMountPoint" ).toString() );
+ reuseHome = gs->value( "reuseHome" ).toBool();
}
- 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.
// 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 );
if ( existingHome.exists() )
{
@@ -216,20 +140,26 @@ CreateUserJob::exec()
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 )
{
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 )
{
return usergroupsResult;
}
- QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName );
- QString homeDir = QString( "/home/%1" ).arg( m_userName );
+ m_status = tr( "Setting file permissions" );
+ 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 } );
if ( commandResult.getExitCode() )
{
diff --git a/src/modules/users/CreateUserJob.h b/src/modules/users/CreateUserJob.h
index 0a46198b9..28a48c886 100644
--- a/src/modules/users/CreateUserJob.h
+++ b/src/modules/users/CreateUserJob.h
@@ -12,23 +12,21 @@
#include "Job.h"
-#include
+class Config;
class CreateUserJob : public Calamares::Job
{
Q_OBJECT
public:
- CreateUserJob( const QString& userName, const QString& fullName, bool autologin, const QStringList& defaultGroups );
+ CreateUserJob( const Config* config );
QString prettyName() const override;
QString prettyDescription() const override;
QString prettyStatusMessage() const override;
Calamares::JobResult exec() override;
private:
- QString m_userName;
- QString m_fullName;
- bool m_autologin;
- QStringList m_defaultGroups;
+ const Config* m_config;
+ QString m_status;
};
#endif /* CREATEUSERJOB_H */
diff --git a/src/modules/users/MiscJobs.cpp b/src/modules/users/MiscJobs.cpp
new file mode 100644
index 000000000..a62ada6d7
--- /dev/null
+++ b/src/modules/users/MiscJobs.cpp
@@ -0,0 +1,194 @@
+/* === This file is part of Calamares - ===
+ *
+ * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac
+ * SPDX-FileCopyrightText: 2020 Adriaan de Groot
+ * 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
+#include
+#include
+
+SetupSudoJob::SetupSudoJob( const QString& group )
+ : m_sudoGroup( group )
+{
+}
+
+QString
+SetupSudoJob::prettyName() const
+{
+ return tr( "Configure sudo
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();
+}
diff --git a/src/modules/users/MiscJobs.h b/src/modules/users/MiscJobs.h
new file mode 100644
index 000000000..fe4ff87c0
--- /dev/null
+++ b/src/modules/users/MiscJobs.h
@@ -0,0 +1,49 @@
+/* === This file is part of Calamares - ===
+ *
+ * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac
+ * SPDX-FileCopyrightText: 2020 Adriaan de Groot
+ * 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
diff --git a/src/modules/users/TestCreateUserJob.cpp b/src/modules/users/TestCreateUserJob.cpp
deleted file mode 100644
index fc2d74dcd..000000000
--- a/src/modules/users/TestCreateUserJob.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/* === This file is part of Calamares - ===
- *
- * SPDX-FileCopyrightText: 2020 Adriaan de Groot
- * 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
-#include
-
-// 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"
diff --git a/src/modules/users/TestGroupInformation.cpp b/src/modules/users/TestGroupInformation.cpp
new file mode 100644
index 000000000..dd4bfe78f
--- /dev/null
+++ b/src/modules/users/TestGroupInformation.cpp
@@ -0,0 +1,106 @@
+/* === This file is part of Calamares - ===
+ *
+ * SPDX-FileCopyrightText: 2020 Adriaan de Groot
+ * 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
+#include
+
+// 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"
diff --git a/src/modules/users/Tests.cpp b/src/modules/users/Tests.cpp
index 9ed7718c7..b687a6434 100644
--- a/src/modules/users/Tests.cpp
+++ b/src/modules/users/Tests.cpp
@@ -16,7 +16,7 @@
#include
// 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 bool addPasswordCheck( const QString& key, const QVariant& value, PasswordCheckList& passwordChecks );
@@ -33,6 +33,9 @@ public:
private Q_SLOTS:
void initTestCase();
+ // Derpy test for getting and setting regular values
+ void testGetSet();
+
void testDefaultGroups();
void testDefaultGroupsYAML_data();
void testDefaultGroupsYAML();
@@ -50,35 +53,97 @@ UserTests::initTestCase()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
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
UserTests::testDefaultGroups()
{
{
- QStringList groups;
+ QList< GroupDescription > groups;
QVariantMap hweelGroup;
QVERIFY( groups.isEmpty() );
hweelGroup.insert( "defaultGroups", QStringList { "hweel" } );
setConfigurationDefaultGroups( hweelGroup, groups );
QCOMPARE( groups.count(), 1 );
- QVERIFY( groups.contains( "hweel" ) );
+ QVERIFY( groups.contains( GroupDescription( "hweel" ) ) );
}
{
QStringList desired { "wheel", "root", "operator" };
- QStringList groups;
+ QList< GroupDescription > groups;
QVariantMap threeGroup;
QVERIFY( groups.isEmpty() );
threeGroup.insert( "defaultGroups", desired );
setConfigurationDefaultGroups( threeGroup, groups );
QCOMPARE( groups.count(), 3 );
- QVERIFY( !groups.contains( "hweel" ) );
- QCOMPARE( groups, desired );
+ QVERIFY( !groups.contains( GroupDescription( "hweel" ) ) );
+ for ( const auto& s : desired )
+ {
+ QVERIFY( groups.contains( GroupDescription( s ) ) );
+ }
}
{
- QStringList groups;
+ QList< GroupDescription > groups;
QVariantMap explicitEmpty;
QVERIFY( groups.isEmpty() );
explicitEmpty.insert( "defaultGroups", QStringList() );
@@ -87,22 +152,22 @@ UserTests::testDefaultGroups()
}
{
- QStringList groups;
+ QList< GroupDescription > groups;
QVariantMap missing;
QVERIFY( groups.isEmpty() );
setConfigurationDefaultGroups( missing, groups );
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;
QVERIFY( groups.isEmpty() );
typeMismatch.insert( "defaultGroups", 1 );
setConfigurationDefaultGroups( typeMismatch, groups );
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( "dashed list" ) << "tests/4-audio.conf" << 4 << "audio";
QTest::newRow( "blocked list" ) << "tests/3-wing.conf" << 3 << "wing";
+ QTest::newRow( "issue 1523" ) << "tests/5-issue-1523.conf" << 4 << "foobar";
}
void
@@ -130,6 +196,7 @@ UserTests::testDefaultGroupsYAML()
QFETCH( int, count );
QFETCH( QString, group );
+ // BUILD_AS_TEST is the source-directory path
QFile fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) );
QVERIFY( fi.exists() );
diff --git a/src/modules/users/tests/5-issue-1523.conf b/src/modules/users/tests/5-issue-1523.conf
new file mode 100644
index 000000000..a0c5e49ba
--- /dev/null
+++ b/src/modules/users/tests/5-issue-1523.conf
@@ -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
diff --git a/src/modules/users/users.conf b/src/modules/users/users.conf
index ee1ccbf1c..2e09ae123 100644
--- a/src/modules/users/users.conf
+++ b/src/modules/users/users.conf
@@ -3,26 +3,42 @@
#
# Configuration for the one-user-system user module.
#
-# Besides these settings, the user module also places the following
-# keys into the globalconfig area, based on user input in the view step.
+# Besides these settings, the users module also places the following
+# keys into the Global Storage area, based on user input in the view step.
#
# - hostname
# - username
# - password (obscured)
# - autologinUser (if enabled, set to username)
#
-# These globalconfig keys are set when the jobs for this module
-# are created.
+# These Global Storage keys are set when the configuration for this module
+# is read and when they are modified in the UI.
---
# Used as default groups for the created user.
# 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:
- - users
+ - name: users
+ must_exist: true
+ system: true
- lp
- video
- network
- storage
- - wheel
+ - name: wheel
+ must_exist: false
+ system: true
- audio
# Some Distributions require a 'autologin' group for the user.
diff --git a/src/modules/users/users.schema.yaml b/src/modules/users/users.schema.yaml
index 310df350f..81088032c 100644
--- a/src/modules/users/users.schema.yaml
+++ b/src/modules/users/users.schema.yaml
@@ -11,7 +11,16 @@ properties:
# Group settings
defaultGroups:
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 }
sudoersGroup: { type: string }
# Skip login (depends on displaymanager support)
diff --git a/src/modules/usersq/CMakeLists.txt b/src/modules/usersq/CMakeLists.txt
index 26c270bfb..7e3fffb8d 100644
--- a/src/modules/usersq/CMakeLists.txt
+++ b/src/modules/usersq/CMakeLists.txt
@@ -13,9 +13,8 @@ find_package( Crypt REQUIRED )
# Add optional libraries here
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 )
set_package_properties(
@@ -33,15 +32,11 @@ calamares_add_plugin( usersq
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
- ${_users}/Config.cpp
- ${_users}/CreateUserJob.cpp
- ${_users}/SetPasswordJob.cpp
UsersQmlViewStep.cpp
- ${_users}/SetHostNameJob.cpp
- ${_users}/CheckPWQuality.cpp
RESOURCES
usersq.qrc
LINK_PRIVATE_LIBRARIES
+ users_internal
calamaresui
${CRYPT_LIBRARIES}
${USER_EXTRA_LIB}