Compare commits

...

100 Commits

Author SHA1 Message Date
Adriaan de Groot
3aa4b7368c [partition] Log partition-model lookup failures 2021-12-15 13:57:20 +01:00
Adriaan de Groot
288674a924 [partition] Stub validator for LV name
- An empty LV name isn't allowed, but the create-partition-dialog
  doesn't check for that.
2021-12-15 13:01:58 +01:00
Adriaan de Groot
30b5be0fd4 [partition] Use name PartitionVector consistently
- move definition to a central place,
- drop per-class definitions and corresponding checks,
- use it and add constness as appropriate.
2021-12-15 12:19:59 +01:00
Adriaan de Groot
10334ab14f [partition] More constness, fixes build 2021-12-15 00:30:23 +01:00
Adriaan de Groot
46d69b04d4 [partition] Make the naming of type PartitionVector consistent
- static assert that various names are consistent (in gui, because we can have
  a dependency gui->core)
2021-12-15 00:20:23 +01:00
Adriaan de Groot
f1185d38d8 [partition] Drop unneeded parameters and stored references
- the dialog can return, by value, the selected PVs and
  doesn't need to hold a reference to external storage.

Doesn't compile because the actions in core want a reference.
2021-12-14 17:52:13 +01:00
Adriaan de Groot
2c16d812cd [partition] Factor out determining which PVs exist 2021-12-14 17:27:15 +01:00
Adriaan de Groot
7a071207a2 [partition] Hit VG code with a hammer until it compiles again 2021-12-14 16:53:33 +01:00
Adriaan de Groot
3edfa5ebb5 [partition] Sanitize the base dialog for VGs
- force broken compilation by changing order of parameters to
  constructor; this is to help identify all the consumers.
- improve naming of UI-access-methods.
2021-12-14 16:28:25 +01:00
Adriaan de Groot
898f4498e8 [partition] document UI-accessors as such 2021-12-14 16:02:26 +01:00
Adriaan de Groot
e2d5f01aa1 [partition] Group partition- and VG-methods 2021-12-14 15:53:50 +01:00
Adriaan de Groot
8d7c08612f [partition] Conventional const-ness 2021-12-14 15:52:41 +01:00
Adriaan de Groot
febc5f5b41 [partition] Conventional const-ness 2021-12-14 15:50:04 +01:00
Adriaan de Groot
35a4273127 [partition] Avoid storing references; provide getter for dialog. 2021-12-14 15:43:58 +01:00
Adriaan de Groot
4402ebd8e8 [partition] Clarify name peSize 2021-12-14 15:24:58 +01:00
Adriaan de Groot
587a18a6fa [partition] Use runCommand() for future-proofing 2021-12-14 12:50:27 +01:00
Adriaan de Groot
043619cd4b Merge branch 'improve-partition-reporting' into calamares
This strips out the === from KPMCore reports so that they are
more readable when presented in the error dialog. Introduces
some code-conveniences, too, but that is all under-the-hood.
2021-12-13 20:03:38 +01:00
Adriaan de Groot
f04394d014 [partition] Improve rendering of KPMCore errors 2021-12-13 20:02:52 +01:00
Adriaan de Groot
07354a26a9 [partition] Simplify debug calls to executables
- Use the Calamares support-functions for running lsblk and mount
  (these might need to have privilege support if Cala is not
  running as root, so this is future-proofing)
2021-12-13 20:02:52 +01:00
Adriaan de Groot
fdf0f208f0 [partition] Use lvalue-overload of execute() convenience
- These jobs may take a long time, and report progress; we need
  the operation around to be able to connect the signals and slots
2021-12-13 20:02:52 +01:00
Adriaan de Groot
6680584724 [partition] Use convenience function execute()
This job needs the lvalue-overload of execute() because it needs to
call a method on the operation after execute() finishes successfully.
2021-12-13 20:02:52 +01:00
Adriaan de Groot
c5573a1997 [partition] Add non-const lvalue overload for execute() 2021-12-13 20:02:52 +01:00
Adriaan de Groot
b8ce21d572 [partition] Use convenience function for running operations 2021-12-13 20:02:52 +01:00
Adriaan de Groot
1356012fb4 [partition] With rvalue, code becomes even more compact 2021-12-13 20:02:52 +01:00
Adriaan de Groot
8bb2c5fc6b [partition] Use convenience-method for running operation 2021-12-13 20:02:52 +01:00
Adriaan de Groot
dc7a1e43b7 [partition] Add helper for running a KPMCore operation
Most *partition* module jobs run an operation and turn that into
a JobResult -- ok if it succeeds, and with the report text otherwise.
Factor it out into a separate method that can be used as shorthand.
2021-12-13 20:02:52 +01:00
Adriaan de Groot
53c90516b2 Merge branch 'issue-1851' into calamares
FIXES #1851
2021-12-13 16:58:59 +01:00
Adriaan de Groot
d3ed5663d0 [preservefiles] Add a schema-file 2021-12-13 16:56:07 +01:00
Adriaan de Groot
778c2855f4 [preservefiles] Introduce the notion of optionally-preserved files 2021-12-13 16:34:38 +01:00
Adriaan de Groot
445ed870cc [preservefiles] Simplify code to help gcc warnings 2021-12-13 15:53:42 +01:00
Adriaan de Groot
3be52f8b37 [preservefiles] Expand tests with reading some existing config-items 2021-12-13 15:53:42 +01:00
Adriaan de Groot
a1b7ba0dc5 [preservefiles] Accessor for item-type (needed for tests) 2021-12-13 15:44:07 +01:00
Adriaan de Groot
8b5e49d980 [preservefiles] Add (stub) tests 2021-12-13 15:07:24 +01:00
Adriaan de Groot
90f6ea1fc8 [preservefiles] polish the documentation 2021-12-13 15:07:24 +01:00
Adriaan de Groot
238672ef78 [preservefiles] Split file-items into separate header
Put the Item class in a separate header; give it functionality
to create itself from Variants (e.g. from the configuration data)
and to run itself (do whatever the item is supposed to do).
This makes the polymorphic approach unnecessary: we just have
items that are sufficiently smart.

This moves do-a-thing to the Item, while the Job now has one
job: be a loop around creating Items and running items.
2021-12-13 15:05:05 +01:00
Adriaan de Groot
b1ecbb4151 [preservefiles] Start cleanup of structure, polymorphism 2021-12-13 15:05:05 +01:00
Adriaan de Groot
795b2c88e8 Merge pull request #1852 from killajoe/patch-1
[preservefiles] Fix typo in preservefiles.conf
2021-12-13 00:19:34 +01:00
Johannes Kamprad
becb1d5710 Update preservefiles.conf 2021-12-12 01:22:22 +01:00
arcolinuxz
5b225cf960 [preservefiles] Put the logs in /var/log 2021-12-11 23:58:23 +01:00
Adriaan de Groot
6261f8a5cb Changes: post-release housekeeping 2021-12-11 15:33:22 +01:00
Adriaan de Groot
132ebd2c2d [networkcfg] NetworkManager files are UTF-8 encoded
The filenames don't matter, but the contents of the file are also
UTF-8, and depending on the default encoding of the Python
interpreter, this can fail on non-ASCII characters in the
file. Set the encoding explicitly while reading and writing
the NetworkManager configuration files.

FIXES #1848
2021-12-11 15:12:51 +01:00
Adriaan de Groot
db86c24638 Changes: pre-hotfix-release housekeeping 2021-12-11 13:23:23 +01:00
Adriaan de Groot
03da766b39 [partition] Keep 64-bit integers for swap sizes
FIXES #1849
2021-12-11 13:19:08 +01:00
Adriaan de Groot
adaed52818 Changes: post-release housekeeping 2021-12-10 17:01:42 +01:00
Adriaan de Groot
7ac42b5f40 [umount] Tests don't like an empty config
- modules with no configuration should be marked 'noconfig',
  but umount is special: it has no **useful** configuration
  (maybe no **non-deprecated** configuration), but isn't
  marked 'noconfig' **yet**.
2021-12-10 16:44:01 +01:00
Calamares CI
3cdb019de7 i18n: [calamares] Automatic merge of Transifex translations 2021-12-10 15:55:58 +01:00
Adriaan de Groot
b4afedc79e Changes: pre-release housekeeping 2021-12-10 15:46:11 +01:00
Adriaan de Groot
c834a5066d [umount] Make it much more clear that the logfiles-thing is going away. 2021-12-05 02:26:23 +01:00
Adriaan de Groot
1d96c5af46 [partition] Table type 'none' is a late addition. 2021-12-05 01:32:51 +01:00
Adriaan de Groot
bc2713ccbb [libcalamares] Add string functions for lstrip() and rstrip()-like 2021-12-08 14:08:37 +01:00
Adriaan de Groot
bb948c47dc [fstab] Cut the example btrfs flags to 'defaults'
Testing shows that the flags can influence -- maybe cause -- data
corruption when noatime is set.

FIXES #1846
2021-12-08 13:06:53 +01:00
Adriaan de Groot
fa29ae2c5e Merge branch 'reduce-warnings' into calamares 2021-12-08 01:02:32 +01:00
Adriaan de Groot
043bdc36d6 Changes: document contributors 2021-12-08 01:02:27 +01:00
Adriaan de Groot
f0eb7ffbda [partition Untangle, Warnings--
The translations apply to labels and a tooltip, which depends on
the partition-table type. Move the strings together and make
the whole range of the switch explicitly.
2021-12-08 00:59:20 +01:00
Adriaan de Groot
e8ca298712 [partition] Reduce warnings 2021-12-08 00:15:01 +01:00
Adriaan de Groot
13700b18c8 [partition] Warnings--
- remove superfluous `break`
- massage types around partition sizes
2021-12-08 00:06:17 +01:00
Adriaan de Groot
1197d8c750 [interactiveterminal] Warnings-- with KF5 5.86-or-later 2021-12-07 18:19:32 +01:00
Adriaan de Groot
09f47b5762 [partition] Build tests with consistent flags (in particular, KPMCore4-API flags) 2021-12-07 15:51:45 +01:00
Adriaan de Groot
4611545f93 [libcalamares] Warnings-- on switch()
- some switch statements handle a bunch of items explicitly,
  then default the rest. Clang complains about that. Turn off
  the warning for these specific switches, since there's dozens
  of values that simply do not need to be handled.
2021-12-07 15:42:14 +01:00
Adriaan de Groot
6e715205d7 [partition] Warnings-- by calling formatting consistently 2021-12-07 15:36:11 +01:00
Adriaan de Groot
09a03fbbc0 [partition] Warnings--: we don't care about one-byte-in-10^12 2021-12-07 15:31:49 +01:00
Adriaan de Groot
bb3f4442f5 [partition] Warnings-reduction
- use consistent size-formatting
- needs an out-of-line virtual function
2021-12-07 15:30:21 +01:00
Adriaan de Groot
5b05110351 [partition] Add convenience function formatByteSize
We want to use the KPMCore function consistently, but Calamares
uses a qint64 most of the time. Centralize the cast to double
in one place in the code.
2021-12-07 15:29:02 +01:00
Adriaan de Groot
eda85c176a [tracking] Avoid unused-deprecated-methods warnings
- these are internal classes, with no real Qt machinery; remove
  the Q_OBJECT macros.
- replace the tr() calls with calls with an explicit context,
  so that translations do not change.
2021-12-07 15:07:07 +01:00
Adriaan de Groot
32da51b44c [libcalamares] Avoid warnings in Boost::Python macros 2021-12-07 14:48:19 +01:00
Adriaan de Groot
0b6239a996 [libcalamaresui] Warnings-- : we know TCP ports are 16 bit 2021-12-07 14:28:55 +01:00
Adriaan de Groot
79ae3cd00f Merge branch 'shuffle-error-dialog' into calamares 2021-12-07 14:27:13 +01:00
Adriaan de Groot
c2e63f4a6b [libcalamaresui] Don't bother tagging nonexistent 3rdparty sources 2021-12-07 14:20:31 +01:00
Adriaan de Groot
8b804c4ae0 [libcalamaresui] Improve icon+heading layout
- Icon was too wide, heading and message placed off to the side
2021-12-07 14:15:43 +01:00
Adriaan de Groot
3030a710cc [libcalamaresui] Simplify 2021-12-07 12:58:22 +01:00
Adriaan de Groot
b07c9bb4af [libcalamaresui] Use meaningful type for Upload info
- use a struct with named fields instead of a tuple
- offer an operator bool() for the logic of does-it-make-sense-to-upload
2021-12-07 12:53:43 +01:00
Adriaan de Groot
3234de5753 [libcalamaresui] Make web-paste decision more readable 2021-12-07 12:48:17 +01:00
Adriaan de Groot
2f9edb3e08 [libcalamaresui] Code style 2021-12-07 12:44:19 +01:00
Adriaan de Groot
ca7f288488 [libcalamaresui] APIDOX for ErrorDialog 2021-12-07 12:40:05 +01:00
Adriaan de Groot
49890acd04 [libcalamaresui] Fix build after move 2021-12-07 12:39:50 +01:00
Adriaan de Groot
dc11dd2203 [libcalamaresui] Move ErrorDialog to the widgets/ part 2021-12-07 12:24:41 +01:00
Adriaan de Groot
6e59177f54 Merge pull request #1843 from LordTermor/calamares
Rework of error dialog
2021-12-07 12:06:01 +01:00
Calamares CI
d2ac201b98 i18n: [desktop] Automatic merge of Transifex translations 2021-12-07 12:00:03 +01:00
Calamares CI
c8ea3bccf7 i18n: [desktop] Automatic merge of Transifex translations 2021-12-07 12:00:03 +01:00
Calamares CI
8e4c9b8bd6 i18n: [calamares] Automatic merge of Transifex translations 2021-12-07 12:00:03 +01:00
Adriaan de Groot
13196ed2e2 CI: sort .desktop entries for i18n
The ordering of entries jumps around sometimes when reading from
Transifex (this might be Python unordered dictionaries, or based
on translation statistics -- I can't tell). Force an order by
sorting on language code and key-name so they all end up grouped
by language code, sorted Name Icon GenericName Comment.

Although this shuffles some more entries now, longer-term it
will reduce churn in the .desktop file.
2021-12-07 11:59:36 +01:00
Adriaan de Groot
eb2cf60466 CI: support FreeBSD when pulling translations 2021-12-07 10:57:06 +01:00
Adriaan de Groot
149f3ff3fe [partition] Reduce warnings about shadowed variables 2021-12-06 14:52:33 +01:00
Adriaan de Groot
d89553a777 [partition] Avoid problems with MessageAndPath in containers (drop const) 2021-12-06 14:46:26 +01:00
Adriaan de Groot
890c17cd71 [libcalamares] Expand error-logging when creating files 2021-12-06 14:46:26 +01:00
Adriaan de Groot
6ef7acc108 [libcalamares] Add minor tests for new readTargetFile 2021-12-06 14:46:26 +01:00
Adriaan de Groot
baf8297cc4 [libcalamares] Reading a file from target system 2021-12-06 14:46:26 +01:00
Adriaan de Groot
47f2dd3c18 Merge pull request #1844 from dalto8/openswap
Add support for unlocking encrypted swap with root on a btrfs subvol
2021-12-06 10:42:44 +01:00
dalto
6e08da6c8d [bootloader] Fix error with systemd-boot when path exists in the ESP 2021-12-06 10:31:58 +01:00
Adriaan de Groot
b8c02587ae Changes: post-release housekeeping 2021-12-06 10:29:16 +01:00
Artem Grinev
aa332477fd [libcalamaresui] Run clang-format on TranslationFix.cpp 2021-12-06 03:11:16 +04:00
Artem Grinev
d9f7726f7d [libcalamaresui] Add SPDX-header for Error Dialog files 2021-12-06 02:41:17 +04:00
Artem Grinev
2f2a418cc4 [libcalamaresui] Run clang-format 2021-12-06 02:37:11 +04:00
Artem Grinev
2dd77ee828 [libcalamaresui] Initialize Error Dialog field 2021-12-06 02:31:05 +04:00
Artem Grinev
bfa7b9a792 [libcalamaresui] Use translation fix for Error Dialog 2021-12-06 02:27:18 +04:00
Artem Grinev
32c5e18db0 [libcalamaresui] Add QDialogButtonBox translation fix 2021-12-06 02:26:13 +04:00
Adriaan de Groot
003e7949e3 Merge pull request #1841 from dalto8/btrfs-nesting
[mount] Ensure path is available when creating nested btrfs subvolumes
2021-12-05 20:54:23 +01:00
dalto
e8936392e3 [luksopenswaphookcfg] Add support unlocking swap with root on a btrfs subvol 2021-12-05 13:17:23 -06:00
Artem Grinev
6bf0da7230 [libcalamaresui] Initial rework of error dialog 2021-12-05 04:50:13 +04:00
dalto
6e8779cbce [mount] Ensure path is available when creating nested btrfs subvolumes 2021-12-04 08:53:15 -06:00
86 changed files with 1691 additions and 766 deletions

View File

@@ -7,6 +7,54 @@ contributors are listed. Note that Calamares does not have a historical
changelog -- this log starts with version 3.2.0. The release notes on the changelog -- this log starts with version 3.2.0. The release notes on the
website will have to do for older versions. website will have to do for older versions.
# 3.2.50 (unreleased) #
This release contains contributions from (alphabetically by first name):
- Erik Dubois
## Core ##
- No core changes yet
## Modules ##
- *networkcfg* could fail to update the NetworkManager configuration
if the SSID or username contained non-ASCII characters **and** the
default Python text-file encoding was set to ASCII. The files are
now read and written in UTF-8, explicitly. #1848
- *preservefiles* was missing some necessary features, needed for it
to replace the deprecated log-file-saving functionality in the *umount*
module. (Thanks Erik and Joe for testing) #1851
# 3.2.49.1 (2021-12-11) #
This is a hot-fix release, to fix a regression in the calculation of
swap-size. Reported by EndeavourOS (Joe Kamprad) and Xero Linux.
# 3.2.49 (2021-12-10) #
This release contains contributions from (alphabetically by first name):
- Artem Grinev
- Evan James
Distributions are **specifically** reminded to update the *umount* module
configuration (and to use *preservefiles* if needed).
## Core ##
- Errors (e.g. when an installation fails for whatever reason) are displayed
in a dialog with a scrollable details panel, rather than growing up
to the size of the screen. (Thanks Artem)
## Modules ##
- *bootloader* better supports multiple installations of the same OS.
- *mount* supports btrfs subvolumes on subdirectories of / now.
- *partition* now supports "deep" btrfs subvolume names, e.g. a
separate subvolume for `/usr/local`. (Thanks Evan)
- The *umount* module now warns if the "preserve log file" feature is used.
This has been deprecated for a long time: use the *preservefiles* module
instead. A future release will turn this into an error.
# 3.2.48 (2021-12-03) # # 3.2.48 (2021-12-03) #
This release contains contributions from (alphabetically by first name): This release contains contributions from (alphabetically by first name):

View File

@@ -41,11 +41,11 @@
# TODO:3.3: Require CMake 3.12 # TODO:3.3: Require CMake 3.12
cmake_minimum_required( VERSION 3.3 FATAL_ERROR ) cmake_minimum_required( VERSION 3.3 FATAL_ERROR )
project( CALAMARES project( CALAMARES
VERSION 3.2.48 VERSION 3.2.50
LANGUAGES C CXX LANGUAGES C CXX
) )
set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development
if( CALAMARES_VERSION_RC EQUAL 1 AND CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR ) if( CALAMARES_VERSION_RC EQUAL 1 AND CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
message( FATAL_ERROR "Do not build development versions in the source-directory." ) message( FATAL_ERROR "Do not build development versions in the source-directory." )
endif() endif()

View File

@@ -21,10 +21,18 @@ Name[as]=চিছটেম ইনস্তল কৰক
Icon[as]=কেলামাৰেচ Icon[as]=কেলামাৰেচ
GenericName[as]=চিছটেম ইনস্তলাৰ GenericName[as]=চিছটেম ইনস্তলাৰ
Comment[as]=কেলামাৰেচ — চিছটেম​ ইনস্তলাৰ Comment[as]=কেলামাৰেচ — চিছটেম​ ইনস্তলাৰ
Name[ast]=Instalar el sistema
Icon[ast]=calamares
GenericName[ast]=Instalador del sistema
Comment[ast]=Calamares — Instalador del sistema
Name[az]=Sistemi Quraşdırmaq Name[az]=Sistemi Quraşdırmaq
Icon[az]=calamares Icon[az]=calamares
GenericName[az]=Sistem Quraşdırıcısı GenericName[az]=Sistem Quraşdırıcısı
Comment[az]=Calamares Sistem Quraşdırıcısı Comment[az]=Calamares Sistem Quraşdırıcısı
Name[az_AZ]=Sistemi quraşdırmaq
Icon[az_AZ]=calamares
GenericName[az_AZ]=Sistem quraşdırcısı
Comment[az_AZ]=Calamares — Sistem Quraşdırıcısı
Name[be]=Усталяваць сістэму Name[be]=Усталяваць сістэму
Icon[be]=calamares Icon[be]=calamares
GenericName[be]=Усталёўшчык сістэмы GenericName[be]=Усталёўшчык сістэмы
@@ -41,6 +49,10 @@ Name[ca]=Instal·la el sistema
Icon[ca]=calamares Icon[ca]=calamares
GenericName[ca]=Instal·lador de sistema GenericName[ca]=Instal·lador de sistema
Comment[ca]=Calamares — Instal·lador de sistema Comment[ca]=Calamares — Instal·lador de sistema
Name[cs_CZ]=Nainstalovat systém
Icon[cs_CZ]=calamares
GenericName[cs_CZ]=Instalátor systému
Comment[cs_CZ]=Calamares instalátor operačních systémů
Name[da]=Installér system Name[da]=Installér system
Icon[da]=calamares Icon[da]=calamares
GenericName[da]=Systeminstallationsprogram GenericName[da]=Systeminstallationsprogram
@@ -57,10 +69,19 @@ Name[en_GB]=Install System
Icon[en_GB]=calamares Icon[en_GB]=calamares
GenericName[en_GB]=System Installer GenericName[en_GB]=System Installer
Comment[en_GB]=Calamares — System Installer Comment[en_GB]=Calamares — System Installer
Name[eo]=Instali Sistemo
Icon[eo]=calamares
GenericName[eo]=Sistema Instalilo
Comment[eo]=Calamares — Sistema Instalilo
Name[es]=Instalar Sistema Name[es]=Instalar Sistema
Icon[es]=calamares Icon[es]=calamares
GenericName[es]=Instalador del Sistema GenericName[es]=Instalador del Sistema
Comment[es]=Calamares — Instalador del Sistema Comment[es]=Calamares — Instalador del Sistema
Name[es_MX]=Instalar el Sistema
Icon[es_MX]=calamares
GenericName[es_MX]=Instalador del sistema
Comment[es_MX]=Calamares - Instalador del sistema
Name[es_PR]=Instalar el sistema
Name[et]=Paigalda süsteem Name[et]=Paigalda süsteem
Icon[et]=calamares Icon[et]=calamares
GenericName[et]=Süsteemipaigaldaja GenericName[et]=Süsteemipaigaldaja
@@ -73,7 +94,10 @@ Name[fa]=نصب سامانه
Icon[fa]=کالامارس Icon[fa]=کالامارس
GenericName[fa]=نصب‌کننده سامانه GenericName[fa]=نصب‌کننده سامانه
Comment[fa]=کالامارس — نصب‌کننده سامانه Comment[fa]=کالامارس — نصب‌کننده سامانه
Name[es_PR]=Instalar el sistema Name[fi_FI]=Asenna järjestelmä
Icon[fi_FI]=calamares
GenericName[fi_FI]=Järjestelmän asennusohjelma
Comment[fi_FI]=Calamares — Järjestelmän asentaja
Name[fr]=Installer le système Name[fr]=Installer le système
Icon[fr]=calamares Icon[fr]=calamares
GenericName[fr]=Installateur système GenericName[fr]=Installateur système
@@ -98,10 +122,6 @@ Name[hr]=Instaliraj sustav
Icon[hr]=calamares Icon[hr]=calamares
GenericName[hr]=Instalacija sustava GenericName[hr]=Instalacija sustava
Comment[hr]=Calamares — Instalacija sustava Comment[hr]=Calamares — Instalacija sustava
Name[ie]=Installar li sistema
Icon[ie]=calamares
GenericName[ie]=Installator del sistema
Comment[ie]=Calamares — Installator del sistema
Name[hu]=Rendszer telepítése Name[hu]=Rendszer telepítése
Icon[hu]=calamares Icon[hu]=calamares
GenericName[hu]=Rendszertelepítő GenericName[hu]=Rendszertelepítő
@@ -110,14 +130,18 @@ Name[id]=Instal Sistem
Icon[id]=calamares Icon[id]=calamares
GenericName[id]=Pemasang GenericName[id]=Pemasang
Comment[id]=Calamares — Pemasang Sistem Comment[id]=Calamares — Pemasang Sistem
Name[ie]=Installar li sistema
Icon[ie]=calamares
GenericName[ie]=Installator del sistema
Comment[ie]=Calamares — Installator del sistema
Name[is]=Setja upp kerfið Name[is]=Setja upp kerfið
Icon[is]=calamares Icon[is]=calamares
GenericName[is]=Kerfis uppsetning GenericName[is]=Kerfis uppsetning
Comment[is]=Calamares — Kerfis uppsetning Comment[is]=Calamares — Kerfis uppsetning
Name[cs_CZ]=Nainstalovat systém Name[it_IT]=Installa il sistema
Icon[cs_CZ]=calamares Icon[it_IT]=calamares
GenericName[cs_CZ]=Instalátor systému GenericName[it_IT]=Programma d'installazione del sistema
Comment[cs_CZ]=Calamares instalátor operačních systémů Comment[it_IT]=Calamares — Programma d'installazione del sistema
Name[ja]=システムをインストール Name[ja]=システムをインストール
Icon[ja]=calamares Icon[ja]=calamares
GenericName[ja]=システムインストーラー GenericName[ja]=システムインストーラー
@@ -130,10 +154,6 @@ Name[lt]=Įdiegti Sistemą
Icon[lt]=calamares Icon[lt]=calamares
GenericName[lt]=Sistemos diegimas į kompiuterį GenericName[lt]=Sistemos diegimas į kompiuterį
Comment[lt]=Calamares — Sistemos diegimo programa Comment[lt]=Calamares — Sistemos diegimo programa
Name[it_IT]=Installa il sistema
Icon[it_IT]=calamares
GenericName[it_IT]=Programma d'installazione del sistema
Comment[it_IT]=Calamares — Programma d'installazione del sistema
Name[mk]=Инсталирај го системот Name[mk]=Инсталирај го системот
Icon[mk]=calamares Icon[mk]=calamares
GenericName[mk]=Системен Инсталер GenericName[mk]=Системен Инсталер
@@ -146,14 +166,14 @@ Name[nb]=Installer System
Icon[nb]=calamares Icon[nb]=calamares
GenericName[nb]=Systeminstallatør GenericName[nb]=Systeminstallatør
Comment[nb]=Calamares-systeminstallatør Comment[nb]=Calamares-systeminstallatør
Name[ne_NP]= सिस्टम इन्स्टल गर्नुहोस्
Icon[ne_NP]=Calamares
GenericName[ne_NP]=सिस्टम इन्स्टलर
Comment[ne_NP]=Calamares - सिस्टम इन्स्टलर
Name[nl]=Installeer systeem Name[nl]=Installeer systeem
Icon[nl]=calamares Icon[nl]=calamares
GenericName[nl]=Installatieprogramma GenericName[nl]=Installatieprogramma
Comment[nl]=Calamares — Installatieprogramma Comment[nl]=Calamares — Installatieprogramma
Name[az_AZ]=Sistemi quraşdırmaq
Icon[az_AZ]=calamares
GenericName[az_AZ]=Sistem quraşdırcısı
Comment[az_AZ]=Calamares — Sistem Quraşdırıcısı
Name[pl]=Zainstaluj system Name[pl]=Zainstaluj system
Icon[pl]=calamares Icon[pl]=calamares
GenericName[pl]=Instalator systemu GenericName[pl]=Instalator systemu
@@ -162,6 +182,10 @@ Name[pt_BR]=Sistema de Instalação
Icon[pt_BR]=calamares Icon[pt_BR]=calamares
GenericName[pt_BR]=Instalador de Sistema GenericName[pt_BR]=Instalador de Sistema
Comment[pt_BR]=Calamares — Instalador de Sistema Comment[pt_BR]=Calamares — Instalador de Sistema
Name[pt_PT]=Instalar Sistema
Icon[pt_PT]=calamares
GenericName[pt_PT]=Instalador de Sistema
Comment[pt_PT]=Instalador de Sistema - Calamares
Name[ro]=Instalează sistemul Name[ro]=Instalează sistemul
Icon[ro]=calamares Icon[ro]=calamares
GenericName[ro]=Instalator de sistem GenericName[ro]=Instalator de sistem
@@ -183,15 +207,11 @@ Name[sq]=Instalo Sistemin
Icon[sq]=calamares Icon[sq]=calamares
GenericName[sq]=Instalues Sistemi GenericName[sq]=Instalues Sistemi
Comment[sq]=Calamares — Instalues Sistemi Comment[sq]=Calamares — Instalues Sistemi
Name[fi_FI]=Asenna järjestelmä
Icon[fi_FI]=calamares
GenericName[fi_FI]=Järjestelmän asennusohjelma
Comment[fi_FI]=Calamares — Järjestelmän asentaja
Name[sr@latin]=Instaliraj sistem
Name[sr]=Инсталирај систем Name[sr]=Инсталирај систем
Icon[sr]=calamares Icon[sr]=calamares
GenericName[sr]=Инсталатер система GenericName[sr]=Инсталатер система
Comment[sr]=Каламарес — инсталатер система Comment[sr]=Каламарес — инсталатер система
Name[sr@latin]=Instaliraj sistem
Name[sv]=Installera system Name[sv]=Installera system
Icon[sv]=calamares Icon[sv]=calamares
GenericName[sv]=Systeminstallerare GenericName[sv]=Systeminstallerare
@@ -201,6 +221,10 @@ Icon[tg]=calamares
GenericName[tg]=Насбкунандаи низомӣ GenericName[tg]=Насбкунандаи низомӣ
Comment[tg]=Calamares — Насбкунандаи низомӣ Comment[tg]=Calamares — Насбкунандаи низомӣ
Name[th]=ติดตั้งระบบ Name[th]=ติดตั้งระบบ
Name[tr_TR]=Sistemi Yükle
Icon[tr_TR]=calamares
GenericName[tr_TR]=Sistem Yükleyici
Comment[tr_TR]=Calamares — Sistem Yükleyici
Name[uk]=Встановити Систему Name[uk]=Встановити Систему
Icon[uk]=calamares Icon[uk]=calamares
GenericName[uk]=Встановлювач системи GenericName[uk]=Встановлювач системи
@@ -217,27 +241,3 @@ Name[zh_TW]=安裝系統
Icon[zh_TW]=calamares Icon[zh_TW]=calamares
GenericName[zh_TW]=系統安裝程式 GenericName[zh_TW]=系統安裝程式
Comment[zh_TW]=Calamares ── 系統安裝程式 Comment[zh_TW]=Calamares ── 系統安裝程式
Name[ast]=Instalar el sistema
Icon[ast]=calamares
GenericName[ast]=Instalador del sistema
Comment[ast]=Calamares — Instalador del sistema
Name[eo]=Instali Sistemo
Icon[eo]=calamares
GenericName[eo]=Sistema Instalilo
Comment[eo]=Calamares — Sistema Instalilo
Name[ne_NP]= सिस्टम इन्स्टल गर्नुहोस्
Icon[ne_NP]=Calamares
GenericName[ne_NP]=सिस्टम इन्स्टलर
Comment[ne_NP]=Calamares - सिस्टम इन्स्टलर
Name[es_MX]=Instalar el Sistema
Icon[es_MX]=calamares
GenericName[es_MX]=Instalador del sistema
Comment[es_MX]=Calamares - Instalador del sistema
Name[pt_PT]=Instalar Sistema
Icon[pt_PT]=calamares
GenericName[pt_PT]=Instalador de Sistema
Comment[pt_PT]=Instalador de Sistema - Calamares
Name[tr_TR]=Sistemi Yükle
Icon[tr_TR]=calamares
GenericName[tr_TR]=Sistem Yükleyici
Comment[tr_TR]=Calamares — Sistem Yükleyici

View File

@@ -108,6 +108,21 @@ awk '
skip=0; print $0; skip=0; print $0;
}}' < calamares.desktop > calamares.desktop.new }}' < calamares.desktop > calamares.desktop.new
mv calamares.desktop.new calamares.desktop mv calamares.desktop.new calamares.desktop
# Now group translated key-names (Name, Icon, Description, ..) by sorted
# language key rather than random-ish language-key order (which shuffles
# entries around).
#
# First, the non-translated lines
grep -v '\[.*\]=' calamares.desktop > calamares.desktop.new
# The translated lines:
# - replace (the first) [] by | so we have a consistent field separator
# - sort based on field 2, then 1 (language code, then reversed key-name)
# - replace the first | by [, the first (remaining) | by ]
# Effectively this puts the fields in this order: Name, Icon, Generic Name,
# Comment -- within each language key. This keeps churn down since the
# language codes and key-names are constant.
grep '\[.*\]=' calamares.desktop | sed -e 's/\[/|/' -e 's/\]/|/' | sort -t '|' -k 2,2 -k 1,1r | sed -e 's/|/\[/' | sed -e 's/|/\]/' >> calamares.desktop.new
mv calamares.desktop.new calamares.desktop
git add --verbose calamares.desktop git add --verbose calamares.desktop
git commit "$AUTHOR" --message="i18n: [desktop] $BOILERPLATE" | true git commit "$AUTHOR" --message="i18n: [desktop] $BOILERPLATE" | true
@@ -116,6 +131,19 @@ git commit "$AUTHOR" --message="i18n: [desktop] $BOILERPLATE" | true
# PO-Created line). This applies only to modules which use po-files. # PO-Created line). This applies only to modules which use po-files.
git diff --numstat src/modules | awk '($1==1 && $2==1){print $3}' | xargs git checkout -- git diff --numstat src/modules | awk '($1==1 && $2==1){print $3}' | xargs git checkout --
# sed either wants -i'' (GNU sed) or -i '' (BSD sed) to
# replace in a file, with no backup extension. Define
# a `reinplace` command to deal with the difference.
if test FreeBSD = `uname` ; then
reinplace() {
sed -i '' "$@"
}
else
reinplace() {
sed -i'' "$@"
}
fi
# Go through the Python modules; those with a lang/ subdir have their # Go through the Python modules; those with a lang/ subdir have their
# own complete gettext-based setup. # own complete gettext-based setup.
for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do
@@ -125,7 +153,7 @@ for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do
if [ -d ${MODULE_DIR}/lang ]; then if [ -d ${MODULE_DIR}/lang ]; then
# Convert PO files to MO files # Convert PO files to MO files
for POFILE in $(find ${MODULE_DIR} -name "*.po") ; do for POFILE in $(find ${MODULE_DIR} -name "*.po") ; do
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE reinplace '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE
# msgfmt -o ${POFILE%.po}.mo $POFILE # msgfmt -o ${POFILE%.po}.mo $POFILE
done done
git add --verbose ${MODULE_DIR}/lang/* git add --verbose ${MODULE_DIR}/lang/*
@@ -135,7 +163,7 @@ for MODULE_DIR in $(find src/modules -maxdepth 1 -mindepth 1 -type d) ; do
done done
for POFILE in $(find lang -name "python.po") ; do for POFILE in $(find lang -name "python.po") ; do
sed -i'' '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE reinplace '/^"Content-Type/s/CHARSET/UTF-8/' $POFILE
# msgfmt -o ${POFILE%.po}.mo $POFILE # msgfmt -o ${POFILE%.po}.mo $POFILE
done done
git add --verbose lang/python* git add --verbose lang/python*

View File

@@ -688,27 +688,27 @@ Alla ändringar kommer att gå förlorade.</translation>
<message> <message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/> <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
<source>Successfully unmounted %1.</source> <source>Successfully unmounted %1.</source>
<translation type="unfinished"/> <translation>Framgångsrikt avmonterade %1.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/> <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
<source>Successfully disabled swap %1.</source> <source>Successfully disabled swap %1.</source>
<translation type="unfinished"/> <translation>Framgångsrikt inaktiverade swap %1.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/> <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
<source>Successfully cleared swap %1.</source> <source>Successfully cleared swap %1.</source>
<translation type="unfinished"/> <translation>Framgångsrikt rensade swap %1.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/> <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
<source>Successfully closed mapper device %1.</source> <source>Successfully closed mapper device %1.</source>
<translation type="unfinished"/> <translation>Framgångsrikt stängde krypterad enhet %1.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/> <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
<source>Successfully disabled volume group %1.</source> <source>Successfully disabled volume group %1.</source>
<translation type="unfinished"/> <translation>Framgångsrikt inaktiverade volymgrupp %1.</translation>
</message> </message>
<message> <message>
<location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/> <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>

View File

@@ -26,6 +26,11 @@ namespace CalamaresPython
boost::python::object boost::python::object
variantToPyObject( const QVariant& variant ) variantToPyObject( const QVariant& variant )
{ {
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch-enum"
#endif
// 49 enumeration values not handled
switch ( variant.type() ) switch ( variant.type() )
{ {
case QVariant::Map: case QVariant::Map:
@@ -62,6 +67,9 @@ variantToPyObject( const QVariant& variant )
default: default:
return bp::object(); return bp::object();
} }
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} }

View File

@@ -23,6 +23,11 @@ static const char* s_preScript = nullptr;
namespace bp = boost::python; namespace bp = boost::python;
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
#endif
BOOST_PYTHON_FUNCTION_OVERLOADS( mount_overloads, CalamaresPython::mount, 2, 4 ); BOOST_PYTHON_FUNCTION_OVERLOADS( mount_overloads, CalamaresPython::mount, 2, 4 );
BOOST_PYTHON_FUNCTION_OVERLOADS( target_env_call_str_overloads, CalamaresPython::target_env_call, 1, 3 ); BOOST_PYTHON_FUNCTION_OVERLOADS( target_env_call_str_overloads, CalamaresPython::target_env_call, 1, 3 );
BOOST_PYTHON_FUNCTION_OVERLOADS( target_env_call_list_overloads, CalamaresPython::target_env_call, 1, 3 ); BOOST_PYTHON_FUNCTION_OVERLOADS( target_env_call_list_overloads, CalamaresPython::target_env_call, 1, 3 );
@@ -42,6 +47,10 @@ BOOST_PYTHON_FUNCTION_OVERLOADS( target_env_process_output_overloads,
4 ); 4 );
BOOST_PYTHON_FUNCTION_OVERLOADS( host_env_process_output_overloads, CalamaresPython::host_env_process_output, 1, 4 ); BOOST_PYTHON_FUNCTION_OVERLOADS( host_env_process_output_overloads, CalamaresPython::host_env_process_output, 1, 4 );
#ifdef __clang__
#pragma clang diagnostic pop
#endif
BOOST_PYTHON_MODULE( libcalamares ) BOOST_PYTHON_MODULE( libcalamares )
{ {
bp::object package = bp::scope(); bp::object package = bp::scope();

View File

@@ -22,6 +22,11 @@ namespace Partition
QString QString
prettyNameForFileSystemType( FileSystem::Type t ) prettyNameForFileSystemType( FileSystem::Type t )
{ {
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch-enum"
#endif
// 13 enumeration values not handled
switch ( t ) switch ( t )
{ {
case FileSystem::Unknown: case FileSystem::Unknown:
@@ -60,11 +65,19 @@ prettyNameForFileSystemType( FileSystem::Type t )
default: default:
return FileSystem::nameForType( t ); return FileSystem::nameForType( t );
} }
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} }
QString QString
untranslatedFS( FileSystem::Type t ) untranslatedFS( FileSystem::Type t )
{ {
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch-enum"
#endif
// 34 enumeration values not handled
switch ( t ) switch ( t )
{ {
case FileSystem::Type::ReiserFS: case FileSystem::Type::ReiserFS:
@@ -72,6 +85,9 @@ untranslatedFS( FileSystem::Type t )
default: default:
return FileSystem::nameForType( t, { QStringLiteral( "C" ) } ); return FileSystem::nameForType( t, { QStringLiteral( "C" ) } );
} }
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} }
} // namespace Partition } // namespace Partition

View File

@@ -115,12 +115,14 @@ System::createTargetFile( const QString& path, const QByteArray& contents, Write
QString completePath = targetPath( path ); QString completePath = targetPath( path );
if ( completePath.isEmpty() ) if ( completePath.isEmpty() )
{ {
cWarning() << "No target path for" << path;
return CreationResult( CreationResult::Code::Invalid ); return CreationResult( CreationResult::Code::Invalid );
} }
QFile f( completePath ); QFile f( completePath );
if ( ( mode == WriteMode::KeepExisting ) && f.exists() ) if ( ( mode == WriteMode::KeepExisting ) && f.exists() )
{ {
cWarning() << "Target file" << completePath << "already exists";
return CreationResult( CreationResult::Code::AlreadyExists ); return CreationResult( CreationResult::Code::AlreadyExists );
} }
@@ -133,13 +135,16 @@ System::createTargetFile( const QString& path, const QByteArray& contents, Write
if ( !f.open( m ) ) if ( !f.open( m ) )
{ {
cWarning() << "Could not open target file" << completePath;
return CreationResult( CreationResult::Code::Failed ); return CreationResult( CreationResult::Code::Failed );
} }
if ( f.write( contents ) != contents.size() ) auto written = f.write( contents );
if ( written != contents.size() )
{ {
f.close(); f.close();
f.remove(); f.remove();
cWarning() << "Short write (" << written << "out of" << contents.size() << "bytes) to" << completePath;
return CreationResult( CreationResult::Code::Failed ); return CreationResult( CreationResult::Code::Failed );
} }
@@ -147,6 +152,30 @@ System::createTargetFile( const QString& path, const QByteArray& contents, Write
return CreationResult( QFileInfo( f ).canonicalFilePath() ); return CreationResult( QFileInfo( f ).canonicalFilePath() );
} }
QStringList
System::readTargetFile( const QString& path ) const
{
const QString completePath = targetPath( path );
if ( completePath.isEmpty() )
{
return QStringList();
}
QFile f( completePath );
if ( !f.open( QIODevice::ReadOnly ) )
{
return QStringList();
}
QTextStream in( &f );
QStringList l;
while ( !f.atEnd() )
{
l << in.readLine();
}
return l;
}
void void
System::removeTargetFile( const QString& path ) const System::removeTargetFile( const QString& path ) const
{ {

View File

@@ -287,6 +287,24 @@ public:
*/ */
DLLEXPORT void removeTargetFile( const QString& path ) const; DLLEXPORT void removeTargetFile( const QString& path ) const;
/** @brief Reads a file from the target system.
*
* @param path Path to the file; this is interpreted from the root of
* the target system (@see targetPath()).
*
* Does no error checking, and returns an empty list if the file does
* not exist.
*
* NOTE: This function is now basically the same as QFile::readAll(),
* splitting into lines, but Calamares may need to change
* permissions or raise privileges to actually read the file,
* which is why there is an API.
*
* NOTE: Since this buffers the whole file in memory, reading big files
* is not recommended.
*/
DLLEXPORT QStringList readTargetFile( const QString& path ) const;
/** @brief Ensure that the directory @p path exists /** @brief Ensure that the directory @p path exists
* *
* @param path a full pathname to a desired directory. * @param path a full pathname to a desired directory.

View File

@@ -224,5 +224,26 @@ truncateMultiLine( const QString& string, CalamaresUtils::LinesStartEnd lines, C
return front + back.right( chars.total / 2 ); return front + back.right( chars.total / 2 );
} }
void
removeLeading( QString& string, QChar c )
{
int count = 0;
while ( string.length() > count && string[ count ] == c )
{
count++;
}
string.remove( 0, count );
}
void
removeTrailing( QString& string, QChar c )
{
int lastIndex = string.length();
while ( lastIndex > 0 && string[ lastIndex - 1 ] == c )
{
lastIndex--;
}
string.remove( lastIndex, string.length() );
}
} // namespace CalamaresUtils } // namespace CalamaresUtils

View File

@@ -100,6 +100,19 @@ DLLEXPORT QString truncateMultiLine( const QString& string,
LinesStartEnd lines = LinesStartEnd { 3, 5 }, LinesStartEnd lines = LinesStartEnd { 3, 5 },
CharCount chars = CharCount { 812 } ); CharCount chars = CharCount { 812 } );
/** @brief Remove all @p c at the beginning of @p string
*
* Modifies the @p string in-place. If @p c is not the first character
* of @p string, the string is left unchanged; otherwise the first character
* is removed and the process repeats.
*/
DLLEXPORT void removeLeading( QString& string, QChar c );
/** @brief Remove all @p c at the end of @p string
*
* Like removeLeading(), but at the end of the string.
*/
DLLEXPORT void removeTrailing( QString& string, QChar c );
} // namespace CalamaresUtils } // namespace CalamaresUtils
#endif #endif

