#!/bin/sh
# Copyright (C) 2016-2017 Endless Mobile, Inc.
# Licensed under the GPLv2
#
# Support booting from an image file hosted on a filesystem, through the use
# of loopback devices.

if [ $# -ge 1 ]; then
  # For testing
  if [ "$1" = "--readonly" ]; then
    shift
    roflag="-r"
  fi

  host_device="$1"
  image_path="$2"
else
  type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh

  image_device=$(getarg endless.image.device)
  image_path=$(getarg endless.image.path)

  # When booting an ISO using HD emulation (e.g. booting an ISO file flashed
  # to USB), udev records that the ISO UUID applies to both the iso9660
  # data partition and the parent disk node, so we have to be careful
  # when finding host_device.
  #
  # We wait for udev to finish probing the partitions, and rely on the data
  # partition node having been probed after the parent device, hence it
  # will win the race to own the by-uuid symlink.
  udevadm settle
  host_device=/dev/disk/by-uuid/${image_device#UUID=}
  if ! [ -b "${host_device}" ]; then
    echo "Failed to find host partition device"
    exit 1
  fi

  # If possible, create partition for persistent data storage
  eos-live-storage-setup "${host_device}"

  getargbool 0 endless.live_boot && roflag="-r"
fi

fstype=$(lsblk --noheadings -o FSTYPE "${host_device}")
if [ $? != 0 ]; then
  echo "image-boot: failed to detect filesystem type"
  exit 1
fi
if [ "${fstype}" = "ntfs" ]; then
  fstype=ntfs3
fi

case "$image_path" in
*.squash)  squashfs=1 ;;
*)         squashfs=  ;;
esac

# Identify the EOS image extents on the host device, and create a dm-linear
# block device that maps exactly to that. If the image is a squashfs, then
# first map that, then setup a loopback device for the uncompressed image
# within it.

# First, map the outermost image file (uncompressed or squashfs)
mkdir -p /outer_image

# /usr/bin/mount recently became busybox, and it seems to have a hard time
# guessing the filesystem when the isofs kernel module isn't loaded.
# If we specify it manually, it should be ok.
#
# Even with util-linux mount, manually specifying this saves a few
# pointless read operations, so it's probably a good idea anyway.
if ! mount -t $fstype ${host_device} /outer_image; then
  echo "Failed to mount ${host_device}"
  exit 1
fi

# Create a loopback device backing the image
image_device=$(losetup -f)
losetup ${roflag} ${image_device} /outer_image/${image_path}
if [ $? != 0 ]; then
  echo "losetup failed for /outer_image/${image_path}"
  exit 1
fi

# If $image_path is a squashfs, now create a loopback device for the
# uncompressed image within it
if [ -n "$squashfs" ]; then
  # Mount the squashfs
  mkdir /squash
  if ! mount ${image_device} /squash; then
    echo "Failed to mount ${image_device}"
    exit 1
  fi

  # Create a loopback device backing the endless image
  image_device=$(losetup -f)
  losetup ${image_device} /squash/endless.img
fi

if ! partx -a -v "${image_device}"; then
  echo "image-boot: failed to probe partitions"
  exit 1
fi

# Setup a stable /dev/disk/endless-image symlink for our image device and
# partitions.
#
# This is used by eos-installer, which doesn't have the knowledge how
# to find the image inside a squashfs, so it uses this path. This also
# requires the device node to have appropriate permissions.
# This path is also used by the rest of the initramfs which is hardcoded
# to find the rootfs at /dev/disk/endless-image3.
image_device_name="${image_device##*/}"
mkdir -p /run/udev/rules.d
cat <<EOF > /run/udev/rules.d/70-endless-image.rules
SUBSYSTEM=="block", KERNEL=="${image_device_name}", MODE="0664", SYMLINK+="disk/endless-image"
SUBSYSTEM=="block", KERNEL=="${image_device_name}p?", MODE="0664", SYMLINK+="disk/endless-image%n"
EOF

udevadm control --reload-rules

# Ideally these rules could be placed before the disk devices appear and they
# would then kick in automatically, but in practice, since there are some
# devices like loop0 which always exist, it is necessary to explicitly trigger.
udevadm trigger "${image_device}"

exit 0
