diff --git a/CHANGES-3.2 b/CHANGES-3.2
index 6cf5a99badaea66137972b9833f2c12f5983b40a..baaf261d44db569028b4b013e94a2cbf67bc9a6b 100644
--- a/CHANGES-3.2
+++ b/CHANGES-3.2
@@ -7,16 +7,30 @@ contributors are listed. Note that Calamares does not have a historical
 changelog -- this log starts with version 3.2.0. The release notes on the
 website will have to do for older versions.
 
-# 3.2.48 (unreleased) #
+# 3.2.48 (2021-12-03) #
 
 This release contains contributions from (alphabetically by first name):
- - No external contributors yet
+ - Evan James
 
 ## Core ##
- - No core changes yet
+ - Python modules now have `warn()` and `error()` methods they can call,
+   alongside the existing `debug()` and `warning()` (all live in the
+   *libcalamares.utils* module).
+ - Python modules can load YAML files via `libcalamares.utils.load_yaml()`.
+   This may be the most useful for test-scripts.
 
 ## Modules ##
- - No module changes yet
+ - *fstab* now has a separate, special, flags-setting for swap subvolumes
+   on btrfs. A swap subvolume is created if a swap **file** (not a separate
+   partition) is selected in the auto-partitioning page. (Thanks Evan)
+ - When using btrfs, the *mount* module creates subvolumes. It was not
+   possible to **avoid** having a subvolume name created for the root.
+   This is now possible. #1837
+ - The *packages* module now has some special settings for the `pacman`
+   package manager (generally used on Arch-derivatives). This allows
+   tweaking of the installation process, if downloads are slow or
+   packages may fail to install. See the `packages.conf` file for
+   details. (Thanks Evan)
 
 
 # 3.2.47 (2021-11-19) #
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 67bb1f319fb03f25da11af9199fd369d814f9ea5..c998d43d08e001cded5057572db8b9baf5d9c312 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,7 +45,10 @@ project( CALAMARES
     LANGUAGES C CXX
 )
 
-set( CALAMARES_VERSION_RC 1 )  # Set to 0 during release cycle, 1 during development
+set( CALAMARES_VERSION_RC 0 )  # Set to 0 during release cycle, 1 during development
+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." )
+endif()
 
 ### OPTIONS
 #
