mirror of
https://github.com/anchore/syft.git
synced 2026-07-05 02:28:25 +02:00
fix: detect compressed kernel modules (.ko.gz, .ko.xz, .ko.zst)
The linux-kernel-cataloger only matched plain *.ko files, missing compressed modules produced when CONFIG_MODULE_COMPRESS is enabled (common on Debian 13 / Ubuntu 24.04+). This resulted in near-zero module packages being reported for such filesystems. Changes: - Add *.ko.gz, *.ko.xz, *.ko.zst glob patterns to both the cataloger and capabilities.yaml so the file resolver picks up compressed modules - Add decompressedModuleReader() which detects the extension and transparently decompresses via compress/gzip, ulikunitz/xz, or klauspost/compress/zstd before handing the ELF bytes to the existing parseLinuxKernelModuleMetadata parser - Promote github.com/klauspost/compress from indirect to direct dependency - Add unit tests covering all three compression formats plus the uncompressed baseline, using a programmatically generated minimal ELF Fixes #4721 Signed-off-by: Will Bates <william.bates11@outlook.com>
This commit is contained in:
parent
07ae2ca08d
commit
9f047fdf11
2
go.mod
2
go.mod
@ -62,6 +62,7 @@ require (
|
|||||||
github.com/jedib0t/go-pretty/v6 v6.7.8
|
github.com/jedib0t/go-pretty/v6 v6.7.8
|
||||||
github.com/jinzhu/copier v0.4.0
|
github.com/jinzhu/copier v0.4.0
|
||||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||||
|
github.com/klauspost/compress v1.18.5
|
||||||
github.com/magiconair/properties v1.8.10
|
github.com/magiconair/properties v1.8.10
|
||||||
github.com/mholt/archives v0.1.5
|
github.com/mholt/archives v0.1.5
|
||||||
github.com/moby/sys/mountinfo v0.7.2
|
github.com/moby/sys/mountinfo v0.7.2
|
||||||
@ -215,7 +216,6 @@ require (
|
|||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.5 // indirect
|
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
|
|||||||
@ -4,7 +4,7 @@ configs: # AUTO-GENERATED - config structs and their fields
|
|||||||
kernel.LinuxKernelCatalogerConfig:
|
kernel.LinuxKernelCatalogerConfig:
|
||||||
fields:
|
fields:
|
||||||
- key: CatalogModules
|
- key: CatalogModules
|
||||||
description: CatalogModules enables cataloging linux kernel modules (`*.ko` files) in addition to the kernel itself.
|
description: CatalogModules enables cataloging linux kernel modules (`*.ko` and compressed `*.ko.gz`, `*.ko.xz`, `*.ko.zst` files) in addition to the kernel itself.
|
||||||
app_key: linux-kernel.catalog-modules
|
app_key: linux-kernel.catalog-modules
|
||||||
catalogers:
|
catalogers:
|
||||||
- ecosystem: linux # MANUAL
|
- ecosystem: linux # MANUAL
|
||||||
@ -36,6 +36,9 @@ catalogers:
|
|||||||
- '**/zImage'
|
- '**/zImage'
|
||||||
- '**/zImage-*'
|
- '**/zImage-*'
|
||||||
- '**/lib/modules/**/*.ko'
|
- '**/lib/modules/**/*.ko'
|
||||||
|
- '**/lib/modules/**/*.ko.gz'
|
||||||
|
- '**/lib/modules/**/*.ko.xz'
|
||||||
|
- '**/lib/modules/**/*.ko.zst'
|
||||||
metadata_types: # AUTO-GENERATED
|
metadata_types: # AUTO-GENERATED
|
||||||
- pkg.LinuxKernel
|
- pkg.LinuxKernel
|
||||||
- pkg.LinuxKernelModule
|
- pkg.LinuxKernelModule
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import (
|
|||||||
var _ pkg.Cataloger = (*linuxKernelCataloger)(nil)
|
var _ pkg.Cataloger = (*linuxKernelCataloger)(nil)
|
||||||
|
|
||||||
type LinuxKernelCatalogerConfig struct {
|
type LinuxKernelCatalogerConfig struct {
|
||||||
// CatalogModules enables cataloging linux kernel modules (`*.ko` files) in addition to the kernel itself.
|
// CatalogModules enables cataloging linux kernel modules (`*.ko` and compressed `*.ko.gz`, `*.ko.xz`, `*.ko.zst` files) in addition to the kernel itself.
|
||||||
// app-config: linux-kernel.catalog-modules
|
// app-config: linux-kernel.catalog-modules
|
||||||
CatalogModules bool `yaml:"catalog-modules" json:"catalog-modules" mapstructure:"catalog-modules"`
|
CatalogModules bool `yaml:"catalog-modules" json:"catalog-modules" mapstructure:"catalog-modules"`
|
||||||
}
|
}
|
||||||
@ -47,6 +47,9 @@ var kernelArchiveGlobs = []string{
|
|||||||
|
|
||||||
var kernelModuleGlobs = []string{
|
var kernelModuleGlobs = []string{
|
||||||
"**/lib/modules/**/*.ko",
|
"**/lib/modules/**/*.ko",
|
||||||
|
"**/lib/modules/**/*.ko.gz",
|
||||||
|
"**/lib/modules/**/*.ko.xz",
|
||||||
|
"**/lib/modules/**/*.ko.zst",
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLinuxKernelCataloger returns a new kernel files cataloger object.
|
// NewLinuxKernelCataloger returns a new kernel files cataloger object.
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
package kernel
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
"context"
|
"context"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"github.com/ulikunitz/xz"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/internal/unionreader"
|
"github.com/anchore/syft/syft/internal/unionreader"
|
||||||
@ -20,7 +26,13 @@ func parseLinuxKernelModuleFile(ctx context.Context, _ file.Resolver, _ *generic
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to get union reader for file: %w", err)
|
return nil, nil, fmt.Errorf("unable to get union reader for file: %w", err)
|
||||||
}
|
}
|
||||||
metadata, err := parseLinuxKernelModuleMetadata(unionReader)
|
|
||||||
|
moduleReader, err := decompressedModuleReader(reader.RealPath, unionReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to decompress kernel module %q: %w", reader.RealPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata, err := parseLinuxKernelModuleMetadata(moduleReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("unable to parse kernel module metadata: %w", err)
|
return nil, nil, fmt.Errorf("unable to parse kernel module metadata: %w", err)
|
||||||
}
|
}
|
||||||
@ -39,6 +51,61 @@ func parseLinuxKernelModuleFile(ctx context.Context, _ file.Resolver, _ *generic
|
|||||||
}, nil, nil
|
}, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decompressedModuleReader returns a UnionReader over the decompressed contents of the kernel module
|
||||||
|
// if the path indicates it is compressed (.ko.gz, .ko.xz, .ko.zst). For plain .ko files, the
|
||||||
|
// original reader is returned unchanged.
|
||||||
|
func decompressedModuleReader(path string, r unionreader.UnionReader) (unionreader.UnionReader, error) {
|
||||||
|
var decompressed []byte
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(path, ".ko.gz"):
|
||||||
|
gz, err := gzip.NewReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create gzip reader: %w", err)
|
||||||
|
}
|
||||||
|
defer gz.Close()
|
||||||
|
decompressed, err = io.ReadAll(gz)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decompress gzip stream: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case strings.HasSuffix(path, ".ko.xz"):
|
||||||
|
xzr, err := xz.NewReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create xz reader: %w", err)
|
||||||
|
}
|
||||||
|
decompressed, err = io.ReadAll(xzr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decompress xz stream: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case strings.HasSuffix(path, ".ko.zst"):
|
||||||
|
zstdr, err := zstd.NewReader(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create zstd reader: %w", err)
|
||||||
|
}
|
||||||
|
defer zstdr.Close()
|
||||||
|
decompressed, err = io.ReadAll(zstdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decompress zstd stream: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
br := bytes.NewReader(decompressed)
|
||||||
|
return struct {
|
||||||
|
io.ReadCloser
|
||||||
|
io.ReaderAt
|
||||||
|
io.Seeker
|
||||||
|
}{
|
||||||
|
ReadCloser: io.NopCloser(br),
|
||||||
|
ReaderAt: br,
|
||||||
|
Seeker: br,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseLinuxKernelModuleMetadata(r unionreader.UnionReader) (p *pkg.LinuxKernelModule, err error) {
|
func parseLinuxKernelModuleMetadata(r unionreader.UnionReader) (p *pkg.LinuxKernelModule, err error) {
|
||||||
// filename: /lib/modules/5.15.0-1031-aws/kernel/zfs/zzstd.ko
|
// filename: /lib/modules/5.15.0-1031-aws/kernel/zfs/zzstd.ko
|
||||||
// version: 1.4.5a
|
// version: 1.4.5a
|
||||||
|
|||||||
258
syft/pkg/cataloger/kernel/parse_linux_kernel_module_file_test.go
Normal file
258
syft/pkg/cataloger/kernel/parse_linux_kernel_module_file_test.go
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
package kernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/ulikunitz/xz"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// minimalKOBytes constructs a minimal ELF64 LE relocatable object with a .modinfo
|
||||||
|
// section containing the given null-terminated key=value entries.
|
||||||
|
func minimalKOBytes(entries []string) []byte {
|
||||||
|
// Build .modinfo section data: each entry is key=value\0
|
||||||
|
var modinfo []byte
|
||||||
|
for _, e := range entries {
|
||||||
|
modinfo = append(modinfo, []byte(e)...)
|
||||||
|
modinfo = append(modinfo, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section name string table: \0 .modinfo\0 .shstrtab\0
|
||||||
|
shstrtab := []byte("\x00.modinfo\x00.shstrtab\x00")
|
||||||
|
modinfoNameOff := uint32(1) // offset of ".modinfo" in shstrtab
|
||||||
|
shstrtabNameOff := uint32(10) // offset of ".shstrtab" in shstrtab
|
||||||
|
|
||||||
|
// ELF64 header is 64 bytes.
|
||||||
|
// We have 3 sections: null, .modinfo, .shstrtab
|
||||||
|
const (
|
||||||
|
elfHeaderSize = 64
|
||||||
|
sectionHdrSize = 64
|
||||||
|
numSections = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
modinfoOff := uint64(elfHeaderSize)
|
||||||
|
modinfoSize := uint64(len(modinfo))
|
||||||
|
|
||||||
|
shstrtabOff := modinfoOff + modinfoSize
|
||||||
|
shstrtabSize := uint64(len(shstrtab))
|
||||||
|
|
||||||
|
// Align section header table to 8 bytes
|
||||||
|
shdrsOff := shstrtabOff + shstrtabSize
|
||||||
|
if shdrsOff%8 != 0 {
|
||||||
|
shdrsOff += 8 - (shdrsOff % 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
le := binary.LittleEndian
|
||||||
|
|
||||||
|
// ELF header
|
||||||
|
buf.Write([]byte{0x7f, 'E', 'L', 'F'}) // magic
|
||||||
|
buf.WriteByte(2) // EI_CLASS: ELFCLASS64
|
||||||
|
buf.WriteByte(1) // EI_DATA: ELFDATA2LSB
|
||||||
|
buf.WriteByte(1) // EI_VERSION: EV_CURRENT
|
||||||
|
buf.WriteByte(0) // EI_OSABI
|
||||||
|
buf.Write(make([]byte, 8)) // EI_ABIVERSION + padding
|
||||||
|
|
||||||
|
writeU16 := func(v uint16) { binary.Write(buf, le, v) } //nolint:errcheck
|
||||||
|
writeU32 := func(v uint32) { binary.Write(buf, le, v) } //nolint:errcheck
|
||||||
|
writeU64 := func(v uint64) { binary.Write(buf, le, v) } //nolint:errcheck
|
||||||
|
|
||||||
|
writeU16(1) // e_type: ET_REL
|
||||||
|
writeU16(62) // e_machine: EM_X86_64
|
||||||
|
writeU32(1) // e_version: EV_CURRENT
|
||||||
|
writeU64(0) // e_entry
|
||||||
|
writeU64(0) // e_phoff (no program headers)
|
||||||
|
writeU64(shdrsOff) // e_shoff
|
||||||
|
writeU32(0) // e_flags
|
||||||
|
writeU16(elfHeaderSize) // e_ehsize
|
||||||
|
writeU16(0) // e_phentsize
|
||||||
|
writeU16(0) // e_phnum
|
||||||
|
writeU16(sectionHdrSize) // e_shentsize
|
||||||
|
writeU16(numSections) // e_shnum
|
||||||
|
writeU16(numSections - 1) // e_shstrndx (.shstrtab is last)
|
||||||
|
|
||||||
|
// Write section data
|
||||||
|
buf.Write(modinfo)
|
||||||
|
buf.Write(shstrtab)
|
||||||
|
|
||||||
|
// Pad to shdrsOff
|
||||||
|
for uint64(buf.Len()) < shdrsOff {
|
||||||
|
buf.WriteByte(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section header 0: null
|
||||||
|
buf.Write(make([]byte, sectionHdrSize))
|
||||||
|
|
||||||
|
// Section header 1: .modinfo (SHT_PROGBITS=1)
|
||||||
|
writeU32(modinfoNameOff) // sh_name
|
||||||
|
writeU32(1) // sh_type: SHT_PROGBITS
|
||||||
|
writeU64(0) // sh_flags
|
||||||
|
writeU64(0) // sh_addr
|
||||||
|
writeU64(modinfoOff) // sh_offset
|
||||||
|
writeU64(modinfoSize) // sh_size
|
||||||
|
writeU32(0) // sh_link
|
||||||
|
writeU32(0) // sh_info
|
||||||
|
writeU64(1) // sh_addralign
|
||||||
|
writeU64(0) // sh_entsize
|
||||||
|
|
||||||
|
// Section header 2: .shstrtab (SHT_STRTAB=3)
|
||||||
|
writeU32(shstrtabNameOff) // sh_name
|
||||||
|
writeU32(3) // sh_type: SHT_STRTAB
|
||||||
|
writeU64(0) // sh_flags
|
||||||
|
writeU64(0) // sh_addr
|
||||||
|
writeU64(shstrtabOff) // sh_offset
|
||||||
|
writeU64(shstrtabSize) // sh_size
|
||||||
|
writeU32(0) // sh_link
|
||||||
|
writeU32(0) // sh_info
|
||||||
|
writeU64(1) // sh_addralign
|
||||||
|
writeU64(0) // sh_entsize
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func gzCompress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w := gzip.NewWriter(&buf)
|
||||||
|
_, _ = w.Write(data)
|
||||||
|
_ = w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func xzCompress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := xz.NewWriter(&buf)
|
||||||
|
_, _ = w.Write(data)
|
||||||
|
_ = w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func zstCompress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := zstd.NewWriter(&buf)
|
||||||
|
_, _ = w.Write(data)
|
||||||
|
_ = w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeLocationReadCloser wraps a byte slice as a file.LocationReadCloser with the given path.
|
||||||
|
func makeLocationReadCloser(path string, data []byte) file.LocationReadCloser {
|
||||||
|
return file.LocationReadCloser{
|
||||||
|
Location: file.NewVirtualLocation(path, path),
|
||||||
|
ReadCloser: io.NopCloser(bytes.NewReader(data)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseLinuxKernelModuleFile_Compressed(t *testing.T) {
|
||||||
|
modinfo := []string{
|
||||||
|
"name=dummy_mod",
|
||||||
|
"version=1.2.3",
|
||||||
|
"vermagic=6.1.0-rc1 SMP mod_unload",
|
||||||
|
"license=GPL v2",
|
||||||
|
}
|
||||||
|
koBytes := minimalKOBytes(modinfo)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
data []byte
|
||||||
|
wantName string
|
||||||
|
wantVer string
|
||||||
|
wantKV string // expected KernelVersion from vermagic
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "uncompressed .ko",
|
||||||
|
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko",
|
||||||
|
data: koBytes,
|
||||||
|
wantName: "dummy_mod",
|
||||||
|
wantVer: "1.2.3",
|
||||||
|
wantKV: "6.1.0-rc1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "gzip-compressed .ko.gz",
|
||||||
|
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko.gz",
|
||||||
|
data: gzCompress(koBytes),
|
||||||
|
wantName: "dummy_mod",
|
||||||
|
wantVer: "1.2.3",
|
||||||
|
wantKV: "6.1.0-rc1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "xz-compressed .ko.xz",
|
||||||
|
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko.xz",
|
||||||
|
data: xzCompress(koBytes),
|
||||||
|
wantName: "dummy_mod",
|
||||||
|
wantVer: "1.2.3",
|
||||||
|
wantKV: "6.1.0-rc1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zstd-compressed .ko.zst",
|
||||||
|
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko.zst",
|
||||||
|
data: zstCompress(koBytes),
|
||||||
|
wantName: "dummy_mod",
|
||||||
|
wantVer: "1.2.3",
|
||||||
|
wantKV: "6.1.0-rc1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
reader := makeLocationReadCloser(tt.path, tt.data)
|
||||||
|
pkgs, rels, err := parseLinuxKernelModuleFile(context.Background(), nil, &generic.Environment{}, reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, pkgs, 1)
|
||||||
|
assert.Empty(t, rels)
|
||||||
|
assert.Equal(t, tt.wantName, pkgs[0].Name)
|
||||||
|
assert.Equal(t, tt.wantVer, pkgs[0].Version)
|
||||||
|
|
||||||
|
meta, ok := pkgs[0].Metadata.(pkg.LinuxKernelModule)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, tt.wantKV, meta.KernelVersion)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecompressedModuleReader(t *testing.T) {
|
||||||
|
koBytes := minimalKOBytes([]string{"name=test", "vermagic=5.15.0 SMP mod_unload"})
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
data []byte
|
||||||
|
}{
|
||||||
|
{"uncompressed", "/lib/modules/5.15.0/kernel/test.ko", koBytes},
|
||||||
|
{"gz", "/lib/modules/5.15.0/kernel/test.ko.gz", gzCompress(koBytes)},
|
||||||
|
{"xz", "/lib/modules/5.15.0/kernel/test.ko.xz", xzCompress(koBytes)},
|
||||||
|
{"zst", "/lib/modules/5.15.0/kernel/test.ko.zst", zstCompress(koBytes)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
wrapped := struct {
|
||||||
|
io.ReadCloser
|
||||||
|
io.ReaderAt
|
||||||
|
io.Seeker
|
||||||
|
}{
|
||||||
|
ReadCloser: io.NopCloser(bytes.NewReader(tt.data)),
|
||||||
|
ReaderAt: bytes.NewReader(tt.data),
|
||||||
|
Seeker: bytes.NewReader(tt.data),
|
||||||
|
}
|
||||||
|
got, err := decompressedModuleReader(tt.path, wrapped)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, got)
|
||||||
|
|
||||||
|
b, err := io.ReadAll(got)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, koBytes, b, "decompressed bytes should match original .ko bytes")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user