1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
package sources
import (
"crypto/sha256"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"path/filepath"
"regexp"
"strings"
"github.com/lxc/distrobuilder/shared"
)
type voidlinux struct {
common
}
// Run downloads a Void Linux rootfs tarball.
func (s *voidlinux) Run() error {
baseURL := s.definition.Source.URL
fname, err := s.getLatestBuild(baseURL, s.definition.Image.ArchitectureMapped, s.definition.Source.Variant)
if err != nil {
return fmt.Errorf("Failed to get latest build: %w", err)
}
if fname == "" {
return errors.New("Failed to determine latest build")
}
tarball := fmt.Sprintf("%s/%s", baseURL, fname)
digests := fmt.Sprintf("%s/sha256sum.txt", baseURL)
signatures := fmt.Sprintf("%s/sha256sum.sig", baseURL)
url, err := url.Parse(tarball)
if err != nil {
return fmt.Errorf("Failed to parse URL %q: %w", tarball, err)
}
if !s.definition.Source.SkipVerification && url.Scheme != "https" &&
len(s.definition.Source.Keys) == 0 {
return errors.New("GPG keys are required if downloading from HTTP")
}
var fpath string
if s.definition.Source.SkipVerification {
fpath, err = s.DownloadHash(s.definition.Image, tarball, "", nil)
} else {
fpath, err = s.DownloadHash(s.definition.Image, tarball, digests, sha256.New())
}
if err != nil {
return fmt.Errorf("Failed to download %q: %w", tarball, err)
}
// Force gpg checks when using http
if !s.definition.Source.SkipVerification && url.Scheme != "https" {
_, err = s.DownloadHash(s.definition.Image, digests, "", nil)
if err != nil {
return fmt.Errorf("Failed to download %q: %w", digests, err)
}
_, err = s.DownloadHash(s.definition.Image, signatures, "", nil)
if err != nil {
return fmt.Errorf("Failed to download %q: %w", signatures, err)
}
valid, err := s.VerifyFile(
filepath.Join(fpath, "sha256sum.txt"),
filepath.Join(fpath, "sha256sum.sig"))
if err != nil {
return fmt.Errorf(`Failed to verify "sha256sum.txt": %w`, err)
}
if !valid {
return errors.New(`Invalid signature for "sha256sum.txt"`)
}
}
s.logger.WithField("file", filepath.Join(fpath, fname)).Info("Unpacking image")
// Unpack
err = shared.Unpack(filepath.Join(fpath, fname), s.rootfsDir)
if err != nil {
return fmt.Errorf("Failed to unpack %q: %w", filepath.Join(fpath, fname), err)
}
return nil
}
func (s *voidlinux) getLatestBuild(baseURL, arch, variant string) (string, error) {
var (
resp *http.Response
err error
)
err = shared.Retry(func() error {
resp, err = http.Get(baseURL)
if err != nil {
return fmt.Errorf("Failed to GET %q: %w", baseURL, err)
}
return nil
}, 3)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Failed to read body: %w", err)
}
// Look for .tar.xz
selector := arch
if variant != "" {
selector = fmt.Sprintf("%s-%s", selector, variant)
}
regex := regexp.MustCompile(fmt.Sprintf(">void-%s-ROOTFS-.*.tar.xz<", selector))
// Find all rootfs related files
matches := regex.FindAllString(string(body), -1)
if len(matches) > 0 {
// Take the first match since they're all the same anyway
return strings.Trim(matches[0], "<>"), nil
}
return "", errors.New("Failed to find latest build")
}
|