diff --git a/calamares.desktop b/calamares.desktop
index 761a7db015247465705a045dcb443c33e0e86281..bfe3955b146dd5c22a22b5ead01dc4981cf44722 100644
--- a/calamares.desktop
+++ b/calamares.desktop
@@ -236,7 +236,7 @@ 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]=Calamares - 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
diff --git a/data/FreeBSD/Makefile b/data/FreeBSD/Makefile
deleted file mode 100644
index d9b7e504365af62fecf6d16f282612cbdc23fdd5..0000000000000000000000000000000000000000
--- a/data/FreeBSD/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-# $FreeBSD$
-# 
-# SPDX-FileCopyrightText: 2020 Adriaan de Groot <adridg@FreeBSD.org>
-# SPDX-License-Identifier: BSD-2-Clause
-
-PORTNAME=	calamares
-DISTVERSION=	3.2.25
-CATEGORIES=	sysutils
-MASTER_SITES=	https://github.com/${PORTNAME}/${PORTNAME}/releases/download/v${DISTVERSION}/
-
-MAINTAINER=	adridg@FreeBSD.org
-COMMENT=	GUI System installer and OEM configurator
-
-LICENSE=	GPLv3
-LICENSE_FILE=	${WRKSRC}/LICENSE
-
-LIB_DEPENDS=	libyaml-cpp.so:devel/yaml-cpp \
-		libpwquality.so:security/libpwquality \
-		libboost_python${PYTHON_SUFFIX}.so:devel/boost-python-libs
-
-USES=		cmake compiler:c++17-lang gettext kde:5 pkgconfig \
-		python:3.3+ qt:5
-USE_QT=		concurrent core dbus declarative gui \
-		network quickcontrols2 svg widgets xml \
-		buildtools_build linguist_build qmake_build
-USE_KDE=	coreaddons dbusaddons parts service \
-		ecm_build
-USE_LDCONFIG=	yes
-
-CMAKE_OFF=	WITH_KF5Crash \
-		INSTALL_CONFIG \
-		INSTALL_COMPLETION \
-		INSTALL_POLKIT
-CMAKE_ON=	CMAKE_DISABLE_FIND_PACKAGE_KPMcore
-CMAKE_ARGS=	-DSKIP_MODULES="webview"
-
-.include <bsd.port.mk>
diff --git a/data/FreeBSD/distinfo b/data/FreeBSD/distinfo
deleted file mode 100644
index e333963a8ddbb1d23b25d37876f3ccfe99606e4d..0000000000000000000000000000000000000000
--- a/data/FreeBSD/distinfo
+++ /dev/null
@@ -1,3 +0,0 @@
-TIMESTAMP = 1592339404
-SHA256 (calamares-3.2.25.tar.gz) = 797ce33db7d4e4c06bbccef95f6c4023f7628e91bd142896695565fed4ae8c4b
-SIZE (calamares-3.2.25.tar.gz) = 3580197
diff --git a/data/FreeBSD/pkg-descr b/data/FreeBSD/pkg-descr
deleted file mode 100644
index 39cb4335c9b185500ba687fd08e3f89462a88558..0000000000000000000000000000000000000000
--- a/data/FreeBSD/pkg-descr
+++ /dev/null
@@ -1,14 +0,0 @@
-Calamares is an installer framework. By design it is very customizable,
-in order to satisfy a wide variety of needs and use cases.
-
-Calamares aims to be easy, usable, beautiful, pragmatic, inclusive and
-distribution-agnostic.
-
-Got a Linux distribution but no system installer? Grab Calamares, mix
-and match any number of Calamares modules (or write your own in Python
-or C++), throw together some branding, package it up and you are ready
-to ship!
-
-(The above applies to FreeBSD as well)
-
-WWW: https://calamares.io/
diff --git a/data/FreeBSD/pkg-plist b/data/FreeBSD/pkg-plist
deleted file mode 100644
index 7f588b7a9dc95a5ae04db6e79e06a07cefab9154..0000000000000000000000000000000000000000
--- a/data/FreeBSD/pkg-plist
+++ /dev/null
@@ -1,224 +0,0 @@
-bin/calamares
-include/libcalamares/CalamaresConfig.h
-include/libcalamares/CppJob.h
-include/libcalamares/DllMacro.h
-include/libcalamares/GlobalStorage.h
-include/libcalamares/Job.h
-include/libcalamares/JobExample.h
-include/libcalamares/JobQueue.h
-include/libcalamares/ProcessJob.h
-include/libcalamares/PythonHelper.h
-include/libcalamares/PythonJob.h
-include/libcalamares/PythonJobApi.h
-include/libcalamares/Settings.h
-include/libcalamares/utils/BoostPython.h
-include/libcalamares/utils/CalamaresUtilsSystem.h
-include/libcalamares/utils/CommandList.h
-include/libcalamares/utils/Dirs.h
-include/libcalamares/utils/Entropy.h
-include/libcalamares/utils/Logger.h
-include/libcalamares/utils/NamedEnum.h
-include/libcalamares/utils/NamedSuffix.h
-include/libcalamares/utils/PluginFactory.h
-include/libcalamares/utils/RAII.h
-include/libcalamares/utils/Retranslator.h
-include/libcalamares/utils/String.h
-include/libcalamares/utils/Tests.h
-include/libcalamares/utils/UMask.h
-include/libcalamares/utils/Units.h
-include/libcalamares/utils/Variant.h
-include/libcalamares/utils/Yaml.h
-include/libcalamares/utils/moc-warnings.h
-lib/calamares/libcalamares.so
-lib/calamares/modules/bootloader/main.py
-lib/calamares/modules/bootloader/module.desc
-lib/calamares/modules/bootloader/test.yaml
-lib/calamares/modules/contextualprocess/libcalamares_job_contextualprocess.so
-lib/calamares/modules/contextualprocess/module.desc
-lib/calamares/modules/displaymanager/main.py
-lib/calamares/modules/displaymanager/module.desc
-lib/calamares/modules/dracut/main.py
-lib/calamares/modules/dracut/module.desc
-lib/calamares/modules/dracutlukscfg/libcalamares_job_dracutlukscfg.so
-lib/calamares/modules/dracutlukscfg/module.desc
-lib/calamares/modules/dummycpp/libcalamares_job_dummycpp.so
-lib/calamares/modules/dummycpp/module.desc
-lib/calamares/modules/dummyprocess/module.desc
-lib/calamares/modules/dummypython/main.py
-lib/calamares/modules/dummypython/module.desc
-lib/calamares/modules/finished/libcalamares_viewmodule_finished.so
-lib/calamares/modules/finished/module.desc
-lib/calamares/modules/fstab/main.py
-lib/calamares/modules/fstab/module.desc
-lib/calamares/modules/fstab/test.yaml
-lib/calamares/modules/grubcfg/main.py
-lib/calamares/modules/grubcfg/module.desc
-lib/calamares/modules/hostinfo/libcalamares_job_hostinfo.so
-lib/calamares/modules/hostinfo/module.desc
-lib/calamares/modules/hwclock/main.py
-lib/calamares/modules/hwclock/module.desc
-lib/calamares/modules/initcpio/libcalamares_job_initcpio.so
-lib/calamares/modules/initcpio/module.desc
-lib/calamares/modules/initcpiocfg/main.py
-lib/calamares/modules/initcpiocfg/module.desc
-lib/calamares/modules/initramfs/libcalamares_job_initramfs.so
-lib/calamares/modules/initramfs/module.desc
-lib/calamares/modules/initramfscfg/encrypt_hook
-lib/calamares/modules/initramfscfg/encrypt_hook_nokey
-lib/calamares/modules/initramfscfg/main.py
-lib/calamares/modules/initramfscfg/module.desc
-lib/calamares/modules/interactiveterminal/libcalamares_viewmodule_interactiveterminal.so
-lib/calamares/modules/interactiveterminal/module.desc
-lib/calamares/modules/keyboard/libcalamares_viewmodule_keyboard.so
-lib/calamares/modules/keyboard/module.desc
-lib/calamares/modules/keyboardq/libcalamares_viewmodule_keyboardq.so
-lib/calamares/modules/keyboardq/module.desc
-lib/calamares/modules/license/libcalamares_viewmodule_license.so
-lib/calamares/modules/license/module.desc
-lib/calamares/modules/locale/libcalamares_viewmodule_locale.so
-lib/calamares/modules/locale/module.desc
-lib/calamares/modules/localecfg/main.py
-lib/calamares/modules/localecfg/module.desc
-lib/calamares/modules/localeq/libcalamares_viewmodule_localeq.so
-lib/calamares/modules/localeq/module.desc
-lib/calamares/modules/luksbootkeyfile/libcalamares_job_luksbootkeyfile.so
-lib/calamares/modules/luksbootkeyfile/module.desc
-lib/calamares/modules/luksopenswaphookcfg/main.py
-lib/calamares/modules/luksopenswaphookcfg/module.desc
-lib/calamares/modules/machineid/libcalamares_job_machineid.so
-lib/calamares/modules/machineid/module.desc
-lib/calamares/modules/mount/main.py
-lib/calamares/modules/mount/module.desc
-lib/calamares/modules/mount/test.yaml
-lib/calamares/modules/netinstall/libcalamares_viewmodule_netinstall.so
-lib/calamares/modules/netinstall/module.desc
-lib/calamares/modules/networkcfg/main.py
-lib/calamares/modules/networkcfg/module.desc
-lib/calamares/modules/notesqml/libcalamares_viewmodule_notesqml.so
-lib/calamares/modules/notesqml/module.desc
-lib/calamares/modules/oemid/libcalamares_viewmodule_oemid.so
-lib/calamares/modules/oemid/module.desc
-lib/calamares/modules/openrcdmcryptcfg/main.py
-lib/calamares/modules/openrcdmcryptcfg/module.desc
-lib/calamares/modules/packagechooser/libcalamares_viewmodule_packagechooser.so
-lib/calamares/modules/packagechooser/module.desc
-lib/calamares/modules/packages/main.py
-lib/calamares/modules/packages/module.desc
-lib/calamares/modules/packages/test.yaml
-lib/calamares/modules/plymouthcfg/main.py
-lib/calamares/modules/plymouthcfg/module.desc
-lib/calamares/modules/preservefiles/libcalamares_job_preservefiles.so
-lib/calamares/modules/preservefiles/module.desc
-lib/calamares/modules/rawfs/main.py
-lib/calamares/modules/rawfs/module.desc
-lib/calamares/modules/removeuser/libcalamares_job_removeuser.so
-lib/calamares/modules/removeuser/module.desc
-lib/calamares/modules/services-openrc/main.py
-lib/calamares/modules/services-openrc/module.desc
-lib/calamares/modules/services-systemd/main.py
-lib/calamares/modules/services-systemd/module.desc
-lib/calamares/modules/shellprocess/libcalamares_job_shellprocess.so
-lib/calamares/modules/shellprocess/module.desc
-lib/calamares/modules/summary/libcalamares_viewmodule_summary.so
-lib/calamares/modules/summary/module.desc
-lib/calamares/modules/tracking/libcalamares_viewmodule_tracking.so
-lib/calamares/modules/tracking/module.desc
-lib/calamares/modules/umount/main.py
-lib/calamares/modules/umount/module.desc
-lib/calamares/modules/unpackfs/main.py
-lib/calamares/modules/unpackfs/module.desc
-lib/calamares/modules/unpackfs/runtests.sh
-lib/calamares/modules/users/libcalamares_viewmodule_users.so
-lib/calamares/modules/users/module.desc
-lib/calamares/modules/welcome/libcalamares_viewmodule_welcome.so
-lib/calamares/modules/welcome/module.desc
-lib/calamares/modules/welcomeq/libcalamares_viewmodule_welcomeq.so
-lib/calamares/modules/welcomeq/module.desc
-lib/cmake/Calamares/CMakeColors.cmake
-lib/cmake/Calamares/CalamaresAddBrandingSubdirectory.cmake
-lib/cmake/Calamares/CalamaresAddLibrary.cmake
-lib/cmake/Calamares/CalamaresAddModuleSubdirectory.cmake
-lib/cmake/Calamares/CalamaresAddPlugin.cmake
-lib/cmake/Calamares/CalamaresAddTest.cmake
-lib/cmake/Calamares/CalamaresAddTranslations.cmake
-lib/cmake/Calamares/CalamaresAutomoc.cmake
-lib/cmake/Calamares/CalamaresConfig.cmake
-lib/cmake/Calamares/CalamaresConfigVersion.cmake
-lib/cmake/Calamares/CalamaresLibraryDepends-%%CMAKE_BUILD_TYPE%%.cmake
-lib/cmake/Calamares/CalamaresLibraryDepends.cmake
-lib/cmake/Calamares/CalamaresUse.cmake
-lib/libcalamares.so
-lib/libcalamares.so.3.2.25
-lib/libcalamaresui.so
-lib/libcalamaresui.so.3.2.25
-man/man8/calamares.8.gz
-share/applications/calamares.desktop
-%%DATADIR%%/branding/default/banner.png
-%%DATADIR%%/branding/default/branding.desc
-%%DATADIR%%/branding/default/lang/calamares-default_ar.qm
-%%DATADIR%%/branding/default/lang/calamares-default_en.qm
-%%DATADIR%%/branding/default/lang/calamares-default_eo.qm
-%%DATADIR%%/branding/default/lang/calamares-default_fr.qm
-%%DATADIR%%/branding/default/lang/calamares-default_nl.qm
-%%DATADIR%%/branding/default/languages.png
-%%DATADIR%%/branding/default/show.qml
-%%DATADIR%%/branding/default/squid.png
-%%DATADIR%%/branding/default/stylesheet.qss
-%%DATADIR%%/qml/calamares/slideshow/BackButton.qml
-%%DATADIR%%/qml/calamares/slideshow/ForwardButton.qml
-%%DATADIR%%/qml/calamares/slideshow/NavButton.qml
-%%DATADIR%%/qml/calamares/slideshow/Presentation.qml
-%%DATADIR%%/qml/calamares/slideshow/Slide.qml
-%%DATADIR%%/qml/calamares/slideshow/SlideCounter.qml
-%%DATADIR%%/qml/calamares/slideshow/qmldir
-share/icons/hicolor/scalable/apps/calamares.svg
-share/locale/ar/LC_MESSAGES/calamares-python.mo
-share/locale/as/LC_MESSAGES/calamares-python.mo
-share/locale/ast/LC_MESSAGES/calamares-python.mo
-share/locale/be/LC_MESSAGES/calamares-python.mo
-share/locale/bg/LC_MESSAGES/calamares-python.mo
-share/locale/ca/LC_MESSAGES/calamares-python.mo
-share/locale/cs_CZ/LC_MESSAGES/calamares-python.mo
-share/locale/da/LC_MESSAGES/calamares-python.mo
-share/locale/de/LC_MESSAGES/calamares-python.mo
-share/locale/el/LC_MESSAGES/calamares-python.mo
-share/locale/en_GB/LC_MESSAGES/calamares-python.mo
-share/locale/eo/LC_MESSAGES/calamares-python.mo
-share/locale/es/LC_MESSAGES/calamares-python.mo
-share/locale/es_MX/LC_MESSAGES/calamares-python.mo
-share/locale/es_PR/LC_MESSAGES/calamares-python.mo
-share/locale/et/LC_MESSAGES/calamares-python.mo
-share/locale/eu/LC_MESSAGES/calamares-python.mo
-share/locale/fi_FI/LC_MESSAGES/calamares-python.mo
-share/locale/fr/LC_MESSAGES/calamares-python.mo
-share/locale/gl/LC_MESSAGES/calamares-python.mo
-share/locale/he/LC_MESSAGES/calamares-python.mo
-share/locale/hi/LC_MESSAGES/calamares-python.mo
-share/locale/hr/LC_MESSAGES/calamares-python.mo
-share/locale/hu/LC_MESSAGES/calamares-python.mo
-share/locale/id/LC_MESSAGES/calamares-python.mo
-share/locale/is/LC_MESSAGES/calamares-python.mo
-share/locale/it_IT/LC_MESSAGES/calamares-python.mo
-share/locale/ja/LC_MESSAGES/calamares-python.mo
-share/locale/ko/LC_MESSAGES/calamares-python.mo
-share/locale/lt/LC_MESSAGES/calamares-python.mo
-share/locale/ml/LC_MESSAGES/calamares-python.mo
-share/locale/mr/LC_MESSAGES/calamares-python.mo
-share/locale/nb/LC_MESSAGES/calamares-python.mo
-share/locale/nl/LC_MESSAGES/calamares-python.mo
-share/locale/pl/LC_MESSAGES/calamares-python.mo
-share/locale/pt_BR/LC_MESSAGES/calamares-python.mo
-share/locale/pt_PT/LC_MESSAGES/calamares-python.mo
-share/locale/ro/LC_MESSAGES/calamares-python.mo
-share/locale/ru/LC_MESSAGES/calamares-python.mo
-share/locale/sk/LC_MESSAGES/calamares-python.mo
-share/locale/sl/LC_MESSAGES/calamares-python.mo
-share/locale/sq/LC_MESSAGES/calamares-python.mo
-share/locale/sr/LC_MESSAGES/calamares-python.mo
-share/locale/sr@latin/LC_MESSAGES/calamares-python.mo
-share/locale/sv/LC_MESSAGES/calamares-python.mo
-share/locale/th/LC_MESSAGES/calamares-python.mo
-share/locale/tr_TR/LC_MESSAGES/calamares-python.mo
-share/locale/uk/LC_MESSAGES/calamares-python.mo
-share/locale/zh_CN/LC_MESSAGES/calamares-python.mo
-share/locale/zh_TW/LC_MESSAGES/calamares-python.mo
diff --git a/lang/calamares_pt_BR.ts b/lang/calamares_pt_BR.ts
index 435f398e45724e281c420ab37ac64c219f7723f0..517a84b3aed45fc035371a2d5249c1c6bffea0af 100644
--- a/lang/calamares_pt_BR.ts
+++ b/lang/calamares_pt_BR.ts
@@ -689,27 +689,27 @@ O instalador será fechado e todas as alterações serão perdidas.</translation
     <message>
       <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="259"/>
       <source>Successfully unmounted %1.</source>
