Loading commands/modutils/depmod.cpp +3 −22 Changes for commands/modutils/depmod.cpp: 3 added lines, 22 removed lines. Original line number Diff line number Diff line Loading @@ -41,31 +41,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <elf.h> #include "shell.h" #include "xz/xz.h" #include "zstd/zstd.h" #include "modfile.h" namespace fresh { namespace cmd { namespace { // The compressed-module suffixes: CONFIG_MODULE_COMPRESS_XZ's // ".ko.xz" and CONFIG_MODULE_COMPRESS_ZSTD's ".ko.zst" (0: neither). size_t compressedSuffixLen(const std::string &s){ for(const char *suf: {".xz",".zst"}){ size_t n=std::strlen(suf); if(s.size()>n && s.compare(s.size()-n,n,suf)==0) return n; } return 0; } // Reads and decompresses a ".ko.xz" or ".ko.zst" file into IMAGE. bool readCompressed(const std::string &path,std::vector<unsigned char> &image){ std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; std::vector<unsigned char> compressed( (std::istreambuf_iterator<char>(in)),std::istreambuf_iterator<char>()); return compressedSuffixLen(path)==4 ? fresh::zstd::decompress(compressed,image) : fresh::xz::decompress(compressed,image); } using modfile::compressedSuffixLen; } // Builds modules.dep by trusting each .ko's own "depends=a,b,c" .modinfo Loading Loading @@ -125,7 +106,7 @@ namespace fresh { namespace cmd { static bool readModinfoSectionFromFile(const std::string &path,std::vector<char> &out){ if(compressedSuffixLen(path)){ std::vector<unsigned char> image; if(!readCompressed(path,image)) return false; if(!modfile::readModule(path,image)) return false; std::string blob(reinterpret_cast<char *>(image.data()),image.size()); std::istringstream mem(blob,std::ios::binary); return readModinfoSection(mem,out); Loading commands/modutils/insmod.cpp +22 −7 Changes for commands/modutils/insmod.cpp: 22 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -35,12 +35,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sys/syscall.h> #include "shell.h" #include "modfile.h" namespace fresh { namespace cmd { // finit_module(2) (kernel >= 3.8): pass an open fd straight to the // kernel rather than init_module(2)'s read-the-whole-file-into-a- // buffer-ourselves approach. // buffer-ourselves approach -- except for a ".ko.xz"/".ko.zst", which // is decompressed in memory and loaded with init_module(2). class InsmodCommand : public ShellCommand { public: InsmodCommand():ShellCommand("insmod"){} Loading @@ -51,18 +53,31 @@ namespace fresh { namespace cmd { return 1; } int fd=open(argv[1],O_RDONLY); if(fd<0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; return 1; } std::string params; for(int i=2;i<argc;++i){ if(!params.empty()) params+=" "; params+=argv[i]; } if(modfile::compressedSuffixLen(argv[1])){ std::vector<unsigned char> image; if(!modfile::readModule(argv[1],image)){ std::cerr << "insmod: " << argv[1] << ": cannot read or decompress the module" << std::endl; return 1; } if(syscall(SYS_init_module,image.data(),image.size(),params.c_str())!=0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; return 1; } return 0; } int fd=open(argv[1],O_RDONLY); if(fd<0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; return 1; } if(syscall(SYS_finit_module,fd,params.c_str(),0)!=0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; close(fd); Loading commands/modutils/modfile.h 0 → 100644 +69 −0 Changes for commands/modutils/modfile.h: 69 added lines, 0 removed lines. Original line number Diff line number Diff line /******************************************************************************* Copyright (c) 2026, Jan Koester jan.koester@gmx.net All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the <organization> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // Compressed kernel modules -- CONFIG_MODULE_COMPRESS_XZ's ".ko.xz" and // CONFIG_MODULE_COMPRESS_ZSTD's ".ko.zst" -- for insmod, modinfo, modprobe // and depmod: a compressed module is decompressed in memory (lib/xz, // lib/zstd) and handed to init_module(), as kmod does. #pragma once #include <string> #include <vector> #include <fstream> #include <iterator> #include <cstring> #include "xz/xz.h" #include "zstd/zstd.h" namespace fresh { namespace modfile { // length of the compressed-module suffix of S (".xz", ".zst"), 0: none inline size_t compressedSuffixLen(const std::string &s){ for(const char *suf: {".xz",".zst"}){ size_t n=std::strlen(suf); if(s.size()>n && s.compare(s.size()-n,n,suf)==0) return n; } return 0; } // Reads the module at PATH into IMAGE, decompressed if it is a // ".ko.xz" or ".ko.zst" (false: unreadable or corrupt). inline bool readModule(const std::string &path,std::vector<unsigned char> &image){ std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; std::vector<unsigned char> data( (std::istreambuf_iterator<char>(in)),std::istreambuf_iterator<char>()); switch(compressedSuffixLen(path)){ case 0: image.swap(data); return true; case 4: return fresh::zstd::decompress(data,image); default: return fresh::xz::decompress(data,image); } } }} commands/modutils/modinfo.cpp +13 −1 Changes for commands/modutils/modinfo.cpp: 13 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <fstream> #include <sstream> #include <string> #include <vector> #include <cstring> Loading @@ -34,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <elf.h> #include "shell.h" #include "modfile.h" namespace fresh { namespace cmd { Loading @@ -47,11 +49,21 @@ namespace fresh { namespace cmd { public: ModinfoCommand():ShellCommand("modinfo"){} // Returns false if not a valid ELF64 file or no .modinfo section. // Returns false if not a valid ELF64 file or no .modinfo section; // a ".ko.xz"/".ko.zst" is decompressed in memory first. static bool readModinfoSection(const std::string &path,std::vector<char> &out){ if(modfile::compressedSuffixLen(path)){ std::vector<unsigned char> image; if(!modfile::readModule(path,image)) return false; std::istringstream mem(std::string(image.begin(),image.end()),std::ios::binary); return readModinfoSection(mem,out); } std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; return readModinfoSection(in,out); } static bool readModinfoSection(std::istream &in,std::vector<char> &out){ Elf64_Ehdr eh; in.read(reinterpret_cast<char *>(&eh),sizeof(eh)); if(!in || std::memcmp(eh.e_ident,ELFMAG,SELFMAG)!=0 || eh.e_ident[EI_CLASS]!=ELFCLASS64) Loading commands/modutils/modprobe.cpp +3 −22 Changes for commands/modutils/modprobe.cpp: 3 added lines, 22 removed lines. Original line number Diff line number Diff line Loading @@ -45,31 +45,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sys/utsname.h> #include "shell.h" #include "xz/xz.h" #include "zstd/zstd.h" #include "modfile.h" namespace fresh { namespace cmd { namespace { // The compressed-module suffixes: CONFIG_MODULE_COMPRESS_XZ's // ".ko.xz" and CONFIG_MODULE_COMPRESS_ZSTD's ".ko.zst" (0: neither). size_t compressedSuffixLen(const std::string &s){ for(const char *suf: {".xz",".zst"}){ size_t n=std::strlen(suf); if(s.size()>n && s.compare(s.size()-n,n,suf)==0) return n; } return 0; } // Reads and decompresses a ".ko.xz" or ".ko.zst" file into IMAGE. bool readCompressed(const std::string &path,std::vector<unsigned char> &image){ std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; std::vector<unsigned char> compressed( (std::istreambuf_iterator<char>(in)),std::istreambuf_iterator<char>()); return compressedSuffixLen(path)==4 ? fresh::zstd::decompress(compressed,image) : fresh::xz::decompress(compressed,image); } using modfile::compressedSuffixLen; // kmod's alias_normalize(): '-' and '_' are the same in module names // and aliases, except inside a [...] class of a pattern Loading Loading @@ -133,7 +114,7 @@ namespace fresh { namespace cmd { static bool loadOne(const std::string &koPath,const std::string ¶ms){ if(compressedSuffixLen(koPath)){ std::vector<unsigned char> image; if(!readCompressed(koPath,image)){ if(!modfile::readModule(koPath,image)){ errno=EINVAL; return false; } Loading Loading
commands/modutils/depmod.cpp +3 −22 Changes for commands/modutils/depmod.cpp: 3 added lines, 22 removed lines. Original line number Diff line number Diff line Loading @@ -41,31 +41,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <elf.h> #include "shell.h" #include "xz/xz.h" #include "zstd/zstd.h" #include "modfile.h" namespace fresh { namespace cmd { namespace { // The compressed-module suffixes: CONFIG_MODULE_COMPRESS_XZ's // ".ko.xz" and CONFIG_MODULE_COMPRESS_ZSTD's ".ko.zst" (0: neither). size_t compressedSuffixLen(const std::string &s){ for(const char *suf: {".xz",".zst"}){ size_t n=std::strlen(suf); if(s.size()>n && s.compare(s.size()-n,n,suf)==0) return n; } return 0; } // Reads and decompresses a ".ko.xz" or ".ko.zst" file into IMAGE. bool readCompressed(const std::string &path,std::vector<unsigned char> &image){ std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; std::vector<unsigned char> compressed( (std::istreambuf_iterator<char>(in)),std::istreambuf_iterator<char>()); return compressedSuffixLen(path)==4 ? fresh::zstd::decompress(compressed,image) : fresh::xz::decompress(compressed,image); } using modfile::compressedSuffixLen; } // Builds modules.dep by trusting each .ko's own "depends=a,b,c" .modinfo Loading Loading @@ -125,7 +106,7 @@ namespace fresh { namespace cmd { static bool readModinfoSectionFromFile(const std::string &path,std::vector<char> &out){ if(compressedSuffixLen(path)){ std::vector<unsigned char> image; if(!readCompressed(path,image)) return false; if(!modfile::readModule(path,image)) return false; std::string blob(reinterpret_cast<char *>(image.data()),image.size()); std::istringstream mem(blob,std::ios::binary); return readModinfoSection(mem,out); Loading
commands/modutils/insmod.cpp +22 −7 Changes for commands/modutils/insmod.cpp: 22 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -35,12 +35,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sys/syscall.h> #include "shell.h" #include "modfile.h" namespace fresh { namespace cmd { // finit_module(2) (kernel >= 3.8): pass an open fd straight to the // kernel rather than init_module(2)'s read-the-whole-file-into-a- // buffer-ourselves approach. // buffer-ourselves approach -- except for a ".ko.xz"/".ko.zst", which // is decompressed in memory and loaded with init_module(2). class InsmodCommand : public ShellCommand { public: InsmodCommand():ShellCommand("insmod"){} Loading @@ -51,18 +53,31 @@ namespace fresh { namespace cmd { return 1; } int fd=open(argv[1],O_RDONLY); if(fd<0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; return 1; } std::string params; for(int i=2;i<argc;++i){ if(!params.empty()) params+=" "; params+=argv[i]; } if(modfile::compressedSuffixLen(argv[1])){ std::vector<unsigned char> image; if(!modfile::readModule(argv[1],image)){ std::cerr << "insmod: " << argv[1] << ": cannot read or decompress the module" << std::endl; return 1; } if(syscall(SYS_init_module,image.data(),image.size(),params.c_str())!=0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; return 1; } return 0; } int fd=open(argv[1],O_RDONLY); if(fd<0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; return 1; } if(syscall(SYS_finit_module,fd,params.c_str(),0)!=0){ std::cerr << "insmod: " << argv[1] << ": " << strerror(errno) << std::endl; close(fd); Loading
commands/modutils/modfile.h 0 → 100644 +69 −0 Changes for commands/modutils/modfile.h: 69 added lines, 0 removed lines. Original line number Diff line number Diff line /******************************************************************************* Copyright (c) 2026, Jan Koester jan.koester@gmx.net All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the <organization> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ // Compressed kernel modules -- CONFIG_MODULE_COMPRESS_XZ's ".ko.xz" and // CONFIG_MODULE_COMPRESS_ZSTD's ".ko.zst" -- for insmod, modinfo, modprobe // and depmod: a compressed module is decompressed in memory (lib/xz, // lib/zstd) and handed to init_module(), as kmod does. #pragma once #include <string> #include <vector> #include <fstream> #include <iterator> #include <cstring> #include "xz/xz.h" #include "zstd/zstd.h" namespace fresh { namespace modfile { // length of the compressed-module suffix of S (".xz", ".zst"), 0: none inline size_t compressedSuffixLen(const std::string &s){ for(const char *suf: {".xz",".zst"}){ size_t n=std::strlen(suf); if(s.size()>n && s.compare(s.size()-n,n,suf)==0) return n; } return 0; } // Reads the module at PATH into IMAGE, decompressed if it is a // ".ko.xz" or ".ko.zst" (false: unreadable or corrupt). inline bool readModule(const std::string &path,std::vector<unsigned char> &image){ std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; std::vector<unsigned char> data( (std::istreambuf_iterator<char>(in)),std::istreambuf_iterator<char>()); switch(compressedSuffixLen(path)){ case 0: image.swap(data); return true; case 4: return fresh::zstd::decompress(data,image); default: return fresh::xz::decompress(data,image); } } }}
commands/modutils/modinfo.cpp +13 −1 Changes for commands/modutils/modinfo.cpp: 13 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <iostream> #include <fstream> #include <sstream> #include <string> #include <vector> #include <cstring> Loading @@ -34,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <elf.h> #include "shell.h" #include "modfile.h" namespace fresh { namespace cmd { Loading @@ -47,11 +49,21 @@ namespace fresh { namespace cmd { public: ModinfoCommand():ShellCommand("modinfo"){} // Returns false if not a valid ELF64 file or no .modinfo section. // Returns false if not a valid ELF64 file or no .modinfo section; // a ".ko.xz"/".ko.zst" is decompressed in memory first. static bool readModinfoSection(const std::string &path,std::vector<char> &out){ if(modfile::compressedSuffixLen(path)){ std::vector<unsigned char> image; if(!modfile::readModule(path,image)) return false; std::istringstream mem(std::string(image.begin(),image.end()),std::ios::binary); return readModinfoSection(mem,out); } std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; return readModinfoSection(in,out); } static bool readModinfoSection(std::istream &in,std::vector<char> &out){ Elf64_Ehdr eh; in.read(reinterpret_cast<char *>(&eh),sizeof(eh)); if(!in || std::memcmp(eh.e_ident,ELFMAG,SELFMAG)!=0 || eh.e_ident[EI_CLASS]!=ELFCLASS64) Loading
commands/modutils/modprobe.cpp +3 −22 Changes for commands/modutils/modprobe.cpp: 3 added lines, 22 removed lines. Original line number Diff line number Diff line Loading @@ -45,31 +45,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sys/utsname.h> #include "shell.h" #include "xz/xz.h" #include "zstd/zstd.h" #include "modfile.h" namespace fresh { namespace cmd { namespace { // The compressed-module suffixes: CONFIG_MODULE_COMPRESS_XZ's // ".ko.xz" and CONFIG_MODULE_COMPRESS_ZSTD's ".ko.zst" (0: neither). size_t compressedSuffixLen(const std::string &s){ for(const char *suf: {".xz",".zst"}){ size_t n=std::strlen(suf); if(s.size()>n && s.compare(s.size()-n,n,suf)==0) return n; } return 0; } // Reads and decompresses a ".ko.xz" or ".ko.zst" file into IMAGE. bool readCompressed(const std::string &path,std::vector<unsigned char> &image){ std::ifstream in(path,std::ios::binary); if(!in.is_open()) return false; std::vector<unsigned char> compressed( (std::istreambuf_iterator<char>(in)),std::istreambuf_iterator<char>()); return compressedSuffixLen(path)==4 ? fresh::zstd::decompress(compressed,image) : fresh::xz::decompress(compressed,image); } using modfile::compressedSuffixLen; // kmod's alias_normalize(): '-' and '_' are the same in module names // and aliases, except inside a [...] class of a pattern Loading Loading @@ -133,7 +114,7 @@ namespace fresh { namespace cmd { static bool loadOne(const std::string &koPath,const std::string ¶ms){ if(compressedSuffixLen(koPath)){ std::vector<unsigned char> image; if(!readCompressed(koPath,image)){ if(!modfile::readModule(koPath,image)){ errno=EINVAL; return false; } Loading