On Mon, 15 Aug 2016 at 13:22:29 +0100, Simon McVittie wrote:
> As a follow-up for these fixes, I'm part way through converting the
> partition/device handling to be based on a list of named partitions,
> so that Filesystem doesn't have to second-guess how the disk would
> have been partitioned for a particular combination of options. This
> should hopefully make it more robust.

Here's that change.

    S
>From c059b1be7bf3c2d4658c70ab0bd938c6649d00cb Mon Sep 17 00:00:00 2001
From: Simon McVittie <s...@debian.org>
Date: Mon, 15 Aug 2016 10:12:23 +0100
Subject: [PATCH 3/6] Filesystem.make_rootfs_part: remove unused method

Signed-off-by: Simon McVittie <s...@debian.org>
---
 vmdebootstrap/filesystem.py | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/vmdebootstrap/filesystem.py b/vmdebootstrap/filesystem.py
index fa7a575..3db100a 100644
--- a/vmdebootstrap/filesystem.py
+++ b/vmdebootstrap/filesystem.py
@@ -297,11 +297,6 @@ class Filesystem(Base):
         except IOError:
             pass
 
-    def make_rootfs_part(self, extent):
-        bootsize = self.settings['esp-size'] / (1024 * 1024) + 1
-        runcmd(['parted', '-s', self.settings['image'],
-                'mkpart', 'primary', str(bootsize), extent])
-
     def convert_image_to_qcow2(self):
         """
         Current images are all prepared as raw
-- 
2.8.1

>From 8f76ea193732da163a895aae80631979a7381a9d Mon Sep 17 00:00:00 2001
From: Simon McVittie <s...@debian.org>
Date: Mon, 15 Aug 2016 11:18:47 +0100
Subject: [PATCH 4/6] Factor out partition creation into a new Partitions
 handler

This is not intended to make any functional difference, except that
creating the ESP now respects bootoffset. It seems unlikely that
UEFI systems will have a nonzero bootoffset in practice, but it
was simpler this way.

Signed-off-by: Simon McVittie <s...@debian.org>
---
 bin/vmdebootstrap           |  75 +++++++++----------------
 vmdebootstrap/base.py       |  20 -------
 vmdebootstrap/partitions.py | 132 ++++++++++++++++++++++++++++++++++++++++++++
 vmdebootstrap/uefi.py       |  26 ++++-----
 4 files changed, 169 insertions(+), 84 deletions(-)
 create mode 100644 vmdebootstrap/partitions.py

diff --git a/bin/vmdebootstrap b/bin/vmdebootstrap
index cc1efe1..9dd2cae 100755
--- a/bin/vmdebootstrap
+++ b/bin/vmdebootstrap
@@ -38,6 +38,7 @@ from vmdebootstrap.codenames import Codenames
 from vmdebootstrap.filesystem import Filesystem
 from vmdebootstrap.uefi import Uefi
 from vmdebootstrap.network import Networking
+from vmdebootstrap.partitions import Partitions
 
 __version__ = '1.6'
 
@@ -62,6 +63,7 @@ class VmDebootstrap(cliapp.Application):  # pylint: disable=too-many-public-meth
             ExtLinux.name: ExtLinux(),
             Filesystem.name: Filesystem(),
             Networking.name: Networking(),
+            Partitions.name: Partitions(),
         }
 
     def add_settings(self):
@@ -402,67 +404,42 @@ class VmDebootstrap(cliapp.Application):  # pylint: disable=too-many-public-meth
         starts at that offset to allow customisation scripts to
         put bootloader images into the space, e.g. u-boot.
         """
-        base = self.handlers[Base.name]
-        base.message('Creating partitions')
+        partitions = self.handlers[Partitions.name]
+        partitions.message('Creating partitions')
+        partitions.create_table()
         uefi = self.handlers[Uefi.name]
-        runcmd(['parted', '-s', self.settings['image'],
-                'mklabel', self.settings['part-type']])
-        partoffset = 0
-        extent = base.check_swap_size()
+
+        extent = partitions.check_swap_size()
 
         # uefi