-      <translation type="unfinished"/>
+      <translation>%1 desmontado com sucesso.</translation>
     </message>
     <message>
       <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="266"/>
       <source>Successfully disabled swap %1.</source>
-      <translation type="unfinished"/>
+      <translation>Swap %1 desativada com sucesso.</translation>
     </message>
     <message>
       <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="292"/>
       <source>Successfully cleared swap %1.</source>
-      <translation type="unfinished"/>
+      <translation>Swap %1 limpa com sucesso.</translation>
     </message>
     <message>
       <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="306"/>
       <source>Successfully closed mapper device %1.</source>
-      <translation type="unfinished"/>
+      <translation>Dispositivo de mapeamento %1 fechado com sucesso.</translation>
     </message>
     <message>
       <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="319"/>
       <source>Successfully disabled volume group %1.</source>
-      <translation type="unfinished"/>
+      <translation>Grupo de volume %1 desativado com sucesso.</translation>
     </message>
     <message>
       <location filename="../src/modules/partition/jobs/ClearMountsJob.cpp" line="358"/>
diff --git a/lang/calamares_pt_PT.ts b/lang/calamares_pt_PT.ts
index dbbc088a952f245ec0a021922dd2ed4e9b94d2a0..951645ab1559d2fd067838b49a628f43400e5780 100644
--- a/lang/calamares_pt_PT.ts
+++ b/lang/calamares_pt_PT.ts
@@ -789,7 +789,7 @@ O instalador será encerrado e todas as alterações serão perdidas.</translati
     <message>
       <location filename="../src/modules/locale/Config.cpp" line="380"/>
       <source>The system language will be set to %1.</source>