View File

@@ -70,12 +70,19 @@ private Q_SLOTS:
void testStringTruncation(); void testStringTruncation();
void testStringTruncationShorter(); void testStringTruncationShorter();
void testStringTruncationDegenerate(); void testStringTruncationDegenerate();
void testStringRemoveLeading_data();
void testStringRemoveLeading();
void testStringRemoveTrailing_data();
void testStringRemoveTrailing();
/** @section Test Runner directory-manipulation. */ /** @section Test Runner directory-manipulation. */
void testRunnerDirs(); void testRunnerDirs();
void testCalculateWorkingDirectory(); void testCalculateWorkingDirectory();
void testRunnerOutput(); void testRunnerOutput();
/** @section Test file-functions */
void testReadWriteFile();
private: private:
void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth ); void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth );
}; };
@@ -751,6 +758,64 @@ LibCalamaresTests::testStringTruncationDegenerate()
} }
} }
void
LibCalamaresTests::testStringRemoveLeading_data()
{
QTest::addColumn< QString >( "string" );
QTest::addColumn< char >( "c" );
QTest::addColumn< QString >( "result" );
QTest::newRow( "empty" ) << QString() << '/' << QString();
QTest::newRow( "one-slash" ) << QStringLiteral( "/tmp" ) << '/' << QStringLiteral( "tmp" );
QTest::newRow( "two-slash" ) << QStringLiteral( "//tmp" ) << '/' << QStringLiteral( "tmp" );
QTest::newRow( "multi-slash" ) << QStringLiteral( "/tmp/p" ) << '/' << QStringLiteral( "tmp/p" );
QTest::newRow( "later-slash" ) << QStringLiteral( "@/tmp" ) << '/' << QStringLiteral( "@/tmp" );
QTest::newRow( "all-one-slash" ) << QStringLiteral( "/" ) << '/' << QString();
QTest::newRow( "all-many-slash" ) << QStringLiteral( "////////////////////" ) << '/' << QString();
QTest::newRow( "trailing" ) << QStringLiteral( "tmp/" ) << '/' << QStringLiteral( "tmp/" );
}
void
LibCalamaresTests::testStringRemoveLeading()
{
QFETCH( QString, string );
QFETCH( char, c );
QFETCH( QString, result );
const QString initial = string;
CalamaresUtils::removeLeading( string, c );
QCOMPARE( string, result );
}
void
LibCalamaresTests::testStringRemoveTrailing_data()
{
QTest::addColumn< QString >( "string" );
QTest::addColumn< char >( "c" );
QTest::addColumn< QString >( "result" );
QTest::newRow( "empty" ) << QString() << '/' << QString();
QTest::newRow( "one-slash" ) << QStringLiteral( "/tmp" ) << '/' << QStringLiteral( "/tmp" );
QTest::newRow( "two-slash" ) << QStringLiteral( "//tmp" ) << '/' << QStringLiteral( "//tmp" );
QTest::newRow( "multi-slash" ) << QStringLiteral( "/tmp//p/" ) << '/' << QStringLiteral( "/tmp//p" );
QTest::newRow( "later-slash" ) << QStringLiteral( "@/tmp/@" ) << '/' << QStringLiteral( "@/tmp/@" );
QTest::newRow( "later-slash2" ) << QStringLiteral( "@/tmp/@//" ) << '/' << QStringLiteral( "@/tmp/@" );
QTest::newRow( "all-one-slash" ) << QStringLiteral( "/" ) << '/' << QString();
QTest::newRow( "all-many-slash" ) << QStringLiteral( "////////////////////" ) << '/' << QString();
QTest::newRow( "trailing" ) << QStringLiteral( "tmp/" ) << '/' << QStringLiteral( "tmp" );
}
void
LibCalamaresTests::testStringRemoveTrailing()
{
QFETCH( QString, string );
QFETCH( char, c );
QFETCH( QString, result );
const QString initial = string;
CalamaresUtils::removeTrailing( string, c );
QCOMPARE( string, result );
}
static QString static QString
dirname( const QTemporaryDir& d ) dirname( const QTemporaryDir& d )
@@ -950,6 +1015,84 @@ LibCalamaresTests::testRunnerOutput()
} }
CalamaresUtils::System*
file_setup( const QTemporaryDir& tempRoot )
{
CalamaresUtils::System* ss = CalamaresUtils::System::instance();
if ( !ss )
{
ss = new CalamaresUtils::System( true );
}
Calamares::GlobalStorage* gs
= Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr;
if ( !gs )
{
cDebug() << "Creating new JobQueue";
(void)new Calamares::JobQueue();
gs = Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr;
}
if ( gs )
{
// Working with a rootMountPoint set
gs->insert( "rootMountPoint", tempRoot.path() );
}
return ss;
}
void
LibCalamaresTests::testReadWriteFile()
{
static const QByteArray otherContents( "derp\n" );
QTemporaryDir tempRoot( QDir::tempPath() + QStringLiteral( "/test-job-XXXXXX" ) );
auto* ss = file_setup( tempRoot );
QVERIFY( ss );
{
auto fullPath = ss->createTargetFile( "test0", QByteArray(), CalamaresUtils::System::WriteMode::Overwrite );
QVERIFY( fullPath );
QVERIFY( !fullPath.path().isEmpty() );
QFileInfo fi( fullPath.path() );
QVERIFY( fi.exists() );
QVERIFY( fi.isFile() );
QCOMPARE( fi.size(), 0 );
}
// It won't overwrite unless you ask for it
{
auto fullPath = ss->createTargetFile( "test0", otherContents );
QVERIFY( !fullPath ); // Failed, because it won't overwrite
QCOMPARE( fullPath.code(), decltype( fullPath )::Code::AlreadyExists );
QVERIFY( fullPath.path().isEmpty() ); // Because it wasn't written
QFileInfo fi( tempRoot.filePath( "test0" ) ); // Compute the name some other way
QVERIFY( fi.exists() );
QVERIFY( fi.isFile() );
QCOMPARE( fi.size(), 0 );
}
// But it will if you say so explicitly
{
auto fullPath = ss->createTargetFile( "test0", otherContents, CalamaresUtils::System::WriteMode::Overwrite );
QVERIFY( fullPath );
QVERIFY( !fullPath.path().isEmpty() );
QFileInfo fi( fullPath.path() );
QVERIFY( fi.exists() );
QVERIFY( fi.isFile() );
QCOMPARE( fi.size(), 5 );
}
// Now it's been written, we can read it, too
{
auto contents = ss->readTargetFile( "test0" );
QVERIFY( !contents.isEmpty() );
QCOMPARE( contents.count(), 1 );
QCOMPARE( contents[ 0 ], QStringLiteral( "derp" ) ); // No trailing \n
}
}
QTEST_GUILESS_MAIN( LibCalamaresTests ) QTEST_GUILESS_MAIN( LibCalamaresTests )
#include "utils/moc-warnings.h" #include "utils/moc-warnings.h"

View File

