cmd/diskimg: new disk imaging tool
This commit is contained in:
parent
f8c64d3be5
commit
237aa46ddd
|
@ -0,0 +1,19 @@
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "diskimg_lib",
|
||||||
|
srcs = ["main.go"],
|
||||||
|
importpath = "git.wntrmute.dev/kyle/goutils/cmd/diskimg",
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
"//ahash",
|
||||||
|
"//dbg",
|
||||||
|
"//die",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_binary(
|
||||||
|
name = "diskimg",
|
||||||
|
embed = [":diskimg_lib"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,34 @@
|
||||||
|
diskimg: write disk images
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
diskimg [-a algo] [-v] image device
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-a algo Select the hashing algorithm to use. The default
|
||||||
|
is 'sha256'. Specifying an algorithm of 'list'
|
||||||
|
will print the supported algorithms to standard
|
||||||
|
output and exit with error code 2.
|
||||||
|
-v Enable verbose (debug) output.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
Copying images/server.img to /dev/sda:
|
||||||
|
|
||||||
|
$ sudo diskimg images/server.img /dev/sda
|
||||||
|
|
||||||
|
Write a bladerunner node image to /dev/sda:
|
||||||
|
|
||||||
|
$ sudo diskimg -v ~/code/bladerunner/packer/build/cm4-cnode-ubuntu-22.04.2.img /dev/sda
|
||||||
|
opening image /home/kyle/code/bladerunner/packer/build/cm4-cnode-ubuntu-22.04.2.img for read
|
||||||
|
/home/kyle/code/bladerunner/packer/build/cm4-cnode-ubuntu-22.04.2.img 416d4c8f890904167419e3d488d097e9c847273376b650546fdb1f6f9809c184
|
||||||
|
opening device /dev/sda for rw
|
||||||
|
writing /home/kyle/code/bladerunner/packer/build/cm4-cnode-ubuntu-22.04.2.img -> /dev/sda
|
||||||
|
wrote 4151312384 bytes to /dev/sda
|
||||||
|
syncing /dev/sda
|
||||||
|
verifying the image was written successfully
|
||||||
|
OK
|
||||||
|
|
||||||
|
Motivation:
|
||||||
|
|
||||||
|
I wanted to write something like balena's Etcher, but commandline only.
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.wntrmute.dev/kyle/goutils/ahash"
|
||||||
|
"git.wntrmute.dev/kyle/goutils/dbg"
|
||||||
|
"git.wntrmute.dev/kyle/goutils/die"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultHashAlgorithm = "sha256"
|
||||||
|
|
||||||
|
var (
|
||||||
|
hAlgo string
|
||||||
|
debug = dbg.New()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func openImage(imageFile string) (image *os.File, hash []byte, err error) {
|
||||||
|
image, err = os.Open(imageFile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err = ahash.SumReader(hAlgo, image)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = image.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.Printf("%s %x\n", imageFile, hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openDevice(devicePath string) (device *os.File, err error) {
|
||||||
|
fi, err := os.Stat(devicePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err = os.OpenFile(devicePath, os.O_RDWR|os.O_SYNC, fi.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.StringVar(&hAlgo, "a", defaultHashAlgorithm, "default hash algorithm")
|
||||||
|
flag.BoolVar(&debug.Enabled, "v", false, "enable debug logging")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if hAlgo == "list" {
|
||||||
|
fmt.Println("Supported hashing algorithms:")
|
||||||
|
for _, algo := range ahash.SecureHashList() {
|
||||||
|
fmt.Printf("\t- %s\n", algo)
|
||||||
|
}
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag.NArg() != 2 {
|
||||||
|
die.With("usage: diskimg image device")
|
||||||
|
}
|
||||||
|
|
||||||
|
imageFile := flag.Arg(0)
|
||||||
|
devicePath := flag.Arg(1)
|
||||||
|
|
||||||
|
debug.Printf("opening image %s for read\n", imageFile)
|
||||||
|
image, hash, err := openImage(imageFile)
|
||||||
|
if image != nil {
|
||||||
|
defer image.Close()
|
||||||
|
}
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
debug.Printf("opening device %s for rw\n", devicePath)
|
||||||
|
device, err := openDevice(devicePath)
|
||||||
|
if device != nil {
|
||||||
|
defer device.Close()
|
||||||
|
}
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
debug.Printf("writing %s -> %s\n", imageFile, devicePath)
|
||||||
|
n, err := io.Copy(device, image)
|
||||||
|
die.If(err)
|
||||||
|
debug.Printf("wrote %d bytes to %s\n", n, devicePath)
|
||||||
|
|
||||||
|
debug.Printf("syncing %s\n", devicePath)
|
||||||
|
err = device.Sync()
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
debug.Println("verifying the image was written successfully")
|
||||||
|
_, err = device.Seek(0, 0)
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
deviceHash, err := ahash.SumLimitedReader(hAlgo, device, n)
|
||||||
|
die.If(err)
|
||||||
|
|
||||||
|
if !bytes.Equal(deviceHash, hash) {
|
||||||
|
fmt.Fprintln(os.Stderr, "Hash mismatch:")
|
||||||
|
fmt.Fprintf(os.Stderr, "\t%s: %s\n", imageFile, hash)
|
||||||
|
fmt.Fprintf(os.Stderr, "\t%s: %s\n", devicePath, deviceHash)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.Println("OK")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
Loading…
Reference in New Issue