From 46aa051751fd215db5df011362a5aba43e554835 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Mon, 15 Jun 2026 16:38:21 -0700 Subject: [PATCH] Allow opt-out of discard on disk format Add a ``discard`` parameter (default ``True``) to ``disk.format_`` so callers can disable block discard during filesystem creation. Without this, mkfs discards all blocks in a disk image, collapsing a 20 GB pre-allocated image to ~44 MB on the host filesystem and causing ENOSPC errors when the host runs low on space. For ext filesystems the flag maps to ``-E nodiscard``; for xfs it maps to ``-K``. The ``blockdev.formatted`` state already forwards ``**kwargs`` to ``disk.format_``, so passing ``discard=False`` in state works without any state-module change. Fixes #64310 --- changelog/64310.added.md | 1 + salt/modules/disk.py | 15 +++++++++++++ tests/pytests/unit/modules/test_disk.py | 28 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 changelog/64310.added.md diff --git a/changelog/64310.added.md b/changelog/64310.added.md new file mode 100644 index 000000000000..d81c037c9348 --- /dev/null +++ b/changelog/64310.added.md @@ -0,0 +1 @@ +Added ``discard`` parameter to ``disk.format_`` and ``blockdev.formatted`` to allow opting out of block discard during filesystem creation, preventing sparse inflation of disk images on host filesystems. diff --git a/salt/modules/disk.py b/salt/modules/disk.py index 4725e09c65e3..74cd66066382 100644 --- a/salt/modules/disk.py +++ b/salt/modules/disk.py @@ -481,6 +481,7 @@ def format_( lazy_itable_init=None, fat=None, force=False, + discard=True, ): """ Format a filesystem onto a device @@ -519,6 +520,15 @@ def format_( This option is dangerous, use it with caution. + discard + Attempt to discard blocks at mkfs time (enabled by default). Set to + ``False`` to disable block discard, which prevents sparse file + inflation when formatting disk images on a host filesystem. + + This option is only supported for ext and xfs filesystems. + + .. versionadded:: 3009.0 + CLI Example: .. code-block:: bash @@ -534,6 +544,11 @@ def format_( if lazy_itable_init is not None: if fs_type[:3] == "ext": cmd.extend(["-E", f"lazy_itable_init={lazy_itable_init}"]) + if not discard: + if fs_type[:3] == "ext": + cmd.extend(["-E", "nodiscard"]) + elif fs_type == "xfs": + cmd.append("-K") if fat is not None and fat in (12, 16, 32): if fs_type[-3:] == "fat": cmd.extend(["-F", fat]) diff --git a/tests/pytests/unit/modules/test_disk.py b/tests/pytests/unit/modules/test_disk.py index b7a73226ac99..162832e600b4 100644 --- a/tests/pytests/unit/modules/test_disk.py +++ b/tests/pytests/unit/modules/test_disk.py @@ -382,6 +382,34 @@ def test_format_(): mock.assert_any_call(["mkfs", "-t", "ext4", device], ignore_retcode=True) +@pytest.mark.skip_on_windows(reason="Skip on Windows") +@pytest.mark.skip_if_binaries_missing("mkfs") +def test_format__nodiscard_ext(): + """ + unit tests for disk.format_ with discard=False on an ext filesystem + """ + device = "/dev/sdX1" + mock = MagicMock(return_value=0) + with patch.dict(disk.__salt__, {"cmd.retcode": mock}): + disk.format_(device=device, fs_type="ext4", discard=False) + mock.assert_any_call( + ["mkfs", "-t", "ext4", "-E", "nodiscard", device], ignore_retcode=True + ) + + +@pytest.mark.skip_on_windows(reason="Skip on Windows") +@pytest.mark.skip_if_binaries_missing("mkfs") +def test_format__nodiscard_xfs(): + """ + unit tests for disk.format_ with discard=False on an xfs filesystem + """ + device = "/dev/sdX1" + mock = MagicMock(return_value=0) + with patch.dict(disk.__salt__, {"cmd.retcode": mock}): + disk.format_(device=device, fs_type="xfs", discard=False) + mock.assert_any_call(["mkfs", "-t", "xfs", "-K", device], ignore_retcode=True) + + @pytest.mark.skip_on_windows(reason="Skip on Windows") @pytest.mark.skip_if_binaries_missing("mkfs") def test_format__fat():