@@ -162,14 +162,15 @@ uploadServerFromMap( const QVariantMap& map )
if ( typestring.isEmpty() || urlstring.isEmpty() ) if ( typestring.isEmpty() || urlstring.isEmpty() )
{ {
return Branding::UploadServerInfo( Branding::UploadServerType::None, QUrl(), 0 ); return Branding::UploadServerInfo { Branding::UploadServerType::None, QUrl(), 0 };
} }
bool bogus = false; // we don't care about type-name lookup success here bool bogus = false; // we don't care about type-name lookup success here
return Branding::UploadServerInfo( return Branding::UploadServerInfo {
names.find( typestring, bogus ), names.find( typestring, bogus ),
QUrl( urlstring, QUrl::ParsingMode::StrictMode ), QUrl( urlstring, QUrl::ParsingMode::StrictMode ),
sizeLimitKiB >= 0 ? CalamaresUtils::KiBtoBytes( static_cast< unsigned long long >( sizeLimitKiB ) ) : -1 ); sizeLimitKiB >= 0 ? CalamaresUtils::KiBtoBytes( static_cast< unsigned long long >( sizeLimitKiB ) ) : -1
};
} }
/** @brief Load the @p map with strings from @p config /** @brief Load the @p map with strings from @p config

View File

@@ -227,7 +227,14 @@ public:
* is irrelevant and usually empty), the URL for the upload and the size limit of upload * is irrelevant and usually empty), the URL for the upload and the size limit of upload
* in bytes (for configuration value < 0, it serves -1, which stands for having no limit). * in bytes (for configuration value < 0, it serves -1, which stands for having no limit).
*/ */
using UploadServerInfo = std::tuple< UploadServerType, QUrl, qint64 >; struct UploadServerInfo
{
UploadServerType type;
QUrl url;
qint64 size;
operator bool() const { return type != Calamares::Branding::UploadServerType::None && size != 0; }
};
UploadServerInfo uploadServer() const { return m_uploadServer; } UploadServerInfo uploadServer() const { return m_uploadServer; }
/** /**

View File

@@ -27,6 +27,7 @@ set( calamaresui_SOURCES
viewpages/ViewStep.cpp viewpages/ViewStep.cpp
widgets/ClickableLabel.cpp widgets/ClickableLabel.cpp
widgets/ErrorDialog.cpp
widgets/FixedAspectRatioLabel.cpp widgets/FixedAspectRatioLabel.cpp
widgets/PrettyRadioButton.cpp widgets/PrettyRadioButton.cpp
widgets/TranslationFix.cpp widgets/TranslationFix.cpp
@@ -39,8 +40,6 @@ set( calamaresui_SOURCES
# Don't warn about third-party sources # Don't warn about third-party sources
mark_thirdparty_code( mark_thirdparty_code(
${CMAKE_SOURCE_DIR}/3rdparty/qjsonitem.cpp
${CMAKE_SOURCE_DIR}/3rdparty/qjsonmodel.cpp
${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp ${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp
) )
@@ -75,6 +74,8 @@ calamares_add_library( calamaresui
Qt5::Svg Qt5::Svg
RESOURCES libcalamaresui.qrc RESOURCES libcalamaresui.qrc
EXPORT Calamares EXPORT Calamares
UI
utils/ErrorDialog/ErrorDialog.ui
VERSION ${CALAMARES_VERSION_SHORT} VERSION ${CALAMARES_VERSION_SHORT}
) )
target_link_libraries( calamaresui PRIVATE yamlcpp::yamlcpp ) target_link_libraries( calamaresui PRIVATE yamlcpp::yamlcpp )

View File

@@ -24,11 +24,13 @@
#include "viewpages/BlankViewStep.h" #include "viewpages/BlankViewStep.h"
#include "viewpages/ExecutionViewStep.h" #include "viewpages/ExecutionViewStep.h"
#include "viewpages/ViewStep.h" #include "viewpages/ViewStep.h"
#include "widgets/ErrorDialog.h"
#include "widgets/TranslationFix.h" #include "widgets/TranslationFix.h"
#include <QApplication> #include <QApplication>
#include <QBoxLayout> #include <QBoxLayout>
#include <QClipboard> #include <QClipboard>
#include <QDialogButtonBox>
#include <QFile> #include <QFile>
#include <QMessageBox> #include <QMessageBox>
#include <QMetaObject> #include <QMetaObject>
@@ -150,56 +152,32 @@ ViewManager::insertViewStep( int before, ViewStep* step )
void void
ViewManager::onInstallationFailed( const QString& message, const QString& details ) ViewManager::onInstallationFailed( const QString& message, const QString& details )
{ {
bool shouldOfferWebPaste = std::get< 0 >( Calamares::Branding::instance()->uploadServer() )
!= Calamares::Branding::UploadServerType::None
and std::get< 2 >( Calamares::Branding::instance()->uploadServer() ) != 0;
cError() << "Installation failed:" << message; cError() << "Installation failed:" << message;
cDebug() << Logger::SubEntry << "- message:" << message; cDebug() << Logger::SubEntry << "- message:" << message;
cDebug() << Logger::SubEntry << "- details:" << Logger::NoQuote << details; cDebug() << Logger::SubEntry << "- details:" << Logger::NoQuote << details;
QString heading QString heading
= Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Failed" ) : tr( "Installation Failed" ); = Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Failed" ) : tr( "Installation Failed" );
QString pasteMsg = tr( "Would you like to paste the install log to the web?" );
QString text = "<p>" + message + "</p>";
if ( !details.isEmpty() )
{
text += "<p>"
+ CalamaresUtils::truncateMultiLine( details, CalamaresUtils::LinesStartEnd { 6, 2 } )
.replace( '\n', QStringLiteral( "<br/>" ) )
+ "</p>";
}
if ( shouldOfferWebPaste )
{
text += "<p>" + pasteMsg + "</p>";
}
QMessageBox* msgBox = new QMessageBox(); ErrorDialog* errorDialog = new ErrorDialog();
msgBox->setIcon( QMessageBox::Critical ); errorDialog->setWindowTitle( tr( "Error" ) );
msgBox->setWindowTitle( tr( "Error" ) ); errorDialog->setHeading( "<strong>" + heading + "</strong>" );
msgBox->setText( "<strong>" + heading + "</strong>" ); errorDialog->setInformativeText( message );
msgBox->setInformativeText( text ); errorDialog->setShouldOfferWebPaste( Calamares::Branding::instance()->uploadServer() );
if ( shouldOfferWebPaste ) errorDialog->setDetails( details );
{ errorDialog->show();
msgBox->setStandardButtons( QMessageBox::Yes | QMessageBox::No );
msgBox->setDefaultButton( QMessageBox::No );
}
else
{
msgBox->setStandardButtons( QMessageBox::Close );
msgBox->setDefaultButton( QMessageBox::Close );
}
Calamares::fixButtonLabels( msgBox );
msgBox->show();
cDebug() << "Calamares will quit when the dialog closes."; cDebug() << "Calamares will quit when the dialog closes.";
connect( msgBox, &QMessageBox::buttonClicked, [msgBox]( QAbstractButton* button ) { connect( errorDialog,
if ( msgBox->buttonRole( button ) == QMessageBox::ButtonRole::YesRole ) &QDialog::finished,
{ [ errorDialog ]( int result )
CalamaresUtils::Paste::doLogUploadUI( msgBox ); {
} if ( result == QDialog::Accepted && errorDialog->shouldOfferWebPaste() )
QApplication::quit(); {
} ); CalamaresUtils::Paste::doLogUploadUI( errorDialog );
}
QApplication::quit();
} );
} }

View File

@@ -69,7 +69,8 @@ STATICTEST QString
ficheLogUpload( const QByteArray& pasteData, const QUrl& serverUrl, QObject* parent ) ficheLogUpload( const QByteArray& pasteData, const QUrl& serverUrl, QObject* parent )
{ {
QTcpSocket* socket = new QTcpSocket( parent ); QTcpSocket* socket = new QTcpSocket( parent );
socket->connectToHost( serverUrl.host(), serverUrl.port() ); // 16 bits of port-number
socket->connectToHost( serverUrl.host(), quint16( serverUrl.port() ) );
if ( !socket->waitForConnected() ) if ( !socket->waitForConnected() )
{ {

View File

@@ -0,0 +1,106 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Artem Grinev <agrinev@manjaro.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "ErrorDialog.h"
#include "ui_ErrorDialog.h"
#include "widgets/TranslationFix.h"
#include <QDialogButtonBox>
#include <QIcon>
namespace Calamares
{
ErrorDialog::ErrorDialog( QWidget* parent )
: QDialog( parent )
, ui( new Ui::ErrorDialog )
{
ui->setupUi( this );
ui->iconLabel->setPixmap( QIcon::fromTheme( "dialog-error" ).pixmap( 64 ) );
ui->detailsWidget->hide();
ui->offerWebPasteLabel->hide();
}
ErrorDialog::~ErrorDialog()
{
delete ui;
}
QString
ErrorDialog::heading() const
{
return ui->headingLabel->text();
}
QString
ErrorDialog::informativeText() const
{
return ui->informativeTextLabel->text();
}
QString
ErrorDialog::details() const
{
return ui->detailsBrowser->toPlainText();
}
void
ErrorDialog::setHeading( const QString& newHeading )
{
if ( ui->headingLabel->text() != newHeading )
{
ui->headingLabel->setText( newHeading );
emit headingChanged();
}
}
void
ErrorDialog::setInformativeText( const QString& newInformativeText )
{
if ( ui->informativeTextLabel->text() != newInformativeText )
{
ui->informativeTextLabel->setText( newInformativeText );
emit informativeTextChanged();
}
}
void
ErrorDialog::setDetails( const QString& newDetails )
{
if ( ui->detailsBrowser->toPlainText() != newDetails )
{
ui->detailsBrowser->setPlainText( newDetails );
ui->detailsWidget->setVisible( !ui->detailsBrowser->toPlainText().trimmed().isEmpty() );
emit detailsChanged();
}
}
bool
ErrorDialog::shouldOfferWebPaste() const
{
return m_shouldOfferWebPaste;
}
void
ErrorDialog::setShouldOfferWebPaste( bool newShouldOfferWebPaste )
{
if ( m_shouldOfferWebPaste != newShouldOfferWebPaste )
{
m_shouldOfferWebPaste = newShouldOfferWebPaste;
ui->offerWebPasteLabel->setVisible( m_shouldOfferWebPaste );
ui->buttonBox->setStandardButtons( m_shouldOfferWebPaste ? ( QDialogButtonBox::Yes | QDialogButtonBox::No )
: QDialogButtonBox::Close );
fixButtonLabels( ui->buttonBox );
emit shouldOfferWebPasteChanged();
}
}
} // namespace Calamares

View File

@@ -0,0 +1,83 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Artem Grinev <agrinev@manjaro.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef LIBCALAMARESUI_ERRORDIALOG_H
#define LIBCALAMARESUI_ERRORDIALOG_H
#include <QDialog>
namespace Ui
{
class ErrorDialog;
}
namespace Calamares
{
class ErrorDialog : public QDialog
{
Q_OBJECT
Q_PROPERTY( QString heading READ heading WRITE setHeading NOTIFY headingChanged )
Q_PROPERTY( QString informativeText READ informativeText WRITE setInformativeText NOTIFY informativeTextChanged )
Q_PROPERTY( QString details READ details WRITE setDetails NOTIFY detailsChanged )
Q_PROPERTY( bool shouldOfferWebPaste READ shouldOfferWebPaste WRITE setShouldOfferWebPaste NOTIFY
shouldOfferWebPasteChanged )
public:
explicit ErrorDialog( QWidget* parent = nullptr );
~ErrorDialog() override;
/** @brief The heading (title) of the error dialog
*
* This is a short (one-line) title. It is human-readable, so should
* be translated at the time it is set.
*/
QString heading() const;
void setHeading( const QString& newHeading );
/** @brief The description of the problem
*
* Longer, human-readable, description of the problem. This text
* is word-wrapped as necessary.
*/
QString informativeText() const;
void setInformativeText( const QString& newInformativeText );
/** @brief Details of the problem
*
* This is generally command-output; it might not be translated
* when set. It should be considered "background to the informative
* text", or maybe "the reasons". Write the informative text for
* the end-user.
*/
QString details() const;
void setDetails( const QString& newDetails );
/** @brief Enable web-paste button
*
* The web-paste button can be configured at a global level,
* but each individual error dialog can be set separately.
*/
bool shouldOfferWebPaste() const;
void setShouldOfferWebPaste( bool newShouldOfferWebPaste );
signals:
void headingChanged();
void informativeTextChanged();
void detailsChanged();
void shouldOfferWebPasteChanged();
private:
Ui::ErrorDialog* ui;
bool m_shouldOfferWebPaste = false;
};
}; // namespace Calamares
#endif // LIBCALAMARESUI_ERRORDIALOG_H

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ErrorDialog</class>
<widget class="QDialog" name="ErrorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>425</width>
<height>262</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="0" colspan="2">
<widget class="QWidget" name="detailsWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="informativeTextLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Details:</string>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="detailsBrowser"/>
</item>
</layout>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="offerWebPasteLabel">
<property name="toolTip">
<string/>
</property>
<property name="text">
<string>Would you like to paste the install log to the web?</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="iconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="headingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ErrorDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ErrorDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -11,30 +11,34 @@
#include <QAbstractButton> #include <QAbstractButton>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDialogButtonBox>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton>
namespace Calamares namespace Calamares
{ {
//Using QMessageBox's StandardButton enum here but according to headers they should be kept in-sync between multiple classes.
static std::pair< decltype( QMessageBox::Ok ), const char* > maps[] = {
{ QMessageBox::Ok, QT_TRANSLATE_NOOP( "StandardButtons", "&OK" ) },
{ QMessageBox::Yes, QT_TRANSLATE_NOOP( "StandardButtons", "&Yes" ) },
{ QMessageBox::No, QT_TRANSLATE_NOOP( "StandardButtons", "&No" ) },
{ QMessageBox::Cancel, QT_TRANSLATE_NOOP( "StandardButtons", "&Cancel" ) },
{ QMessageBox::Close, QT_TRANSLATE_NOOP( "StandardButtons", "&Close" ) },
};
template < typename TButtonBox >
void void
fixButtonLabels( QMessageBox* box ) fixButtonLabels( TButtonBox* box )
{ {
if ( !box ) if ( !box )
{ {
return; return;
} }
static std::pair< decltype( QMessageBox::Ok ), const char* > maps[] = {
{ QMessageBox::Ok, QT_TRANSLATE_NOOP( "StandardButtons", "&OK" ) },
{ QMessageBox::Yes, QT_TRANSLATE_NOOP( "StandardButtons", "&Yes" ) },
{ QMessageBox::No, QT_TRANSLATE_NOOP( "StandardButtons", "&No" ) },
{ QMessageBox::Cancel, QT_TRANSLATE_NOOP( "StandardButtons", "&Cancel" ) },
{ QMessageBox::Close, QT_TRANSLATE_NOOP( "StandardButtons", "&Close" ) },
};
for ( auto [ sb, label ] : maps ) for ( auto [ sb, label ] : maps )
{ {
auto* button = box->button( sb ); auto* button = box->button( static_cast< typename TButtonBox::StandardButton >( int( sb ) ) );
if ( button ) if ( button )
{ {
button->setText( QCoreApplication::translate( "StandardButtons", label ) ); button->setText( QCoreApplication::translate( "StandardButtons", label ) );
@@ -42,4 +46,16 @@ fixButtonLabels( QMessageBox* box )
} }
} }
void
fixButtonLabels( QMessageBox* box )
{
fixButtonLabels< QMessageBox >( box );
}
void
fixButtonLabels( QDialogButtonBox* box )
{
fixButtonLabels< QDialogButtonBox >( box );
}
} // namespace Calamares } // namespace Calamares

View File

@@ -13,6 +13,7 @@
#include "DllMacro.h" #include "DllMacro.h"
class QMessageBox; class QMessageBox;
class QDialogButtonBox;
namespace Calamares namespace Calamares
{ {
@@ -26,6 +27,8 @@ namespace Calamares
* guess the context. * guess the context.
*/ */
void UIDLLEXPORT fixButtonLabels( QMessageBox* ); void UIDLLEXPORT fixButtonLabels( QMessageBox* );
void UIDLLEXPORT fixButtonLabels( QDialogButtonBox* );
} // namespace Calamares } // namespace Calamares
#endif #endif

View File

@@ -224,7 +224,7 @@ def create_systemd_boot_conf(install_path, efi_dir, uuid, entry, entry_name, ker
# Copy kernel and initramfs to a subdirectory of /efi partition # Copy kernel and initramfs to a subdirectory of /efi partition
files_dir = os.path.join(install_path + efi_dir, entry_name) files_dir = os.path.join(install_path + efi_dir, entry_name)
os.mkdir(files_dir) os.makedirs(files_dir, exist_ok=True)
kernel_path = install_path + kernel kernel_path = install_path + kernel
kernel_name = os.path.basename(kernel_path) kernel_name = os.path.basename(kernel_path)

View File

@@ -18,10 +18,16 @@
# #
# btrfs_swap options are used when a swapfile is chosen with a btrfs root # btrfs_swap options are used when a swapfile is chosen with a btrfs root
# the options are applied to the subvolume which holds the swap partition # the options are applied to the subvolume which holds the swap partition
#
# The settings shown here apply only the btrfs defaults; these
# are generally the right ones. Commented-out lines show other
# options wich **might** be applicable for specific situations.
mountOptions: mountOptions:
default: defaults,noatime default: defaults,noatime
btrfs: defaults,noatime,autodefrag,compress=zstd # btrfs: defaults,noatime,autodefrag,compress=zstd
btrfs_swap: defaults,noatime btrfs: defaults
# btrfs_swap: defaults,noatime
btrfs_swap: defaults
# Mount options to use for the EFI System Partition. If not defined, the # Mount options to use for the EFI System Partition. If not defined, the
# *mountOptions* for *vfat* are used, or if that is not set either, # *mountOptions* for *vfat* are used, or if that is not set either,

View File

@@ -18,6 +18,7 @@
#include <KParts/ReadOnlyPart> #include <KParts/ReadOnlyPart>
#include <KParts/kde_terminal_interface.h> #include <KParts/kde_terminal_interface.h>
#include <KService> #include <KService>
#include <kcoreaddons_version.h>
#include <QApplication> #include <QApplication>
#include <QDir> #include <QDir>
@@ -41,8 +42,10 @@ InteractiveTerminalPage::InteractiveTerminalPage( QWidget* parent )
void void
InteractiveTerminalPage::errorKonsoleNotInstalled() InteractiveTerminalPage::errorKonsoleNotInstalled()
{ {
QMessageBox mb(QMessageBox::Critical, QMessageBox mb( QMessageBox::Critical,
tr( "Konsole not installed" ), tr( "Please install KDE Konsole and try again!" ), QMessageBox::Ok ); tr( "Konsole not installed" ),
tr( "Please install KDE Konsole and try again!" ),
QMessageBox::Ok );
Calamares::fixButtonLabels( &mb ); Calamares::fixButtonLabels( &mb );
mb.exec(); mb.exec();
} }
@@ -54,20 +57,30 @@ InteractiveTerminalPage::onActivate()
{ {
return; return;
} }
// For whatever reason, instead of simply linking against a library we
// need to do a runtime query to KService just to get a sodding terminal #if KCOREADDONS_VERSION_MAJOR != 5
// widget. #error Incompatible with not-KF5
#endif
#if KCOREADDONS_VERSION_MINOR >= 86
// 5.86 deprecated a bunch of KService and PluginFactory and related methods
auto md = KPluginMetaData::findPluginById( QString(), "konsolepart" );
if ( !md.isValid() )
{
errorKonsoleNotInstalled();
return;
}
auto* p = KPluginFactory::instantiatePlugin< KParts::ReadOnlyPart >( md, this ).plugin;
#else
KService::Ptr service = KService::serviceByDesktopName( "konsolepart" ); KService::Ptr service = KService::serviceByDesktopName( "konsolepart" );
if ( !service ) if ( !service )
{ {
// And all of this hoping the Konsole application is installed. If not,
// tough cookies.
errorKonsoleNotInstalled(); errorKonsoleNotInstalled();
return; return;
} }
// Create one instance of konsolepart. // Create one instance of konsolepart.
KParts::ReadOnlyPart* p = service->createInstance< KParts::ReadOnlyPart >( this, this, {} ); KParts::ReadOnlyPart* p = service->createInstance< KParts::ReadOnlyPart >( this, this, {} );
#endif
if ( !p ) if ( !p )
{ {
// One more opportunity for the loading operation to fail. // One more opportunity for the loading operation to fail.
@@ -91,7 +104,6 @@ InteractiveTerminalPage::onActivate()
m_termHostWidget = p->widget(); m_termHostWidget = p->widget();
m_layout->addWidget( m_termHostWidget ); m_layout->addWidget( m_termHostWidget );
cDebug() << "Part widget ought to be" << m_termHostWidget->metaObject()->className();
t->showShellInDir( QDir::home().path() ); t->showShellInDir( QDir::home().path() );
t->sendInput( QString( "%1\n" ).arg( m_command ) ); t->sendInput( QString( "%1\n" ).arg( m_command ) );

View File

@@ -64,6 +64,11 @@ def write_openswap_conf(partitions, root_mount_point, openswap_conf_path):
elif lines[i].startswith("keyfile_filename"): elif lines[i].startswith("keyfile_filename"):
lines[i] = "keyfile_filename=crypto_keyfile.bin" lines[i] = "keyfile_filename=crypto_keyfile.bin"
elif lines[i].startswith("#keyfile_device_mount_options"):
if libcalamares.globalstorage.contains("btrfsRootSubvolume"):
btrfs_root_subvolume = libcalamares.globalstorage.value("btrfsRootSubvolume")
lines[i] = "keyfile_device_mount_options=--options=subvol=" + btrfs_root_subvolume.lstrip("/")
with open(os.path.join(root_mount_point, with open(os.path.join(root_mount_point,
openswap_conf_path), 'w') as openswap_file: openswap_conf_path), 'w') as openswap_file:
openswap_file.write("\n".join(lines) + "\n") openswap_file.write("\n".join(lines) + "\n")
@@ -84,11 +89,11 @@ def run():
if not partitions: if not partitions:
libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) libcalamares.utils.warning("partitions is empty, {!s}".format(partitions))
return (_("Configuration Error"), return (_("Configuration Error"),
_("No partitions are defined for <pre>{!s}</pre> to use." ).format("luksopenswaphookcfg")) _("No partitions are defined for <pre>{!s}</pre> to use.").format("luksopenswaphookcfg"))
if not root_mount_point: if not root_mount_point:
libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point))
return (_("Configuration Error"), return (_("Configuration Error"),
_("No root mount point is given for <pre>{!s}</pre> to use." ).format("luksopenswaphookcfg")) _("No root mount point is given for <pre>{!s}</pre> to use.").format("luksopenswaphookcfg"))
openswap_conf_path = openswap_conf_path.lstrip('/') openswap_conf_path = openswap_conf_path.lstrip('/')

View File

@@ -193,6 +193,7 @@ def mount_partition(root_mount_point, partition, partitions):
for s in btrfs_subvolumes: for s in btrfs_subvolumes:
if not s["subvolume"]: if not s["subvolume"]:
continue continue
os.makedirs(root_mount_point + os.path.dirname(s["subvolume"]), exist_ok=True)
subprocess.check_call(["btrfs", "subvolume", "create", subprocess.check_call(["btrfs", "subvolume", "create",
root_mount_point + s["subvolume"]]) root_mount_point + s["subvolume"]])
if s["mountPoint"] == "/": if s["mountPoint"] == "/":

View File

@@ -73,12 +73,12 @@ def replace_username(nm_config_filename, live_user, target_user):
if not os.path.exists(nm_config_filename): if not os.path.exists(nm_config_filename):
return return
with open(nm_config_filename, "r") as network_conf: with open(nm_config_filename, "r", encoding="UTF-8") as network_conf:
text = network_conf.readlines() text = network_conf.readlines()
live_permissions = 'permissions=user:{}:;'.format(live_user) live_permissions = 'permissions=user:{}:;'.format(live_user)
target_permissions = 'permissions=user:{}:;\n'.format(target_user) target_permissions = 'permissions=user:{}:;\n'.format(target_user)
with open(nm_config_filename, "w") as network_conf: with open(nm_config_filename, "w", encoding="UTF-8") as network_conf:
for line in text: for line in text:
if live_permissions in line: if live_permissions in line:
line = target_permissions line = target_permissions

View File

@@ -146,15 +146,12 @@ modeDescription( Config::InstallChoice choice )
case Config::InstallChoice::Alongside: case Config::InstallChoice::Alongside:
return QCoreApplication::translate( context, "Install %1 <strong>alongside</strong> another operating system." ) return QCoreApplication::translate( context, "Install %1 <strong>alongside</strong> another operating system." )
.arg( branding->shortVersionedName() ); .arg( branding->shortVersionedName() );
break;
case Config::InstallChoice::Erase: case Config::InstallChoice::Erase:
return QCoreApplication::translate( context, "<strong>Erase</strong> disk and install %1." ) return QCoreApplication::translate( context, "<strong>Erase</strong> disk and install %1." )
.arg( branding->shortVersionedName() ); .arg( branding->shortVersionedName() );
break;
case Config::InstallChoice::Replace: case Config::InstallChoice::Replace:
return QCoreApplication::translate( context, "<strong>Replace</strong> a partition with %1." ) return QCoreApplication::translate( context, "<strong>Replace</strong> a partition with %1." )
.arg( branding->shortVersionedName() ); .arg( branding->shortVersionedName() );
break;
case Config::InstallChoice::NoChoice: case Config::InstallChoice::NoChoice:
case Config::InstallChoice::Manual: case Config::InstallChoice::Manual:
return QCoreApplication::translate( context, "<strong>Manual</strong> partitioning." ); return QCoreApplication::translate( context, "<strong>Manual</strong> partitioning." );
@@ -187,21 +184,18 @@ diskDescription( int listLength, const PartitionCoreModule::SummaryInfo& info, C
.arg( branding->shortVersionedName() ) .arg( branding->shortVersionedName() )
.arg( info.deviceNode ) .arg( info.deviceNode )
.arg( info.deviceName ); .arg( info.deviceName );
break;
case Config::Erase: case Config::Erase:
return QCoreApplication::translate( context, return QCoreApplication::translate( context,
"<strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1." ) "<strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1." )
.arg( branding->shortVersionedName() ) .arg( branding->shortVersionedName() )
.arg( info.deviceNode ) .arg( info.deviceNode )
.arg( info.deviceName ); .arg( info.deviceName );
break;
case Config::Replace: case Config::Replace:
return QCoreApplication::translate( return QCoreApplication::translate(
context, "<strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1." ) context, "<strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1." )
.arg( branding->shortVersionedName() ) .arg( branding->shortVersionedName() )
.arg( info.deviceNode ) .arg( info.deviceNode )
.arg( info.deviceName ); .arg( info.deviceName );
break;
case Config::NoChoice: case Config::NoChoice:
case Config::Manual: case Config::Manual:
return QCoreApplication::translate( return QCoreApplication::translate(
@@ -558,7 +552,7 @@ PartitionViewStep::onLeave()
if ( !okSize ) if ( !okSize )
{ {
cDebug() << o << "ESP too small"; cDebug() << o << "ESP too small";
const auto atLeastBytes = PartUtils::efiFilesystemMinimumSize(); const qint64 atLeastBytes = static_cast< qint64 >( PartUtils::efiFilesystemMinimumSize() );
const auto atLeastMiB = CalamaresUtils::BytesToMiB( atLeastBytes ); const auto atLeastMiB = CalamaresUtils::BytesToMiB( atLeastBytes );
description.append( ' ' ); description.append( ' ' );
description.append( tr( "The filesystem must be at least %1 MiB in size." ).arg( atLeastMiB ) ); description.append( tr( "The filesystem must be at least %1 MiB in size." ).arg( atLeastMiB ) );

View File

@@ -11,6 +11,7 @@
#include "DeviceList.h" #include "DeviceList.h"
#include "partition/PartitionIterator.h" #include "partition/PartitionIterator.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include <kpmcore/backend/corebackend.h> #include <kpmcore/backend/corebackend.h>
@@ -43,11 +44,9 @@ hasRootPartition( Device* device )
static bool static bool
blkIdCheckIso9660( const QString& path ) blkIdCheckIso9660( const QString& path )
{ {
QProcess blkid; // If blkid fails, there's no output, but we don't care
blkid.start( "blkid", { path } ); auto r = CalamaresUtils::System::runCommand( { "blkid", path }, std::chrono::seconds( 30 ) );
blkid.waitForFinished(); return r.getOutput().contains( "iso9660" );
QString output = QString::fromLocal8Bit( blkid.readAllStandardOutput() );
return output.contains( "iso9660" );
} }
static bool static bool

View File

@@ -8,9 +8,10 @@
* Calamares is Free Software: see the License-Identifier above. * Calamares is Free Software: see the License-Identifier above.
* *
*/ */
#include "core/DeviceModel.h" #include "DeviceModel.h"
#include "core/PartitionModel.h" #include "core/PartitionModel.h"
#include "core/SizeUtils.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@@ -18,9 +19,6 @@
// KPMcore // KPMcore
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
// KF5
#include <KFormat>
#include <QIcon> #include <QIcon>
#include <QStandardItemModel> #include <QStandardItemModel>
@@ -83,7 +81,7 @@ DeviceModel::data( const QModelIndex& index, int role ) const
//: device[name] - size[number] (device-node[name]) //: device[name] - size[number] (device-node[name])
return tr( "%1 - %2 (%3)" ) return tr( "%1 - %2 (%3)" )
.arg( device->name() ) .arg( device->name() )
.arg( KFormat().formatByteSize( device->capacity() ) ) .arg( formatByteSize( device->capacity() ) )
.arg( device->deviceNode() ); .arg( device->deviceNode() );
} }
else else

View File

@@ -15,8 +15,8 @@
#include "partition/PartitionIterator.h" #include "partition/PartitionIterator.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/String.h"
// KPMcore
#include <kpmcore/backend/corebackendmanager.h> #include <kpmcore/backend/corebackendmanager.h>
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
@@ -127,4 +127,23 @@ clonePartition( Device* device, Partition* partition )
partition->activeFlags() ); partition->activeFlags() );
} }
Calamares::JobResult
execute( Operation& operation, const QString& failureMessage )
{
operation.setStatus( Operation::StatusRunning );
Report report( nullptr );
if ( operation.execute( report ) )
{
return Calamares::JobResult::ok();
}
// Remove the === lines from the report by trimming them to empty
QStringList l = report.toText().split( '\n' );
std::for_each( l.begin(), l.end(), []( QString& s ) { CalamaresUtils::removeLeading( s, '=' ); } );
return Calamares::JobResult::error( failureMessage, l.join( '\n' ) );
}
} // namespace KPMHelpers } // namespace KPMHelpers

View File

@@ -11,12 +11,15 @@
#ifndef KPMHELPERS_H #ifndef KPMHELPERS_H
#define KPMHELPERS_H #define KPMHELPERS_H
// KPMcore #include "Job.h"
#include <kpmcore/core/partitiontable.h> #include <kpmcore/core/partitiontable.h>
#include <kpmcore/fs/filesystem.h> #include <kpmcore/fs/filesystem.h>
#include <kpmcore/ops/operation.h>
#include <kpmcore/util/report.h>
// Qt
#include <QList> #include <QList>
#include <QVector>
#include <functional> #include <functional>
@@ -35,6 +38,8 @@ class PartitionRole;
#define KPM_PARTITION_FLAG_ESP PartitionTable::FlagEsp #define KPM_PARTITION_FLAG_ESP PartitionTable::FlagEsp
#endif #endif
using PartitionVector = QVector< const Partition* >;
/** /**
* Helper functions to manipulate partitions * Helper functions to manipulate partitions
*/ */
@@ -72,6 +77,24 @@ Partition* createNewEncryptedPartition( PartitionNode* parent,
Partition* clonePartition( Device* device, Partition* partition ); Partition* clonePartition( Device* device, Partition* partition );
/** @brief Return a result for an @p operation
*
* Executes the operation, and if successful, returns a success result.
* Otherwise returns an error using @p failureMessage as the primary part
* of the error, and details obtained from the operation.
*/
Calamares::JobResult execute( Operation& operation, const QString& failureMessage );
/** @brief Return a result for an @p operation
*
* It's acceptable to use an rvalue: the operation-running is the effect
* you're interested in, rather than keeping the temporary around.
*/
static inline Calamares::JobResult
execute( Operation&& operation, const QString& failureMessage )
{
return execute( operation, failureMessage );
}
} // namespace KPMHelpers } // namespace KPMHelpers
#endif /* KPMHELPERS_H */ #endif /* KPMHELPERS_H */

View File

@@ -451,6 +451,8 @@ isEfiFilesystemSuitableType( const Partition* candidate )
{ {
auto type = candidate->fileSystem().type(); auto type = candidate->fileSystem().type();
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG( "-Wswitch-enum" )
switch ( type ) switch ( type )
{ {
case FileSystem::Type::Fat32: case FileSystem::Type::Fat32:
@@ -465,6 +467,7 @@ isEfiFilesystemSuitableType( const Partition* candidate )
cWarning() << "EFI boot partition must be FAT32"; cWarning() << "EFI boot partition must be FAT32";
return false; return false;
} }
QT_WARNING_POP
} }
bool bool
@@ -526,14 +529,15 @@ efiFilesystemMinimumSize()
{ {
using CalamaresUtils::Units::operator""_MiB; using CalamaresUtils::Units::operator""_MiB;
auto uefisys_part_sizeB = 300_MiB; size_t uefisys_part_sizeB = 300_MiB;
// The default can be overridden; the key used here comes // The default can be overridden; the key used here comes
// from the partition module Config.cpp // from the partition module Config.cpp
auto* gs = Calamares::JobQueue::instance()->globalStorage(); auto* gs = Calamares::JobQueue::instance()->globalStorage();
if ( gs->contains( "efiSystemPartitionSize_i" ) ) if ( gs->contains( "efiSystemPartitionSize_i" ) )
{ {
uefisys_part_sizeB = gs->value( "efiSystemPartitionSize_i" ).toLongLong(); qint64 v = gs->value( "efiSystemPartitionSize_i" ).toLongLong();
uefisys_part_sizeB = v > 0 ? static_cast< size_t >( v ) : 0;
} }
// There is a lower limit of what can be configured // There is a lower limit of what can be configured
if ( uefisys_part_sizeB < 32_MiB ) if ( uefisys_part_sizeB < 32_MiB )

View File

@@ -71,15 +71,15 @@ swapSuggestion( const qint64 availableSpaceB, Config::SwapChoice swap )
// Allow for a fudge factor // Allow for a fudge factor
suggestedSwapSizeB *= overestimationFactor; suggestedSwapSizeB = qRound64( suggestedSwapSizeB * overestimationFactor );
// don't use more than 10% of available space // don't use more than 10% of available space
if ( !ensureSuspendToDisk ) if ( !ensureSuspendToDisk )
{ {
suggestedSwapSizeB = qMin( suggestedSwapSizeB, qint64( 0.10 * availableSpaceB ) ); suggestedSwapSizeB = qMin( suggestedSwapSizeB, availableSpaceB / 10 /* 10% is 0.1 */ );
} }
cDebug() << "Suggested swap size:" << suggestedSwapSizeB / 1024. / 1024. / 1024. << "GiB"; cDebug() << "Suggested swap size:" << CalamaresUtils::BytesToGiB( suggestedSwapSizeB ) << "GiB";
return suggestedSwapSizeB; return suggestedSwapSizeB;
} }

View File

@@ -262,11 +262,9 @@ PartitionCoreModule::doInit()
// Gives ownership of the Device* to the DeviceInfo object // Gives ownership of the Device* to the DeviceInfo object
auto deviceInfo = new DeviceInfo( device ); auto deviceInfo = new DeviceInfo( device );
m_deviceInfos << deviceInfo; m_deviceInfos << deviceInfo;
cDebug() << Logger::SubEntry cDebug() << Logger::SubEntry << device->deviceNode() << device->capacity()
<< device->deviceNode() << Logger::RedactedName( "DevName", device->name() )
<< device->capacity() << Logger::RedactedName( "DevNamePretty", device->prettyName() );
<< Logger::RedactedName( "DevName", device->name() )
<< Logger::RedactedName( "DevNamePretty", device->prettyName() );
} }
else else
{ {
@@ -360,7 +358,12 @@ PartitionModel*
PartitionCoreModule::partitionModelForDevice( const Device* device ) const PartitionCoreModule::partitionModelForDevice( const Device* device ) const
{ {
DeviceInfo* info = infoForDevice( device ); DeviceInfo* info = infoForDevice( device );
Q_ASSERT( info ); if ( !info )
{
cWarning() << "No DeviceInfo for" << Logger::Pointer( device )
<< ( device ? device->deviceNode() : QStringLiteral( "<null>" ) );
return nullptr;
}
return info->partitionModel.data(); return info->partitionModel.data();
} }
@@ -411,15 +414,16 @@ PartitionCoreModule::createPartition( Device* device, Partition* partition, Part
} }
void void
PartitionCoreModule::createVolumeGroup( QString& vgName, QVector< const Partition* > pvList, qint32 peSize ) PartitionCoreModule::createVolumeGroup( const QString& vgName, const PartitionVector& pvList, qint32 peSize )
{ {
QString actualName( vgName );
// Appending '_' character in case of repeated VG name // Appending '_' character in case of repeated VG name
while ( hasVGwithThisName( vgName ) ) while ( hasVGwithThisName( actualName ) )
{ {
vgName.append( '_' ); actualName.append( '_' );
} }
LvmDevice* device = new LvmDevice( vgName ); LvmDevice* device = new LvmDevice( actualName );
for ( const Partition* p : pvList ) for ( const Partition* p : pvList )
{ {
device->physicalVolumes() << p; device->physicalVolumes() << p;
@@ -430,12 +434,12 @@ PartitionCoreModule::createVolumeGroup( QString& vgName, QVector< const Partitio
m_deviceModel->addDevice( device ); m_deviceModel->addDevice( device );
m_deviceInfos << deviceInfo; m_deviceInfos << deviceInfo;
deviceInfo->makeJob< CreateVolumeGroupJob >( vgName, pvList, peSize ); deviceInfo->makeJob< CreateVolumeGroupJob >( actualName, pvList, peSize );
refreshAfterModelChange(); refreshAfterModelChange();
} }
void void
PartitionCoreModule::resizeVolumeGroup( LvmDevice* device, QVector< const Partition* >& pvList ) PartitionCoreModule::resizeVolumeGroup( LvmDevice* device, const PartitionVector& pvList )
{ {
auto* deviceInfo = infoForDevice( device ); auto* deviceInfo = infoForDevice( device );
Q_ASSERT( deviceInfo ); Q_ASSERT( deviceInfo );
@@ -676,7 +680,7 @@ PartitionCoreModule::efiSystemPartitions() const
return m_efiSystemPartitions; return m_efiSystemPartitions;
} }
QVector< const Partition* > PartitionVector
PartitionCoreModule::lvmPVs() const PartitionCoreModule::lvmPVs() const
{ {
return m_lvmPVs; return m_lvmPVs;

View File

@@ -136,25 +136,18 @@ public:
*/ */
void void
createPartition( Device* device, Partition* partition, PartitionTable::Flags flags = KPM_PARTITION_FLAG( None ) ); createPartition( Device* device, Partition* partition, PartitionTable::Flags flags = KPM_PARTITION_FLAG( None ) );
void deletePartition( Device* device, Partition* partition );
void formatPartition( Device* device, Partition* partition );
void resizePartition( Device* device, Partition* partition, qint64 first, qint64 last );
void setPartitionFlags( Device* device, Partition* partition, PartitionTable::Flags flags );
void createVolumeGroup( QString& vgName, QVector< const Partition* > pvList, qint32 peSize ); void createVolumeGroup( const QString& vgName, const PartitionVector& pvList, qint32 peSize );
void resizeVolumeGroup( LvmDevice* device, const PartitionVector& pvList );
void resizeVolumeGroup( LvmDevice* device, QVector< const Partition* >& pvList );
void deactivateVolumeGroup( LvmDevice* device ); void deactivateVolumeGroup( LvmDevice* device );
void removeVolumeGroup( LvmDevice* device ); void removeVolumeGroup( LvmDevice* device );
void deletePartition( Device* device, Partition* partition );
void formatPartition( Device* device, Partition* partition );
void setFilesystemLabel( Device* device, Partition* partition, const QString& newLabel ); void setFilesystemLabel( Device* device, Partition* partition, const QString& newLabel );
void resizePartition( Device* device, Partition* partition, qint64 first, qint64 last );
void setPartitionFlags( Device* device, Partition* partition, PartitionTable::Flags flags );
/// @brief Retrieve the path where the bootloader will be installed /// @brief Retrieve the path where the bootloader will be installed
QString bootLoaderInstallPath() const { return m_bootLoaderInstallPath; } QString bootLoaderInstallPath() const { return m_bootLoaderInstallPath; }
/// @brief Set the path where the bootloader will be installed /// @brief Set the path where the bootloader will be installed
@@ -185,7 +178,7 @@ public:
QList< Partition* > efiSystemPartitions() const; QList< Partition* > efiSystemPartitions() const;
QVector< const Partition* > lvmPVs() const; PartitionVector lvmPVs() const;
bool hasVGwithThisName( const QString& name ) const; bool hasVGwithThisName( const QString& name ) const;
@@ -255,7 +248,7 @@ private:
QList< DeviceInfo* > m_deviceInfos; QList< DeviceInfo* > m_deviceInfos;
QList< Partition* > m_efiSystemPartitions; QList< Partition* > m_efiSystemPartitions;
QVector< const Partition* > m_lvmPVs; PartitionVector m_lvmPVs;
DeviceModel* m_deviceModel; DeviceModel* m_deviceModel;
BootLoaderModel* m_bootLoaderModel; BootLoaderModel* m_bootLoaderModel;

View File

@@ -141,6 +141,8 @@ void
PartitionLayout::setDefaultFsType( FileSystem::Type defaultFsType ) PartitionLayout::setDefaultFsType( FileSystem::Type defaultFsType )
{ {
using FileSystem = FileSystem::Type; using FileSystem = FileSystem::Type;
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG( "-Wswitch-enum" )
switch ( defaultFsType ) switch ( defaultFsType )
{ {
case FileSystem::Unknown: case FileSystem::Unknown:
@@ -196,6 +198,7 @@ PartitionLayout::setDefaultFsType( FileSystem::Type defaultFsType )
<< "Using ext4 instead."; << "Using ext4 instead.";
defaultFsType = FileSystem::Ext4; defaultFsType = FileSystem::Ext4;
} }
QT_WARNING_POP
m_defaultFsType = defaultFsType; m_defaultFsType = defaultFsType;
} }

View File

@@ -8,11 +8,12 @@
* *
*/ */
#include "core/PartitionModel.h" #include "PartitionModel.h"
#include "core/ColorUtils.h" #include "core/ColorUtils.h"
#include "core/KPMHelpers.h" #include "core/KPMHelpers.h"
#include "core/PartitionInfo.h" #include "core/PartitionInfo.h"
#include "core/SizeUtils.h"
#include "partition/FileSystem.h" #include "partition/FileSystem.h"
#include "partition/PartitionQuery.h" #include "partition/PartitionQuery.h"
@@ -24,9 +25,6 @@
#include <kpmcore/core/partitiontable.h> #include <kpmcore/core/partitiontable.h>
#include <kpmcore/fs/filesystem.h> #include <kpmcore/fs/filesystem.h>
// KF5
#include <KFormat>
// Qt // Qt
#include <QColor> #include <QColor>
@@ -178,7 +176,7 @@ PartitionModel::data( const QModelIndex& index, int role ) const
if ( col == SizeColumn ) if ( col == SizeColumn )
{ {
qint64 size = ( partition->lastSector() - partition->firstSector() + 1 ) * m_device->logicalSize(); qint64 size = ( partition->lastSector() - partition->firstSector() + 1 ) * m_device->logicalSize();
return KFormat().formatByteSize( size ); return formatByteSize( size );
} }
cDebug() << "Unknown column" << col; cDebug() << "Unknown column" << col;
return QVariant(); return QVariant();
@@ -210,7 +208,7 @@ PartitionModel::data( const QModelIndex& index, int role ) const
QString prettyFileSystem QString prettyFileSystem
= CalamaresUtils::Partition::prettyNameForFileSystemType( partition->fileSystem().type() ); = CalamaresUtils::Partition::prettyNameForFileSystemType( partition->fileSystem().type() );
qint64 size = ( partition->lastSector() - partition->firstSector() + 1 ) * m_device->logicalSize(); qint64 size = ( partition->lastSector() - partition->firstSector() + 1 ) * m_device->logicalSize();
QString prettySize = KFormat().formatByteSize( size ); QString prettySize = formatByteSize( size );
return QVariant( name + " " + prettyFileSystem + " " + prettySize ); return QVariant( name + " " + prettyFileSystem + " " + prettySize );
} }
case SizeRole: case SizeRole:

View File

@@ -0,0 +1,27 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef PARTITION_CORE_SIZEUTILS_H
#define PARTITION_CORE_SIZEUTILS_H
#include <kpmcore/util/capacity.h>
/** @brief Helper function for printing sizes consistently.
*
* Most of Calamares uses a qint64 for partition sizes, so use that
* parameter type. However, the human-visible formatting doesn't need
* to bother with one-byte accuracy (and anyway, a double has at least 50 bits
* at which point we're printing giga (or gibi) bytes).
*/
static inline QString formatByteSize( qint64 sizeValue )
{
return Capacity::formatByteSize( static_cast< double >( sizeValue ) );
}
#endif // PARTITION_CORE_SIZEUTILS_H

View File

@@ -66,19 +66,22 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
m_ui->encryptWidget->setText( tr( "En&crypt" ) ); m_ui->encryptWidget->setText( tr( "En&crypt" ) );
m_ui->encryptWidget->hide(); m_ui->encryptWidget->hide();
if ( m_device->type() != Device::Type::LVM_Device )
{
m_ui->lvNameLabel->hide();
m_ui->lvNameLineEdit->hide();
}
if ( m_device->type() == Device::Type::LVM_Device ) if ( m_device->type() == Device::Type::LVM_Device )
{ {
m_ui->lvNameLabel->show();
m_ui->lvNameLineEdit->show();
/* LVM logical volume name can consist of: letters numbers _ . - + /* LVM logical volume name can consist of: letters numbers _ . - +
* It cannot start with underscore _ and must not be equal to . or .. or any entry in /dev/ * It cannot start with underscore _ and must not be equal to . or .. or any entry in /dev/
* QLineEdit accepts QValidator::Intermediate, so we just disable . at the beginning */ * QLineEdit accepts QValidator::Intermediate, so we just disable . at the beginning */
QRegularExpression re( QStringLiteral( R"(^(?!_|\.)[\w\-.+]+)" ) ); QRegularExpression re( QStringLiteral( R"(^(?!_|\.)[\w\-.+]+)" ) );
QRegularExpressionValidator* validator = new QRegularExpressionValidator( re, this ); QRegularExpressionValidator* validator = new QRegularExpressionValidator( re, this );
m_ui->lvNameLineEdit->setValidator( validator ); m_ui->lvNameLineEdit->setValidator( validator );
connect( m_ui->lvNameLineEdit, &QLineEdit::textChanged, this, &CreatePartitionDialog::updateOkButton );
}
else
{
m_ui->lvNameLabel->hide();
m_ui->lvNameLineEdit->hide();
} }
if ( device->partitionTable()->type() == PartitionTable::msdos if ( device->partitionTable()->type() == PartitionTable::msdos
@@ -347,3 +350,13 @@ CreatePartitionDialog::initPartResizerWidget( Partition* partition )
m_partitionSizeController->setPartResizerWidget( m_ui->partResizerWidget ); m_partitionSizeController->setPartResizerWidget( m_ui->partResizerWidget );
m_partitionSizeController->setSpinBox( m_ui->sizeSpinBox ); m_partitionSizeController->setSpinBox( m_ui->sizeSpinBox );
} }
void
CreatePartitionDialog::updateOkButton()
{
if ( m_device->type() == Device::Type::LVM_Device )
{
QString lvName = m_ui->lvNameLineEdit->text();
cDebug() << "LVName" << lvName << m_ui->lvNameLineEdit->hasAcceptableInput();
}
}

View File

@@ -45,10 +45,12 @@ private:
QWidget* parentWidget ); QWidget* parentWidget );
public: public:
/// @brief Tag-type for creating partition from free space
struct FreeSpace struct FreeSpace
{ {
Partition* p; Partition* p;
}; };
/// @brief Tag-type for editing (re-creating) a new partition
struct FreshPartition struct FreshPartition
{ {
Partition* p; Partition* p;
@@ -81,6 +83,7 @@ public:
private Q_SLOTS: private Q_SLOTS:
void updateMountPointUi(); void updateMountPointUi();
void checkMountPointSelection(); void checkMountPointSelection();
void updateOkButton(); // Check if dialog can be accepted
private: private:
QScopedPointer< Ui_CreatePartitionDialog > m_ui; QScopedPointer< Ui_CreatePartitionDialog > m_ui;

View File

@@ -16,32 +16,13 @@
#include <QLineEdit> #include <QLineEdit>
#include <QSpinBox> #include <QSpinBox>
CreateVolumeGroupDialog::CreateVolumeGroupDialog( QString& vgName, CreateVolumeGroupDialog::CreateVolumeGroupDialog( const PartitionVector& pvList,
QVector< const Partition* >& selectedPVs, qint32 physicalExtentSize,
QVector< const Partition* > pvList,
qint64& pSize,
QWidget* parent ) QWidget* parent )
: VolumeGroupBaseDialog( vgName, pvList, parent ) : VolumeGroupBaseDialog( parent, QString(), pvList )
, m_selectedPVs( selectedPVs )
, m_peSize( pSize )
{ {
setWindowTitle( tr( "Create Volume Group" ) ); setWindowTitle( tr( "Create Volume Group" ) );
peSize()->setValue( pSize ); peSizeWidget()->setValue( physicalExtentSize );
vgTypeWidget()->setEnabled( false );
vgType()->setEnabled( false );
}
void
CreateVolumeGroupDialog::accept()
{
QString& name = vgNameValue();
name = vgName()->text();
m_selectedPVs << checkedItems();
qint64& pe = m_peSize;
pe = peSize()->value();
QDialog::accept();
} }

View File

@@ -16,18 +16,7 @@ class CreateVolumeGroupDialog : public VolumeGroupBaseDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
CreateVolumeGroupDialog( QString& vgName, CreateVolumeGroupDialog( const PartitionVector& pvList, qint32 physicalExtentSize, QWidget* parent );
QVector< const Partition* >& selectedPVs,
QVector< const Partition* > pvList,
qint64& pSize,
QWidget* parent );
void accept() override;
private:
QVector< const Partition* >& m_selectedPVs;
qint64& m_peSize;
}; };
#endif // CREATEVOLUMEGROUPDIALOG_H #endif // CREATEVOLUMEGROUPDIALOG_H

View File

@@ -68,7 +68,8 @@ DeviceInfoWidget::setPartitionTableType( PartitionTable::TableType type )
void void
DeviceInfoWidget::retranslateUi() DeviceInfoWidget::retranslateUi()
{ {
QString typeString = PartitionTable::tableTypeToName( m_tableType ).toUpper(); QString typeString;
QString toolTipString;
// fix up if the name shouldn't be uppercase: // fix up if the name shouldn't be uppercase:
switch ( m_tableType ) switch ( m_tableType )
@@ -76,38 +77,34 @@ DeviceInfoWidget::retranslateUi()
case PartitionTable::msdos: case PartitionTable::msdos:
case PartitionTable::msdos_sectorbased: case PartitionTable::msdos_sectorbased:
typeString = "MBR"; typeString = "MBR";
toolTipString += tr( "<br><br>This partition table type is only advisable on older "
"systems which start from a <strong>BIOS</strong> boot "
"environment. GPT is recommended in most other cases.<br><br>"
"<strong>Warning:</strong> the MBR partition table "
"is an obsolete MS-DOS era standard.<br>"
"Only 4 <em>primary</em> partitions may be created, and of "
"those 4, one can be an <em>extended</em> partition, which "
"may in turn contain many <em>logical</em> partitions." );
break;
case PartitionTable::gpt:
// TypeString is ok
toolTipString += tr( "<br><br>This is the recommended partition table type for modern "
"systems which start from an <strong>EFI</strong> boot "
"environment." );
break; break;
case PartitionTable::loop: case PartitionTable::loop:
typeString = "loop"; typeString = "loop";
break;
case PartitionTable::mac:
typeString = "Mac";
break;
case PartitionTable::amiga:
typeString = "Amiga";
break;
case PartitionTable::sun:
typeString = "Sun";
break;
case PartitionTable::unknownTableType:
typeString = " ? ";
}
QString toolTipString = tr( "This device has a <strong>%1</strong> partition "
"table." )
.arg( typeString );
switch ( m_tableType )
{
case PartitionTable::loop:
toolTipString = tr( "This is a <strong>loop</strong> " toolTipString = tr( "This is a <strong>loop</strong> "
"device.<br><br>" "device.<br><br>"
"It is a pseudo-device with no partition table " "It is a pseudo-device with no partition table "
"that makes a file accessible as a block device. " "that makes a file accessible as a block device. "
"This kind of setup usually only contains a single filesystem." ); "This kind of setup usually only contains a single filesystem." );
break; break;
#if defined( WITH_KPMCORE42API )
case PartitionTable::none:
#endif
case PartitionTable::unknownTableType: case PartitionTable::unknownTableType:
typeString = " ? ";
toolTipString = tr( "This installer <strong>cannot detect a partition table</strong> on the " toolTipString = tr( "This installer <strong>cannot detect a partition table</strong> on the "
"selected storage device.<br><br>" "selected storage device.<br><br>"
"The device either has no partition " "The device either has no partition "
@@ -117,21 +114,35 @@ DeviceInfoWidget::retranslateUi()
"either automatically, or through the manual partitioning " "either automatically, or through the manual partitioning "
"page." ); "page." );
break; break;
case PartitionTable::gpt: // The next ones need to have the name adjusted, but the default tooltip is OK
toolTipString += tr( "<br><br>This is the recommended partition table type for modern " case PartitionTable::mac:
"systems which start from an <strong>EFI</strong> boot " typeString = "Mac";
"environment." );
break; break;
case PartitionTable::msdos: case PartitionTable::amiga:
case PartitionTable::msdos_sectorbased: typeString = "Amiga";
toolTipString += tr( "<br><br>This partition table type is only advisable on older " break;
"systems which start from a <strong>BIOS</strong> boot " case PartitionTable::sun:
"environment. GPT is recommended in most other cases.<br><br>" typeString = "Sun";
"<strong>Warning:</strong> the MBR partition table " break;
"is an obsolete MS-DOS era standard.<br>" // Peculiar tables, do nothing and use default type and tooltip strings
"Only 4 <em>primary</em> partitions may be created, and of " case PartitionTable::aix:
"those 4, one can be an <em>extended</em> partition, which " case PartitionTable::bsd:
"may in turn contain many <em>logical</em> partitions." ); case PartitionTable::dasd:
case PartitionTable::dvh:
case PartitionTable::pc98:
case PartitionTable::vmd:
break;
}
if ( typeString.isEmpty() )
{
typeString = PartitionTable::tableTypeToName( m_tableType ).toUpper();
}
if ( toolTipString.isEmpty() )
{
toolTipString = tr( "This device has a <strong>%1</strong> partition "
"table." )
.arg( typeString );
} }
m_ptLabel->setText( typeString ); m_ptLabel->setText( typeString );

View File

@@ -9,11 +9,10 @@
#include "ListPhysicalVolumeWidgetItem.h" #include "ListPhysicalVolumeWidgetItem.h"
#include <kpmcore/util/capacity.h> #include "core/SizeUtils.h"
ListPhysicalVolumeWidgetItem::ListPhysicalVolumeWidgetItem( const Partition* partition, bool checked ) ListPhysicalVolumeWidgetItem::ListPhysicalVolumeWidgetItem( const Partition* partition, bool checked )
: QListWidgetItem( : QListWidgetItem( QString( "%1 | %2" ).arg( partition->deviceNode(), formatByteSize( partition->capacity() ) ) )
QString( "%1 | %2" ).arg( partition->deviceNode(), Capacity::formatByteSize( partition->capacity() ) ) )
, m_partition( partition ) , m_partition( partition )
{ {
setToolTip( partition->deviceNode() ); setToolTip( partition->deviceNode() );
@@ -26,3 +25,5 @@ ListPhysicalVolumeWidgetItem::partition() const
{ {
return m_partition; return m_partition;
} }
ListPhysicalVolumeWidgetItem::~ListPhysicalVolumeWidgetItem() {}

View File

@@ -18,6 +18,7 @@ class ListPhysicalVolumeWidgetItem : public QListWidgetItem
{ {
public: public:
ListPhysicalVolumeWidgetItem( const Partition* partition, bool checked ); ListPhysicalVolumeWidgetItem( const Partition* partition, bool checked );
~ListPhysicalVolumeWidgetItem() override;
const Partition* partition() const; const Partition* partition() const;

View File

@@ -12,6 +12,7 @@
#include "core/ColorUtils.h" #include "core/ColorUtils.h"
#include "core/PartitionModel.h" #include "core/PartitionModel.h"
#include "core/SizeUtils.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@@ -20,8 +21,6 @@
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/fs/filesystem.h> #include <kpmcore/fs/filesystem.h>
#include <KFormat>
// Qt // Qt
#include <QGuiApplication> #include <QGuiApplication>
#include <QMouseEvent> #include <QMouseEvent>
@@ -39,7 +38,7 @@ static QStringList
buildUnknownDisklabelTexts( Device* dev ) buildUnknownDisklabelTexts( Device* dev )
{ {
QStringList texts = { QObject::tr( "Unpartitioned space or unknown partition table" ), QStringList texts = { QObject::tr( "Unpartitioned space or unknown partition table" ),
KFormat().formatByteSize( dev->totalLogical() * dev->logicalSize() ) }; formatByteSize( dev->totalLogical() * dev->logicalSize() ) };
return texts; return texts;
} }

View File

@@ -278,26 +278,30 @@ PartitionPage::checkCanCreate( Device* device )
} }
} }
void static inline PartitionVector
PartitionPage::onNewVolumeGroupClicked() availablePVs( PartitionCoreModule* core )
{ {
QString vgName; PartitionVector availablePVs;
QVector< const Partition* > selectedPVs;
qint64 peSize = 4;
QVector< const Partition* > availablePVs; for ( const Partition* p : core->lvmPVs() )
{
for ( const Partition* p : m_core->lvmPVs() ) if ( !core->isInVG( p ) )
if ( !m_core->isInVG( p ) )
{ {
availablePVs << p; availablePVs << p;
} }
}
return availablePVs;
}
QPointer< CreateVolumeGroupDialog > dlg
= new CreateVolumeGroupDialog( vgName, selectedPVs, availablePVs, peSize, this ); void
PartitionPage::onNewVolumeGroupClicked()
{
QPointer< CreateVolumeGroupDialog > dlg = new CreateVolumeGroupDialog( availablePVs( m_core ), 4, this );
if ( dlg->exec() == QDialog::Accepted ) if ( dlg->exec() == QDialog::Accepted )
{ {
const PartitionVector selectedPVs = dlg->selectedPVs();
QModelIndex partitionIndex = m_ui->partitionTreeView->currentIndex(); QModelIndex partitionIndex = m_ui->partitionTreeView->currentIndex();
if ( partitionIndex.isValid() ) if ( partitionIndex.isValid() )
@@ -321,7 +325,7 @@ PartitionPage::onNewVolumeGroupClicked()
QVariant previousIndexDeviceData = m_core->deviceModel()->data( deviceIndex, Qt::ToolTipRole ); QVariant previousIndexDeviceData = m_core->deviceModel()->data( deviceIndex, Qt::ToolTipRole );
// Creating new VG // Creating new VG
m_core->createVolumeGroup( vgName, selectedPVs, peSize ); m_core->createVolumeGroup( dlg->volumeGroupName(), selectedPVs, dlg->physicalExtentSize() );
// As createVolumeGroup method call resets deviceModel, // As createVolumeGroup method call resets deviceModel,
// is needed to set the current index in deviceComboBox as the previous one // is needed to set the current index in deviceComboBox as the previous one
@@ -342,20 +346,11 @@ PartitionPage::onResizeVolumeGroupClicked()
Q_ASSERT( device && device->type() == Device::Type::LVM_Device ); Q_ASSERT( device && device->type() == Device::Type::LVM_Device );
QVector< const Partition* > availablePVs; QPointer< ResizeVolumeGroupDialog > dlg = new ResizeVolumeGroupDialog( device, availablePVs( m_core ), this );
QVector< const Partition* > selectedPVs;
for ( const Partition* p : m_core->lvmPVs() )
if ( !m_core->isInVG( p ) )
{
availablePVs << p;
}
QPointer< ResizeVolumeGroupDialog > dlg = new ResizeVolumeGroupDialog( device, availablePVs, selectedPVs, this );
if ( dlg->exec() == QDialog::Accepted ) if ( dlg->exec() == QDialog::Accepted )
{ {
m_core->resizeVolumeGroup( device, selectedPVs ); m_core->resizeVolumeGroup( device, dlg->selectedPVs() );
} }
delete dlg; delete dlg;

View File

@@ -212,7 +212,8 @@ ReplaceWidget::onPartitionSelected()
} }
} }
if ( partition->capacity() < requiredSpaceB ) // The loss of precision is ok; we're not going to fall over from a single byte
if ( static_cast< double >( partition->capacity() ) < requiredSpaceB )
{ {
updateStatus( CalamaresUtils::Fail, updateStatus( CalamaresUtils::Fail,
tr( "<strong>%4</strong><br/><br/>" tr( "<strong>%4</strong><br/><br/>"

View File

@@ -22,38 +22,28 @@
ResizeVolumeGroupDialog::ResizeVolumeGroupDialog( LvmDevice* device, ResizeVolumeGroupDialog::ResizeVolumeGroupDialog( LvmDevice* device,
const PartitionVector& availablePVs, const PartitionVector& availablePVs,
PartitionVector& selectedPVs,
QWidget* parent ) QWidget* parent )
: VolumeGroupBaseDialog( device->name(), device->physicalVolumes(), parent ) : VolumeGroupBaseDialog( parent, device->name(), device->physicalVolumes() )
, m_selectedPVs( selectedPVs )
{ {
setWindowTitle( tr( "Resize Volume Group" ) ); setWindowTitle( tr( "Resize Volume Group" ) );
for ( int i = 0; i < pvList()->count(); i++ ) for ( int i = 0; i < pvListWidget()->count(); i++ )
{ {
pvList()->item( i )->setCheckState( Qt::Checked ); pvListWidget()->item( i )->setCheckState( Qt::Checked );
} }
for ( const Partition* p : availablePVs ) for ( const Partition* p : availablePVs )
{ {
pvList()->addItem( new ListPhysicalVolumeWidgetItem( p, false ) ); pvListWidget()->addItem( new ListPhysicalVolumeWidgetItem( p, false ) );
} }
peSize()->setValue( peSizeWidget()->setValue(
static_cast< int >( device->peSize() / Capacity::unitFactor( Capacity::Unit::Byte, Capacity::Unit::MiB ) ) ); static_cast< int >( device->peSize() / Capacity::unitFactor( Capacity::Unit::Byte, Capacity::Unit::MiB ) ) );
vgName()->setEnabled( false ); vgNameWidget()->setEnabled( false );
peSize()->setEnabled( false ); peSizeWidget()->setEnabled( false );
vgType()->setEnabled( false ); vgTypeWidget()->setEnabled( false );
setUsedSizeValue( device->allocatedPE() * device->peSize() ); setUsedSizeValue( device->allocatedPE() * device->peSize() );
setLVQuantity( device->partitionTable()->children().count() ); setLVQuantity( device->partitionTable()->children().count() );
} }
void
ResizeVolumeGroupDialog::accept()
{
m_selectedPVs << checkedItems();
QDialog::accept();
}

View File

@@ -19,17 +19,7 @@ class ResizeVolumeGroupDialog : public VolumeGroupBaseDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
using PartitionVector = QVector< const Partition* >; ResizeVolumeGroupDialog( LvmDevice* device, const PartitionVector& availablePVs, QWidget* parent );
ResizeVolumeGroupDialog( LvmDevice* device,
const PartitionVector& availablePVs,
PartitionVector& selectedPVs,
QWidget* parent );
void accept() override;
private:
PartitionVector& m_selectedPVs;
}; };
#endif // RESIZEVOLUMEGROUPDIALOG_H #endif // RESIZEVOLUMEGROUPDIALOG_H

View File

@@ -10,10 +10,10 @@
#include "VolumeGroupBaseDialog.h" #include "VolumeGroupBaseDialog.h"
#include "ui_VolumeGroupBaseDialog.h" #include "ui_VolumeGroupBaseDialog.h"
#include "core/PartitionCoreModule.h"
#include "core/SizeUtils.h"
#include "gui/ListPhysicalVolumeWidgetItem.h" #include "gui/ListPhysicalVolumeWidgetItem.h"
#include <kpmcore/util/capacity.h>
#include <QComboBox> #include <QComboBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel> #include <QLabel>
@@ -21,10 +21,11 @@
#include <QPushButton> #include <QPushButton>
#include <QSpinBox> #include <QSpinBox>
VolumeGroupBaseDialog::VolumeGroupBaseDialog( QString& vgName, QVector< const Partition* > pvList, QWidget* parent ) // Keeping the sources consistent
VolumeGroupBaseDialog::VolumeGroupBaseDialog( QWidget* parent, const QString& vgName, PartitionVector pvList )
: QDialog( parent ) : QDialog( parent )
, ui( new Ui::VolumeGroupBaseDialog ) , ui( new Ui::VolumeGroupBaseDialog )
, m_vgNameValue( vgName ) , m_volumeGroupName( vgName )
, m_totalSizeValue( 0 ) , m_totalSizeValue( 0 )
, m_usedSizeValue( 0 ) , m_usedSizeValue( 0 )
{ {
@@ -35,13 +36,12 @@ VolumeGroupBaseDialog::VolumeGroupBaseDialog( QString& vgName, QVector< const Pa
ui->pvList->addItem( new ListPhysicalVolumeWidgetItem( p, false ) ); ui->pvList->addItem( new ListPhysicalVolumeWidgetItem( p, false ) );
} }
ui->vgType->addItems( QStringList() << "LVM" ui->vgType->addItems( { "LVM", "RAID" } );
<< "RAID" );
ui->vgType->setCurrentIndex( 0 ); ui->vgType->setCurrentIndex( 0 );
QRegularExpression re( R"(^(?!_|\.)[\w\-.+]+)" ); QRegularExpression re( R"(^(?!_|\.)[\w\-.+]+)" );
ui->vgName->setValidator( new QRegularExpressionValidator( re, this ) ); ui->vgName->setValidator( new QRegularExpressionValidator( re, this ) );
ui->vgName->setText( m_vgNameValue ); ui->vgName->setText( vgName );
updateOkButton(); updateOkButton();
updateTotalSize(); updateTotalSize();
@@ -56,7 +56,10 @@ VolumeGroupBaseDialog::VolumeGroupBaseDialog( QString& vgName, QVector< const Pa
updateOkButton(); updateOkButton();
} ); } );
connect( ui->vgName, &QLineEdit::textChanged, this, [&]( const QString& ) { updateOkButton(); } ); connect( ui->vgName, &QLineEdit::textChanged, this, [&]( const QString& s ) {
m_volumeGroupName = s;
updateOkButton();
} );
} }
VolumeGroupBaseDialog::~VolumeGroupBaseDialog() VolumeGroupBaseDialog::~VolumeGroupBaseDialog()
@@ -64,10 +67,10 @@ VolumeGroupBaseDialog::~VolumeGroupBaseDialog()
delete ui; delete ui;
} }
QVector< const Partition* > PartitionVector
VolumeGroupBaseDialog::checkedItems() const VolumeGroupBaseDialog::selectedPVs() const
{ {
QVector< const Partition* > items; PartitionVector items;
for ( int i = 0; i < ui->pvList->count(); i++ ) for ( int i = 0; i < ui->pvList->count(); i++ )
{ {
@@ -91,8 +94,8 @@ VolumeGroupBaseDialog::isSizeValid() const
void void
VolumeGroupBaseDialog::updateOkButton() VolumeGroupBaseDialog::updateOkButton()
{ {
okButton()->setEnabled( isSizeValid() && !checkedItems().empty() && !ui->vgName->text().isEmpty() okButtonWidget()->setEnabled( isSizeValid() && !selectedPVs().empty() && !ui->vgName->text().isEmpty()
&& ui->peSize->value() > 0 ); && ui->peSize->value() > 0 );
} }
void void
@@ -100,7 +103,7 @@ VolumeGroupBaseDialog::setUsedSizeValue( qint64 usedSize )
{ {
m_usedSizeValue = usedSize; m_usedSizeValue = usedSize;
ui->usedSize->setText( Capacity::formatByteSize( m_usedSizeValue ) ); ui->usedSize->setText( formatByteSize( m_usedSizeValue ) );
} }
void void
@@ -112,16 +115,17 @@ VolumeGroupBaseDialog::setLVQuantity( qint32 lvQuantity )
void void
VolumeGroupBaseDialog::updateTotalSize() VolumeGroupBaseDialog::updateTotalSize()
{ {
m_physicalExtentSize = peSizeWidget()->value();
m_totalSizeValue = 0; m_totalSizeValue = 0;
for ( const Partition* p : checkedItems() ) for ( const Partition* p : selectedPVs() )
{ {
m_totalSizeValue += p->capacity() m_totalSizeValue += p->capacity()
- p->capacity() - p->capacity()
% ( ui->peSize->value() * Capacity::unitFactor( Capacity::Unit::Byte, Capacity::Unit::MiB ) ); % ( m_physicalExtentSize * Capacity::unitFactor( Capacity::Unit::Byte, Capacity::Unit::MiB ) );
} }
ui->totalSize->setText( Capacity::formatByteSize( m_totalSizeValue ) ); ui->totalSize->setText( formatByteSize( m_totalSizeValue ) );
updateTotalSectors(); updateTotalSectors();
} }
@@ -129,9 +133,10 @@ VolumeGroupBaseDialog::updateTotalSize()
void void
VolumeGroupBaseDialog::updateTotalSectors() VolumeGroupBaseDialog::updateTotalSectors()
{ {
qint64 totalSectors = 0; m_physicalExtentSize = peSizeWidget()->value();
qint64 extentSize = ui->peSize->value() * Capacity::unitFactor( Capacity::Unit::Byte, Capacity::Unit::MiB ); qint64 totalSectors = 0;
qint64 extentSize = m_physicalExtentSize * Capacity::unitFactor( Capacity::Unit::Byte, Capacity::Unit::MiB );
if ( extentSize > 0 ) if ( extentSize > 0 )
{ {
@@ -141,38 +146,32 @@ VolumeGroupBaseDialog::updateTotalSectors()
ui->totalSectors->setText( QString::number( totalSectors ) ); ui->totalSectors->setText( QString::number( totalSectors ) );
} }
QString&
VolumeGroupBaseDialog::vgNameValue() const
{
return m_vgNameValue;
}
QLineEdit* QLineEdit*
VolumeGroupBaseDialog::vgName() const VolumeGroupBaseDialog::vgNameWidget() const
{ {
return ui->vgName; return ui->vgName;
} }
QComboBox* QComboBox*
VolumeGroupBaseDialog::vgType() const VolumeGroupBaseDialog::vgTypeWidget() const
{ {
return ui->vgType; return ui->vgType;
} }
QSpinBox* QSpinBox*
VolumeGroupBaseDialog::peSize() const VolumeGroupBaseDialog::peSizeWidget() const
{ {
return ui->peSize; return ui->peSize;
} }
QListWidget* QListWidget*
VolumeGroupBaseDialog::pvList() const VolumeGroupBaseDialog::pvListWidget() const
{ {
return ui->pvList; return ui->pvList;
} }
QPushButton* QPushButton*
VolumeGroupBaseDialog::okButton() const VolumeGroupBaseDialog::okButtonWidget() const
{ {
return ui->buttonBox->button( QDialogButtonBox::StandardButton::Ok ); return ui->buttonBox->button( QDialogButtonBox::StandardButton::Ok );
} }

View File

@@ -10,7 +10,7 @@
#ifndef VOLUMEGROUPBASEDIALOG_H #ifndef VOLUMEGROUPBASEDIALOG_H
#define VOLUMEGROUPBASEDIALOG_H #define VOLUMEGROUPBASEDIALOG_H
#include <kpmcore/core/partition.h> #include "core/KPMHelpers.h"
#include <QDialog> #include <QDialog>
@@ -29,43 +29,46 @@ class VolumeGroupBaseDialog : public QDialog
Q_OBJECT Q_OBJECT
public: public:
explicit VolumeGroupBaseDialog( QString& vgName, QVector< const Partition* > pvList, QWidget* parent = nullptr ); explicit VolumeGroupBaseDialog( QWidget* parent, const QString& vgName, PartitionVector pvList );
~VolumeGroupBaseDialog() override; ~VolumeGroupBaseDialog() override;
qint32 physicalExtentSize() const { return m_physicalExtentSize; }
QString volumeGroupName() const { return m_volumeGroupName; }
/** @brief Which PVs (partitions) are selected for this VG
*
* The vector contains non-owned pointers.
*/
PartitionVector selectedPVs() const;
protected: protected:
virtual void updateOkButton(); virtual void updateOkButton();
void setUsedSizeValue( qint64 usedSize ); void setUsedSizeValue( qint64 usedSize );
void setLVQuantity( qint32 lvQuantity ); void setLVQuantity( qint32 lvQuantity );
void updateTotalSize();
void updateTotalSectors();
QVector< const Partition* > checkedItems() const;
bool isSizeValid() const; bool isSizeValid() const;
QString& vgNameValue() const; void updateTotalSize();
void updateTotalSectors();
QLineEdit* vgName() const;
QComboBox* vgType() const; /** @section UI-widget accessors
*
QSpinBox* peSize() const; * These methods get UI internal widgets, so that subclasses
* can manipulate the values in those widgets.
QListWidget* pvList() const; */
QLineEdit* vgNameWidget() const;
QPushButton* okButton() const; QComboBox* vgTypeWidget() const;
QSpinBox* peSizeWidget() const;
QListWidget* pvListWidget() const;
QPushButton* okButtonWidget() const;
private: private:
Ui::VolumeGroupBaseDialog* ui; Ui::VolumeGroupBaseDialog* ui;
QString& m_vgNameValue; QString m_volumeGroupName;
qint64 m_totalSizeValue; qint64 m_totalSizeValue;
qint64 m_usedSizeValue; qint64 m_usedSizeValue;
qint32 m_physicalExtentSize;
}; };
#endif // VOLUMEGROUPBASEDIALOG_H #endif // VOLUMEGROUPBASEDIALOG_H

View File

@@ -243,14 +243,8 @@ public:
} }
private: private:
#if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) )
// TODO: 3.3 remove because newer Qt does support constness
const char* m_message = nullptr; const char* m_message = nullptr;
QString m_path; QString m_path;
#else
const char* const m_message = nullptr;
QString const m_path;
#endif
}; };
STATICTEST inline QDebug& STATICTEST inline QDebug&