-        uefi.partition_esp()
-
-        # /boot partitioning offset calculation
-        # returns partoffset
-        if self.settings['bootoffset'] and self.settings['bootoffset'] is not '0':
-            # turn v.small offsets into something at least possible to create.
-            if self.settings['bootoffset'] < 1048576:
-                partoffset = 1
-                logging.info(
-                    "Setting bootoffset %smib to allow for %s bytes",
-                    partoffset, self.settings['bootoffset'])
-            else:
-                partoffset = self.settings['bootoffset'] / (1024 * 1024)
-                base.message(
-                    "Using bootoffset: %smib %s bytes" %
-                    (partoffset, self.settings['bootoffset']))
+        esp = uefi.partition_esp(partitions)
 
-        # /boot creation - move into base but keep the check
-        # needs extent, partoffset, bootsize: no return
+        # /boot creation - move into partitions but keep the check
+        # needs extent, bootsize: no return
         if self.settings['bootsize'] and self.settings['bootsize'] is not '0%':
-            boot_fs_type = 'ext2'
+            boot = partitions.new('boot')
+
+            boot.type = self.settings['boottype']
+
+            # FIXME: what if it is neither ext2 nor FAT?
+            boot.parted_type = 'ext2'
             if self.settings['boottype'] in ('vfat', 'msdos'):
-                boot_fs_type = 'fat16'
-            if self.settings['grub'] and not partoffset:
-                partoffset = 1
+                boot.parted_type = 'fat16'
+
             bootsize = self.settings['bootsize'] / (1024 * 1024)
-            bootsize += partoffset
-            base.message("Using bootsize %smib: %s bytes" % (bootsize, self.settings['bootsize']))
-            logging.debug("Starting boot partition at %sMB", bootsize)
-            runcmd(['parted', '-s', self.settings['image'],
-                    'mkpart', 'primary', boot_fs_type, str(partoffset),
-                    str(bootsize)])
-            logging.debug("Starting root partition at %sMB", partoffset)
-            runcmd(['parted', '-s', self.settings['image'],
-                    'mkpart', 'primary', str(bootsize), extent])
+            partitions.message("Using bootsize %smib: %s bytes" % (bootsize, self.settings['bootsize']))
+            boot.end = boot.start + bootsize
 
-        # uefi - make rootfs partition after end of ESP
-        # needs extent
-        elif self.settings['use-uefi']:
-            uefi.make_root(extent)
+            partitions.commit(boot)
 
-        # no boot partition
-        else:
-            runcmd(['parted', '-s', self.settings['image'],
-                    'mkpart', 'primary', '0%', extent])
+        root = partitions.new('root')
+        root.end = extent
+        partitions.commit(root)
 
-        # whatever we create, something needs the boot flag
-        runcmd(['parted', '-s', self.settings['image'],
-                'set', '1', 'boot', 'on'])
+        partitions.ensure_something_bootable()
 
         # return to doing swap setup
-        base.make_swap(extent)
+        partitions.make_swap(extent)
 
     def _bootstrap_packages(self):
         base = self.handlers[Base.name]
diff --git a/vmdebootstrap/base.py b/vmdebootstrap/base.py
index d090f0e..ea8ebe0 100644
--- a/vmdebootstrap/base.py
+++ b/vmdebootstrap/base.py
@@ -175,26 +175,6 @@ class Base(object):
             with open(inittab, 'a') as ftab:
                 ftab.write('\nS0:23:respawn:%s\n' % serial_command)
 
-    def check_swap_size(self):
-        # swap - modifies extent
-        extent = '100%'
-        swap = 256 * 1024 * 1024
-        if self.settings['swap'] > 0:
-            if self.settings['swap'] > swap:
-                swap = self.settings['swap']
-            else:
-                # minimum 256MB as default qemu ram is 128MB
-                logging.debug("Setting minimum 256MB swap space")
-            extent = "%s%%" % int(100 * (self.settings['size'] - swap) / self.settings['size'])
-        return extent
-
-    def make_swap(self, extent):
-        if self.settings['swap'] > 0:
-            logging.debug("Creating swap partition")
-            runcmd([
-                'parted', '-s', self.settings['image'],
-                'mkpart', 'primary', 'linux-swap', extent, '100%'])
-
     def base_packages(self):
         packages = []
         if not self.settings['foreign'] and not self.settings['no-acpid']:
