1 Commits
1.1 ... 1.0.2

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 202 additions and 320 deletions

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:
- .gitlab-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

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 ./...

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=

132
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,19 +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)
} }
// 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)
} }
} }
@@ -110,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
@@ -344,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
@@ -461,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
@@ -505,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
@@ -595,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 {
@@ -622,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) {
@@ -651,34 +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) {
var modNameReStr string log.Fatal("Kernel module.dep not found: ", modDir)
for _, s := range splitRe.Split(modName, -1) {
if modNameReStr != "" {
modNameReStr += "[-_]+" + s
} else {
modNameReStr = s
} }
}
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,53 +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/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
`
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"}},
}
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,126 +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
BootimgAppendSEAndroidEnforce string Deviceinfo_bootimg_append_seandroidenforce string
BootimgBlobpack string Deviceinfo_bootimg_blobpack string
BootimgDtbSecond string Deviceinfo_bootimg_dtb_second string
BootimgMtkMkimage string Deviceinfo_bootimg_mtk_mkimage string
BootimgPxa string Deviceinfo_bootimg_pxa string
BootimgQcdt string Deviceinfo_bootimg_qcdt string
Dtb string Deviceinfo_dtb string
FlashKernelOnUpdate string Deviceinfo_flash_offset_base string
FlashOffsetBase string Deviceinfo_flash_offset_kernel string
FlashOffsetKernel string Deviceinfo_flash_offset_ramdisk string
FlashOffsetRamdisk string Deviceinfo_flash_offset_second string
FlashOffsetSecond string Deviceinfo_flash_offset_tags string
FlashOffsetTags string Deviceinfo_flash_pagesize string
FlashPagesize string Deviceinfo_generate_bootimg string
GenerateBootimg string Deviceinfo_generate_legacy_uboot_initfs string
GenerateLegacyUbootInitfs string Deviceinfo_mesa_driver string
InitfsCompression string Deviceinfo_mkinitfs_postprocess string
KernelCmdline string Deviceinfo_initfs_compression string
LegacyUbootLoadAddress string Deviceinfo_kernel_cmdline string
MesaDriver string Deviceinfo_legacy_uboot_load_address string
MkinitfsPostprocess string Deviceinfo_modules_initfs string
ModulesInitfs string Deviceinfo_flash_kernel_on_update 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 (