-      <translation>A linguagem do sistema será definida para %1.</translation>
+      <translation>O idioma do sistema será definido para %1.</translation>
     </message>
     <message>
       <location filename="../src/modules/locale/Config.cpp" line="387"/>
@@ -1684,7 +1684,7 @@ O instalador será encerrado e todas as alterações serão perdidas.</translati
     <message>
       <location filename="../src/modules/hostinfo/HostInfoJob.cpp" line="42"/>
       <source>Collecting information about your machine.</source>
-      <translation>A recolher informação acerca da sua máquina.</translation>
+      <translation>A recolher informação sobre a sua máquina.</translation>
     </message>
   </context>
   <context>
@@ -3988,7 +3988,7 @@ Saída de Dados:
     <message>
       <location filename="../src/modules/welcome/WelcomePage.cpp" line="228"/>
       <source>%1 support</source>
-      <translation>%1 suporte</translation>
+      <translation>Suporte do %1</translation>
     </message>
     <message>
       <location filename="../src/modules/welcome/WelcomePage.cpp" line="235"/>
@@ -3998,7 +3998,7 @@ Saída de Dados:
     <message>
       <location filename="../src/modules/welcome/WelcomePage.cpp" line="235"/>
       <source>About %1 installer</source>
-      <translation>Acerca %1 instalador</translation>
+      <translation>Acerca do instalador %1</translation>
     </message>
     <message>
       <location filename="../src/modules/welcome/WelcomePage.cpp" line="238"/>
diff --git a/lang/calamares_ru.ts b/lang/calamares_ru.ts
index 71266222492a02173b5b1908ae84879cca729c0f..c722e2359f3febe47015278b836478dd618079b1 100644
--- a/lang/calamares_ru.ts
+++ b/lang/calamares_ru.ts
@@ -189,7 +189,7 @@
     <message>
       <location filename="../src/libcalamares/ProcessJob.cpp" line="43"/>
       <source>Run command '%1' in target system.</source>
-      <translation>Запустить комманду'%1'в целевой системе.</translation>
+      <translation>Запустить команду '%1' в целевой системе.</translation>
     </message>
     <message>
       <location filename="../src/libcalamares/ProcessJob.cpp" line="43"/>
@@ -274,10 +274,10 @@
       <location filename="../src/libcalamares/modulesystem/RequirementsChecker.cpp" line="116"/>
       <source>(%n second(s))</source>
       <translation>
-        <numerusform>(% секунда)</numerusform>
-        <numerusform>(% секунд)</numerusform>
-        <numerusform>(% секунд)</numerusform>
+        <numerusform>(%n секунда)</numerusform>
+        <numerusform>(%n секунды)</numerusform>
         <numerusform>(%n секунд)</numerusform>
+        <numerusform>(%n секунд(ы))</numerusform>
       </translation>
     </message>
     <message>
@@ -2673,7 +2673,7 @@ The installer will quit and all changes will be lost.</source>
     <message>
       <location filename="../src/modules/partition/gui/PartitionLabelsView.cpp" line="203"/>
       <source>EFI system</source>
-      <translation>Система EFI</translation>
+      <translation>Системный раздел EFI</translation>
     </message>
     <message>
       <location filename="../src/modules/partition/gui/PartitionLabelsView.cpp" line="207"/>
diff --git a/lang/python/pt_BR/LC_MESSAGES/python.po b/lang/python/pt_BR/LC_MESSAGES/python.po
index d3cf730a0251f87eff07b141bd849238b62af74f..f8f3e8ca7e9e5fcfe7f6192786107d7244e8a721 100644
--- a/lang/python/pt_BR/LC_MESSAGES/python.po
+++ b/lang/python/pt_BR/LC_MESSAGES/python.po
@@ -330,7 +330,7 @@ msgstr "Não é possível habilitar o alvo <code>{name!s}</code> do systemd."
 
 #: src/modules/services-systemd/main.py:67
 msgid "Cannot enable systemd timer <code>{name!s}</code>."
-msgstr ""
+msgstr "Não foi possível ativar o cronômetro systemd <code>{name!s}</code>."
 
 #: src/modules/services-systemd/main.py:71
 msgid "Cannot disable systemd target <code>{name!s}</code>."
@@ -410,6 +410,8 @@ msgid ""
 "Failed to find unsquashfs, make sure you have the squashfs-tools package "
 "installed."
 msgstr ""
+"Não foi possível encontrar o unsquashfs, certifique-se de que o pacote "
+"squashfs-tools foi instalado."
 
 #: src/modules/unpackfs/main.py:479
 msgid "The destination \"{}\" in the target system is not a directory"
