Add install flag to generate separate boot and system images (#1442)

* Usage: pmbootstrap install --split
* Make obvious that export is the next step when split images are created
* Fix note for missing rootfs image on export
  * Change wording from "system image" to "rootfs image"
  * The idea was to show the note only when the rootfs image was not
    generated yet. But this was broken, because the path we checked for
    was missing the chroot path prefix (which is added now).
  * Also don't display the message, when the split image files exist
This commit is contained in:
Daniele Debernardi 2018-05-01 02:18:40 +02:00 committed by Oliver Smith
parent 4e665a2190
commit 827a60cd25
5 changed files with 79 additions and 46 deletions

View file

@ -1,3 +1,4 @@
import glob
import logging import logging
import os import os
@ -13,10 +14,11 @@ def frontend(args):
if not os.path.exists(target): if not os.path.exists(target):
pmb.helpers.run.user(args, ["mkdir", "-p", target]) pmb.helpers.run.user(args, ["mkdir", "-p", target])
# System image note # Rootfs image note
img_path = "/home/pmos/rootfs/" + args.device + ".img" chroot = args.work + "/chroot_native"
if not os.path.exists(args.work + "/chroot_native" + img_path): pattern = chroot + "/home/pmos/rootfs/" + args.device + "*.img"
logging.info("NOTE: To export the system image, run 'pmbootstrap" if not glob.glob(pattern):
logging.info("NOTE: To export the rootfs image, run 'pmbootstrap"
" install' first (without the 'sdcard' parameter).") " install' first (without the 'sdcard' parameter).")
# Rebuild the initramfs, just to make sure (see #69) # Rebuild the initramfs, just to make sure (see #69)

View file

@ -42,6 +42,8 @@ def symlinks(args, flavor, folder):
"uImage-" + flavor: "Kernel, legacy u-boot image format", "uImage-" + flavor: "Kernel, legacy u-boot image format",
"vmlinuz-" + flavor: "Linux kernel", "vmlinuz-" + flavor: "Linux kernel",
args.device + ".img": "Rootfs with partitions for /boot and /", args.device + ".img": "Rootfs with partitions for /boot and /",
args.device + "-boot.img": "Boot partition image",
args.device + "-root.img": "Root partition image",
"pmos-" + args.device + ".zip": "Android recovery flashable zip", "pmos-" + args.device + ".zip": "Android recovery flashable zip",
} }
@ -51,6 +53,8 @@ def symlinks(args, flavor, folder):
path_buildroot = args.work + "/chroot_buildroot_" + args.deviceinfo["arch"] path_buildroot = args.work + "/chroot_buildroot_" + args.deviceinfo["arch"]
patterns = [path_boot + "/*-" + flavor, patterns = [path_boot + "/*-" + flavor,
path_native + "/home/pmos/rootfs/" + args.device + ".img", path_native + "/home/pmos/rootfs/" + args.device + ".img",
path_native + "/home/pmos/rootfs/" + args.device + "-boot.img",
path_native + "/home/pmos/rootfs/" + args.device + "-root.img",
path_buildroot + path_buildroot +
"/var/lib/postmarketos-android-recovery-installer/pmos-" + "/var/lib/postmarketos-android-recovery-installer/pmos-" +
args.device + ".zip"] args.device + ".zip"]

View file

