summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Bauer2018-11-12 12:42:55 +0100
committerJonathan Bauer2018-11-12 17:30:33 +0100
commit4a26a9fb86d98983c1485c63399fc89d593e8f9c (patch)
treead4996aec7b863aa1275104206c3ac11a5068519
parent[dnbd3-rfs] config: add PXE server info to config (diff)
downloadsystemd-init-4a26a9fb86d98983c1485c63399fc89d593e8f9c.tar.gz
systemd-init-4a26a9fb86d98983c1485c63399fc89d593e8f9c.tar.xz
systemd-init-4a26a9fb86d98983c1485c63399fc89d593e8f9c.zip
[slx-partitioner] new module for scratch partition handling
-rwxr-xr-xbuilder/modules.d/slx-partitioner/module-setup.sh17
-rw-r--r--builder/modules.d/slx-partitioner/scripts/gen-fstab-persistent52
-rw-r--r--builder/modules.d/slx-partitioner/scripts/generate-fstab-swap.sh33
-rwxr-xr-xbuilder/modules.d/slx-partitioner/scripts/get_partitions_by_id57
-rwxr-xr-xbuilder/modules.d/slx-partitioner/scripts/slx_partitioner391
5 files changed, 550 insertions, 0 deletions
diff --git a/builder/modules.d/slx-partitioner/module-setup.sh b/builder/modules.d/slx-partitioner/module-setup.sh
new file mode 100755
index 00000000..6ab8a994
--- /dev/null
+++ b/builder/modules.d/slx-partitioner/module-setup.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+check() {
+ return 255
+}
+depends() {
+ echo ""
+}
+install() {
+ inst "$moddir/scripts/get_partitions_by_id" "/usr/bin/get_partitions_by_id"
+ inst "$moddir/scripts/slx_partitioner" "/usr/bin/slx_partitioner"
+ inst_hook pre-pivot 10 "$moddir/scripts/generate-fstab-swap.sh"
+ inst_multiple mkfs.ext4 mkfs.xfs fsck.ext4 fsck.xfs blockdev xxd
+# inst_hook pre-pivot 10 "$moddir/scripts/gen-fstab-persistent"
+}
+installkernel() {
+ instmods dm-thin-pool dm-snapshot dm-crypt crc32c ext4 xfs # btrfs
+}
diff --git a/builder/modules.d/slx-partitioner/scripts/gen-fstab-persistent b/builder/modules.d/slx-partitioner/scripts/gen-fstab-persistent
new file mode 100644
index 00000000..ed00b5de
--- /dev/null
+++ b/builder/modules.d/slx-partitioner/scripts/gen-fstab-persistent
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+#
+# Hook to generate stage4's fstab entries for persistent partitions
+#
+# Persistent identifiers (MBR types, GPT partition labels)
+# are expected to be specified in the OpenSLX config
+# as 'SLX_PERSISTENT_DEVICE_IDENTIFIER' and their filesystem
+# as 'SLX_PERSISTENT_DEVICE_FILESYSTEM', e.g ext4 or xfs.
+# If not specified, will default to 'auto' but will not
+# active systemd's features 'x-systemd.makefs' and 'x-systemd.growfs'
+
+. /etc/openslx
+
+type -p emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh
+
+# NOTE: systemd makes the mount point path automatically.
+# if multiple exists, take the biggest one (first in the list)
+if [ -n "$SLX_PERSISTENT_DEVICE_IDENTIFIER" ]; then
+ declare -a persistent_dev_list
+ for persistent_dev in \
+ $(get_partitions_by_id ${SLX_PERSISTENT_DEVICE_IDENTIFIER//,/ /}); do
+ [ -z "$persistent_dev" ] && continue
+ persistent_dev_list+=("$persistent_dev")
+ done
+ if [ "${#persistent_dev[@]}" -gt 0 ]; then
+ if [ "${#persistent_dev[@]}" -gt 1 ]; then
+ warn "$0: More than one persistent device found."
+ warn "$0: Will use the biggest one: ${persistent_dev[0]}"
+ fi
+ persistent_dev_systemd_name="$( tr '/' '-' <<< ${persistent_dev[0]:1})"
+ persistent_mount_opts="nofail"
+ if [ -n "$SLX_PERSISTENT_DEVICE_FILESYSTEM" ]; then
+ #persistent_mount_opts+=",x-systemd.requires=ensure-fs@${persistent_dev_systemd_name}"
+ persistent_mount_opts+=",x-systemd.after=ensure-fs@${persistent_dev_systemd_name}"
+ fi
+ (
+ echo -ne "${persistent_dev[0]}\t"
+ echo -ne "${SLX_PERSISTENT_DEVICE_MOUNT_POINT:-/opt/openslx/persistent}\t"
+ echo -ne "${SLX_PERSISTENT_DEVICE_FILESYSTEM:-auto}\t"
+ echo -ne "${persistent_mount_opts}\t0\t2"
+ ) >> "$NEWROOT/etc/fstab"
+
+ # drop-in to create filesystem if needed and
+ #persistent_dev_systemd_name="$( tr '/' '-' <<< ${persistent_dev[0]:1})"
+ #mkdir -p "$NEWROOT/etc/systemd/system/${persistent_dev_systemd_name}"
+ #cat <<- EOF > "$NEWROOT"
+ #EOF
+ else
+ warn "$0: No device with ID '$SLX_PERSISTENT_DEVICE_IDENTIFIER' found."
+ fi
+fi
+true
diff --git a/builder/modules.d/slx-partitioner/scripts/generate-fstab-swap.sh b/builder/modules.d/slx-partitioner/scripts/generate-fstab-swap.sh
new file mode 100644
index 00000000..5330730a
--- /dev/null
+++ b/builder/modules.d/slx-partitioner/scripts/generate-fstab-swap.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+#
+# Script to create stage4's fstab entry for swap devices
+#
+# Will use swap partitions of MBR's type '82' or
+# GPT UUID '0657fd6d-a4ab-43c4-84e5-0933c84b4f4f'
+#
+. /etc/openslx
+
+for swap_dev in \
+ $(get_partitions_by_id "82" "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f"); do
+
+ [ -z "$swap_dev" ] && continue
+
+ # Generate swap fstab entry for NEWROOT. Use priority 10 to prefer zram
+ echo -e "$swap_dev\tswap\t\tswap\t\tx-systemd.makefs,pri=10\t0\t0" \
+ >> "$NEWROOT/etc/fstab"
+
+ # check if configured not to wipe any existing filesystem
+ [ "$SLX_WIPE_SWAP_DEVICE" = "no" ] && continue
+
+ # create a drop-in to wipe the device's filesystem
+ swap_dev_systemd_escaped="$(tr '/' '-' <<< ${swap_dev:1})"
+ base_dir="$NEWROOT/etc/systemd/system"
+ dropin_dir="$base_dir/systemd-mkswap@${swap_dev_systemd_escaped}.service.d"
+ mkdir -p "$dropin_dir"
+ cat <<- EOF > "$dropin_dir/wipefs.conf"
+ [Service]
+ ExecStartPre=/sbin/wipefs -a %f
+ EOF
+done
+
+true
diff --git a/builder/modules.d/slx-partitioner/scripts/get_partitions_by_id b/builder/modules.d/slx-partitioner/scripts/get_partitions_by_id
new file mode 100755
index 00000000..986001a1
--- /dev/null
+++ b/builder/modules.d/slx-partitioner/scripts/get_partitions_by_id
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+#
+# Get all partitions with given ids (list of /dev/sdXX)
+# Examples of supported ID types:
+# * MBR's type: 44
+# * GPT's UUID: 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f (swap)
+# * GPT's Name: OpenSLX-ID44
+#
+# First argument can be a block device to limit the search to
+# partitions thereof, e.g. for /dev/loop0 to look for /dev/loop0pX
+#
+# NOTE: for compatibility reasons, MBR's type will also
+# be matched against part name 'OpenSLX-ID<id>'.
+# The output will be a list of matching devices,
+# sorted from largest to smallest.
+get_partitions_by_id () {
+ local ID dev exp target
+ exp=
+ # target for the scan, defaults to /dev to check everything
+ target=/dev
+ if [ -b "$1" ]; then
+ target="$1"'*'
+ shift
+ fi
+ while [ $# -gt 0 ]; do
+ ID=$1
+ shift
+ [ -z "$ID" ] && continue
+ # if single digit, e.g. 7, look for 0x7 and 0x07
+ [[ $ID =~ ^[0-9]$ ]] && ID="0?$ID"
+ if [[ $ID =~ ^[0-9]{2}$ ]]; then
+ # if double digit look for MBR types and OpenSLX-ID$ID GPT labels
+ exp="$exp|ID_PART_ENTRY_(NAME=OpenSLX-ID|TYPE=0x)$ID"
+ elif [[ $ID =~ ^(0x[0-9]{2}|[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})$ ]]; then
+ # if full MBR type (e.g. 0x44) or UUID look for TYPE
+ exp="$exp|ID_PART_ENTRY_TYPE=$ID"
+ else
+ # something else, look for names of partitions / filesystems
+ exp="$exp|ID_(PART_ENTRY_NAME|FS_LABEL)=$ID"
+ fi
+ done
+ exp=${exp:1}
+ #echo "Partition find is '$exp'" >&2
+ for dev in $(find $target -type b); do
+ udevadm info --name="$dev" | grep -iqE "($exp)\$" \
+ && echo "$(blockdev --getsize64 "$dev") $dev"
+ done | sort -n -k1 -r | cut -d' ' -f2
+}
+
+## MAIN
+if [ $# -eq 0 ]; then
+ echo "$0 needs at least one argument."
+ exit 1
+fi
+
+get_partitions_by_id $@
+
diff --git a/builder/modules.d/slx-partitioner/scripts/slx_partitioner b/builder/modules.d/slx-partitioner/scripts/slx_partitioner
new file mode 100755
index 00000000..098bc469
--- /dev/null
+++ b/builder/modules.d/slx-partitioner/scripts/slx_partitioner
@@ -0,0 +1,391 @@
+#!/usr/bin/env bash
+#
+# Script to back given read-only device using the block device
+# specified by SLX_WRITABLE_DEVICE_IDENTIFIER in the SLX config.
+# If SLX_WRITABLE_DEVICE_PARTITION_TABLE is sepcified, it will
+# further create device mapper devices accordingly.
+#
+# Example partition config:
+# <type> <name> <size> <crypt>
+# thin-snapshot root 10G 1
+# thin-volume tmp 20G 0
+# linear data0 5-10G 1
+# linear data1 1-50G 1
+#
+# NOTE: Encrypting thin-snapshot will actually encrypt the
+# entire pool data device used for the pool.
+# TODO: Support external keys
+
+type -p emergency_shell || . /lib/dracut-lib.sh
+exec &> /run/openslx/dmsetup.log
+
+# read-only device to prepare for CoW
+[ -z "$1" ] && emergency_shell "Read-only device was not given!"
+declare -rg read_only_device="$1"
+declare -rg read_only_device_size="$(blockdev --getsz $1)"
+
+# global array variables storing the configuration of the partitions
+declare -ag linear snapshot thin_snapshot thin_volume
+parse_config() {
+ [ -z "$1" ] && return 1
+ while IFS= read -r line; do
+ [ -z "$line" ] && continue
+ read -r type name range crypt ignore <<< "$line"
+ type=${type//-/_} # to use the type as variable in eval
+ if ! [[ "$type" =~ \
+ ^(linear|snapshot|thin_snapshot|thin_volume)$ ]]; then
+ echo "$0: Ignoring invalid type: $line"
+ continue
+ fi
+ if [[ -z "$name" ]]; then
+ echo "$0: Ignoring nameless entry: $line"
+ continue
+ fi
+ unset unit min max
+ # ranges can be like: 40G, 40-80G, 10-20%
+ if ! [[ $range =~ ^([0-9]+-)*([0-9])+[GgMmKkBb%]$ ]]; then
+ echo "$0: Ignoring invalid range: $line"
+ continue
+ fi
+ # process ranges: convert percentages (of read_only_device!)
+ # to actual sizes (in sectors!) before saving them
+ local unit=${range: -1}
+ local min="$(cut -d'-' -f1 <<< "${range%?}" )"
+ local max="$(cut -d'-' -f2 <<< "${range%?}" )"
+ if [ "$min" -gt "$max" ]; then
+ echo "$0: Ignoring invalid range: $line"
+ continue
+ fi
+ # default for bytes
+ local -i potency=0
+ case "$unit" in
+ [%])
+ if [ "$max" -gt 100 ]; then
+ echo "Ignoring invalid percentages: $min/$max"
+ continue
+ fi
+ min=$(( $writable_device_size * $min / 100 ))
+ max=$(( $writable_device_size * $max / 100 ))
+ ;;
+ [Kk]) potency=1 ;;&
+ [Mm]) potency=2 ;;&
+ [Gg]) potency=3 ;;&
+ *)
+ # => 1024 ** potency for G, M, K, etc results in bytes
+ # => bytes / 512 = sectors
+ min=$(( $min * ( 1024 ** $potency) / 512 ))
+ max=$(( $max * ( 1024 ** $potency) / 512 ))
+ ;;
+ esac
+ # finally save it to the global array for this type
+ eval "${type}"'+=("'${name} ${crypt:-0} ${min} ${max}'")'
+ done <<< "$1"
+}
+
+# Helper to call 'dmsetup setup' without syncing with udev
+# and then actively create the devices with the mknodes command.
+# dmsetup_create_noudevsync <name> <table>
+dmsetup_create_noudevsync() {
+ (
+ set -o errexit
+ dmsetup create "$1" --noudevsync --table "$2"
+ dmsetup mknodes --noudevsync "$1"
+ )
+ local ret=$?
+ [ $ret -ne 0 ] && dmsetup remove --noudevsync "$1"
+ return $ret
+}
+
+# encrypt_device <dev_path> <encrypted_name> [<size>]
+encrypt_device() {
+ modprobe dm-crypt || return 1
+ [ -b "$1" ] || return 1
+ [ -n "$2" ] || return 1
+ [ -z "$3" ] && local size="$(blockdev --getsz $1)"
+ local key="$(head -c32 /dev/random | xxd -p | tr -d '\n')"
+ if ! dmsetup_create_noudevsync "$2" \
+ "0 ${3:-${size}} crypt aes-xts-plain64 $key 0 $1 0 1 allow_discards"; then
+ echo "$0: Failed to encrypt $1."
+ return 1
+ fi
+ return 0
+}
+# create_snapshot "<name> <persist>"
+create_snapshot() {
+ modprobe dm-snapshot || return 1
+ read -r name persist ignore <<< "$1"
+ if ! dmsetup_create_noudevsync "$name" \
+ "0 $read_only_device_size snapshot $read_only_device $writable_device ${persist:-N} 8"; then
+ echo "$0: Failed to create snapshot on '$writable_device' for '$read_only_device'."
+ return 1
+ fi
+ return 0
+}
+
+# Call this to fallback to a RAMdisk stored under /run/openslx
+# This will call terminate the whole script by calling finish_setup, if successful
+ramdisk_fallback() {
+ echo "$0: Falling back to regular dm-snapshot on a RAMdisk."
+ local file="$(mktemp -u -p /run/openslx dnbd_cow.XXX)"
+ local size="$SLX_RAMDISK_SIZE_IN_MB"
+ [ -z "$size" ] && size="$(awk '/MemTotal/ {printf("%d\n", $2 / 2 / 1024 )}' /proc/meminfo)"
+ dd of="$file" seek="$size" bs=1M count=0 &> /dev/null
+ writable_device="$(losetup --show --find "$file")"
+ cow_device_candidate="root"
+ while [ -b "/dev/mapper/$cow_device_candidate" ]; do
+ cow_device_candidate="root.$RANDOM"
+ done
+ if [ -z "$writable_device" ] || ! create_snapshot "$cow_device_candidate N"; then
+ emergency_shell "CRITICAL: failed to setup RAMdisk fallback."
+ exit 1
+ fi
+ finish_setup "/dev/mapper/$cow_device_candidate"
+}
+
+# finish_setup <device>
+# Set the resulting writable dnbd3 device in the global config and exit
+finish_setup() {
+ if [ -z "$1" ] || [ ! -b "$1" ]; then
+ emergency_shell "'$1' not a block device. Failed to setup CoW layer."
+ exit 1
+ fi
+ (
+ echo "# Generated by '$0'."
+ echo "SLX_DNBD3_DEVICE_COW=$1"
+ ) >> /etc/openslx
+ exit 0
+}
+
+###
+## MAIN
+###
+
+. /etc/openslx
+# This is the main variable driving this script
+declare -g writable_device=
+if [ -n "$SLX_WRITABLE_DEVICE_IDENTIFIER" ]; then
+ # only first one for now TODO create linear devices of all ID44s
+ writable_device="$(get_partitions_by_id "$SLX_WRITABLE_DEVICE_IDENTIFIER" | head -n 1)"
+fi
+if [ -z "$writable_device" ]; then
+ echo "$0: Could not find writable device with id '$SLX_WRITABLE_DEVICE_IDENTIFIER'."
+ ramdisk_fallback
+fi
+
+# NOTE: from here on out, every value related to size is in 512 bytes sectors!
+declare -g writable_device_size="$(blockdev --getsz $writable_device)"
+
+
+# If SLX_WRITABLE_DEVICE_PARTITION_TABLE is not set, just do
+# regular thin-snapshot for the CoW layer, else parse it.
+if [ -n "$SLX_WRITABLE_DEVICE_PARTITION_TABLE" ]; then
+ parse_config "$SLX_WRITABLE_DEVICE_PARTITION_TABLE"
+fi
+# Default to thin-snapshot, if none were configured
+if [ -z "$snapshot" ] && [ -z "$thin_snapshot" ]; then
+ thin_snapshot="thin-snapshot root 0 10G"
+fi
+
+# Sanity checks for weird configurations
+if [ "${#snapshot[@]}" -gt 1 ]; then
+ echo "Multiple snapshots specified, using first one: ${snapshot[0]}"
+ snapshot="${snapshot[0]}"
+fi
+if [ "${#thin_snapshot[@]}" -gt 1 ]; then
+ echo "Multiple thin-snapshots specified, using first one: ${thin_snapshot[0]}"
+ thin_snapshot="${thin_snapshot[0]}"
+fi
+if [ -n "$snapshot" ] && [ -n "$thin_snapshot" ]; then
+ echo "$0: Both snapshot and thin-snapshot specified, prefering thin-snapshot."
+ snapshot=
+fi
+
+###
+## LINEAR SLICES
+###
+
+# start allocating spaces to the configured devices
+declare -g writable_device_allocated=0
+# reserve the space for the snapshot (of either type)...
+read -r name crypt min max ignore <<< "${snapshot:-${thin_snapshot}}"
+
+declare -g scratch_device_size=0
+if (( $min <= $writable_device_size )); then
+ scratch_device_size=$max
+ while (( $scratch_device_size >= 0 )) && (( $scratch_device_size > $writable_device_size )); do
+ (( scratch_device_size -= 2097152 )) # 1G steps => 2097152 sectors
+ done
+ (( $scratch_device_size < $min )) && scratch_device_size="$min"
+else
+ # minimum snapshot size is bigger than physical device size
+ echo "$0: Minimum snapshot size is too big for the scratch partition."
+ echo "$0: You probably need to use a more conservative value."
+ echo "$0: Using this client maximum scratch space ($writable_device_size sectors)."
+ scratch_device_size="$writable_device_size"
+fi
+
+# ... and slice it from the start of the writable device (for performance).
+if ! dmsetup_create_noudevsync "scratch" \
+ "0 $scratch_device_size linear $writable_device $writable_device_allocated"; then
+ echo "$0: Failed to create scratch space for the CoW layer."
+ ramdisk_fallback
+fi
+scratch_device="/dev/mapper/scratch"
+# encrypt the scratch device, if configured
+if [ "$crypt" -ne 0 ] && encrypt_device \
+ "/dev/mapper/scratch" "scratch-crypt" "$scratch_device_size"; then
+ scratch_device="/dev/mapper/scratch-crypt"
+fi
+
+writable_device_allocated="$scratch_device_size"
+
+# first setup linear slices of the writable device
+for i in ${!linear[@]}; do
+ [ -z "${linear[$i]}" ] && continue
+ read -r name crypt min max ignore <<< "${linear[$i]}"
+ free_space=$(( $writable_device_size - $writable_device_allocated ))
+ if [ "$min" -gt "$free_space" ]; then
+ echo "$0: Not enough space left for linear devices: ${linear[$i]}"
+ break
+ fi
+ # allocate its max if it fits within the free space, otherwise use the space left.
+ to_allocate="$max"
+ [ "$to_allocate" -gt "$free_space" ] && to_allocate="$free_space"
+
+ if ! dmsetup_create_noudevsync "$name" "0 $to_allocate linear $writable_device $writable_device_allocated"; then
+ echo "$0: Failed to create linear device: ${linear[$i]}"
+ continue
+ fi
+ if [ "$crypt" -ne 0 ] && \
+ ! encrypt_device "/dev/mapper/$name" "${name}-crypt" "$to_allocate"; then
+ echo "$0: Failed to encrypt '$name'."
+ fi
+ writable_device_allocated=$(( $to_allocate + $writable_device_allocated ))
+done
+
+# we are done with the physical device, use the scratch space from now on
+writable_device="$scratch_device"
+writable_device_size="$scratch_device_size"
+
+###
+## THIN-PROVISIONING
+###
+declare -rg pool_metadata_dev="/dev/mapper/pool-metadata"
+declare -rg pool_data_dev="/dev/mapper/pool-data"
+declare -rg pool_dev="/dev/mapper/pool"
+create_pool() {
+ # create external snapshot for read-only device
+ # create remaining thin volumes
+ modprobe dm-thin-pool || return 1
+ # create temporary metadata device
+ data_block_size=128
+ # calculate number of sectors needed and check boundaries:
+ metadata_dev_size="$(( 48 * $writable_device_size / $data_block_size / 512 ))"
+ # Min 2MB -> 4096 sectors, max 16GB -> 33554432 sectors
+ [ "$metadata_dev_size" -lt 4096 ] && metadata_dev_size="4096"
+ # TODO handle the exotic case of a too large metadata device to fit within RAM.
+ [ "$metadata_dev_size" -gt 33554432 ] && metadata_dev_size="33554432"
+ # TODO handle persistent metadata device on disk
+ # create RAMdisk in /run for metadata device
+ metadata_dev="$(mktemp -p /run/openslx .pool-metadata.XXX)"
+ dd of="$metadata_dev" bs=512 seek="$metadata_dev_size" &> /dev/null
+ metadata_dev="$(losetup --show --find $metadata_dev)"
+ if ! dmsetup_create_noudevsync "${pool_metadata_dev##*/}" \
+ "0 $metadata_dev_size linear $metadata_dev 0"; then
+ echo "$0: Failed to create pool metadata device on '$writable_device'."
+ return 1
+ fi
+ writable_device_size=$(( $writable_device_size - $metadata_dev_size ))
+ if ! dmsetup_create_noudevsync "${pool_data_dev##*/}" \
+ "0 $writable_device_size linear $writable_device $metadata_dev_size"; then
+ echo "$0: Failed to create pool data device on '$writable_device'."
+ return 1
+ fi
+ low_water_mark=32
+ if ! dmsetup_create_noudevsync "${pool_dev##*/}" \
+ "0 $writable_device_size thin-pool $pool_metadata_dev $pool_data_dev $data_block_size $low_water_mark"; then
+ echo "$0: Failed to create thin-pool device on '$writable_device'."
+ return 1
+ fi
+ return 0
+}
+
+# create_volume "<name> <id> <size> <backing_dev>"
+create_volume() {
+ if [ -z "$pool_dev" -o ! -b "$pool_dev" ]; then
+ echo "$0: Global pool device not set or present."
+ return 1
+ fi
+ if [ $# -ne 1 -o -z "$1" ]; then
+ echo "$0: create_volume requires one non-empty argument."
+ return 1
+ fi
+ local name id size backing_dev ignore
+ read -r name id size backing_dev ignore <<< "$1"
+
+ if ! dmsetup message "$pool_dev" 0 "create_thin $id"; then
+ echo "$0: Failed to create thin volume with id '$id' in pool '$pool_dev'."
+ echo "$0: It might already exists, trying anyway..."
+ fi
+ if ! dmsetup_create_noudevsync "$name" "0 $size thin $pool_dev $id $backing_dev"; then
+ echo "$0: Failed to create external snapshot named '$name':"
+ echo " Size: $size"
+ echo " Backing device: $backing_dev"
+ echo " Thin volume id: $id"
+ return 1
+ fi
+ return 0
+}
+if [ -n "$thin_snapshot" ] || [ -n "$thin_volume" ]; then
+ if ! create_pool ; then
+ echo "Failed to create thin pool. Will ignore:"
+ echo -e "\tThin snapshot: $(declare -p thin_snapshot)"
+ echo -e "\tThin volumes: $(declare -p thin_volume)"
+ ramdisk_fallback
+ fi
+ # the order in which pool devices are created does not matter
+ # so start with thin volumes starting with id 2 and end with
+ # the thin-snapshot with id 1
+ volume_id=2
+ # go over thin-volumes
+ for i in ${!thin_volume[@]}; do
+ [ -z "${thin_volume[$i]}" ] && continue
+ read -r name crypt min max ignore <<< "${thin_volume[$i]}"
+ # thin-volume can be safely created with max size,
+ # since they are overprovisioned anyway.
+ if ! create_volume "$name $(( volume_id++ )) $max"; then
+ echo "Failed to create thin volume '$name'."
+ fi
+ if [ "$crypt" -ne 0 ] && ! encrypt_device \
+ "/dev/mapper/$name" "$name-crypt" "$max"; then
+ echo "Failed to encrypt thin volume '$name'."
+ fi
+ done
+
+ if [ -n "$thin_snapshot" ]; then
+ # create thin-snapshot, use first one
+ read -r name crypt min max ignore <<< "$thin_snapshot"
+ # min/max was used for the pool data device, ignore it here!
+ if ! create_volume "$name 1 $read_only_device_size $read_only_device"; then
+ echo "Failed to create external snapshot for '$read_only_device'."
+ ramdisk_fallback
+ fi
+ finish_setup "/dev/mapper/$name"
+ fi
+fi
+
+###
+## SNAPSHOT (OLD FUNCTIONALITY)
+###
+if [ -n "$snapshot" ]; then
+ read -r name crypt persist ignore <<< "$snapshot"
+ if ! create_snapshot "$name $persist"; then
+ echo "Failed to create regular snapshot for '$read_only_device' on '$writable_device'."
+ ramdisk_fallback
+ fi
+ finish_setup "/dev/mapper/$name"
+fi
+
+# ultimate fallback
+ramdisk_fallback
+exit 1