diff --git a/vmdebootstrap/partitions.py b/vmdebootstrap/partitions.py
new file mode 100644
index 0000000..4756c43
--- /dev/null
+++ b/vmdebootstrap/partitions.py
@@ -0,0 +1,132 @@
+"""
+  Partitioning
+"""
+# -*- coding: utf-8 -*-
+#
+#  partitions.py
+#
+#  Copyright 2015 Neil Williams
+#  Copyright 2016 Simon McVittie
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+import logging
+
+from vmdebootstrap.base import (
+    Base,
+    runcmd,
+)
+
+class Partition(object):
+    def __init__(self, name, index):
+        self.name = name
+        self.index = index
+        self.parted_type = None
+        self.type = None
+        self.flags = set()
+        # in MiB
+        self.start = None
+        self.end = None
+
+class Partitions(Base):
+    name = 'partitions'
+
+    def __init__(self):
+        super(Partitions, self).__init__()
+
+        self.partitions = []
+        # in MiB
+        self.start_space = 0
+
+    def create_table(self):
+        partoffset = 0
+
+        runcmd(['parted', '-s', self.settings['image'],
+                'mklabel', self.settings['part-type']])
+
+        # /boot partitioning offset calculation
+        # returns partoffset
+        if self.settings['bootoffset'] and self.settings['bootoffset'] is not '0':
+            # turn v.small offsets into something at least possible to create.
+            if self.settings['bootoffset'] < 1048576:
+                partoffset = 1
+                logging.info(
+                    "Setting bootoffset %smib to allow for %s bytes",
+                    partoffset, self.settings['bootoffset'])
+            else:
+                partoffset = self.settings['bootoffset'] / (1024 * 1024)
+                self.message(
+                    "Using bootoffset: %smib %s bytes" %
+                    (partoffset, self.settings['bootoffset']))
+
+        self.start_space = partoffset
+
+    def new(self, name):
+        part = Partition(name, len(self.partitions) + 1)
+        self.partitions.append(part)
+        part.start = self.start_space
+        return part
+
+    def commit(self, part):
+        parted_type = part.parted_type
+
+        if parted_type is None:
+            parted_type = part.type
+
+        if parted_type in ('vfat', 'msdos'):
+            parted_type = 'fat16'
+
+        if parted_type is None:
+            maybe_type = []
+        else:
+            maybe_type = [parted_type]
+
+        runcmd(['parted', '-s', self.settings['image'],
+                'mkpart', 'primary'] + maybe_type + [
+                str(part.start), str(part.end)])
+        for f in part.flags:
+            runcmd(['parted', '-s', self.settings['image'],
+                'set', str(part.index), f, 'on'])
+
+        self.start_space = part.end
+
+    def ensure_something_bootable(self):
+        # whatever we create, something needs the boot flag
+        for p in self.partitions:
+            if 'boot' in p.flags:
+                break
+        else:
+            # assume the first partition is a good choice...
+            runcmd(['parted', '-s', self.settings['image'],
+                    'set', '1', 'boot', 'on'])
+
+    def check_swap_size(self):
+        # swap - modifies extent
+        extent = '100%'
+        swap = 256 * 1024 * 1024
+        if self.settings['swap'] > 0:
+            if self.settings['swap'] > swap:
+                swap = self.settings['swap']
+            else:
+                # minimum 256MB as default qemu ram is 128MB
+                logging.debug("Setting minimum 256MB swap space")
+            extent = "%s%%" % int(100 * (self.settings['size'] - swap) / self.settings['size'])
+        return extent
+
+    def make_swap(self, extent):
+        if self.settings['swap'] > 0:
+            logging.debug("Creating swap partition")
+            runcmd([
+                'parted', '-s', self.settings['image'],
+                'mkpart', 'primary', 'linux-swap', extent, '100%'])
diff --git a/vmdebootstrap/uefi.py b/vmdebootstrap/uefi.py
index b2ca410..362b9b2 100644
--- a/vmdebootstrap/uefi.py
+++ b/vmdebootstrap/uefi.py
@@ -29,7 +29,6 @@ import logging
 import cliapp
 from vmdebootstrap.base import (
     Base,
-    runcmd,
     mount_wrapper,
     umount_wrapper,
 )