@ -46,9 +46,9 @@ def mount_device_rootfs(args, suffix="native"):
def get_subpartitions_size(args): def get_subpartitions_size(args):
""" """
Calculate the size of the whole image and boot subpartition. Calculate the size of the boot and root subpartition.
:returns: (full, boot) the size of the full image and boot :returns: (boot, root) the size of the boot and root
partition as integer in bytes partition as integer in bytes
""" """
# Calculate required sizes first # Calculate required sizes first
@ -66,7 +66,7 @@ def get_subpartitions_size(args):
full *= 1.20 full *= 1.20
full += 50 * 1024 * 1024 full += 50 * 1024 * 1024
boot += 15 * 1024 * 1024 boot += 15 * 1024 * 1024
return (full, boot) return (boot, full - boot)
def get_nonfree_packages(args, device): def get_nonfree_packages(args, device):
@ -284,11 +284,13 @@ def install_system_image(args):
# Partition and fill image/sdcard # Partition and fill image/sdcard
logging.info("*** (3/5) PREPARE INSTALL BLOCKDEVICE ***") logging.info("*** (3/5) PREPARE INSTALL BLOCKDEVICE ***")
pmb.chroot.shutdown(args, True) pmb.chroot.shutdown(args, True)
(size_image, size_boot) = get_subpartitions_size(args) (size_boot, size_root) = get_subpartitions_size(args)
if not args.rsync: if not args.rsync:
pmb.install.blockdevice.create(args, size_image) pmb.install.blockdevice.create(args, size_boot, size_root)
pmb.install.partition(args, size_boot) if not args.split:
pmb.install.partitions_mount(args) pmb.install.partition(args, size_boot)
if not args.split:
pmb.install.partitions_mount(args)
if args.full_disk_encryption: if args.full_disk_encryption:
logging.info("WARNING: Full disk encryption is enabled!") logging.info("WARNING: Full disk encryption is enabled!")
@ -309,7 +311,7 @@ def install_system_image(args):
pmb.chroot.shutdown(args, True) pmb.chroot.shutdown(args, True)
# Convert system image to sparse using img2simg # Convert system image to sparse using img2simg
if args.deviceinfo["flash_sparse"] == "true": if args.deviceinfo["flash_sparse"] == "true" and not args.split:
logging.info("(native) make sparse system image") logging.info("(native) make sparse system image")
pmb.chroot.apk.install(args, ["libsparse"]) pmb.chroot.apk.install(args, ["libsparse"])
sys_image = args.device + ".img" sys_image = args.device + ".img"
@ -325,7 +327,7 @@ def install_system_image(args):
" target device:") " target device:")
# System flash information # System flash information
if not args.sdcard: if not args.sdcard and not args.split:
logging.info("* pmbootstrap flasher flash_rootfs") logging.info("* pmbootstrap flasher flash_rootfs")
logging.info(" Flashes the generated rootfs image to your device:") logging.info(" Flashes the generated rootfs image to your device:")
logging.info(" " + args.work + "/chroot_native/home/pmos/rootfs/" + logging.info(" " + args.work + "/chroot_native/home/pmos/rootfs/" +
@ -346,9 +348,14 @@ def install_system_image(args):
" Use 'pmbootstrap flasher boot' to do that.)") " Use 'pmbootstrap flasher boot' to do that.)")
# Export information # Export information
logging.info("* If the above steps do not work, you can also create" if args.split:
" symlinks to the generated files with 'pmbootstrap export'" logging.info("* Boot and root image files have been generated, run"
" and flash outside of pmbootstrap.") " 'pmbootstrap export' to create symlinks and flash"
" outside of pmbootstrap.")
else:
logging.info("* If the above steps do not work, you can also create"
" symlinks to the generated files with 'pmbootstrap export'"
" and flash outside of pmbootstrap.")
def install_recovery_zip(args): def install_recovery_zip(args):

View file

