feat(extensions): add netboot extension for TFTP+NFS diskless boot#9656
feat(extensions): add netboot extension for TFTP+NFS diskless boot#9656iav wants to merge 31 commits intoarmbian:mainfrom
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds a netboot extension to produce TFTP/NFS boot artifacts and NFS-root deployments, integrates Changes
Sequence Diagram(s)sequenceDiagram
participant Builder as Armbian Builder
participant Ext as Netboot Extension
participant KernelCfg as Kernel Config
participant ImgGen as Image Generator
participant TFTP as TFTP Staging
participant Hook as netboot_artifacts_ready Hook
Builder->>Ext: triggered with ROOTFS_TYPE=nfs-root
Ext->>Ext: normalize & validate vars (NETBOOT_SERVER, NETBOOT_CLIENT_MAC, paths)
Ext->>KernelCfg: enable NFS-root kernel options
Ext->>ImgGen: customize image (disable resize service, skip firstlogin)
ImgGen->>ImgGen: build rootfs
Ext->>ImgGen: create/archive or export rootfs based on ROOTFS_COMPRESSION/ROOTFS_EXPORT_DIR
Ext->>TFTP: collect kernel (Image→zImage→vmlinuz), DTBs, optional uInitrd
Ext->>TFTP: generate PXELINUX entries (per-MAC or default)
Ext->>Hook: invoke netboot_artifacts_ready with artifact context
sequenceDiagram
participant DHCP as DHCP Server
participant Client as Target Device
participant UBoot as U-Boot
participant TFTP as TFTP Server
participant NFS as NFS Server
Client->>DHCP: request IP + boot options
DHCP->>Client: reply (serverip, bootfile)
Client->>UBoot: obtain bootfile (pxe/extlinux)
UBoot->>TFTP: fetch pxelinux.cfg, kernel, dtb, uInitrd
TFTP->>UBoot: deliver artifacts
UBoot->>NFS: mount nfsroot (from kernel cmdline)
NFS->>Client: provide root filesystem
Client->>Client: kernel boots with NFS root
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extensions/netboot/netboot.sh`:
- Around line 38-42: The function
extension_prepare_config__netboot_force_nfs_rootfs sets ROOTFS_TYPE too late (it
runs during do_extra_configuration) so NFS-specific branches in
do_main_configuration never see it; move the ROOTFS_TYPE assignment earlier by
ensuring extension_prepare_config__netboot_force_nfs_rootfs (or its logic) runs
before do_main_configuration—either call that function in the init/startup hook
that executes prior to main configuration (e.g., extension_init or top-level
script before do_main_configuration) or export/set declare -g ROOTFS_TYPE="nfs"
at script initialization so the global variable is in effect for
do_main_configuration and the NFS setup in
lib/functions/configuration/main-config.sh executes.
- Around line 147-149: The PXE staging copies ${MOUNT}/boot/uInitrd but never
references it in the generated extlinux.cfg, so U-Boot won't load the initramfs;
modify the netboot stanza generation to set an initrd_line variable when the
file exists (mirror how fdt_line is created — e.g. check for
"${MOUNT}/boot/uInitrd", set initrd_line="INITRD ${tftp_prefix_dir}/uInitrd")
and then include ${initrd_line} in the emitted PXE stanza alongside fdt_line so
the INITRD directive is present for U-Boot.
In `@extensions/netboot/README.md`:
- Around line 137-155: The README has multiple unlabeled fenced code blocks
(e.g., the directory tree block showing "/srv/netboot/" and other blocks around
lines 159-164, 171-175, 465-489, 544-547); update each triple-backtick fence to
include an appropriate info string (for example use sh, ini, or text as
appropriate) so markdownlint stops flagging them—locate the fences by searching
for the blocks that display the directory tree and configuration snippets and
add the matching language tag to each opening ``` line.
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 82-93: The rsync into ROOTFS_EXPORT_DIR can leave stale files
behind when the export tree is reused; update the rsync invocation in
rootfs-to-image.sh (the run_host_command_logged rsync call) to include --delete
(and optionally --delete-excluded) so files removed from the source are removed
from "${ROOTFS_EXPORT_DIR}" as well; add the flag either into $rsync_ea or
directly in the rsync command invocation to ensure exported NFS root mirrors the
built image.
- Around line 69-76: The archive creation pipeline (tar … | pv … |
${archive_filter} > "${ROOTFS_ARCHIVE_PATH}") must be guarded with pipefail so
failures in tar or pv aren't masked by a later stage; wrap that pipeline in a
shell context with set -o pipefail (or enable set -o pipefail locally before
running the pipeline) so a nonzero exit from tar or pv will cause the script to
fail and propagate the error from creating ROOTFS_ARCHIVE_PATH; update the block
around display_alert/ tar / pv / ${archive_filter} to ensure pipefail is active
for that pipeline.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fca3bef5-36a8-4aaa-81dd-645853e7579e
📒 Files selected for processing (5)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/rootfs-to-image.sh
`nfs-root` is a new rootfs type distinct from the existing `nfs` hybrid mode. Selecting it wires the `netboot` extension from the core `ROOTFS_TYPE` dispatch in `do_main_configuration`, so callers no longer need a separate `ENABLE_EXTENSIONS=netboot`. The legacy `nfs` branch (kernel+DTB on local boot partition, `/` over NFS) is untouched — both paths coexist until the hybrid mode's future is decided. Core plumbing mirrors the `nfs` branch for all paths where local root storage would be meaningless: partition layout skip (`prepare_partitions`), archive/export gate and version suffix (`rootfs-to-image.sh`), and the host-side filesystem compatibility check in `main-config.sh`. Extension hooks now key on `ROOTFS_TYPE=nfs-root` instead of guessing from `nfs`, removing the `force ROOTFS_TYPE=nfs` shim that ran too late relative to `do_main_configuration`. Also folded in from CodeRabbit review on PR armbian#9656: - pipefail around tar|pv|compressor so truncated archives no longer slip through on an intermediate stage failure - `rsync --delete` on `ROOTFS_EXPORT_DIR` so stale files from a previous build don't linger in the NFS export tree - explicit `INITRD` directive in extlinux when `uInitrd` is staged; U-Boot only loads an initramfs when the stanza names it - README updated to document `ROOTFS_TYPE=nfs-root` as the single switch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Very nice. With recent u-boot (2026.04, thanks to Kwiboo), one can enable LWIP networking stack for better TFTP performance (loading kernel and initramfs) -- there's also some TFTP Window Size stuff that can be tuned. U-boot with LWIP + u-boot's |
|
Thanks for the pointer! Let's get this merged first — iterating on something that works is the easy part. |
a76e4b2 to
fec99ac
Compare
a10feb4 to
4ff6016
Compare
|
@coderabbitai review |
|
Pull my Helios64 to v2026.04 in #9675 |
a61ff64 to
dac19c8
Compare
…ble ROOTFS_COMPRESSION
…ble ROOTFS_COMPRESSION
ee8e0e7 to
88c8e6f
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extensions/netboot/netboot.sh`:
- Around line 188-200: The conditional and assignments that reference
BOOT_FDT_FILE must use a safe fallback expansion to avoid unbound-variable
errors under set -u; update all occurrences that read BOOT_FDT_FILE in this
block (the conditional, the declare fdt_file assignment, and the .dts check) to
use parameter expansion with a default (e.g. ${BOOT_FDT_FILE-}) so the test [[
-n ... ]] and subsequent logic can fall through to setting fdt_line="FDTDIR
${NETBOOT_TFTP_PREFIX}/dtb" when BOOT_FDT_FILE is unset.
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 147-162: Reject and normalize dangerous ROOTFS_EXPORT_DIR values
before creating or rsyncing: compute a canonical path (e.g. with realpath -m or
similar) and if the result is empty, "/" or otherwise equals the host root or SD
card root (compare against "$SDCARD" and "/"), abort with a clear error via
display_alert and non-zero exit; add this guard immediately before the
run_host_command_logged mkdir -pv "${ROOTFS_EXPORT_DIR}" and before the rsync
invocation so the run_host_command_logged mkdir and the rsync -aHWh --delete
--delete-excluded ... "$SDCARD/" "${ROOTFS_EXPORT_DIR}/" never run with a
dangerous target.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e9d5e2a3-2e6e-4178-94ba-bd86daa392f4
📒 Files selected for processing (6)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/partitioning.shlib/functions/image/rootfs-to-image.sh
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
lib/functions/image/rootfs-to-image.sh (1)
147-170:⚠️ Potential issue | 🟠 MajorAlso reject export paths that resolve to the live rootfs.
The guard rejects
/, butROOTFS_EXPORT_DIRcan still resolve to${SDCARD},${MOUNT}, or a child of either. Withrsync --delete --delete-excluded, that can delete excluded paths from the rootfs being archived/exported or recurse into the export directory itself. This is the same dangerous-target class previously flagged, but the source/mount checks are still missing.Proposed guard extension
declare rootfs_export_dir_resolved rootfs_export_dir_resolved="$(realpath -m "${ROOTFS_EXPORT_DIR}")" - if [[ "${rootfs_export_dir_resolved}" == "/" ]]; then - exit_with_error "ROOTFS_EXPORT_DIR must not resolve to /" "${ROOTFS_EXPORT_DIR}" + declare rootfs_source_resolved rootfs_mount_resolved + rootfs_source_resolved="$(realpath -m "${SDCARD}")" + rootfs_mount_resolved="$(realpath -m "${MOUNT}")" + if [[ "${rootfs_export_dir_resolved}" == "/" || + "${rootfs_export_dir_resolved}" == "${rootfs_source_resolved}" || + "${rootfs_export_dir_resolved}" == "${rootfs_source_resolved}/"* || + "${rootfs_export_dir_resolved}" == "${rootfs_mount_resolved}" || + "${rootfs_export_dir_resolved}" == "${rootfs_mount_resolved}/"* ]]; then + exit_with_error "ROOTFS_EXPORT_DIR must not resolve to / or the live rootfs tree" "${ROOTFS_EXPORT_DIR}" fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/functions/image/rootfs-to-image.sh` around lines 147 - 170, The current guard only rejects "/" but must also reject export paths that resolve to the live rootfs/mounts; update the check after rootfs_export_dir_resolved="$(realpath -m "${ROOTFS_EXPORT_DIR}")" to compute resolved paths for "${SDCARD}" and "${MOUNT}" (e.g., sdcard_resolved and mount_resolved via realpath -m) and fail if rootfs_export_dir_resolved equals either of those or is a descendant (prefix) of either (use a trailing-slash prefix comparison to avoid partial matches); call exit_with_error with a clear message if the export target is the same as or inside SDCARD or MOUNT before running run_host_command_logged and rsync to prevent destructive --delete behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extensions/netboot/netboot.sh`:
- Around line 193-199: The PXE stanza normalization sets fdt_file from
BOOT_FDT_FILE but doesn't verify the compiled ${fdt_file} actually exists under
${NETBOOT_TFTP_PREFIX}/dtb; add a validation step inside the existing if [[ -n
"${BOOT_FDT_FILE:-}" ... ]] block (where fdt_file is derived) to check for the
file's presence in the dtb tree and if missing emit a clear error via
echo/process logger and exit non‑zero so the build fails fast; reference
BOOT_FDT_FILE, fdt_file, NETBOOT_TFTP_PREFIX and the dtb/ destination when
implementing the existence check and failure path.
In `@extensions/netboot/README.md`:
- Line 75: The README currently documents NETBOOT_TFTP_OUT as pointing to
output/images but the implementation stages it at
${FINALDEST}/${version}-netboot-tftp; update the NETBOOT_TFTP_OUT description to
read "Absolute path of the staging directory
(${FINALDEST}/${version}-netboot-tftp)" and replace any other occurrences that
hard-code "output/images/<version>-netboot-tftp" (the other instances in the
same README) to use ${FINALDEST}/${version}-netboot-tftp so docs match the
actual behavior.
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 108-115: The comment and error message for accepted ROOTFS
compression values are inconsistent: the case handler accepts "zst" but the top
comment and the exit_with_error message only advertise "gzip|zstd|none"; update
the human-facing text to include "zst" everywhere. Edit the comment line that
lists accepted values and the exit_with_error call that mentions expected:
gzip|zstd|none to include zst (e.g., gzip|zstd|zst|none) so the
ROOTFS_COMPRESSION, rootfs_compression case and error paths are consistent.
---
Duplicate comments:
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 147-170: The current guard only rejects "/" but must also reject
export paths that resolve to the live rootfs/mounts; update the check after
rootfs_export_dir_resolved="$(realpath -m "${ROOTFS_EXPORT_DIR}")" to compute
resolved paths for "${SDCARD}" and "${MOUNT}" (e.g., sdcard_resolved and
mount_resolved via realpath -m) and fail if rootfs_export_dir_resolved equals
either of those or is a descendant (prefix) of either (use a trailing-slash
prefix comparison to avoid partial matches); call exit_with_error with a clear
message if the export target is the same as or inside SDCARD or MOUNT before
running run_host_command_logged and rsync to prevent destructive --delete
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 486fa577-2c72-4fae-b71d-d9b68e2fb384
📒 Files selected for processing (6)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/partitioning.shlib/functions/image/rootfs-to-image.sh
🚧 Files skipped from review as they are similar to previous changes (2)
- lib/functions/configuration/main-config.sh
- config/templates/nfs-boot.cmd.template
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
lib/functions/image/rootfs-to-image.sh (1)
147-170:⚠️ Potential issue | 🟠 MajorComplete the
ROOTFS_EXPORT_DIRsafety guard for source aliases.The guard rejects
/, butrsync --delete --delete-excluded "$SDCARD/" "$ROOTFS_EXPORT_DIR/"is still dangerous if the export path resolves to${SDCARD},${MOUNT}, or a child of either. In those cases the receiver is the staged rootfs itself or inside it, so excluded paths like/boot//homecan be deleted or recursively copied.🛡️ Proposed guard
declare rootfs_export_dir_resolved rootfs_export_dir_resolved="$(realpath -m "${ROOTFS_EXPORT_DIR}")" - if [[ "${rootfs_export_dir_resolved}" == "/" ]]; then - exit_with_error "ROOTFS_EXPORT_DIR must not resolve to /" "${ROOTFS_EXPORT_DIR}" + declare sdcard_resolved mount_resolved + sdcard_resolved="$(realpath -m "${SDCARD}")" + mount_resolved="$(realpath -m "${MOUNT}")" + if [[ "${rootfs_export_dir_resolved}" == "/" || + "${rootfs_export_dir_resolved}" == "${sdcard_resolved}" || + "${rootfs_export_dir_resolved}" == "${sdcard_resolved}/"* || + "${rootfs_export_dir_resolved}" == "${mount_resolved}" || + "${rootfs_export_dir_resolved}" == "${mount_resolved}/"* ]]; then + exit_with_error "ROOTFS_EXPORT_DIR must not resolve to /, SDCARD, MOUNT, or a child of them" "${ROOTFS_EXPORT_DIR}" fiRun this read-only check to confirm the current guard only rejects
/and has no source-alias checks:#!/bin/bash # Description: Inspect ROOTFS_EXPORT_DIR validation in rootfs-to-image.sh. # Expected before fix: only "/" is rejected. # Expected after fix: SDCARD/MOUNT equality and child-path checks are present. fd -i '^rootfs-to-image\.sh$' -x awk 'NR>=147 && NR<=170 { printf "%s:%d:%s\n", FILENAME, NR, $0 }' {}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/functions/image/rootfs-to-image.sh` around lines 147 - 170, The current safety guard only rejects "/" via rootfs_export_dir_resolved but misses cases where ROOTFS_EXPORT_DIR points at or inside the source (SDCARD or MOUNT), which makes the rsync (in the rsync ... "$SDCARD/" "${rootfs_export_dir_resolved}/") destructive; update the validation around rootfs_export_dir_resolved to also realpath -m SDCARD and MOUNT into variables (e.g. sdcard_resolved, mount_resolved) and then reject if rootfs_export_dir_resolved equals or is a descendant of either (string-equals or prefix-with-slash checks to avoid false matches), calling exit_with_error (same message style) when detected; keep using run_host_command_logged and the existing rsync logic otherwise.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extensions/netboot/netboot.sh`:
- Around line 80-84: The path validation for ROOTFS_EXPORT_DIR is insufficient
and can allow dangerous values like "/" or the build checkout (SRC) which rsync
--delete could wipe; update the validation in the netboot export logic to reject
ROOTFS_EXPORT_DIR when it is "/" or when it resolves to the same path as the
source/checkout (SRC), and also reject empty when ROOTFS_COMPRESSION="none" as
before; use the existing exit_with_error call to fail with a clear message
referencing EXTENSION and ROOTFS_EXPORT_DIR when the value is "/" or equals SRC
(or resolves to it), and ensure you perform resolution/comparison (realpath or
equivalent) before comparing to avoid symlink tricks.
In `@extensions/netboot/README.md`:
- Line 65: The README's ROOTFS_COMPRESSION documentation and any workflow text
referencing accepted values should be updated to include the accepted alias
"zst" in addition to "zstd" (e.g., list as "gzip|zstd|zst|none"); specifically
update the variable table entry for ROOTFS_COMPRESSION and any mentions around
the create_image_from_sdcard_rootfs workflow so they advertise "zst" as a valid
value alongside "zstd".
- Line 80: The README incorrectly calls NETBOOT_CLIENT_MAC and NETBOOT_HOSTNAME
"sanitized" values; update the text to state that NETBOOT_CLIENT_MAC is the
original user-provided value and that any normalized/cleaned MAC is exposed via
NETBOOT_PXE_FILE (or another appropriate variable), and clarify NETBOOT_HOSTNAME
is the extension-used hostname rather than a sanitized hook value; edit the
table row describing `NETBOOT_HOSTNAME, NETBOOT_CLIENT_MAC` to explicitly
distinguish "user-provided/raw" vs "normalized/used by extension
(NETBOOT_PXE_FILE)" and adjust wording accordingly.
---
Duplicate comments:
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 147-170: The current safety guard only rejects "/" via
rootfs_export_dir_resolved but misses cases where ROOTFS_EXPORT_DIR points at or
inside the source (SDCARD or MOUNT), which makes the rsync (in the rsync ...
"$SDCARD/" "${rootfs_export_dir_resolved}/") destructive; update the validation
around rootfs_export_dir_resolved to also realpath -m SDCARD and MOUNT into
variables (e.g. sdcard_resolved, mount_resolved) and then reject if
rootfs_export_dir_resolved equals or is a descendant of either (string-equals or
prefix-with-slash checks to avoid false matches), calling exit_with_error (same
message style) when detected; keep using run_host_command_logged and the
existing rsync logic otherwise.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 49bee0c1-488d-4f80-8b38-daede5b3bc82
📒 Files selected for processing (6)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/partitioning.shlib/functions/image/rootfs-to-image.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/functions/configuration/main-config.sh
…ble ROOTFS_COMPRESSION
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
lib/functions/image/rootfs-to-image.sh (1)
147-162:⚠️ Potential issue | 🟠 MajorValidate
ROOTFS_EXPORT_DIRbefore running destructive rsync.Line 154 runs
rsync --delete --delete-excludedagainst a raw user path. IfROOTFS_EXPORT_DIRresolves to/,${SDCARD},${MOUNT}, or a child of the source tree, this can delete host/container files or the build rootfs itself. Keep the guard at the destructive operation site so it covers bothROOTFS_TYPE=nfsandROOTFS_TYPE=nfs-root.🛡️ Proposed guard
if [[ -n "${ROOTFS_EXPORT_DIR}" ]]; then - display_alert "Exporting rootfs tree" "${ROOTFS_EXPORT_DIR}" "info" - run_host_command_logged mkdir -pv "${ROOTFS_EXPORT_DIR}" + declare rootfs_export_dir_resolved sdcard_resolved mount_resolved + rootfs_export_dir_resolved="$(realpath -m "${ROOTFS_EXPORT_DIR}")" + sdcard_resolved="$(realpath -m "${SDCARD}")" + mount_resolved="$(realpath -m "${MOUNT}")" + + case "${rootfs_export_dir_resolved}" in + / | "${sdcard_resolved}" | "${sdcard_resolved}/"* | "${mount_resolved}" | "${mount_resolved}/"*) + exit_with_error "Refusing unsafe ROOTFS_EXPORT_DIR" "${ROOTFS_EXPORT_DIR} resolves to ${rootfs_export_dir_resolved}" + ;; + esac + + display_alert "Exporting rootfs tree" "${rootfs_export_dir_resolved}" "info" + run_host_command_logged mkdir -pv "${rootfs_export_dir_resolved}" # --delete so files removed from the source rootfs don't survive in a # reused export tree (otherwise the NFS root silently drifts from the image). # --delete-excluded additionally purges receiver-side files that match our # excludes (e.g. stale /home/* left over from a prior INCLUDE_HOME_DIR=yes build). @@ --exclude="/tmp/*" \ --exclude="/sys/*" \ $exclude_home \ - --info=progress0,stats1 "$SDCARD/" "${ROOTFS_EXPORT_DIR}/" + --info=progress0,stats1 "$SDCARD/" "${rootfs_export_dir_resolved}/" fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/functions/image/rootfs-to-image.sh` around lines 147 - 162, Add a safety check immediately before the destructive rsync invocation that verifies ROOTFS_EXPORT_DIR is a sane, non-root, non-source path: ensure ROOTFS_EXPORT_DIR is non-empty, absolute, not "/" and not equal to or a parent/child of the source variables (SDCARD or MOUNT/build rootfs), and fail early with a clear error if it does; implement this guard in the same scope around the rsync call (the block that calls run_host_command_logged rsync) so it covers both ROOTFS_TYPE=nfs and nfs-root scenarios and prevents accidental deletes of host/container files.extensions/netboot/README.md (2)
80-80:⚠️ Potential issue | 🟡 MinorDo not call raw hook values sanitized.
NETBOOT_CLIENT_MACremains the user-supplied value; the normalized MAC is represented byNETBOOT_PXE_FILEwhen a per-MAC PXE filename is generated.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/netboot/README.md` at line 80, Update the README description to stop implying hook outputs are sanitized: clarify that NETBOOT_CLIENT_MAC is the raw user-supplied MAC and not normalized, and that any normalized/per-MAC PXE filename is provided via NETBOOT_PXE_FILE; adjust the table row mentioning `NETBOOT_HOSTNAME`, `NETBOOT_CLIENT_MAC` to explicitly state that `NETBOOT_CLIENT_MAC` is the original user value and `NETBOOT_PXE_FILE` contains the normalized/derived PXE filename when applicable.
65-65:⚠️ Potential issue | 🟡 MinorInclude the accepted
zstalias in the README.The implementation accepts
ROOTFS_COMPRESSION=zst, but the variable table and workflow text still advertise onlygzip|zstd|none.Also applies to: 315-317
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/netboot/README.md` at line 65, Update the README entries that document the ROOTFS_COMPRESSION variable and the workflow text around create_image_from_sdcard_rootfs to include the accepted alias "zst" alongside "zstd", i.e. advertise accepted values as gzip|zstd|zst|none; ensure both the variable table cell and any prose that lists allowed values (including the paragraphs covering create_image_from_sdcard_rootfs) mention "zst" so the docs match the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@extensions/netboot/netboot.sh`:
- Line 62: Reject any path-traversal in NETBOOT_TFTP_PREFIX before using it to
build tftp_prefix_dir and before calling mkdir/cp: validate NETBOOT_TFTP_PREFIX
(and any places where it’s re-read at the later block around the logic currently
at lines 171-174) to ensure it is not empty, does not contain “..” segments or
leading “/”, and only contains safe characters (letters, digits, -, _, /). If
needed, normalize it to remove redundant slashes and resolve/deny any ..
segments (or refuse the value) and then construct tftp_prefix_dir by joining the
validated prefix to the known netboot staging root so mkdir/cp cannot write
outside the staging area; also mirror this validation when composing PXE paths
to avoid generating unusable paths.
- Around line 104-113: The normalize function rewrites container-side bind-mount
targets like /armbian/netboot-export into ${SRC}/output/netboot-export/...
causing rsync to miss the mounted path; update _netboot_normalize_export_dir to
treat the Docker bind-mount target as already normalized by returning early when
ROOTFS_EXPORT_DIR is exactly /armbian/netboot-export or starts with
/armbian/netboot-export/ (in addition to the existing check for
${SRC}/output/netboot-export/), and apply the same guard in the analogous logic
around the other occurrence (lines ~153-164) so in-container config prep
preserves the bind-mount target instead of prefixing
${SRC}/output/netboot-export/.
In `@extensions/netboot/README.md`:
- Line 66: Update the ROOTFS_EXPORT_DIR documentation to reflect the confinement
behavior: explain that any path is rewritten into ${SRC}/output/netboot-export/,
so setting ROOTFS_EXPORT_DIR=/srv/netboot/rootfs/... will not populate
/srv/netboot/rootfs/... directly; instead document two supported workflows—(a)
specify a relative subpath inside ROOTFS_EXPORT_DIR (e.g.
ROOTFS_EXPORT_DIR=relative/path) and then create or document a host-side symlink
or NFS export that maps ${SRC}/output/netboot-export/relative/path →
/srv/netboot/rootfs, or (b) keep ROOTFS_EXPORT_DIR as an absolute host path but
explicitly state that the NFS server must export the confined directory
${SRC}/output/netboot-export/... (i.e., export the rewritten path). Update the
paragraph describing ROOTFS_EXPORT_DIR and the corresponding guidance around
lines 274-299 to include this behavior and example commands or symlink/export
notes.
---
Duplicate comments:
In `@extensions/netboot/README.md`:
- Line 80: Update the README description to stop implying hook outputs are
sanitized: clarify that NETBOOT_CLIENT_MAC is the raw user-supplied MAC and not
normalized, and that any normalized/per-MAC PXE filename is provided via
NETBOOT_PXE_FILE; adjust the table row mentioning `NETBOOT_HOSTNAME`,
`NETBOOT_CLIENT_MAC` to explicitly state that `NETBOOT_CLIENT_MAC` is the
original user value and `NETBOOT_PXE_FILE` contains the normalized/derived PXE
filename when applicable.
- Line 65: Update the README entries that document the ROOTFS_COMPRESSION
variable and the workflow text around create_image_from_sdcard_rootfs to include
the accepted alias "zst" alongside "zstd", i.e. advertise accepted values as
gzip|zstd|zst|none; ensure both the variable table cell and any prose that lists
allowed values (including the paragraphs covering
create_image_from_sdcard_rootfs) mention "zst" so the docs match the
implementation.
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 147-162: Add a safety check immediately before the destructive
rsync invocation that verifies ROOTFS_EXPORT_DIR is a sane, non-root, non-source
path: ensure ROOTFS_EXPORT_DIR is non-empty, absolute, not "/" and not equal to
or a parent/child of the source variables (SDCARD or MOUNT/build rootfs), and
fail early with a clear error if it does; implement this guard in the same scope
around the rsync call (the block that calls run_host_command_logged rsync) so it
covers both ROOTFS_TYPE=nfs and nfs-root scenarios and prevents accidental
deletes of host/container files.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4833b431-e485-46e9-ad62-6393c9235918
📒 Files selected for processing (6)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/partitioning.shlib/functions/image/rootfs-to-image.sh
| function _netboot_normalize_export_dir() { | ||
| [[ -z "${ROOTFS_EXPORT_DIR}" ]] && return 0 | ||
| # Already normalized — extension_prepare_config may fire after host_pre_docker_launch | ||
| # has already run on the host side. | ||
| [[ "${ROOTFS_EXPORT_DIR}" == "${SRC}/output/netboot-export/"* ]] && return 0 | ||
| case "${ROOTFS_EXPORT_DIR}" in | ||
| *..*) exit_with_error "${EXTENSION}: ROOTFS_EXPORT_DIR must not contain '..'" "${ROOTFS_EXPORT_DIR}" ;; | ||
| esac | ||
| declare -g ROOTFS_EXPORT_DIR="${SRC}/output/netboot-export/${ROOTFS_EXPORT_DIR}" | ||
| } |
There was a problem hiding this comment.
Preserve the Docker bind-mount target during in-container config prep.
host_pre_docker_launch__netboot_mount_export_dir exports ROOTFS_EXPORT_DIR=/armbian/netboot-export, but _netboot_normalize_export_dir does not treat that as already normalized. When extension_prepare_config runs in the container, it rewrites the value to ${SRC}/output/netboot-export//armbian/netboot-export, so rsync no longer writes to the bind mount.
🐛 Proposed fix
function _netboot_normalize_export_dir() {
[[ -z "${ROOTFS_EXPORT_DIR}" ]] && return 0
+ declare container_export_dir="${NETBOOT_CONTAINER_EXPORT_DIR:-/armbian/netboot-export}"
+ [[ "${ROOTFS_EXPORT_DIR}" == "${container_export_dir}" || "${ROOTFS_EXPORT_DIR}" == "${container_export_dir}/"* ]] && return 0
# Already normalized — extension_prepare_config may fire after host_pre_docker_launch
# has already run on the host side.
[[ "${ROOTFS_EXPORT_DIR}" == "${SRC}/output/netboot-export/"* ]] && return 0
@@
# Host-phase normalization: extension_prepare_config runs inside the container,
# which is too late — docker needs an absolute source path for the bind-mount below.
_netboot_normalize_export_dir
- declare container_export_dir="/armbian/netboot-export"
+ declare container_export_dir="${NETBOOT_CONTAINER_EXPORT_DIR:-/armbian/netboot-export}"Also applies to: 153-164
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@extensions/netboot/netboot.sh` around lines 104 - 113, The normalize function
rewrites container-side bind-mount targets like /armbian/netboot-export into
${SRC}/output/netboot-export/... causing rsync to miss the mounted path; update
_netboot_normalize_export_dir to treat the Docker bind-mount target as already
normalized by returning early when ROOTFS_EXPORT_DIR is exactly
/armbian/netboot-export or starts with /armbian/netboot-export/ (in addition to
the existing check for ${SRC}/output/netboot-export/), and apply the same guard
in the analogous logic around the other occurrence (lines ~153-164) so
in-container config prep preserves the bind-mount target instead of prefixing
${SRC}/output/netboot-export/.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
extensions/netboot/README.md (1)
1-614: LGTM — thorough documentation matching the current implementation.The guide now correctly reflects
BOOTSIZE=0behavior, thepost_create_rootfs_archivehook dispatch location, subnet-restricted NFS exports (no wildcardno_root_squashdefaults), theROOTFS_EXPORT_DIRconfinement semantics,FINALDEST-based artifact paths, and the conditional empty-file handling of/root/.not_logged_in_yetforpreset-firstruncompatibility. The security note onroot/1234appropriately warns against leaving the default on untrusted networks.Minor nit (non-blocking): the inline hook docstring in
extensions/netboot/netboot.sh(around Line 284-293) listsNETBOOT_CLIENT_MACin the exposed context but omitsNETBOOT_CLIENT_MAC_NORMALIZED, which this README documents at Line 81. Consider adding it to the in-file docstring for consistency so hook authors reading either source see the same context.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/netboot/README.md` around lines 1 - 614, The inline hook docstring in extensions/netboot/netboot.sh currently documents NETBOOT_CLIENT_MAC but misses NETBOOT_CLIENT_MAC_NORMALIZED; update that docstring (around the netboot_artifacts_ready/context block in netboot.sh) to include NETBOOT_CLIENT_MAC_NORMALIZED (lowercase, dashes) alongside NETBOOT_CLIENT_MAC so the in-file hook context matches the README's exposed variables.extensions/netboot/netboot.sh (1)
38-42: Misleading comment on no-op stub — consider removing.
add_host_dependencies__netbootis an empty hook. The comment claims it "ensures host_pre_docker_launch fires before docker starts", but that hook firing is unrelated toadd_host_dependencies— it is driven solely byhost_pre_docker_launch__netboot_mount_export_direxisting as a named function and being dispatched bylib/functions/host/docker.sh(line 579). The stub adds no registration either, since the file already defines multiple hook functions. Either drop the function and its comment, or replace the comment with the actual reason if there is one (e.g. forcingEXTRA_BUILD_DEPSevaluation).🧹 Suggested cleanup
-# Ensures host_pre_docker_launch__netboot_mount_export_dir fires before docker starts — -# otherwise ROOTFS_EXPORT_DIR never reaches the container and rsync writes into nowhere. -function add_host_dependencies__netboot() { - : -} -🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@extensions/netboot/netboot.sh` around lines 38 - 42, The comment on the no-op function add_host_dependencies__netboot is misleading; either remove the stub and its comment entirely or replace the comment with the true rationale (e.g., "keeps shell parsing/evaluation of EXTRA_BUILD_DEPS" or similar) and document why the empty function must exist; locate add_host_dependencies__netboot in this file and either delete the function and comment or change the comment to reference the actual mechanism (and mention host_pre_docker_launch__netboot_mount_export_dir only as related but not caused by this stub) so readers aren’t misled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@extensions/netboot/netboot.sh`:
- Around line 38-42: The comment on the no-op function
add_host_dependencies__netboot is misleading; either remove the stub and its
comment entirely or replace the comment with the true rationale (e.g., "keeps
shell parsing/evaluation of EXTRA_BUILD_DEPS" or similar) and document why the
empty function must exist; locate add_host_dependencies__netboot in this file
and either delete the function and comment or change the comment to reference
the actual mechanism (and mention
host_pre_docker_launch__netboot_mount_export_dir only as related but not caused
by this stub) so readers aren’t misled.
In `@extensions/netboot/README.md`:
- Around line 1-614: The inline hook docstring in extensions/netboot/netboot.sh
currently documents NETBOOT_CLIENT_MAC but misses NETBOOT_CLIENT_MAC_NORMALIZED;
update that docstring (around the netboot_artifacts_ready/context block in
netboot.sh) to include NETBOOT_CLIENT_MAC_NORMALIZED (lowercase, dashes)
alongside NETBOOT_CLIENT_MAC so the in-file hook context matches the README's
exposed variables.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0f8a1414-b5f8-49ab-aac5-b3fabb8ef468
📒 Files selected for processing (6)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/partitioning.shlib/functions/image/rootfs-to-image.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/functions/configuration/main-config.sh
|
@coderabbitai thanks, but keeping the stub. The ccache-remote extension is an apparent counter-example (has netboot auto-enables from I refined the stub's comment to explain this precisely (ce264c4). |
|
Tip For best results, initiate chat on the files or code changes.
(´。• ᵕ •。`) ♡ ✏️ Learnings added
🧠 Learnings used |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
lib/functions/image/rootfs-to-image.sh (1)
147-162:⚠️ Potential issue | 🟠 MajorAdd a final safety guard before destructive NFS export sync.
This block also runs for legacy
ROOTFS_TYPE=nfs, which does not go throughextensions/netboot/netboot.shnormalization. A badROOTFS_EXPORT_DIR=/or${SRC}can still be hit byrsync --delete --delete-excluded, so the destructive sync should defend itself at the final call site.🛡️ Proposed guard
if [[ -n "${ROOTFS_EXPORT_DIR}" ]]; then - display_alert "Exporting rootfs tree" "${ROOTFS_EXPORT_DIR}" "info" - run_host_command_logged mkdir -pv "${ROOTFS_EXPORT_DIR}" + declare rootfs_export_dir_resolved + rootfs_export_dir_resolved="$(realpath -m "${ROOTFS_EXPORT_DIR}")" + if [[ -z "${rootfs_export_dir_resolved}" || + "${rootfs_export_dir_resolved}" == "/" || + "${rootfs_export_dir_resolved}" == "${SRC%/}" || + "${rootfs_export_dir_resolved}" == "${SDCARD%/}" || + "${rootfs_export_dir_resolved}" == "${MOUNT%/}" ]]; then + exit_with_error "ROOTFS_EXPORT_DIR must point to a dedicated export directory" "${ROOTFS_EXPORT_DIR}" + fi + display_alert "Exporting rootfs tree" "${rootfs_export_dir_resolved}" "info" + run_host_command_logged mkdir -pv "${rootfs_export_dir_resolved}" @@ - --info=progress0,stats1 "$SDCARD/" "${ROOTFS_EXPORT_DIR}/" + --info=progress0,stats1 "$SDCARD/" "${rootfs_export_dir_resolved}/" fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/functions/image/rootfs-to-image.sh` around lines 147 - 162, Before running the destructive rsync call, add a final safety guard that validates ROOTFS_EXPORT_DIR and SDCARD are non-empty, not "/" and not equal to each other (or to unexpected roots), and refuse to run if any check fails; log a clear error via display_alert/processLogger and exit nonzero. Implement this check immediately before the run_host_command_logged rsync invocation that uses "$SDCARD/" and "${ROOTFS_EXPORT_DIR}/", so the destructive --delete/--delete-excluded sync cannot run against the host root or other dangerous targets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@lib/functions/image/rootfs-to-image.sh`:
- Around line 147-162: Before running the destructive rsync call, add a final
safety guard that validates ROOTFS_EXPORT_DIR and SDCARD are non-empty, not "/"
and not equal to each other (or to unexpected roots), and refuse to run if any
check fails; log a clear error via display_alert/processLogger and exit nonzero.
Implement this check immediately before the run_host_command_logged rsync
invocation that uses "$SDCARD/" and "${ROOTFS_EXPORT_DIR}/", so the destructive
--delete/--delete-excluded sync cannot run against the host root or other
dangerous targets.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 88240bd8-1c9d-4d98-8cca-805dfef6a58f
📒 Files selected for processing (6)
config/templates/nfs-boot.cmd.templateextensions/netboot/README.mdextensions/netboot/netboot.shlib/functions/configuration/main-config.shlib/functions/image/partitioning.shlib/functions/image/rootfs-to-image.sh
🚧 Files skipped from review as they are similar to previous changes (2)
- lib/functions/image/partitioning.sh
- config/templates/nfs-boot.cmd.template
Summary
Adds an opt-in
netbootextension that produces a full network-boot payload — kernel, DTB, optionaluInitrd,pxelinux.cfgand an NFS-exportable rootfs — from a regular Armbian build. For netboot the only thing that has to live on the device's local storage is U-Boot itself: the kernel and DTB come from TFTP,/is mounted over NFS, nothing else on mmc/eMMC/USB is touched during early boot.Today
ROOTFS_TYPE=nfsalone only gives a hybrid image (kernel+DTB still on SD/eMMC, only/over NFS). This PR keeps that path working and layers a real netboot flow on top via a newROOTFS_TYPE=nfs-root.What changes
lib/functions/configuration/main-config.sh— newROOTFS_TYPE=nfs-rootcase branch, symmetric with the existingfs-f2fs-support/fs-btrfswiring: the branch auto-enables thenetbootextension soROOTFS_TYPE=nfs-rootis the single switch that selects the full netboot flow.check_filesystem_compatibility_on_hostis skipped for bothnfsandnfs-root— the host-side check is a sanity net for block-device targets and falsely rejects valid NFS configurations.extensions/netboot/netboot.sh— new, directory-based extension. Directory layout (rather than a singleextensions/netboot.shfile) exists so the long-form usage guide can live next to the code asREADME.md; inlining documentation of that size into the script body would be unwieldy. Hooks:extension_prepare_config— validate variables, compute defaults forNETBOOT_TFTP_PREFIX/NETBOOT_NFS_PATH(shared byLINUXFAMILY/BOARD/BRANCH/RELEASE, or per-host whenNETBOOT_HOSTNAMEis set), normalizeNETBOOT_CLIENT_MACto the01-<mac>PXELINUX form, fail fast on badROOTFS_COMPRESSION/ROOTFS_EXPORT_DIRcombinations before debootstrap.custom_kernel_config— enableROOT_NFS,NFS_FS,NFS_V3,IP_PNP,IP_PNP_DHCPsoroot=/dev/nfs ip=dhcpworks without an initrd.post_customize_image— droparmbian-resize-filesystem.service(meaningless on NFS root) and/root/.not_logged_in_yet(thearmbian-firstlogininteractive wizard blocks bring-up when there is no interactive console).armbian-firstrun.servicestays — it only regenerates SSH host keys.host_pre_docker_launch— bind-mountROOTFS_EXPORT_DIRinto the build container when it lives outside${SRC}, so the single-step builder-as-NFS-server workflow works the same inside and outside Docker.pre_umount_final_image— assemble the TFTP tree (Image/zImage,dtb/,uInitrd), writepxelinux.cfg/{default.example | 01-<mac>}with the rightFDT/FDTDIRline and an explicitINITRDdirective whenuInitrdis present (U-Boot's PXE parser only loads an initramfs when the stanza names it), expose anetboot_artifacts_readyhook for userpatches that deploy to a real server.lib/functions/image/rootfs-to-image.sh— two new controls for NFS-rootfs builds (bothROOTFS_TYPE=nfsandnfs-root):ROOTFS_COMPRESSION=zstd|gzip|none(defaultzstd).zstd→.tar.zst,gzip→.tar.gz,none→ no archive at all. Rejected early ifnoneis set withoutROOTFS_EXPORT_DIR. Thetar | pv | compressorpipeline now runs in aset -o pipefailsubshell so a broken archive step actually fails the build instead of producing a silently truncated file.ROOTFS_EXPORT_DIR=/abs/path— also (or only) rsync the rootfs tree into this directory. Can point anywhere on the build host's filesystem — inside or outside the Armbian tree (the extension bind-mounts external paths into the Docker container automatically). If the path is an NFS mount or any other network filesystem, the build writes directly to the remote server with no intermediate archive. The rsync uses--deleteso a re-used export dir stays in sync with the new build instead of accumulating stale files from previous runs. Combined withROOTFS_COMPRESSION=nonethis turns deploy-to-NFS-server into a single build step.ROOTFS_ARCHIVE_PATHis exported so downstream hooks (the newnetboot_artifacts_ready) can reference the produced archive.lib/functions/image/partitioning.sh—prepare_partitionsskips the root partition entry fornfs-rootthe same way it does fornfs, and the SD-size sanity check no longer applies to either (there is no image file to size).config/templates/nfs-boot.cmd.template— previously sunxi/arm32-only (zImage+bootz). Made arch-agnostic so the hybridROOTFS_TYPE=nfspath builds for arm64 boards too.extensions/netboot/README.md— long-form guide: variable reference, server setup (tftpd-hpa+nfs-kernel-server), DHCP 66/67 config (OpenWRT + others), per-host / per-MAC deployments, firstrun vs firstlogin, end-to-end helios64 example, troubleshooting.Variables (quick reference)
All optional.
ROOTFS_TYPE=nfs-rootalone gives you a shared-rootfs build with apxelinux.cfg/default.examplefile.NETBOOT_SERVERnfsroot=keeps${serverip}literal for U-Boot to fill from DHCP siaddr.NETBOOT_TFTP_PREFIXarmbian/${LINUXFAMILY}/${BOARD}/${BRANCH}-${RELEASE}NETBOOT_NFS_PATH/srv/netboot/rootfs/shared/...or/srv/netboot/rootfs/hosts/<hostname>nfsroot=.NETBOOT_HOSTNAMEhosts/<hostname>/— each machine owns its own writable copy.NETBOOT_CLIENT_MAC01-<mac>(per-MAC PXELINUX override). Accepts:or-, normalized to lowercase.ROOTFS_COMPRESSIONzstdzstd/gzip/none.ROOTFS_EXPORT_DIRTest plan
Validated end-to-end on helios64 (
rockchip64,edge/resolute,ttyS2 @ 1500000), Armbian 26.05:ROOTFS_TYPE=nfs-root NETBOOT_SERVER=192.168.1.125, shared NFS path, FDTDIR fallback for boards withoutBOOT_FDT_FILE, no hardcodedconsole=). 6:57 min, all artifacts correct.NETBOOT_HOSTNAME=helios64-a NETBOOT_CLIENT_MAC=aa:bb:cc:dd:ee:ff).pxelinux.cfg/01-aa-bb-cc-dd-ee-ffcontainsnfsroot=.../hosts/helios64-a. 7:44 min.ROOTFS_COMPRESSION=gzip— produces.tar.gz(defaultzstd→.tar.zst). On the helios64 rootfs: 503 MB gzip vs 460 MB zstd.ROOTFS_COMPRESSION=none ROOTFS_EXPORT_DIR=...— single-step tree workflow, 1.5 GB tree in export dir, no archive produced. 5:17 min.ROOTFS_COMPRESSION=nonewithoutROOTFS_EXPORT_DIR— fails fast inextension_prepare_configbefore debootstrap, not hours later.tftpd-hpa+ NFS rootfs via rsync tonfs-kernel-server. DHCP options 66/67 on OpenWRT. Helios64 cold boot → U-Bootbootflow scan -lb→ TFTP →booti→ kernel →ip_auto_config→ NFS mount → systemdgraphical.target→ login prompt onttyS2@1500000. Noarmbian-firstloginwizard, noresize2fserrors,armbian-firstrun.serviceregenerates SSH host keys normally. Verified twice: once with archive workflow + manual unpack, once with tree workflow + rsync deploy.mvebu,armhf, U-Boot v2025.10) — second SoC family. PXE netboot: DHCP → TFTP (zImage + DTB) →bootz→ kernel → NFS root → systemd → login prompt. No initrd needed (see notes below).BOOT_FDT_FILEset (the FDTDIR vs FDT code path is exercised by helios64's missingBOOT_FDT_FILE, but a board with it set should also work).Known issues found during helios4 testing (not blockers for this PR):
ramdisk_addr_r(0x2880000) is only 8 MB abovekernel_addr_r(0x2080000), but zImage 6.12 is 8.1 MB → U-Boot refuses to boot with initrd. Workaround: omitINITRDfrom PXE config — kernel withCONFIG_ROOT_NFS=ymounts NFS root directly without initrd.ip=dhcpwrites DNS to/proc/net/pnpbutsystemd-resolveddoesn't read it → "No DNS servers known". Workaround:resolvectl dns <iface> <gateway>. Needs a networkd.networkfile or oneshot service inpost_customize_image(future improvement).Post-review update: architecture reworked after CodeRabbit #1 — the extension no longer forces
ROOTFS_TYPEfrom insideextension_prepare_config(too late in the config lifecycle); instead a newROOTFS_TYPE=nfs-rootcase branch inmain-config.shenables the extension, symmetric with existing filesystem-support extensions.pipefailon the archive pipeline (#4),rsync --deleteon the export dir (#5), and an explicitINITRDdirective whenuInitrdis present (#2) are all in.Post-review update (round 3): archive creation moved from the early
if/elseblock to afterpre_umount_final_imagehooks so the tarball/export tree captures the fully finalized rootfs (includesupdate_initramfsoutput and all hook edits). Split out as a separate pre-feature commit — also fixes the same bug in the originalROOTFS_TYPE=nfshybrid flow. Additionally: the netboot extension now setsBOOTSIZE=0fornfs-root, preventing the phantom/bootfstab entry at its source; the compensatingpre_umount_final_image__100_netboot_clean_fstabhook is gone.Post-review update (round 4):
config/templates/nfs-boot.cmd.templatenow wraps the DTB load in anif load ... ; then true; else echo FATAL; reset; fiblock so a missing/wrongdtb/${fdtfile}aborts the boot with a clear message instead of continuing with an invalid${fdt_addr_r}— consistent with the ramdisk and kernel handling on the following lines.Related work
A companion PR to armbian/documentation will add
Developer-Guide_Netboot.mdwith the short overview + variable reference; this extension'sREADME.mdis the long-form guide and is linked from that page.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes / Reliability
Documentation