diff -Nru goget-ubuntu-touch-0.19/bootimg/bootimg.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/bootimg/bootimg.go
--- goget-ubuntu-touch-0.19/bootimg/bootimg.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/bootimg/bootimg.go 2016-03-29 15:39:25.000000000 +0000
@@ -22,8 +22,8 @@
import (
"bytes"
"encoding/binary"
- "io/ioutil"
"errors"
+ "io/ioutil"
)
type bootHeader map[uint]uint32
@@ -40,13 +40,13 @@
pageSize
)
-type AndroidBootImg struct{
+type AndroidBootImg struct {
kernelOffset, ramdiskOffset, secondOffset uint32
- hdr bootHeader
- img []byte
+ hdr bootHeader
+ img []byte
}
-// readChunk reads a chunk o b []bytes returns it's Little Endian
+// readChunk reads a chunk o b []bytes returns it's Little Endian
// unsigned size(4) value
func readChunk(b []byte) (value uint32, err error) {
if len(b) != 4 {
@@ -66,10 +66,10 @@
if BOOT_MAGIC != string(magic) {
return boot, errors.New("This is not on an android bootimg")
}
-
+
boot.hdr = make(bootHeader)
for i, start := kernelSize, len(BOOT_MAGIC); i <= pageSize; i++ {
- if boot.hdr[uint(i)], err = readChunk(img[start:start + 4]); err != nil {
+ if boot.hdr[uint(i)], err = readChunk(img[start : start+4]); err != nil {
return boot, err
}
// sizeof(unsigned)
diff -Nru goget-ubuntu-touch-0.19/bootimg/example/abootimg-extract/main.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/bootimg/example/abootimg-extract/main.go
--- goget-ubuntu-touch-0.19/bootimg/example/abootimg-extract/main.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/bootimg/example/abootimg-extract/main.go 2016-03-29 15:39:25.000000000 +0000
@@ -20,9 +20,10 @@
// with this program. If not, see .
import (
- "log"
"io/ioutil"
+ "log"
"os"
+
"launchpad.net/goget-ubuntu-touch/bootimg"
)
diff -Nru goget-ubuntu-touch-0.19/.bzr-builddeb/default.conf goget-ubuntu-touch-0.33-0ubuntu1~xenial/.bzr-builddeb/default.conf
--- goget-ubuntu-touch-0.19/.bzr-builddeb/default.conf 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/.bzr-builddeb/default.conf 1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-[BUILDDEB]
-split = True
diff -Nru goget-ubuntu-touch-0.19/debian/bzr-builder.manifest goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/bzr-builder.manifest
--- goget-ubuntu-touch-0.19/debian/bzr-builder.manifest 2015-03-19 12:01:26.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/bzr-builder.manifest 1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-# bzr-builder format 0.3 deb-version {debupstream}-0~142
-lp:goget-ubuntu-touch revid:sergio.schvezov@canonical.com-20150318120201-bbwu2m4t1rboenb6
diff -Nru goget-ubuntu-touch-0.19/debian/changelog goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/changelog
--- goget-ubuntu-touch-0.19/debian/changelog 2015-03-19 12:01:26.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/changelog 2016-04-01 15:49:26.000000000 +0000
@@ -1,8 +1,162 @@
-goget-ubuntu-touch (0.19-0~142~ubuntu14.04.1) trusty; urgency=low
+goget-ubuntu-touch (0.33-0ubuntu1~xenial-staging) xenial; urgency=medium
- * Auto build.
+ * Intermmediate release.
- -- Zoltan Balogh Thu, 19 Mar 2015 12:01:26 +0000
+ -- Sergio Schvezov Fri, 01 Apr 2016 07:34:26 -0300
+
+goget-ubuntu-touch (0.33-0ubuntu1) xenial; urgency=medium
+
+ * Exit 1 on parameter errors (LP: #1473333)
+
+ -- Sergio Schvezov Tue, 17 Nov 2015 16:56:44 -0300
+
+goget-ubuntu-touch (0.32-0ubuntu1) xenial; urgency=medium
+
+ [ Michael Vogt ]
+ * Increase the size of the boot partition to 128M
+ * create fw/ directory under the efi directory
+ * support 'dst' keyword in boot-assets
+ * update build-dep version for golang-snappy-dev
+
+ [ Sergio Schvezov ]
+ * updated for upstream snappy changes
+
+ -- Michael Vogt Fri, 13 Nov 2015 10:21:18 +0100
+
+goget-ubuntu-touch (0.31-0ubuntu1) wily; urgency=medium
+
+ * Discriminate legacy from non legacy grub setup required installs.
+
+ -- Sergio Schvezov Wed, 16 Sep 2015 08:10:39 -0300
+
+goget-ubuntu-touch (0.30-0ubuntu4) wily; urgency=medium
+
+ * debian/control: Adding golang-uboot-go-dev as a build dep.
+
+ -- Sergio Schvezov Tue, 15 Sep 2015 21:44:26 -0300
+
+goget-ubuntu-touch (0.30-0ubuntu3) wily; urgency=medium
+
+ * No change rebuild.
+
+ -- Sergio Schvezov Tue, 15 Sep 2015 21:36:27 -0300
+
+goget-ubuntu-touch (0.30-0ubuntu2) wily; urgency=medium
+
+ * No change rebuild.
+
+ -- Sergio Schvezov Tue, 15 Sep 2015 21:22:34 -0300
+
+goget-ubuntu-touch (0.30-0ubuntu1) wily; urgency=medium
+
+ * Remove grub legacy handling for core.
+
+ -- Sergio Schvezov Tue, 15 Sep 2015 16:12:21 -0300
+
+goget-ubuntu-touch (0.29-0ubuntu1) wily; urgency=medium
+
+ * Align core images to 512.
+ * Copy original modprobe.d for core.
+
+ -- Sergio Schvezov Wed, 09 Sep 2015 17:44:05 -0300
+
+goget-ubuntu-touch (0.28-0ubuntu1) wily; urgency=medium
+
+ * Support setting the store id.
+ * Support modprobe.d config hooks from ubuntu-core.
+
+ -- Sergio Schvezov Wed, 09 Sep 2015 13:59:52 -0300
+
+goget-ubuntu-touch (0.27-0ubuntu1) wily; urgency=medium
+
+ * Avoid double unmap errors (LP: #1472516)
+ * Expose errors from external commands run in diskimage.
+
+ -- Sergio Schvezov Fri, 10 Jul 2015 14:28:26 -0300
+
+goget-ubuntu-touch (0.26-0ubuntu1) wily; urgency=medium
+
+ * snappy:
+ - Avoid data races when downloading snappy assets.
+ - Use common code for setting up the snappy bootloader.
+ - Normalize kernel and initrd on deploy.
+ * debian/control:
+ - Add gettext dependency.
+ * debian/changelog:
+ - Remove extra whitespaces.
+
+ -- Sergio Schvezov Mon, 06 Jul 2015 11:00:45 -0300
+
+goget-ubuntu-touch (0.25-0ubuntu1) wily; urgency=medium
+
+ * Removing unneeded logic for creating /writable.
+ * Add logic to prevent creating images lower than the unsupported size.
+ * Add the snappy personal target.
+ * Warn about the consequences of using --device-part for core and personal.
+
+ -- Sergio Schvezov Tue, 23 Jun 2015 14:09:54 -0300
+
+goget-ubuntu-touch (0.24-0ubuntu2) wily; urgency=medium
+
+ * Rebuild to use the latest ubuntu-snappy.
+
+ -- Sergio Schvezov Wed, 17 Jun 2015 16:54:55 -0300
+
+goget-ubuntu-touch (0.24-0ubuntu1) wily; urgency=medium
+
+ * core:
+ - Stamp a yaml with install/setup information.
+ - Remove code duplication and manage mounting/unmounting better.
+ * Rebuild against latest ubuntu-snappy (LP: #1465879)
+
+ -- Sergio Schvezov Wed, 17 Jun 2015 10:04:06 -0300
+
+goget-ubuntu-touch (0.23-0ubuntu2) wily; urgency=medium
+
+ * No change rebuild for latest ubuntu-snappy
+
+ -- Ricardo Salveti de Araujo Wed, 10 Jun 2015 16:46:14 -0300
+
+goget-ubuntu-touch (0.23-0ubuntu1) wily; urgency=medium
+
+ * Update to us the latest snappy API.
+ * Updating manpage
+
+ -- Sergio Schvezov Tue, 09 Jun 2015 10:57:15 -0300
+
+goget-ubuntu-touch (0.22-0ubuntu2) wily; urgency=medium
+
+ * debian/control: make ubuntu-snappy-cli a build-dep to side step the
+ powerpc build failure from ubuntu-snappy.
+
+ -- Sergio Schvezov Mon, 08 Jun 2015 20:40:04 -0300
+
+goget-ubuntu-touch (0.22-0ubuntu1) wily; urgency=medium
+
+ [ Steve Langasek ]
+ * Add UEFI support for grub.
+
+ [ Sergio Schvezov ]
+ * ubuntu-device-flash:
+ - List channels for specific devices on query. (LP: #1460015)
+
+ [ Jani Monoses ]
+ * ubuntu-device-flash: support skipping tls on query.
+
+ [ James Hunt ]
+ * ubuntu-device-flash: set the correct mode for uboot directory.
+
+ -- Sergio Schvezov Fri, 08 May 2015 12:41:56 -0300
+
+goget-ubuntu-touch (0.20-0ubuntu1) vivid; urgency=medium
+
+ * ubuntu-device-flash: core enhancements:
+ - Using snappy go API for setup.
+ - oem package support.
+ - Ubuntu store integration with package installation.
+ * ubuntu-device-flash: removed implicit touch command.
+
+ -- Sergio Schvezov Wed, 01 Apr 2015 15:03:46 -0300
goget-ubuntu-touch (0.19-0ubuntu1) vivid; urgency=medium
@@ -55,7 +209,7 @@
goget-ubuntu-touch (0.13-0ubuntu1) vivid; urgency=medium
* Setting a fixed set of ssh host keys to create.
- * Updating ubuntu-device-flash's manpage and code to generate it.
+ * Updating ubuntu-device-flash's manpage and code to generate it.
-- Sergio Schvezov Mon, 19 Jan 2015 18:45:29 -0300
@@ -134,13 +288,13 @@
goget-ubuntu-touch (0.6-0ubuntu1) vivid; urgency=medium
- * ubuntu-device-flash: enable ssh on developer mode toggle for core.
+ * ubuntu-device-flash: enable ssh on developer mode toggle for core.
-- Sergio Schvezov Tue, 02 Dec 2014 23:16:22 -0300
goget-ubuntu-touch (0.5-0ubuntu1) vivid; urgency=medium
- * ubuntu-device-flash:
+ * ubuntu-device-flash:
- adding a verbose flag and minimizing output using friendly messages
- dual is the default for core
- use system-a, system-b and writbale (instead of user-data) for labels
diff -Nru goget-ubuntu-touch-0.19/debian/control goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/control
--- goget-ubuntu-touch-0.19/debian/control 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/control 2016-04-01 15:49:26.000000000 +0000
@@ -6,10 +6,17 @@
dh-golang,
bash-completion,
golang-go,
+ golang-ar-dev,
+ golang-gettext-dev,
golang-go-flags-dev,
golang-gocheck-dev,
+ golang-github-mvo5-goconfigparser-dev,
+ golang-juju-loggo-dev,
golang-pb-dev,
- golang-goyaml-dev,
+ golang-github-ubuntu-core-snappy-dev,
+ golang-github-mvo5-uboot-go-dev,
+ golang-yaml.v2-dev,
+ ubuntu-snappy-cli,
Standards-Version: 3.9.5
Homepage: https://launchpad.net/goget-ubuntu-touch
Vcs-Browser: http://bazaar.launchpad.net/~phablet-team/goget-ubuntu-touch/trunk/files
@@ -19,9 +26,14 @@
Architecture: any
Depends: android-tools-adb,
android-tools-fastboot,
+ debsig-verify,
+ click-ubuntu-policy,
+ dosfstools,
fakeroot,
kpartx,
+ parted,
qemu-user-static,
+ ubuntu-snappy-cli,
${misc:Depends},
${shlibs:Depends},
Built-Using: ${misc:Built-Using}
diff -Nru goget-ubuntu-touch-0.19/debian/ubuntu-device-flash.1 goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/ubuntu-device-flash.1
--- goget-ubuntu-touch-0.19/debian/ubuntu-device-flash.1 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/ubuntu-device-flash.1 2016-04-01 15:49:26.000000000 +0000
@@ -1,4 +1,4 @@
-.TH ubuntu-device-flash 1 "19 January 2015"
+.TH ubuntu-device-flash 1 "9 June 2015"
.SH NAME
ubuntu-device-flash \-
.SH SYNOPSIS
@@ -34,35 +34,32 @@
\fB--channel\fP
Specify the channel to use
.TP
-\fB--device\fP
-Specify the device to use
-.TP
-\fB--keyboard-layout\fP
-Specify the keyboard layout
-.TP
\fB-o, --output\fP
Name of the image file to create
.TP
\fB-s, --size\fP
Size of image file to create in GB (min 4)
.TP
+\fB--oem\fP
+The snappy oem package to base the image out of
+.TP
+\fB--device-part\fP
+Specify a local device part to override the one from the server
+.TP
\fB--developer-mode\fP
Finds the latest public key in your ~/.ssh and sets it up using cloud-init
.TP
\fB--enable-ssh\fP
Enable ssh on the image through cloud-init(not needed with developer mode)
.TP
+\fB--install\fP
+Install additional packages (can be called multiple times)
+.TP
\fB--cloud\fP
Generate a pure cloud image without setting up cloud-init
.TP
-\fB--platform\fP
-specify the boards platform
-.TP
-\fB--install\fP
-install additional packages (can be called multiple times)
-.TP
-\fB--device-part\fP
-Specify a local device part to override the one from the server
+\fB--device\fP
+Specify the device to use
.SS query
Run queries against the image server
diff -Nru goget-ubuntu-touch-0.19/dependencies.tsv goget-ubuntu-touch-0.33-0ubuntu1~xenial/dependencies.tsv
--- goget-ubuntu-touch-0.19/dependencies.tsv 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/dependencies.tsv 2016-04-01 09:50:49.000000000 +0000
@@ -0,0 +1,8 @@
+github.com/cheggaaa/pb git da1f27ad1d9509b16f65f52fd9d8138b0f2dc7b2 2015-08-13T11:06:09Z
+github.com/gosexy/gettext git 98b7b91596d20b96909e6b60d57411547dd9959c 2013-02-21T11:21:43Z
+github.com/jessevdk/go-flags git 4047bd797dd935ae2b557a79cc43f223066c9659 2015-10-18T21:15:10Z
+github.com/mvo5/goconfigparser git 26426272dda20cc76aa1fa44286dc743d2972fe8 2015-02-12T09:37:50Z
+github.com/mvo5/uboot-go git 69978a3e4b05cca9d7cfee489b3453dfed45e72c 2015-07-23T08:17:10Z
+github.com/ubuntu-core/snappy git a6e683cb4eca7027b331713c2f10e26a31050f20 2016-01-14T16:39:06Z
+gopkg.in/yaml.v2 git 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 2015-06-24T10:29:02Z
+launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 87
diff -Nru goget-ubuntu-touch-0.19/devices/fastboot.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/devices/fastboot.go
--- goget-ubuntu-touch-0.19/devices/fastboot.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/devices/fastboot.go 2016-03-29 15:39:25.000000000 +0000
@@ -76,7 +76,7 @@
cmd := append(fastboot.params, []string{"getvar", "product"}...)
deviceOutput, err := exec.Command(fastbootCommand, cmd...).CombinedOutput()
lines := strings.Split(string(deviceOutput), "\n")
- for _, line := range(lines) {
+ for _, line := range lines {
fields := strings.Split(line, ":")
if strings.Contains(fields[0], "product") {
device = strings.TrimSpace(fields[1])
diff -Nru goget-ubuntu-touch-0.19/diskimage/bootloader.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/bootloader.go
--- goget-ubuntu-touch-0.19/diskimage/bootloader.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/bootloader.go 2016-04-01 09:50:49.000000000 +0000
@@ -0,0 +1,148 @@
+//
+// diskimage - handles ubuntu disk images
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package diskimage
+
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License version 3, as published
+// by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranties of
+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see .
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+
+ "launchpad.net/goget-ubuntu-touch/sysutils"
+)
+
+func setupBootAssetFiles(bootMount, bootPath, oemRootPath string, files []BootAssetFiles) error {
+ printOut("Setting up boot asset files from", oemRootPath, "...")
+ for _, file := range files {
+ dst := filepath.Join(bootPath, filepath.Base(file.Path))
+ if file.Dst != "" {
+ dst = filepath.Join(bootMount, file.Dst)
+ } else if file.Target != "" {
+ dst = filepath.Join(bootPath, file.Target)
+ }
+ dstDir := filepath.Dir(dst)
+ if _, err := os.Stat(dstDir); os.IsNotExist(err) {
+ if err := os.MkdirAll(dstDir, 0755); err != nil {
+ return err
+ }
+ }
+
+ src := filepath.Join(oemRootPath, file.Path)
+ printOut("Copying", src, "to", dst)
+ if err := sysutils.CopyFile(src, dst); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func setupBootAssetRawFiles(imagePath, oemRootPath string, rawFiles []BootAssetRawFiles) error {
+ printOut("Setting up raw boot assets from", oemRootPath, "...")
+ img, err := os.OpenFile(imagePath, os.O_WRONLY, 0644)
+ if err != nil {
+ return err
+ }
+ defer img.Close()
+
+ for _, asset := range rawFiles {
+ offsetBytes, err := offsetBytes(asset.Offset)
+ if err != nil {
+ return err
+ }
+
+ src := filepath.Join(oemRootPath, asset.Path)
+ printOut("Writing", src)
+ assetFile, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+
+ if err := rawwrite(img, assetFile, offsetBytes); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func setupBootAssetRawPartitions(imagePath string, partCount int, rawPartitions []BootAssetRawPartitions) error {
+ printOut("Setting up raw boot asset partitions for", imagePath, "...")
+ var part int = partCount
+
+ pos := 0
+ for _, asset := range rawPartitions {
+ part += 1
+
+ printOut("creating partition:", asset.Name)
+ // override position if specified, otherwise use the
+ // previous position
+ if asset.Pos > 0 {
+ pos = asset.Pos
+ }
+
+ // why * 2?
+ size := asset.Size * 2
+
+ opts := fmt.Sprintf("%d:%d:+%d", part, pos, size)
+ if output, err := exec.Command("sgdisk", "-a", "1", "-n", opts, imagePath).CombinedOutput(); err != nil {
+ println(string(output))
+ return err
+ }
+ // move postition forward
+ pos += size
+
+ opts = fmt.Sprintf("%d:%s", part, asset.Name)
+ if err := exec.Command("sgdisk", "-c", opts, imagePath).Run(); err != nil {
+ return err
+ }
+
+ opts = fmt.Sprintf("%d:%s", part, asset.Type)
+ if err := exec.Command("sgdisk", "-t", opts, imagePath).Run(); err != nil {
+ return err
+ }
+ }
+
+ printOut("aligning partitions")
+ if err := exec.Command("sgdisk", "-s", imagePath).Run(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func offsetBytes(offset string) (int64, error) {
+ // TODO add support for units
+ return strconv.ParseInt(offset, 10, 64)
+}
+
+func rawwrite(img *os.File, asset io.Reader, offset int64) error {
+ if _, err := img.Seek(offset, 0); err != nil {
+ return err
+ }
+
+ if _, err := io.Copy(img, asset); err != nil {
+ return err
+ }
+
+ return nil
+}
diff -Nru goget-ubuntu-touch-0.19/diskimage/bootloader_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/bootloader_test.go
--- goget-ubuntu-touch-0.19/diskimage/bootloader_test.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/bootloader_test.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,219 @@
+//
+// diskimage - handles ubuntu disk images
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package diskimage
+
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License version 3, as published
+// by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranties of
+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see .
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ . "launchpad.net/gocheck"
+ "launchpad.net/goget-ubuntu-touch/sysutils"
+)
+
+// Hook up gocheck into the "go test" runner.
+func Test(t *testing.T) { TestingT(t) }
+
+type BootAssetFilesTestSuite struct {
+ bootMountDir string
+ bootPathDir string
+ oemRootPathDir string
+ files []BootAssetFiles
+}
+
+var _ = Suite(&BootAssetFilesTestSuite{})
+
+const fileName = "files-%d"
+
+func (s *BootAssetFilesTestSuite) createTestFiles(c *C) {
+ for i := 0; i < 3; i++ {
+ f := fmt.Sprintf(fileName, i)
+ fAbsolutePath := filepath.Join(s.oemRootPathDir, f)
+ c.Assert(ioutil.WriteFile(fAbsolutePath, []byte(f), 0644), IsNil)
+ s.files = append(s.files, BootAssetFiles{Path: f})
+ }
+}
+
+func (s *BootAssetFilesTestSuite) verifyTestFiles(c *C) {
+ for i := range s.files {
+ f := fmt.Sprintf(fileName, i)
+
+ fAbsolutePath := filepath.Join(s.bootPathDir, f)
+ if s.files[i].Dst != "" {
+ fAbsolutePath = filepath.Join(s.bootMountDir, s.files[i].Dst)
+ } else if s.files[i].Target != "" {
+ fAbsolutePath = filepath.Join(s.bootPathDir, s.files[i].Target)
+ }
+
+ content, err := ioutil.ReadFile(fAbsolutePath)
+ c.Assert(err, IsNil)
+ c.Assert(string(content), Equals, fmt.Sprintf("files-%d", i))
+ }
+}
+
+func (s *BootAssetFilesTestSuite) SetUpTest(c *C) {
+ s.bootMountDir = c.MkDir()
+ s.bootPathDir = c.MkDir()
+ s.oemRootPathDir = c.MkDir()
+
+ s.createTestFiles(c)
+}
+
+func (s *BootAssetFilesTestSuite) TearDownTest(c *C) {
+ s.files = nil
+}
+
+func (s *BootAssetFilesTestSuite) TestCopyFiles(c *C) {
+ c.Assert(setupBootAssetFiles(s.bootMountDir, s.bootPathDir, s.oemRootPathDir, s.files), IsNil)
+
+ s.verifyTestFiles(c)
+}
+
+func (s *BootAssetFilesTestSuite) TestCopyFilesWithTarget(c *C) {
+ f := fmt.Sprintf(fileName, len(s.files))
+ fAbsolutePath := filepath.Join(s.oemRootPathDir, f)
+ c.Assert(ioutil.WriteFile(fAbsolutePath, []byte(f), 0644), IsNil)
+ s.files = append(s.files, BootAssetFiles{Path: f, Target: "otherpath"})
+
+ c.Assert(setupBootAssetFiles(s.bootMountDir, s.bootPathDir, s.oemRootPathDir, s.files), IsNil)
+
+ s.verifyTestFiles(c)
+}
+
+func (s *BootAssetFilesTestSuite) TestCopyFilesWithTargetInDir(c *C) {
+ f := fmt.Sprintf(fileName, len(s.files))
+ fAbsolutePath := filepath.Join(s.oemRootPathDir, f)
+ c.Assert(ioutil.WriteFile(fAbsolutePath, []byte(f), 0644), IsNil)
+ s.files = append(s.files, BootAssetFiles{Path: f, Target: filepath.Join("subpath", "otherpath")})
+
+ c.Assert(setupBootAssetFiles(s.bootMountDir, s.bootPathDir, s.oemRootPathDir, s.files), IsNil)
+
+ s.verifyTestFiles(c)
+}
+
+func (s *BootAssetFilesTestSuite) TestCopyFilesWithDst(c *C) {
+ f := fmt.Sprintf(fileName, len(s.files))
+ fAbsolutePath := filepath.Join(s.oemRootPathDir, f)
+ c.Assert(ioutil.WriteFile(fAbsolutePath, []byte(f), 0644), IsNil)
+ s.files = append(s.files, BootAssetFiles{Path: f, Dst: "otherpath"})
+
+ c.Assert(setupBootAssetFiles(s.bootMountDir, s.bootPathDir, s.oemRootPathDir, s.files), IsNil)
+
+ s.verifyTestFiles(c)
+}
+
+func (s *BootAssetFilesTestSuite) TestCopyFilesWithDstAndSubdir(c *C) {
+ f := fmt.Sprintf(fileName, len(s.files))
+ fAbsolutePath := filepath.Join(s.oemRootPathDir, f)
+ c.Assert(ioutil.WriteFile(fAbsolutePath, []byte(f), 0644), IsNil)
+ s.files = append(s.files, BootAssetFiles{Path: f, Dst: filepath.Join("path", "more-path", "otherpath")})
+
+ c.Assert(setupBootAssetFiles(s.bootMountDir, s.bootPathDir, s.oemRootPathDir, s.files), IsNil)
+
+ s.verifyTestFiles(c)
+}
+
+type BootAssetRawFilesTestSuite struct {
+ oemRootPathDir string
+ imagePath string
+ files []BootAssetRawFiles
+}
+
+var _ = Suite(&BootAssetRawFilesTestSuite{})
+
+const baseOffset = 60
+
+func (s *BootAssetRawFilesTestSuite) createTestFiles(c *C) {
+ for i := 0; i < 2; i++ {
+ f := fmt.Sprintf(fileName, i)
+ fAbsolutePath := filepath.Join(s.oemRootPathDir, f)
+ c.Assert(ioutil.WriteFile(fAbsolutePath, []byte(f), 0644), IsNil)
+ offset := fmt.Sprintf("%d", baseOffset+i*20)
+ s.files = append(s.files, BootAssetRawFiles{Path: f, Offset: offset})
+ }
+}
+
+func (s *BootAssetRawFilesTestSuite) verifyTestFiles(c *C) {
+ img, err := os.Open(s.imagePath)
+ c.Assert(err, IsNil)
+
+ for i := range s.files {
+ content := fmt.Sprintf(fileName, i)
+ readContent := make([]byte, len(content))
+
+ n, err := img.ReadAt(readContent, int64(baseOffset+i*20))
+ c.Assert(err, IsNil)
+ c.Assert(n, Equals, len(content))
+ c.Assert(string(readContent), Equals, content)
+
+ // check for zeros before and after
+ zero := make([]byte, 1)
+ _, err = img.ReadAt(zero, int64(baseOffset+i*20-1))
+ c.Assert(err, IsNil)
+ c.Assert(zero[0], Equals, uint8(0x0))
+
+ _, err = img.ReadAt(zero, int64(baseOffset+i*20+len(content)))
+ c.Assert(err, IsNil)
+ c.Assert(zero[0], Equals, uint8(0x0))
+ }
+}
+
+func (s *BootAssetRawFilesTestSuite) SetUpTest(c *C) {
+ s.oemRootPathDir = c.MkDir()
+ s.imagePath = filepath.Join(c.MkDir(), "image.img")
+
+ c.Assert(sysutils.CreateEmptyFile(s.imagePath, 1, sysutils.GB), IsNil)
+
+ s.createTestFiles(c)
+}
+
+func (s *BootAssetRawFilesTestSuite) TearDownTest(c *C) {
+ s.files = nil
+}
+
+func (s *BootAssetRawFilesTestSuite) TestRawWrite(c *C) {
+ c.Assert(setupBootAssetRawFiles(s.imagePath, s.oemRootPathDir, s.files), IsNil)
+
+ s.verifyTestFiles(c)
+}
+
+func (s *BootAssetRawFilesTestSuite) TestRawWriteNoValidOffset(c *C) {
+ f := fileName
+ fAbsolutePath := filepath.Join(s.oemRootPathDir, f)
+ c.Assert(ioutil.WriteFile(fAbsolutePath, []byte(f), 0644), IsNil)
+ offset := "NaN"
+ s.files = []BootAssetRawFiles{BootAssetRawFiles{Path: f, Offset: offset}}
+
+ c.Assert(setupBootAssetRawFiles(s.imagePath, s.oemRootPathDir, s.files), NotNil)
+}
+
+func (s *BootAssetRawFilesTestSuite) TestRawWriteNoValidFile(c *C) {
+ f := fileName
+ offset := "10"
+ s.files = []BootAssetRawFiles{BootAssetRawFiles{Path: f, Offset: offset}}
+
+ c.Assert(setupBootAssetRawFiles(s.imagePath, s.oemRootPathDir, s.files), NotNil)
+}
+
+func (s *BootAssetRawFilesTestSuite) TestRawWriteNoValidImage(c *C) {
+ c.Assert(setupBootAssetRawFiles("where_does_this_ref", s.oemRootPathDir, s.files), NotNil)
+}
diff -Nru goget-ubuntu-touch-0.19/diskimage/common.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common.go
--- goget-ubuntu-touch-0.19/diskimage/common.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common.go 2016-04-01 09:50:49.000000000 +0000
@@ -8,11 +8,15 @@
package diskimage
import (
+ "bufio"
+ "errors"
"fmt"
+ "io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
+ "syscall"
)
// This program is free software: you can redistribute it and/or modify it
@@ -35,17 +39,26 @@
}
}
+const (
+ hardwareFileName = "hardware.yaml"
+ kernelFileName = "vmlinuz"
+ initrdFileName = "initrd.img"
+)
+
+var (
+ syscallSync = syscall.Sync
+)
+
type Image interface {
Mount() error
Unmount() error
Format() error
Partition() error
- Map() error
- Unmap() error
BaseMount() string
}
type SystemImage interface {
+ Boot() string
System() string
Writable() string
}
@@ -53,28 +66,121 @@
type CoreImage interface {
Image
SystemImage
- SetupBoot(OemDescription) error
- FlashExtra(string) error
+ SetupBoot() error
+ FlashExtra() error
}
type HardwareDescription struct {
- Kernel string `yaml:"kernel"`
- Dtbs string `yaml:"dtbs"`
- Initrd string `yaml:"initrd"`
- PartitionLayout string `yaml:"partition-layout,omitempty"`
- Bootloader string `yaml:"bootloader"`
-}
-
-type OemDescription struct {
- Name string `yaml:"name"`
- Version string `yaml:"version"`
- Hardware struct {
- Dtb string `yaml:"dtb,omitempty"`
- } `yaml:"hardware,omitempty"`
+ Kernel string `yaml:"kernel"`
+ Dtbs string `yaml:"dtbs"`
+ Initrd string `yaml:"initrd"`
+}
+
+type BootAssetRawFiles struct {
+ Path string `yaml:"path"`
+ Offset string `yaml:"offset"`
+}
+
+type BootAssetRawPartitions struct {
+ Name string `yaml:"name"`
+ Size int `yaml:"size"`
+ Pos int `yaml:"pos"`
+ Type string `yaml:"type"`
+}
+
+type BootAssetFiles struct {
+ Path string `yaml:"path"`
+ // Target is the deprecated target relative to $bootloader dir
+ Target string `yaml:"target,omitempty"`
+ // Dst is the destination relative to the actual boot partition
+ Dst string `yaml:"dst,omitempty"`
+}
+
+type BootAssets struct {
+ Files []BootAssetFiles `yaml:"files,omitempty"`
+ RawFiles []BootAssetRawFiles `yaml:"raw-files,omitempty"`
+ RawPartitions []BootAssetRawPartitions `yaml:"raw-partitions,omitempty"`
+}
+
+type GadgetDescription struct {
+ Name string `yaml:"name"`
+ Version string `yaml:"version"`
+
+ Gadget struct {
+ Hardware struct {
+ Bootloader string `yaml:"bootloader"`
+ PartitionLayout string `yaml:"partition-layout"`
+ Dtb string `yaml:"dtb,omitempty"`
+ Platform string `yaml:"platform"`
+ Architecture string `yaml:"architecture"`
+ BootAssets *BootAssets `yaml:"boot-assets,omitempty"`
+ } `yaml:"hardware,omitempty"`
+
+ Software struct {
+ BuiltIn []string `yaml:"built-in,omitempty"`
+ Preinstalled []string `yaml:"preinstalled,omitempty"`
+ } `yaml:"software,omitempty"`
+
+ Store *struct {
+ ID string `yaml:"id,omitempty"`
+ }
+ } `yaml:"gadget,omitempty"`
+
+ Config struct {
+ UbuntuCore struct {
+ Modprobe *string `yaml:"modprobe,omitempty"`
+ } `yaml:"ubuntu-core,omitempty"`
+ } `yaml:"config,omitempty"`
+
+ rootDir string
+}
+
+func (o *GadgetDescription) SetRoot(rootDir string) {
+ o.rootDir = rootDir
+}
+
+// SystemParts returns the system labels depending on the partition layout.
+//
+// The default is to return a flat structure for any unknown layout.
+func (o *GadgetDescription) SystemParts() []string {
+ switch o.Gadget.Hardware.PartitionLayout {
+ default:
+ return []string{""}
+ }
+}
+
+func (o GadgetDescription) InstallPath() (string, error) {
+
+ glob, err := filepath.Glob(fmt.Sprintf("%s/gadget/*/*", o.rootDir))
+ if err != nil {
+ return "", err
+ }
+
+ if len(glob) != 1 {
+ return "", errors.New("gadget package not installed")
+ }
+
+ return glob[0], nil
+}
+
+func (o GadgetDescription) Architecture() string {
+ return o.Gadget.Hardware.Architecture
+}
+
+func (o *GadgetDescription) SetArchitecture(architecture string) {
+ o.Gadget.Hardware.Architecture = architecture
+}
+
+func (o GadgetDescription) PartitionLayout() string {
+ return o.Gadget.Hardware.PartitionLayout
}
-func (o OemDescription) InstallPath() string {
- return filepath.Join("/oem", o.Name, o.Version)
+func (o GadgetDescription) Platform() string {
+ return o.Gadget.Hardware.Platform
+}
+
+func (o *GadgetDescription) SetPlatform(platform string) {
+ o.Gadget.Hardware.Platform = platform
}
func sectorSize(dev string) (string, error) {
@@ -86,8 +192,348 @@
return strings.TrimSpace(string(out)), err
}
+// BaseImage implements the basic primitives to manage images.
+type BaseImage struct {
+ baseMount string
+ bindMounts []string
+ hardware HardwareDescription
+ location string
+ gadget GadgetDescription
+ parts []partition
+ partCount int
+ size int64
+ rootSize int
+ label string
+}
+
+var bindMounts = []string{"dev", "sys", "proc", filepath.Join("sys", "firmware")}
+
+// Mount mounts the image. This also maps the loop device.
+func (img *BaseImage) Mount() error {
+ if err := img.doMap(); err != nil {
+ return err
+ }
+
+ baseMount, err := ioutil.TempDir(os.TempDir(), "diskimage")
+ if err != nil {
+ return err
+ }
+
+ //Remove Mountpoint if we fail along the way
+ defer func() {
+ if err != nil {
+ if err := os.Remove(baseMount); err != nil {
+ fmt.Println("WARNING: cannot remove", baseMount, "due to", err)
+ }
+ }
+ }()
+
+ // We change the mode so snappy can unpack as non root
+ if err := os.Chmod(baseMount, 0755); err != nil {
+ return err
+ }
+
+ for _, part := range img.parts {
+ if part.fs == fsNone {
+ continue
+ }
+
+ mountpoint := filepath.Join(baseMount, string(part.dir))
+ if err := os.MkdirAll(mountpoint, 0755); err != nil {
+ return err
+ }
+
+ dev := filepath.Join("/dev/mapper", part.loop)
+ printOut("Mounting", dev, part.fs, "to", mountpoint)
+ if out, errMount := exec.Command("mount", filepath.Join("/dev/mapper", part.loop), mountpoint).CombinedOutput(); errMount != nil {
+ return ErrMount{dev: dev, mountpoint: mountpoint, fs: part.fs, out: out}
+ }
+ // this is cleanup in case one of the mounts fail
+ defer func() {
+ if err != nil {
+ if err := exec.Command("umount", mountpoint).Run(); err != nil {
+ fmt.Println("WARNING:", mountpoint, "could not be unmounted")
+ return
+ }
+
+ if err := os.Remove(mountpoint); err != nil {
+ fmt.Println("WARNING: could not remove ", mountpoint)
+ }
+ }
+ }()
+ }
+ img.baseMount = baseMount
+
+ mountpoints := make([]string, 0, len(bindMounts))
+ if img.gadget.PartitionLayout() == "minimal" {
+ mountpoints = bindMounts
+
+ for _, d := range mountpoints {
+ p := filepath.Join(baseMount, d)
+
+ if err := os.MkdirAll(p, 0755); err != nil {
+ return err
+ }
+
+ printOut("Bind mounting", d, "to", p)
+ if err := bindMount(filepath.Join("/", d), p); err != nil {
+ return err
+ }
+
+ img.bindMounts = append(img.bindMounts, p)
+ }
+ }
+
+ return nil
+}
+
+// Unmount unmounts the image. This also unmaps the loop device.
+func (img *BaseImage) Unmount() error {
+ defer func() {
+ if isMapped(img.parts) {
+ fmt.Println("WARNING: could not unmap partitions")
+ }
+ }()
+
+ if img.baseMount == "" {
+ panic("No base mountpoint set")
+ }
+
+ for i := len(img.bindMounts) - 1; i >= 0; i-- {
+ if err := unmount(img.bindMounts[i]); err != nil {
+ return err
+ }
+ }
+ img.bindMounts = nil
+
+ syscallSync()
+
+ for _, part := range img.parts {
+ if part.fs == fsNone {
+ continue
+ }
+
+ mountpoint := filepath.Join(img.baseMount, string(part.dir))
+ if out, err := exec.Command("umount", mountpoint).CombinedOutput(); err != nil {
+ lsof, _ := exec.Command("lsof", "-w", mountpoint).CombinedOutput()
+ printOut(string(lsof))
+ dev := filepath.Join("/dev/mapper", part.loop)
+ return ErrMount{dev: dev, mountpoint: mountpoint, fs: part.fs, out: out}
+ }
+ }
+
+ if err := os.RemoveAll(img.baseMount); err != nil {
+ return err
+ }
+ img.baseMount = ""
+
+ return img.doUnmap()
+}
+
+// doMap maps the image to loop devices
+func (img *BaseImage) doMap() error {
+ if isMapped(img.parts) {
+ panic("cannot double map partitions")
+ }
+
+ kpartxCmd := exec.Command("kpartx", "-avs", img.location)
+ stdout, err := kpartxCmd.StdoutPipe()
+ if err != nil {
+ return err
+ }
+
+ if err := kpartxCmd.Start(); err != nil {
+ return err
+ }
+
+ loops := make([]string, 0, img.partCount)
+ scanner := bufio.NewScanner(stdout)
+ for scanner.Scan() {
+ fields := strings.Fields(scanner.Text())
+
+ if len(fields) > 2 {
+ loops = append(loops, fields[2])
+ } else {
+ return fmt.Errorf("issues while determining drive mappings (%q)", fields)
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return err
+ }
+
+ if len(loops) != img.partCount {
+ return ErrMapCount{expectedParts: img.partCount, foundParts: len(loops)}
+ }
+
+ mapPartitions(img.parts, loops)
+
+ if err := kpartxCmd.Wait(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// doUnmap destroys loop devices for the partitions
+func (img *BaseImage) doUnmap() error {
+ if img.baseMount != "" {
+ panic("cannot unmap mounted partitions")
+ }
+
+ for _, part := range img.parts {
+ dmsetupCmd := []string{"dmsetup", "clear", part.loop}
+ if out, err := exec.Command(dmsetupCmd[0], dmsetupCmd[1:]...).CombinedOutput(); err != nil {
+ return &ErrExec{command: dmsetupCmd, output: out}
+ }
+ }
+
+ kpartxCmd := []string{"kpartx", "-ds", img.location}
+ if out, err := exec.Command(kpartxCmd[0], kpartxCmd[1:]...).CombinedOutput(); err != nil {
+ return &ErrExec{command: kpartxCmd, output: out}
+ }
+
+ unmapPartitions(img.parts)
+
+ return nil
+}
+
+// Format formats the image following the partition types and labels them
+// accordingly.
+func (img BaseImage) Format() (err error) {
+ if err := img.doMap(); err != nil {
+ return err
+ }
+ defer func() {
+ if errUnmap := img.doUnmap(); errUnmap != nil {
+ if err == nil {
+ err = errUnmap
+ } else {
+ fmt.Println("WARNING: could not unmap partitions after error:", errUnmap)
+ }
+ }
+ }()
+
+ for _, part := range img.parts {
+ dev := filepath.Join("/dev/mapper", part.loop)
+
+ if part.fs == fsFat32 {
+ cmd := []string{"mkfs.vfat", "-F", "32", "-n", string(part.label)}
+
+ size, err := sectorSize(dev)
+ if err != nil {
+ return err
+ }
+
+ if size != "512" {
+ cmd = append(cmd, "-s", "1")
+ }
+
+ cmd = append(cmd, "-S", size, dev)
+
+ if out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput(); err != nil {
+ return &ErrExec{command: cmd, output: out}
+ }
+ } else {
+ cmd := []string{"mkfs.ext4", "-F", "-L", string(part.label), dev}
+ if out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput(); err != nil {
+ return &ErrExec{command: cmd, output: out}
+ }
+ }
+ }
+
+ return nil
+}
+
+// User returns the writable path
+func (img BaseImage) Writable() string {
+ if img.parts == nil {
+ panic("img is not setup with partitions")
+ }
+
+ if img.baseMount == "" {
+ panic("img not mounted")
+ }
+
+ return filepath.Join(img.baseMount, string(writableDir))
+}
+
+func (img BaseImage) pathToMount(dir directory) string {
+ if img.parts == nil {
+ panic("img is not setup with partitions")
+ }
+
+ if img.baseMount == "" {
+ panic("img not mounted")
+ }
+
+ return filepath.Join(img.baseMount, string(dir))
+}
+
+//System returns the system path
+func (img BaseImage) System() string {
+ return img.pathToMount(systemADir)
+}
+
+// Boot returns the system-boot path
+func (img BaseImage) Boot() string {
+ return img.pathToMount(bootDir)
+}
+
+// BaseMount returns the base directory used to mount the image partitions.
+func (img BaseImage) BaseMount() string {
+ if img.baseMount == "" {
+ panic("image needs to be mounted")
+ }
+
+ return img.baseMount
+}
+
+func (img *BaseImage) GenericBootSetup(bootPath string) error {
+ gadgetRoot, err := img.gadget.InstallPath()
+ if err != nil {
+ return err
+ }
+
+ return setupBootAssetFiles(img.Boot(), bootPath, gadgetRoot, img.gadget.Gadget.Hardware.BootAssets.Files)
+}
+
+func (img *BaseImage) FlashExtra() error {
+ gadgetRoot, err := img.gadget.InstallPath()
+ if err != nil {
+ return err
+ }
+
+ if bootAssets := img.gadget.Gadget.Hardware.BootAssets; bootAssets != nil {
+ if bootAssets.RawPartitions != nil {
+ if err := setupBootAssetRawPartitions(img.location, img.partCount, bootAssets.RawPartitions); err != nil {
+ return err
+ }
+ }
+
+ return setupBootAssetRawFiles(img.location, gadgetRoot, bootAssets.RawFiles)
+ }
+
+ return nil
+}
+
func printOut(args ...interface{}) {
if debugPrint {
fmt.Println(args...)
}
}
+
+func bindMount(src, dst string) error {
+ if out, err := exec.Command("mount", "--bind", src, dst).CombinedOutput(); err != nil {
+ return fmt.Errorf("issues while bind mounting: %s", out)
+ }
+
+ return nil
+}
+
+func unmount(dst string) error {
+ if out, err := exec.Command("umount", dst).CombinedOutput(); err != nil {
+ return fmt.Errorf("issues while unmounting: %s", out)
+ }
+
+ return nil
+}
diff -Nru goget-ubuntu-touch-0.19/diskimage/common_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common_test.go
--- goget-ubuntu-touch-0.19/diskimage/common_test.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common_test.go 2016-04-01 10:12:22.000000000 +0000
@@ -0,0 +1,63 @@
+//
+// diskimage - handles ubuntu disk images
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package diskimage
+
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License version 3, as published
+// by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranties of
+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see .
+
+import (
+ "os"
+ "path/filepath"
+
+ . "launchpad.net/gocheck"
+)
+
+type CommonTestSuite struct {
+ tmpdir string
+ gadget GadgetDescription
+ packageInst string
+}
+
+var _ = Suite(&CommonTestSuite{})
+
+func (s *CommonTestSuite) SetUpTest(c *C) {
+ s.tmpdir = c.MkDir()
+ s.gadget = GadgetDescription{Name: "packagename", Version: "42"}
+ s.packageInst = s.gadget.Name
+}
+
+func (s *CommonTestSuite) TestOemInstallPath(c *C) {
+ err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst, "current"), 0755)
+ c.Assert(err, IsNil)
+
+ s.gadget.SetRoot(s.tmpdir)
+ installPath, err := s.gadget.InstallPath()
+
+ c.Assert(err, IsNil)
+ c.Assert(installPath, Equals, filepath.Join(s.tmpdir, "gadget/packagename/current"))
+}
+
+func (s *CommonTestSuite) TestOemInstallPathNoOem(c *C) {
+ err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst), 0755)
+ c.Assert(err, IsNil)
+
+ s.gadget.SetRoot(s.tmpdir)
+ installPath, err := s.gadget.InstallPath()
+
+ c.Assert(err, NotNil)
+ c.Assert(installPath, Equals, "")
+}
diff -Nru goget-ubuntu-touch-0.19/diskimage/core_grub.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_grub.go
--- goget-ubuntu-touch-0.19/diskimage/core_grub.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_grub.go 2016-04-01 09:50:49.000000000 +0000
@@ -1,22 +1,20 @@
//
// diskimage - handles ubuntu disk images
//
-// Copyright (c) 2013 Canonical Ltd.
+// Copyright (c) 2013-2015 Canonical Ltd.
//
// Written by Sergio Schvezov
//
+
package diskimage
import (
- "bufio"
"errors"
"fmt"
"io"
- "io/ioutil"
"os"
"os/exec"
"path/filepath"
- "strings"
"time"
"launchpad.net/goget-ubuntu-touch/sysutils"
@@ -34,19 +32,34 @@
// You should have received a copy of the GNU General Public License along
// with this program. If not, see .
+// CoreGrubImage holds the logic to create a core image using grub.
type CoreGrubImage struct {
- CoreImage
- location string
- size int64
- baseMount string
- parts []partition
+ BaseImage
+
+ legacyGrub bool
}
-func NewCoreGrubImage(location string, size int64) *CoreGrubImage {
+// NewCoreGrubImage creates a new instance of CoreGrubImage
+func NewCoreGrubImage(location string, size int64, rootSize int, hw HardwareDescription, gadget GadgetDescription, updateGrub bool, label string) *CoreGrubImage {
+ var partCount int
+ switch gadget.PartitionLayout() {
+ case "minimal":
+ partCount = 3
+ }
+
return &CoreGrubImage{
- location: location,
- size: size,
+ BaseImage: BaseImage{
+ location: location,
+ size: size,
+ rootSize: rootSize,
+ hardware: hw,
+ gadget: gadget,
+ partCount: partCount,
+ label: label,
+ },
+ legacyGrub: updateGrub,
}
+
}
const grubCfgContent = `# console only, no graphics/vga
@@ -56,61 +69,9 @@
GRUB_RECORDFAIL_TIMEOUT=0
`
-func (img *CoreGrubImage) Mount() (err error) {
- img.baseMount, err = ioutil.TempDir(os.TempDir(), "core-grub-disk")
- if err != nil {
- return errors.New(fmt.Sprintf("Unable to create temp dir to create system image: %s", err))
- }
- //Remove Mountpoint if we fail along the way
- defer func() {
- if err != nil {
- os.Remove(img.baseMount)
- }
- }()
-
- for _, part := range img.parts {
- if part.fs == fsNone {
- continue
- }
-
- mountpoint := filepath.Join(img.baseMount, string(part.dir))
- if err := os.MkdirAll(mountpoint, 0755); err != nil {
- return err
- }
- if out, err := exec.Command("mount", filepath.Join("/dev/mapper", part.loop), mountpoint).CombinedOutput(); err != nil {
- return fmt.Errorf("unable to mount dir to create system image: %s", out)
- }
- }
-
- return nil
-}
-
-func (img *CoreGrubImage) Unmount() (err error) {
- if img.baseMount == "" {
- panic("No base mountpoint set")
- }
- defer os.Remove(img.baseMount)
-
- if out, err := exec.Command("sync").CombinedOutput(); err != nil {
- return fmt.Errorf("Failed to sync filesystems before unmounting: %s", out)
- }
-
- for _, part := range img.parts {
- if part.fs == fsNone {
- continue
- }
-
- mountpoint := filepath.Join(img.baseMount, string(part.dir))
- if out, err := exec.Command("umount", mountpoint).CombinedOutput(); err != nil {
- return fmt.Errorf("unable to unmount dir for image: %s", out)
- } else {
- }
- }
-
- img.baseMount = ""
-
- return nil
-}
+const grubStubContent = `set prefix=($root)'/EFI/ubuntu/grub'
+configfile $prefix/grub.cfg
+`
//Partition creates a partitioned image from an img
func (img *CoreGrubImage) Partition() error {
@@ -124,9 +85,10 @@
}
parted.addPart(grubLabel, "", fsNone, 4)
- parted.addPart(bootLabel, bootDir, fsFat32, 64)
- parted.addPart(systemALabel, systemADir, fsExt4, 1024)
- parted.addPart(systemBLabel, systemBDir, fsExt4, 1024)
+ switch img.gadget.PartitionLayout() {
+ case "minimal":
+ parted.addPart(bootLabel, bootDir, fsFat32, 64)
+ }
parted.addPart(writableLabel, writableDir, fsExt4, -1)
parted.setBoot(2)
@@ -137,151 +99,20 @@
return parted.create(img.location)
}
-//Map creates loop devices for the partitions
-func (img *CoreGrubImage) Map() error {
- if isMapped(img.parts) {
- panic("cannot double map partitions")
- }
-
- kpartxCmd := exec.Command("kpartx", "-avs", img.location)
- stdout, err := kpartxCmd.StdoutPipe()
- if err != nil {
- return err
- }
-
- if err := kpartxCmd.Start(); err != nil {
- return err
- }
-
- loops := make([]string, 0, 4)
- scanner := bufio.NewScanner(stdout)
- for scanner.Scan() {
- fields := strings.Fields(scanner.Text())
-
- if len(fields) > 2 {
- loops = append(loops, fields[2])
- } else {
- return errors.New("issues while determining drive mappings")
- }
- }
- if err := scanner.Err(); err != nil {
- return err
- }
-
- // there are 5 partitions, so there should be five loop mounts
- if len(loops) != 5 {
- return errors.New("more partitions then expected while creating loop mapping")
- }
-
- mapPartitions(img.parts, loops)
-
- if err := kpartxCmd.Wait(); err != nil {
- return err
- }
-
- return nil
-}
-
-//Unmap destroys loop devices for the partitions
-func (img *CoreGrubImage) Unmap() error {
- if img.baseMount != "" {
- panic("cannot unmap mounted partitions")
- }
-
- for _, part := range img.parts {
- if err := exec.Command("dmsetup", "clear", part.loop).Run(); err != nil {
+// SetupBoot sets up the bootloader logic for the image.
+func (img *CoreGrubImage) SetupBoot() error {
+ if !img.legacyGrub {
+ // destinations
+ bootPath := filepath.Join(img.baseMount, string(bootDir), "EFI", "ubuntu", "grub")
+ if err := img.GenericBootSetup(bootPath); err != nil {
return err
}
}
- if err := exec.Command("kpartx", "-d", img.location).Run(); err != nil {
- return err
- }
-
- unmapPartitions(img.parts)
-
- return nil
-}
-
-func (img CoreGrubImage) Format() error {
- for _, part := range img.parts {
- dev := filepath.Join("/dev/mapper", part.loop)
-
- if part.fs == fsFat32 {
- cmd := []string{"-F", "32", "-n", string(part.label)}
-
- size, err := sectorSize(dev)
- if err != nil {
- return err
- }
-
- if size != "512" {
- cmd = append(cmd, "-s", "1")
- }
-
- cmd = append(cmd, "-S", size, dev)
-
- if out, err := exec.Command("mkfs.vfat", cmd...).CombinedOutput(); err != nil {
- return fmt.Errorf("unable to create filesystem: %s", out)
- }
- } else if part.fs == fsExt4 {
- if out, err := exec.Command("mkfs.ext4", "-F", "-L", string(part.label), dev).CombinedOutput(); err != nil {
- return fmt.Errorf("unable to create filesystem: %s", out)
- }
- }
- }
-
- return nil
-}
-
-// User returns the writable path
-func (img CoreGrubImage) Writable() string {
- if img.parts == nil {
- panic("img is not setup with partitions")
- }
-
- if img.baseMount == "" {
- panic("img not mounted")
- }
-
- return filepath.Join(img.baseMount, string(writableDir))
-}
-
-// Boot returns the system-boot path
-func (img CoreGrubImage) Boot() string {
- if img.parts == nil {
- panic("img is not setup with partitions")
- }
-
- if img.baseMount == "" {
- panic("img not mounted")
- }
-
- return filepath.Join(img.baseMount, string(bootDir))
-}
-
-//System returns the system path
-func (img CoreGrubImage) System() string {
- if img.parts == nil {
- panic("img is not setup with partitions")
- }
-
- if img.baseMount == "" {
- panic("img not mounted")
- }
-
- return filepath.Join(img.baseMount, string(systemADir))
+ return img.setupGrub()
}
-func (img CoreGrubImage) BaseMount() string {
- if img.baseMount == "" {
- panic("image needs to be mounted")
- }
-
- return img.baseMount
-}
-
-func (img *CoreGrubImage) SetupBoot(oem OemDescription) error {
+func (img *CoreGrubImage) setupGrub() error {
for _, dev := range []string{"dev", "proc", "sys"} {
src := filepath.Join("/", dev)
dst := filepath.Join(img.System(), dev)
@@ -302,14 +133,14 @@
return errors.New("cannot determined absolute path for output image")
}
- rootDevPath := filepath.Join(img.System(), "root_dev")
+ rootDevPath := filepath.Join(img.System(), "tmp", "root_dev")
- if f, err := os.Create(rootDevPath); err != nil {
+ f, err := os.Create(rootDevPath)
+ if err != nil {
return err
- } else {
- f.Close()
- defer os.Remove(rootDevPath)
}
+ f.Close()
+ defer os.Remove(rootDevPath)
if err := bindMount(outputPath, rootDevPath); err != nil {
return err
@@ -332,18 +163,67 @@
return fmt.Errorf("unable to create %s dir: %s", efiGrubDir, err)
}
- bootGrubDir := filepath.Join(img.System(), "boot", "grub")
+ // create fw update directory
+ fwDir := filepath.Join(img.System(), "boot", "efi", "EFI", "ubuntu", "fw")
+ if err := os.MkdirAll(fwDir, 0755); err != nil {
+ return fmt.Errorf("unable to create %s dir: %s", fwDir, err)
+ }
+ bootGrubDir := filepath.Join(img.System(), "boot", "grub")
if err := bindMount(efiGrubDir, bootGrubDir); err != nil {
return err
}
defer unmount(bootGrubDir)
- // install grub
- if out, err := exec.Command("chroot", img.System(), "grub-install", "/root_dev").CombinedOutput(); err != nil {
- return fmt.Errorf("unable to install grub: %s", out)
+ var grubTarget string
+
+ arch := img.gadget.Architecture()
+
+ switch arch {
+ case "armhf":
+ grubTarget = "arm-efi"
+ case "amd64":
+ grubTarget = "x86_64-efi"
+ case "i386":
+ grubTarget = "i386-efi"
+ case "arm64":
+ grubTarget = "arm64-efi"
+ default:
+ return fmt.Errorf("unsupported architecture for GRUB on EFI: %s", arch)
+ }
+
+ if arch == "amd64" || arch == "i386" {
+ // install grub BIOS support
+ if out, err := exec.Command("chroot", img.System(), "grub-install", "tmp/root_dev").CombinedOutput(); err != nil {
+ return fmt.Errorf("unable to install grub (BIOS): %s", out)
+ }
+ }
+
+ // install grub EFI
+ if out, err := exec.Command("chroot", img.System(), "grub-install", fmt.Sprint("--target="+grubTarget), "--no-nvram", "--removable", "--efi-directory=/boot/efi").CombinedOutput(); err != nil {
+ return fmt.Errorf("unable to install grub (EFI): %s", out)
+ }
+ // tell our EFI grub where to find its full config
+ efiBootDir := filepath.Join(img.System(), "boot", "efi", "EFI", "BOOT")
+ grubStub, err := os.Create(filepath.Join(efiBootDir, "grub.cfg"))
+ if err != nil {
+ return fmt.Errorf("unable to create %s file: %s", grubStub.Name(), err)
+ }
+ defer grubStub.Close()
+ if _, err := io.WriteString(grubStub, grubStubContent); err != nil {
+ return err
+ }
+
+ if img.legacyGrub {
+ if err := img.updateGrub(); err != nil {
+ return err
+ }
}
+ return nil
+}
+
+func (img *CoreGrubImage) updateGrub() error {
// ensure we run not into recordfail issue
grubDir := filepath.Join(img.System(), "etc", "default", "grub.d")
if err := os.MkdirAll(grubDir, 0755); err != nil {
@@ -351,7 +231,7 @@
}
grubFile, err := os.Create(filepath.Join(grubDir, "50-system-image.cfg"))
if err != nil {
- return fmt.Errorf("unable to create %s file: %s", grubFile, err)
+ return fmt.Errorf("unable to create %s file: %s", grubFile.Name(), err)
}
defer grubFile.Close()
if _, err := io.WriteString(grubFile, grubCfgContent); err != nil {
@@ -366,24 +246,4 @@
}
return nil
-}
-
-func (img *CoreGrubImage) FlashExtra(devicePart string) error {
- return nil
-}
-
-func bindMount(src, dst string) error {
- if out, err := exec.Command("mount", "--bind", src, dst).CombinedOutput(); err != nil {
- return fmt.Errorf("issues while bind mounting: %s", out)
- }
-
- return nil
-}
-
-func unmount(dst string) error {
- if out, err := exec.Command("umount", dst).CombinedOutput(); err != nil {
- return fmt.Errorf("issues while unmounting: %s", out)
- }
-
- return nil
}
diff -Nru goget-ubuntu-touch-0.19/diskimage/core_uboot.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_uboot.go
--- goget-ubuntu-touch-0.19/diskimage/core_uboot.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_uboot.go 2016-04-01 09:50:49.000000000 +0000
@@ -8,19 +8,13 @@
package diskimage
import (
- "bufio"
- "errors"
"fmt"
- "io"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
- "strings"
"text/template"
"launchpad.net/goget-ubuntu-touch/sysutils"
- "launchpad.net/goyaml"
)
// This program is free software: you can redistribute it and/or modify it
@@ -36,14 +30,7 @@
// with this program. If not, see .
type CoreUBootImage struct {
- CoreImage
- SystemImage
- hardware HardwareDescription
- location string
- size int64
- baseMount string
- parts []partition
- platform string
+ BaseImage
}
const snappySystemTemplate = `# This is a snappy variables and boot logic file and is entirely generated and
@@ -56,9 +43,9 @@
loadfdt=load mmc ${mmcdev}:${mmcpart} ${fdtaddr} ${snappy_ab}/dtbs/${fdtfile}
# standard kernel and initrd file names; NB: fdtfile is set early from bootcmd
-kernel_file=vmlinuz
-initrd_file=initrd.img
-{{ . }}
+kernel_file={{ .Kernel }}
+initrd_file={{ .Initrd }}
+{{ .Fdt }}
# extra kernel cmdline args, set via mmcroot
snappy_cmdline=init=/lib/systemd/systemd ro panic=-1 fixrtc
@@ -79,61 +66,24 @@
Bootloader []string `yaml:"bootloader"`
}
-func NewCoreUBootImage(location string, size int64, hw HardwareDescription, platform string) *CoreUBootImage {
- return &CoreUBootImage{
- hardware: hw,
- location: location,
- size: size,
- platform: platform,
- }
-}
-
-func (img *CoreUBootImage) Mount() (err error) {
- img.baseMount, err = ioutil.TempDir(os.TempDir(), "core-uboot-disk")
- if err != nil {
- return errors.New(fmt.Sprintf("Unable to create temp dir to create system image: %s", err))
- }
- //Remove Mountpoint if we fail along the way
- defer func() {
- if err != nil {
- os.Remove(img.baseMount)
- }
- }()
-
- for _, part := range img.parts {
- mountpoint := filepath.Join(img.baseMount, string(part.dir))
- if err := os.MkdirAll(mountpoint, 0755); err != nil {
- return err
- }
- if out, err := exec.Command("mount", filepath.Join("/dev/mapper", part.loop), mountpoint).CombinedOutput(); err != nil {
- return fmt.Errorf("unable to mount dir to create system image: %s", out)
- }
+func NewCoreUBootImage(location string, size int64, rootSize int, hw HardwareDescription, gadget GadgetDescription, label string) *CoreUBootImage {
+ var partCount int
+ switch gadget.PartitionLayout() {
+ case "minimal":
+ partCount = 2
}
- return nil
-}
-
-func (img *CoreUBootImage) Unmount() (err error) {
- if img.baseMount == "" {
- panic("No base mountpoint set")
- }
- defer func() {
- os.Remove(img.baseMount)
- img.baseMount = ""
- }()
-
- if out, err := exec.Command("sync").CombinedOutput(); err != nil {
- return fmt.Errorf("Failed to sync filesystems before unmounting: %s", out)
- }
-
- for _, part := range img.parts {
- mountpoint := filepath.Join(img.baseMount, string(part.dir))
- if out, err := exec.Command("umount", mountpoint).CombinedOutput(); err != nil {
- panic(fmt.Sprintf("unable to unmount dir for image: %s", out))
- }
+ return &CoreUBootImage{
+ BaseImage{
+ hardware: hw,
+ gadget: gadget,
+ location: location,
+ size: size,
+ rootSize: rootSize,
+ partCount: partCount,
+ label: label,
+ },
}
-
- return nil
}
//Partition creates a partitioned image from an img
@@ -141,15 +91,20 @@
if err := sysutils.CreateEmptyFile(img.location, img.size, sysutils.GB); err != nil {
return err
}
+ table := mkLabelMsdos
- parted, err := newParted(mkLabelMsdos)
+ if img.label == "gpt" {
+ table = mkLabelGpt
+ }
+ parted, err := newParted(table)
if err != nil {
return err
}
- parted.addPart(bootLabel, bootDir, fsFat32, 64)
- parted.addPart(systemALabel, systemADir, fsExt4, 1024)
- parted.addPart(systemBLabel, systemBDir, fsExt4, 1024)
+ switch img.gadget.PartitionLayout() {
+ case "minimal":
+ parted.addPart(bootLabel, bootDir, fsFat32, 128)
+ }
parted.addPart(writableLabel, writableDir, fsExt4, -1)
parted.setBoot(1)
@@ -159,190 +114,21 @@
return parted.create(img.location)
}
-//Map creates loop devices for the partitions
-func (img *CoreUBootImage) Map() error {
- if isMapped(img.parts) {
- panic("cannot double map partitions")
- }
-
- kpartxCmd := exec.Command("kpartx", "-avs", img.location)
- stdout, err := kpartxCmd.StdoutPipe()
- if err != nil {
- return err
- }
-
- if err := kpartxCmd.Start(); err != nil {
- return err
- }
-
- loops := make([]string, 0, 4)
- scanner := bufio.NewScanner(stdout)
- for scanner.Scan() {
- fields := strings.Fields(scanner.Text())
-
- if len(fields) > 2 {
- loops = append(loops, fields[2])
- } else {
- return errors.New("issues while determining drive mappings")
- }
- }
- if err := scanner.Err(); err != nil {
- return err
- }
-
- // there are 5 partitions, so there should be five loop mounts
- if len(loops) != 4 {
- return errors.New("more partitions then expected while creating loop mapping")
- }
-
- mapPartitions(img.parts, loops)
-
- if err := kpartxCmd.Wait(); err != nil {
- return err
- }
-
- return nil
-}
-
-//Unmap destroys loop devices for the partitions
-func (img *CoreUBootImage) Unmap() error {
- if img.baseMount != "" {
- panic("cannot unmap mounted partitions")
- }
-
- for _, part := range img.parts {
- if err := exec.Command("dmsetup", "clear", part.loop).Run(); err != nil {
- return err
- }
- }
-
- if err := exec.Command("kpartx", "-d", img.location).Run(); err != nil {
- return err
- }
-
- unmapPartitions(img.parts)
-
- return nil
-}
-
-func (img CoreUBootImage) Format() error {
- for _, part := range img.parts {
- dev := filepath.Join("/dev/mapper", part.loop)
-
- if part.fs == fsFat32 {
- cmd := []string{"-F", "32", "-n", string(part.label)}
-
- size, err := sectorSize(dev)
- if err != nil {
- return err
- }
-
- if size != "512" {
- cmd = append(cmd, "-s", "1")
- }
-
- cmd = append(cmd, "-S", size, dev)
-
- if out, err := exec.Command("mkfs.vfat", cmd...).CombinedOutput(); err != nil {
- return fmt.Errorf("unable to create filesystem: %s", out)
- }
- } else {
- if out, err := exec.Command("mkfs.ext4", "-F", "-L", string(part.label), dev).CombinedOutput(); err != nil {
- return fmt.Errorf("unable to create filesystem: %s", out)
- }
- }
- }
-
- return nil
-}
-
-// User returns the writable path
-func (img *CoreUBootImage) Writable() string {
- if img.parts == nil {
- panic("img is not setup with partitions")
- }
-
- if img.baseMount == "" {
- panic("img not mounted")
- }
-
- return filepath.Join(img.baseMount, string(writableDir))
-}
-
-//System returns the system path
-func (img *CoreUBootImage) System() string {
- if img.parts == nil {
- panic("img is not setup with partitions")
- }
-
- if img.baseMount == "" {
- panic("img not mounted")
- }
-
- return filepath.Join(img.baseMount, string(systemADir))
-}
-
-func (img CoreUBootImage) BaseMount() string {
- if img.baseMount == "" {
- panic("image needs to be mounted")
- }
-
- return img.baseMount
-}
-
-func (img CoreUBootImage) SetupBoot(oem OemDescription) error {
+func (img CoreUBootImage) SetupBoot() error {
// destinations
bootPath := filepath.Join(img.baseMount, string(bootDir))
- bootAPath := filepath.Join(bootPath, "a")
- bootBPath := filepath.Join(bootPath, "b")
- bootuEnvPath := filepath.Join(bootPath, "uEnv.txt")
bootSnappySystemPath := filepath.Join(bootPath, "snappy-system.txt")
- // origins
- hardwareYamlPath := filepath.Join(img.baseMount, "hardware.yaml")
- kernelPath := filepath.Join(img.baseMount, img.hardware.Kernel)
- initrdPath := filepath.Join(img.baseMount, img.hardware.Initrd)
-
- if err := os.MkdirAll(bootBPath, 0755); err != nil {
+ if err := img.GenericBootSetup(bootPath); err != nil {
return err
}
// populate both A/B
- for _, path := range []string{bootAPath, bootBPath} {
- if err := os.MkdirAll(path, 0755); err != nil {
+ for _, part := range img.gadget.SystemParts() {
+ bootDtbPath := filepath.Join(bootPath, part, "dtbs")
+ if err := img.provisionDtbs(bootDtbPath); err != nil {
return err
}
-
- if err := copyFile(hardwareYamlPath, filepath.Join(path, "hardware.yaml")); err != nil {
- return err
- }
-
- if err := copyFile(kernelPath, filepath.Join(path, filepath.Base(kernelPath))); err != nil {
- return err
- }
-
- if err := copyFile(initrdPath, filepath.Join(path, filepath.Base(initrdPath))); err != nil {
- return err
- }
-
- // create layout
- bootDtbPath := filepath.Join(path, "dtbs")
- if err := os.MkdirAll(bootDtbPath, 0755); err != nil {
- return err
- }
-
- if err := img.provisionDtbs(oem, bootDtbPath); err != nil {
- return err
- }
- }
-
- if err := img.provisionUenv(bootuEnvPath); err != nil {
- return err
- }
-
- // create /boot/uboot
- if err := os.MkdirAll(filepath.Join(img.System(), "boot", "uboot"), 755); err != nil {
- return err
}
snappySystemFile, err := os.Create(bootSnappySystemPath)
@@ -352,79 +138,67 @@
defer snappySystemFile.Close()
var fdtfile string
- if img.platform != "" {
- fdtfile = fmt.Sprintf("fdtfile=%s.dtb", img.platform)
+ if platform := img.gadget.Platform(); platform != "" {
+ fdtfile = fmt.Sprintf("fdtfile=%s.dtb", platform)
+ }
+
+ templateData := struct{ Fdt, Kernel, Initrd string }{
+ Fdt: fdtfile, Kernel: kernelFileName, Initrd: initrdFileName,
}
t := template.Must(template.New("snappy-system").Parse(snappySystemTemplate))
- t.Execute(snappySystemFile, fdtfile)
+ t.Execute(snappySystemFile, templateData)
return nil
}
-func (img CoreUBootImage) provisionUenv(bootuEnvPath string) error {
- if img.platform == "" {
- printOut("No platform select, not searching for uEnv.txt")
- return nil
- }
-
- flashAssetsPath := filepath.Join(img.baseMount, "flashtool-assets", img.platform)
- uEnvPath := filepath.Join(flashAssetsPath, "uEnv.txt")
+func (img CoreUBootImage) provisionDtbs(bootDtbPath string) error {
+ dtbsPath := filepath.Join(img.baseMount, img.hardware.Dtbs)
- if _, err := os.Stat(flashAssetsPath); os.IsNotExist(err) {
- printOut("No flash assets path available")
+ if _, err := os.Stat(dtbsPath); os.IsNotExist(err) {
return nil
} else if err != nil {
return err
}
- // if a uEnv.txt is provided in the flashtool-assets, use it
- if _, err := os.Stat(uEnvPath); err == nil {
- printOut("Adding uEnv.txt to", bootuEnvPath)
- if err := copyFile(uEnvPath, bootuEnvPath); err != nil {
+ var dtbFis []os.FileInfo
+ if img.hardware.Dtbs != "" {
+ var err error
+ dtbFis, err = ioutil.ReadDir(dtbsPath)
+ if err != nil {
return err
}
- } else {
- printOut("Can't copy", uEnvPath, "to", bootuEnvPath, "due to:", err)
- }
-
- return nil
-}
-
-func (img CoreUBootImage) provisionDtbs(oem OemDescription, bootDtbPath string) error {
- dtbsPath := filepath.Join(img.baseMount, img.hardware.Dtbs)
-
- if _, err := os.Stat(dtbsPath); os.IsNotExist(err) {
- return nil
- } else if err != nil {
- return err
}
- dtbFis, err := ioutil.ReadDir(dtbsPath)
- if err != nil {
+ if err := os.MkdirAll(bootDtbPath, 0755); err != nil {
return err
}
- dtb := filepath.Join(dtbsPath, fmt.Sprintf("%s.dtb", img.platform))
+ dtb := filepath.Join(dtbsPath, fmt.Sprintf("%s.dtb", img.gadget.Platform()))
// if there is a specific dtb for the platform, copy it.
- // First look in oem and then in device.
- if oem.Hardware.Dtb != "" && img.platform != "" {
- oemDtb := filepath.Join(img.System(), oem.InstallPath(), oem.Hardware.Dtb)
+ // First look in gadget and then in device.
+ if gadgetDtb := img.gadget.Gadget.Hardware.Dtb; gadgetDtb != "" && img.gadget.Platform() != "" {
+ gadgetRoot, err := img.gadget.InstallPath()
+ if err != nil {
+ return err
+ }
+
+ gadgetDtb := filepath.Join(gadgetRoot, gadgetDtb)
dst := filepath.Join(bootDtbPath, filepath.Base(dtb))
- if err := copyFile(oemDtb, dst); err != nil {
+ if err := sysutils.CopyFile(gadgetDtb, dst); err != nil {
return err
}
} else if _, err := os.Stat(dtb); err == nil {
dst := filepath.Join(bootDtbPath, filepath.Base(dtb))
- if err := copyFile(dtb, dst); err != nil {
+ if err := sysutils.CopyFile(dtb, dst); err != nil {
return err
}
} else {
for _, dtbFi := range dtbFis {
src := filepath.Join(dtbsPath, dtbFi.Name())
dst := filepath.Join(bootDtbPath, dtbFi.Name())
- if err := copyFile(src, dst); err != nil {
+ if err := sysutils.CopyFile(src, dst); err != nil {
return err
}
}
@@ -432,86 +206,3 @@
return nil
}
-
-func (img *CoreUBootImage) FlashExtra(devicePart string) error {
- if img.platform == "" {
- return nil
- }
-
- tmpdir, err := ioutil.TempDir("", "device")
- if err != nil {
- return errors.New("cannot create tempdir to extract hardware.yaml from device part")
- }
- defer os.RemoveAll(tmpdir)
-
- if out, err := exec.Command("tar", "xf", devicePart, "-C", tmpdir, "flashtool-assets").CombinedOutput(); err != nil {
- fmt.Println("No flashtool-assets found, skipping...")
- if debugPrint {
- fmt.Println("device part:", devicePart)
- fmt.Println("command output:", string(out))
- }
-
- return nil
- }
-
- flashAssetsPath := filepath.Join(tmpdir, "flashtool-assets", img.platform)
- flashPath := filepath.Join(flashAssetsPath, "flash.yaml")
-
- if _, err := os.Stat(flashPath); err != nil && os.IsNotExist(err) {
- return nil
- } else if err != nil {
- return err
- }
-
- data, err := ioutil.ReadFile(flashPath)
- if err != nil {
- return err
- }
-
- var flash FlashInstructions
- if err := goyaml.Unmarshal([]byte(data), &flash); err != nil {
- return err
- }
-
- fmt.Println("Running flashtool-asset commands")
- for _, cmd := range flash.Bootloader {
- cmd = fmt.Sprintf(cmd, flashAssetsPath, img.location)
- cmdFields := strings.Fields(cmd)
-
- if out, err := exec.Command(cmdFields[0], cmdFields[1:]...).CombinedOutput(); err != nil {
- return fmt.Errorf("failed to run flash command: %s : %s", cmd, out)
- } else {
- fmt.Println(cmd)
- fmt.Println(string(out))
- }
- }
-
- return nil
-}
-
-func copyFile(src, dst string) error {
- dstFile, err := os.Create(dst)
- if err != nil {
- return err
- }
- defer dstFile.Close()
-
- srcFile, err := os.Open(src)
- if err != nil {
- return err
- }
- defer srcFile.Close()
-
- reader := bufio.NewReader(srcFile)
- writer := bufio.NewWriter(dstFile)
- defer func() {
- if err != nil {
- writer.Flush()
- }
- }()
- if _, err = io.Copy(writer, reader); err != nil {
- return err
- }
- writer.Flush()
- return nil
-}
diff -Nru goget-ubuntu-touch-0.19/diskimage/errors.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/errors.go
--- goget-ubuntu-touch-0.19/diskimage/errors.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/errors.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,45 @@
+//
+// diskimage - handles ubuntu disk images
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package diskimage
+
+import (
+ "fmt"
+ "strings"
+)
+
+// ErrMount represents a mount error
+type ErrMount struct {
+ dev string
+ mountpoint string
+ fs fsType
+ out []byte
+}
+
+func (e ErrMount) Error() string {
+ return fmt.Sprintf("cannot mount %s(%s) on %s: %s", e.dev, e.fs, e.mountpoint, e.out)
+}
+
+// ErrMapCount represents an error on the expected amount of partitions
+type ErrMapCount struct {
+ foundParts int
+ expectedParts int
+}
+
+func (e ErrMapCount) Error() string {
+ return fmt.Sprintf("expected %d partitons but found %d", e.expectedParts, e.foundParts)
+}
+
+// ErrExec is an error from an external command
+type ErrExec struct {
+ output []byte
+ command []string
+}
+
+func (e ErrExec) Error() string {
+ return fmt.Sprintf("error while executing external command %s: %s", strings.Join(e.command, " "), e.output)
+}
diff -Nru goget-ubuntu-touch-0.19/diskimage/image.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/image.go
--- goget-ubuntu-touch-0.19/diskimage/image.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/image.go 2016-03-29 15:39:25.000000000 +0000
@@ -221,7 +221,7 @@
return err
}
} else if !fi.IsDir() {
- return fmt.Errorf("extract dir %s is not a directory")
+ return fmt.Errorf("extract dir %s is not a directory", fi.Name())
}
dstFile, err := os.Create(filepath.Join(dir, filePath))
if err != nil {
diff -Nru goget-ubuntu-touch-0.19/diskimage/partition.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/partition.go
--- goget-ubuntu-touch-0.19/diskimage/partition.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/partition.go 2016-04-01 09:50:49.000000000 +0000
@@ -5,6 +5,7 @@
//
// Written by Sergio Schvezov
//
+
package diskimage
import (
diff -Nru goget-ubuntu-touch-0.19/run-checks goget-ubuntu-touch-0.33-0ubuntu1~xenial/run-checks
--- goget-ubuntu-touch-0.19/run-checks 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/run-checks 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+set -e
+
+if which goctest >/dev/null; then
+ goctest="goctest"
+else
+ goctest="go test"
+fi
+
+echo Checking formatting
+fmt=$(gofmt -l .)
+
+if [ -n "$fmt" ]; then
+ echo "Formatting wrong in following files"
+ echo $fmt
+ exit 1
+fi
+
+echo Installing godeps
+go get launchpad.net/godeps
+export PATH=$PATH:$GOPATH/bin
+
+echo Install golint
+go get github.com/golang/lint/golint
+export PATH=$PATH:$GOPATH/bin
+
+echo Obtaining dependencies
+godeps -u dependencies.tsv
+
+
+
+echo Building
+go build -v launchpad.net/goget-ubuntu-touch/...
+
+
+# tests
+echo Running tests from $(pwd)
+$goctest -v -cover ./...
+
+
+# go vet
+echo Running vet
+go vet ./...
+
+# golint
+# TODO enable!
+#echo Running lint
+#lint=$(golint ./...)
+#if [ -n "$lint" ]; then
+# echo "Lint complains:"
+# echo $lint
+# exit 1
+#fi
+
+echo "All good, what could possibly go wrong"
diff -Nru goget-ubuntu-touch-0.19/sysutils/fileops.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/fileops.go
--- goget-ubuntu-touch-0.19/sysutils/fileops.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/fileops.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,39 @@
+package sysutils
+
+import (
+ "bufio"
+ "io"
+ "os"
+)
+
+func CopyFile(src, dst string) error {
+ srcStat, err := os.Stat(src)
+ if err != nil {
+ return err
+ }
+
+ dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, srcStat.Mode())
+ if err != nil {
+ return err
+ }
+ defer dstFile.Close()
+
+ srcFile, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer srcFile.Close()
+
+ reader := bufio.NewReader(srcFile)
+ writer := bufio.NewWriter(dstFile)
+ defer func() {
+ if err != nil {
+ writer.Flush()
+ }
+ }()
+ if _, err = io.Copy(writer, reader); err != nil {
+ return err
+ }
+ writer.Flush()
+ return nil
+}
diff -Nru goget-ubuntu-touch-0.19/sysutils/fileops_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/fileops_test.go
--- goget-ubuntu-touch-0.19/sysutils/fileops_test.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/fileops_test.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,51 @@
+package sysutils
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ . "launchpad.net/gocheck"
+)
+
+type FileOpsTestSuite struct {
+ tmpdir string
+ srcPath string
+ dstPath string
+}
+
+var _ = Suite(&FileOpsTestSuite{})
+
+func (s *FileOpsTestSuite) SetUpTest(c *C) {
+ s.tmpdir = c.MkDir()
+ s.srcPath = filepath.Join(s.tmpdir, "src")
+ s.dstPath = filepath.Join(s.tmpdir, "dst")
+}
+
+func (s *FileOpsTestSuite) TestCopyModes(c *C) {
+ c.Assert(ioutil.WriteFile(s.srcPath, []byte("src"), 0755), IsNil)
+ c.Assert(CopyFile(s.srcPath, s.dstPath), IsNil)
+
+ stat, err := os.Stat(s.dstPath)
+ c.Assert(err, IsNil)
+
+ mode := stat.Mode()
+ c.Assert(mode.IsRegular(), Equals, true)
+ c.Assert(mode.String(), Equals, "-rwxr-xr-x")
+
+ contents, err := ioutil.ReadFile(s.dstPath)
+ c.Assert(err, IsNil)
+ c.Assert(string(contents), Equals, "src")
+}
+
+func (s *FileOpsTestSuite) TestCopyNoSourceFails(c *C) {
+ err := CopyFile(s.srcPath, s.dstPath)
+ c.Assert(err, NotNil)
+ c.Assert(os.IsNotExist(err), Equals, true)
+}
+
+func (s *FileOpsTestSuite) TestCopyDirFails(c *C) {
+ c.Assert(os.MkdirAll(s.srcPath, 0755), IsNil)
+ err := CopyFile(s.srcPath, s.dstPath)
+ c.Assert(err, NotNil)
+}
diff -Nru goget-ubuntu-touch-0.19/sysutils/utils.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/utils.go
--- goget-ubuntu-touch-0.19/sysutils/utils.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/utils.go 2016-03-29 15:39:25.000000000 +0000
@@ -52,7 +52,9 @@
case GB:
// The image size will be reduced to fit commercial drives that are
// smaller than what they claim, 975 comes from 97.5% of the total size
- size = size * 1000 * 1000 * 975
+ // but we want to be a multiple of 512 (and size is an int) we divide by
+ // 512 and multiply it again
+ size = size * 1000 * 1000 * 975 / 512 * 512
default:
panic("improper sizing unit used")
}
diff -Nru goget-ubuntu-touch-0.19/sysutils/utils_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/utils_test.go
--- goget-ubuntu-touch-0.19/sysutils/utils_test.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/sysutils/utils_test.go 2016-03-29 15:39:25.000000000 +0000
@@ -38,7 +38,7 @@
fStat1, err := os.Stat(f1)
c.Assert(err, IsNil)
- c.Assert(fStat1.Size(), Equals, int64(1000*1000*975))
+ c.Assert(fStat1.Size(), Equals, int64(974999552))
f2 := filepath.Join(s.tmpdir, "gb")
c.Assert(CreateEmptyFile(f2, 2, GB), IsNil)
@@ -46,5 +46,5 @@
fStat2, err := os.Stat(f2)
c.Assert(err, IsNil)
- c.Assert(fStat2.Size(), Equals, int64(2*1000*1000*975))
+ c.Assert(fStat2.Size(), Equals, int64(1949999616))
}
diff -Nru goget-ubuntu-touch-0.19/.tarmac.sh goget-ubuntu-touch-0.33-0ubuntu1~xenial/.tarmac.sh
--- goget-ubuntu-touch-0.19/.tarmac.sh 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/.tarmac.sh 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+set -ev
+
+# we always run in a fresh dir in tarmac
+export GOPATH=$(mktemp -d)
+trap 'rm -rf "$GOPATH"' EXIT
+
+# this is a hack, but not sure tarmac is golang friendly
+mkdir -p $GOPATH/src/launchpad.net/goget-ubuntu-touch
+cp -a . $GOPATH/src/launchpad.net/goget-ubuntu-touch
+cd $GOPATH/src/launchpad.net/goget-ubuntu-touch
+
+./run-checks
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/channel_maps.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/channel_maps.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/channel_maps.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/channel_maps.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,56 @@
+//
+// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image
+// based system
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+
+package main
+
+import "path"
+
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License version 3, as published
+// by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranties of
+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see .
+
+func systemImageChannel(flavor, release, channel string) string {
+ return path.Join(flavor, release, channel)
+}
+
+const (
+ archArmhf = "armhf"
+ archArm64 = "arm64"
+ archAmd64 = "amd64"
+ archi386 = "i386"
+)
+
+const (
+ deviceArmhf = "generic_armhf"
+ deviceArm64 = "generic_arm64"
+ deviceAmd64 = "generic_amd64"
+ devicei386 = "generic_i386"
+)
+
+func systemImageDeviceChannel(arch string) string {
+ switch arch {
+ case archArmhf:
+ return deviceArmhf
+ case archArm64:
+ return deviceArm64
+ case archAmd64:
+ return deviceAmd64
+ case archi386:
+ return devicei386
+ }
+
+ return arch
+}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/common.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/common.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/common.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/common.go 2016-04-01 09:50:49.000000000 +0000
@@ -1,9 +1,7 @@
package main
import (
- "bufio"
"fmt"
- "io"
"log"
"os"
"path/filepath"
@@ -63,11 +61,11 @@
// expandFile checks for file existence, correct permissions and returns the absolute path.
func expandFile(path string) (abspath string, err error) {
- if p, err := filepath.Abs(path); err != nil {
+ p, err := filepath.Abs(path)
+ if err != nil {
return "", err
- } else {
- abspath = p
}
+ abspath = p
fi, err := os.Lstat(abspath)
if err != nil {
@@ -90,30 +88,3 @@
func isCustomPart(path string) bool {
return strings.Contains(path, "custom")
}
-
-func copyFile(src, dst string) error {
- dstFile, err := os.Create(dst)
- if err != nil {
- return err
- }
- defer dstFile.Close()
-
- srcFile, err := os.Open(src)
- if err != nil {
- return err
- }
- defer srcFile.Close()
-
- reader := bufio.NewReader(srcFile)
- writer := bufio.NewWriter(dstFile)
- defer func() {
- if err != nil {
- writer.Flush()
- }
- }()
- if _, err = io.Copy(writer, reader); err != nil {
- return err
- }
- writer.Flush()
- return nil
-}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/core.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/core.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/core.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/core.go 2016-04-01 09:50:49.000000000 +0000
@@ -14,19 +14,9 @@
"io"
"io/ioutil"
"os"
- "os/exec"
- "os/signal"
"path/filepath"
- "runtime"
"strings"
- "syscall"
"time"
-
- "launchpad.net/goyaml"
-
- "launchpad.net/goget-ubuntu-touch/diskimage"
- "launchpad.net/goget-ubuntu-touch/sysutils"
- "launchpad.net/goget-ubuntu-touch/ubuntuimage"
)
// This program is free software: you can redistribute it and/or modify it
@@ -49,22 +39,15 @@
}
type CoreCmd struct {
- Channel string `long:"channel" description:"Specify the channel to use" default:"ubuntu-core/devel"`
- Device string `long:"device" description:"Specify the device to use" default:"generic_amd64"`
- Keyboard string `long:"keyboard-layout" description:"Specify the keyboard layout" default:"us"`
- Output string `long:"output" short:"o" description:"Name of the image file to create" required:"true"`
- Size int64 `long:"size" short:"s" description:"Size of image file to create in GB (min 4)" default:"20"`
- DeveloperMode bool `long:"developer-mode" description:"Finds the latest public key in your ~/.ssh and sets it up using cloud-init"`
- EnableSsh bool `long:"enable-ssh" description:"Enable ssh on the image through cloud-init(not needed with developer mode)"`
- Cloud bool `long:"cloud" description:"Generate a pure cloud image without setting up cloud-init"`
- Platform string `long:"platform" description:"specify the boards platform"`
- Install []string `long:"install" description:"install additional packages (can be called multiple times)"`
-
- Development struct {
- DevicePart string `long:"device-part" description:"Specify a local device part to override the one from the server"`
- } `group:"Development"`
+ EnableSsh bool `long:"enable-ssh" description:"Enable ssh on the image through cloud-init(not needed with developer mode)"`
+ Size int64 `long:"size" short:"s" description:"Size of image file to create in GB (min 4)" default:"4"`
+
+ Deprecated struct {
+ Cloud bool `long:"cloud" description:"Generate a pure cloud image without setting up cloud-init"`
+ Device string `long:"device" description:"Specify the device to use"`
+ } `group:"Deprecated"`
- hardware diskimage.HardwareDescription
+ Snapper
}
var coreCmd CoreCmd
@@ -80,325 +63,76 @@
`
func (coreCmd *CoreCmd) Execute(args []string) error {
- if coreCmd.EnableSsh && coreCmd.Cloud {
- return errors.New("--cloud and --enable-ssh cannot be used together")
- }
-
- var devicePart string
- if coreCmd.Development.DevicePart != "" {
- p, err := expandFile(coreCmd.Development.DevicePart)
- if err != nil {
- return err
- }
-
- devicePart = p
- }
-
- if !globalArgs.DownloadOnly {
- if syscall.Getuid() != 0 {
- return errors.New("command requires sudo/pkexec (root)")
- }
-
- // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
- runtime.GOMAXPROCS(1)
- runtime.LockOSThread()
- if err := sysutils.DropPrivs(); err != nil {
- return err
- }
- }
-
- fmt.Println("Fetching information from server...")
-
- channels, err := ubuntuimage.NewChannels(globalArgs.Server)
- if err != nil {
- return err
- }
-
- deviceChannel, err := channels.GetDeviceChannel(globalArgs.Server, coreCmd.Channel, coreCmd.Device)
- if err != nil {
- return err
- }
-
- image, err := getImage(deviceChannel)
- if err != nil {
- return err
- }
-
- filesChan := make(chan Files, len(image.Files))
- defer close(filesChan)
-
- sigFiles := ubuntuimage.GetGPGFiles()
- sigFilesChan := make(chan Files, len(sigFiles))
- defer close(sigFilesChan)
-
- fmt.Println("Downloading and setting up...")
-
- for _, f := range sigFiles {
- go bitDownloader(f, sigFilesChan, globalArgs.Server, cacheDir)
- }
-
- filePathChan := make(chan string, len(image.Files))
- hwChan := make(chan diskimage.HardwareDescription)
-
- go func() {
- for i := 0; i < len(image.Files); i++ {
- f := <-filesChan
-
- if isDevicePart(f.FilePath) {
- devicePart = f.FilePath
-
- if hardware, err := extractHWDescription(f.FilePath); err != nil {
- fmt.Println("Failed to read harware.yaml from device part, provisioning may fail:", err)
- } else {
- hwChan <- hardware
- }
- }
-
- printOut("Download finished for", f.FilePath)
- filePathChan <- f.FilePath
- }
- close(hwChan)
- }()
+ coreCmd.flavor = flavorCore
+ coreCmd.size = coreCmd.Size
- for _, f := range image.Files {
- if devicePart != "" && isDevicePart(f.Path) {
- printOut("Using a custom device tarball")
- filesChan <- Files{FilePath: devicePart}
- } else {
- go bitDownloader(f, filesChan, globalArgs.Server, cacheDir)
- }
- }
-
- coreCmd.hardware = <-hwChan
-
- var img diskimage.CoreImage
-
- if globalArgs.DownloadOnly {
- workDir, err := os.Getwd()
- if err != nil {
- return err
- }
-
- downloadedFiles := make([]string, 0, len(image.Files))
-
- for i := 0; i < len(image.Files); i++ {
- f := <-filePathChan
- baseFile := filepath.Base(f)
-
- if err := copyFile(f, filepath.Join(workDir, baseFile)); err != nil {
- return err
- }
- downloadedFiles = append(downloadedFiles, baseFile)
- }
-
- fmt.Println("Files downloaded to current directory: ")
- for _, f := range downloadedFiles {
- fmt.Println(" -", f)
- }
- fmt.Println()
-
- return nil
+ if coreCmd.EnableSsh && coreCmd.Deprecated.Cloud {
+ return errors.New("--cloud and --enable-ssh cannot be used together")
}
- switch coreCmd.hardware.Bootloader {
- case "grub":
- img = diskimage.NewCoreGrubImage(coreCmd.Output, coreCmd.Size)
- case "u-boot":
- img = diskimage.NewCoreUBootImage(coreCmd.Output, coreCmd.Size, coreCmd.hardware, coreCmd.Platform)
- default:
- fmt.Printf("Bootloader set to '%s' in hardware description, assuming grub as a fallback\n", coreCmd.hardware.Bootloader)
- img = diskimage.NewCoreGrubImage(coreCmd.Output, coreCmd.Size)
+ if coreCmd.Deprecated.Device != "" {
+ fmt.Println("WARNING: this option should only be used to build azure images")
+ coreCmd.device = coreCmd.Deprecated.Device
}
- printOut("Partitioning...")
- if err := img.Partition(); err != nil {
- return err
+ if !coreCmd.Deprecated.Cloud {
+ coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupCloudInit)
+ coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupGadgetConfigs)
}
- defer func() {
- if err != nil {
- //os.Remove(coreCmd.Output)
- }
- }()
- // Handle SIGINT and SIGTERM.
- go func() {
- ch := make(chan os.Signal)
- signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
-
- for sig := range ch {
- printOut("Received", sig, "... ignoring")
- }
- }()
-
- // Execute the following code with escalated privs and drop them when done
- err = func() error {
- // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
- runtime.GOMAXPROCS(1)
- runtime.LockOSThread()
- if err := sysutils.EscalatePrivs(); err != nil {
- return err
- }
- defer sysutils.DropPrivs()
-
- if err := format(img); err != nil {
- return err
- }
-
- if err := coreCmd.setup(img, filePathChan, len(image.Files)); err != nil {
- return err
- }
+ return coreCmd.create()
+}
+// this is a hackish way to get the config in place
+func (coreCmd *CoreCmd) setupGadgetConfigs() error {
+ modprobeDContent := coreCmd.gadget.Config.UbuntuCore.Modprobe
+ if modprobeDContent == nil {
return nil
- }()
- if err != nil {
- return err
- }
-
- if err := img.FlashExtra(devicePart); err != nil {
- return err
}
- fmt.Println("New image complete")
- if coreCmd.Device != "generic_armhf" {
- fmt.Println("Launch by running: kvm -m 768", coreCmd.Output)
- }
-
- return nil
-}
-
-func format(img diskimage.Image) error {
- printOut("Mapping...")
- if err := img.Map(); err != nil {
- return fmt.Errorf("issue while mapping partitions: %s", err)
- }
- defer img.Unmap()
+ fmt.Println("Setting up gadget hooks...")
- printOut("Formatting...")
- return img.Format()
-}
+ writablePath := coreCmd.img.Writable()
-func (coreCmd *CoreCmd) setup(img diskimage.CoreImage, filePathChan <-chan string, fileCount int) error {
- printOut("Mapping...")
- if err := img.Map(); err != nil {
- return err
- }
- defer func() {
- printOut("Unmapping...")
- defer img.Unmap()
- }()
-
- printOut("Mounting...")
- if err := img.Mount(); err != nil {
- fmt.Println(err)
+ modprobeDir := filepath.Join(writablePath, "system-data", "etc", "modprobe.d")
+ if err := os.MkdirAll(modprobeDir, 0755); err != nil {
return err
}
- defer func() {
- printOut("Unmounting...")
- if err := img.Unmount(); err != nil {
- fmt.Println(err)
- }
- }()
-
- printOut("Provisioning...")
- for i := 0; i < fileCount; i++ {
- f := <-filePathChan
- if out, err := exec.Command("fakeroot", "tar", "--numeric-owner", "-axvf", f, "-C", img.BaseMount()).CombinedOutput(); err != nil {
- fmt.Println(string(out))
- return fmt.Errorf("issues while extracting: %s", out)
- }
- }
-
- writablePath := img.Writable()
- for _, dir := range []string{"system-data", "cache"} {
- dirPath := filepath.Join(writablePath, dir)
- if err := os.Mkdir(dirPath, 0755); err != nil {
- return err
- }
- }
-
- systemPath := img.System()
-
- if err := coreCmd.install(systemPath); err != nil {
+ // first we need to copy all the files in modprobe.d
+ systemModprobeDir := filepath.Join(coreCmd.img.System(), "etc", "modprobe.d")
+ // FIXME: can we do "cp -a" here?
+ if err := RSyncWithDelete(systemModprobeDir, modprobeDir); err != nil {
return err
}
- // check if we installed an oem snap
- oem, err := loadOem(systemPath)
+ modprobeD := filepath.Join(modprobeDir, "ubuntu-core.conf")
+ modprobeDFile, err := os.Create(modprobeD)
if err != nil {
return err
}
+ defer modprobeDFile.Close()
- if err := img.SetupBoot(oem); err != nil {
+ if _, err := io.WriteString(modprobeDFile, *modprobeDContent); err != nil {
return err
}
- if err := coreCmd.setupKeyboardLayout(systemPath); err != nil {
- return err
- }
-
- if !coreCmd.Cloud {
- cloudBaseDir := filepath.Join("var", "lib", "cloud")
-
- if err := os.MkdirAll(filepath.Join(systemPath, cloudBaseDir), 0755); err != nil {
- return err
- }
-
- if err := coreCmd.setupCloudInit(cloudBaseDir, filepath.Join(writablePath, "system-data")); err != nil {
- return err
- }
- }
-
- // if the device is armhf, we can't to make this copy here since it's faster
- // than on the device.
- if coreCmd.Device == "generic_armhf" && coreCmd.hardware.PartitionLayout == "system-AB" {
- printOut("Replicating system-a into system-b")
-
- src := fmt.Sprintf("%s/.", systemPath)
- dst := fmt.Sprintf("%s/system-b", img.BaseMount())
-
- cmd := exec.Command("cp", "-r", "--preserve=all", src, dst)
- if out, err := cmd.CombinedOutput(); err != nil {
- return fmt.Errorf("failed to replicate image contents: %s", out)
- }
- }
-
return nil
}
-func (coreCmd *CoreCmd) install(systemPath string) error {
- if strings.Contains(coreCmd.Device, "armhf") {
- if err := sysutils.AddQemuStatic(systemPath); err != nil {
- return err
- }
- defer sysutils.RemoveQemuStatic(systemPath)
- }
-
- if err := sysutils.ChrootBindMount(systemPath); err != nil {
- return err
- }
- defer sysutils.ChrootBindUnmount(systemPath)
-
- for _, snap := range coreCmd.Install {
- snapBase := filepath.Base(snap)
- fmt.Println("Installing", snapBase)
+// this function is a hack and should be part of first boot.
+func (coreCmd *CoreCmd) setupCloudInit() error {
+ systemPath := coreCmd.img.System()
+ writablePath := coreCmd.img.Writable()
- if err := copyFile(snap, filepath.Join(systemPath, snapBase)); err != nil {
- return err
- }
- defer os.Remove(filepath.Join(systemPath, snapBase))
+ cloudBaseDir := filepath.Join("var", "lib", "cloud")
- if err := sysutils.ChrootRun(systemPath, "snappy", "install", snapBase); err != nil {
- return err
- }
+ if err := os.MkdirAll(filepath.Join(systemPath, cloudBaseDir), 0755); err != nil {
+ return err
}
- return nil
-}
-
-func (coreCmd *CoreCmd) setupCloudInit(cloudBaseDir, systemData string) error {
// create a basic cloud-init seed
- cloudDir := filepath.Join(systemData, cloudBaseDir, "seed", "nocloud-net")
+ cloudDir := filepath.Join(writablePath, "system-data", cloudBaseDir, "seed", "nocloud-net")
if err := os.MkdirAll(cloudDir, 0755); err != nil {
return err
}
@@ -413,7 +147,7 @@
return err
}
- if coreCmd.DeveloperMode {
+ if coreCmd.Development.DeveloperMode {
fmt.Println("Enabling developer mode...")
authorizedKey, err := getAuthorizedSshKey()
@@ -441,7 +175,7 @@
return err
}
- if coreCmd.DeveloperMode || coreCmd.EnableSsh {
+ if coreCmd.Development.DeveloperMode || coreCmd.EnableSsh {
if _, err := io.WriteString(userDataFile, "snappy:\n ssh_enabled: True\n"); err != nil {
return err
}
@@ -450,25 +184,6 @@
return nil
}
-func (coreCmd *CoreCmd) setupKeyboardLayout(systemPath string) error {
- kbFilePath := filepath.Join(systemPath, "etc", "default", "keyboard")
-
- // do not error if the image has no keyboard
- if _, err := os.Stat(kbFilePath); err != nil && os.IsNotExist(err) {
- return nil
- }
-
- kbFileContents, err := ioutil.ReadFile(kbFilePath)
- if err != nil {
- return err
- }
-
- r := strings.NewReplacer("XKBLAYOUT=\"us\"", fmt.Sprintf("XKBLAYOUT=\"%s\"", coreCmd.Keyboard))
- kbFileContents = []byte(r.Replace(string(kbFileContents)))
-
- return ioutil.WriteFile(kbFilePath, kbFileContents, 0644)
-}
-
func getAuthorizedSshKey() (string, error) {
sshDir := os.ExpandEnv("$HOME/.ssh")
@@ -499,60 +214,3 @@
return string(pubKey), err
}
-
-func loadOem(systemPath string) (oem diskimage.OemDescription, err error) {
- pkgs, err := filepath.Glob(filepath.Join(systemPath, "/oem/*/*/meta/package.yaml"))
- if err != nil {
- return oem, err
- }
-
- // checking for len(pkgs) > 2 due to the 'current' symlink
- if len(pkgs) == 0 {
- return oem, nil
- } else if len(pkgs) > 2 || err != nil {
- return oem, errors.New("too many oem packages installed")
- }
-
- f, err := ioutil.ReadFile(pkgs[0])
- if err != nil {
- return oem, errors.New("failed to read oem yaml")
- }
-
- if err := goyaml.Unmarshal([]byte(f), &oem); err != nil {
- return oem, errors.New("cannot decode oem yaml")
- }
-
- return oem, nil
-}
-
-func extractHWDescription(path string) (hw diskimage.HardwareDescription, err error) {
- // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
- if syscall.Getuid() == 0 {
- runtime.GOMAXPROCS(1)
- runtime.LockOSThread()
-
- if err := sysutils.DropPrivs(); err != nil {
- return hw, err
- }
- }
-
- printOut("Searching for hardware.yaml in device part")
- tmpdir, err := ioutil.TempDir("", "hardware")
- if err != nil {
- return hw, errors.New("cannot create tempdir to extract hardware.yaml from device part")
- }
- defer os.RemoveAll(tmpdir)
-
- if out, err := exec.Command("tar", "xf", path, "-C", tmpdir, "hardware.yaml").CombinedOutput(); err != nil {
- return hw, fmt.Errorf("failed to extract a hardware.yaml from the device part: %s", out)
- }
-
- data, err := ioutil.ReadFile(filepath.Join(tmpdir, "hardware.yaml"))
- if err != nil {
- return hw, err
- }
-
- err = goyaml.Unmarshal([]byte(data), &hw)
-
- return hw, err
-}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/helpers.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/helpers.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/helpers.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/helpers.go 2016-04-01 09:50:49.000000000 +0000
@@ -0,0 +1,89 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2014-2015 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "syscall"
+
+ "github.com/ubuntu-core/snappy/osutil"
+)
+
+// RSyncWithDelete syncs srcDir to destDir
+func RSyncWithDelete(srcDirName, destDirName string) error {
+ // first remove everything thats not in srcdir
+ err := filepath.Walk(destDirName, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // relative to the root "destDirName"
+ relPath := path[len(destDirName):]
+ if !osutil.FileExists(filepath.Join(srcDirName, relPath)) {
+ if err := os.RemoveAll(path); err != nil {
+ return err
+ }
+ if info.IsDir() {
+ return filepath.SkipDir
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+
+ // then copy or update the data from srcdir to destdir
+ err = filepath.Walk(srcDirName, func(src string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // relative to the root "srcDirName"
+ relPath := src[len(srcDirName):]
+ dst := filepath.Join(destDirName, relPath)
+ if info.IsDir() {
+ if err := os.MkdirAll(dst, info.Mode()); err != nil {
+ return err
+ }
+
+ // this can panic. The alternative would be to use the "st, ok" pattern, and then if !ok... panic?
+ st := info.Sys().(*syscall.Stat_t)
+ ts := []syscall.Timespec{st.Atim, st.Mtim}
+
+ return syscall.UtimesNano(dst, ts)
+ }
+ if !osutil.FilesAreEqual(src, dst) {
+ // XXX: we should (eventually) use CopyFile here,
+ // but we need to teach it about preserving
+ // of atime/mtime and permissions
+ output, err := exec.Command("cp", "-va", src, dst).CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("Failed to copy %s to %s (%s)", src, dst, output)
+ }
+ }
+ return nil
+ })
+
+ return err
+}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/helpers_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/helpers_test.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/helpers_test.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/helpers_test.go 2016-04-01 10:09:12.000000000 +0000
@@ -0,0 +1,135 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2014-2015 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+
+ . "launchpad.net/gocheck"
+)
+
+type HTestSuite struct{}
+
+var _ = Suite(&HTestSuite{})
+
+func makeTestFiles(c *C, srcDir, destDir string) {
+ // a new file
+ err := ioutil.WriteFile(filepath.Join(srcDir, "new"), []byte(nil), 0644)
+ c.Assert(err, IsNil)
+
+ // a existing file that needs update
+ err = ioutil.WriteFile(filepath.Join(destDir, "existing-update"), []byte("old-content"), 0644)
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-update"), []byte("some-new-content"), 0644)
+ c.Assert(err, IsNil)
+
+ // existing file that needs no update
+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-unchanged"), []byte(nil), 0644)
+ c.Assert(err, IsNil)
+ err = exec.Command("cp", "-a", filepath.Join(srcDir, "existing-unchanged"), filepath.Join(destDir, "existing-unchanged")).Run()
+ c.Assert(err, IsNil)
+
+ // a file that needs removal
+ err = ioutil.WriteFile(filepath.Join(destDir, "to-be-deleted"), []byte(nil), 0644)
+ c.Assert(err, IsNil)
+}
+
+func compareDirs(c *C, srcDir, destDir string) {
+ d1, err := exec.Command("ls", "-al", srcDir).CombinedOutput()
+ c.Assert(err, IsNil)
+ d2, err := exec.Command("ls", "-al", destDir).CombinedOutput()
+ c.Assert(err, IsNil)
+ c.Assert(string(d1), Equals, string(d2))
+ // ensure content got updated
+ c1, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", srcDir)).CombinedOutput()
+ c.Assert(err, IsNil)
+ c2, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", destDir)).CombinedOutput()
+ c.Assert(err, IsNil)
+ c.Assert(string(c1), Equals, string(c2))
+}
+
+func (ts *HTestSuite) TestSyncDirs(c *C) {
+
+ for _, l := range [][2]string{
+ [2]string{"src-short", "dst-loooooooooooong"},
+ [2]string{"src-loooooooooooong", "dst-short"},
+ [2]string{"src-eq", "dst-eq"},
+ } {
+
+ // ensure we have src, dest dirs with different length
+ srcDir := filepath.Join(c.MkDir(), l[0])
+ err := os.MkdirAll(srcDir, 0755)
+ c.Assert(err, IsNil)
+ destDir := filepath.Join(c.MkDir(), l[1])
+ err = os.MkdirAll(destDir, 0755)
+ c.Assert(err, IsNil)
+
+ // add a src subdir
+ subdir := filepath.Join(srcDir, "subdir")
+ err = os.Mkdir(subdir, 0755)
+ c.Assert(err, IsNil)
+ makeTestFiles(c, subdir, destDir)
+
+ // add a dst subdir that needs to get deleted
+ subdir2 := filepath.Join(destDir, "to-be-deleted-subdir")
+ err = os.Mkdir(subdir2, 0755)
+ subdir3 := filepath.Join(subdir2, "to-be-deleted-sub-subdir")
+ err = os.Mkdir(subdir3, 0755)
+
+ // and a toplevel
+ makeTestFiles(c, srcDir, destDir)
+
+ // do it
+ err = RSyncWithDelete(srcDir, destDir)
+ c.Assert(err, IsNil)
+
+ // ensure meta-data is identical
+ compareDirs(c, srcDir, destDir)
+ compareDirs(c, filepath.Join(srcDir, "subdir"), filepath.Join(destDir, "subdir"))
+ }
+}
+
+func (ts *HTestSuite) TestSyncDirFails(c *C) {
+ srcDir := c.MkDir()
+ err := os.MkdirAll(srcDir, 0755)
+ c.Assert(err, IsNil)
+
+ destDir := c.MkDir()
+ err = os.MkdirAll(destDir, 0755)
+ c.Assert(err, IsNil)
+
+ err = ioutil.WriteFile(filepath.Join(destDir, "meep"), []byte(nil), 0644)
+ c.Assert(err, IsNil)
+
+ // ensure remove fails
+ err = os.Chmod(destDir, 0100)
+ c.Assert(err, IsNil)
+ // make tempdir cleanup work again
+ defer os.Chmod(destDir, 0755)
+
+ // do it
+ err = RSyncWithDelete(srcDir, destDir)
+ c.Check(err, NotNil)
+ c.Check(err, ErrorMatches, ".*permission denied.*")
+}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/main.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/main.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/main.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/main.go 2016-03-29 15:39:25.000000000 +0000
@@ -43,8 +43,10 @@
var cacheDir = ubuntuimage.GetCacheDir()
func main() {
- args := os.Args
+ execute(os.Args)
+}
+func execute(args []string) {
if v := os.Getenv("MANPAGE"); v != "" {
manpagePath := "/tmp/ubuntu-device-flash.manpage"
w, err := os.Create(manpagePath)
@@ -59,24 +61,11 @@
return
}
- if _, err := parser.ParseArgs(args); err != nil && parser.Active == nil {
- if e, ok := err.(*flags.Error); ok {
- if e.Type == flags.ErrHelp {
- fmt.Println(err)
- os.Exit(0)
- }
- }
-
- fmt.Println("DEPRECATED: Implicit 'touch' subcommand assumed")
- args = append(args[:1], append([]string{"touch"}, args[1:]...)...)
- if _, err := parser.ParseArgs(args); err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- } else if err != nil {
+ if _, err := parser.ParseArgs(args); err != nil {
fmt.Println(err)
os.Exit(1)
}
+
}
func printOut(args ...interface{}) {
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/main_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/main_test.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/main_test.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/main_test.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,46 @@
+//
+// ubuntu-device-flash - handles ubuntu disk images
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package main
+
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License version 3, as published
+// by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranties of
+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see .
+
+import (
+ "os"
+ "os/exec"
+
+ . "launchpad.net/gocheck"
+)
+
+type ParserTestSuite struct{}
+
+var _ = Suite(&ParserTestSuite{})
+
+func (s *ParserTestSuite) TestExitStatusOnBadArgs(c *C) {
+ if os.Getenv("BE_CRASHER") == "1" {
+ execute([]string{"./ubuntu-device-flash", "--verbose", "core", "--output=/tmp/snappy.img", "--developer-mode"})
+ return
+ }
+
+ cmd := exec.Command(os.Args[0], "-gocheck.f", "ParserTestSuite.TestExitStatusOnBadArgs")
+ cmd.Env = append(os.Environ(), "BE_CRASHER=1")
+ out, err := cmd.CombinedOutput()
+ e, ok := err.(*exec.ExitError)
+ c.Assert(ok, Equals, true)
+ c.Assert(e.Success(), Equals, false)
+ c.Assert(string(out), Equals, "the required argument `release` was not provided\n")
+}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/personal.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/personal.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/personal.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/personal.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,43 @@
+//
+// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image
+// based system
+//
+// Copyright (c) 2013 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package main
+
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License version 3, as published
+// by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranties of
+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see .
+
+func init() {
+ parser.AddCommand("personal",
+ "Creates ubuntu personal images",
+ "",
+ &personalCmd)
+}
+
+type PersonalCmd struct {
+ Size int64 `long:"size" short:"s" description:"Size of image file to create in GB (min 10)" default:"10"`
+
+ Snapper
+}
+
+var personalCmd PersonalCmd
+
+func (personalCmd *PersonalCmd) Execute(args []string) error {
+ personalCmd.flavor = flavorPersonal
+ personalCmd.size = personalCmd.Size
+
+ return personalCmd.create()
+}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/query.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/query.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/query.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/query.go 2016-03-29 15:39:25.000000000 +0000
@@ -47,6 +47,10 @@
var queryCmd QueryCmd
func (queryCmd *QueryCmd) Execute(args []string) error {
+ if globalArgs.TLSSkipVerify {
+ ubuntuimage.TLSSkipVerify()
+ }
+
if queryCmd.ListChannels {
return queryCmd.printChannelList()
}
@@ -84,6 +88,10 @@
}
for k, v := range channels {
+ if _, ok := v.Devices[queryCmd.Device]; !ok {
+ continue
+ }
+
if !v.Hidden {
if v.Alias != "" {
fmt.Printf("%s (alias to %s)\n", k, v.Alias)
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/snappy.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/snappy.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/snappy.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/snappy.go 2016-04-01 09:50:49.000000000 +0000
@@ -0,0 +1,816 @@
+//
+// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image
+// based system
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package main
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "runtime"
+ "syscall"
+ "time"
+
+ "github.com/ubuntu-core/snappy/arch"
+ "github.com/ubuntu-core/snappy/dirs"
+ "github.com/ubuntu-core/snappy/osutil"
+ "github.com/ubuntu-core/snappy/partition"
+ "github.com/ubuntu-core/snappy/progress"
+ "github.com/ubuntu-core/snappy/provisioning"
+ "github.com/ubuntu-core/snappy/release"
+ "github.com/ubuntu-core/snappy/snap"
+ "github.com/ubuntu-core/snappy/snappy"
+
+ "gopkg.in/yaml.v2"
+ "launchpad.net/goget-ubuntu-touch/diskimage"
+ "launchpad.net/goget-ubuntu-touch/sysutils"
+ "launchpad.net/goget-ubuntu-touch/ubuntuimage"
+)
+
+type imageFlavor string
+
+const (
+ minSizePersonal = 10
+ minSizeCore = 4
+)
+
+const (
+ rootSizePersonal = 4096
+ rootSizeCore = 1024
+)
+
+const (
+ flavorPersonal imageFlavor = "personal"
+ flavorCore imageFlavor = "core"
+)
+
+func (f imageFlavor) Channel() string {
+ return fmt.Sprintf("ubuntu-%s", f)
+}
+
+func (f imageFlavor) minSize() int64 {
+ switch f {
+ case flavorPersonal:
+ return minSizePersonal
+ case flavorCore:
+ return minSizeCore
+ default:
+ panic("invalid flavor")
+ }
+}
+
+func (f imageFlavor) rootSize() int {
+ switch f {
+ case flavorPersonal:
+ return rootSizePersonal
+ case flavorCore:
+ return rootSizeCore
+ default:
+ panic("invalid flavor")
+ }
+}
+
+// Snapper holds common options applicable to snappy based images.
+type Snapper struct {
+ Channel string `long:"channel" description:"Specify the channel to use" default:"stable"`
+ Output string `long:"output" short:"o" description:"Name of the image file to create" required:"true"`
+ Gadget string `long:"gadget" description:"The snappy gadget package to base the image out of" default:"generic-amd64"`
+ StoreID string `long:"store" description:"Set an alternate store id."`
+ OS string `long:"os" description:"path to the OS snap."`
+ Kernel string `long:"kernel" description:"path to the kernel snap."`
+
+ Development struct {
+ Install []string `long:"install" description:"Install additional packages (can be called multiple times)"`
+ DevicePart string `long:"device-part" description:"Specify a local device part to override the one from the server"`
+ DeveloperMode bool `long:"developer-mode" description:"Finds the latest public key in your ~/.ssh and sets it up using cloud-init"`
+ } `group:"Development"`
+
+ Positional struct {
+ Release string `positional-arg-name:"release" description:"The release to base the image out of (15.04 or rolling)" required:"true"`
+ } `positional-args:"yes" required:"yes"`
+
+ img diskimage.CoreImage
+ hardware diskimage.HardwareDescription
+ gadget diskimage.GadgetDescription
+ stagingRootPath string
+
+ size int64
+
+ flavor imageFlavor
+ device string
+
+ customizationFunc []func() error
+}
+
+func (s Snapper) sanityCheck() error {
+ // we don't want to overwrite the output, people might get angry :-)
+ if osutil.FileExists(s.Output) {
+ return fmt.Errorf("Giving up, the desired target output file %#v already exists", s.Output)
+ }
+
+ if s.size < s.flavor.minSize() {
+ return fmt.Errorf("minimum size for %s is %d", s.flavor, s.flavor.minSize())
+ }
+
+ if syscall.Getuid() != 0 {
+ return errors.New("command requires sudo/pkexec (root)")
+ }
+ if s.Positional.Release == "15.04" {
+ return errors.New("building 15.04 core images is no longer supported. Please use the ppa:snappy-dev/tools 15.04 version of this tool")
+ }
+
+ return nil
+}
+
+func (s *Snapper) systemImage() (*ubuntuimage.Image, error) {
+ channels, err := ubuntuimage.NewChannels(globalArgs.Server)
+ if err != nil {
+ return nil, err
+ }
+
+ channel := systemImageChannel(s.flavor.Channel(), s.Positional.Release, s.Channel)
+ // TODO: remove once azure channel is gone
+ if s.device == "" {
+ s.device = systemImageDeviceChannel(s.gadget.Architecture())
+ }
+
+ deviceChannel, err := channels.GetDeviceChannel(globalArgs.Server, channel, s.device)
+ if err != nil {
+ return nil, err
+ }
+
+ systemImage, err := getImage(deviceChannel)
+ if err != nil {
+ return nil, err
+ }
+
+ // avoid passing more args to setup()
+ globalArgs.Revision = systemImage.Version
+
+ return &systemImage, nil
+}
+
+func (s *Snapper) installFlags() snappy.InstallFlags {
+ flags := snappy.InhibitHooks | snappy.AllowGadget
+
+ if s.Development.DeveloperMode {
+ flags |= snappy.AllowUnauthenticated
+ }
+
+ return flags
+}
+
+func (s *Snapper) install(systemPath string) error {
+ dirs.SetRootDir(systemPath)
+ defer dirs.SetRootDir("/")
+
+ flags := s.installFlags()
+ gadgetSoftware := s.gadget.Gadget.Software
+ packageCount := len(s.Development.Install) + len(gadgetSoftware.BuiltIn) + len(gadgetSoftware.Preinstalled) + 3
+ if s.Gadget != "" {
+ packageCount++
+ }
+
+ packageQueue := make([]string, 0, packageCount)
+ if s.Gadget != "" {
+ packageQueue = append(packageQueue, s.Gadget)
+ }
+ if s.OS != "" && s.Kernel != "" {
+ packageQueue = append(packageQueue, s.Kernel)
+ packageQueue = append(packageQueue, s.OS)
+ }
+ packageQueue = append(packageQueue, gadgetSoftware.BuiltIn...)
+ packageQueue = append(packageQueue, gadgetSoftware.Preinstalled...)
+ packageQueue = append(packageQueue, s.Development.Install...)
+
+ for _, snap := range packageQueue {
+ fmt.Println("Installing", snap)
+
+ pb := progress.NewTextProgress()
+ name := snap
+ if _, err := snappy.Install(name, s.Channel, flags, pb); err != nil {
+ return fmt.Errorf("failed to install %q from %q: %s", name, s.Channel, err)
+ }
+ }
+
+ // set the bootvars for kernel/os snaps, the latest snappy is
+ // not activating the snaps on install anymore (with inhibit)
+ // so we need to work around that here (only on first boot)
+ //
+ // there is also no mounted os/kernel snap in the systemPath
+ // all we have here is the blobs
+ if s.OS != "" && s.Kernel != "" {
+ bootloader, err := partition.FindBootloader()
+ if err != nil {
+ return fmt.Errorf("can not set kernel/os bootvars: %s", err)
+ }
+
+ snaps, _ := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*.snap"))
+ for _, fullname := range snaps {
+ bootvar := ""
+
+ // detect type
+ snapFile, err := snap.Open(fullname)
+ if err != nil {
+ return fmt.Errorf("can not read %v", fullname)
+ }
+ info, err := snapFile.Info()
+ if err != nil {
+ return fmt.Errorf("can not get info for %v", fullname)
+ }
+ switch info.Type {
+ case snap.TypeOS:
+ bootvar = "snappy_os"
+ case snap.TypeKernel:
+ bootvar = "snappy_kernel"
+ }
+
+ name := filepath.Base(fullname)
+ if bootvar != "" {
+ if err := bootloader.SetBootVar(bootvar, name); err != nil {
+ return err
+ }
+ }
+ }
+
+ // HORRIBLE, snappy.Install() will check if running
+ // on a grub system based on the gadget snap and if
+ // it is grub it will not extract the kernel/os
+ //
+ // HOWEVER this won't work in u-d-f because there
+ // is no current symlink so kernel.go always unpacks
+ // the kernel. undo this here
+ if s.gadget.Gadget.Hardware.Bootloader == "grub" {
+ dirs, _ := filepath.Glob(filepath.Join(s.img.Boot(), "/EFI/ubuntu/grub/*.snap"))
+ for _, d := range dirs {
+ fmt.Printf("Removing unneeded: %s\n", d)
+ if err := os.RemoveAll(d); err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func (s *Snapper) extractGadget(gadgetPackage string) error {
+ if gadgetPackage == "" {
+ return nil
+ }
+
+ tempDir, err := ioutil.TempDir("", "gadget")
+ if err != nil {
+ return err
+ }
+
+ // we need to fix the permissions for tempdir to be seteuid friendly
+ if err := os.Chmod(tempDir, 0755); err != nil {
+ return err
+ }
+
+ s.stagingRootPath = tempDir
+ os.MkdirAll(filepath.Join(tempDir, "/snaps"), 0755)
+
+ dirs.SetRootDir(tempDir)
+ defer dirs.SetRootDir("/")
+ release.Override(release.Release{
+ Flavor: string(s.flavor),
+ Series: s.Positional.Release,
+ Channel: s.Channel,
+ })
+
+ // we need to download and extract the squashfs snap
+ downloadedSnap := gadgetPackage
+ if !osutil.FileExists(gadgetPackage) {
+ repo := snappy.NewConfiguredUbuntuStoreSnapRepository()
+ snap, err := repo.Snap(gadgetPackage, s.Channel)
+ if err != nil {
+ return fmt.Errorf("expected a gadget snaps: %s", err)
+ }
+
+ pb := progress.NewTextProgress()
+ downloadedSnap, err = repo.Download(snap, pb)
+ if err != nil {
+ return err
+ }
+ }
+
+ // the fake snap needs to be in an expected location so that
+ // s.loadGadget() is happy
+ fakeGadgetDir := filepath.Join(tempDir, "/gadget/fake-gadget/1.0-fake/")
+ if err := os.MkdirAll(fakeGadgetDir, 0755); err != nil {
+ return err
+ }
+ cmd := exec.Command("unsquashfs", "-f", "-d", fakeGadgetDir, downloadedSnap)
+ if output, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("snap unpack failed with: %v (%v)", err, string(output))
+ }
+
+ if err := s.loadGadget(tempDir); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *Snapper) loadGadget(systemPath string) error {
+ pkgs, err := filepath.Glob(filepath.Join(systemPath, "/gadget/*/*/meta/snap.yaml"))
+ if err != nil {
+ return err
+ }
+
+ // checking for len(pkgs) > 2 due to the 'current' symlink
+ if len(pkgs) == 0 {
+ return errors.New("no gadget package found")
+ } else if len(pkgs) > 2 || err != nil {
+ return errors.New("too many gadget packages installed")
+ }
+
+ f, err := ioutil.ReadFile(pkgs[0])
+ if err != nil {
+ return errors.New("failed to read gadget yaml")
+ }
+
+ var gadget diskimage.GadgetDescription
+ if err := yaml.Unmarshal([]byte(f), &gadget); err != nil {
+ return errors.New("cannot decode gadget yaml")
+ }
+ s.gadget = gadget
+ s.gadget.SetRoot(systemPath)
+
+ // ensure we can download and install snaps
+ arch.SetArchitecture(arch.ArchitectureType(s.gadget.Architecture()))
+
+ return nil
+}
+
+// Creates a YAML file inside the image that contains metadata relating
+// to the installation.
+func (s Snapper) writeInstallYaml(bootMountpoint string) error {
+ selfPath, err := exec.LookPath(os.Args[0])
+ if err != nil {
+ return err
+ }
+
+ bootDir := ""
+
+ switch s.gadget.Gadget.Hardware.Bootloader {
+ // Running systems use a bindmount for /boot/grub, but
+ // since the system isn't booted, create the file in the
+ // real location.
+ case "grub":
+ bootDir = "/EFI/ubuntu/grub"
+ }
+
+ installYamlFilePath := filepath.Join(bootMountpoint, bootDir, provisioning.InstallYamlFile)
+
+ i := provisioning.InstallYaml{
+ InstallMeta: provisioning.InstallMeta{
+ Timestamp: time.Now(),
+ },
+ InstallTool: provisioning.InstallTool{
+ Name: filepath.Base(selfPath),
+ Path: selfPath,
+ // FIXME: we don't know our own version yet :)
+ // Version: "???",
+ },
+ InstallOptions: provisioning.InstallOptions{
+ Size: s.size,
+ SizeUnit: "GB",
+ Output: s.Output,
+ Channel: s.Channel,
+ DevicePart: s.Development.DevicePart,
+ Gadget: s.Gadget,
+ OS: s.OS,
+ Kernel: s.Kernel,
+ DeveloperMode: s.Development.DeveloperMode,
+ },
+ }
+
+ data, err := yaml.Marshal(&i)
+ if err != nil {
+ return err
+ }
+
+ // the file isn't supposed to be modified, hence r/o.
+ return ioutil.WriteFile(installYamlFilePath, data, 0444)
+}
+
+func extractHWDescription(path string) (hw diskimage.HardwareDescription, err error) {
+ // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
+ if syscall.Getuid() == 0 {
+ runtime.GOMAXPROCS(1)
+ runtime.LockOSThread()
+
+ if err := sysutils.DropPrivs(); err != nil {
+ return hw, err
+ }
+ }
+
+ printOut("Searching for hardware.yaml in device part")
+ tmpdir, err := ioutil.TempDir("", "hardware")
+ if err != nil {
+ return hw, errors.New("cannot create tempdir to extract hardware.yaml from device part")
+ }
+ defer os.RemoveAll(tmpdir)
+
+ if out, err := exec.Command("tar", "xf", path, "-C", tmpdir, "hardware.yaml").CombinedOutput(); err != nil {
+ return hw, fmt.Errorf("failed to extract a hardware.yaml from the device part: %s", out)
+ }
+
+ data, err := ioutil.ReadFile(filepath.Join(tmpdir, "hardware.yaml"))
+ if err != nil {
+ return hw, err
+ }
+
+ err = yaml.Unmarshal([]byte(data), &hw)
+
+ return hw, err
+}
+
+func (s *Snapper) bindMount(d string) (string, error) {
+ src := filepath.Join(s.img.Writable(), "system-data", d)
+ dst := filepath.Join(s.img.System(), d)
+
+ if err := os.MkdirAll(src, 0755); err != nil {
+ return "", err
+ }
+ cmd := exec.Command("mount", "--bind", src, dst)
+ if o, err := cmd.CombinedOutput(); err != nil {
+ return "", fmt.Errorf("bind mount failed for %s to %s with: %s %v ", src, dst, err, string(o))
+ }
+ return dst, nil
+}
+
+func (s *Snapper) downloadOS(osPackage string) (string, error) {
+ if osPackage == "" {
+ return "", nil
+ }
+ // if its pointing to a local file, just return that
+ if _, err := os.Stat(osPackage); err == nil {
+ return osPackage, nil
+ }
+
+ release.Override(release.Release{
+ Flavor: string(s.flavor),
+ Series: s.Positional.Release,
+ Channel: s.Channel,
+ })
+ m := snappy.NewConfiguredUbuntuStoreSnapRepository()
+ snap, err := m.Snap(osPackage, s.Channel)
+ if err != nil {
+ return "", fmt.Errorf("failed to find os snap: %s", err)
+ }
+ pb := progress.NewTextProgress()
+ path, err := m.Download(snap, pb)
+ if err != nil {
+ return "", err
+ }
+
+ return path, nil
+}
+
+func (s *Snapper) setup(systemImageFiles []Files) error {
+ printOut("Mounting...")
+ if err := s.img.Mount(); err != nil {
+ return err
+ }
+ defer func() {
+ printOut("Unmounting...")
+ if err := s.img.Unmount(); err != nil {
+ fmt.Println("WARNING: unexpected issue:", err)
+ }
+ }()
+
+ printOut("Provisioning...")
+ for i := range systemImageFiles {
+ if out, err := exec.Command("fakeroot", "tar", "--numeric-owner", "-axvf", systemImageFiles[i].FilePath, "-C", s.img.BaseMount()).CombinedOutput(); err != nil {
+ printOut(string(out))
+ return fmt.Errorf("issues while extracting: %s", out)
+ }
+ }
+
+ systemPath := s.img.System()
+
+ // setup a fake system
+ if s.gadget.PartitionLayout() == "minimal" {
+ if err := os.MkdirAll(systemPath, 0755); err != nil {
+ return err
+ }
+
+ // this is a bit terrible, we need to download the OS
+ // mount it, "sync dirs" (see below) and then we
+ // will need to download it again to install it properly
+ osSnap, err := s.downloadOS(s.OS)
+ if err != nil {
+ return err
+ }
+
+ // mount os snap
+ cmd := exec.Command("mount", osSnap, systemPath)
+ if o, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("os snap mount failed with: %s %v ", err, string(o))
+ }
+ defer exec.Command("umount", systemPath).Run()
+
+ // we need to do what "writable-paths" normally does on
+ // boot for etc/systemd/system, i.e. copy all the stuff
+ // from the os into the writable partition. normally
+ // this is the job of the initrd, however it won't touch
+ // the dir if there are files in there already. and a
+ // kernel/os install will create auto-mount units in there
+ src := filepath.Join(systemPath, "etc", "systemd", "system")
+ dst := filepath.Join(s.img.Writable(), "system-data", "etc", "systemd")
+ if err := os.MkdirAll(dst, 0755); err != nil {
+ return err
+ }
+ cmd = exec.Command("cp", "-a", src, dst)
+ if o, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("copy failed: %s %s", err, o)
+ }
+
+ // bind mount all relevant dirs
+ for _, d := range []string{"snaps", "var/lib/snaps", "var/lib/snappy", "etc/systemd/system/", "tmp"} {
+ dst, err := s.bindMount(d)
+ if err != nil {
+ return err
+ }
+ defer exec.Command("umount", dst).Run()
+ }
+
+ // bind mount /boot/efi
+ dst = filepath.Join(systemPath, "/boot/efi")
+ cmd = exec.Command("mount", "--bind", s.img.Boot(), dst)
+ if o, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("boot bind mount failed with: %s %v ", err, string(o))
+ }
+ defer exec.Command("umount", dst).Run()
+ switch s.gadget.Gadget.Hardware.Bootloader {
+ case "grub":
+ // grub needs this
+ grubUbuntu := filepath.Join(s.img.Boot(), "EFI/ubuntu/grub")
+ os.MkdirAll(grubUbuntu, 0755)
+
+ // and /boot/grub
+ src = grubUbuntu
+ dst = filepath.Join(systemPath, "/boot/grub")
+ cmd = exec.Command("mount", "--bind", src, dst)
+ if o, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("boot/ubuntu bind mount failed with: %s %v ", err, string(o))
+ }
+ defer exec.Command("umount", dst).Run()
+
+ // TERRIBLE but we need a /boot/grub/grub.cfg so that
+ // the kernel and os snap can be installed
+ glob, err := filepath.Glob(filepath.Join(s.stagingRootPath, "gadget", "*", "*", "grub.cfg"))
+ if err != nil {
+ return fmt.Errorf("grub.cfg glob failed: %s", err)
+ }
+ if len(glob) != 1 {
+ return fmt.Errorf("can not find a valid grub.cfg, found %v instead", len(glob))
+ }
+ gadgetGrubCfg := glob[0]
+ cmd = exec.Command("cp", gadgetGrubCfg, grubUbuntu)
+ o, err := cmd.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("failed to copy %s %s", err, o)
+ }
+ case "u-boot":
+ src = s.img.Boot()
+ dst = filepath.Join(systemPath, "/boot/uboot")
+ cmd = exec.Command("mount", "--bind", src, dst)
+ if o, err := cmd.CombinedOutput(); err != nil {
+ return fmt.Errorf("boot/ubuntu bind mount failed with: %s %v ", err, string(o))
+ }
+ defer exec.Command("umount", dst).Run()
+ }
+ }
+
+ if err := s.img.SetupBoot(); err != nil {
+ return err
+ }
+
+ if err := s.install(systemPath); err != nil {
+ return err
+ }
+
+ for i := range s.customizationFunc {
+ if err := s.customizationFunc[i](); err != nil {
+ return err
+ }
+ }
+
+ return s.writeInstallYaml(s.img.Boot())
+}
+
+// deploy orchestrates the priviledged part of the setup
+func (s *Snapper) deploy(systemImageFiles []Files) error {
+ // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
+ runtime.GOMAXPROCS(1)
+ runtime.LockOSThread()
+ if err := sysutils.EscalatePrivs(); err != nil {
+ return err
+ }
+ defer sysutils.DropPrivs()
+
+ printOut("Formatting...")
+ if err := s.img.Format(); err != nil {
+ return err
+ }
+
+ if err := s.setup(systemImageFiles); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s Snapper) printSummary() {
+ fmt.Println("New image complete")
+ fmt.Println("Summary:")
+ fmt.Println(" Output:", s.Output)
+ fmt.Println(" Architecture:", s.gadget.Architecture())
+ fmt.Println(" Channel:", s.Channel)
+ fmt.Println(" Version:", globalArgs.Revision)
+}
+
+func (s *Snapper) getSystemImage() ([]Files, error) {
+ var devicePart string
+ if s.Development.DevicePart != "" {
+ p, err := expandFile(s.Development.DevicePart)
+ if err != nil {
+ return nil, err
+ }
+
+ fmt.Println("Using a custom OS or Kernel part will prevent updates for these components")
+
+ devicePart = p
+ }
+
+ fmt.Println("Fetching information from server...")
+ systemImage, err := s.systemImage()
+ if err != nil {
+ return nil, err
+ }
+
+ filesChan := make(chan Files, len(systemImage.Files))
+ sigFiles := ubuntuimage.GetGPGFiles()
+
+ fmt.Println("Downloading and setting up...")
+
+ go func() {
+ sigFilesChan := make(chan Files, len(sigFiles))
+ defer close(sigFilesChan)
+
+ for _, f := range sigFiles {
+ bitDownloader(f, sigFilesChan, globalArgs.Server, cacheDir)
+ }
+ }()
+
+ filePaths := make([]Files, 0, len(systemImage.Files))
+ hwChan := make(chan diskimage.HardwareDescription)
+
+ go func() {
+ for i := 0; i < len(systemImage.Files); i++ {
+ f := <-filesChan
+
+ if isDevicePart(f.FilePath) {
+ devicePart = f.FilePath
+
+ if hardware, err := extractHWDescription(f.FilePath); err != nil {
+ fmt.Println("Failed to read harware.yaml from device part, provisioning may fail:", err)
+ } else {
+ hwChan <- hardware
+ }
+ }
+
+ printOut("Download finished for", f.FilePath)
+ filePaths = append(filePaths, f)
+ }
+ close(hwChan)
+ close(filesChan)
+ }()
+
+ for _, f := range systemImage.Files {
+ if devicePart != "" && isDevicePart(f.Path) {
+ printOut("Using a custom device tarball")
+ filesChan <- Files{FilePath: devicePart}
+ } else {
+ go bitDownloader(f, filesChan, globalArgs.Server, cacheDir)
+ }
+ }
+
+ s.hardware = <-hwChan
+
+ return filePaths, nil
+}
+
+func (s *Snapper) create() (err error) {
+ if err := s.sanityCheck(); err != nil {
+ return err
+ }
+
+ if s.StoreID != "" {
+ fmt.Println("Setting store id to", s.StoreID)
+ os.Setenv("UBUNTU_STORE_ID", s.StoreID)
+ }
+
+ fmt.Println("Determining gadget configuration")
+ if err := s.extractGadget(s.Gadget); err != nil {
+ return err
+ }
+ defer os.RemoveAll(s.stagingRootPath)
+
+ // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
+ runtime.GOMAXPROCS(1)
+ runtime.LockOSThread()
+ if err := sysutils.DropPrivs(); err != nil {
+ return err
+ }
+
+ systemImageFiles := []Files{}
+ switch s.gadget.Gadget.Hardware.PartitionLayout {
+ case "minimal":
+ if s.OS == "" && s.Kernel == "" {
+ return errors.New("kernel and os have to be specified to support partition-layout: minimal")
+ }
+ }
+
+ switch s.gadget.Gadget.Hardware.Bootloader {
+ case "grub":
+ legacy := isLegacy(s.Positional.Release, s.Channel, globalArgs.Revision)
+ if legacy {
+ printOut("Using legacy setup")
+ }
+
+ s.img = diskimage.NewCoreGrubImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.gadget, legacy, "gpt")
+ case "u-boot":
+ label := "msdos"
+ if s.gadget.Architecture() == archArm64 {
+ label = "gpt"
+ }
+ s.img = diskimage.NewCoreUBootImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.gadget, label)
+ default:
+ return errors.New("no hardware description in Gadget snap")
+ }
+
+ printOut("Partitioning...")
+ if err := s.img.Partition(); err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ os.Remove(s.Output)
+ }
+ }()
+
+ // Handle SIGINT and SIGTERM.
+ go func() {
+ ch := make(chan os.Signal)
+ signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
+
+ for sig := range ch {
+ printOut("Received", sig, "... ignoring")
+ }
+ }()
+
+ // Execute the following code with escalated privs and drop them when done
+ if err := s.deploy(systemImageFiles); err != nil {
+ return err
+ }
+
+ if err := s.img.FlashExtra(); err != nil {
+ return err
+ }
+
+ s.printSummary()
+
+ return nil
+}
+
+func isLegacy(release, channel string, revision int) bool {
+ if release != "15.04" {
+ return false
+ }
+
+ switch channel {
+ case "edge":
+ return revision <= 149
+ case "alpha":
+ return revision <= 9
+ case "stable":
+ return revision <= 4
+ }
+
+ return false
+}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/snappy_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/snappy_test.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/snappy_test.go 1970-01-01 00:00:00.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/snappy_test.go 2016-03-29 15:39:25.000000000 +0000
@@ -0,0 +1,49 @@
+//
+// diskimage - handles ubuntu disk images
+//
+// Copyright (c) 2015 Canonical Ltd.
+//
+// Written by Sergio Schvezov
+//
+package main
+
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License version 3, as published
+// by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranties of
+// MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see .
+
+import (
+ "testing"
+
+ . "launchpad.net/gocheck"
+)
+
+// Hook up gocheck into the "go test" runner.
+func Test(t *testing.T) { TestingT(t) }
+
+type SnappyTestSuite struct{}
+
+var _ = Suite(&SnappyTestSuite{})
+
+func (s *SnappyTestSuite) TestLegacy(c *C) {
+ c.Check(isLegacy("rolling", "edge", 1), Equals, false)
+
+ c.Check(isLegacy("15.04", "edge", 1), Equals, true)
+ c.Check(isLegacy("15.04", "edge", 149), Equals, true)
+ c.Check(isLegacy("15.04", "edge", 150), Equals, false)
+
+ c.Check(isLegacy("15.04", "alpha", 1), Equals, true)
+ c.Check(isLegacy("15.04", "alpha", 9), Equals, true)
+ c.Check(isLegacy("15.04", "alpha", 10), Equals, false)
+
+ c.Check(isLegacy("15.04", "stable", 1), Equals, true)
+ c.Check(isLegacy("15.04", "stable", 4), Equals, true)
+ c.Check(isLegacy("15.04", "stable", 5), Equals, false)
+}
diff -Nru goget-ubuntu-touch-0.19/ubuntu-device-flash/touch.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/touch.go
--- goget-ubuntu-touch-0.19/ubuntu-device-flash/touch.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/touch.go 2016-04-01 10:33:39.000000000 +0000
@@ -36,6 +36,7 @@
Wipe bool `long:"wipe" description:"Clear all data after flashing"`
Serial string `long:"serial" description:"Serial of the device to operate"`
DeveloperMode bool `long:"developer-mode" description:"Enables developer mode after the factory reset, this is meant for automation and makes the device insecure by default (requires --password)"`
+ AdbKeys string `long:"adb-keys" description:"Specify a local adb keys files, instead of using default ~/.android/adbkey.pub (requires --developer-mode)"`
DeviceTarball string `long:"device-tarball" description:"Specify a local device tarball to override the one from the server (using official Ubuntu images with different device tarballs)"`
CustomTarball string `long:"custom-tarball" description:"Specify a local custom tarball to override the one from the server (using official Ubuntu images with different custom tarballs)"`
RunScript string `long:"run-script" description:"Run a script given by path to finish the flashing process, instead of rebooting to recovery (mostly used during development to work around quirky or incomplete recovery images)"`
@@ -79,6 +80,30 @@
log.Fatal("Developer mode requires --password to be set (and --wipe or --bootstrap)")
}
+ if touchCmd.AdbKeys != "" && !touchCmd.DeveloperMode {
+ log.Fatal("Adb keys requires --developer-mode to be set")
+ }
+
+ var adbKeyPath string
+ if touchCmd.DeveloperMode {
+ if touchCmd.AdbKeys != "" {
+ p, err := expandFile(touchCmd.AdbKeys)
+ if err != nil {
+ log.Fatalln("Issues with custom adb keys file", err)
+ }
+ adbKeyPath = p
+ } else {
+ home := os.Getenv("HOME")
+ p, err := expandFile(filepath.Join(home, "/.android/adbkey.pub"))
+ if err != nil {
+ fmt.Println("WARNING: missing ~/.android/adbkey.pub, your device will not be preauthorised")
+ } else {
+ fmt.Println("no --adb-keys defined, using default ~/.android/adbkey.pub")
+ adbKeyPath = p
+ }
+ }
+ }
+
if touchCmd.Password != "" && !touchCmd.Wipe {
log.Fatal("Default password setup requires --wipe or --bootstrap")
}
@@ -87,6 +112,10 @@
fmt.Println("WARNING --developer-mode and --password are dangerous as they remove security features from your device")
}
+ if touchCmd.AdbKeys != "" && touchCmd.DeveloperMode {
+ fmt.Println("WARNING: --adb-keys is dangerous, potentially authorising multiple cliets to connect to your device")
+ }
+
var deviceTarballPath string
if touchCmd.DeviceTarball != "" {
p, err := expandFile(touchCmd.DeviceTarball)
@@ -220,7 +249,13 @@
var enableList []string
if touchCmd.DeveloperMode {
enableList = append(enableList, "developer_mode")
- enableList = append(enableList, "adb_onlock")
+ // provision target device with adbkeys if available
+ if adbKeyPath != "" {
+ err := touchCmd.adb.Push(adbKeyPath, "/cache/recovery/adbkey.pub")
+ if err == nil {
+ enableList = append(enableList, "adb_keys adbkey.pub")
+ }
+ }
}
if touchCmd.Password != "" {
enableList = append(enableList, "default_password "+touchCmd.Password)
diff -Nru goget-ubuntu-touch-0.19/ubuntu-emulator/common.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/common.go
--- goget-ubuntu-touch-0.19/ubuntu-emulator/common.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/common.go 2016-03-29 15:39:25.000000000 +0000
@@ -23,10 +23,11 @@
"errors"
"fmt"
"io/ioutil"
- "launchpad.net/goget-ubuntu-touch/bootimg"
"os"
"path/filepath"
"strings"
+
+ "launchpad.net/goget-ubuntu-touch/bootimg"
)
func getDeviceTar(files []string) (string, error) {
@@ -52,7 +53,7 @@
if err := boot.WriteRamdisk(ramdiskPath); err != nil {
return err
}
- if (ramdiskName == bootRamdisk) {
+ if ramdiskName == bootRamdisk {
kernelPath := filepath.Join(dataDir, kernelName)
if boot.WriteKernel(kernelPath); err != nil {
return err
diff -Nru goget-ubuntu-touch-0.19/ubuntu-emulator/constants.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/constants.go
--- goget-ubuntu-touch-0.19/ubuntu-emulator/constants.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/constants.go 2016-03-29 15:39:25.000000000 +0000
@@ -21,14 +21,14 @@
//Global constansts
const (
- kernelName = "ubuntu-kernel"
- dataImage = "userdata.img"
- sdcardImage = "sdcard.img"
- systemImage = "system.img"
- cacheImage = "cache.img"
- bootImage = "boot.img"
- bootRamdisk = "ramdisk.img"
- recoveryImage = "recovery.img"
+ kernelName = "ubuntu-kernel"
+ dataImage = "userdata.img"
+ sdcardImage = "sdcard.img"
+ systemImage = "system.img"
+ cacheImage = "cache.img"
+ bootImage = "boot.img"
+ bootRamdisk = "ramdisk.img"
+ recoveryImage = "recovery.img"
recoveryRamdisk = "recovery-ramdisk.img"
)
diff -Nru goget-ubuntu-touch-0.19/ubuntu-emulator/run_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/run_test.go
--- goget-ubuntu-touch-0.19/ubuntu-emulator/run_test.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/run_test.go 2016-03-29 15:39:25.000000000 +0000
@@ -25,6 +25,7 @@
"os"
"path/filepath"
"testing"
+
. "launchpad.net/gocheck"
)
diff -Nru goget-ubuntu-touch-0.19/ubuntu-emulator/snapshot.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/snapshot.go
--- goget-ubuntu-touch-0.19/ubuntu-emulator/snapshot.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-emulator/snapshot.go 2016-03-29 15:39:25.000000000 +0000
@@ -77,7 +77,7 @@
}
}
} else {
- errors.New("Command not implemented or supported")
+ return errors.New("Command not implemented or supported")
}
return nil
diff -Nru goget-ubuntu-touch-0.19/ubuntuimage/channels.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntuimage/channels.go
--- goget-ubuntu-touch-0.19/ubuntuimage/channels.go 2015-03-19 12:01:25.000000000 +0000
+++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntuimage/channels.go 2016-03-29 15:39:25.000000000 +0000
@@ -25,8 +25,8 @@
"encoding/json"
"errors"
"fmt"
- "net/http"
"io/ioutil"
+ "net/http"
)
const (
@@ -106,7 +106,7 @@
return err
}
- if (resp.StatusCode != 200) {
+ if resp.StatusCode != 200 {
statusErr := errors.New(fmt.Sprintf("Invalid HTTP response: %d", resp.StatusCode))
return statusErr
}