First repo-dev release uploaded 1000/1078 assets before hitting
GitHub's documented per-release limit of 1000 assets. The cap is
hit because every package shipped a paired .sig, doubling the
count without adding meaningful security.
The trust path now flows entirely through the database:
- archr-keyring ships the master public key.
- archr.db.sig proves the db came from the maintainer.
- The db records the SHA256 of every package; pacman re-hashes
after download and refuses mismatches.
To compromise a package the attacker would have to publish a
modified db that lists their hash, which requires the signing
subkey. The per-package .sig was redundant against this attack.
This is the same model Arch Linux ARM, EndeavourOS and SteamOS
use in production.
pacman.conf SigLevel flips from "Required DatabaseOptional" to
"PackageOptional DatabaseRequired"; comment expanded so a future
reader sees the why.
gen-pacman-repo drops the `gpg --detach-sign` step inside
build_one_pkg(); repo-add --sign still signs the db at the end.
Asset count shrinks from 1078 to 543 (535 packages + 8 db
artifacts), which fits comfortably in a single GitHub Release.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The ROCKNIX-sourced rewrite of 003-midgard-refactor-power-init-and-
fixed-unbalanced-run.patch (picked up in 5805267f90) targets a
different kbase tree: it patches platform/devicetree/mali_kbase_runtime_pm.c
expecting `else if (!regulator_is_enabled(...))` on line 38.
Our mali_kbase pin (github.com/archr-linux/mali_kbase @ 422e192b)
does not have that line, so the hunk rejects and mali-bifrost:target
fails to install.
Restore the original 199-line patch that targets platform/meson/
mali_kbase_runtime_pm.c, which is the path that exists in our tree.
This was the file as of commit 5805267f90^ — the working pre-sync
state.
The ROCKNIX cleanup will become applicable when (and if) we move to
the same kbase revision they ship, which would itself be a bigger
upgrade.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
In F7 I added explicit bridge symlinks for hatari and openbor:
ln -sf /storage/.hatari /var/lib/archr/data/hatari
ln -sf /storage/openbor /var/lib/archr/games/openbor
That breaks the build at install time. The F5/F6 umbrella bridges
already turn /var/lib/archr/data and /var/lib/archr/games into
symlinks (-> /storage/.local/share and /storage/roms). When the
hatari ln -sf tries to create a child node under
/var/lib/archr/data, it follows the umbrella symlink, hits
/storage/.local/share which does not exist on the rootfs at build
time, and aborts with "No such file or directory".
Resolution: rely on the umbrella alone. /var/lib/archr/data/hatari
resolves to /storage/.local/share/hatari at runtime through the
umbrella; start_hatari.sh and start_OpenBOR.sh already mkdir -p the
final dir on first launch, so the dir materializes when needed.
Keep the /opt/jdk bridge because there is no /opt umbrella to fall
through.
This is the second clean-build fix on top of the wayland patch
rebase (7eff5d02a6).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The 00-fix-wayland-scanner-pkgconfig.patch was anchored on a context
line ('bindir=' + join_paths(...)) that Wayland removed between 1.24
and 1.25. After our F0 bump to 1.25.0 the unpack step rejected the
patch and aborted the build_compat arm pass.
Pull the rebased patch from ROCKNIX (commit a9e464274a in their
graphics/display sync) where the same context shift was already
resolved: the bindir line is gone, the filebase line stays.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Master generated 2026-06-23 offline (ed25519 cert-only with ed25519
sign subkey expiring 2028-06-23). Private master lives on encrypted
cold storage; the signer subkey is on the build workstation only.
Revocation certificate kept offline in two physical locations.
Fingerprint: 0CB282379EBB394EF380AEB98A762D5706C602A1
archr.gpg is the binary public keyring that pacman-key --populate archr
walks; archr-trusted gives the master full ownertrust (level 4) so
packages signed by its subkey are accepted; archr-revoked stays empty
until a key is actually revoked.
archr-keyring/package.mk already auto-detects these files (instead of
the empty placeholders it shipped before) and includes them in the
image. With the keyring populated, the next gen-pacman-repo run with
SIGNER set will produce a properly signed archr.db and per-package
.sig files that ArchR clients can verify.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Decision: pacman repo lives on GitHub Releases at
archr-linux/archr-repo. No VPS, no repo.arch-r.io. Google Drive stays
out of runtime (backup/archive only). Cloudflare R2 fallback deferred.
Detail in docs/release-policy.md.
archr-update rewritten end-to-end:
- 188 lines of dead POST-to-update.arch-r.io code replaced by an
87-line wrapper around pacman -Syu. The POST endpoint never existed
so there is no legacy compat to preserve.
- subcommands: check, update, info. system.cfg "updates.branch" maps
to repo-stable / repo-next / repo-dev tags on archr-linux/archr-repo;
the mirrorlist is rewritten on every invocation so channel switches
are immediate.
pacman.conf rebuilt around a single [archr] repo (Arch Linux ARM
upstream removed). SigLevel starts at "Required DatabaseOptional"
during bootstrap; will tighten to "Required" once the production
master key signs the .db.
pacman-init reachability test points at github.com and the keyring
populate target is "archr" instead of "archlinuxarm".
archr-keyring repurposed: the three archlinuxarm keyring files are
removed; package.mk now expects archr.gpg / archr-trusted /
archr-revoked under keys/ and ships empty placeholders until the real
ArchR master key is generated.
scripts/repo/gen-pacman-repo + README: take the build.*-RK3326 tree
that "make docker-RK3326" leaves behind, package each install_pkg/<x>
as a .pkg.tar.zst with synthesized .PKGINFO, run repo-add to build
archr.db, optionally GPG-sign everything. Emits a `gh release create`
command for the publish step. CI hook left as a follow-up.
docs/improvements.md 2.1 marked partial: client and build script are
ready; what blocks the first usable release is generating the GPG
master key, creating archr-linux/archr-repo on GitHub, and one manual
publish for the repo-dev tag to validate the end-to-end loop.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
projects/ArchR/packages/hardware/quirks/ shipped 50 device dirs and
10 platform dirs inherited from JELOS/ROCKNIX. ArchR only ever
supports RK3326 (one platform), and the per-device quirks are
selected at runtime by reading /sys/firmware/devicetree/base/archr,device_switch/this
— a name that can only match a DTB we compiled in. The 42 device
dirs without a matching DTB and the 9 platform dirs for SoCs we
don't have (H700, RK3399, RK3566, RK3588, S922X, SDM845, SM8250,
SM8550, SM8650) were unreachable code.
Keep: 14 device dirs and 1 platform (RK3326). All correspond to a
DTS file in projects/ArchR/devices/RK3326/linux/dts/rockchip/ so
they can actually be selected at boot.
Cross-references to the dead platforms in other scripts (case
${DEVICE} in RK3399) ...; case ${DEVICE} in SM8250) ...) stay
as harmless dead branches that never trigger when DEVICE=RK3326;
cleaning those up is in scope for a follow-up sweep.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three concrete Class Z migrations land via bridges in the archr meta-
package plus single-line script tweaks:
- /storage/jdk -> /opt/jdk (runemu.sh JAVA_HOME, freej2me.sh JDKDEST)
- /storage/.hatari -> /var/lib/archr/data/hatari (start_hatari.sh
HATARI_DIR_HOME; the pre-baked Atari-ST .cfg files keep working
through the symlink because both addresses resolve to the same
overlay dir).
- /storage/openbor -> /var/lib/archr/games/openbor (start_OpenBOR.sh
CONFIGDIR).
Other Class Z paths fall out cleanly:
- /storage/.opt was already covered by /opt -> /storage/.opt symlink
in virtual/image/package.mk.
- /storage/.emulationstation already gets redirected to
/storage/.config/emulationstation by emulationstation's autostart,
which is in turn covered by the umbrella bridge F5.
- /storage/psvita/vita3k/ux0/app and /storage/.nfs-mount are
low-traffic top-level paths; no bridge added.
docs/fhs-mapping.md: Fase 7 detail + system-wide audit summary. 17
umbrella bridges in archr meta-package + 5 modular (openssh, iwd,
bluez, connman, fontconfig). All seven ARCHR_* variables resolve to
Arch-friendly paths. The remaining 1625 hardcoded /storage refs in
projects/ArchR continue to work through compat (both paths reach the
same storage overlay); refactoring those is left as an organic
follow-up.
Status header marks the FHS migration complete. Section 2.2 of the
Arch-ification roadmap is now closed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same playbook as F5, applied to user data. /storage/roms appears in
117 files in projects/ArchR and refactoring every reference in one
shot would be a huge diff over the directory where the user's games
library lives. Bad risk-reward.
Two new umbrella bridges in the archr meta-package:
/var/lib/archr/games -> /storage/roms
/var/lib/archr/backup -> /storage/backup
ARCHR_GAMES flipped to the FHS path; man page mirrors it.
The other Class E paths (screenshots, recordings, downloads, music,
pictures, videos, tvshows) audit as zero references in
projects/ArchR; they are LibreELEC top-level heritage and not used at
ArchR runtime.
/storage/.ssh stays as is. busybox/package.mk sets the root user's
HOME to /storage, so /storage/.ssh is literally $HOME/.ssh — FHS
already. Moving to /root/.ssh would require flipping the HOME of root
to /root first, which has knock-on effects (cd ~, every script that
assumes cwd at login, etc) outside the FHS scope.
No data migration script: nothing on disk moves. ROMs and saves keep
exactly the same bytes; the FHS path is just another way to address
them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Original F5 plan was "one commit per emulator, refactor every
hardcoded /storage/.config/<emu> reference". Audit showed that path
appears across 22+ device quirk files, multiple start scripts and a
handful of compiled-in patches per emulator. Mass refactor would be a
huge diff with real regression risk and zero FHS gain on the running
system because, with the bridge installed, the FHS path already
resolves correctly.
Pivot: install two umbrella bridges in the archr meta-package:
/var/lib/archr/config -> /storage/.config
/var/lib/archr/data -> /storage/.local/share
Flip the ARCHR_CONFIG and ARCHR_DATA exports in 010-archr-fhs to the
FHS paths so downstream scripts that already use the variables get
the Arch-friendly surface for free. Update archr(7) ENVIRONMENT
section: both variables now describe the FHS location as the actual
address, with /storage/... noted as the rw-overlay substrate.
Net effect: every /var/lib/archr/config/<emu> and
/var/lib/archr/data/<emu> path exists at runtime and is writable.
Inspection tools see a normal Arch layout. Hardcoded /storage/.config
references in JELOS-inherited quirks and start scripts keep working
because the symlink resolves both ways — writes through either path
land in the same overlay file. Per-emulator refactors stay possible
later, as incremental PRs, without a single big-bang.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wider Class D batch. Every state directory referenced by a systemd
unit or runtime script now lives at an Arch-conventional path with a
bridge symlink into /storage:
service enable/disable flags
/storage/.cache/services -> /var/lib/archr/services
ConsumerS: 099-networkservices, archr-report-stats.timer, and the
12 unit files that gate themselves on .conf / .disabled sentinels
(avahi-daemon, avahi-defaults, sshd, nmbd, smbd, simple-http-server,
syncthing, tailscaled, zerotier-one, cron, cron-defaults). The
systemd-scripts/usercache-setup migration helper updated too.
samba passdb
/storage/.cache/samba -> /var/lib/samba/private (Arch standard).
smb.conf passdb line + 007-rootpw boot init script updated.
connman + wireguard
/storage/.cache/connman -> /var/lib/connman
/storage/.config/wireguard -> /etc/wireguard (vpn_storagedir)
Bridges installed by the connman package; connman-setup script
follows the bridge.
tailscale
/storage/.cache/tailscale -> /var/lib/tailscale.
tailscaled --state= flag flipped to the FHS path; bridge in archr
meta-package.
cron
/storage/.cache/cron -> /var/spool/cron (Arch convention; busybox
crond reads spool/<user>). cron.service ExecStartPre updated.
archr meta-package now ships seven Class D bridge symlinks alongside
the five Class C bridges from earlier phases. Everything else stays
on the storage overlay; nothing on disk moved.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
bluez configure storagedir flipped to /var/lib/bluetooth (Arch
convention for paired-device state). post_makeinstall_target plants
the bridge symlink to /storage/.cache/bluetooth so pairings persist
on the rw overlay across reboots.
archr-config forgetBT helper and backuptool default list also point
at the FHS path.
tmpfiles z_05_bluez.conf intentionally keeps creating
/storage/.cache/bluetooth as the real overlay dir.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two security-sensitive state dirs migrated to Arch-standard locations.
openssh:
- --with-keydir flipped from /storage/.cache/ssh to /var/lib/sshd.
- post_install plants a bridge symlink /var/lib/sshd ->
/storage/.cache/ssh so keys persist on the rw overlay.
- sshd.service updates all three ExecStartPre paths.
- tmpfiles z_04_openssh.conf keeps creating /storage/.cache/ssh as
the real overlay dir.
iwd:
- Environment STATE_DIRECTORY in iwd.service now /var/lib/iwd.
- post_makeinstall_target adds the /var/lib/iwd ->
/storage/.cache/iwd bridge symlink.
- wifictl (IWD_DIR, IWD_AP_CFG_DIR), post-update copy target and
userconfig-setup all flipped to /var/lib/iwd.
- tmpfiles z_03_iwd.conf keeps creating the backing dirs.
Both follow the same bridge pattern as F2/F3: FHS surface, storage
overlay substrate. SSH host keys and wifi credentials survive the
migration because the underlying bytes never move (symlink swap).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Class F (logs) and Class G (temp/update) reshuffled.
Logs:
- /var/log was already a mount from /storage/.cache/log via
packages/sysutils/busybox/system.d/var-log.mount, so journald already
writes to /var/log/journal in FHS terms. runemu.sh debug log moves
from /storage/.cache/log/runemu-debug.log to /var/log/runemu-debug.log
(same physical bytes, FHS surface).
- createlog (top-level busybox script) packs archive zips into
/var/log/archr/archive instead of /storage/logfiles. ArchR tmpfiles
z_01_archr.conf creates the backing dir under the storage overlay so
the path is writable on first boot.
Update:
- /var/cache/archr/update is a bridge symlink to /storage/.update added
by the archr meta-package. archr-update, automount and the [update]
samba share all flipped to the FHS path.
- factoryreset still preserves /storage/.update by its real name in
the find regex; that path is also what the pre-boot bootloader
scripts read, so the backing path is the canonical one and the
bridge is just the userland surface. Comment added so this is
obvious to future readers.
- busybox/scripts/init keeps UPDATE_ROOT=/storage/.update because it
runs in the initramfs before /var is even available.
Overlayfs workdirs (/storage/.tmp/*-workdir) and /storage/.boot.hint
documented as no-op in fhs-mapping.md: the overlayfs kernel layer
requires upperdir and workdir to share a filesystem, and the boot
hint is part of the U-Boot handshake.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Last Class C bridge: /var/cache/archr/fstrim.run -> /storage/.cache.
The stamp file does not exist at first boot; touch follows the symlink
and creates the file on the storage side, where future fstrim runs
read it to remember the last run.
docs/fhs-mapping.md updated:
- Fase 2 marked complete with the 5 caches actually migrated
(mesa, fontconfig, kernel-overlays, locpath, fstrim.run).
- Documents that the initial Class C bucket of 44 paths was a coarse
auto-classification: most of those paths are state (-> F4 / Class D),
logs (-> F3 / Class F) or already redirected upstream.
- Three already-no-op redirects called out: journald.conf.d,
systemd-machine-id, system_timezone (already symlinked by their
owning packages).
Next: F3 (Class F+G, logs and temp).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two more Class C bridges, same pattern as F2.1/F2.2:
- /var/cache/archr/kernel-overlays -> /storage/.cache/kernel-overlays
- /var/cache/archr/locpath -> /storage/.cache/locpath
Consumers flipped to the Arch-friendly path:
- busybox/scripts/kernel-overlays-setup: OVERLAY_CONFIG_DIR
- busybox/profile.d/10-locale.conf: LOCPATH
- wayland/util/foot/scripts/foot.sh: LOCPATH (ArchR override only)
Service files (locale.service, kernel-overlays tmpfiles.d) keep
addressing the /storage backing path because they are the layer that
creates the actual rw overlay dir; the bridge symlink points at it.
Top-level packages/wayland/util/foot/scripts/foot.sh is upstream
LibreELEC heritage and stays untouched; the ArchR override is the
file the runtime actually executes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First migration of the Class C cache work: the Mesa shader cache moves
from the LibreELEC-style /storage/.cache/mesa_shader_cache to the
Arch-standard /var/cache/mesa.
Implementation follows the bridge-symlink pattern systemd already uses
for /etc/*: the archr meta-package creates /var/cache/mesa as a
build-time symlink into /storage/.cache/mesa_shader_cache. Mesa,
runemu.sh and any inspector see the FHS path; writes still land on the
storage overlay so the cache survives reboots.
archr/package.mk: mkdir /var/cache, ln -sf the symlink.
quirks/profile.d/041-panfrost: MESA_SHADER_CACHE_DIR=/var/cache/mesa.
Comment explains the bridge so future readers do not assume tmpfs.
runemu.sh: create the cache dir through /var/cache/mesa instead of
the legacy /storage path. Comment updated to mention the symlink.
systemd/tmpfiles.d/z_01_archr.conf is intentionally left targeting the
backing path /storage/.cache/mesa_shader_cache: it is internal plumbing
that must guarantee the real overlay dir exists; /var/cache/mesa is
the surface, /storage/.cache/mesa_shader_cache stays the substrate.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
profile.d/010-archr-fhs exports seven variables (ARCHR_ETC,
ARCHR_CONFIG, ARCHR_CACHE, ARCHR_DATA, ARCHR_GAMES, ARCHR_LOG,
ARCHR_TMP) that future ArchR scripts should read instead of
hardcoding /storage/.config and siblings. Every variable defaults to
its current /storage location so behaviour is identical to today;
subsequent FHS phases will flip the right-hand side without touching
the callers.
archr(7) gains an ENVIRONMENT section describing each variable, its
current value and its FHS target so anyone writing a new script knows
which name to use.
docs/fhs-mapping.md: F0 marked complete; F1..F7 still pending.
Risk: zero. The new file is read at login but only sets variables.
No script reads them yet.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Commit 4273d71442 was meant to bump PKG_VERSION 6.12.79 -> 6.12.94 and
remove the now-upstream u_ether patch in one commit, but the git add
failed silently on a path that had already been git-rm'd, so only the
patch deletion landed and the version bump itself stayed unstaged.
This adds the missing PKG_VERSION and PKG_SHA256 lines so the build
actually picks up 6.12.94.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
scripts/image: rewrite /etc/os-release block to the freedesktop layout
expected by third-party tools. ID_LIKE=arch is the change that makes
ArchR detect-cleanly as Arch family across distros, package managers
and analytics. PRETTY_NAME, ANSI_COLOR, LOGO, DOCUMENTATION_URL,
SUPPORT_URL and BUG_REPORT_URL added on top, with sensible fallbacks
from GIT_ORGANIZATION/GIT_REPO when the user has not overridden them
in distributions/ArchR/options.
Drop the empty /etc/archr-release sentinel file alongside os-release.
Arch ships /etc/arch-release the same way (empty by convention) for
tools that detect distro family by file presence rather than by parsing
os-release.
distributions/ArchR/options: fix typo HOME_URL "arc-r.io" -> "arch-r.io".
projects/ArchR/packages/archr: ship archr(7) under /usr/share/man/man7.
First man page of the distro, covers the public command surface
(archr-update, archr-config, archr-systems, etc.), the file locations
that matter (/etc/os-release, /etc/archr-release, /storage layout) and
the architectural decision recorded today (LibreELEC-style read-only
rootfs + storage overlay is by design, not technical debt; SteamOS
validates the model).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sliding within the same 6.12 LTS branch (longterm support runs until
December 2026), this picks up 15 stable patch releases of bugfix
and CVE work without touching ABI/API.
Pin the source tarball with PKG_SHA256 so a network MITM cannot
silently swap the kernel under us.
Drop 0020-usb-gadget-u_ether-guard-NULL-gadget-in-eth_get_drvinfo.patch
— that fix landed upstream in 6.12.81 stable. Keeping our copy would
conflict on apply.
The remaining nine patches all touch areas (panfrost, ntsync,
rtl8xxxu) that received no churn in the 79..94 window, so they
should apply unchanged; the next docker-RK3326 build will confirm.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
systemd: re-enable timedated (was -Dtimedated=false). Drop the
post-install safe_remove that nuked /usr/bin/timedatectl so users
can finally run timedatectl set-timezone from the shell. Two
upstream-equivalent patches accompany the build: 0600 redirects
the timedated write target to /var/run/localtime (writable on our
read-only rootfs), 0601 makes the timezone reader follow indirect
symlinks (matches the /etc -> /storage layout).
udevil: pull the cleaned udevil.conf (UFS gate moved from
allowed_devices to forbidden_devices, /dev/sd* whitelisted as
removable). The udev rule now bails on anything that is not
usb-storage. Internal mmc devices were never matched by the prior
KERNEL=="sd*|sr*" filter anyway, but this makes the intent
explicit and stops misclassified eSATA-like devices.
input_sense: grep -line-buffered before the parse loop so only
EV_KEY / EV_SW / error-reading lines reach the case statement.
evtest output is mostly EV_ABS noise we threw away anyway; this
cuts the CPU spent on the dispatch hot path on every controller.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
retroarch: bump to commit bdba046f (v1.22.2 + fixes) and rebase the
two local patches (0010-paths, 0011-cheevos-sounds) so they apply
cleanly against the new tree.
setsettings.sh: configure_hotkeys now detects the InputPlumber-emulated
DualSense pad first, then falls back to js0 / joypad. Without this,
retroarch took the raw event device and the hotkey binding never
matched what InputPlumber actually exposed to userspace.
drastic-sa: pull the new libdrastouch (sharp-shimmerless + quilez
pixel shaders, SDL2 hook) and the matching start_drastic.sh, which
now reads the shader setting and exports DSHOOK_SHADER. Drops the
dead S922X libmali block we never executed on RK3326.
hypseus-singe: rewrite of start_hypseus.sh fixes broken controls
(autoconfig.cfg lookup was matching too loosely) and adds the
set_kill on exit so the quit hotkey actually terminates the
emulator instead of leaving it parented to ES.
ppsspp-sa: add 005-hide-unsupported-ui-items.patch to drop the
"www.ppsspp.org" link and the "Buy PPSSPP Gold" button from the
main screen. Handheld UX cleanup, no behavioral change.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mali-bifrost (RK3326): replace patch 003 with the clean RK3576-style
version. Drops the dead BSP-style probe-defer dance, balances the
regulator refcount during probe (mirrors the clock prepare/enable),
and removes the regulator_is_enabled guard that was silently skipping
the disable on suspend. Frees the kernel log of the "missed warnings"
sunshineinabox flagged.
iwd (network): ControlPortOverNL80211=true so 802.1X EAPOL frames go
through nl80211 instead of the legacy raw socket. Some clones with
modern wifi firmware fail association otherwise.
linux 6.12-LTS: add 0020-usb-gadget-u_ether-guard-NULL-gadget upstream
fix. Without it, querying ethtool on the surviving NCM interface
during gadget detach dereferences a NULL dev->gadget pointer and
panics. Affects anyone who flips USB gadget mode at runtime.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>