From 7af2f1a5c131d08e3d7549f35cc7e59ede04c8b1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 25 Sep 2023 14:51:58 +0200 Subject: [PATCH] Create additional option to setup FS geometry Some platforms like the TI AM62 require a particular FAT geometry for their CPU to read the file system (and thus the bootloader). Failing that the CPU will simply not boot and keep looking for a bootloader. Let's add some options to enforce a particular filesystem geometry through the -g option of mkfs.fat. Signed-off-by: Maxime Ripard --- stages/org.osbuild.mkfs.fat | 20 +++++++++++ test/data/stages/fat/manifest.json | 41 ++++++++++++++++++++++ test/data/stages/fat/manifest.mpp.yaml | 20 +++++++++++ test/run/test_stages.py | 48 +++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 test/data/stages/fat/manifest.json create mode 100644 test/data/stages/fat/manifest.mpp.yaml diff --git a/stages/org.osbuild.mkfs.fat b/stages/org.osbuild.mkfs.fat index adab6084..d87417f9 100755 --- a/stages/org.osbuild.mkfs.fat +++ b/stages/org.osbuild.mkfs.fat @@ -46,6 +46,22 @@ SCHEMA_2 = r""" "description": "Label for the file system", "type": "integer", "enum": [12, 16, 32] + }, + "geometry": { + "description": "Disk Geometry for the file system", + "type": "object", + "additionalProperties": false, + "required": ["heads", "sectors-per-track"], + "properties": { + "heads": { + "description": "Number of Heads", + "type": "integer" + }, + "sectors-per-track": { + "description": "Number of Sectors per Track", + "type": "integer" + } + } } } } @@ -58,6 +74,7 @@ def main(devices, options): volid = options["volid"] label = options.get("label") fatsize = options.get("fat-size") + geometry = options.get("geometry") opts = ["mkfs.fat", "-I", "-i", volid] if label: @@ -66,6 +83,9 @@ def main(devices, options): if fatsize: opts += ["-F", str(fatsize)] + if geometry: + opts += ["-g", f"{geometry['heads']}/{geometry['sectors-per-track']}"] + subprocess.run(opts + [device], encoding='utf8', check=True) diff --git a/test/data/stages/fat/manifest.json b/test/data/stages/fat/manifest.json new file mode 100644 index 00000000..0a7c128d --- /dev/null +++ b/test/data/stages/fat/manifest.json @@ -0,0 +1,41 @@ +{ + "version": "2", + "pipelines": [ + { + "name": "image", + "stages": [ + { + "type": "org.osbuild.truncate", + "options": { + "filename": "disk.img", + "size": "64M" + } + }, + { + "type": "org.osbuild.mkfs.fat", + "options": { + "volid": "7B7795E7", + "geometry": { + "heads": 12, + "sectors-per-track": 42 + } + }, + "devices": { + "device": { + "type": "org.osbuild.loopback", + "options": { + "filename": "disk.img", + "lock": true + } + } + } + } + ] + } + ], + "sources": { + "org.osbuild.curl": { + "items": {} + } + } +} diff --git a/test/data/stages/fat/manifest.mpp.yaml b/test/data/stages/fat/manifest.mpp.yaml new file mode 100644 index 00000000..c281a34e --- /dev/null +++ b/test/data/stages/fat/manifest.mpp.yaml @@ -0,0 +1,20 @@ +version: '2' +pipelines: + - name: image + stages: + - type: org.osbuild.truncate + options: + filename: disk.img + size: '64M' + - type: org.osbuild.mkfs.fat + options: + volid: 7B7795E7 + geometry: + heads: 12 + sectors-per-track: 42 + devices: + device: + type: org.osbuild.loopback + options: + filename: disk.img + lock: true diff --git a/test/run/test_stages.py b/test/run/test_stages.py index 5c911e83..93152d4b 100644 --- a/test/run/test_stages.py +++ b/test/run/test_stages.py @@ -16,7 +16,7 @@ import tempfile import unittest import xml from collections.abc import Mapping -from typing import Callable, Dict, Optional +from typing import Callable, Dict, List, Optional from osbuild.util import checksum, selinux from .test_assemblers import mount @@ -595,3 +595,49 @@ class TestStages(test.TestBase): assert len(subvols) == 2 assert "path root" in subvols[0] assert "path home" in subvols[1] + + @unittest.skipUnless(test.TestBase.has_filesystem_support("fat"), "FAT needed") + def test_fat(self): + def _get_file_fields(image: str) -> List[str]: + r = subprocess.run( + [ + "file", + "--special-files", + image + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf8", + check=True, + ) + + return [field.strip() for field in r.stdout.split(',')] + + datadir = self.locate_test_data() + testdir = os.path.join(datadir, "stages", "fat") + + with self.osbuild as osb, tempfile.TemporaryDirectory(dir="/var/tmp") as outdir: + osb.compile_file(os.path.join(testdir, "manifest.json"), exports=["image"], output_dir=outdir) + + image = os.path.join(outdir, "image", "disk.img") + assert os.path.isfile(image) + + # check that it's indeed FAT + r = subprocess.run( + [ + "blkid", + "--output", "export", + image + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="utf8", + check=True, + ) + + assert "TYPE=vfat" in r.stdout.splitlines() + + # Check that our custom geometry was enforced + fields = _get_file_fields(image) + assert "heads 12" in fields + assert "sectors/track 42" in fields