diff --git a/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/Partition_PostBoot_Validation.yaml b/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/Partition_PostBoot_Validation.yaml new file mode 100755 index 00000000..c40a4688 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/Partition_PostBoot_Validation.yaml @@ -0,0 +1,20 @@ +metadata: + name: partition-postboot-validation + format: "Lava-Test Test Definition 1.0" + description: "Validate critical post-boot partitions and mount health on Qualcomm Linux platforms." + os: + - linux + scope: + - functional + +params: + ALLOW_DEGRADED: "0" + SCAN_DMESG: "1" + MOUNT_MATRIX: "/:ext4,erofs,squashfs:0:0;/efi:autofs,vfat:0:1;/var/lib/tee:ext4:1:0" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/ || true + - ALLOW_DEGRADED="${ALLOW_DEGRADED}" SCAN_DMESG="${SCAN_DMESG}" MOUNT_MATRIX="${MOUNT_MATRIX}" ./run.sh || true + - $REPO_PATH/Runner/utils/send-to-lava.sh Partition_PostBoot_Validation.res diff --git a/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/Partition_PostBoot_Validation_README.md b/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/Partition_PostBoot_Validation_README.md new file mode 100644 index 00000000..4e2f892d --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/Partition_PostBoot_Validation_README.md @@ -0,0 +1,242 @@ +# Partition_PostBoot_Validation + +## Overview + +`Partition_PostBoot_Validation` validates that critical partitions and mount points are healthy after boot, before higher-level functional tests are executed. + +This test is intended to act as an early post-boot gate and helps detect issues such as: + +- missing required mounts +- wrong filesystem type on expected mount points +- partitions mounted read-only when read-write is expected +- autofs mount points that are not accessible +- storage or mount-related kernel errors after boot +- incomplete or degraded boot state due to filesystem or mount failures + +The test is CI-friendly and writes a `.res` file with `PASS`, `FAIL`, or `SKIP`. + +--- + +## What the test validates + +The test performs the following checks: + +1. Confirms required user-space tools are present. +2. Logs platform details. +3. Logs current mount inventory and block device inventory. +4. Verifies boot and mount readiness. +5. Validates expected mount points using a configurable mount matrix. +6. Optionally scans mount/storage-related kernel log errors. + +This makes it useful as a post-boot storage sanity gate before running display, multimedia, networking, or application-level tests. + +--- + +## Default validation matrix + +By default, the test validates the following mount points: + +- `/` + - allowed filesystems: `ext4`, `erofs`, `squashfs` + - read-write not required + - no trigger access required + +- `/efi` + - allowed filesystems: `autofs`, `vfat` + - read-write not required + - trigger access required + +- `/var/lib/tee` + - allowed filesystem: `ext4` + - read-write required + - no trigger access required + +Default matrix: + +```sh +/:ext4,erofs,squashfs:0:0;/efi:autofs,vfat:0:1;/var/lib/tee:ext4:1:0 +``` + +--- + +## Mount matrix format + +The mount matrix is provided through `MOUNT_MATRIX` using the format: + +```sh +mountpoint:fstype1,fstype2:rw_required:trigger_access +``` + +Where: + +- `mountpoint` = expected mount path +- `fstype1,fstype2` = allowed filesystem types +- `rw_required` + - `1` = mount must be writable + - `0` = writable check not required +- `trigger_access` + - `1` = access the path to trigger automount or autofs behavior + - `0` = no access trigger needed + +Multiple entries are separated by `;`. + +Example: + +```sh +MOUNT_MATRIX='/:ext4,erofs,squashfs:0:0;/efi:autofs,vfat:0:1;/var/lib/tee:ext4:1:0' +``` + +--- + +## Dependencies + +The test expects the following tools to be available: + +- `findmnt` +- `mount` +- `awk` +- `grep` +- `sed` +- `dmesg` +- `systemctl` +- `lsblk` +- `blkid` + +If required dependencies are missing, the test will report `SKIP`. + +--- + +## Parameters + +### `ALLOW_DEGRADED` + +Controls whether a degraded boot state is acceptable. + +Values: + +- `0` = degraded boot state is treated as failure +- `1` = degraded boot state is allowed + +Default: + +```sh +ALLOW_DEGRADED=0 +``` + +--- + +### `SCAN_DMESG` + +Controls whether storage and mount-related kernel logs are scanned. + +Values: + +- `1` = enable dmesg scan +- `0` = disable dmesg scan + +Default: + +```sh +SCAN_DMESG=1 +``` + +--- + +### `MOUNT_MATRIX` + +Defines the expected mount points and validation rules. + +Default: + +```sh +MOUNT_MATRIX='/:ext4,erofs,squashfs:0:0;/efi:autofs,vfat:0:1;/var/lib/tee:ext4:1:0' +``` + +--- + +## Usage + +Run with defaults: + +```sh +./run.sh +``` + +Run while allowing degraded boot state: + +```sh +ALLOW_DEGRADED=1 ./run.sh +``` + +Run without dmesg scanning: + +```sh +SCAN_DMESG=0 ./run.sh +``` + +Run with a custom mount matrix: + +```sh +MOUNT_MATRIX='/:ext4:0:0;/efi:autofs,vfat:0:1;/var/lib/tee:ext4:1:0;/persist:ext4:1:0' ./run.sh +``` + +--- + +## Result file + +The test generates: + +```sh +Partition_PostBoot_Validation.res +``` + +Possible results: + +- `Partition_PostBoot_Validation PASS` +- `Partition_PostBoot_Validation FAIL` +- `Partition_PostBoot_Validation SKIP` + +--- + +## Pass criteria + +The test passes when: + +- required tools are available +- boot state is acceptable +- all required mount points are present +- each validated mount has an allowed filesystem type +- writable mounts pass writeability checks when required +- autofs or trigger-access mounts are accessible +- no blocking mount/storage-related issues are detected + +--- + +## Fail criteria + +The test fails when any of the following occurs: + +- boot state is not acceptable +- a required mount point is missing +- filesystem type does not match the expected matrix +- a mount expected to be writable is not writable +- automount or autofs path is inaccessible +- mount/storage validation detects blocking errors +- optional dmesg scan detects relevant storage or mount failures + +--- + +## Skip criteria + +The test is skipped when: + +- one or more required dependencies are unavailable +- the environment does not support the required validation flow + +--- + +## Notes + +- `/efi` may appear as `autofs` before access and transition to a real backing mount after access. +- The test is intended to be lightweight and suitable as an early boot validation gate. +- For platform-specific layouts, adjust `MOUNT_MATRIX` rather than changing the test logic. diff --git a/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/run.sh b/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/run.sh new file mode 100755 index 00000000..641ba419 --- /dev/null +++ b/Runner/suites/Kernel/Baseport/Storage/Partition_PostBoot_Validation/run.sh @@ -0,0 +1,84 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause +# +# Post-boot partition validation: +# - logs current mount inventory +# - logs block device inventory +# - validates expected mountpoints from the mount matrix +# - triggers autofs mounts where needed +# - performs RW probe only where required +# - does not gate on systemd or unrelated service health + +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi + +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +TESTNAME="Partition_PostBoot_Validation" +MOUNT_MATRIX="${MOUNT_MATRIX:-/:ext4,erofs,squashfs:0:0;/efi:autofs,vfat:0:1;/var/lib/tee:ext4:1:0}" + +test_path="$(find_test_case_by_name "$TESTNAME")" +if [ -n "$test_path" ]; then + cd "$test_path" || exit 1 +else + cd "$SCRIPT_DIR" || exit 1 +fi + +RES_FILE="./$TESTNAME.res" +rm -f "$RES_FILE" + +if ! CHECK_DEPS_NO_EXIT=1 check_dependencies findmnt mount awk grep sed lsblk blkid; then + log_skip "$TESTNAME SKIP: missing dependencies" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +fi + +log_info "--------------------------------------------------------------------------" +log_info "------------------- Starting $TESTNAME Testcase --------------------------" +log_info "Mount matrix, $MOUNT_MATRIX" + +if command -v detect_platform >/dev/null 2>&1; then + detect_platform +fi + +log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='$(uname -r 2>/dev/null || echo unknown)' arch='$(uname -m 2>/dev/null || echo unknown)'" + +partition_log_current_mounts +partition_log_block_devices + +log_info "----- Partition mount validation -----" +if partition_validate_mount_matrix "$MOUNT_MATRIX"; then + log_info "----- End partition mount validation -----" + log_pass "$TESTNAME : PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi +log_info "----- End partition mount validation -----" + +log_fail "$TESTNAME : FAIL" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 0 diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index ac26bc32..898954b9 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -4724,3 +4724,558 @@ extract_interrupt_cpu_counts() { count_interrupt_cpu_counts() { printf '%s\n' "$1" | awk 'NF { c++ } END { print c + 0 }' } + +# --------------------------------------------------------------------------- +# Partition / mount validation helpers +# --------------------------------------------------------------------------- + +# Check whether a mountpoint exists in /proc/mounts. +partition_mount_exists() { + part_path="$1" + + [ -n "$part_path" ] || return 1 + [ -r /proc/mounts ] || return 1 + + awk -v p="$part_path" ' + $2 == p { found=1; exit } + END { exit(found ? 0 : 1) } + ' /proc/mounts 2>/dev/null +} + +# Print the mount source for a mountpoint. +partition_get_mount_source() { + part_path="$1" + + [ -n "$part_path" ] || return 1 + [ -r /proc/mounts ] || return 1 + + awk -v p="$part_path" ' + $2 == p { print $1; exit } + ' /proc/mounts 2>/dev/null +} + +# Print the filesystem type for a mountpoint. +partition_get_mount_fstype() { + part_path="$1" + + [ -n "$part_path" ] || return 1 + [ -r /proc/mounts ] || return 1 + + awk -v p="$part_path" ' + $2 == p { print $3; exit } + ' /proc/mounts 2>/dev/null +} + +# Print the mount options for a mountpoint. +partition_get_mount_options() { + part_path="$1" + + [ -n "$part_path" ] || return 1 + [ -r /proc/mounts ] || return 1 + + awk -v p="$part_path" ' + $2 == p { print $4; exit } + ' /proc/mounts 2>/dev/null +} + +# Log platform details using existing helpers where available. +partition_log_platform_details() { + log_info "----- Partition platform details -----" + + if command -v detect_platform >/dev/null 2>&1; then + detect_platform >/dev/null 2>&1 || true + fi + + if [ -n "${PLATFORM_MACHINE:-}" ]; then + log_info "Platform machine: ${PLATFORM_MACHINE}" + fi + if [ -n "${PLATFORM_TARGET:-}" ]; then + log_info "Platform target: ${PLATFORM_TARGET}" + fi + if [ -n "${PLATFORM_OS_NAME:-}" ]; then + log_info "Platform OS: ${PLATFORM_OS_NAME}" + fi + if [ -n "${PLATFORM_KERNEL:-}" ]; then + log_info "Platform kernel: ${PLATFORM_KERNEL}" + else + log_info "Platform kernel: $(uname -r 2>/dev/null)" + fi + if [ -n "${PLATFORM_ARCH:-}" ]; then + log_info "Platform arch: ${PLATFORM_ARCH}" + else + log_info "Platform arch: $(uname -m 2>/dev/null)" + fi + + if command -v log_soc_info >/dev/null 2>&1; then + log_soc_info || true + fi + + log_info "----- End partition platform details -----" +} + +# Log mount/storage inventory for post-boot debug visibility. +partition_log_mount_inventory() { + log_info "----- Partition / mount inventory -----" + + if command -v findmnt >/dev/null 2>&1; then + log_info "findmnt output:" + findmnt -R / 2>/dev/null | while IFS= read -r line; do + [ -n "$line" ] && log_info "[findmnt] $line" + done + else + log_warn "findmnt not available" + fi + + if [ -r /proc/mounts ]; then + log_info "/proc/mounts output:" + while IFS= read -r line; do + [ -n "$line" ] && log_info "[mounts] $line" + done < /proc/mounts + else + log_warn "/proc/mounts not readable" + fi + + if command -v lsblk >/dev/null 2>&1; then + log_info "lsblk output:" + lsblk -o NAME,TYPE,FSTYPE,SIZE,MOUNTPOINT,PARTLABEL,UUID 2>/dev/null | while IFS= read -r line; do + [ -n "$line" ] && log_info "[lsblk] $line" + done + else + log_warn "lsblk not available" + fi + + if command -v blkid >/dev/null 2>&1; then + log_info "blkid output:" + blkid 2>/dev/null | while IFS= read -r line; do + [ -n "$line" ] && log_info "[blkid] $line" + done + else + log_warn "blkid not available" + fi + + log_info "----- End partition / mount inventory -----" +} + +# Log failed mount/fs related systemd units. Return 1 if any are failed. +partition_systemd_mount_failures() { + part_found_failed=0 + + if ! command -v systemctl >/dev/null 2>&1; then + log_info "systemctl not available; skipping failed mount/fs unit scan" + return 0 + fi + + systemctl --failed --no-legend --plain 2>/dev/null | while IFS= read -r line; do + unit_name=$(printf '%s\n' "$line" | awk '{print $1}') + [ -n "$unit_name" ] || continue + + case "$unit_name" in + *.mount|systemd-fsck*|local-fs.target|local-fs-pre.target) + log_warn "Failed systemd mount/fs unit: $line" + printf '%s\n' "$unit_name" + ;; + esac + done > /tmp/partition_failed_units.$$ 2>/dev/null + + if [ -s /tmp/partition_failed_units.$$ ]; then + part_found_failed=1 + fi + rm -f /tmp/partition_failed_units.$$ 2>/dev/null || true + + if [ "$part_found_failed" -eq 1 ]; then + return 1 + fi + + return 0 +} + +# Check overall systemd readiness. +# Args: +# $1 = allow_degraded (0/1, default 0) +partition_systemd_is_ready() { + part_allow_degraded="${1:-0}" + part_state="" + + if ! command -v systemctl >/dev/null 2>&1; then + log_info "systemctl not available; skipping systemd readiness check" + return 0 + fi + + part_state="$(systemctl is-system-running 2>/dev/null || true)" + [ -n "$part_state" ] || part_state="unknown" + + case "$part_state" in + running) + log_info "systemd state: $part_state" + return 0 + ;; + degraded) + if [ "$part_allow_degraded" = "1" ]; then + log_warn "systemd state: $part_state (accepted)" + return 0 + fi + log_fail "systemd state: $part_state" + return 1 + ;; + *) + log_fail "systemd state: $part_state" + return 1 + ;; + esac +} + +# Validate the system is boot-ready enough for partition checks. +# Args: +# $1 = allow_degraded (0/1, default 0) +# $2 = require_boot_ready (0/1, default 1) +# $3 = require_systemd (0/1, default 0) +partition_check_boot_ready() { + part_allow_degraded="${1:-0}" + part_require_boot_ready="${2:-1}" + part_require_systemd="${3:-0}" + + log_info "----- Boot readiness check -----" + + if ! command -v systemctl >/dev/null 2>&1; then + if [ "$part_require_systemd" = "1" ]; then + log_fail "systemctl not available but systemd readiness is required" + return 1 + fi + log_info "systemctl not available; boot readiness check skipped" + return 0 + fi + + if ! partition_systemd_is_ready "$part_allow_degraded"; then + return 1 + fi + + if command -v systemd_service_exists >/dev/null 2>&1; then + if systemd_service_exists local-fs.target; then + if command -v systemd_service_is_active >/dev/null 2>&1; then + if ! systemd_service_is_active local-fs.target; then + log_fail "local-fs.target is not active" + return 1 + fi + log_info "local-fs.target is active" + fi + fi + fi + + if [ "$part_require_boot_ready" = "1" ]; then + if systemctl list-jobs --no-legend 2>/dev/null | grep -q '\.mount'; then + log_warn "Pending systemd mount jobs are still present" + fi + fi + + if ! partition_systemd_mount_failures; then + log_fail "One or more mount/fs-related systemd units are failed" + return 1 + fi + + log_info "----- End boot readiness check -----" + return 0 +} + +# Trigger an autofs mountpoint by touching the path in a non-destructive way. +partition_trigger_autofs() { + part_path="$1" + + [ -n "$part_path" ] || return 1 + + log_info "Triggering autofs path: $part_path" + + if [ ! -e "$part_path" ]; then + log_fail "Autofs trigger path does not exist: $part_path" + return 1 + fi + + if [ -d "$part_path" ]; then + ls "$part_path" >/dev/null 2>&1 || true + stat "$part_path" >/dev/null 2>&1 || true + else + stat "$part_path" >/dev/null 2>&1 || true + fi + + if [ ! -r "$part_path" ] && [ ! -x "$part_path" ] && [ ! -d "$part_path" ]; then + log_fail "Autofs trigger path is not accessible: $part_path" + return 1 + fi + + sleep 1 + return 0 +} + +# Non-destructive RW probe for a mountpoint. +partition_check_rw() { + part_path="$1" + part_testfile="" + part_now="" + + [ -n "$part_path" ] || return 1 + [ -d "$part_path" ] || { + log_fail "RW probe path is not a directory: $part_path" + return 1 + } + + part_now="$(date +%s 2>/dev/null || echo 0)" + part_testfile="$part_path/.partition_rw_test_${part_now}_$$" + + if ! ( umask 077 && printf 'partition-rw-test\n' > "$part_testfile" ); then + log_fail "RW probe write failed at $part_path" + return 1 + fi + + sync >/dev/null 2>&1 || true + + if [ ! -f "$part_testfile" ]; then + log_fail "RW probe file was not created at $part_path" + return 1 + fi + + if ! rm -f "$part_testfile" 2>/dev/null; then + log_fail "RW probe cleanup failed at $part_path" + return 1 + fi + + log_info "RW probe passed at $part_path" + return 0 +} + +# Validate a single mount entry. +# Args: +# $1 = path +# $2 = expected fs regex (ex: ext4|erofs|autofs|vfat) +# $3 = rw required (0/1) +# $4 = autofs trigger (0/1) +partition_validate_mount() { + part_path="$1" + part_expected_fs="$2" + part_rw_required="${3:-0}" + part_autofs_trigger="${4:-0}" + + part_src_before="" + part_fs_before="" + part_opts_before="" + part_src_after="" + part_fs_after="" + part_opts_after="" + part_fs_ok=0 + + [ -n "$part_path" ] || { + log_fail "partition_validate_mount: empty mount path" + return 1 + } + + if ! partition_mount_exists "$part_path"; then + log_fail "Required mountpoint is missing: $part_path" + return 1 + fi + + part_src_before="$(partition_get_mount_source "$part_path" 2>/dev/null)" + part_fs_before="$(partition_get_mount_fstype "$part_path" 2>/dev/null)" + part_opts_before="$(partition_get_mount_options "$part_path" 2>/dev/null)" + + log_info "Mount before validation: path=$part_path source=${part_src_before:-unknown} fstype=${part_fs_before:-unknown} opts=${part_opts_before:-unknown}" + + if [ "$part_autofs_trigger" = "1" ] || [ "$part_fs_before" = "autofs" ]; then + if ! partition_trigger_autofs "$part_path"; then + log_fail "Autofs trigger failed for $part_path" + return 1 + fi + fi + + part_src_after="$(partition_get_mount_source "$part_path" 2>/dev/null)" + part_fs_after="$(partition_get_mount_fstype "$part_path" 2>/dev/null)" + part_opts_after="$(partition_get_mount_options "$part_path" 2>/dev/null)" + + log_info "Mount after validation: path=$part_path source=${part_src_after:-unknown} fstype=${part_fs_after:-unknown} opts=${part_opts_after:-unknown}" + + if [ -n "$part_expected_fs" ]; then + if printf '%s\n' "$part_fs_before" | grep -Eq "^(${part_expected_fs})$"; then + part_fs_ok=1 + fi + if printf '%s\n' "$part_fs_after" | grep -Eq "^(${part_expected_fs})$"; then + part_fs_ok=1 + fi + + if [ "$part_fs_ok" -ne 1 ]; then + log_fail "Filesystem type mismatch for $part_path: expected=(${part_expected_fs}) actual_before=${part_fs_before:-unknown} actual_after=${part_fs_after:-unknown}" + return 1 + fi + fi + + if [ "$part_rw_required" = "1" ]; then + if ! printf '%s\n' "$part_opts_after" | grep -Eq '(^|,)rw(,|$)'; then + if ! printf '%s\n' "$part_opts_before" | grep -Eq '(^|,)rw(,|$)'; then + log_fail "Mountpoint is not RW as required: $part_path" + return 1 + fi + fi + + if ! partition_check_rw "$part_path"; then + return 1 + fi + fi + + log_pass "Mount validation passed: $part_path" + return 0 +} + +# Validate a semicolon-separated mount matrix. +# Entry format: +# /path:fs1,fs2:rw_required:autofs_trigger +# Example: +# /efi:autofs,vfat:0:1;/var/lib/tee:ext4:1:0 +partition_validate_mount_matrix() { + part_matrix="$1" + part_fail_count=0 + part_entry="" + part_path="" + part_expected="" + part_rw_required="" + part_autofs_trigger="" + oldifs="$IFS" + + if [ -z "$part_matrix" ]; then + log_fail "partition_validate_mount_matrix: empty mount matrix" + return 1 + fi + + IFS=';' + for part_entry in $part_matrix; do + [ -n "$part_entry" ] || continue + + part_path="$(printf '%s\n' "$part_entry" | awk -F: '{print $1}')" + part_expected="$(printf '%s\n' "$part_entry" | awk -F: '{print $2}')" + part_rw_required="$(printf '%s\n' "$part_entry" | awk -F: '{print $3}')" + part_autofs_trigger="$(printf '%s\n' "$part_entry" | awk -F: '{print $4}')" + + [ -n "$part_path" ] || continue + + if [ -z "$part_rw_required" ]; then + part_rw_required="0" + fi + if [ -z "$part_autofs_trigger" ]; then + part_autofs_trigger="0" + fi + + part_expected="$(printf '%s\n' "$part_expected" | tr ',' '|')" + + log_info "Validating mount matrix entry: path=$part_path expected_fs=${part_expected:-} rw_required=$part_rw_required autofs_trigger=$part_autofs_trigger" + + if ! partition_validate_mount "$part_path" "$part_expected" "$part_rw_required" "$part_autofs_trigger"; then + part_fail_count=$((part_fail_count + 1)) + fi + done + IFS="$oldifs" + + if [ "$part_fail_count" -ne 0 ]; then + log_fail "Partition mount matrix validation failed for $part_fail_count entry(s)" + return 1 + fi + + log_pass "Partition mount matrix validation passed" + return 0 +} + +# Scan mount/storage related dmesg using existing scan_dmesg_errors(). +# Returns 0 when clean, 1 when relevant errors are found. +partition_scan_mount_dmesg() { + part_log_dir="$1" + + if [ -z "$part_log_dir" ]; then + part_log_dir="." + fi + + mkdir -p "$part_log_dir" 2>/dev/null || true + + if ! command -v scan_dmesg_errors >/dev/null 2>&1; then + log_warn "scan_dmesg_errors not available; skipping partition dmesg scan" + return 0 + fi + + log_info "Scanning dmesg for mount/storage/filesystem issues" + + if scan_dmesg_errors \ + "$part_log_dir" \ + "EXT4-fs .*|F2FS-fs .*|BTRFS .*|XFS .*|VFS.*|systemd-fsck.*|erofs.*" \ + "dummy regulator|supply [^ ]+ not found|using dummy regulator" + then + log_fail "Relevant mount/storage/filesystem dmesg errors were found" + return 1 + fi + + log_info "No relevant mount/storage/filesystem dmesg errors were found" + return 0 +} + +# Log the current mount inventory in a CI-friendly format. +# Prefers findmnt, falls back to /proc/mounts, then mount(8). +partition_log_current_mounts() { + log_info "----- Current mount inventory -----" + + if command -v findmnt >/dev/null 2>&1; then + findmnt -rn -o TARGET,SOURCE,FSTYPE,OPTIONS 2>/dev/null | \ + while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[mount] $line" + fi + done + log_info "----- End current mount inventory -----" + return 0 + fi + + if [ -r /proc/mounts ]; then + while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[mount] $line" + fi + done < /proc/mounts + log_info "----- End current mount inventory -----" + return 0 + fi + + if command -v mount >/dev/null 2>&1; then + mount 2>/dev/null | while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[mount] $line" + fi + done + log_info "----- End current mount inventory -----" + return 0 + fi + + log_warn "Unable to log current mount inventory" + log_info "----- End current mount inventory -----" + return 1 +} + +# Log block device and partition inventory for post-boot validation. +# Uses lsblk for topology and blkid for filesystem metadata. +partition_log_block_devices() { + log_info "----- Block device inventory -----" + + if command -v lsblk >/dev/null 2>&1; then + lsblk -o NAME,TYPE,FSTYPE,SIZE,MOUNTPOINT,LABEL,PARTLABEL,UUID 2>/dev/null | \ + while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[lsblk] $line" + fi + done + else + log_warn "lsblk not available for block device inventory" + fi + + if command -v blkid >/dev/null 2>&1; then + blkid 2>/dev/null | while IFS= read -r line; do + if [ -n "$line" ]; then + log_info "[blkid] $line" + fi + done + else + log_warn "blkid not available for block device inventory" + fi + + log_info "----- End block device inventory -----" + return 0 +}