View File

@@ -11,7 +11,9 @@
#include "CreatePartitionJob.h" #include "CreatePartitionJob.h"
#include "core/KPMHelpers.h"
#include "core/PartitionInfo.h" #include "core/PartitionInfo.h"
#include "partition/FileSystem.h" #include "partition/FileSystem.h"
#include "partition/PartitionQuery.h" #include "partition/PartitionQuery.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/CalamaresUtilsSystem.h"
@@ -60,24 +62,24 @@ createZfs( Partition* partition, Device* device )
// Now we need to do some things that would normally be done by kpmcore // Now we need to do some things that would normally be done by kpmcore
// First we get the device node from the output and set it as the partition path // First we get the device node from the output and set it as the partition path
QRegularExpression re( QStringLiteral( "Created a new partition (\\d+)" ) );
QRegularExpressionMatch rem = re.match( r.getOutput() );
QString deviceNode; QString deviceNode;
if ( rem.hasMatch() )
{ {
if ( partition->devicePath().back().isDigit() ) QRegularExpression re( QStringLiteral( "Created a new partition (\\d+)" ) );
QRegularExpressionMatch rem = re.match( r.getOutput() );
if ( rem.hasMatch() )
{ {
deviceNode = partition->devicePath() + QLatin1Char( 'p' ) + rem.captured( 1 ); if ( partition->devicePath().back().isDigit() )
} {
else deviceNode = partition->devicePath() + QLatin1Char( 'p' ) + rem.captured( 1 );
{ }
deviceNode = partition->devicePath() + rem.captured( 1 ); else
{
deviceNode = partition->devicePath() + rem.captured( 1 );
}
} }
partition->setPartitionPath( deviceNode );
} }
partition->setPartitionPath( deviceNode );
// If it is a gpt device, set the partition UUID // If it is a gpt device, set the partition UUID
if ( device->partitionTable()->type() == PartitionTable::gpt && partition->uuid().isEmpty() ) if ( device->partitionTable()->type() == PartitionTable::gpt && partition->uuid().isEmpty() )
{ {
@@ -273,17 +275,9 @@ CreatePartitionJob::exec()
return createZfs( m_partition, m_device ); return createZfs( m_partition, m_device );
} }
Report report( nullptr ); return KPMHelpers::execute(
NewOperation op( *m_device, m_partition ); NewOperation( *m_device, m_partition ),
op.setStatus( Operation::StatusRunning ); tr( "The installer failed to create partition on disk '%1'." ).arg( m_device->name() ) );
QString message = tr( "The installer failed to create partition on disk '%1'." ).arg( m_device->name() );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( message, report.toText() );
} }
void void

