# vim: filetype=sh

# partitioning and formatting
# ---------------------------

# Selection of ext4 features:
# We must select only features available on the *oldest* system
# version we want to support. We can get these features by
# creating a sample filesystem on such a system:

# $ cd /tmp
# $ dd of=test.ext4 bs=1G count=0 seek=100
# $ mkfs.ext4 -F -q -L ROOT -T big -m 2 test.ext4
# $ dumpe2fs test.ext4 | grep features

# Note about the '-T big' option:
# We try to create a USB stick as small as possible.
# However, the embedded system may later by copied on a
# potentially large disk.
# As a result, we should select appropriate ext4 features even if
# the filesystem might be considered 'small' at first.
# This may seem cosmetic but it's not: if initialized with
# '-T small' (or with no -T option and run on a small disk),
# when we move to a large disk, resize2fs apparently
# enables the 'meta_bg' option (supposedly trying to adapt as much
# as possible this 'small' filesystem to a much larger device).
# Since this option is not handled by grub, it prevents the
# system from booting properly.

EXT4_OVERHEAD_PERCENT=18
LVM_OVERHEAD_PERCENT=4
EXT4_FEATURES=$(cat << EOF
has_journal ext_attr resize_inode dir_index filetype extent
flex_bg sparse_super large_file huge_file uninit_bg dir_nlink
extra_isize
EOF
)

make_root_fs()
{
    rootfs_device=$1
    rootfs_label=$2
    features="$(echo $EXT4_FEATURES | tr ' ' ',')"
    mkfs.ext4 -F -q -L $rootfs_label -b 4096 -O "none,$features" -m 2 $rootfs_device
}

setup_root_filesystem()
{
    root_device=$1
    mountpoint=$2
    rootfs_label=$3

    # format this lvm volume
    make_root_fs $root_device $rootfs_label

    # mount it
    mkdir "$mountpoint"
    failsafe mount $root_device "$mountpoint"
}

setup_root_filesystem_on_lvm()
{
    part_device=$1
    mountpoint=$2
    lvm_vg=$3
    lv_device=$4
    rootfs_label=$5

    quiet pvcreate $part_device
    quiet vgcreate $lvm_vg $part_device
    quiet lvcreate -n ROOT -l 100%FREE $lvm_vg

    setup_root_filesystem $lv_device $mountpoint $rootfs_label
}

get_partition_devices()
{
    image_device="$1"
    kpartx -l $image_device | awk '{ print "/dev/mapper/"$1 }'
}

create_formatted_image()
{
    image_name=$1   # 'draft' or 'final'
    stick_size_kb=$2
    stick_os_id=$3
    image_file="$4"
    work_dir="$DBSTCK_TMPDIR/$image_name"
    if [ -z "$image_file" ]
    then
        image_file="$work_dir/file"
    fi

    mkdir -p "$work_dir"

    # create image file
    rm -f "$image_file"
    $DD bs=1024 seek=$stick_size_kb count=0 of="$image_file"

    # call target_specific formatting function
    target_partition_image "$image_file"

    # let the kernel know about this device
    image_device=$(losetup -f)
    failsafe losetup $image_device "$image_file"
    failsafe kpartx -a $image_device

    # retrieve the partition devices
    partition_devices="$(get_partition_devices $image_device)"

    # the last one will get the OS filesystem
    root_partition_num="$(echo "$partition_devices" | wc -l)"

    # format and optionally mount partitions
    i=1
    for part_device in $(echo "$partition_devices")
    do
        wait_for_device $part_device
        if [ $i -eq $root_partition_num ]
        then
            # root partition
            mp="$work_dir/rootfs"
            root_part_device=$part_device
            rootfs_label="$(get_rootfs_label $stick_os_id)"
            if $(target_use_lvm)
            then
                lvm_vg="$(get_vg_name $image_name $stick_os_id)"
                lv_device=/dev/$lvm_vg/ROOT
                setup_root_filesystem_on_lvm $part_device "$mp" \
                                $lvm_vg $lv_device $rootfs_label
                mounts_info="$lv_device $mp $mounts_info"
            else
                setup_root_filesystem $part_device "$mp" $rootfs_label
                mounts_info="$part_device $mp $mounts_info"
            fi
        else
            mp="$work_dir/part$i"
            # other target-specific partitions
            if target_format_partition $i $part_device
            then
                mkdir "$mp" && failsafe mount $part_device "$mp"
                mounts_info="$part_device $mp $mounts_info"
            fi
        fi
        i=$((i+1))
    done

    # let the calling code know what we have done
    eval "$(cat << EOF
${image_name}_file="$image_file"
${image_name}_device=$image_device
${image_name}_rootpart_device="$root_part_device"
${image_name}_rootfs_mountpoint="$work_dir/rootfs"
${image_name}_mounts_info="$mounts_info"
EOF
    )"
}

release_image()
{
    image_name="$1"     # 'draft' or 'final'

    # read variables with prefix draft or final
    eval "$(cat << EOF
image_file="\${${image_name}_file}"
image_device=\${${image_name}_device}
mounts_info=\${${image_name}_mounts_info}
EOF
    )"

    # detach things
    set -- $mounts_info
    while [ ! -z "$1" ]
    do
        undo mount $1 $2
        shift 2
    done
    undo kpartx -a $image_device
    undo losetup $image_device "$image_file"
}

estimate_minimal_rootpart_size_kb()
{
    rootfs_mountpoint="$1"
    data_size_kb=$(estimated_size_kb "$rootfs_mountpoint")
    overheads=$EXT4_OVERHEAD_PERCENT
    if $(target_use_lvm)
    then
        overheads="$overheads $LVM_OVERHEAD_PERCENT"
    fi
    echo $(apply_overheads_percent $data_size_kb $overheads)
}
