Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,57 @@ jobs:
test -f build/dmf/dmblkid.dmf
test -f build/dmf/dmblkid_version.txt
test -f build/dmf/test_dmblkid.dmf
test -f build/dmf/blkid.dmf
test -f build/dmf/dmpart.dmf
test -f build/dmf/test_dmpart.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.
- 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 \
-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

- name: Run tests with dmod_loader
run: |
export DMOD_DMF_DIR=$(pwd)/build/dmf
dmf-get install -d ${DMOD_DMF_DIR}/test_dmpart-local.dmd -y
${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
${DMOD_LOADER} build/dmf/test_dmblkid.dmf

build-targets:
name: Build for ${{ matrix.arch }}
runs-on: ubuntu-latest
permissions:
contents: read
container:
image: chocotechnologies/dmod:1.0.4
strategy:
matrix:
arch: [ armv7/cortex-m4, armv7/cortex-m7 ]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Build dmblkid, dmpart and blkid
run: |
mkdir -p build
cd build
cmake .. -DDMOD_TOOLS_NAME=arch/${{ matrix.arch }}
cmake --build . > build.log 2>&1 || { cat build.log; exit 1; }
cat build.log
if grep -E "warning:|error:" build.log | grep -v "build-id"; then
echo "Compiler warnings found"
exit 1
fi
test -f dmf/dmblkid.dmf
test -f dmf/blkid.dmf
test -f dmf/dmpart.dmf
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ build/
.idea/

# Temporary files
dmblkid_test.img
*.tmp
*.swp
*~
27 changes: 21 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ set(DMOD_BUILD_TEMPLATES OFF CACHE BOOL "Build templates")
FetchContent_MakeAvailable(dmod)

project(dmblkid VERSION ${DMOD_MODULE_VERSION}
DESCRIPTION "dmblkid DMOD library module"
DESCRIPTION "Block device content (filesystem / partition table) probing"
LANGUAGES C CXX)

set(DMOD_DIR ${dmod_SOURCE_DIR} CACHE PATH "DMOD source directory")
Expand Down Expand Up @@ -60,8 +60,13 @@ set(DMOD_DIF_IMPLS )
# (for example, to link libraries)
#
dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION}
# List of source files - can include C and C++ files
src/dmblkid.c
src/dmblkid_source.c
src/dmblkid_util.c
src/dmblkid_ptable.c
src/dmblkid_fat.c
src/dmblkid_exfat.c
src/dmblkid_dmffs.c
)