@ -70,56 +70,73 @@ def mount_sdcard(args):
raise RuntimeError("Aborted.") raise RuntimeError("Aborted.")
def create_and_mount_image(args, size): def create_and_mount_image(args, size_boot, size_root):
""" """
Create a new image file, and mount it as /dev/install. Create a new image file, and mount it as /dev/install.
:param size: of the whole image in bytes :param size_boot: size of the boot partition in bytes
:param size_root: size of the root partition in bytes
""" """
# Short variables for paths # Short variables for paths
chroot = args.work + "/chroot_native" chroot = args.work + "/chroot_native"
img_path = "/home/pmos/rootfs/" + args.device + ".img" img_path_prefix = "/home/pmos/rootfs/" + args.device
img_path_outside = chroot + img_path img_path_full = img_path_prefix + ".img"
img_path_boot = img_path_prefix + "-boot.img"
img_path_root = img_path_prefix + "-root.img"
# Umount and delete existing image # Umount and delete existing images
if os.path.exists(img_path_outside): for img_path in [img_path_full, img_path_boot, img_path_root]:
pmb.helpers.mount.umount_all(args, chroot + "/mnt") outside = chroot + img_path
pmb.install.losetup.umount(args, img_path) if os.path.exists(outside):
pmb.chroot.root(args, ["rm", img_path]) pmb.helpers.mount.umount_all(args, chroot + "/mnt")
if os.path.exists(img_path_outside): pmb.install.losetup.umount(args, img_path)
raise RuntimeError("Failed to remove old image file: " + pmb.chroot.root(args, ["rm", img_path])
img_path_outside)
# Make sure there is enough free space # Make sure there is enough free space
size_mb = round(size / (1024**2)) size_mb = round((size_boot + size_root) / (1024**2))
disk_data = os.statvfs(args.work) disk_data = os.statvfs(args.work)
free = round((disk_data.f_bsize * disk_data.f_bavail) / (1024**2)) free = round((disk_data.f_bsize * disk_data.f_bavail) / (1024**2))
if size_mb > free: if size_mb > free:
raise RuntimeError("Not enough free space to create rootfs image! (free: " + str(free) + "M, required: " + str(size_mb) + "M)") raise RuntimeError("Not enough free space to create rootfs image! (free: " + str(free) + "M, required: " + str(size_mb) + "M)")
mb = str(size_mb) + "M"
# Create empty image file # Create empty image files
logging.info("(native) create " + args.device + ".img (" + mb + ")")
pmb.chroot.user(args, ["mkdir", "-p", "/home/pmos/rootfs"]) pmb.chroot.user(args, ["mkdir", "-p", "/home/pmos/rootfs"])
pmb.chroot.root(args, ["truncate", "-s", mb, img_path]) size_mb_full = str(size_mb) + "M"
size_mb_boot = str(round(size_boot / (1024**2))) + "M"
size_mb_root = str(round(size_root / (1024**2))) + "M"
images = {img_path_full: size_mb_full}
if args.split:
images = {img_path_boot: size_mb_boot,
img_path_root: size_mb_root}
for img_path, size_mb in images.items():
logging.info("(native) create " + os.path.basename(img_path) + " (" + size_mb + ")")
pmb.chroot.root(args, ["truncate", "-s", size_mb, img_path])
# Mount to /dev/install # Mount to /dev/install
logging.info("(native) mount /dev/install (" + args.device + ".img)") mount_image_paths = {img_path_full: "/dev/install"}
pmb.install.losetup.mount(args, img_path) if args.split:
device = pmb.install.losetup.device_by_back_file(args, img_path) mount_image_paths = {img_path_boot: "/dev/installp1",
pmb.helpers.mount.bind_blockdevice(args, device, args.work + img_path_root: "/dev/installp2"}
"/chroot_native/dev/install")
for img_path, mount_point in mount_image_paths.items():
logging.info("(native) mount " + mount_point +
" (" + os.path.basename(img_path) + ")")
pmb.install.losetup.mount(args, img_path)
device = pmb.install.losetup.device_by_back_file(args, img_path)
pmb.helpers.mount.bind_blockdevice(args, device, args.work +
"/chroot_native" + mount_point)
def create(args, size): def create(args, size_boot, size_root):
""" """
Create /dev/install (the "install blockdevice"). Create /dev/install (the "install blockdevice").
:param size: of the whole image in bytes :param size_boot: size of the boot partition in bytes
:param size_root: size of the root partition in bytes
""" """
pmb.helpers.mount.umount_all( pmb.helpers.mount.umount_all(
args, args.work + "/chroot_native/dev/install") args, args.work + "/chroot_native/dev/install")
if args.sdcard: if args.sdcard:
mount_sdcard(args) mount_sdcard(args)
else: else:
create_and_mount_image(args, size) create_and_mount_image(args, size_boot, size_root)

View file

@ -321,8 +321,14 @@ def arguments():
# Action: install # Action: install
install = sub.add_parser("install", help="set up device specific" + install = sub.add_parser("install", help="set up device specific" +
" chroot and install to sdcard or image file") " chroot and install to sdcard or image file")
install.add_argument("--sdcard", help="path to the sdcard device," group = install.add_mutually_exclusive_group()
" eg. /dev/mmcblk0") group.add_argument("--sdcard", help="path to the sdcard device,"
" eg. /dev/mmcblk0")
group.add_argument("--split", help="install the boot and root partition"
" in separated image files", action="store_true")
group.add_argument("--android-recovery-zip",
help="generate TWRP flashable zip",
action="store_true", dest="android_recovery_zip")
install.add_argument("--rsync", help="update the sdcard using rsync," install.add_argument("--rsync", help="update the sdcard using rsync,"
" only works with --no-fde", action="store_true") " only works with --no-fde", action="store_true")
install.add_argument("--cipher", help="cryptsetup cipher used to" install.add_argument("--cipher", help="cryptsetup cipher used to"
@ -337,9 +343,6 @@ def arguments():
install.add_argument("--flavor", install.add_argument("--flavor",
help="Specify kernel flavor to include in recovery" help="Specify kernel flavor to include in recovery"
" flashable zip", default=None) " flashable zip", default=None)
install.add_argument("--android-recovery-zip",
help="generate TWRP flashable zip",
action="store_true", dest="android_recovery_zip")
install.add_argument("--recovery-install-partition", default="system", install.add_argument("--recovery-install-partition", default="system",
help="partition to flash from recovery," help="partition to flash from recovery,"
" eg. external_sd", " eg. external_sd",