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
image: alpine:edge
image: alpine:latest
stages:
- lint
@@ -27,11 +27,9 @@ gofmt linting:
allow_failure: true
<<: *only-default
before_script:
# specific mirror used because staticcheck hasn't made it to the other mirrors yet...
- 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
- apk -q add go
script:
- .ci/check_linting.sh
- .gitlab-ci/check_gofmt.sh
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
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/klauspost/compress v1.13.3 // indirect
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/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/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/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
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/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>
// SPDX-License-Identifier: GPL-3.0-or-later
package main
import (
"bufio"
"debug/elf"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
@@ -15,10 +13,10 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
"git.sr.ht/~sircmpwn/getopt"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/archive"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/deviceinfo"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/misc"
@@ -30,22 +28,21 @@ func timeFunc(start time.Time, name string) {
}
func main() {
deviceinfoFile := "/etc/deviceinfo"
if !exists(deviceinfoFile) {
devinfo, err := deviceinfo.ReadDeviceinfo()
if err != nil {
log.Print("NOTE: deviceinfo (from device package) not installed yet, " +
"not building the initramfs now (it should get built later " +
"automatically.)")
return
os.Exit(0)
}
devinfo, err := deviceinfo.ReadDeviceinfo(deviceinfoFile)
if err != nil {
var outDir string
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)
}
outDir := flag.String("d", "/boot", "Directory to output initfs(-extra) and other boot files")
flag.Parse()
defer timeFunc(time.Now(), "mkinitfs")
kernVer, err := getKernelVersion()
@@ -53,6 +50,10 @@ func main() {
log.Fatal(err)
}
if err != nil {
log.Fatal(err)
}
// temporary working dir
workDir, err := ioutil.TempDir("", "mkinitfs")
if err != nil {
@@ -61,27 +62,19 @@ func main() {
defer os.RemoveAll(workDir)
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 {
log.Fatal("generateInitfs: ", err)
log.Fatal(err)
}
if err := generateInitfsExtra("initramfs-extra", workDir, devinfo); err != nil {
log.Fatal("generateInitfsExtra: ", 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)
}
log.Fatal(err)
}
// Final processing of initramfs / kernel is done by boot-deploy
if err := bootDeploy(workDir, *outDir); err != nil {
log.Fatal("bootDeploy: ", err)
if err := bootDeploy(workDir, outDir); err != nil {
log.Fatal(err)
}
}
@@ -94,29 +87,18 @@ func bootDeploy(workDir string, outDir string) error {
if len(kernels) == 0 {
return errors.New("Unable to find any kernels at " + filepath.Join(outDir, "vmlinuz*"))
}
// 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)
kernFile, err := os.Open(kernels[0])
if err != nil {
return err
}
defer kernFd.Close()
defer kernFile.Close()
kernFileCopy, err := os.Create(filepath.Join(workDir, "vmlinuz"))
if err != nil {
return err
}
if _, err = io.Copy(kernFileCopy, kernFd); err != nil {
if _, err = io.Copy(kernFileCopy, kernFile); err != nil {
return err
}
kernFileCopy.Close()
@@ -129,19 +111,33 @@ func bootDeploy(workDir string, outDir string) error {
"-o", outDir,
"initramfs-extra")
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.Stderr = os.Stderr
// err is ignored, since shellcheck will return != 0 if there are issues
if err := cmd.Run(); err != nil {
log.Print("'boot-deploy' command failed")
log.Print("'boot-deploy' command failed: ")
return err
}
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 {
if _, err := os.Stat(file); err == nil {
return true
@@ -164,9 +160,10 @@ func getHookFiles(filesdir string) misc.StringSet {
defer f.Close()
s := bufio.NewScanner(f)
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)
}
files[s.Text()] = false
}
if err := s.Err(); err != nil {
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 {
// Expand glob expression
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 !exists(file) {
if required {
return errors.New("getFile: File does not exist :" + file)
}
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
// get dependencies for binaries
@@ -294,7 +262,8 @@ func getFile(files misc.StringSet, file string, required bool) error {
return nil
}
if err := getBinaryDeps(files, file); err != nil {
err := getBinaryDeps(files, file)
if err != nil {
return err
}
@@ -390,13 +359,13 @@ func getFdeFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) error {
}
// mesa hw accel
if devinfo.MesaDriver != "" {
if devinfo.Deviceinfo_mesa_driver != "" {
mesaFiles := misc.StringSet{
"/usr/lib/libEGL.so.1": false,
"/usr/lib/libGLESv2.so.2": false,
"/usr/lib/libgbm.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 {
return err
@@ -417,7 +386,6 @@ func getInitfsExtraFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) er
log.Println("== Generating initramfs extra ==")
binariesExtra := misc.StringSet{
"/lib/libz.so.1": false,
"/sbin/btrfs": false,
"/sbin/dmsetup": false,
"/sbin/e2fsck": false,
"/usr/sbin/parted": false,
@@ -450,7 +418,6 @@ func getInitfsFiles(files misc.StringSet, devinfo deviceinfo.DeviceInfo) error {
"/usr/sbin/telnetd": false,
"/sbin/kpartx": false,
"/etc/deviceinfo": false,
"/usr/bin/unudhcpd": false,
}
// Hook files & scripts
@@ -509,19 +476,21 @@ func getInitfsModules(files misc.StringSet, devinfo deviceinfo.DeviceInfo, kerne
return err
}
}
continue
} else if dir == "" {
// item is a module name
if err := getModule(files, file, modDir); err != nil {
log.Print("Unable to get module: ", file)
return err
}
continue
} else {
log.Printf("Unknown module entry: %q", item)
}
}
// 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 {
log.Print("Unable to get modules from deviceinfo")
return err
@@ -553,7 +522,7 @@ func getKernelReleaseFile() (string, error) {
files, _ := filepath.Glob("/usr/share/kernel/*/kernel.release")
// only one kernel flavor supported
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
@@ -575,46 +544,6 @@ func getKernelVersion() (string, error) {
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 {
initfsArchive, err := archive.New()
if err != nil {
@@ -683,7 +612,13 @@ func generateInitfsExtra(name string, path string, devinfo deviceinfo.DeviceInfo
}
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 {
@@ -710,23 +645,24 @@ func getModulesInDir(files misc.StringSet, modPath string) error {
// anywhere
func getModule(files misc.StringSet, modName string, modDir string) error {
modDep := filepath.Join(modDir, "modules.dep")
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)
deps, err := getModuleDeps(modName, modDir)
if err != nil {
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 {
p := filepath.Join(modDir, dep)
if !exists(p) {
@@ -739,27 +675,30 @@ func getModule(files misc.StringSet, modName string, modDir string) error {
return err
}
// Get the canonicalized name for the module as represented in the given modules.dep io.reader
func getModuleDeps(modName string, modulesDep io.Reader) ([]string, error) {
func getModuleDeps(modName string, modDir string) ([]string, error) {
var deps []string
// split the module name on - and/or _, build a regex for matching
splitRe := regexp.MustCompile("[-_]+")
modNameReStr := splitRe.ReplaceAllString(modName, "[-_]+")
re := regexp.MustCompile("^" + modNameReStr + "$")
modDep := filepath.Join(modDir, "modules.dep")
if !exists(modDep) {
log.Fatal("Kernel module.dep not found: ", modDir)
}
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() {
fields := strings.Fields(s.Text())
if len(fields) == 0 {
fields[0] = strings.TrimSuffix(fields[0], ":")
if modName != filepath.Base(stripExts(fields[0])) {
continue
}
fields[0] = strings.TrimSuffix(fields[0], ":")
found := re.FindAll([]byte(filepath.Base(stripExts(fields[0]))), -1)
if len(found) > 0 {
deps = append(deps, fields...)
break
for _, modPath := range fields {
deps = append(deps, modPath)
}
}
if err := s.Err(); err != nil {

View File

@@ -1,10 +1,8 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later
package main
import (
"strings"
"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>
// SPDX-License-Identifier: GPL-3.0-or-later
package archive
import (
"bytes"
"compress/flate"
"crypto/sha256"
"encoding/hex"
"github.com/cavaliercoder/go-cpio"
"github.com/klauspost/pgzip"
"gitlab.com/postmarketOS/postmarketos-mkinitfs/pkgs/misc"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)
@@ -50,6 +52,12 @@ func (archive *Archive) Write(path string, mode os.FileMode) error {
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 {
return err
}
@@ -57,6 +65,38 @@ func (archive *Archive) Write(path string, mode os.FileMode) error {
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 {
if err := archive.addDir(filepath.Dir(dest)); err != nil {
return err
@@ -104,9 +144,6 @@ func (archive *Archive) AddFile(file string, dest string) error {
// make sure target is an absolute path
if !filepath.IsAbs(target) {
target, err = misc.RelativeSymlinkTargetToDir(target, filepath.Dir(file))
if err != nil {
return err
}
}
// TODO: add verbose mode, print stuff like this:
// log.Printf("symlink: %q, target: %q", file, target)
@@ -143,6 +180,19 @@ func (archive *Archive) AddFile(file string, dest string) error {
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 {
// TODO: support other compression formats, based on deviceinfo
fd, err := os.Create(path)

View File

@@ -1,127 +1,53 @@
// Copyright 2021 Clayton Craft <clayton@craftyguy.net>
// SPDX-License-Identifier: GPL-3.0-or-later
package deviceinfo
import (
"bufio"
"fmt"
"io"
"log"
"errors"
"github.com/BurntSushi/toml"
"os"
"reflect"
"strings"
)
// Note: fields must be exported (start with capital letter)
// https://github.com/BurntSushi/toml/issues/121
type DeviceInfo struct {
AppendDtb string
Arch string
UbootBoardname string
BootimgAppendSEAndroidEnforce string
BootimgBlobpack string
BootimgDtbSecond string
BootimgMtkMkimage string
BootimgPxa string
BootimgQcdt string
Dtb string
FlashKernelOnUpdate string
FlashOffsetBase string
FlashOffsetKernel string
FlashOffsetRamdisk string
FlashOffsetSecond string
FlashOffsetTags string
FlashPagesize string
GenerateBootimg string
GenerateLegacyUbootInitfs string
InitfsCompression string
KernelCmdline string
LegacyUbootLoadAddress string
MesaDriver string
MkinitfsPostprocess string
ModulesInitfs string
Deviceinfo_append_dtb string
Deviceinfo_arch string
Deviceinfo_bootimg_append_seandroidenforce string
Deviceinfo_bootimg_blobpack string
Deviceinfo_bootimg_dtb_second string
Deviceinfo_bootimg_mtk_mkimage string
Deviceinfo_bootimg_pxa string
Deviceinfo_bootimg_qcdt string
Deviceinfo_dtb string
Deviceinfo_flash_offset_base string
Deviceinfo_flash_offset_kernel string
Deviceinfo_flash_offset_ramdisk string
Deviceinfo_flash_offset_second string
Deviceinfo_flash_offset_tags string
Deviceinfo_flash_pagesize string
Deviceinfo_generate_bootimg string
Deviceinfo_generate_legacy_uboot_initfs string
Deviceinfo_mesa_driver string
Deviceinfo_mkinitfs_postprocess string
Deviceinfo_initfs_compression string
Deviceinfo_kernel_cmdline string
Deviceinfo_legacy_uboot_load_address string
Deviceinfo_modules_initfs string
Deviceinfo_flash_kernel_on_update string
}
func ReadDeviceinfo(file string) (DeviceInfo, error) {
func ReadDeviceinfo() (DeviceInfo, error) {
file := "/etc/deviceinfo"
var deviceinfo DeviceInfo
fd, err := os.Open(file)
_, err := os.Stat(file)
if err != nil {
return deviceinfo, err
}
defer fd.Close()
if err := unmarshal(fd, &deviceinfo); err != nil {
return deviceinfo, err
return deviceinfo, errors.New("Unable to find deviceinfo: " + file)
}
if _, err := toml.DecodeFile(file, &deviceinfo); err != nil {
return deviceinfo, err
}
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>
// SPDX-License-Identifier: GPL-3.0-or-later
package misc
import (