target_include_directories(${DMOD_MODULE_NAME} PRIVATE
Expand All @@ -72,9 +77,19 @@ target_include_directories(${DMOD_MODULE_NAME}_if INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)

# To depend on another module's headers, use:
# dmod_link_modules(${DMOD_MODULE_NAME}
# other_module@1.0
# )
# dmdrvi: DMDRVI_IOCTL_BLOCK_GET_INFO, the geometry of dmdevfs block nodes
dmod_link_modules(${DMOD_MODULE_NAME}
dmdrvi@>=2.0
)

# dmpart - the partition table parser, a separate library module of this
# repository (dmdevfs uses it too). Local sibling: its headers come from its
# _if target rather than a dmod_link_modules() fetch.
target_link_libraries(${DMOD_MODULE_NAME} dmpart_if)
add_subdirectory(libs/dmpart)

# blkid - command line front end
add_subdirectory(services/blkid)

enable_testing()
add_subdirectory(tests)
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# #############################################################################
#
# This is an example of a simple library module.
#
# dmblkid - block device content probing library.
#
# #############################################################################
DMOD_DIR=@DMOD_DIR@
Expand All @@ -24,13 +24,14 @@ DMOD_MODULE_VERSION=0.1
DMOD_AUTHOR_NAME=Patryk Kubiak

# The list of C sources
DMOD_CSOURCES=src/dmblkid.c
DMOD_CSOURCES=src/dmblkid.c src/dmblkid_source.c src/dmblkid_util.c src/dmblkid_ptable.c \
src/dmblkid_fat.c src/dmblkid_exfat.c src/dmblkid_dmffs.c

# The list of C++ sources
DMOD_CXXSOURCES=

# The list of include directories
DMOD_INC_DIRS=include
DMOD_INC_DIRS=include libs/dmpart/include

# The list of libraries to link
DMOD_LIBS=
Expand Down
175 changes: 137 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,110 @@
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![CI](https://github.com/choco-technologies/dmblkid/actions/workflows/ci.yml/badge.svg)](https://github.com/choco-technologies/dmblkid/actions/workflows/ci.yml)

dmblkid DMOD library module.
Identifies what a block device or partition contains - the DMOD counterpart
of `libblkid`. Automount uses it to pick the filesystem module for a node
(and to leave whole disks with partitions alone); the `blkid` application
prints it.

## Description

TODO: describe what this module does.
`dmblkid_probe()` reads a node such as `/dev/dmsdio0/0` or
`/dev/dmsdio0/0p1` (or an image file) through the regular file API with
64-bit offsets and reports one of:

| Found | `usage` | `type` | Also reported |
|-------|---------|--------|---------------|
| MBR (incl. extended/logical partitions) | `partition_table` | `mbr` | disk signature, partition count, block size |
| GPT (primary or backup header) | `partition_table` | `gpt` | disk GUID, partition count, block size |
| FAT12/16/32 | `filesystem` | `vfat` | `FAT12`/`FAT16`/`FAT32`, label, serial |
| exFAT | `filesystem` | `exfat` | revision, label, serial |
| dmffs image | `filesystem` | `dmffs` | VERSION value |
| 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.

Detection is strict, so that nothing is mounted by mistake:

- **FAT** - the whole BPB must be consistent, not only the `0x55AA` marker:
jump instruction, sector and cluster sizes, reserved sectors, FAT count,
media byte, regions inside the volume, FATs large enough for every cluster,
volume inside the node. The FAT type comes from the cluster count, as in
the Microsoft specification.
- **exFAT** - boot sector fields, region layout and the boot region checksum.
- **dmffs** - no magic number: the top-level TLV chain must be well formed.
- **MBR/GPT** - [dmpart](libs/dmpart/README.md), the partition table
parser library of this repository, which dmdevfs uses to create partition
nodes too: a disk dmdevfs splits into partitions is always reported as a
partition table. A FAT/exFAT boot sector (a partitionless "superfloppy") is never
taken for an MBR, even if its boot code looks like partition entries. GPT
headers and entry arrays are CRC-checked; the backup header is used if the
primary one is damaged.

Probing never writes: the node is opened read-only. The block size comes from
`DMDRVI_IOCTL_BLOCK_GET_INFO` when the node answers it (dmdevfs block and
partition nodes); otherwise it is 512, or the size at which a GPT header is
found (1024/2048/4096). A node without a size (not a block device) is
reported as unknown without being read.

## Usage

```c
#include "dmblkid.h"

dmblkid_t* result = NULL;
if (dmblkid_probe("/dev/dmsdio0/0p1", &result) == 0)
{
if (dmblkid_get_usage(result) == dmblkid_usage_filesystem && dmblkid_is_mountable(result))
{
mount(dmblkid_get_module(result), ...);
}
dmblkid_destroy(result);
}
```

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"
```

See [services/blkid/README.md](services/blkid/README.md).

## API

| Function | Description |
|----------|-------------|
| `dmblkid_probe()` | Identify the contents of a node; returns a result to query. |
| `dmblkid_destroy()` | Release a result. |
| `dmblkid_get_usage()` | Filesystem, partition table or unknown. |
| `dmblkid_get_type()` | `vfat`, `exfat`, `dmffs`, `mbr`, `gpt`. |
| `dmblkid_get_version()` | `FAT32`, exFAT revision, dmffs VERSION. |
| `dmblkid_get_label()` | Volume label. |
| `dmblkid_get_uuid()` | Serial, MBR disk signature or GPT disk GUID. |
| `dmblkid_get_module()` | DMOD filesystem module for the type. |
| `dmblkid_is_mountable()` | Whether that module can mount the node. |
| `dmblkid_get_size()` | Node size in bytes. |
| `dmblkid_get_block_size()` | Block size the partition table was read with. |
| `dmblkid_get_partition_count()` | Number of partitions in the table. |

The MBR/GPT parser itself is the separate [dmpart](libs/dmpart/README.md)
library module (`dmpart_scan()`), built and released with dmblkid.

See [include/dmblkid.h](include/dmblkid.h) and
[docs/api-reference.md](docs/api-reference.md).

## Requirements

dmblkid uses the 64-bit file API (`Dmod_FileSeek`/`Dmod_FileTell`/
`Dmod_FileSize` 2.0, dmod#312), so the system it runs in - the firmware or
`dmod_loader` - must be built from a dmod with those APIs. The
`dmod_loader` in the `chocotechnologies/dmod:1.0.4` image predates them; CI
builds one from dmod `develop` (see below).

## Building

Expand All @@ -21,7 +120,8 @@ cmake --build .
```

Pass `-DDMOD_DIR=/path/to/local/dmod` to build against a local dmod checkout
instead of fetching `develop` from GitHub.
instead of fetching `develop` from GitHub, or
`-DDMOD_TOOLS_NAME=arch/armv7/cortex-m7` to build for a target.

### Using Make

Expand All @@ -31,44 +131,30 @@ make DMOD_MODE=DMOD_MODULE DMOD_DIR=/path/to/dmod

## Testing

Tests are built automatically alongside the module (see `tests/`). Once built,
run them with `ctest`:
The tests build disk images (sparse files, up to 6 GiB) and probe them:
FAT12/16/32, exFAT with its label above 4 GiB, a damaged exFAT boot region,
dmffs, an MBR with a logical partition, GPT with 512 and 4096-byte blocks,
GPT with only the backup header valid at the end of a 6 GiB disk, a blank
device, a file too small for any format, damaged FAT boot sectors, and that
probing leaves the image unchanged.

```bash
cd build
ctest --output-on-failure
```

`ctest` installs the test module's dependencies with `dmf-get` and then runs
it through `dmod_loader`. To run it manually instead:
They need a `dmod_loader` with the 64-bit file API:

```bash
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 -DDMOD_BUILD_EXAMPLES=ON -DDMOD_BUILD_TESTS=OFF
cmake --build /tmp/dmod/build --target dmod_loader

export DMOD_DMF_DIR=$(pwd)/build/dmf
dmf-get install -d ${DMOD_DMF_DIR}/test_dmblkid-local.dmd -y
dmod_loader build/dmf/test_dmblkid.dmf
```

## Usage

<TBD>

This library module provides functions that can be used by other modules:
/tmp/dmod/build/examples/system/dmod_loader/dmod_loader build/dmf/test_dmblkid.dmf

```c
#include "dmblkid.h"
dmf-get install -d ${DMOD_DMF_DIR}/test_dmpart-local.dmd -y
/tmp/dmod/build/examples/system/dmod_loader/dmod_loader build/dmf/test_dmpart.dmf
```

## API

| Function | Description |
|----------|-------------|
| `dmblkid_create()` | Create a new `dmblkid_t` instance. |
| `dmblkid_destroy()` | Destroy an instance created by `_create()`. |
| `dmblkid_is_valid()` | Check whether a handle is a valid instance. |

See [include/dmblkid.h](include/dmblkid.h) for the full
declarations and [docs/api-reference.md](docs/api-reference.md) for the
complete reference.
`ctest` runs the same; point it to that loader with
`-DDMOD_LOADER_EXECUTABLE=/tmp/dmod/build/examples/system/dmod_loader/dmod_loader`.

## Documentation

Expand All @@ -77,18 +163,31 @@ See the `docs/` directory:
- **[api-reference.md](docs/api-reference.md)** - Complete API documentation

View documentation using `dmf-man dmblkid`.

## Project Structure

```
dmblkid/
├── docs/ # Documentation (markdown format)
├── include/ # Public headers
│ └── dmblkid.h
├── docs/ # Documentation (markdown format)
├── include/
│ └── dmblkid.h # Public API
├── libs/
│ └── dmpart/ # dmpart - MBR/GPT parser library (also used by dmdevfs)
├── services/
│ └── blkid/ # blkid command line application
├── src/
│ └── dmblkid.c
│ ├── dmblkid.c # dmblkid_probe(), result accessors
│ ├── dmblkid_internal.h
│ ├── dmblkid_source.c # Node access (file API, geometry)
│ ├── dmblkid_util.c # Result/string helpers
│ ├── dmblkid_ptable.c # Partition tables (through dmpart)
│ ├── dmblkid_fat.c # FAT12/16/32
│ ├── dmblkid_exfat.c # exFAT
│ └── dmblkid_dmffs.c # dmffs
├── tests/
│ ├── CMakeLists.txt
│ └── dmblkid_test.c
│ ├── dmblkid_test.c
│ └── test_images.c # Image builders
├── CMakeLists.txt
├── Makefile
├── dmblkid.dmr
Expand Down
13 changes: 12 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# dmblkid Documentation

Welcome to the dmblkid module documentation.
dmblkid identifies what a block device or partition contains: a partition
table (MBR/GPT) or a filesystem (FAT12/16/32, exFAT, dmffs), with its label,
uuid and the DMOD filesystem module that mounts it.

## Contents

Expand All @@ -10,8 +12,17 @@ Welcome to the dmblkid module documentation.

```c
#include "dmblkid.h"

dmblkid_t* result = NULL;
if (dmblkid_probe("/dev/dmsdio0/0p1", &result) == 0)
{
const char* type = dmblkid_get_type(result); /* "vfat", "exfat", "dmffs", "mbr", "gpt" or NULL */
dmblkid_destroy(result);
}
```

From the shell: `blkid <node>...` (the `blkid` application module).

View documentation using `dmf-man`:

```bash
Expand Down
Loading
Loading