@@ -141,18 +140,20 @@ class Uefi(Base):
             finally:
                 umount_wrapper(rootdir)
 
-    def partition_esp(self):
+    def partition_esp(self, partitions):
         if not self.settings['use-uefi']:
-            return
+            return None
+
         espsize = self.settings['esp-size'] / (1024 * 1024)
         self.message("Using ESP size: %smib %s bytes" % (espsize, self.settings['esp-size']))
-        runcmd(['parted', '-s', self.settings['image'],
-                'mkpart', 'primary', 'fat32',
-                '1', str(espsize)])
-        runcmd(['parted', '-s', self.settings['image'],
-                'set', '1', 'boot', 'on'])
-        runcmd(['parted', '-s', self.settings['image'],
-                'set', '1', 'esp', 'on'])
+
+        esp = partitions.new('esp')
+        esp.parted_type = 'fat32'
+        esp.type = 'vfat'
+        esp.flags = set(['boot', 'esp'])
+        esp.end = esp.start + espsize
+        partitions.commit(esp)
+        return esp
 
     def prepare_esp(self, rootdir, bootdev):
         self.bootdir = '%s/%s/%s' % (rootdir, 'boot', 'efi')
@@ -160,8 +161,3 @@ class Uefi(Base):
         self.mkfs(bootdev, fstype='vfat')
         os.makedirs(self.bootdir)
         return self.bootdir
-
-    def make_root(self, extent):
-        bootsize = self.settings['esp-size'] / (1024 * 1024) + 1
-        runcmd(['parted', '-s', self.settings['image'],
-                'mkpart', 'primary', str(bootsize), extent])
-- 
2.8.1

>From ecd29b08c2db4d9962a1d92ee3016ce547c3f492 Mon Sep 17 00:00:00 2001
From: Simon McVittie <s...@debian.org>
Date: Mon, 15 Aug 2016 11:28:03 +0100
Subject: [PATCH 5/6] Partitions: do swap creation in terms of 1 MiB offsets,
 not percent

Signed-off-by: Simon McVittie <s...@debian.org>
---
 bin/vmdebootstrap           |  2 +-
 vmdebootstrap/partitions.py | 17 +++++++++++------
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/bin/vmdebootstrap b/bin/vmdebootstrap
index 9dd2cae..5c554b2 100755
--- a/bin/vmdebootstrap
+++ b/bin/vmdebootstrap
@@ -439,7 +439,7 @@ class VmDebootstrap(cliapp.Application):  # pylint: disable=too-many-public-meth
         partitions.ensure_something_bootable()
 
         # return to doing swap setup
-        partitions.make_swap(extent)
+        partitions.make_swap()
 
     def _bootstrap_packages(self):
         base = self.handlers[Base.name]
diff --git a/vmdebootstrap/partitions.py b/vmdebootstrap/partitions.py
index 4756c43..4d4a036 100644
--- a/vmdebootstrap/partitions.py
+++ b/vmdebootstrap/partitions.py
@@ -48,6 +48,7 @@ class Partitions(Base):
         self.partitions = []
         # in MiB
         self.start_space = 0
+        self.end_space = 0
 
     def create_table(self):
         partoffset = 0
@@ -71,6 +72,9 @@ class Partitions(Base):
                     (partoffset, self.settings['bootoffset']))
 
         self.start_space = partoffset
+        self.end_space = self.settings['size'] / (1024 * 1024)
+        # Allow 1 MiB of space for the backup GPT
+        self.end_space -= 1
 
     def new(self, name):
         part = Partition(name, len(self.partitions) + 1)
@@ -113,7 +117,7 @@ class Partitions(Base):
 
     def check_swap_size(self):
         # swap - modifies extent
-        extent = '100%'
+        extent = self.end_space
         swap = 256 * 1024 * 1024
         if self.settings['swap'] > 0:
             if self.settings['swap'] > swap:
