diff -Nru goget-ubuntu-touch-0.32/debian/changelog goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/changelog --- goget-ubuntu-touch-0.32/debian/changelog 2016-04-01 10:40:21.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/changelog 2016-04-01 10:40:21.000000000 +0000 @@ -1,9 +1,22 @@ +goget-ubuntu-touch (0.33-0ubuntu1~xenial-staging) xenial; urgency=medium + + * Intermmediate release. + + -- 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 diff -Nru goget-ubuntu-touch-0.32/debian/control goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/control --- goget-ubuntu-touch-0.32/debian/control 2016-04-01 10:40:21.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/debian/control 2016-04-01 10:40:21.000000000 +0000 @@ -10,11 +10,11 @@ golang-gettext-dev, golang-go-flags-dev, golang-gocheck-dev, - golang-goconfigparser-dev, + golang-github-mvo5-goconfigparser-dev, golang-juju-loggo-dev, golang-pb-dev, - golang-snappy-dev, - golang-uboot-go-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 diff -Nru goget-ubuntu-touch-0.32/dependencies.tsv goget-ubuntu-touch-0.33-0ubuntu1~xenial/dependencies.tsv --- goget-ubuntu-touch-0.32/dependencies.tsv 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/dependencies.tsv 2016-04-01 09:50:49.000000000 +0000 @@ -1,9 +1,8 @@ -github.com/blakesmith/ar git 8bd4349a67f2533b078dbc524689d15dba0f4659 2015-03-11T14:59:44Z 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 55b7cffec309684b0c875ab5d67028a431a85ba9 2015-11-04T00:02:50Z +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.32/diskimage/bootloader.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/bootloader.go --- goget-ubuntu-touch-0.32/diskimage/bootloader.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/bootloader.go 2016-04-01 09:50:49.000000000 +0000 @@ -20,8 +20,10 @@ // with this program. If not, see . import ( + "fmt" "io" "os" + "os/exec" "path/filepath" "strconv" @@ -81,6 +83,51 @@ } 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) { diff -Nru goget-ubuntu-touch-0.32/diskimage/common.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common.go --- goget-ubuntu-touch-0.32/diskimage/common.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common.go 2016-04-01 09:50:49.000000000 +0000 @@ -17,8 +17,6 @@ "path/filepath" "strings" "syscall" - - "launchpad.net/goget-ubuntu-touch/sysutils" ) // This program is free software: you can redistribute it and/or modify it @@ -47,10 +45,6 @@ initrdFileName = "initrd.img" ) -const ( - partLayoutSystemAB = "system-AB" -) - var ( syscallSync = syscall.Sync ) @@ -87,6 +81,13 @@ 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 @@ -96,15 +97,16 @@ } type BootAssets struct { - Files []BootAssetFiles `yaml:"files,omitempty"` - RawFiles []BootAssetRawFiles `yaml:"raw-files,omitempty"` + Files []BootAssetFiles `yaml:"files,omitempty"` + RawFiles []BootAssetRawFiles `yaml:"raw-files,omitempty"` + RawPartitions []BootAssetRawPartitions `yaml:"raw-partitions,omitempty"` } -type OemDescription struct { +type GadgetDescription struct { Name string `yaml:"name"` Version string `yaml:"version"` - OEM struct { + Gadget struct { Hardware struct { Bootloader string `yaml:"bootloader"` PartitionLayout string `yaml:"partition-layout"` @@ -122,7 +124,7 @@ Store *struct { ID string `yaml:"id,omitempty"` } - } `yaml:"oem,omitempty"` + } `yaml:"gadget,omitempty"` Config struct { UbuntuCore struct { @@ -133,53 +135,52 @@ rootDir string } -func (o *OemDescription) SetRoot(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 *OemDescription) SystemParts() []string { - switch o.OEM.Hardware.PartitionLayout { - case partLayoutSystemAB: - return []string{"a", "b"} +func (o *GadgetDescription) SystemParts() []string { + switch o.Gadget.Hardware.PartitionLayout { default: return []string{""} } } -func (o OemDescription) InstallPath() (string, error) { - glob, err := filepath.Glob(fmt.Sprintf("%s/oem/%s/current", o.rootDir, o.Name)) +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("oem package not installed") + return "", errors.New("gadget package not installed") } return glob[0], nil } -func (o OemDescription) Architecture() string { - return o.OEM.Hardware.Architecture +func (o GadgetDescription) Architecture() string { + return o.Gadget.Hardware.Architecture } -func (o *OemDescription) SetArchitecture(architecture string) { - o.OEM.Hardware.Architecture = architecture +func (o *GadgetDescription) SetArchitecture(architecture string) { + o.Gadget.Hardware.Architecture = architecture } -func (o OemDescription) PartitionLayout() string { - return o.OEM.Hardware.PartitionLayout +func (o GadgetDescription) PartitionLayout() string { + return o.Gadget.Hardware.PartitionLayout } -func (o OemDescription) Platform() string { - return o.OEM.Hardware.Platform +func (o GadgetDescription) Platform() string { + return o.Gadget.Hardware.Platform } -func (o *OemDescription) SetPlatform(platform string) { - o.OEM.Hardware.Platform = platform +func (o *GadgetDescription) SetPlatform(platform string) { + o.Gadget.Hardware.Platform = platform } func sectorSize(dev string) (string, error) { @@ -193,16 +194,20 @@ // BaseImage implements the basic primitives to manage images. type BaseImage struct { - baseMount string - hardware HardwareDescription - location string - oem OemDescription - parts []partition - partCount int - size int64 - rootSize int + 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 { @@ -259,8 +264,27 @@ } img.baseMount = baseMount - return nil + 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. @@ -275,6 +299,13 @@ 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 { @@ -458,50 +489,28 @@ } func (img *BaseImage) GenericBootSetup(bootPath string) error { - // origins - hardwareYamlPath := filepath.Join(img.baseMount, hardwareFileName) - kernelPath := filepath.Join(img.baseMount, img.hardware.Kernel) - initrdPath := filepath.Join(img.baseMount, img.hardware.Initrd) - - // populate both A/B - for _, part := range img.oem.SystemParts() { - path := filepath.Join(bootPath, part) - - printOut("Setting up", path) - - if err := os.MkdirAll(path, 0755); err != nil { - return err - } - - if err := sysutils.CopyFile(hardwareYamlPath, filepath.Join(path, hardwareFileName)); err != nil { - return err - } - - if err := sysutils.CopyFile(kernelPath, filepath.Join(path, kernelFileName)); err != nil { - return err - } - - if err := sysutils.CopyFile(initrdPath, filepath.Join(path, initrdFileName)); err != nil { - return err - } - } - - oemRoot, err := img.oem.InstallPath() + gadgetRoot, err := img.gadget.InstallPath() if err != nil { return err } - return setupBootAssetFiles(img.Boot(), bootPath, oemRoot, img.oem.OEM.Hardware.BootAssets.Files) + return setupBootAssetFiles(img.Boot(), bootPath, gadgetRoot, img.gadget.Gadget.Hardware.BootAssets.Files) } func (img *BaseImage) FlashExtra() error { - oemRoot, err := img.oem.InstallPath() + gadgetRoot, err := img.gadget.InstallPath() if err != nil { return err } - if bootAssets := img.oem.OEM.Hardware.BootAssets; bootAssets != nil { - return setupBootAssetRawFiles(img.location, oemRoot, bootAssets.RawFiles) + 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 @@ -512,3 +521,19 @@ 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.32/diskimage/common_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common_test.go --- goget-ubuntu-touch-0.32/diskimage/common_test.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/common_test.go 2016-04-01 10:12:22.000000000 +0000 @@ -28,7 +28,7 @@ type CommonTestSuite struct { tmpdir string - oem OemDescription + gadget GadgetDescription packageInst string } @@ -36,27 +36,27 @@ func (s *CommonTestSuite) SetUpTest(c *C) { s.tmpdir = c.MkDir() - s.oem = OemDescription{Name: "packagename", Version: "42"} - s.packageInst = s.oem.Name + 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, "oem", s.packageInst, "current"), 0755) + err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst, "current"), 0755) c.Assert(err, IsNil) - s.oem.SetRoot(s.tmpdir) - installPath, err := s.oem.InstallPath() + s.gadget.SetRoot(s.tmpdir) + installPath, err := s.gadget.InstallPath() c.Assert(err, IsNil) - c.Assert(installPath, Equals, filepath.Join(s.tmpdir, "oem/packagename/current")) + c.Assert(installPath, Equals, filepath.Join(s.tmpdir, "gadget/packagename/current")) } func (s *CommonTestSuite) TestOemInstallPathNoOem(c *C) { - err := os.MkdirAll(filepath.Join(s.tmpdir, "oem", s.packageInst), 0755) + err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst), 0755) c.Assert(err, IsNil) - s.oem.SetRoot(s.tmpdir) - installPath, err := s.oem.InstallPath() + s.gadget.SetRoot(s.tmpdir) + installPath, err := s.gadget.InstallPath() c.Assert(err, NotNil) c.Assert(installPath, Equals, "") diff -Nru goget-ubuntu-touch-0.32/diskimage/core_grub.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_grub.go --- goget-ubuntu-touch-0.32/diskimage/core_grub.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_grub.go 2016-04-01 09:50:49.000000000 +0000 @@ -5,6 +5,7 @@ // // Written by Sergio Schvezov // + package diskimage import ( @@ -31,24 +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 { BaseImage legacyGrub bool } -func NewCoreGrubImage(location string, size int64, rootSize int, hw HardwareDescription, oem OemDescription, updateGrub bool) *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{ BaseImage: BaseImage{ location: location, size: size, rootSize: rootSize, hardware: hw, - oem: oem, - partCount: 5, + gadget: gadget, + partCount: partCount, + label: label, }, legacyGrub: updateGrub, } + } const grubCfgContent = `# console only, no graphics/vga @@ -74,9 +85,10 @@ } parted.addPart(grubLabel, "", fsNone, 4) - parted.addPart(bootLabel, bootDir, fsFat32, 128) - parted.addPart(systemALabel, systemADir, fsExt4, img.rootSize) - parted.addPart(systemBLabel, systemBDir, fsExt4, img.rootSize) + switch img.gadget.PartitionLayout() { + case "minimal": + parted.addPart(bootLabel, bootDir, fsFat32, 64) + } parted.addPart(writableLabel, writableDir, fsExt4, -1) parted.setBoot(2) @@ -87,6 +99,7 @@ return parted.create(img.location) } +// SetupBoot sets up the bootloader logic for the image. func (img *CoreGrubImage) SetupBoot() error { if !img.legacyGrub { // destinations @@ -120,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 @@ -164,7 +177,7 @@ var grubTarget string - arch := img.oem.Architecture() + arch := img.gadget.Architecture() switch arch { case "armhf": @@ -173,13 +186,15 @@ 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", "/root_dev").CombinedOutput(); err != nil { + 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) } } @@ -231,20 +246,4 @@ } 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.32/diskimage/core_uboot.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_uboot.go --- goget-ubuntu-touch-0.32/diskimage/core_uboot.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/core_uboot.go 2016-04-01 09:50:49.000000000 +0000 @@ -66,15 +66,22 @@ Bootloader []string `yaml:"bootloader"` } -func NewCoreUBootImage(location string, size int64, rootSize int, hw HardwareDescription, oem OemDescription) *CoreUBootImage { +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 &CoreUBootImage{ BaseImage{ hardware: hw, - oem: oem, + gadget: gadget, location: location, size: size, rootSize: rootSize, - partCount: 4, + partCount: partCount, + label: label, }, } } @@ -84,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, 128) - 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) @@ -112,7 +124,7 @@ } // populate both A/B - for _, part := range img.oem.SystemParts() { + for _, part := range img.gadget.SystemParts() { bootDtbPath := filepath.Join(bootPath, part, "dtbs") if err := img.provisionDtbs(bootDtbPath); err != nil { return err @@ -126,7 +138,7 @@ defer snappySystemFile.Close() var fdtfile string - if platform := img.oem.Platform(); platform != "" { + if platform := img.gadget.Platform(); platform != "" { fdtfile = fmt.Sprintf("fdtfile=%s.dtb", platform) } @@ -149,28 +161,32 @@ return err } - dtbFis, err := ioutil.ReadDir(dtbsPath) - if err != nil { - return err + var dtbFis []os.FileInfo + if img.hardware.Dtbs != "" { + var err error + dtbFis, err = ioutil.ReadDir(dtbsPath) + if err != nil { + return err + } } if err := os.MkdirAll(bootDtbPath, 0755); err != nil { return err } - dtb := filepath.Join(dtbsPath, fmt.Sprintf("%s.dtb", img.oem.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 oemDtb := img.oem.OEM.Hardware.Dtb; oemDtb != "" && img.oem.Platform() != "" { - oemRoot, err := img.oem.InstallPath() + // 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 } - oemDtb := filepath.Join(oemRoot, oemDtb) + gadgetDtb := filepath.Join(gadgetRoot, gadgetDtb) dst := filepath.Join(bootDtbPath, filepath.Base(dtb)) - if err := sysutils.CopyFile(oemDtb, dst); err != nil { + if err := sysutils.CopyFile(gadgetDtb, dst); err != nil { return err } } else if _, err := os.Stat(dtb); err == nil { diff -Nru goget-ubuntu-touch-0.32/diskimage/partition.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/diskimage/partition.go --- goget-ubuntu-touch-0.32/diskimage/partition.go 2015-11-13 09:19:18.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.32/ubuntu-device-flash/channel_maps.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/channel_maps.go --- goget-ubuntu-touch-0.32/ubuntu-device-flash/channel_maps.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/channel_maps.go 2016-03-29 15:39:25.000000000 +0000 @@ -28,12 +28,14 @@ const ( archArmhf = "armhf" + archArm64 = "arm64" archAmd64 = "amd64" archi386 = "i386" ) const ( deviceArmhf = "generic_armhf" + deviceArm64 = "generic_arm64" deviceAmd64 = "generic_amd64" devicei386 = "generic_i386" ) @@ -42,6 +44,8 @@ switch arch { case archArmhf: return deviceArmhf + case archArm64: + return deviceArm64 case archAmd64: return deviceAmd64 case archi386: diff -Nru goget-ubuntu-touch-0.32/ubuntu-device-flash/common.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/common.go --- goget-ubuntu-touch-0.32/ubuntu-device-flash/common.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/common.go 2016-04-01 09:50:49.000000000 +0000 @@ -61,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 { diff -Nru goget-ubuntu-touch-0.32/ubuntu-device-flash/core.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/core.go --- goget-ubuntu-touch-0.32/ubuntu-device-flash/core.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/core.go 2016-04-01 09:50:49.000000000 +0000 @@ -17,8 +17,6 @@ "path/filepath" "strings" "time" - - "github.com/ubuntu-core/snappy/helpers" ) // This program is free software: you can redistribute it and/or modify it @@ -79,21 +77,20 @@ if !coreCmd.Deprecated.Cloud { coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupCloudInit) - coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupOemConfigs) + coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupGadgetConfigs) } return coreCmd.create() } // this is a hackish way to get the config in place -func (coreCmd *CoreCmd) setupOemConfigs() error { - modprobeDContent := coreCmd.oem.Config.UbuntuCore.Modprobe +func (coreCmd *CoreCmd) setupGadgetConfigs() error { + modprobeDContent := coreCmd.gadget.Config.UbuntuCore.Modprobe if modprobeDContent == nil { - printOut("no modprobe") return nil } - fmt.Println("Setting up oem hooks...") + fmt.Println("Setting up gadget hooks...") writablePath := coreCmd.img.Writable() @@ -104,7 +101,8 @@ // first we need to copy all the files in modprobe.d systemModprobeDir := filepath.Join(coreCmd.img.System(), "etc", "modprobe.d") - if err := helpers.RSyncWithDelete(systemModprobeDir, modprobeDir); err != nil { + // FIXME: can we do "cp -a" here? + if err := RSyncWithDelete(systemModprobeDir, modprobeDir); err != nil { return err } diff -Nru goget-ubuntu-touch-0.32/ubuntu-device-flash/helpers.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/helpers.go --- goget-ubuntu-touch-0.32/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.32/ubuntu-device-flash/helpers_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/helpers_test.go --- goget-ubuntu-touch-0.32/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.32/ubuntu-device-flash/main.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/main.go --- goget-ubuntu-touch-0.32/ubuntu-device-flash/main.go 2015-11-13 09:19:18.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) @@ -61,7 +63,7 @@ if _, err := parser.ParseArgs(args); err != nil { fmt.Println(err) - return + os.Exit(1) } } diff -Nru goget-ubuntu-touch-0.32/ubuntu-device-flash/main_test.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/main_test.go --- goget-ubuntu-touch-0.32/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.32/ubuntu-device-flash/snappy.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/snappy.go --- goget-ubuntu-touch-0.32/ubuntu-device-flash/snappy.go 2015-11-13 09:19:18.000000000 +0000 +++ goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/snappy.go 2016-04-01 09:50:49.000000000 +0000 @@ -20,12 +20,16 @@ "syscall" "time" + "github.com/ubuntu-core/snappy/arch" "github.com/ubuntu-core/snappy/dirs" - "github.com/ubuntu-core/snappy/helpers" + "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" @@ -75,11 +79,14 @@ } } +// 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"` - Oem string `long:"oem" description:"The snappy oem package to base the image out of" default:"generic-amd64"` + 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)"` @@ -93,7 +100,7 @@ img diskimage.CoreImage hardware diskimage.HardwareDescription - oem diskimage.OemDescription + gadget diskimage.GadgetDescription stagingRootPath string size int64 @@ -106,7 +113,7 @@ func (s Snapper) sanityCheck() error { // we don't want to overwrite the output, people might get angry :-) - if helpers.FileExists(s.Output) { + if osutil.FileExists(s.Output) { return fmt.Errorf("Giving up, the desired target output file %#v already exists", s.Output) } @@ -117,6 +124,9 @@ 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 } @@ -130,7 +140,7 @@ channel := systemImageChannel(s.flavor.Channel(), s.Positional.Release, s.Channel) // TODO: remove once azure channel is gone if s.device == "" { - s.device = systemImageDeviceChannel(s.oem.Architecture()) + s.device = systemImageDeviceChannel(s.gadget.Architecture()) } deviceChannel, err := channels.GetDeviceChannel(globalArgs.Server, channel, s.device) @@ -150,7 +160,7 @@ } func (s *Snapper) installFlags() snappy.InstallFlags { - flags := snappy.InhibitHooks | snappy.AllowOEM + flags := snappy.InhibitHooks | snappy.AllowGadget if s.Development.DeveloperMode { flags |= snappy.AllowUnauthenticated @@ -164,38 +174,101 @@ defer dirs.SetRootDir("/") flags := s.installFlags() - oemSoftware := s.oem.OEM.Software - packageCount := len(s.Development.Install) + len(oemSoftware.BuiltIn) + len(oemSoftware.Preinstalled) - if s.Oem != "" { + 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.Oem != "" { - packageQueue = append(packageQueue, s.Oem) + 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, oemSoftware.BuiltIn...) - packageQueue = append(packageQueue, oemSoftware.Preinstalled...) + 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() - if _, err := snappy.Install(snap, flags, pb); err != nil { - return err + 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) extractOem(oemPackage string) error { - if oemPackage == "" { +func (s *Snapper) extractGadget(gadgetPackage string) error { + if gadgetPackage == "" { return nil } - tempDir, err := ioutil.TempDir("", "oem") + tempDir, err := ioutil.TempDir("", "gadget") if err != nil { return err } @@ -206,6 +279,7 @@ } s.stagingRootPath = tempDir + os.MkdirAll(filepath.Join(tempDir, "/snaps"), 0755) dirs.SetRootDir(tempDir) defer dirs.SetRootDir("/") @@ -215,43 +289,67 @@ Channel: s.Channel, }) - flags := s.installFlags() - pb := progress.NewTextProgress() - if _, err := snappy.Install(oemPackage, flags, pb); err != nil { + // 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.loadOem(tempDir); err != nil { + if err := s.loadGadget(tempDir); err != nil { return err } return nil } -func (s *Snapper) loadOem(systemPath string) error { - pkgs, err := filepath.Glob(filepath.Join(systemPath, "/oem/*/*/meta/package.yaml")) +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 oem package found") + return errors.New("no gadget package found") } else if len(pkgs) > 2 || err != nil { - return errors.New("too many oem packages installed") + return errors.New("too many gadget packages installed") } f, err := ioutil.ReadFile(pkgs[0]) if err != nil { - return errors.New("failed to read oem yaml") + return errors.New("failed to read gadget yaml") } - var oem diskimage.OemDescription - if err := yaml.Unmarshal([]byte(f), &oem); err != nil { - return errors.New("cannot decode oem yaml") + var gadget diskimage.GadgetDescription + if err := yaml.Unmarshal([]byte(f), &gadget); err != nil { + return errors.New("cannot decode gadget yaml") } - s.oem = oem - s.oem.SetRoot(systemPath) + s.gadget = gadget + s.gadget.SetRoot(systemPath) + + // ensure we can download and install snaps + arch.SetArchitecture(arch.ArchitectureType(s.gadget.Architecture())) return nil } @@ -266,7 +364,7 @@ bootDir := "" - switch s.oem.OEM.Hardware.Bootloader { + 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. @@ -278,9 +376,7 @@ i := provisioning.InstallYaml{ InstallMeta: provisioning.InstallMeta{ - Timestamp: time.Now(), - InitialVersion: fmt.Sprintf("%d", globalArgs.Revision), - SystemImageServer: globalArgs.Server, + Timestamp: time.Now(), }, InstallTool: provisioning.InstallTool{ Name: filepath.Base(selfPath), @@ -294,7 +390,9 @@ Output: s.Output, Channel: s.Channel, DevicePart: s.Development.DevicePart, - Oem: s.Oem, + Gadget: s.Gadget, + OS: s.OS, + Kernel: s.Kernel, DeveloperMode: s.Development.DeveloperMode, }, } @@ -340,7 +438,49 @@ return hw, err } -func (s *Snapper) setup(filePathChan <-chan string, fileCount int) error { +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 @@ -353,9 +493,8 @@ }() printOut("Provisioning...") - for i := 0; i < fileCount; i++ { - f := <-filePathChan - if out, err := exec.Command("fakeroot", "tar", "--numeric-owner", "-axvf", f, "-C", s.img.BaseMount()).CombinedOutput(); err != nil { + 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) } @@ -363,39 +502,119 @@ systemPath := s.img.System() - if err := s.install(systemPath); err != nil { - return err + // 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 } } - // if the device is armhf, we can't to make this copy here since it's faster - // than on the device. - if s.oem.Architecture() == archArmhf && s.oem.PartitionLayout() == "system-AB" { - printOut("Replicating system-a into system-b") - - src := fmt.Sprintf("%s/.", systemPath) - dst := fmt.Sprintf("%s/system-b", s.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 s.writeInstallYaml(s.img.Boot()) } // deploy orchestrates the priviledged part of the setup -func (s *Snapper) deploy(systemImage *ubuntuimage.Image, filePathChan <-chan string) error { +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() @@ -409,7 +628,7 @@ return err } - if err := s.setup(filePathChan, len(systemImage.Files)); err != nil { + if err := s.setup(systemImageFiles); err != nil { return err } @@ -420,39 +639,17 @@ fmt.Println("New image complete") fmt.Println("Summary:") fmt.Println(" Output:", s.Output) - fmt.Println(" Architecture:", s.oem.Architecture()) + fmt.Println(" Architecture:", s.gadget.Architecture()) fmt.Println(" Channel:", s.Channel) fmt.Println(" Version:", globalArgs.Revision) } -func (s *Snapper) create() 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 oem configuration") - if err := s.extractOem(s.Oem); 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 - } - +func (s *Snapper) getSystemImage() ([]Files, error) { var devicePart string if s.Development.DevicePart != "" { p, err := expandFile(s.Development.DevicePart) if err != nil { - return err + return nil, err } fmt.Println("Using a custom OS or Kernel part will prevent updates for these components") @@ -463,7 +660,7 @@ fmt.Println("Fetching information from server...") systemImage, err := s.systemImage() if err != nil { - return err + return nil, err } filesChan := make(chan Files, len(systemImage.Files)) @@ -480,7 +677,7 @@ } }() - filePathChan := make(chan string, len(systemImage.Files)) + filePaths := make([]Files, 0, len(systemImage.Files)) hwChan := make(chan diskimage.HardwareDescription) go func() { @@ -498,7 +695,7 @@ } printOut("Download finished for", f.FilePath) - filePathChan <- f.FilePath + filePaths = append(filePaths, f) } close(hwChan) close(filesChan) @@ -515,19 +712,56 @@ s.hardware = <-hwChan - loader := s.oem.OEM.Hardware.Bootloader - switch loader { + 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.oem, legacy) + s.img = diskimage.NewCoreGrubImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.gadget, legacy, "gpt") case "u-boot": - s.img = diskimage.NewCoreUBootImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.oem) + 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 OEM snap") + return errors.New("no hardware description in Gadget snap") } printOut("Partitioning...") @@ -551,7 +785,7 @@ }() // Execute the following code with escalated privs and drop them when done - if err := s.deploy(systemImage, filePathChan); err != nil { + if err := s.deploy(systemImageFiles); err != nil { return err } diff -Nru goget-ubuntu-touch-0.32/ubuntu-device-flash/touch.go goget-ubuntu-touch-0.33-0ubuntu1~xenial/ubuntu-device-flash/touch.go --- goget-ubuntu-touch-0.32/ubuntu-device-flash/touch.go 2015-11-13 09:19:18.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)