From 6e7b0ad879fb06973476104204e6efe2085678e5 Mon Sep 17 00:00:00 2001 From: Patryk Kubiak Date: Wed, 30 Sep 2026 13:13:47 +0200 Subject: [PATCH 1/2] Add the automount service; FAT and exFAT are mounted by dmfatfs dmblkid now reports vfat and exfat with module "dmfatfs" and as mountable, now that dmfatfs mounts them straight from a node. services/automount: one instance per block node dmdevfs reports ([class=block] start=automount@%name). It probes the node with dmblkid and mounts a mountable file system at /mnt/, or at /mnt/ when the volume has no label or that path is taken. Characters that do not belong in a path or a shell word become '_'. A partition table, unknown contents or dmffs leave it with nothing to do (exit 0); a mounted instance waits for its stop (node removed, or "service stop") and unmounts. Picking the path and mounting happen under a lock shared by all instances; only /mnt itself is created, as dmvfs needs no directory under a mount point. The mount logic (automount.c) is tested on the host with a fake tree and images probed by the real dmblkid (test_automount). CI: clone dmod for dmod_loader into a fresh directory - the dmod:1.0.4 image already has a /tmp/dmod, which made the step fail on main - and run test_automount. Tested on STM32F746G-DISCO: the card's partition was mounted at boot, the whole-medium instance exited, "service stop" unmounted it (the node could be mounted by hand again), "service start" mounted it again. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 17 +- CMakeLists.txt | 3 + README.md | 18 +- docs/api-reference.md | 12 +- include/dmblkid.h | 4 +- manifest.dmm | 4 + services/automount/CMakeLists.txt | 26 ++ services/automount/README.md | 92 +++++++ services/automount/automount.dmr | 25 ++ services/automount/configs/automount.rules | 11 + services/automount/configs/automount@.ini | 20 ++ services/automount/src/automount.c | 226 ++++++++++++++++++ services/automount/src/automount.h | 85 +++++++ services/automount/src/main.c | 140 +++++++++++ services/blkid/README.md | 2 +- services/blkid/src/main.c | 2 +- src/dmblkid_exfat.c | 2 +- src/dmblkid_fat.c | 2 +- tests/CMakeLists.txt | 43 +++- tests/automount_test.c | 265 +++++++++++++++++++++ tests/dmblkid_test.c | 14 +- 21 files changed, 978 insertions(+), 35 deletions(-) create mode 100644 services/automount/CMakeLists.txt create mode 100644 services/automount/README.md create mode 100644 services/automount/automount.dmr create mode 100644 services/automount/configs/automount.rules create mode 100644 services/automount/configs/automount@.ini create mode 100644 services/automount/src/automount.c create mode 100644 services/automount/src/automount.h create mode 100644 services/automount/src/main.c create mode 100644 tests/automount_test.c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45a92df..16ec90b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,19 +36,23 @@ jobs: test -f build/dmf/blkid.dmf test -f build/dmf/dmpart.dmf test -f build/dmf/test_dmpart.dmf + test -f build/dmf/automount.dmf + test -f build/dmf/test_automount.dmf echo "Module files present" # dmblkid uses the 64-bit file API (Dmod_FileSeek/_FileTell/_FileSize # 2.0, dmod#312). The dmod_loader in the 1.0.4 image predates it, so # the tests run in one built from the same dmod develop the module - # is built against. + # is built against. Cloned into a fresh directory: the image already + # has a /tmp/dmod of its own. - name: Build dmod_loader from dmod develop run: | - git clone --depth 1 -b develop https://github.com/choco-technologies/dmod.git /tmp/dmod - cmake -S /tmp/dmod -B /tmp/dmod/build -DDMOD_MODE=DMOD_SYSTEM \ + DMOD_SRC=$(mktemp -d) + git clone --depth 1 -b develop https://github.com/choco-technologies/dmod.git "$DMOD_SRC" + cmake -S "$DMOD_SRC" -B "$DMOD_SRC/build" -DDMOD_MODE=DMOD_SYSTEM \ -DDMOD_BUILD_EXAMPLES=ON -DDMOD_BUILD_TESTS=OFF - cmake --build /tmp/dmod/build --target dmod_loader - echo "DMOD_LOADER=/tmp/dmod/build/examples/system/dmod_loader/dmod_loader" >> $GITHUB_ENV + cmake --build "$DMOD_SRC/build" --target dmod_loader + echo "DMOD_LOADER=$DMOD_SRC/build/examples/system/dmod_loader/dmod_loader" >> $GITHUB_ENV - name: Run tests with dmod_loader run: | @@ -57,6 +61,8 @@ jobs: ${DMOD_LOADER} build/dmf/test_dmpart.dmf dmf-get install -d ${DMOD_DMF_DIR}/test_dmblkid-local.dmd -y ${DMOD_LOADER} build/dmf/test_dmblkid.dmf + dmf-get install -d ${DMOD_DMF_DIR}/test_automount-local.dmd -y + ${DMOD_LOADER} build/dmf/test_automount.dmf build-targets: name: Build for ${{ matrix.arch }} @@ -87,3 +93,4 @@ jobs: test -f dmf/dmblkid.dmf test -f dmf/blkid.dmf test -f dmf/dmpart.dmf + test -f dmf/automount.dmf diff --git a/CMakeLists.txt b/CMakeLists.txt index 855475b..fb2c958 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,5 +91,8 @@ add_subdirectory(libs/dmpart) # blkid - command line front end add_subdirectory(services/blkid) +# automount - mounts block nodes as dmdevfs reports them +add_subdirectory(services/automount) + enable_testing() add_subdirectory(tests) diff --git a/README.md b/README.md index 4380a81..042f9d7 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,10 @@ prints it. | blank, damaged or unsupported | `unknown` | - | size | Every type is mapped to the DMOD filesystem module that handles it and -whether that module can mount the node. Today none can: FAT and exFAT have -no module yet, and dmffs mounts memory-mapped flash rather than a node - so -they are reported, but marked not mountable. +whether that module can mount the node: FAT and exFAT are mounted by +[dmfatfs](https://github.com/choco-technologies/dmfatfs) from the node +itself; dmffs is reported, but marked not mountable - it mounts +memory-mapped flash rather than a node. Detection is strict, so that nothing is mounted by mistake: @@ -72,11 +73,18 @@ The command line front end: ``` > blkid /dev/dmsdio0/0 /dev/dmsdio0/0p1 /dev/dmsdio0/0: PTTYPE="mbr" PTUUID="1a2b3c4d" PARTITIONS="1" BLOCK_SIZE="512" SIZE="15931539456" -/dev/dmsdio0/0p1: TYPE="vfat" VERSION="FAT32" LABEL="SDCARD" UUID="CAFE-BABE" SIZE="15930490880" MOUNTABLE="no" +/dev/dmsdio0/0p1: TYPE="vfat" VERSION="FAT32" LABEL="SDCARD" UUID="CAFE-BABE" SIZE="15930490880" MODULE="dmfatfs" MOUNTABLE="yes" ``` See [services/blkid/README.md](services/blkid/README.md). +### Automount + +The `automount` service mounts every block node dmdevfs reports that holds +a mountable file system, at `/mnt/` (or `/mnt/` +when there is no label or it is taken), and unmounts it when the node goes +away. See [services/automount/README.md](services/automount/README.md). + ## API | Function | Description | @@ -174,6 +182,7 @@ dmblkid/ ├── libs/ │ └── dmpart/ # dmpart - MBR/GPT parser library (also used by dmdevfs) ├── services/ +│ ├── automount/ # automount service (mounts block nodes under /mnt) │ └── blkid/ # blkid command line application ├── src/ │ ├── dmblkid.c # dmblkid_probe(), result accessors @@ -187,6 +196,7 @@ dmblkid/ ├── tests/ │ ├── CMakeLists.txt │ ├── dmblkid_test.c +│ ├── automount_test.c # automount service logic │ └── test_images.c # Image builders ├── CMakeLists.txt ├── Makefile diff --git a/docs/api-reference.md b/docs/api-reference.md index 6b70f95..55b22f1 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -123,9 +123,9 @@ const char* dmblkid_get_uuid(const dmblkid_t* result); const char* dmblkid_get_module(const dmblkid_t* result); ``` -The DMOD filesystem module that handles the type: `"dmffs"` for dmffs. -`NULL` for types the ecosystem has no module for yet (FAT, exFAT), for -partition tables and for unknown contents. +The DMOD filesystem module that handles the type: `"dmfatfs"` for FAT and +exFAT, `"dmffs"` for dmffs. `NULL` for partition tables and for unknown +contents. ### `dmblkid_is_mountable` @@ -133,9 +133,9 @@ partition tables and for unknown contents. bool dmblkid_is_mountable(const dmblkid_t* result); ``` -`true` only if `dmblkid_get_module()` can mount the node itself. `false` for -dmffs: its module mounts memory-mapped flash (`flash_addr`/`flash_size`), not -a node. +`true` only if `dmblkid_get_module()` can mount the node itself - FAT and +exFAT (`dmvfs_mount_fs("dmfatfs", , )`). `false` for dmffs: its +module mounts memory-mapped flash (`flash_addr`/`flash_size`), not a node. ### `dmblkid_get_size` diff --git a/include/dmblkid.h b/include/dmblkid.h index 4a46a23..2de2f94 100644 --- a/include/dmblkid.h +++ b/include/dmblkid.h @@ -76,8 +76,8 @@ dmod_dmblkid_api(1.0, const char*, _get_label, ( const dmblkid_t* result )); dmod_dmblkid_api(1.0, const char*, _get_uuid, ( const dmblkid_t* result )); /** - * @return Name of the DMOD filesystem module that handles this format, or - * NULL if the ecosystem has none (FAT, exFAT today). + * @return Name of the DMOD filesystem module that handles this format + * ("dmfatfs" for FAT/exFAT, "dmffs"), or NULL if there is none. */ dmod_dmblkid_api(1.0, const char*, _get_module, ( const dmblkid_t* result )); diff --git a/manifest.dmm b/manifest.dmm index 44e55c6..d6aafda 100644 --- a/manifest.dmm +++ b/manifest.dmm @@ -8,6 +8,10 @@ dmblkid https://github.com/choco-technologies/dmblkid/releases/download/v/blkid-v-.zip +# automount - mount service for block nodes (services/automount), built and +# released together with dmblkid +automount https://github.com/choco-technologies/dmblkid/releases/download/v/automount-v-.zip + # dmpart - partition table parser library (libs/dmpart), used by dmblkid and # dmdevfs; built and released together with dmblkid dmpart https://github.com/choco-technologies/dmblkid/releases/download/v/dmpart-v-.zip diff --git a/services/automount/CMakeLists.txt b/services/automount/CMakeLists.txt new file mode 100644 index 0000000..ae96233 --- /dev/null +++ b/services/automount/CMakeLists.txt @@ -0,0 +1,26 @@ +# ===================================================================== +# automount - mount service for block nodes +# ===================================================================== +# Mounts every block node dmdevfs reports that holds a mountable file +# system, started per node by libsystemd - see README.md. +set(DMOD_MODULE_NAME automount) +set(DMOD_AUTHOR_NAME "Patryk Kubiak") +set(DMOD_STACK_SIZE 2048) +set(DMOD_PRIORITY 0) +set(DMOD_DMR_PATH ${CMAKE_CURRENT_SOURCE_DIR}/automount.dmr) + +dmod_add_executable(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION} + src/main.c + src/automount.c +) + +target_include_directories(${DMOD_MODULE_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) + +target_link_libraries(${DMOD_MODULE_NAME} dmblkid_if) + +dmod_link_modules(${DMOD_MODULE_NAME} + dmosi + libsystemd +) diff --git a/services/automount/README.md b/services/automount/README.md new file mode 100644 index 0000000..12a90d5 --- /dev/null +++ b/services/automount/README.md @@ -0,0 +1,92 @@ +# automount + +Mount service for block nodes: one instance per node dmdevfs reports as a +block device (a whole medium or one of its partitions). If the node holds a +file system that a DMOD module can mount from a node - FAT and exFAT through +[dmfatfs](https://github.com/choco-technologies/dmfatfs) today - it is +mounted under `/mnt` for as long as the node exists. + +``` +> ls /mnt +bootfs dmsdio0_0p2 +> ls /mnt/bootfs +config.txt cmdline.txt overlays ... +``` + +## How it runs + +1. dmdevfs reports every block node to libsystemd as class `block`, e.g. + `("block", "dmsdio0_0p1", "/dev/dmsdio0/0p1")`. +2. The `[class=block]` rule in [`configs/automount.rules`](configs/automount.rules) + starts `automount@dmsdio0_0p1` from [`configs/automount@.ini`](configs/automount@.ini) + with the node path and the node name as arguments. +3. automount probes the node with dmblkid: + - **mountable file system** - mounted with the module dmblkid names + (`dmblkid_get_module()`) at `/mnt/