@@ -121,12 +125,13 @@ class Partitions(Base):
             else:
                 # minimum 256MB as default qemu ram is 128MB
                 logging.debug("Setting minimum 256MB swap space")
-            extent = "%s%%" % int(100 * (self.settings['size'] - swap) / self.settings['size'])
+            extent = self.end_space - (swap / (1024 * 1024))
         return extent
 
-    def make_swap(self, extent):
+    def make_swap(self):
         if self.settings['swap'] > 0:
             logging.debug("Creating swap partition")
-            runcmd([
-                'parted', '-s', self.settings['image'],
-                'mkpart', 'primary', 'linux-swap', extent, '100%'])
+            swap = self.new('swap')
+            swap.parted_type = 'linux-swap'
+            swap.end = self.end_space
+            self.commit(swap)
-- 
2.8.1

>From 304b4145a75aad53042ee364c0908d889d1bbdfc Mon Sep 17 00:00:00 2001
From: Simon McVittie <s...@debian.org>
Date: Mon, 15 Aug 2016 13:46:40 +0100
Subject: [PATCH 6/6] Filesystem: use Partitions to set up kpartx

This avoids second-guessing which partition corresponds to which index.

Signed-off-by: Simon McVittie <s...@debian.org>
---
 bin/vmdebootstrap           |  3 ++-
 vmdebootstrap/filesystem.py | 37 +++++++++++--------------------------
 2 files changed, 13 insertions(+), 27 deletions(-)

diff --git a/bin/vmdebootstrap b/bin/vmdebootstrap
index 5c554b2..cbeb185 100755
--- a/bin/vmdebootstrap
+++ b/bin/vmdebootstrap
@@ -220,10 +220,11 @@ class VmDebootstrap(cliapp.Application):  # pylint: disable=too-many-public-meth
         filesystem = self.handlers[Filesystem.name]
         extlinux = self.handlers[ExtLinux.name]
         distro = self.handlers[Codenames.name]
+        partitions = self.handlers[Partitions.name]
         base.create_empty_image()
         self.partition_image()
         extlinux.install_mbr()
-        filesystem.setup_kpartx()
+        filesystem.setup_kpartx(partitions)
         rootdev = filesystem.devices['rootdev']
         roottype = filesystem.devices['roottype']
         bootdev = filesystem.devices['bootdev']
diff --git a/vmdebootstrap/filesystem.py b/vmdebootstrap/filesystem.py
index 3db100a..20749ab 100644
--- a/vmdebootstrap/filesystem.py
+++ b/vmdebootstrap/filesystem.py
@@ -85,35 +85,20 @@ class Filesystem(Base):
             self.message("Updating the initramfs")
             runcmd(['chroot', rootdir, cmd, '-u'])
 
-    def setup_kpartx(self):
+    def setup_kpartx(self, partitions):
         bootindex = None
+        rootindex = None
         swapindex = None
         out = runcmd(['kpartx', '-avs', self.settings['image']])
-        if self.settings['bootsize'] and self.settings['swap'] > 0:
-            bootindex = 0
-            rootindex = 1
-            swapindex = 2
-            parts = 3
-        elif self.settings['use-uefi'] and self.settings['swap'] > 0:
-            bootindex = 0
-            rootindex = 1
-            swapindex = 2
-            parts = 3
-        elif self.settings['use-uefi']:
-            bootindex = 0
-            rootindex = 1
-            parts = 2
-        elif self.settings['bootsize']:
-            bootindex = 0
-            rootindex = 1
-            parts = 2
-        elif self.settings['swap'] > 0:
-            rootindex = 0
-            swapindex = 1
-            parts = 2
-        else:
-            rootindex = 0
-            parts = 1
+        parts = len(partitions.partitions)
+        for part in partitions.partitions:
+            if part.name in ('boot', 'esp'):
+                bootindex = part.index - 1
+            elif part.name == 'root':
+                rootindex = part.index - 1
+            elif part.name == 'swap':
+                swapindex = part.index - 1
+        assert rootindex is not None
         boot = None
         swap = None
         devices = [line.decode('utf-8').split()[2]
-- 
2.8.1

Reply via email to