diff --git a/lang/python/vi/LC_MESSAGES/python.po b/lang/python/vi/LC_MESSAGES/python.po
index 83928edd66388cb0cbdc7c8913364847d6a97a26..900b9945479216745e06c47c2530179fe45f2f97 100644
--- a/lang/python/vi/LC_MESSAGES/python.po
+++ b/lang/python/vi/LC_MESSAGES/python.po
@@ -5,6 +5,7 @@
 # 
 # Translators:
 # T. Tran <transifex@emiu.net>, 2020
+# Th1nhhdk, 2021
 # 
 #, fuzzy
 msgid ""
@@ -13,7 +14,7 @@ msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-11-02 15:45+0100\n"
 "PO-Revision-Date: 2017-08-09 10:34+0000\n"
-"Last-Translator: T. Tran <transifex@emiu.net>, 2020\n"
+"Last-Translator: Th1nhhdk, 2021\n"
 "Language-Team: Vietnamese (https://www.transifex.com/calamares/teams/20061/vi/)\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -61,13 +62,15 @@ msgstr "Đang cài đặt bộ khởi động."
 
 #: src/modules/bootloader/main.py:508
 msgid "Bootloader installation error"
-msgstr ""
+msgstr "Lỗi cài đặt trình khởi động(bootloader)"
 
 #: src/modules/bootloader/main.py:509
 msgid ""
 "The bootloader could not be installed. The installation command "
 "<pre>{!s}</pre> returned error code {!s}."
 msgstr ""
+"Trình khởi động(bootloader) không thể được cài đặt. Lệnh cài đặt "
+"<pre>{!s}</pre>đã trả mã lỗi {!s}."
 
 #: src/modules/fstab/main.py:29
 msgid "Writing fstab."
diff --git a/src/libcalamares/PythonJob.cpp b/src/libcalamares/PythonJob.cpp
index afeebbe07529b69764381a53c7a7d1899d7e61bc..291adbc54cc71e66857864cf02216282abe6fc86 100644
--- a/src/libcalamares/PythonJob.cpp
+++ b/src/libcalamares/PythonJob.cpp
@@ -79,13 +79,25 @@ BOOST_PYTHON_MODULE( libcalamares )
     bp::scope utilsScope = utilsModule;
     Q_UNUSED( utilsScope )
 
+    // .. Logging functions
     bp::def(
         "debug", &CalamaresPython::debug, bp::args( "s" ), "Writes the given string to the Calamares debug stream." );
     bp::def( "warning",
              &CalamaresPython::warning,
              bp::args( "s" ),
              "Writes the given string to the Calamares warning stream." );
+    bp::def( "warn",
+             &CalamaresPython::warning,
+             bp::args( "s" ),
+             "Writes the given string to the Calamares warning stream." );
+    bp::def(
+        "error", &CalamaresPython::warning, bp::args( "s" ), "Writes the given string to the Calamares error stream." );
 
+
+    // .. YAML functions
+    bp::def( "load_yaml", &CalamaresPython::load_yaml, bp::args( "path" ), "Loads YAML from a file." );
+
+    // .. Filesystem functions
     bp::def( "mount",
              &CalamaresPython::mount,
              mount_overloads( bp::args( "device_path", "mount_point", "filesystem_name", "options" ),
@@ -94,6 +106,8 @@ BOOST_PYTHON_MODULE( libcalamares )
                               "-1 = QProcess crash\n"
                               "-2 = QProcess cannot start\n"
                               "-3 = bad arguments" ) );
+
+    // .. Process functions
     bp::def(
         "target_env_call",
         static_cast< int ( * )( const std::string&, const std::string&, int ) >( &CalamaresPython::target_env_call ),
@@ -152,6 +166,7 @@ BOOST_PYTHON_MODULE( libcalamares )
              host_env_process_output_overloads( bp::args( "command", "callback", "stdin", "timeout" ),
                                                 "Runs the specified command in the host system." ) );
 
+    // .. String functions
     bp::def( "obscure",
              &CalamaresPython::obscure,
              bp::args( "s" ),
@@ -160,7 +175,7 @@ BOOST_PYTHON_MODULE( libcalamares )
              "Applying the function to a string obscured by this function will result "
              "in the original string." );
 
-
+    // .. Translation functions
     bp::def( "gettext_languages",
              &CalamaresPython::gettext_languages,
              "Returns list of languages (most to least-specific) for gettext." );
diff --git a/src/libcalamares/PythonJobApi.cpp b/src/libcalamares/PythonJobApi.cpp
index 1713569a41557a033e63a6f9622fa6a917ee9075..bb2b8749e4a3775a99d0255438a909b45e458ae5 100644
--- a/src/libcalamares/PythonJobApi.cpp
+++ b/src/libcalamares/PythonJobApi.cpp
@@ -19,6 +19,7 @@
 #include "utils/RAII.h"
 #include "utils/Runner.h"
 #include "utils/String.h"
+#include "utils/Yaml.h"
 
 #include <QCoreApplication>
 #include <QDir>
@@ -139,19 +140,44 @@ check_target_env_output( const bp::list& args, const std::string& stdin, int tim
 }
 
 static const char output_prefix[] = "[PYTHON JOB]:";
+static inline void
+log_action( unsigned int level, const std::string& s )
+{
+    Logger::CDebug( level ) << output_prefix << QString::fromStdString( s );
+}
 
 void
 debug( const std::string& s )
 {
-    Logger::CDebug( Logger::LOGDEBUG ) << output_prefix << QString::fromStdString( s );
+    log_action( Logger::LOGDEBUG, s );
 }
 
 void
 warning( const std::string& s )
 {
-    Logger::CDebug( Logger::LOGWARNING ) << output_prefix << QString::fromStdString( s );
+    log_action( Logger::LOGWARNING, s );
+}
+
+void
+error( const std::string& s )
+{
+    log_action( Logger::LOGERROR, s );
 }
 
