Skip to content

Instantly share code, notes, and snippets.

@stapelberg
Created June 21, 2018 17:28
Show Gist options
  • Save stapelberg/2264acd713fed3ae58594ce4d4476c50 to your computer and use it in GitHub Desktop.
Save stapelberg/2264acd713fed3ae58594ce4d4476c50 to your computer and use it in GitHub Desktop.
Squashfs correctness verification for https://github.com/gokrazy/internal/pull/1
// Binary squashvrfy creates squashfs file system images using the gokrazy
// squashfs writer, then verifies that the resulting image yields the same
// result when mounted.
package main
import (
"bufio"
"bytes"
"compress/gzip"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"syscall"
"time"
"pault.ag/go/debian/control"
"github.com/gokrazy/internal/squashfs"
)
func copyDir(dir *squashfs.Directory, fn string) error {
fis, err := ioutil.ReadDir(fn)
if err != nil {
return err
}
for _, fi := range fis {
if fi.IsDir() {
subdir := dir.Directory(fi.Name(), fi.ModTime())
if err := copyDir(subdir, filepath.Join(fn, fi.Name())); err != nil {
return err
}
if err := subdir.Flush(); err != nil {
return err
}
continue
}
if !fi.Mode().IsRegular() {
if err := os.Remove(filepath.Join(fn, fi.Name())); err != nil {
return err
}
continue
}
wc, err := dir.File(fi.Name(), fi.ModTime(), fi.Mode())
b, err := ioutil.ReadFile(filepath.Join(fn, fi.Name()))
if err != nil {
return err
}
if _, err := wc.Write(b); err != nil {
return err
}
if err := wc.Close(); err != nil {
return err
}
}
return nil
}
func logic(src string) error {
mountpoint, err := ioutil.TempDir("", "mnt")
if err != nil {
return err
}
defer os.RemoveAll(mountpoint)
img, err := os.Create("/tmp/broken.squashfs")
if err != nil {
return err
}
defer img.Close()
now := time.Now()
w, err := squashfs.NewWriter(img, now)
if err != nil {
return err
}
if err := copyDir(w.Root, src); err != nil {
return err
}
if err := w.Root.Flush(); err != nil {
return err
}
if err := w.Flush(); err != nil {
return err
}
if err := img.Close(); err != nil {
return err
}
if err := exec.Command("unsquashfs", "-l", img.Name()).Run(); err != nil {
return err
}
loopb, err := exec.Command("losetup", "-f").Output()
if err != nil {
return fmt.Errorf("losetup -f: %v", err)
}
loop := strings.TrimSpace(string(loopb))
if err := exec.Command("losetup", loop, img.Name()).Run(); err != nil {
return fmt.Errorf("losetup: %v", err)
}
defer func() {
exec.Command("losetup", "-d", loop).Run()
}()
// Mount the image
if err := syscall.Mount(loop, mountpoint, "squashfs", 0, ""); err != nil {
return fmt.Errorf("mount: %v", err)
}
defer func() {
syscall.Unmount(mountpoint, 0)
}()
ls := exec.Command("diff", "-ur", src, mountpoint)
ls.Stdout = os.Stdout
ls.Stderr = os.Stderr
if err := ls.Run(); err != nil {
return err
}
if err := syscall.Unmount(mountpoint, 0); err != nil {
return fmt.Errorf("unmount: %v", err)
}
return nil
}
func vrfyOrig(url string) error {
destdir, err := ioutil.TempDir("", "deb")
if err != nil {
return err
}
defer os.RemoveAll(destdir)
base := path.Base(url)
dest, err := os.Create(filepath.Join(destdir, base))
if err != nil {
return err
}
defer dest.Close()
log.Printf("downloading %s", url)
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if _, err := io.Copy(dest, resp.Body); err != nil {
return err
}
if err := dest.Close(); err != nil {
return err
}
if err := os.MkdirAll(filepath.Join(destdir, "extracted"), 0755); err != nil {
return err
}
log.Printf("extracting %s", base)
ex := filepath.Join(destdir, "extracted")
tar := exec.Command("tar", "xf", filepath.Join(destdir, base), "-C", ex)
tar.Stdout = os.Stdout
tar.Stderr = os.Stderr
if err := tar.Run(); err != nil {
return err
}
if err := logic(ex); err != nil {
log.Printf("FAIL: %s: %v", url, err)
} else {
log.Printf("PASS: %s", url)
}
return nil
}
func debian() error {
resp, err := http.Get("http://ftp.ch.debian.org/debian/dists/sid/main/source/Sources.gz")
if err != nil {
return err
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
rd, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return err
}
idx, err := control.ParseSourceIndex(bufio.NewReader(rd))
if err != nil {
return err
}
seen := make(map[string]bool)
for _, x := range idx {
for _, f := range x.Files {
parts := strings.Split(f, " ")
if len(parts) != 3 {
continue
}
if !strings.Contains(parts[2], ".orig.") ||
strings.HasSuffix(parts[2], ".asc") {
continue
}
if seen[x.Directory+"/"+parts[2]] {
continue
}
seen[x.Directory+"/"+parts[2]] = true
if err := vrfyOrig("http://ftp.ch.debian.org/debian/" + x.Directory + "/" + parts[2]); err != nil {
return err
}
}
}
return nil
}
func main() {
flag.Parse()
if flag.NArg() > 0 {
if strings.HasPrefix(flag.Arg(0), "http://") {
if err := vrfyOrig(flag.Arg(0)); err != nil {
log.Fatal(err)
}
} else {
if err := logic(flag.Arg(0)); err != nil {
log.Fatal(err)
}
}
} else {
if err := debian(); err != nil {
log.Fatal(err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment