added update & uboot functions
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
			
		||||
 #!/bin/bash
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
# get current partition index
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
 #!/bin/bash
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
image_file=$1
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
        data[1] = 0;
 | 
			
		||||
 | 
			
		||||
        // boot partition
 | 
			
		||||
        data[2] = 2; // A=2, B=3
 | 
			
		||||
        data[2] = 2; // A=1, B=2
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // handle commands
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,7 @@ func SystemDisableSSH() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SystemShutdown start shutdown of system
 | 
			
		||||
func SystemShutdown() error {
 | 
			
		||||
	cmd := exec.Command(systemShutdown)
 | 
			
		||||
	err := cmd.Run()
 | 
			
		||||
@@ -74,6 +75,7 @@ func SystemShutdown() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SystemReboot start reboot of system
 | 
			
		||||
func SystemReboot() error {
 | 
			
		||||
	cmd := exec.Command(systemReboot)
 | 
			
		||||
	err := cmd.Run()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										143
									
								
								update.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								update.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
			
		||||
package alpine_builder
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"compress/gzip"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"hash/crc32"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ubootFile = "/uboot/uboot.dat"
 | 
			
		||||
var ubootRemountRW = "mount -o remount,rw /uboot"
 | 
			
		||||
var ubootRemountRO = "mount -o remount,ro /uboot"
 | 
			
		||||
 | 
			
		||||
var rootPartitionA = "/dev/mmcblk0p1"
 | 
			
		||||
var rootPartitionB = "/dev/mmcblk0p2"
 | 
			
		||||
 | 
			
		||||
// loadUbootDat file to byte array
 | 
			
		||||
func loadUbootDat() ([]byte, error) {
 | 
			
		||||
	defaultData := make([]byte, 1024)
 | 
			
		||||
	defaultData[0] = 1 // file version
 | 
			
		||||
	defaultData[1] = 0 // boot counter
 | 
			
		||||
	defaultData[2] = 1 // boot partition  A=1, B=2
 | 
			
		||||
 | 
			
		||||
	data, err := ioutil.ReadFile(ubootFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return defaultData, fmt.Errorf("failed to open file: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// invalid dat file?
 | 
			
		||||
	if len(data) < 1024 {
 | 
			
		||||
		return defaultData, errors.New("invalid dat file -> fallback to defaults")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	crc := binary.LittleEndian.Uint32(data[1020:])
 | 
			
		||||
	bla := crc32.ChecksumIEEE(data[:1020])
 | 
			
		||||
	if crc != bla {
 | 
			
		||||
		return defaultData, errors.New("invalid crc -> fallback to defaults")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// saveUbootDat from byte array
 | 
			
		||||
func saveUbootDat(data []byte) error {
 | 
			
		||||
	// update crc
 | 
			
		||||
	binary.LittleEndian.PutUint32(data[1020:],
 | 
			
		||||
		crc32.ChecksumIEEE(data[:1020]))
 | 
			
		||||
 | 
			
		||||
	// remount boot partition - writable
 | 
			
		||||
	cmd := exec.Command(ubootRemountRW)
 | 
			
		||||
	err := cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to remount RW: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// update uboot dat file
 | 
			
		||||
	err = ioutil.WriteFile(ubootFile, data[:1024], os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed write uboot dat: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// remount boot partition - readonly
 | 
			
		||||
	cmd = exec.Command(ubootRemountRO)
 | 
			
		||||
	err = cmd.Run()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to remount RO: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UBootResetCounter to 0
 | 
			
		||||
func UBootResetCounter() error {
 | 
			
		||||
	data, _ := loadUbootDat()
 | 
			
		||||
	data[1] = 0
 | 
			
		||||
	return saveUbootDat(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UBootActive returns the active partition. A=1, B=2
 | 
			
		||||
func UBootActive() uint8 {
 | 
			
		||||
	data, _ := loadUbootDat()
 | 
			
		||||
	return data[2]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UBootSetActive sets the active partition. A=1, B=2
 | 
			
		||||
func UBootSetActive(active uint8) error {
 | 
			
		||||
	data, _ := loadUbootDat()
 | 
			
		||||
	if active == 1 {
 | 
			
		||||
		data[2] = 1
 | 
			
		||||
	} else {
 | 
			
		||||
		data[2] = 2
 | 
			
		||||
	}
 | 
			
		||||
	return saveUbootDat(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateSystem with the given image
 | 
			
		||||
func UpdateSystem(image string) error {
 | 
			
		||||
	data, _ := loadUbootDat()
 | 
			
		||||
	rootPart := rootPartitionA
 | 
			
		||||
	if data[2] == 1 {
 | 
			
		||||
		rootPart = rootPartitionB
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// open image file
 | 
			
		||||
	inFile, err := os.Open(image)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer inFile.Close()
 | 
			
		||||
 | 
			
		||||
	// decompress image
 | 
			
		||||
	inDecompress, err := gzip.NewReader(inFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer inDecompress.Close()
 | 
			
		||||
 | 
			
		||||
	// open root partition
 | 
			
		||||
	out, err := os.OpenFile(rootPart,
 | 
			
		||||
		os.O_WRONLY|os.O_TRUNC|os.O_SYNC, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer out.Close()
 | 
			
		||||
 | 
			
		||||
	// write update
 | 
			
		||||
	_, err = io.Copy(out, inDecompress)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// switch active partition
 | 
			
		||||
	if data[2] == 1 {
 | 
			
		||||
		return UBootSetActive(2)
 | 
			
		||||
	} else {
 | 
			
		||||
		return UBootSetActive(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										179
									
								
								update_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								update_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,179 @@
 | 
			
		||||
package alpine_builder
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"compress/gzip"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	ubootFile = "test_uboot"
 | 
			
		||||
	ubootRemountRW = "true"
 | 
			
		||||
	ubootRemountRO = "true"
 | 
			
		||||
	rootPartitionA = "test_rootA"
 | 
			
		||||
	rootPartitionB = "test_rootB"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestLoadUbootDat(t *testing.T) {
 | 
			
		||||
	ass := assert.New(t)
 | 
			
		||||
 | 
			
		||||
	// file missing
 | 
			
		||||
	data, err := loadUbootDat()
 | 
			
		||||
	ass.EqualError(err, "failed to open file: open test_uboot: no such file or directory")
 | 
			
		||||
	ass.Equal([]byte{1, 0, 1}, data[:3])
 | 
			
		||||
 | 
			
		||||
	// file with invalid data
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(ubootFile, []byte{}, os.ModePerm))
 | 
			
		||||
 | 
			
		||||
	data, err = loadUbootDat()
 | 
			
		||||
	ass.EqualError(err, "invalid dat file -> fallback to defaults")
 | 
			
		||||
	ass.Equal([]byte{1, 0, 1}, data[:3])
 | 
			
		||||
 | 
			
		||||
	// file with invalid CRC
 | 
			
		||||
	testData := make([]byte, 1024)
 | 
			
		||||
	testData[0] = 1
 | 
			
		||||
	testData[1] = 2
 | 
			
		||||
	testData[2] = 2
 | 
			
		||||
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
			
		||||
	data, err = loadUbootDat()
 | 
			
		||||
	ass.EqualError(err, "invalid crc -> fallback to defaults")
 | 
			
		||||
	ass.Equal([]byte{1, 0, 1}, data[:3])
 | 
			
		||||
 | 
			
		||||
	// file with valid CRC
 | 
			
		||||
	binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
			
		||||
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
			
		||||
	data, err = loadUbootDat()
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	ass.Equal(testData, data)
 | 
			
		||||
 | 
			
		||||
	_ = os.Remove(ubootFile)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSaveUbootDat(t *testing.T) {
 | 
			
		||||
	ass := assert.New(t)
 | 
			
		||||
 | 
			
		||||
	testData := make([]byte, 1024)
 | 
			
		||||
	testData[0] = 1
 | 
			
		||||
	testData[1] = 2
 | 
			
		||||
	testData[2] = 2
 | 
			
		||||
	ass.NoError(saveUbootDat(testData))
 | 
			
		||||
 | 
			
		||||
	binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
			
		||||
 | 
			
		||||
	data, err := ioutil.ReadFile(ubootFile)
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	ass.Equal(testData, data)
 | 
			
		||||
 | 
			
		||||
	_ = os.Remove(ubootFile)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUBootResetCounter(t *testing.T) {
 | 
			
		||||
	ass := assert.New(t)
 | 
			
		||||
 | 
			
		||||
	// write test file
 | 
			
		||||
	testData := make([]byte, 1024)
 | 
			
		||||
	testData[0] = 1
 | 
			
		||||
	testData[1] = 2
 | 
			
		||||
	testData[2] = 2
 | 
			
		||||
	binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
			
		||||
 | 
			
		||||
	ass.NoError(UBootResetCounter())
 | 
			
		||||
 | 
			
		||||
	data, err := ioutil.ReadFile(ubootFile)
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	ass.Zero(data[1])
 | 
			
		||||
 | 
			
		||||
	_ = os.Remove(ubootFile)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUBootActive(t *testing.T) {
 | 
			
		||||
	ass := assert.New(t)
 | 
			
		||||
 | 
			
		||||
	// write test file
 | 
			
		||||
	testData := make([]byte, 1024)
 | 
			
		||||
	testData[0] = 1
 | 
			
		||||
	testData[1] = 2
 | 
			
		||||
	testData[2] = 2
 | 
			
		||||
	binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
			
		||||
 | 
			
		||||
	ass.Equal(uint8(2), UBootActive())
 | 
			
		||||
 | 
			
		||||
	_ = os.Remove(ubootFile)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUBootSetActive(t *testing.T) {
 | 
			
		||||
	ass := assert.New(t)
 | 
			
		||||
 | 
			
		||||
	// write test file
 | 
			
		||||
	testData := make([]byte, 1024)
 | 
			
		||||
	testData[0] = 1
 | 
			
		||||
	testData[1] = 2
 | 
			
		||||
	testData[2] = 2
 | 
			
		||||
	binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
			
		||||
 | 
			
		||||
	ass.NoError(UBootSetActive(1))
 | 
			
		||||
 | 
			
		||||
	data, err := ioutil.ReadFile(ubootFile)
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	ass.Equal(uint8(1), data[2])
 | 
			
		||||
 | 
			
		||||
	_ = os.Remove(ubootFile)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpdateSystem(t *testing.T) {
 | 
			
		||||
	ass := assert.New(t)
 | 
			
		||||
 | 
			
		||||
	// test uboot file
 | 
			
		||||
	testData := make([]byte, 1024)
 | 
			
		||||
	testData[0] = 1
 | 
			
		||||
	testData[1] = 2
 | 
			
		||||
	testData[2] = 2
 | 
			
		||||
	binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
			
		||||
 | 
			
		||||
	// generate test image content
 | 
			
		||||
	size := int64(1024 * 1024 * 5)
 | 
			
		||||
	testImgData := make([]byte, size)
 | 
			
		||||
	buffer := bytes.NewBuffer(testImgData)
 | 
			
		||||
	_, err := io.CopyN(buffer, rand.Reader, size)
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// write compressed image file
 | 
			
		||||
	file, err := os.Create("test_image.gz")
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	gzipWriter := gzip.NewWriter(file)
 | 
			
		||||
	_, err = gzipWriter.Write(testImgData)
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	ass.NoError(gzipWriter.Close())
 | 
			
		||||
	ass.NoError(file.Close())
 | 
			
		||||
 | 
			
		||||
	ass.NoError(ioutil.WriteFile(rootPartitionA, nil, os.ModePerm))
 | 
			
		||||
	ass.NoError(UpdateSystem("test_image.gz"))
 | 
			
		||||
 | 
			
		||||
	// check if image was written
 | 
			
		||||
	data, err := ioutil.ReadFile(rootPartitionA)
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	ass.Equal(testImgData, data)
 | 
			
		||||
 | 
			
		||||
	// check if uboot dat was updated
 | 
			
		||||
	data, err = ioutil.ReadFile(ubootFile)
 | 
			
		||||
	ass.NoError(err)
 | 
			
		||||
	ass.Equal(uint8(1), data[2])
 | 
			
		||||
 | 
			
		||||
	_ = os.Remove("test_image.gz")
 | 
			
		||||
	_ = os.Remove(ubootFile)
 | 
			
		||||
	_ = os.Remove(rootPartitionA)
 | 
			
		||||
	_ = os.Remove(rootPartitionB)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user