1 Commits
1.4 ... 1.0.x

Author SHA1 Message Date
Clayton Craft
8f53926fb5 getInitfsModules: don't fail if kernel modules dir does not exist
This directory doesn't exist all the time, e.g. if the kernel was built
without modules or no modules were installed for some reason. Assume the
kernel package knows what it is doing and just print a message that
might be helpful if the kernel package ends up not knowing what it is
doing.
2021-09-05 14:23:58 -07:00
11 changed files with 211 additions and 415 deletions

View File

@@ -1,13 +0,0 @@
#!/bin/sh
echo "### Running gofmt..."
files="$(gofmt -l .)"
if [ ! -z "$files" ]; then
# run gofmt to print out the diff of what needs to be changed
gofmt -d -e .
exit 1
fi
echo "### Running staticcheck..."
staticcheck ./...

View File

@@ -1,7 +1,7 @@
--- ---
# global settings # global settings
image: alpine:edge image: alpine:latest
stages: stages:
- lint - lint
@@ -27,11 +27,9 @@ gofmt linting:
allow_failure: true allow_failure: true
<<: *only-default <<: *only-default
before_script: before_script:
# specific mirror used because staticcheck hasn't made it to the other mirrors yet... - apk -q add go
- apk -q update --repository http://dl-4.alpinelinux.org/alpine/edge/testing
- apk -q add --repository http://dl-4.alpinelinux.org/alpine/edge/testing go staticcheck
script: script:
- .ci/check_linting.sh - .gitlab-ci/check_gofmt.sh
build: build:
stage: build stage: build

11
.gitlab-ci/check_gofmt.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
files="$(gofmt -l .)"
[ -z "$files" ] && exit 0
# run gofmt to print out the diff of what needs to be changed
gofmt -d -e .
exit 1

2
go.mod
View File