View File

@@ -12,9 +12,11 @@
#include "CreatePartitionTableJob.h" #include "CreatePartitionTableJob.h"
#include "partition/PartitionIterator.h" #include "partition/PartitionIterator.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
// KPMcore #include "core/KPMHelpers.h"
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
#include <kpmcore/core/partitiontable.h> #include <kpmcore/core/partitiontable.h>
@@ -63,8 +65,6 @@ CreatePartitionTableJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
CreatePartitionTableJob::exec() CreatePartitionTableJob::exec()
{ {
Report report( nullptr );
QString message = tr( "The installer failed to create a partition table on %1." ).arg( m_device->name() );
PartitionTable* table = m_device->partitionTable(); PartitionTable* table = m_device->partitionTable();
@@ -76,30 +76,16 @@ CreatePartitionTableJob::exec()
cDebug() << Logger::SubEntry << ( ( *it ) ? ( *it )->deviceNode() : QString( "<null device>" ) ); cDebug() << Logger::SubEntry << ( ( *it ) ? ( *it )->deviceNode() : QString( "<null device>" ) );
} }
QProcess lsblk; auto lsblkResult = CalamaresUtils::System::runCommand( { "lsblk" }, std::chrono::seconds( 30 ) );
lsblk.setProgram( "lsblk" ); cDebug() << Logger::SubEntry << "lsblk output:\n" << Logger::NoQuote << lsblkResult.getOutput();
lsblk.setProcessChannelMode( QProcess::MergedChannels );
lsblk.start();
lsblk.waitForFinished();
cDebug() << Logger::SubEntry << "lsblk output:\n" << Logger::NoQuote << lsblk.readAllStandardOutput();
QProcess mount; auto mountResult = CalamaresUtils::System::runCommand( { "mount" }, std::chrono::seconds( 30 ) );
mount.setProgram( "mount" ); // Debug output only, not mounting something cDebug() << Logger::SubEntry << "mount output:\n" << Logger::NoQuote << mountResult.getOutput();
mount.setProcessChannelMode( QProcess::MergedChannels );
mount.start();
mount.waitForFinished();
cDebug() << Logger::SubEntry << "mount output:\n" << Logger::NoQuote << mount.readAllStandardOutput();
} }
CreatePartitionTableOperation op( *m_device, table ); return KPMHelpers::execute(
op.setStatus( Operation::StatusRunning ); CreatePartitionTableOperation( *m_device, table ),
tr( "The installer failed to create a partition table on %1." ).arg( m_device->name() ) );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( message, report.toText() );
} }
void void

