From 205cc7c8804d32e12bd9bb64c95de9408d2a6a33 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 19 Aug 2026 20:09:44 -0400 Subject: [PATCH 1/6] configs: enable SQUASHFS_XZ on 4.10 and 6.13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both kernel lines ship CONFIG_SQUASHFS=y without the xz decompressor, so a guest that mounts a squashfs 4.0/xz image gets Filesystem uses "xz" compression. This is not supported mount: mounting /dev/loop0 on /sqfs failed: Invalid argument Measured on penguin v3.1.7, mipseb, on both the 4.10 and 6.13 kernels; 6.13 prints the same line without the "squashfs:" prefix, which is why grepping a console log for "squashfs" misses it there. Loop devices themselves are fine — loop0 is created, capacity is detected and the whole filesystem probe list runs. xz is the dominant squashfs compressor in firmware, and firmware that keeps a *nested* squashfs inside its rootfs and loop-mounts it at boot is common in switch and AP images (a kernel-embedded initramfs whose real userland lives in a nested .img). Those targets cannot boot as shipped: the mount fails, and every symlink into the mount point dangles. The failure also does not look like a kernel-config problem from downstream — it reads as a broken extraction — so it costs a while to find. SQUASHFS_XZ depends only on SQUASHFS and selects XZ_DEC, whose per-arch BCJ filter decoders are all default y, so nothing further is needed and olddefconfig has no unmet dependency to drop it on. Every target chains to all-common.inc (directly or via an arch-common.inc), so one line per version covers the matrix; confirmed with config-explain on 4.10/{armel,x86_64} and 6.13/{mipseb,riscv64, powerpc}. --- configs/4.10/all-common.inc | 1 + configs/6.13/all-common.inc | 1 + 2 files changed, 2 insertions(+) diff --git a/configs/4.10/all-common.inc b/configs/4.10/all-common.inc index 3d4b85b..64aacce 100644 --- a/configs/4.10/all-common.inc +++ b/configs/4.10/all-common.inc @@ -280,6 +280,7 @@ CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_SMACK=y CONFIG_SLAB=y +CONFIG_SQUASHFS_XZ=y CONFIG_SQUASHFS=y CONFIG_SYN_COOKIES=y CONFIG_SYSVIPC=y diff --git a/configs/6.13/all-common.inc b/configs/6.13/all-common.inc index 0e9b6d1..5e133ce 100644 --- a/configs/6.13/all-common.inc +++ b/configs/6.13/all-common.inc @@ -289,6 +289,7 @@ CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_SMACK=y CONFIG_SLAB=y +CONFIG_SQUASHFS_XZ=y CONFIG_SQUASHFS=y CONFIG_SYN_COOKIES=y CONFIG_SYSVIPC=y From f3befe3b75038d3abf437c9f6614c813d03a87ce Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 19 Aug 2026 20:09:59 -0400 Subject: [PATCH 2/6] configs: enable the remaining squashfs decompressors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same class of gap as the previous commit, same one-line-per-option fix: lzo is what pre-4.x-era squashfs images use, lz4 and zstd are what recent OpenWrt-derived images use. Each depends only on SQUASHFS and selects its own decompress library, so like SQUASHFS_XZ they cost one line and survive olddefconfig. ZSTD support landed in squashfs in 4.14, so it is 6.13-only here. Unlike xz this is not backed by a measured failure yet — it is the rest of the compressor set the kernels can be asked for, added so the next image that uses one is not a second round of this. Drop this commit if you would rather add them on evidence. --- configs/4.10/all-common.inc | 2 ++ configs/6.13/all-common.inc | 3 +++ 2 files changed, 5 insertions(+) diff --git a/configs/4.10/all-common.inc b/configs/4.10/all-common.inc index 64aacce..792b08b 100644 --- a/configs/4.10/all-common.inc +++ b/configs/4.10/all-common.inc @@ -280,6 +280,8 @@ CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_SMACK=y CONFIG_SLAB=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_XZ=y CONFIG_SQUASHFS=y CONFIG_SYN_COOKIES=y diff --git a/configs/6.13/all-common.inc b/configs/6.13/all-common.inc index 5e133ce..0fbd140 100644 --- a/configs/6.13/all-common.inc +++ b/configs/6.13/all-common.inc @@ -289,7 +289,10 @@ CONFIG_SECURITY_SELINUX_BOOTPARAM=y CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_SMACK=y CONFIG_SLAB=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y CONFIG_SQUASHFS=y CONFIG_SYN_COOKIES=y CONFIG_SYSVIPC=y From bba821a41a791ebcc18451725a4b1f9b56afb9bd Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 19 Aug 2026 20:10:29 -0400 Subject: [PATCH 3/6] config-tools: hold SQUASHFS_XZ in the required contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the failure class the gate exists for. A kernel without it compiles, links, has the right ELF shape, boots, passes boot-check — and then cannot mount the firmware it was built to run. Nothing in the matrix notices; the symptom surfaces several repos downstream as "that target does not extract", which is where it cost real time. The entry also keeps the fragment honest: all-common.inc is the file everything chains to, so the one line that fixes every cell is also the one line a later config reshuffle can drop for every cell at once. requiredCheck reads the shipped .config, so it catches that. Deliberately only xz, not the other three decompressors added in the previous commit: xz is the one with a measured failure behind it, and the contract is meant to stay the subset whose absence is silent, not the list of options we happen to set. --- nix/config-tools.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nix/config-tools.nix b/nix/config-tools.nix index 25124e6..a1c0f15 100644 --- a/nix/config-tools.nix +++ b/nix/config-tools.nix @@ -32,6 +32,7 @@ let CONFIG_KALLSYMS_ALL = "OSI symbol resolution reads kallsyms"; CONFIG_KPROBES = "penguin's kprobe-based instrumentation"; CONFIG_DEBUG_INFO = "dwarf2json builds the ISF from DWARF; no debug info, no ISF"; + CONFIG_SQUASHFS_XZ = "xz is the dominant squashfs compressor in firmware. CONFIG_SQUASHFS=y without it mounts nothing: firmware that loop-mounts a nested squashfs at boot fails EINVAL and reads downstream as a broken extraction, not a kernel config"; }; # --------------------------------------------------------------------- From 4059f8168f8942c9b27b7adf91705fc35dbc33dd Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 19 Aug 2026 20:23:27 -0400 Subject: [PATCH 4/6] configs: read xattrs on squashfs and jffs2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both kernels build squashfs and jffs2 without xattr support, so a firmware image's extended attributes are dropped at mount time. The squashfs case is visible in the shipped kernels: they carry the string SQUASHFS error: Xattrs in filesystem, these will be ignored which is the stub that gets compiled in when SQUASHFS_XATTR=n. What is actually lost is `security.capability`. Firmware that ships a setcap'd binary instead of a setuid one — increasingly the norm — gets a binary with no capabilities under emulation, which does not fail at mount time or at exec: it fails later, inside the program, as a permission error that looks like a missing model or a wrong uid. SELinux/Smack labels on firmware images go the same way, and both LSMs are enabled here. JFFS2_FS_XATTR pulls POSIX_ACL and SECURITY with it (both default y under it); SQUASHFS_XATTR has no sub-options. jffs2 is included because a jffs2 *rootfs* is common on older NOR devices, and a jffs2 config partition alongside a squashfs rootfs is common on newer ones — the same argument in both places. --- configs/4.10/all-common.inc | 2 ++ configs/6.13/all-common.inc | 2 ++ 2 files changed, 4 insertions(+) diff --git a/configs/4.10/all-common.inc b/configs/4.10/all-common.inc index 792b08b..c690ff0 100644 --- a/configs/4.10/all-common.inc +++ b/configs/4.10/all-common.inc @@ -141,6 +141,7 @@ CONFIG_IP_ROUTE_VERBOSE=y CONFIG_IP_SET=y CONFIG_ISO9660_FS=y CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS_XATTR=y CONFIG_JFFS2_FS=y CONFIG_JFFS2_LZO=y CONFIG_JFFS2_RUBIN=y @@ -282,6 +283,7 @@ CONFIG_SECURITY_SMACK=y CONFIG_SLAB=y CONFIG_SQUASHFS_LZ4=y CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XATTR=y CONFIG_SQUASHFS_XZ=y CONFIG_SQUASHFS=y CONFIG_SYN_COOKIES=y diff --git a/configs/6.13/all-common.inc b/configs/6.13/all-common.inc index 0fbd140..6f657ec 100644 --- a/configs/6.13/all-common.inc +++ b/configs/6.13/all-common.inc @@ -141,6 +141,7 @@ CONFIG_IPV6_SIT_6RD=y CONFIG_IPV6_TUNNEL=y CONFIG_ISO9660_FS=y CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS_XATTR=y CONFIG_JFFS2_FS=y CONFIG_JFFS2_LZO=y CONFIG_JFFS2_RUBIN=y @@ -291,6 +292,7 @@ CONFIG_SECURITY_SMACK=y CONFIG_SLAB=y CONFIG_SQUASHFS_LZ4=y CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XATTR=y CONFIG_SQUASHFS_XZ=y CONFIG_SQUASHFS_ZSTD=y CONFIG_SQUASHFS=y From 9644594cfd1c901f61b0aae2b2a09dbc8a3110d4 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 19 Aug 2026 20:23:54 -0400 Subject: [PATCH 5/6] configs: UBI and UBIFS, which were entirely absent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neither kernel has any UBI at all — no MTD_UBI, no UBIFS_FS. Verified on the shipped mipseb images: zero occurrences of UBIFS, ubi_attach or ubi_ctrl in either 4.10 or 6.13, despite MTD, JFFS2 and NAND all being on. UBI is how NAND firmware stores its rootfs. The standard modern layout is a squashfs rootfs on `ubiblock` with a UBIFS overlay for the writable half, and a guest that runs `ubiattach` or `mount -t ubifs` cannot get off the ground without these — the attach fails, so the volume never appears and neither does anything that lives on it. This is the NAND counterpart of the jffs2 support that is already here for NOR. MTD_UBI_BLOCK is what provides `/dev/ubiblock*`, so it is part of the same unit rather than an extra: without it UBIFS mounts but the squashfs-on-UBI half of that layout still cannot. The compressors come free — UBIFS_FS_LZO and UBIFS_FS_ZLIB are default y in both versions, plus UBIFS_FS_ZSTD in 6.13 — and UBIFS_FS depends on MTD_UBI, which is why both lines are needed for either to survive olddefconfig. Not included: MTD_UBI_GLUEBI, the compat layer that re-exposes UBI volumes as MTD devices. Firmware that expects mtdchar on top of UBI would want it, but it changes device enumeration for every target, so it deserves its own change with a target behind it. --- configs/4.10/all-common.inc | 3 +++ configs/6.13/all-common.inc | 3 +++ 2 files changed, 6 insertions(+) diff --git a/configs/4.10/all-common.inc b/configs/4.10/all-common.inc index c690ff0..9e88e96 100644 --- a/configs/4.10/all-common.inc +++ b/configs/4.10/all-common.inc @@ -171,6 +171,8 @@ CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_NAND=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI=y CONFIG_NAMESPACES=y CONFIG_NCP_FS=y CONFIG_NET=y @@ -293,6 +295,7 @@ CONFIG_TASKSTATS=y CONFIG_TIPC=y CONFIG_TMPFS=y CONFIG_TUN=y +CONFIG_UBIFS_FS=y CONFIG_UDF_FS=y CONFIG_UFS_FS=y CONFIG_UNIX=y diff --git a/configs/6.13/all-common.inc b/configs/6.13/all-common.inc index 6f657ec..b0fbe06 100644 --- a/configs/6.13/all-common.inc +++ b/configs/6.13/all-common.inc @@ -173,6 +173,8 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD=y CONFIG_MTD_NAND=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_MTD_UBI=y CONFIG_NAMESPACES=y CONFIG_NCP_FS=y CONFIG_NET=y @@ -303,6 +305,7 @@ CONFIG_TASKSTATS=y CONFIG_TIPC=y CONFIG_TMPFS=y CONFIG_TUN=y +CONFIG_UBIFS_FS=y CONFIG_UDF_FS=y CONFIG_UFS_FS=y CONFIG_UNIX=y From 1ee5e57f80beb0642819e293e78ec41aa593f3f8 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 19 Aug 2026 20:24:06 -0400 Subject: [PATCH 6/6] configs: erofs, with its optional compressors (6.13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit erofs is absent from both kernels; the only EROFS hits in the shipped images are the `-EROFS` errno in a jffs2 message. 4.10 predates the filesystem entirely, so this is 6.13-only. It matters going forward rather than today: erofs is what Android images use and it is turning up in newer consumer firmware as the read-only rootfs, in the role squashfs has held. Adding it now costs four lines and means the first such image is a rehost rather than a kernel rebuild. EROFS_FS_ZIP (lz4) and EROFS_FS_XATTR are default y, so the three explicit lines are the compressors that are not: deflate, lzma and zstd. That mirrors what the first two commits do for squashfs — the filesystem being enabled says nothing about which images it can actually read. --- configs/6.13/all-common.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configs/6.13/all-common.inc b/configs/6.13/all-common.inc index b0fbe06..d696632 100644 --- a/configs/6.13/all-common.inc +++ b/configs/6.13/all-common.inc @@ -62,6 +62,10 @@ CONFIG_DUMMY=y CONFIG_ECRYPT_FS=y CONFIG_EFS_FS=y CONFIG_EMBEDDED=y +CONFIG_EROFS_FS=y +CONFIG_EROFS_FS_ZIP_DEFLATE=y +CONFIG_EROFS_FS_ZIP_LZMA=y +CONFIG_EROFS_FS_ZIP_ZSTD=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y CONFIG_F2FS_CHECK_FS=y