+boost::python::dict
+load_yaml( const std::string& path )
+{
+    const QString filePath = QString::fromStdString( path );
+    bool ok = false;
+    auto map = CalamaresUtils::loadYaml( filePath, &ok );
+    if ( !ok )
+    {
+        cWarning() << "Loading YAML from" << filePath << "failed.";
+    }
+    return variantMapToPyDict( map );
+}
+
+
 PythonJobInterface::PythonJobInterface( Calamares::PythonJob* parent )
     : m_parent( parent )
 {
diff --git a/src/libcalamares/PythonJobApi.h b/src/libcalamares/PythonJobApi.h
index 48bd4f87c2f6f3603bb839711f40709c37a43f6c..62346ceda5b064681be635deec0f11dacaeb0428 100644
--- a/src/libcalamares/PythonJobApi.h
+++ b/src/libcalamares/PythonJobApi.h
@@ -60,6 +60,12 @@ boost::python::list gettext_languages();
 
 void debug( const std::string& s );
 void warning( const std::string& s );
+void error( const std::string& s );
+
+/** @brief Loads YAML and returns (nested) dicts representing it
+ *
+ */
+boost::python::dict load_yaml( const std::string& path );
 
 class PythonJobInterface
 {
diff --git a/src/modules/fstab/fstab.conf b/src/modules/fstab/fstab.conf
index d35bebb0352e65ebaec3b903fcb0e34992c3207a..9c76f0c46bbe4dda30b86e578fd5552eeb6facea 100644
--- a/src/modules/fstab/fstab.conf
+++ b/src/modules/fstab/fstab.conf
@@ -15,9 +15,13 @@
 # With kernels 5.15 and newer be cautious of adding the option space_cache
 # to the btrfs mount options.  The default in 5.15 changed to space_cache=v2.
 # If space_cache or space_cache=v1 are specified, it may fail to remount.
+#
+# btrfs_swap options are used when a swapfile is chosen with a btrfs root
+# the options are applied to the subvolume which holds the swap partition
 mountOptions:
     default: defaults,noatime
     btrfs: defaults,noatime,autodefrag,compress=zstd
+    btrfs_swap: defaults,noatime
 
 # Mount options to use for the EFI System Partition. If not defined, the
 # *mountOptions* for *vfat* are used, or if that is not set either,
diff --git a/src/modules/fstab/fstab.schema.yaml b/src/modules/fstab/fstab.schema.yaml
index fc68fd2c58249eb2a705d021a1897bddac4cae77..087e82cac3333be787df5a2365e785bd11447011 100644
--- a/src/modules/fstab/fstab.schema.yaml
+++ b/src/modules/fstab/fstab.schema.yaml
@@ -22,6 +22,7 @@ properties:
             xfs: { type: string }
             swap: { type: string }
             btrfs: { type: string }
+            btrfs_swap: { type: string }
     efiMountOptions: { type: string }
     crypttabOptions: { type: string }
 required: [ mountOptions ]
diff --git a/src/modules/fstab/main.py b/src/modules/fstab/main.py
index 3a2dbcf41b62dbdb98a9e9bc06b48f051204a7c3..6a771a24b34e1b4b85258c9a004534cc3b4cee3e 100644
--- a/src/modules/fstab/main.py
+++ b/src/modules/fstab/main.py
@@ -236,7 +236,11 @@ class FstabGenerator(object):
             libcalamares.utils.debug("Ignoring foreign swap {!s} {!s}".format(disk_name, partition.get("uuid", None)))
             return None
 
-        options = self.get_mount_options(filesystem, mount_point)
+        # If this is btrfs subvol a dedicated to a swapfile, use different options than a normal btrfs subvol
+        if filesystem == "btrfs" and partition.get("subvol", None) == "/@swap":
+            options = self.get_mount_options("btrfs_swap", mount_point)
+        else:
+            options = self.get_mount_options(filesystem, mount_point)
 
         if is_ssd:
             extra = self.ssd_extra_mount_options.get(filesystem)
@@ -254,7 +258,8 @@ class FstabGenerator(object):
         if mount_point == "/":
             self.root_is_ssd = is_ssd
 
-        if filesystem == "btrfs" and "subvol" in partition:
+        # If there's a set-and-not-empty subvolume set, add it
+        if filesystem == "btrfs" and partition.get("subvol",None):
             options = "subvol={},".format(partition["subvol"]) + options
 
         if has_luks:
diff --git a/src/modules/locale/images/timezone_5.0.png b/src/modules/locale/images/timezone_5.0.png
index e8ea14466a852dc81a104b69da9da34f6103c5ab..a15aaccc09ddb9fdd6491ac3aab70342d72da645 100644
Binary files a/src/modules/locale/images/timezone_5.0.png and b/src/modules/locale/images/timezone_5.0.png differ
diff --git a/src/modules/mount/main.py b/src/modules/mount/main.py
index 900342e6d460799bee9536118ef2c6dd96cdce27..f186b0d26000c340a5878cacd5f187432a4d7828 100644
--- a/src/modules/mount/main.py
+++ b/src/modules/mount/main.py
@@ -191,6 +191,8 @@ def mount_partition(root_mount_point, partition, partitions):
         libcalamares.globalstorage.insert("btrfsSubvolumes", btrfs_subvolumes)
         # Create the subvolumes that are in the completed list
         for s in btrfs_subvolumes:
+            if not s["subvolume"]:
+                continue
             subprocess.check_call(["btrfs", "subvolume", "create",
                                    root_mount_point + s["subvolume"]])
             if s["mountPoint"] == "/":
diff --git a/src/modules/mount/mount.conf b/src/modules/mount/mount.conf
index 6168e97ccd13c465bf143fd728ec3bcf1f553d11..84dca05a76f2e7026a40190acdedd6c833e2117a 100644
--- a/src/modules/mount/mount.conf
+++ b/src/modules/mount/mount.conf
@@ -42,15 +42,24 @@ extraMountsEfi:
       mountPoint: /sys/firmware/efi/efivars
 
 # Btrfs subvolumes to create if root filesystem is on btrfs volume.
-# If mountpoint is mounted already to another partition, it is ignored.
+# If *mountpoint* is mounted already to another partition, it is ignored.
 # Separate subvolume for swapfile is handled separately and automatically.
+#
+# It is possible to prevent subvolume creation -- this is likely only relevant
+# for the root (/) subvolume -- by giving an empty string as a subvolume
+# name. In this case no subvolume will be created. When using snapper as
+# a rollback mechanism, it is recommended to **not** create a subvolume
+# for root.
 
 btrfsSubvolumes:
     - mountPoint: /
       subvolume: /@
+      # As an alternative:
+      #
+      # subvolume: ""
     - mountPoint: /home
       subvolume: /@home
     - mountPoint: /var/cache
       subvolume: /@cache
     - mountPoint: /var/log
-      subvolume: /@log
\ No newline at end of file
+      subvolume: /@log
diff --git a/src/modules/packages/main.py b/src/modules/packages/main.py
index 98faa9b637399f1531d31cba6570fadb64c1d0de..10371777e6276b18882d2765265ca9bc51f01105 100644
--- a/src/modules/packages/main.py
+++ b/src/modules/packages/main.py
@@ -379,6 +379,7 @@ class PMPacman(PackageManager):
     def __init__(self):
         import re
         progress_match = re.compile("^\\((\\d+)/(\\d+)\\)")
+
         def line_cb(line):
             if line.startswith(":: "):
                 self.in_package_changes = "package changes" in line
@@ -396,30 +397,79 @@ class PMPacman(PackageManager):
         self.in_package_changes = False
         self.line_cb = line_cb
 
+        pacman = libcalamares.job.configuration.get("pacman", None)
+        if pacman is None:
+            pacman = dict()
+        if type(pacman) is not dict:
+            libcalamares.utils.warning("Job configuration *pacman* will be ignored.")
+            pacman = dict()
+        self.pacman_num_retries = pacman.get("num_retries", 0)
+        self.pacman_disable_timeout = pacman.get("disable_download_timeout", False)
+        self.pacman_needed_only = pacman.get("needed_only", False)
+
     def reset_progress(self):
         self.in_package_changes = False
         # These are globals
         self.progress_fraction = (completed_packages * 1.0 / total_packages)
 
+    def run_pacman(self, command, callback=False):
+        """
+        Call pacman in a loop until it is successful or the number of retries is exceeded
+        :param command: The pacman command to run
+        :param callback: An optional boolean that indicates if this pacman run should use the callback
+        :return:
+        """
+
+        pacman_count = 0
+        while pacman_count <= self.pacman_num_retries:
+            pacman_count += 1
+            try:
+                if callback is True:
+                    libcalamares.utils.target_env_process_output(command, self.line_cb)
+                else:
+                    libcalamares.utils.target_env_process_output(command)
+
+                return
+            except subprocess.CalledProcessError:
+                if pacman_count <= self.pacman_num_retries:
+                    pass
+                else:
+                    raise
+
     def install(self, pkgs, from_local=False):
+        command = ["pacman"]
+
         if from_local:
-            pacman_flags = "-U"
+            command.append("-U")
         else:
-            pacman_flags = "-S"
+            command.append("-S")
+
+        command.append("--noconfirm")
+
+        if self.pacman_needed_only is True:
+            command.append("--needed")
+
+        if self.pacman_disable_timeout is True:
+            command.append("--disable-download-timeout")
+
+        command += pkgs
 
         self.reset_progress()
-        libcalamares.utils.target_env_process_output(["pacman", pacman_flags,
-                               "--noconfirm"] + pkgs, self.line_cb)
+        self.run_pacman(command, True)
 
     def remove(self, pkgs):
         self.reset_progress()
-        libcalamares.utils.target_env_process_output(["pacman", "-Rs", "--noconfirm"] + pkgs, self.line_cb)
+        self.run_pacman(["pacman", "-Rs", "--noconfirm"] + pkgs, True)
 
     def update_db(self):
-        check_target_env_call(["pacman", "-Sy"])
+        self.run_pacman(["pacman", "-Sy"])
 
     def update_system(self):
-        check_target_env_call(["pacman", "-Su", "--noconfirm"])
+        command = ["pacman", "-Su", "--noconfirm"]
+        if self.pacman_disable_timeout is True:
+            command.append("--disable-download-timeout")
+
+        self.run_pacman(command)
 
 
 class PMPamac(PackageManager):
diff --git a/src/modules/packages/packages.conf b/src/modules/packages/packages.conf
index 49fdbb6d6a2015ee471e83c5e3a39dd5a4584c5d..6e62f4b5f6cc815035e63a549000a0e8de087f94 100644
--- a/src/modules/packages/packages.conf
+++ b/src/modules/packages/packages.conf
@@ -62,6 +62,23 @@ skip_if_no_internet: false
 update_db: true
 update_system: false
 
+# pacman specific options
+#
+# *num_retries* should be a positive integer which specifies the
+# number of times the call to pacman will be retried in the event of a
+# failure.  If it is missing, it will be set to 0.
+#
+# *disable_download_timeout* is a boolean that, when true, includes
+# the flag --disable-download-timeout on calls to pacman.  When missing,
+# false is assumed.
+#
+# *needed_only* is a boolean that includes the pacman argument --needed
+# when set to true.  If missing, false is assumed.
+pacman:
+    num_retries: 0
+    disable_download_timeout: false
+    needed_only: false
+
 #
 # List of maps with package operations such as install or remove.
 # Distro developers can provide a list of packages to remove
diff --git a/src/modules/packages/packages.schema.yaml b/src/modules/packages/packages.schema.yaml
index 989bf11ddcff53000d77b0e4728c1d740b77a6db..d12f0507e660d88cc7227fd069199dbb2268be61 100644
--- a/src/modules/packages/packages.schema.yaml
+++ b/src/modules/packages/packages.schema.yaml
@@ -26,6 +26,14 @@ properties:
     update_system: { type: boolean, default: false }
     skip_if_no_internet: { type: boolean, default: false }
 
+    pacman:
+        additionalProperties: false
+        type: object
+        properties:
+            num_retries: { type: integer, default: 0 }
+            disable_download_timeout: { type: boolean, default: false }
+            needed_only: { type: boolean, default: false }
+
     operations:
         type: array
         items:
diff --git a/src/modules/packages/tests/1.global b/src/modules/packages/tests/1.global
new file mode 100644
index 0000000000000000000000000000000000000000..ee06ccfe1e7b3f04f046ac7d2bdc0baa09624a4b
--- /dev/null
+++ b/src/modules/packages/tests/1.global
@@ -0,0 +1,3 @@
+# SPDX-FileCopyrightText: no
+# SPDX-License-Identifier: CC0-1.0
+rootMountPoint: /tmp
diff --git a/src/modules/packages/test.yaml b/src/modules/packages/tests/2.job
similarity index 90%
rename from src/modules/packages/test.yaml
rename to src/modules/packages/tests/2.job
index 130214dfd52fdf95e634fcb2c25bbee85bd9ceb3..ba205ed442716f809eb3cc4a1b4e1cd249c1e350 100644
--- a/src/modules/packages/test.yaml
+++ b/src/modules/packages/tests/2.job
@@ -1,7 +1,6 @@
 # SPDX-FileCopyrightText: no
 # SPDX-License-Identifier: CC0-1.0
 backend: dummy
-rootMountPoint: /tmp/mount
 operations:
   - install:
     - pre-script: touch /tmp/foo
diff --git a/src/modules/packages/tests/CMakeTests.txt b/src/modules/packages/tests/CMakeTests.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4f7d6185f2a6ed5634a10e4adf212a2e80dc9774
--- /dev/null
+++ b/src/modules/packages/tests/CMakeTests.txt
@@ -0,0 +1,42 @@
+#   SPDX-FileCopyrightText: no
+#   SPDX-License-Identifier: CC0-1.0
+#
+# We have tests to load (some) of the package-managers specifically, to
+# test their configuration code and implementation. Those tests conventionally
+# live in Python files here in the tests/ directory. Add them.
+
+# Pacman (Arch) tests
+set(_pm pacman)
+add_test(
+    NAME configure-packages-${_pm}
+    COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-pm-${_pm}.py
+    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+)
+add_test(
+    NAME configure-packages-${_pm}-ops-1
+    COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-pm-${_pm}.py ${CMAKE_CURRENT_LIST_DIR}/pm-pacman-1.yaml 4 1 1
+    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+)
+add_test(
+    NAME configure-packages-${_pm}-ops-2
+    COMMAND env PYTHONPATH=.: python3 ${CMAKE_CURRENT_LIST_DIR}/test-pm-${_pm}.py ${CMAKE_CURRENT_LIST_DIR}/pm-pacman-2.yaml 3 0 0
+    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+)
+
+if ( BUILD_TESTING AND BUILD_SCHEMA_TESTING AND PYTHONINTERP_FOUND AND PYTHON_EXECUTABLE )
+    set( _module packages )
+    set( _schema_file "${CMAKE_CURRENT_SOURCE_DIR}/${_module}/${_module}.schema.yaml" )
+    message(STATUS "Schema ${_schema_file}")
+    foreach( _cf pm-pacman-1.yaml pm-pacman-2.yaml )
+        set( _conf_file "${CMAKE_CURRENT_SOURCE_DIR}/${_module}/tests/${_cf}" )
+        if ( EXISTS "${_schema_file}" AND EXISTS "${_conf_file}" )
+            add_test(
+                NAME validate-packages-${_cf}
+                COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_SOURCE_DIR}/ci/configvalidator.py" "${_schema_file}" "${_conf_file}"
+            )
+        else()
+            message(FATAL_ERROR "Missing ${_conf_file}")
+        endif()
+    endforeach()
+endif()
+
diff --git a/src/modules/packages/tests/pm-pacman-1.yaml b/src/modules/packages/tests/pm-pacman-1.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..aeb5b862517b9c2344c0f0ed65e163922213d9a4
--- /dev/null
+++ b/src/modules/packages/tests/pm-pacman-1.yaml
@@ -0,0 +1,10 @@
+# SPDX-FileCopyrightText: no
+# SPDX-License-Identifier: CC0-1.0
+backend: pacman
+operations: []
+
+pacman:
+    num_retries: 4
+    disable_download_timeout: true
+    needed_only: true
+
diff --git a/src/modules/packages/tests/pm-pacman-2.yaml b/src/modules/packages/tests/pm-pacman-2.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8b0bda39765cbc238a159b14bff467d278265aef
--- /dev/null
+++ b/src/modules/packages/tests/pm-pacman-2.yaml
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: no
+# SPDX-License-Identifier: CC0-1.0
+backend: pacman
+operations: []
+
+# Leave some things unspecified
+pacman:
+    num_retries: 3
+
diff --git a/src/modules/packages/tests/test-pm-pacman.py b/src/modules/packages/tests/test-pm-pacman.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee814b6203d712169698550acfec464fbacdf5e0
--- /dev/null
+++ b/src/modules/packages/tests/test-pm-pacman.py
@@ -0,0 +1,36 @@
+#   SPDX-FileCopyrightText: no
+#   SPDX-License-Identifier: CC0-1.0
+#
+# Calamares Boilerplate
+import libcalamares
+libcalamares.globalstorage = libcalamares.GlobalStorage(None)
+libcalamares.globalstorage.insert("testing", True)
+
+# Module prep-work
+from src.modules.packages import main
+
+# .. we don't have a job in this test, so fake one
+class Job(object):
+    def __init__(self, filename):
+        self.configuration = libcalamares.utils.load_yaml(filename) if filename is not None else dict()
+
+import sys
+if len(sys.argv) > 4:
+    filename = sys.argv[1]
+    retry = int(sys.argv[2])
+    timeout = bool(int(sys.argv[3]))
+    needed = bool(int(sys.argv[4]))
+else:
+    filename = None
+    retry = 0
+    timeout = False
+    needed = False
+
+libcalamares.utils.warning("Expecting {!s} retry={!s} timeout={!s} needed={!s}".format(filename, retry, timeout, needed))
+
+# Specific PM test
+libcalamares.job = Job(filename)
+p = main.PMPacman()
+assert p.pacman_num_retries == retry, "{!r} vs {!r}".format(p.pacman_num_retries, retry)
+assert p.pacman_disable_timeout == timeout, "{!r} vs {!r}".format(p.pacman_disable_timeout, timeout)
+assert p.pacman_needed_only == needed, "{!r} vs {!r}".format(p.pacman_needed_only, needed)
diff --git a/src/modules/zfs/README.md b/src/modules/zfs/README.md
index 9138a0598ec76e2802fed9e6ce45ea1846c0d465..992fa5cb3b49a4002106b2f643308a4cb4da31c5 100644
--- a/src/modules/zfs/README.md
+++ b/src/modules/zfs/README.md
@@ -6,7 +6,10 @@
 
 There are a few considerations to be aware of when enabling the zfs module
 * You must provide zfs kernel modules or kernel support on the ISO for the zfs module to function
+    * The zfs kernel module must be loaded prior to the partition module running
+    * One way to achieve this is by running `modprobe zfs`
 * Support for zfs in the partition module is conditional on the zfs module being enabled
+* The config for the default pools and datasets is configured and described in modules/zfs.conf
 * If you use grub with zfs, you must have `ZPOOL_VDEV_NAME_PATH=1` in your environment when running grub-install or grub-mkconfig.
    * Calamares will ensure this happens during the bootloader module.
    * It will also add it to `/etc/environment` so it will be available in the installation
diff --git a/src/modules/zfs/zfs.conf b/src/modules/zfs/zfs.conf
index f2f8f52b0ecf955bb35c6d1e36b6ae4b2f9cf328..e5a0aa3484b2e8c5d30454def02c94c0ac0cc9df 100644
--- a/src/modules/zfs/zfs.conf
+++ b/src/modules/zfs/zfs.conf
@@ -10,6 +10,9 @@
 poolName: zpcala
 
 # A list of options that will be passed to zpool create
+#
+# Encryption options should generally not be added here since they will be added by
+# selecting the encrypt disk option in the partition module
 poolOptions: "-f -o ashift=12 -O mountpoint=none -O acltype=posixacl -O relatime=on"
 
 # A list of options that will be passed to zfs create when creating each dataset
@@ -17,6 +20,10 @@ poolOptions: "-f -o ashift=12 -O mountpoint=none -O acltype=posixacl -O relatime
 datasetOptions: "-o compression=lz4 -o atime=off -o xattr=sa"
 
 # An array of datasets that will be created on the zpool mounted at /
+#
+# This default configuration is commonly used when support for booting more than one distro
+# out of a single zpool is desired.  If you decide to keep this default configuration,
+# you should replace "distro" with an identifier that represents your distro.
 datasets:
     - dsName: ROOT
       mountpoint: none