View File

@@ -9,19 +9,20 @@
#include "CreateVolumeGroupJob.h" #include "CreateVolumeGroupJob.h"
// KPMcore #include "core/KPMHelpers.h"
#include <kpmcore/core/lvmdevice.h> #include <kpmcore/core/lvmdevice.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
#include <kpmcore/ops/createvolumegroupoperation.h> #include <kpmcore/ops/createvolumegroupoperation.h>
#include <kpmcore/util/report.h> #include <kpmcore/util/report.h>
CreateVolumeGroupJob::CreateVolumeGroupJob( Device*, CreateVolumeGroupJob::CreateVolumeGroupJob( Device*,
QString& vgName, const QString& vgName,
QVector< const Partition* > pvList, const PartitionVector& pvList,
const qint32 peSize ) const qint32 peSize )
: m_vgName( vgName ) : m_vgName( vgName )
, m_pvList( pvList ) , m_pvList( pvList )
, m_peSize( peSize ) , m_physicalExtentSize( peSize )
{ {
} }
@@ -46,19 +47,8 @@ CreateVolumeGroupJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
CreateVolumeGroupJob::exec() CreateVolumeGroupJob::exec()
{ {
Report report( nullptr ); return KPMHelpers::execute( CreateVolumeGroupOperation( m_vgName, m_pvList, m_physicalExtentSize ),
tr( "The installer failed to create a volume group named '%1'." ).arg( m_vgName ) );
CreateVolumeGroupOperation op( m_vgName, m_pvList, m_peSize );
op.setStatus( Operation::StatusRunning );
QString message = tr( "The installer failed to create a volume group named '%1'." ).arg( m_vgName );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( message, report.toText() );
} }
void void

View File

@@ -10,19 +10,23 @@
#ifndef CREATEVOLUMEGROUPJOB_H #ifndef CREATEVOLUMEGROUPJOB_H
#define CREATEVOLUMEGROUPJOB_H #define CREATEVOLUMEGROUPJOB_H
#include "core/KPMHelpers.h"
#include "Job.h" #include "Job.h"
#include "partition/KPMManager.h" #include "partition/KPMManager.h"
#include <QVector>
class Device;
class Partition;
class CreateVolumeGroupJob : public Calamares::Job class CreateVolumeGroupJob : public Calamares::Job
{ {
Q_OBJECT Q_OBJECT
public: public:
CreateVolumeGroupJob( Device*, QString& vgName, QVector< const Partition* > pvList, const qint32 peSize ); /** @brief Make a job that will create a volume group
*
* The @p physicalExtentSize is given in MiB; typically this is 4 (MiB).
*/
CreateVolumeGroupJob( Device*,
const QString& vgName,
const PartitionVector& pvList,
const qint32 physicalExtentSize );
QString prettyName() const override; QString prettyName() const override;
QString prettyDescription() const override; QString prettyDescription() const override;
@@ -35,8 +39,8 @@ public:
private: private:
CalamaresUtils::Partition::KPMManager m_kpmcore; CalamaresUtils::Partition::KPMManager m_kpmcore;
QString m_vgName; QString m_vgName;
QVector< const Partition* > m_pvList; PartitionVector m_pvList;
qint32 m_peSize; qint32 m_physicalExtentSize;
}; };
#endif // CREATEVOLUMEGROUPJOB_H #endif // CREATEVOLUMEGROUPJOB_H

View File

@@ -9,6 +9,8 @@
#include "DeactivateVolumeGroupJob.h" #include "DeactivateVolumeGroupJob.h"
#include "core/KPMHelpers.h"
#include <kpmcore/core/lvmdevice.h> #include <kpmcore/core/lvmdevice.h>
#include <kpmcore/ops/deactivatevolumegroupoperation.h> #include <kpmcore/ops/deactivatevolumegroupoperation.h>
#include <kpmcore/util/report.h> #include <kpmcore/util/report.h>
@@ -39,18 +41,12 @@ DeactivateVolumeGroupJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
DeactivateVolumeGroupJob::exec() DeactivateVolumeGroupJob::exec()
{ {
Report report( nullptr );
DeactivateVolumeGroupOperation op( *m_device ); DeactivateVolumeGroupOperation op( *m_device );
auto r = KPMHelpers::execute(
op.setStatus( Operation::OperationStatus::StatusRunning ); op, tr( "The installer failed to deactivate a volume group named %1." ).arg( m_device->name() ) );
if ( r )
QString message = tr( "The installer failed to deactivate a volume group named %1." ).arg( m_device->name() );
if ( op.execute( report ) )
{ {
op.preview(); op.preview();
return Calamares::JobResult::ok();
} }
return r;
return Calamares::JobResult::error( message, report.toText() );
} }

View File

@@ -10,9 +10,11 @@
*/ */
#include "DeletePartitionJob.h" #include "DeletePartitionJob.h"
#include "core/KPMHelpers.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/CalamaresUtilsSystem.h"
// KPMcore
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
#include <kpmcore/core/partitiontable.h> #include <kpmcore/core/partitiontable.h>
@@ -45,7 +47,7 @@ removePartition( Partition* partition )
auto r = CalamaresUtils::System::instance()->runCommand( auto r = CalamaresUtils::System::instance()->runCommand(
{ "sfdisk", "--delete", "--force", partition->devicePath(), QString::number( partition->number() ) }, { "sfdisk", "--delete", "--force", partition->devicePath(), QString::number( partition->number() ) },
std::chrono::seconds( 5 ) ); std::chrono::seconds( 5 ) );
if ( r.getExitCode() !=0 || r.getOutput().contains("failed") ) if ( r.getExitCode() != 0 || r.getOutput().contains( "failed" ) )
{ {
return Calamares::JobResult::error( return Calamares::JobResult::error(
QCoreApplication::translate( DeletePartitionJob::staticMetaObject.className(), "Deletion Failed" ), QCoreApplication::translate( DeletePartitionJob::staticMetaObject.className(), "Deletion Failed" ),
@@ -96,17 +98,8 @@ DeletePartitionJob::exec()
return removePartition( m_partition ); return removePartition( m_partition );
} }
Report report( nullptr ); return KPMHelpers::execute( DeleteOperation( *m_device, m_partition ),
DeleteOperation op( *m_device, m_partition ); tr( "The installer failed to delete partition %1." ).arg( m_partition->devicePath() ) );
op.setStatus( Operation::StatusRunning );
QString message = tr( "The installer failed to delete partition %1." ).arg( m_partition->devicePath() );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( message, report.toText() );
} }
void void

View File

@@ -11,6 +11,8 @@
#include "FormatPartitionJob.h" #include "FormatPartitionJob.h"
#include "core/KPMHelpers.h"
#include "partition/FileSystem.h" #include "partition/FileSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@@ -65,17 +67,7 @@ FormatPartitionJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
FormatPartitionJob::exec() FormatPartitionJob::exec()
{ {
Report report( nullptr ); // Root of the report tree, no parent return KPMHelpers::execute( CreateFileSystemOperation( *m_device, *m_partition, m_partition->fileSystem().type() ),
CreateFileSystemOperation op( *m_device, *m_partition, m_partition->fileSystem().type() ); tr( "The installer failed to format partition %1 on disk '%2'." )
op.setStatus( Operation::StatusRunning ); .arg( m_partition->partitionPath(), m_device->name() ) );
QString message = tr( "The installer failed to format partition %1 on disk '%2'." )
.arg( m_partition->partitionPath(), m_device->name() );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( message, report.toText() );
} }

View File

@@ -9,6 +9,8 @@
#include "RemoveVolumeGroupJob.h" #include "RemoveVolumeGroupJob.h"
#include "core/KPMHelpers.h"
#include <kpmcore/core/lvmdevice.h> #include <kpmcore/core/lvmdevice.h>
#include <kpmcore/ops/removevolumegroupoperation.h> #include <kpmcore/ops/removevolumegroupoperation.h>
#include <kpmcore/util/report.h> #include <kpmcore/util/report.h>
@@ -39,17 +41,7 @@ RemoveVolumeGroupJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
RemoveVolumeGroupJob::exec() RemoveVolumeGroupJob::exec()
{ {
Report report( nullptr ); return KPMHelpers::execute(
RemoveVolumeGroupOperation( *m_device ),
RemoveVolumeGroupOperation op( *m_device ); tr( "The installer failed to remove a volume group named '%1'." ).arg( m_device->name() ) );
op.setStatus( Operation::OperationStatus::StatusRunning );
QString message = tr( "The installer failed to remove a volume group named '%1'." ).arg( m_device->name() );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( message, report.toText() );
} }

View File

@@ -11,9 +11,10 @@
#include "ResizePartitionJob.h" #include "ResizePartitionJob.h"
#include "core/KPMHelpers.h"
#include "utils/Units.h" #include "utils/Units.h"
// KPMcore
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/ops/resizeoperation.h> #include <kpmcore/ops/resizeoperation.h>
#include <kpmcore/util/report.h> #include <kpmcore/util/report.h>
@@ -66,23 +67,16 @@ ResizePartitionJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
ResizePartitionJob::exec() ResizePartitionJob::exec()
{ {
Report report( nullptr );
// Restore partition sectors that were modified for preview // Restore partition sectors that were modified for preview
m_partition->setFirstSector( m_oldFirstSector ); m_partition->setFirstSector( m_oldFirstSector );
m_partition->setLastSector( m_oldLastSector ); m_partition->setLastSector( m_oldLastSector );
ResizeOperation op( *m_device, *m_partition, m_newFirstSector, m_newLastSector ); ResizeOperation op( *m_device, *m_partition, m_newFirstSector, m_newLastSector );
op.setStatus( Operation::StatusRunning );
connect( &op, &Operation::progress, this, &ResizePartitionJob::iprogress ); connect( &op, &Operation::progress, this, &ResizePartitionJob::iprogress );
return KPMHelpers::execute( op,
QString errorMessage = tr( "The installer failed to resize partition %1 on disk '%2'." ) tr( "The installer failed to resize partition %1 on disk '%2'." )
.arg( m_partition->partitionPath() ) .arg( m_partition->partitionPath() )
.arg( m_device->name() ); .arg( m_device->name() ) );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( errorMessage, report.toText() );
} }
void void

View File

@@ -9,13 +9,12 @@
#include "ResizeVolumeGroupJob.h" #include "ResizeVolumeGroupJob.h"
// KPMcore
#include <kpmcore/core/lvmdevice.h> #include <kpmcore/core/lvmdevice.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
#include <kpmcore/ops/resizevolumegroupoperation.h> #include <kpmcore/ops/resizevolumegroupoperation.h>
#include <kpmcore/util/report.h> #include <kpmcore/util/report.h>
ResizeVolumeGroupJob::ResizeVolumeGroupJob( Device*, LvmDevice* device, QVector< const Partition* >& partitionList ) ResizeVolumeGroupJob::ResizeVolumeGroupJob( Device*, LvmDevice* device, const PartitionVector& partitionList )
: m_device( device ) : m_device( device )
, m_partitionList( partitionList ) , m_partitionList( partitionList )
{ {
@@ -51,19 +50,9 @@ ResizeVolumeGroupJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
ResizeVolumeGroupJob::exec() ResizeVolumeGroupJob::exec()
{ {
Report report( nullptr ); return KPMHelpers::execute(
ResizeVolumeGroupOperation( *m_device, m_partitionList ),
ResizeVolumeGroupOperation op( *m_device, m_partitionList ); tr( "The installer failed to resize a volume group named '%1'." ).arg( m_device->name() ) );
op.setStatus( Operation::OperationStatus::StatusRunning );
QString message = tr( "The installer failed to resize a volume group named '%1'." ).arg( m_device->name() );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( message, report.toText() );
} }
QString QString

View File

@@ -10,20 +10,19 @@
#ifndef RESIZEVOLUMEGROUPJOB_H #ifndef RESIZEVOLUMEGROUPJOB_H
#define RESIZEVOLUMEGROUPJOB_H #define RESIZEVOLUMEGROUPJOB_H
#include "core/KPMHelpers.h"
#include "Job.h" #include "Job.h"
#include "partition/KPMManager.h" #include "partition/KPMManager.h"
#include <QVector>
class Device; class Device;
class LvmDevice; class LvmDevice;
class Partition;
class ResizeVolumeGroupJob : public Calamares::Job class ResizeVolumeGroupJob : public Calamares::Job
{ {
Q_OBJECT Q_OBJECT
public: public:
ResizeVolumeGroupJob( Device*, LvmDevice* device, QVector< const Partition* >& partitionList ); ResizeVolumeGroupJob( Device*, LvmDevice* device, const PartitionVector& partitionList );
QString prettyName() const override; QString prettyName() const override;
QString prettyDescription() const override; QString prettyDescription() const override;
@@ -37,7 +36,7 @@ private:
private: private:
CalamaresUtils::Partition::KPMManager m_kpmcore; CalamaresUtils::Partition::KPMManager m_kpmcore;
LvmDevice* m_device; LvmDevice* m_device;
QVector< const Partition* > m_partitionList; PartitionVector m_partitionList;
}; };
#endif // RESIZEVOLUMEGROUPJOB_H #endif // RESIZEVOLUMEGROUPJOB_H

View File

@@ -13,6 +13,8 @@
#include "SetPartitionFlagsJob.h" #include "SetPartitionFlagsJob.h"
#include "core/KPMHelpers.h"
#include "partition/FileSystem.h" #include "partition/FileSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Units.h" #include "utils/Units.h"
@@ -148,17 +150,8 @@ SetPartFlagsJob::exec()
cDebug() << "Setting flags on" << m_device->deviceNode() << "partition" << partition()->deviceNode() cDebug() << "Setting flags on" << m_device->deviceNode() << "partition" << partition()->deviceNode()
<< Logger::DebugList( flagsList ); << Logger::DebugList( flagsList );
Report report( nullptr );
SetPartFlagsOperation op( *m_device, *partition(), m_flags ); SetPartFlagsOperation op( *m_device, *partition(), m_flags );
op.setStatus( Operation::StatusRunning );
connect( &op, &Operation::progress, this, &SetPartFlagsJob::iprogress ); connect( &op, &Operation::progress, this, &SetPartFlagsJob::iprogress );
return KPMHelpers::execute(
QString errorMessage op, tr( "The installer failed to set flags on partition %1." ).arg( m_partition->partitionPath() ) );
= tr( "The installer failed to set flags on partition %1." ).arg( m_partition->partitionPath() );
if ( op.execute( report ) )
{
return Calamares::JobResult::ok();
}
return Calamares::JobResult::error( errorMessage, report.toText() );
} }

View File

@@ -61,6 +61,7 @@ calamares_add_test(
SOURCES SOURCES
${PartitionModule_SOURCE_DIR}/jobs/AutoMountManagementJob.cpp ${PartitionModule_SOURCE_DIR}/jobs/AutoMountManagementJob.cpp
AutoMountTests.cpp AutoMountTests.cpp
DEFINITIONS ${_partition_defs}
) )
calamares_add_test( calamares_add_test(
@@ -70,4 +71,5 @@ calamares_add_test(
${PartitionModule_SOURCE_DIR}/core/DeviceList.cpp ${PartitionModule_SOURCE_DIR}/core/DeviceList.cpp
LIBRARIES LIBRARIES
kpmcore kpmcore
DEFINITIONS ${_partition_defs}
) )

View File

@@ -3,14 +3,20 @@
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> # SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
# #
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
calamares_add_plugin( preservefiles calamares_add_plugin( preservefiles
TYPE job TYPE job
EXPORT_MACRO PLUGINDLLEXPORT_PRO EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES SOURCES
Item.cpp
PreserveFiles.cpp PreserveFiles.cpp
# REQUIRES mount # To set the rootMountPoint # REQUIRES mount # To set the rootMountPoint
SHARED_LIB SHARED_LIB
EMERGENCY EMERGENCY
) )
calamares_add_test(
preservefilestest
SOURCES
Item.cpp
Tests.cpp
)

View File

@@ -0,0 +1,159 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2018, 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "Item.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include "utils/Units.h"
#include "utils/Variant.h"
#include <QFile>
using namespace CalamaresUtils::Units;
static bool
copy_file( const QString& source, const QString& dest )
{
QFile sourcef( source );
if ( !sourcef.open( QFile::ReadOnly ) )
{
cWarning() << "Could not read" << source;
return false;
}
QFile destf( dest );
if ( !destf.open( QFile::WriteOnly ) )
{
sourcef.close();
cWarning() << "Could not open" << destf.fileName() << "for writing; could not copy" << source;
return false;
}
QByteArray b;
do
{
b = sourcef.read( 1_MiB );
destf.write( b );
} while ( b.count() > 0 );
sourcef.close();
destf.close();
return true;
}
Item
Item::fromVariant( const QVariant& v, const CalamaresUtils::Permissions& defaultPermissions )
{
if ( v.type() == QVariant::String )
{
QString filename = v.toString();
if ( !filename.isEmpty() )
{
return { filename, filename, defaultPermissions, ItemType::Path, false };
}
else
{
cWarning() << "Empty filename for preservefiles, item" << v;
return {};
}
}
else if ( v.type() == QVariant::Map )
{
const auto map = v.toMap();
CalamaresUtils::Permissions perm( defaultPermissions );
ItemType t = ItemType::None;
bool optional = CalamaresUtils::getBool( map, "optional", false );
{
QString perm_string = map[ "perm" ].toString();
if ( !perm_string.isEmpty() )
{
perm = CalamaresUtils::Permissions( perm_string );
}
}
{
QString from = map[ "from" ].toString();
t = ( from == "log" ) ? ItemType::Log : ( from == "config" ) ? ItemType::Config : ItemType::None;
if ( t == ItemType::None && !map[ "src" ].toString().isEmpty() )
{
t = ItemType::Path;
}
}
QString dest = map[ "dest" ].toString();
if ( dest.isEmpty() )
{
cWarning() << "Empty dest for preservefiles, item" << v;
return {};
}
switch ( t )
{
case ItemType::Config:
return { QString(), dest, perm, t, optional };
case ItemType::Log:
return { QString(), dest, perm, t, optional };
case ItemType::Path:
return { map[ "src" ].toString(), dest, perm, t, optional };
case ItemType::None:
cWarning() << "Invalid type for preservefiles, item" << v;
return {};
}
}
cWarning() << "Invalid type for preservefiles, item" << v;
return {};
}
bool
Item::exec( const std::function< QString( QString ) >& replacements ) const
{
QString expanded_dest = replacements( dest );
QString full_dest = CalamaresUtils::System::instance()->targetPath( expanded_dest );
bool success = false;
switch ( m_type )
{
case ItemType::None:
cWarning() << "Invalid item for preservefiles skipped.";
return false;
case ItemType::Config:
if ( !( success = Calamares::JobQueue::instance()->globalStorage()->saveJson( full_dest ) ) )
{
cWarning() << "Could not write a JSON dump of global storage to" << full_dest;
}
break;
case ItemType::Log:
if ( !( success = copy_file( Logger::logFile(), full_dest ) ) )
{
cWarning() << "Could not preserve log file to" << full_dest;
}
break;
case ItemType::Path:
if ( !( success = copy_file( source, full_dest ) ) )
{
cWarning() << "Could not preserve" << source << "to" << full_dest;
}
break;
}
if ( !success )
{
CalamaresUtils::System::instance()->removeTargetFile( expanded_dest );
return false;
}
else
{
return perm.apply( full_dest );
}
}

View File

@@ -0,0 +1,76 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2018, 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef PRESERVEFILES_ITEM_H
#define PRESERVEFILES_ITEM_H
#include "utils/Permissions.h"
#include <QString>
#include <QVariant>
#include <memory>
enum class ItemType
{
None,
Path,
Log,
Config
};
/** @brief Represents one item to copy
*
* All item types need a destination (to place the data), this is
* intepreted within the target system. All items need a permission,
* which is applied to the data once written.
*
* The source may be a path, but not all types need a source.
*/
class Item
{
QString source;
QString dest;
CalamaresUtils::Permissions perm;
ItemType m_type = ItemType::None;
bool m_optional = false;
public:
Item( const QString& src, const QString& d, CalamaresUtils::Permissions p, ItemType t, bool optional )
: source( src )
, dest( d )
, perm( std::move( p ) )
, m_type( t )
, m_optional( optional )
{
}
Item()
: m_type( ItemType::None )
{
}
operator bool() const { return m_type != ItemType::None; }
ItemType type() const { return m_type; }
bool isOptional() const { return m_optional; }
bool exec( const std::function< QString( QString ) >& replacements ) const;
/** @brief Create an Item -- or one of its subclasses -- from @p v
*
* Depending on the structure and contents of @p v, a pointer
* to an Item is returned. If @p v cannot be interpreted meaningfully,
* then a nullptr is returned.
*
* When the entry contains a *perm* key, use that permission, otherwise
* apply @p defaultPermissions to the item.
*/
static Item fromVariant( const QVariant& v, const CalamaresUtils::Permissions& defaultPermissions );
};
#endif

View File

@@ -7,46 +7,20 @@
#include "PreserveFiles.h" #include "PreserveFiles.h"
#include "Item.h"
#include "CalamaresVersion.h" #include "CalamaresVersion.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/CalamaresUtilsSystem.h"
#include "utils/CommandList.h" #include "utils/CommandList.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Permissions.h"
#include "utils/Units.h" #include "utils/Units.h"
#include <QFile> #include <QFile>
using namespace CalamaresUtils::Units; using namespace CalamaresUtils::Units;
QString
targetPrefix()
{
if ( CalamaresUtils::System::instance()->doChroot() )
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( gs && gs->contains( "rootMountPoint" ) )
{
QString r = gs->value( "rootMountPoint" ).toString();
if ( !r.isEmpty() )
{
return r;
}
else
{
cDebug() << "RootMountPoint is empty";
}
}
else
{
cDebug() << "No rootMountPoint defined, preserving files to '/'";
}
}
return QLatin1String( "/" );
}
QString QString
atReplacements( QString s ) atReplacements( QString s )
{ {
@@ -79,95 +53,34 @@ PreserveFiles::prettyName() const
return tr( "Saving files for later ..." ); return tr( "Saving files for later ..." );
} }
static bool
copy_file( const QString& source, const QString& dest )
{
QFile sourcef( source );
if ( !sourcef.open( QFile::ReadOnly ) )
{
cWarning() << "Could not read" << source;
return false;
}
QFile destf( dest );
if ( !destf.open( QFile::WriteOnly ) )
{
sourcef.close();
cWarning() << "Could not open" << destf.fileName() << "for writing; could not copy" << source;
return false;
}
QByteArray b;
do
{
b = sourcef.read( 1_MiB );
destf.write( b );
} while ( b.count() > 0 );
sourcef.close();
destf.close();
return true;
}
Calamares::JobResult Calamares::JobResult
PreserveFiles::exec() PreserveFiles::exec()
{ {
if ( m_items.isEmpty() ) if ( m_items.empty() )
{ {
return Calamares::JobResult::error( tr( "No files configured to save for later." ) ); return Calamares::JobResult::error( tr( "No files configured to save for later." ) );
} }
QString prefix = targetPrefix();
if ( !prefix.endsWith( '/' ) )
{
prefix.append( '/' );
}
int count = 0; int count = 0;
for ( const auto& it : m_items ) for ( const auto& it : qAsConst( m_items ) )
{ {
QString source = it.source; if ( !it )
QString bare_dest = atReplacements( it.dest );
QString dest = prefix + bare_dest;
if ( it.type == ItemType::Log )
{ {
source = Logger::logFile(); // Invalid entries are nullptr, ignore them but count as a success
// because they shouldn't block the installation. There are
// warnings in the log showing what the configuration problem is.
++count;
continue;
} }
if ( it.type == ItemType::Config ) // Try to preserve the file. If it's marked as optional, count it
// as a success regardless.
if ( it.exec( atReplacements ) || it.isOptional() )
{ {
if ( !Calamares::JobQueue::instance()->globalStorage()->saveJson( dest ) ) ++count;
{
cWarning() << "Could not write a JSON dump of global storage to" << dest;
}
else
{
++count;
}
}
else if ( source.isEmpty() )
{
cWarning() << "Skipping unnamed source file for" << dest;
}
else
{
if ( copy_file( source, dest ) )
{
if ( it.perm.isValid() )
{
if ( !it.perm.apply( CalamaresUtils::System::instance()->targetPath( bare_dest ) ) )
{
cWarning() << "Could not set attributes of" << bare_dest;
}
}
++count;
}
} }
} }
return count == m_items.count() return count == m_items.size()
? Calamares::JobResult::ok() ? Calamares::JobResult::ok()
: Calamares::JobResult::error( tr( "Not all of the configured files could be preserved." ) ); : Calamares::JobResult::error( tr( "Not all of the configured files could be preserved." ) );
} }
@@ -193,53 +106,11 @@ PreserveFiles::setConfigurationMap( const QVariantMap& configurationMap )
{ {
defaultPermissions = QStringLiteral( "root:root:0400" ); defaultPermissions = QStringLiteral( "root:root:0400" );
} }
CalamaresUtils::Permissions perm( defaultPermissions );
QVariantList l = files.toList(); for ( const auto& li : files.toList() )
unsigned int c = 0;
for ( const auto& li : l )
{ {
if ( li.type() == QVariant::String ) m_items.push_back( Item::fromVariant( li, perm ) );
{
QString filename = li.toString();
if ( !filename.isEmpty() )
m_items.append(
Item { filename, filename, CalamaresUtils::Permissions( defaultPermissions ), ItemType::Path } );
else
{
cDebug() << "Empty filename for preservefiles, item" << c;
}
}
else if ( li.type() == QVariant::Map )
{
const auto map = li.toMap();
QString dest = map[ "dest" ].toString();
QString from = map[ "from" ].toString();
ItemType t = ( from == "log" ) ? ItemType::Log : ( from == "config" ) ? ItemType::Config : ItemType::None;
QString perm = map[ "perm" ].toString();
if ( perm.isEmpty() )
{
perm = defaultPermissions;
}
if ( dest.isEmpty() )
{
cDebug() << "Empty dest for preservefiles, item" << c;
}
else if ( t == ItemType::None )
{
cDebug() << "Invalid type for preservefiles, item" << c;
}
else
{
m_items.append( Item { QString(), dest, CalamaresUtils::Permissions( perm ), t } );
}
}
else
{
cDebug() << "Invalid type for preservefiles, item" << c;
}
++c;
} }
} }