@@ -3,6 +3,8 @@ module gitlab.com/postmarketOS/postmarketos-mkinitfs
go 1.16 go 1.16
require ( require (
git.sr.ht/~sircmpwn/getopt v0.0.0-20201218204720-9961a9c6298f
github.com/BurntSushi/toml v0.4.0
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e
github.com/klauspost/compress v1.13.3 // indirect github.com/klauspost/compress v1.13.3 // indirect
github.com/klauspost/pgzip v1.2.5 github.com/klauspost/pgzip v1.2.5

21
go.sum
View File

@@ -1,9 +1,30 @@
git.sr.ht/~sircmpwn/getopt v0.0.0-20201218204720-9961a9c6298f h1:f5axCdaRzGDCihN3o1Lq0ydn0VlkhY+11G0JOyY5qss=
git.sr.ht/~sircmpwn/getopt v0.0.0-20201218204720-9961a9c6298f/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
github.com/BurntSushi/toml v0.3.2-0.20210614224209-34d990aa228d/go.mod h1:2QZjSXA5e+XyFeCAxxtL8Z4StYUsTquL8ODGPR3C3MA=
github.com/BurntSushi/toml v0.3.2-0.20210621044154-20a94d639b8e/go.mod h1:t4zg8TkHfP16Vb3x4WKIw7zVYMit5QFtPEO8lOWxzTg=
github.com/BurntSushi/toml v0.3.2-0.20210624061728-01bfc69d1057/go.mod h1:NMj2lD5LfMqcE0w8tnqOsH6944oaqpI1974lrIwerfE=
github.com/BurntSushi/toml v0.3.2-0.20210704081116-ccff24ee4463/go.mod h1:EkRrMiQQmfxK6kIldz3QbPlhmVkrjW1RDJUnbDqGYvc=
github.com/BurntSushi/toml v0.4.0 h1:qD/r9AL67srjW6O3fcSKZDsXqzBNX6ieSRywr2hRrdE=
github.com/BurntSushi/toml v0.4.0/go.mod h1:wtejDu7Q0FhCWAo2aXkywSJyYFg01EDTKozLNCz2JBA=
github.com/BurntSushi/toml-test v0.1.1-0.20210620192437-de01089bbf76/go.mod h1:P/PrhmZ37t5llHfDuiouWXtFgqOoQ12SAh9j6EjrBR4=
github.com/BurntSushi/toml-test v0.1.1-0.20210624055653-1f6389604dc6/go.mod h1:UAIt+Eo8itMZAAgImXkPGDMYsT1SsJkVdB5TuONl86A=
github.com/BurntSushi/toml-test v0.1.1-0.20210704062846-269931e74e3f/go.mod h1:fnFWrIwqgHsEjVsW3RYCJmDo86oq9eiJ9u6bnqhtm2g=
github.com/BurntSushi/toml-test v0.1.1-0.20210723065233-facb9eccd4da h1:2QGUaQtV2u8V1USTI883wo+uxtZFAiZ4TCNupHJ98IU=
github.com/BurntSushi/toml-test v0.1.1-0.20210723065233-facb9eccd4da/go.mod h1:ve9Q/RRu2vHi42LocPLNvagxuUJh993/95b18bw/Nws=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/klauspost/compress v1.13.3 h1:BtAvtV1+h0YwSVwWoYXMREPpYu9VzTJ9QDI1TEg/iQQ= github.com/klauspost/compress v1.13.3 h1:BtAvtV1+h0YwSVwWoYXMREPpYu9VzTJ9QDI1TEg/iQQ=
github.com/klauspost/compress v1.13.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
zgo.at/zli v0.0.0-20210619044753-e7020a328e59/go.mod h1:HLAc12TjNGT+VRXr76JnsNE3pbooQtwKWhX+RlDjQ2Y=

231
main.go
View File

@@ -1,13 +1,11 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net> // Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package main package main
import ( import (
"bufio" "bufio"
"debug/elf" "debug/elf"
"errors" "errors"
"flag"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@@ -15,10 +13,10 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
"time" "time"
"git.sr.ht/~sircmpwn/getopt"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/archive" "gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/archive"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/deviceinfo" "gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/deviceinfo"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/misc" "gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/misc"
@@ -30,22 +28,21 @@ func timeFunc(start time.Time, name string) {
} }
func main() { func main() {
deviceinfoFile := "/etc/deviceinfo" devinfo, err := deviceinfo.ReadDeviceinfo()
if !exists(deviceinfoFile) { if err != nil {
log.Print("NOTE: deviceinfo (from device package) not installed yet, " + log.Print("NOTE: deviceinfo (from device package) not installed yet, " +
"not building the initramfs now (it should get built later " + "not building the initramfs now (it should get built later " +
"automatically.)") "automatically.)")
return os.Exit(0)
} }
devinfo, err := deviceinfo.ReadDeviceinfo(deviceinfoFile) var outDir string
if err != nil { getopt.StringVar(&outDir, "d", "/boot", "Directory to output initfs(-extra) and other boot files, default: /boot")
if err := getopt.Parse(); err != nil {
log.Fatal(err) log.Fatal(err)
} }
outDir := flag.String("d", "/boot", "Directory to output initfs(-extra) and other boot files")
flag.Parse()
defer timeFunc(time.Now(), "mkinitfs") defer timeFunc(time.Now(), "mkinitfs")
kernVer, err := getKernelVersion() kernVer, err := getKernelVersion()
@@ -53,6 +50,10 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
if err != nil {
log.Fatal(err)
}
// temporary working dir // temporary working dir
workDir, err := ioutil.TempDir("", "mkinitfs") workDir, err := ioutil.TempDir("", "mkinitfs")
if err != nil { if err != nil {
@@ -61,27 +62,19 @@ func main() {
defer os.RemoveAll(workDir) defer os.RemoveAll(workDir)
log.Print("Generating for kernel version: ", kernVer) log.Print("Generating for kernel version: ", kernVer)
log.Print("Output directory: ", *outDir) log.Print("Output directory: ", outDir)
if err := generateInitfs("initramfs", workDir, kernVer, devinfo); err != nil { if err := generateInitfs("initramfs", workDir, kernVer, devinfo); err != nil {
log.Fatal("generateInitfs: ", err) log.Fatal(err)
} }
if err := generateInitfsExtra("initramfs-extra", workDir, devinfo); err != nil { if err := generateInitfsExtra("initramfs-extra", workDir, devinfo); err != nil {
log.Fatal("generateInitfsExtra: ", err) log.Fatal(err)
}
if err := copyUbootFiles(workDir, devinfo); errors.Is(err, os.ErrNotExist) {
log.Println("u-boot files copying skipped: ", err)
} else {
if err != nil {
log.Fatal("copyUbootFiles: ", err)
}
} }
// Final processing of initramfs / kernel is done by boot-deploy // Final processing of initramfs / kernel is done by boot-deploy
if err := bootDeploy(workDir, *outDir); err != nil { if err := bootDeploy(workDir, outDir); err != nil {
log.Fatal("bootDeploy: ", err) log.Fatal(err)
} }
} }
@@ -94,29 +87,18 @@ func bootDeploy(workDir string, outDir string) error {
if len(kernels) == 0 { if len(kernels) == 0 {
return errors.New("Unable to find any kernels at " + filepath.Join(outDir, "vmlinuz*")) return errors.New("Unable to find any kernels at " + filepath.Join(outDir, "vmlinuz*"))
} }
kernFile, err := os.Open(kernels[0])
// Pick a kernel that does not have suffixes added by boot-deploy
var kernFile string
for _, f := range kernels {
if strings.HasSuffix(f, "-dtb") || strings.HasSuffix(f, "-mtk") {
continue
}
kernFile = f
break
}
kernFd, err := os.Open(kernFile)
if err != nil { if err != nil {
return err return err
} }
defer kernFd.Close() defer kernFile.Close()
kernFileCopy, err := os.Create(filepath.Join(workDir, "vmlinuz")) kernFileCopy, err := os.Create(filepath.Join(workDir, "vmlinuz"))
if err != nil { if err != nil {
return err return err
} }
if _, err = io.Copy(kernFileCopy, kernFd); err != nil { if _, err = io.Copy(kernFileCopy, kernFile); err != nil {
return err return err
} }
kernFileCopy.Close() kernFileCopy.Close()
@@ -129,19 +111,33 @@ func bootDeploy(workDir string, outDir string) error {
"-o", outDir, "-o", outDir,
"initramfs-extra") "initramfs-extra")
if !exists(cmd.Path) { if !exists(cmd.Path) {
return errors.New("boot-deploy command not found") return errors.New("boot-deploy command not found.")
} }
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
// err is ignored, since shellcheck will return != 0 if there are issues
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
log.Print("'boot-deploy' command failed") log.Print("'boot-deploy' command failed: ")
return err return err
} }
return nil return nil
} }
func createInitfsRootDirs(initfsRoot string) {
dirs := []string{
"/bin", "/sbin", "/usr/bin", "/usr/lib", "/usr/sbin", "/proc", "/sys",
"/dev", "/tmp", "/lib", "/boot", "/sysroot", "/etc",
}
for _, dir := range dirs {
if err := os.MkdirAll(filepath.Join(initfsRoot, dir), os.FileMode(0775)); err != nil {
log.Fatal(err)
}
}
}
func exists(file string) bool { func exists(file string) bool {
if _, err := os.Stat(file); err == nil { if _, err := os.Stat(file); err == nil {
return true return true
@@ -164,9 +160,10 @@ func getHookFiles(filesdir string) misc.StringSet {
defer f.Close() defer f.Close()
s := bufio.NewScanner(f) s := bufio.NewScanner(f)
for s.Scan() { for s.Scan() {
if err := getFile(files, s.Text(), true); err != nil { if !exists(s.Text()) {
log.Fatalf("Unable to find file %q required by %q", s.Text(), path) log.Fatalf("Unable to find file %q required by %q", s.Text(), path)
} }
files[s.Text()] = false
} }
if err := s.Err(); err != nil { if err := s.Err(); err != nil {
log.Fatal(err) log.Fatal(err)
@@ -250,42 +247,13 @@ func getFiles(files misc.StringSet, newFiles misc.StringSet, required bool) erro
} }
func getFile(files misc.StringSet, file string, required bool) error { func getFile(files misc.StringSet, file string, required bool) error {
// Expand glob expression if !exists(file) {
expanded, _ := filepath.Glob(file)
if len(expanded) > 0 && expanded[0] != file {
for _, path := range expanded {
if err := getFile(files, path, required); err != nil {
return err
}
}
return nil
}
fileInfo, err := os.Stat(file)
if err != nil {
if required { if required {
return errors.New("getFile: File does not exist :" + file) return errors.New("getFile: File does not exist :" + file)
} }
return nil return nil
} }
if fileInfo.IsDir() {
// Recurse over directory contents
err := filepath.Walk(file, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if f.IsDir() {
return nil
}
return getFile(files, path, required)
})
if err != nil {
return err
}
return nil
}
files[file] = false files[file] = false
// get dependencies for binaries // get dependencies for binaries
@@ -294,7 +262,8 @@ func getFile(files misc.StringSet, file string, required bool) error {
return nil return nil
} }
if err := getBinaryDeps(files, file); err != nil { err := getBinaryDeps(files, file)
if err != nil {
return err return err
} }
@@ -390,13 +359,13 @@ func getFdeFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) error {
} }
// mesa hw accel // mesa hw accel
if devinfo.MesaDriver != "" { if devinfo.Deviceinfo_mesa_driver != "" {
mesaFiles := misc.StringSet{ mesaFiles := misc.StringSet{
"/usr/lib/libEGL.so.1": false, "/usr/lib/libEGL.so.1": false,
"/usr/lib/libGLESv2.so.2": false, "/usr/lib/libGLESv2.so.2": false,
"/usr/lib/libgbm.so.1": false, "/usr/lib/libgbm.so.1": false,
"/usr/lib/libudev.so.1": false, "/usr/lib/libudev.so.1": false,
"/usr/lib/xorg/modules/dri/" + devinfo.MesaDriver + "_dri.so": false, "/usr/lib/xorg/modules/dri/" + devinfo.Deviceinfo_mesa_driver + "_dri.so": false,
} }
if err := getFiles(files, mesaFiles, true); err != nil { if err := getFiles(files, mesaFiles, true); err != nil {
return err return err
@@ -417,7 +386,6 @@ func getInitfsExtraFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) er
log.Println("== Generating initramfs extra ==") log.Println("== Generating initramfs extra ==")
binariesExtra := misc.StringSet{ binariesExtra := misc.StringSet{
"/lib/libz.so.1": false, "/lib/libz.so.1": false,
"/sbin/btrfs": false,
"/sbin/dmsetup": false, "/sbin/dmsetup": false,
"/sbin/e2fsck": false, "/sbin/e2fsck": false,
"/usr/sbin/parted": false, "/usr/sbin/parted": false,
@@ -450,7 +418,6 @@ func getInitfsFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) error {
"/usr/sbin/telnetd": false, "/usr/sbin/telnetd": false,
"/sbin/kpartx": false, "/sbin/kpartx": false,
"/etc/deviceinfo": false, "/etc/deviceinfo": false,
"/usr/bin/unudhcpd": false,
} }
// Hook files & scripts // Hook files & scripts
@@ -509,19 +476,21 @@ func getInitfsModules(files misc.StringSet, devinfo deviceinfo.DeviceInfo, kerne
return err return err
} }
} }
continue
} else if dir == "" { } else if dir == "" {
// item is a module name // item is a module name
if err := getModule(files, file, modDir); err != nil { if err := getModule(files, file, modDir); err != nil {
log.Print("Unable to get module: ", file) log.Print("Unable to get module: ", file)
return err return err
} }
continue
} else { } else {
log.Printf("Unknown module entry: %q", item) log.Printf("Unknown module entry: %q", item)
} }
} }
// deviceinfo modules // deviceinfo modules
for _, module := range strings.Fields(devinfo.ModulesInitfs) { for _, module := range strings.Fields(devinfo.Deviceinfo_modules_initfs) {
if err := getModule(files, module, modDir); err != nil { if err := getModule(files, module, modDir); err != nil {
log.Print("Unable to get modules from deviceinfo") log.Print("Unable to get modules from deviceinfo")
return err return err
@@ -553,7 +522,7 @@ func getKernelReleaseFile() (string, error) {
files, _ := filepath.Glob("/usr/share/kernel/*/kernel.release") files, _ := filepath.Glob("/usr/share/kernel/*/kernel.release")
// only one kernel flavor supported // only one kernel flavor supported
if len(files) != 1 { if len(files) != 1 {
return "", fmt.Errorf("only one kernel release/flavor is supported, found: %q", files) return "", errors.New(fmt.Sprintf("Only one kernel release/flavor is supported, found: %q", files))
} }
return files[0], nil return files[0], nil
@@ -575,46 +544,6 @@ func getKernelVersion() (string, error) {
return strings.TrimSpace(string(contents)), nil return strings.TrimSpace(string(contents)), nil
} }
func Copy(srcFile, dstFile string) error {
out, err := os.Create(dstFile)
if err != nil {
return err
}
defer out.Close()
in, err := os.Open(srcFile)
if err != nil {
return err
}
defer in.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return nil
}
func copyUbootFiles(path string, devinfo deviceinfo.DeviceInfo) error {
srcDir := filepath.Join("/usr/share/u-boot", devinfo.UbootBoardname)
entries, err := ioutil.ReadDir(srcDir)
if err != nil {
return err
}
for _, entry := range entries {
sourcePath := filepath.Join(srcDir, entry.Name())
destPath := filepath.Join(path, entry.Name())
if err := Copy(sourcePath, destPath); err != nil {
return err
}
}
return nil
}
func generateInitfs(name string, path string, kernVer string, devinfo deviceinfo.DeviceInfo) error { func generateInitfs(name string, path string, kernVer string, devinfo deviceinfo.DeviceInfo) error {
initfsArchive, err := archive.New() initfsArchive, err := archive.New()
if err != nil { if err != nil {
@@ -683,7 +612,13 @@ func generateInitfsExtra(name string, path string, devinfo deviceinfo.DeviceInfo
} }
func stripExts(file string) string { func stripExts(file string) string {
return strings.Split(file, ".")[0] for {
if filepath.Ext(file) == "" {
break
}
file = strings.TrimSuffix(file, filepath.Ext(file))
}
return file
} }
func getModulesInDir(files misc.StringSet, modPath string) error { func getModulesInDir(files misc.StringSet, modPath string) error {
@@ -710,23 +645,24 @@ func getModulesInDir(files misc.StringSet, modPath string) error {
// anywhere // anywhere
func getModule(files misc.StringSet, modName string, modDir string) error { func getModule(files misc.StringSet, modName string, modDir string) error {
modDep := filepath.Join(modDir, "modules.dep") deps, err := getModuleDeps(modName, modDir)
if !exists(modDep) {
log.Fatal("Kernel module.dep not found: ", modDir)
}
fd, err := os.Open(modDep)
if err != nil {
log.Print("Unable to open modules.dep: ", modDep)
return err
}
defer fd.Close()
deps, err := getModuleDeps(modName, fd)
if err != nil { if err != nil {
return err return err
} }
if len(deps) == 0 {
// retry and swap - and _ in module name
if strings.Contains(modName, "-") {
modName = strings.ReplaceAll(modName, "-", "_")
} else {
modName = strings.ReplaceAll(modName, "_", "-")
}
deps, err = getModuleDeps(modName, modDir)
if err != nil {
return err
}
}
for _, dep := range deps { for _, dep := range deps {
p := filepath.Join(modDir, dep) p := filepath.Join(modDir, dep)
if !exists(p) { if !exists(p) {
@@ -739,27 +675,30 @@ func getModule(files misc.StringSet, modName string, modDir string) error {
return err return err
} }
// Get the canonicalized name for the module as represented in the given modules.dep io.reader func getModuleDeps(modName string, modDir string) ([]string, error) {
func getModuleDeps(modName string, modulesDep io.Reader) ([]string, error) {
var deps []string var deps []string
// split the module name on - and/or _, build a regex for matching modDep := filepath.Join(modDir, "modules.dep")
splitRe := regexp.MustCompile("[-_]+") if !exists(modDep) {
modNameReStr := splitRe.ReplaceAllString(modName, "[-_]+") log.Fatal("Kernel module.dep not found: ", modDir)
re := regexp.MustCompile("^" + modNameReStr + "$") }
s := bufio.NewScanner(modulesDep) fd, err := os.Open(modDep)
if err != nil {
log.Print("Unable to open modules.dep: ", modDep)
return deps, err
}
defer fd.Close()
s := bufio.NewScanner(fd)
for s.Scan() { for s.Scan() {
fields := strings.Fields(s.Text()) fields := strings.Fields(s.Text())
if len(fields) == 0 { fields[0] = strings.TrimSuffix(fields[0], ":")
if modName != filepath.Base(stripExts(fields[0])) {
continue continue
} }
fields[0] = strings.TrimSuffix(fields[0], ":") for _, modPath := range fields {
deps = append(deps, modPath)
found := re.FindAll([]byte(filepath.Base(stripExts(fields[0]))), -1)
if len(found) > 0 {
deps = append(deps, fields...)
break
} }
} }
if err := s.Err(); err != nil { if err := s.Err(); err != nil {

View File

@@ -1,10 +1,8 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net> // Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package main package main
import ( import (
"strings"
"testing" "testing"
) )
@@ -26,57 +24,3 @@ func TestStripExts(t *testing.T) {
} }
} }
} }
func stringSlicesEqual(a []string, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
var testModuleDep string = `
kernel/sound/soc/codecs/snd-soc-msm8916-digital.ko:
kernel/net/sched/act_ipt.ko.xz: kernel/net/netfilter/x_tables.ko.xz
kernel/drivers/watchdog/watchdog.ko.xz:
kernel/drivers/usb/serial/ir-usb.ko.xz: kernel/drivers/usb/serial/usbserial.ko.xz
kernel/drivers/gpu/drm/scheduler/gpu-sched.ko.xz:
kernel/drivers/hid/hid-alps.ko.xz:
kernel/net/netfilter/xt_u32.ko.xz: kernel/net/netfilter/x_tables.ko.xz
kernel/net/netfilter/xt_sctp.ko.xz: kernel/net/netfilter/x_tables.ko.xz
kernel/drivers/hwmon/gl518sm.ko.xz:
kernel/drivers/watchdog/dw_wdt.ko.xz: kernel/drivers/watchdog/watchdog.ko.xz
kernel/net/bluetooth/hidp/hidp.ko.xz: kernel/net/bluetooth/bluetooth.ko.xz kernel/net/rfkill/rfkill.ko.xz kernel/crypto/ecdh_generic.ko.xz kernel/crypto/ecc.ko.xz
kernel/fs/nls/nls_iso8859-1.ko.xz:
kernel/net/vmw_vsock/vmw_vsock_virtio_transport.ko.xz: kernel/net/vmw_vsock/vmw_vsock_virtio_transport_common.ko.xz kernel/drivers/virtio/virtio.ko.xz kernel/drivers/virtio/virtio_ring.ko.xz kernel/net/vmw_vsock/vsock.ko.xz
kernel/drivers/gpu/drm/panfrost/panfrost.ko.xz: kernel/drivers/gpu/drm/scheduler/gpu-sched.ko.xz
kernel/drivers/gpu/drm/msm/msm.ko: kernel/drivers/gpu/drm/drm_kms_helper.ko
`
func TestGetModuleDeps(t *testing.T) {
tables := []struct {
in string
expected []string
}{
{"nls-iso8859-1", []string{"kernel/fs/nls/nls_iso8859-1.ko.xz"}},
{"gpu_sched", []string{"kernel/drivers/gpu/drm/scheduler/gpu-sched.ko.xz"}},
{"dw-wdt", []string{"kernel/drivers/watchdog/dw_wdt.ko.xz",
"kernel/drivers/watchdog/watchdog.ko.xz"}},
{"gl518sm", []string{"kernel/drivers/hwmon/gl518sm.ko.xz"}},
{"msm", []string{"kernel/drivers/gpu/drm/msm/msm.ko",
"kernel/drivers/gpu/drm/drm_kms_helper.ko"}},
}
for _, table := range tables {
out, err := getModuleDeps(table.in, strings.NewReader(testModuleDep))
if err != nil {
t.Errorf("unexpected error with input: %q, error: %q", table.expected, err)
}
if !stringSlicesEqual(out, table.expected) {
t.Errorf("Expected: %q, got: %q", table.expected, out)
}
}
}

View File

@@ -1,17 +1,19 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net> // Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package archive package archive
import ( import (
"bytes" "bytes"
"compress/flate" "compress/flate"
"crypto/sha256"
"encoding/hex"
"github.com/cavaliercoder/go-cpio" "github.com/cavaliercoder/go-cpio"
"github.com/klauspost/pgzip" "github.com/klauspost/pgzip"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/misc" "gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/misc"
"io" "io"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
) )
@@ -50,6 +52,12 @@ func (archive *Archive) Write(path string, mode os.FileMode) error {
return err return err
} }
// test the archive to make sure it's valid
if err := test(path); err != nil {
log.Print("Verification of archive failed!")
return err
}
if err := os.Chmod(path, mode); err != nil { if err := os.Chmod(path, mode); err != nil {
return err return err
} }
@@ -57,6 +65,38 @@ func (archive *Archive) Write(path string, mode os.FileMode) error {
return nil return nil
} }
func checksum(path string) (string, error) {
var sum string
buf := make([]byte, 64*1024)
sha256 := sha256.New()
fd, err := os.Open(path)
defer fd.Close()
if err != nil {
log.Print("Unable to checksum: ", path)
return sum, err
}
// Read file in chunks
for {
bytes, err := fd.Read(buf)
if bytes > 0 {
_, err := sha256.Write(buf[:bytes])
if err != nil {
log.Print("Unable to checksum: ", path)
return sum, err
}
}
if err == io.EOF {
break
}
}
sum = hex.EncodeToString(sha256.Sum(nil))
return sum, nil
}
func (archive *Archive) AddFile(file string, dest string) error { func (archive *Archive) AddFile(file string, dest string) error {
if err := archive.addDir(filepath.Dir(dest)); err != nil { if err := archive.addDir(filepath.Dir(dest)); err != nil {
return err return err
@@ -104,9 +144,6 @@ func (archive *Archive) AddFile(file string, dest string) error {
// make sure target is an absolute path // make sure target is an absolute path
if !filepath.IsAbs(target) { if !filepath.IsAbs(target) {
target, err = misc.RelativeSymlinkTargetToDir(target, filepath.Dir(file)) target, err = misc.RelativeSymlinkTargetToDir(target, filepath.Dir(file))
if err != nil {
return err
}
} }
// TODO: add verbose mode, print stuff like this: // TODO: add verbose mode, print stuff like this:
// log.Printf("symlink: %q, target: %q", file, target) // log.Printf("symlink: %q, target: %q", file, target)
@@ -143,6 +180,19 @@ func (archive *Archive) AddFile(file string, dest string) error {
return nil return nil
} }
// Use busybox gzip to test archive
func test(path string) error {
cmd := exec.Command("busybox", "gzip", "-t", path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Print("'boot-deploy' command failed: ")
return err
}
return nil
}
func (archive *Archive) writeCompressed(path string, mode os.FileMode) error { func (archive *Archive) writeCompressed(path string, mode os.FileMode) error {
// TODO: support other compression formats, based on deviceinfo // TODO: support other compression formats, based on deviceinfo
fd, err := os.Create(path) fd, err := os.Create(path)

View File

@@ -1,127 +1,53 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net> // Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package deviceinfo package deviceinfo
import ( import (
"bufio" "errors"
"fmt" "github.com/BurntSushi/toml"
"io"
"log"
"os" "os"
"reflect"
"strings"
) )
// Note: fields must be exported (start with capital letter)
// https://github.com/BurntSushi/toml/issues/121
type DeviceInfo struct { type DeviceInfo struct {
AppendDtb string Deviceinfo_append_dtb string
Arch string Deviceinfo_arch string
UbootBoardname string Deviceinfo_bootimg_append_seandroidenforce string
BootimgAppendSEAndroidEnforce string Deviceinfo_bootimg_blobpack string
BootimgBlobpack string Deviceinfo_bootimg_dtb_second string
BootimgDtbSecond string Deviceinfo_bootimg_mtk_mkimage string
BootimgMtkMkimage string Deviceinfo_bootimg_pxa string
BootimgPxa string Deviceinfo_bootimg_qcdt string
BootimgQcdt string Deviceinfo_dtb string
Dtb string Deviceinfo_flash_offset_base string
FlashKernelOnUpdate string Deviceinfo_flash_offset_kernel string
FlashOffsetBase string Deviceinfo_flash_offset_ramdisk string
FlashOffsetKernel string Deviceinfo_flash_offset_second string
FlashOffsetRamdisk string Deviceinfo_flash_offset_tags string
FlashOffsetSecond string Deviceinfo_flash_pagesize string
FlashOffsetTags string Deviceinfo_generate_bootimg string
FlashPagesize string Deviceinfo_generate_legacy_uboot_initfs string
GenerateBootimg string Deviceinfo_mesa_driver string
GenerateLegacyUbootInitfs string Deviceinfo_mkinitfs_postprocess string
InitfsCompression string Deviceinfo_initfs_compression string
KernelCmdline string Deviceinfo_kernel_cmdline string
LegacyUbootLoadAddress string Deviceinfo_legacy_uboot_load_address string
MesaDriver string Deviceinfo_modules_initfs string
MkinitfsPostprocess string Deviceinfo_flash_kernel_on_update string
ModulesInitfs string
} }
func ReadDeviceinfo(file string) (DeviceInfo, error) { func ReadDeviceinfo() (DeviceInfo, error) {
file := "/etc/deviceinfo"
var deviceinfo DeviceInfo var deviceinfo DeviceInfo
fd, err := os.Open(file) _, err := os.Stat(file)
if err != nil { if err != nil {
return deviceinfo, err return deviceinfo, errors.New("Unable to find deviceinfo: " + file)
}
defer fd.Close()
if err := unmarshal(fd, &deviceinfo); err != nil {
return deviceinfo, err
} }
if _, err := toml.DecodeFile(file, &deviceinfo); err != nil {
return deviceinfo, err
}
return deviceinfo, nil return deviceinfo, nil
} }
// Unmarshals a deviceinfo into a DeviceInfo struct
func unmarshal(r io.Reader, devinfo *DeviceInfo) error {
s := bufio.NewScanner(r)
for s.Scan() {
line := s.Text()
if strings.HasPrefix(line, "#") {
continue
}
// line isn't setting anything, so just ignore it
if !strings.Contains(line, "=") {
continue
}
// sometimes line has a comment at the end after setting an option
line = strings.SplitN(line, "#", 2)[0]
line = strings.TrimSpace(line)
// must support having '=' in the value (e.g. kernel cmdline)
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
return fmt.Errorf("error parsing deviceinfo line, invalid format: %s", line)
}
name, val := parts[0], parts[1]
val = strings.ReplaceAll(val, "\"", "")
if name == "deviceinfo_format_version" && val != "0" {
return fmt.Errorf("deviceinfo format version %q is not supported", val)
}
fieldName := nameToField(name)
if fieldName == "" {
return fmt.Errorf("error parsing deviceinfo line, invalid format: %s", line)
}
field := reflect.ValueOf(devinfo).Elem().FieldByName(fieldName)
if !field.IsValid() {
// an option that meets the deviceinfo "specification", but isn't
// one we care about in this module
continue
}
field.SetString(val)
}
if err := s.Err(); err != nil {
log.Print("unable to parse deviceinfo: ", err)
return err
}
return nil
}
// Convert string into the string format used for DeviceInfo fields.
// Note: does not test that the resulting field name is a valid field in the
// DeviceInfo struct!
func nameToField(name string) string {
var field string
parts := strings.Split(name, "_")
for _, p := range parts {
if p == "deviceinfo" {
continue
}
field = field + strings.Title(p)
}
return field
}

View File

@@ -1,81 +0,0 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later
package deviceinfo
import (
"fmt"
"reflect"
"strings"
"testing"
)
// Test conversion of name to DeviceInfo struct field format
func TestNameToField(t *testing.T) {
tables := []struct {
in string
expected string
}{
{"deviceinfo_dtb", "Dtb"},
{"dtb", "Dtb"},
{"deviceinfo_modules_initfs", "ModulesInitfs"},
{"modules_initfs", "ModulesInitfs"},
{"deviceinfo_modules_initfs___", "ModulesInitfs"},
}
for _, table := range tables {
out := nameToField(table.in)
if out != table.expected {
t.Errorf("expected: %q, got: %q", table.expected, out)
}
}
}
// Test unmarshalling with lines in deviceinfo
func TestUnmarshal(t *testing.T) {
tables := []struct {
// field is just used for reflection within the test, so it must be a
// valid DeviceInfo field
field string
in string
expected string
}{
{"ModulesInitfs", "deviceinfo_modules_initfs=\"panfrost foo bar bazz\"\n", "panfrost foo bar bazz"},
{"ModulesInitfs", "deviceinfo_modules_initfs=\"panfrost foo bar bazz\"", "panfrost foo bar bazz"},
// line with multiple '='
{"KernelCmdline",
"deviceinfo_kernel_cmdline=\"PMOS_NO_OUTPUT_REDIRECT fw_devlink=off nvme_core.default_ps_max_latency_us=5500 pcie_aspm.policy=performance\"\n",
"PMOS_NO_OUTPUT_REDIRECT fw_devlink=off nvme_core.default_ps_max_latency_us=5500 pcie_aspm.policy=performance"},
// empty option
{"ModulesInitfs", "deviceinfo_modules_initfs=\"\"\n", ""},
{"Dtb", "deviceinfo_dtb=\"freescale/imx8mq-librem5-r2 freescale/imx8mq-librem5-r3 freescale/imx8mq-librem5-r4\"\n",
"freescale/imx8mq-librem5-r2 freescale/imx8mq-librem5-r3 freescale/imx8mq-librem5-r4"},
// valid deviceinfo line, just not used in this module
{"", "deviceinfo_codename=\"pine64-pinebookpro\"", ""},
// line with comment at the end
{"MesaDriver", "deviceinfo_mesa_driver=\"panfrost\" # this is a nice driver", "panfrost"},
{"", "# this is a comment!\n", ""},
// empty lines are fine
{"", "", ""},
// line with whitepace characters only
{"", " \t \n\r", ""},
}
var d DeviceInfo
for _, table := range tables {
testName := fmt.Sprintf("unmarshal::'%s':", strings.ReplaceAll(table.in, "\n", "\\n"))
if err := unmarshal(strings.NewReader(table.in), &d); err != nil {
t.Errorf("%s received an unexpected err: ", err)
}
// Check against expected value
field := reflect.ValueOf(&d).Elem().FieldByName(table.field)
out := ""
if table.field != "" {
out = field.String()
}
if out != table.expected {
t.Errorf("%s expected: %q, got: %q", testName, table.expected, out)
}
}
}

View File

@@ -1,6 +1,5 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net> // Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package misc package misc
import ( import (