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 }