View File

@@ -10,33 +10,14 @@
#include "CppJob.h" #include "CppJob.h"
#include "DllMacro.h" #include "DllMacro.h"
#include "utils/Permissions.h"
#include "utils/PluginFactory.h" #include "utils/PluginFactory.h"
#include <QList> class Item;
#include <QObject>
#include <QVariantMap>
class PLUGINDLLEXPORT PreserveFiles : public Calamares::CppJob class PLUGINDLLEXPORT PreserveFiles : public Calamares::CppJob
{ {
Q_OBJECT Q_OBJECT
enum class ItemType
{
None,
Path,
Log,
Config
};
struct Item
{
QString source;
QString dest;
CalamaresUtils::Permissions perm;
ItemType type;
};
using ItemList = QList< Item >; using ItemList = QList< Item >;
public: public:

View File

@@ -0,0 +1,93 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "Item.h"
#include "Settings.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include "utils/NamedEnum.h"
#include "utils/Yaml.h"
#include <QtTest/QtTest>
class PreserveFilesTests : public QObject
{
Q_OBJECT
public:
PreserveFilesTests();
~PreserveFilesTests() override {}
private Q_SLOTS:
void initTestCase();
void testItems_data();
void testItems();
};
PreserveFilesTests::PreserveFilesTests() {}
void
PreserveFilesTests::initTestCase()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
cDebug() << "PreserveFiles test started.";
// Ensure we have a system object, expect it to be a "bogus" one
CalamaresUtils::System* system = CalamaresUtils::System::instance();
QVERIFY( system );
cDebug() << Logger::SubEntry << "System @" << Logger::Pointer( system );
const auto* settings = Calamares::Settings::instance();
if ( !settings )
{
(void)new Calamares::Settings( true );
}
}
void
PreserveFilesTests::testItems_data()
{
QTest::addColumn< QString >( "filename" );
QTest::addColumn< bool >( "ok" );
QTest::addColumn< int >( "type_i" );
QTest::newRow( "log " ) << QString( "1a-log.conf" ) << true << smash( ItemType::Log );
QTest::newRow( "config " ) << QString( "1b-config.conf" ) << true << smash( ItemType::Config );
QTest::newRow( "src " ) << QString( "1c-src.conf" ) << true << smash( ItemType::Path );
QTest::newRow( "filename" ) << QString( "1d-filename.conf" ) << true << smash( ItemType::Path );
QTest::newRow( "empty " ) << QString( "1e-empty.conf" ) << false << smash( ItemType::None );
QTest::newRow( "bad " ) << QString( "1f-bad.conf" ) << false << smash( ItemType::None );
}
void
PreserveFilesTests::testItems()
{
QFETCH( QString, filename );
QFETCH( bool, ok );
QFETCH( int, type_i );
QFile fi( QString( "%1/tests/%2" ).arg( BUILD_AS_TEST, filename ) );
QVERIFY( fi.exists() );
bool config_file_ok = false;
const auto map = CalamaresUtils::loadYaml( fi, &config_file_ok );
QVERIFY( config_file_ok );
CalamaresUtils::Permissions perm( QStringLiteral( "adridg:adridg:0750" ) );
auto i = Item::fromVariant( map[ "item" ], perm );
QCOMPARE( bool( i ), ok );
QCOMPARE( smash( i.type() ), type_i );
}
QTEST_GUILESS_MAIN( PreserveFilesTests )
#include "utils/moc-warnings.h"
#include "Tests.moc"

View File

@@ -7,42 +7,58 @@
# the list should have one of these forms: # the list should have one of these forms:
# #
# - an absolute path (probably within the host system). This will be preserved # - an absolute path (probably within the host system). This will be preserved
# as the same path within the target system (chroot). If, globally, dontChroot # as the same path within the target system (chroot). If, globally,
# is true, then these items are ignored (since the destination is the same # *dontChroot* is true, then these items will be ignored (since the
# as the source). # destination is the same as the source).
# - a map with a *dest* key. The *dest* value is a path interpreted in the # - a map with a *dest* key. The *dest* value is a path interpreted in the
# target system (if dontChroot is true, in the host system). Relative paths # target system (if the global *dontChroot* is true, then the host is the
# are not recommended. There are three possible other keys in the map: # target as well). Relative paths are not recommended. There are two
# ways to select the source data for the file:
# - *from*, which must have one of the values, below; it is used to # - *from*, which must have one of the values, below; it is used to
# preserve files whose pathname is known to Calamares internally. # preserve files whose pathname is known to Calamares internally.
# - *src*, to refer to a path interpreted in the host system. Relative # - *src*, to refer to a path interpreted in the host system. Relative
# paths are not recommended, and are interpreted relative to where # paths are not recommended, and are interpreted relative to where
# Calamares is being run. # Calamares is being run.
# Exactly one of the two source keys (either *from* or *src*) must be set.
#
# Special values for the key *from* are:
# - *log*, for the complete log file (up to the moment the preservefiles
# module is run),
# - *config*, for a JSON dump of the contents of global storage.
# Note that this may contain sensitive information, and should be
# given restrictive permissions.
#
# A map with a *dest* key can have these additional fields:
# - *perm*, is a colon-separated tuple of <user>:<group>:<mode> # - *perm*, is a colon-separated tuple of <user>:<group>:<mode>
# where <mode> is in octal (e.g. 4777 for wide-open, 0400 for read-only # where <mode> is in octal (e.g. 4777 for wide-open, 0400 for read-only
# by owner). If set, the file's ownership and permissions are set to # by owner). If set, the file's ownership and permissions are set to
# those values within the target system; if not set, no permissions # those values within the target system; if not set, no permissions
# are changed. # are changed.
# Only one of the two source keys (either *from* or *src*) may be set. # - *optional*, is a boolean; if this is set to `true` then failure to
# preserve the file will **not** be counted as a failure of the
# module, and installation will proceed. Set this for files that might
# not exist in the host system (e.g. nvidia configuration files that
# are created in some boot scenarios and not in others).
# #
# The target filename is modified as follows: # The target path (*dest*) is modified as follows:
# - `@@ROOT@@` is replaced by the path to the target root (may be /) # - `@@ROOT@@` is replaced by the path to the target root (may be /).
# There is never any reason to use this, since the *dest* is already
# interpreted in the target system.
# - `@@USER@@` is replaced by the username entered by on the user # - `@@USER@@` is replaced by the username entered by on the user
# page (may be empty, for instance if no user page is enabled) # page (may be empty, for instance if no user page is enabled)
# #
# Special values for the key *from* are: #
# - *log*, for the complete log file (up to the moment the preservefiles #
# module is run),
# - *config*, for a JSON dump of the contents of global storage
---
files: files:
- /etc/oem-information
- from: log - from: log
dest: /root/install.log dest: /var/log/Calamares.log
perm: root:wheel:644 perm: root:wheel:600
- from: config - from: config
dest: /root/install.json dest: /var/log/Calamares-install.json
perm: root:wheel:400 perm: root:wheel:600
# - src: /var/log/nvidia.conf
# dest: /var/log/Calamares-nvidia.conf
# optional: true
# The *perm* key contains a default value to apply to all files listed # The *perm* key contains a default value to apply to all files listed
# above that do not have a *perm* key of their own. If not set, # above that do not have a *perm* key of their own. If not set,

View File

@@ -0,0 +1,37 @@
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: GPL-3.0-or-later
---
$schema: https://json-schema.org/schema#
$id: https://calamares.io/schemas/preservefiles
additionalProperties: false
type: object
properties:
# TODO: it's a particularly-formatted string
perm: { type: string }
files:
type: array
items:
# There are three entries here because: string, or an entry with
# a src (but no from) or an entry with from (but no src).
anyOf:
- type: string
- type: object
properties:
dest: { type: string }
src: { type: string }
# TODO: it's a particularly-formatted string
perm: { type: string }
optional: { type: boolean }
required: [ dest ]
additionalProperties: false
- type: object
properties:
dest: { type: string }
from: { type: string, enum: [config, log] }
# TODO: it's a particularly-formatted string
perm: { type: string }
optional: { type: boolean }
required: [ dest ]
additionalProperties: false
required: [ files ]

View File

@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
item:
from: log
dest: /var/log/Calamares.log
perm: root:wheel:601

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
item:
from: config
dest: /var/log/Calamares-install.json
perm: root:wheel:600

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
item:
src: /root/.cache/calamares/session.log
dest: /var/log/Calamares.log
perm: root:wheel:600

View File

@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
item:
src: /root/.cache/calamares/session.log
dest: /var/log/Calamares.log
perm: root:wheel:600

View File

@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
item: []

View File

@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
item:
bop: 1

View File

@@ -19,6 +19,8 @@
#include <KMacroExpander> #include <KMacroExpander>
#include <QCoreApplication>
#include <chrono> #include <chrono>
@@ -37,7 +39,6 @@ namespace
*/ */
class TrackingInstallJob : public Calamares::Job class TrackingInstallJob : public Calamares::Job
{ {
Q_OBJECT
public: public:
TrackingInstallJob( const QString& url ); TrackingInstallJob( const QString& url );
~TrackingInstallJob() override; ~TrackingInstallJob() override;
@@ -58,7 +59,6 @@ private:
*/ */
class TrackingMachineUpdateManagerJob : public Calamares::Job class TrackingMachineUpdateManagerJob : public Calamares::Job
{ {
Q_OBJECT
public: public:
~TrackingMachineUpdateManagerJob() override; ~TrackingMachineUpdateManagerJob() override;
@@ -75,7 +75,6 @@ public:
*/ */
class TrackingKUserFeedbackJob : public Calamares::Job class TrackingKUserFeedbackJob : public Calamares::Job
{ {
Q_OBJECT
public: public:
TrackingKUserFeedbackJob( const QString& username, const QStringList& areas ); TrackingKUserFeedbackJob( const QString& username, const QStringList& areas );
~TrackingKUserFeedbackJob() override; ~TrackingKUserFeedbackJob() override;
@@ -99,13 +98,13 @@ TrackingInstallJob::~TrackingInstallJob() {}
QString QString
TrackingInstallJob::prettyName() const TrackingInstallJob::prettyName() const
{ {
return tr( "Installation feedback" ); return QCoreApplication::translate( "TrackingInstallJob", "Installation feedback" );
} }
QString QString
TrackingInstallJob::prettyStatusMessage() const TrackingInstallJob::prettyStatusMessage() const
{ {
return tr( "Sending installation feedback." ); return QCoreApplication::translate( "TrackingInstallJob", "Sending installation feedback." );
} }
Calamares::JobResult Calamares::JobResult
@@ -122,8 +121,9 @@ TrackingInstallJob::exec()
if ( result.status == RequestStatus::Timeout ) if ( result.status == RequestStatus::Timeout )
{ {
cWarning() << "install-tracking request timed out."; cWarning() << "install-tracking request timed out.";
return Calamares::JobResult::error( tr( "Internal error in install-tracking." ), return Calamares::JobResult::error(
tr( "HTTP request timed out." ) ); QCoreApplication::translate( "TrackingInstallJob", "Internal error in install-tracking." ),
QCoreApplication::translate( "TrackingInstallJob", "HTTP request timed out." ) );
} }
return Calamares::JobResult::ok(); return Calamares::JobResult::ok();
} }
@@ -133,13 +133,13 @@ TrackingMachineUpdateManagerJob::~TrackingMachineUpdateManagerJob() {}
QString QString
TrackingMachineUpdateManagerJob::prettyName() const TrackingMachineUpdateManagerJob::prettyName() const
{ {
return tr( "Machine feedback" ); return QCoreApplication::translate( "TrackingMachineUpdateManagerJob", "Machine feedback" );
} }
QString QString
TrackingMachineUpdateManagerJob::prettyStatusMessage() const TrackingMachineUpdateManagerJob::prettyStatusMessage() const
{ {
return tr( "Configuring machine feedback." ); return QCoreApplication::translate( "TrackingMachineUpdateManagerJob", "Configuring machine feedback." );
} }
Calamares::JobResult Calamares::JobResult
@@ -162,14 +162,20 @@ TrackingMachineUpdateManagerJob::exec()
else if ( r > 0 ) else if ( r > 0 )
{ {
return Calamares::JobResult::error( return Calamares::JobResult::error(
tr( "Error in machine feedback configuration." ), QCoreApplication::translate( "TrackingMachineUpdateManagerJob",
tr( "Could not configure machine feedback correctly, script error %1." ).arg( r ) ); "Error in machine feedback configuration." ),
QCoreApplication::translate( "TrackingMachineUpdateManagerJob",
"Could not configure machine feedback correctly, script error %1." )
.arg( r ) );
} }
else else
{ {
return Calamares::JobResult::error( return Calamares::JobResult::error(
tr( "Error in machine feedback configuration." ), QCoreApplication::translate( "TrackingMachineUpdateManagerJob",
tr( "Could not configure machine feedback correctly, Calamares error %1." ).arg( r ) ); "Error in machine feedback configuration." ),
QCoreApplication::translate( "TrackingMachineUpdateManagerJob",
"Could not configure machine feedback correctly, Calamares error %1." )
.arg( r ) );
} }
} }
@@ -184,13 +190,13 @@ TrackingKUserFeedbackJob::~TrackingKUserFeedbackJob() {}
QString QString
TrackingKUserFeedbackJob::prettyName() const TrackingKUserFeedbackJob::prettyName() const
{ {
return tr( "KDE user feedback" ); return QCoreApplication::translate( "TrackingKUserFeedbackJob", "KDE user feedback" );
} }
QString QString
TrackingKUserFeedbackJob::prettyStatusMessage() const TrackingKUserFeedbackJob::prettyStatusMessage() const
{ {
return tr( "Configuring KDE user feedback." ); return QCoreApplication::translate( "TrackingKUserFeedbackJob", "Configuring KDE user feedback." );
} }
Calamares::JobResult Calamares::JobResult
@@ -212,21 +218,25 @@ FeedbackLevel=16
if ( r > 0 ) if ( r > 0 )
{ {
return Calamares::JobResult::error( return Calamares::JobResult::error(
tr( "Error in KDE user feedback configuration." ), QCoreApplication::translate( "TrackingKUserFeedbackJob", "Error in KDE user feedback configuration." ),
tr( "Could not configure KDE user feedback correctly, script error %1." ).arg( r ) ); QCoreApplication::translate( "TrackingKUserFeedbackJob",
"Could not configure KDE user feedback correctly, script error %1." )
.arg( r ) );
} }
else if ( r < 0 ) else if ( r < 0 )
{ {
return Calamares::JobResult::error( return Calamares::JobResult::error(
tr( "Error in KDE user feedback configuration." ), QCoreApplication::translate( "TrackingKUserFeedbackJob", "Error in KDE user feedback configuration." ),
tr( "Could not configure KDE user feedback correctly, Calamares error %1." ).arg( r ) ); QCoreApplication::translate( "TrackingKUserFeedbackJob",
"Could not configure KDE user feedback correctly, Calamares error %1." )
.arg( r ) );
} }
} }
return Calamares::JobResult::ok(); return Calamares::JobResult::ok();
} }
} // namespace } // namespace
void void
addJob( Calamares::JobList& list, InstallTrackingConfig* config ) addJob( Calamares::JobList& list, InstallTrackingConfig* config )
@@ -290,7 +300,3 @@ addJob( Calamares::JobList& list, UserTrackingConfig* config )
} }
} }
} }
#include "utils/moc-warnings.h"
#include "TrackingJobs.moc"

View File

@@ -80,6 +80,7 @@ def run():
if(libcalamares.job.configuration and if(libcalamares.job.configuration and
"srcLog" in libcalamares.job.configuration and "srcLog" in libcalamares.job.configuration and
"destLog" in libcalamares.job.configuration): "destLog" in libcalamares.job.configuration):
libcalamares.utils.warning("Log-file preserving is **deprecated** in the *umount* module")
log_source = libcalamares.job.configuration["srcLog"] log_source = libcalamares.job.configuration["srcLog"]
log_destination = libcalamares.job.configuration["destLog"] log_destination = libcalamares.job.configuration["destLog"]
# Relocate log_destination into target system # Relocate log_destination into target system

View File

@@ -10,16 +10,21 @@
# The "copy log files" functionality is deprecated; use the *preservefiles* # The "copy log files" functionality is deprecated; use the *preservefiles*
# module instead, which is more flexible. # module instead, which is more flexible.
# #
# This module has two configuration keys:
# srcLog location in the live system where the log is
# destLog location in the target system to copy the log
# #
--- ---
# example when using the normal Calamares log: # This is a **deprecated** example. Use the *preservefiles* module
srcLog: "/root/.cache/calamares/session.log" # instead, where the equivalent configuration is this:
destLog: "/var/log/Calamares.log" #
# files:
# - from: log
# dest: /var/log/installation.log
#
# Note that the "equivalent configuration" always finds the log,
# and is not dependent on specific user names or the vagaries of
# polkit configuration -- so it is a **better** "equivalent".
#
# example when using a log created by `sudo calamares -d`: # example when using a log created by `sudo calamares -d`:
#srcLog: "/home/live/installation.log" #srcLog: "/home/live/installation.log"
#destLog: "/var/log/installation.log" #destLog: "/var/log/installation.log"
srcLog: "/bogus/just/do/not/use/this/anymore.txt"