Add partial support for NetBSD and OpenBSD
[snf-image] / snf-image-helper / tasks / 10FixPartitionTable.in
index 5b499c8..6a37d73 100644 (file)
 ### BEGIN TASK INFO
 # Provides:            FixPartitionTable
 # RunBefore:           FilesystemResizeUnmounted
-# Short-Description:   Resize filesystem to use all the available space
+# Short-Description:   Enlarge last partition to use all the available space
 ### END TASK INFO
 
 set -e
 . "@commondir@/common.sh"
 
+trap task_cleanup EXIT
+report_task_start
+# Check if the task should be prevented from running.
+check_if_excluded
+
 if [ ! -b "$SNF_IMAGE_DEV" ]; then
     log_error "Device file:\`${SNF_IMAGE_DEV}' is not a block device"
 fi
 
-if [ $(get_partition_count "$SNF_IMAGE_DEV") -eq 0 ]; then
+if [[ "$SNF_IMAGE_PROPERTY_OSFAMILY" =~ (net)|(open)bsd ]]; then
+    os=${SNF_IMAGE_PROPERTY_OSFAMILY^[bsd]}
+    warn "Partition resizing currently not supported for ${os^?}"
+    exit 0
+fi
+
+table=$(get_partition_table "$SNF_IMAGE_DEV")
+
+if [ $(get_partition_count "$table") -eq 0 ]; then
     log_error "Device: \`${SNF_IMAGE_DEV}' does not contain any partition"
 fi
 
-retval=$(get_last_partition "$SNF_IMAGE_DEV")
+table_type=$(get_partition_table_type "$table")
 
-id=$(echo $retval | cut -d: -f1)
-pstart=$(echo $retval | cut -d: -f2)
-pend=$(echo $retval | cut -d: -f3)
-ptype=$(echo $retval | cut -d: -f5)
+if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" = "freebsd" -a "$table_type" != "gpt" ]; then
+    log_error "The image contains a(n) $table_type partition table. " \
+        "For FreeBSD images only GUID Partition Tables are supported."
+fi
 
-if [ $id -gt 4 ]; then
-    log_error "We don't support logical volumes"
+if [ "$table_type" == "gpt" ]; then
+    "$SGDISK" --move-second-header "$SNF_IMAGE_DEV"
+elif [ "$table_type" != "msdos" ]; then
+    log_error "Device: \'${SNF_IMAGE_DEV}' contains unsupported partition " \
+        "table type: \`$table_type'. Only msdos & gpt partitions are supported."
 fi
 
-if [ x"$ptype" = "x" ]; then
-    # Don't know how to handle this
-    warn "Last partition with id: \`$id' is empty or has unknown filesystem"
-    warn "I won't resize the partition"
-    exit 0
+last_part=$(get_last_partition "$table")
+last_part_id=$(cut -d':' -f1 <<< "$last_part")
+
+# Check if swap is defined...
+if [ -n "$SNF_IMAGE_PROPERTY_SWAP" ]; then
+    if [[ "$SNF_IMAGE_PROPERTY_SWAP" =~ ^([0-9]+):([0-9]+)$ ]]; then
+        swap_num=${BASH_REMATCH[1]}
+        swap_size=${BASH_REMATCH[2]}
+        swap_unit="MB"
+    else
+        log_error "SWAP property \`$SNF_IMAGE_PROPERTY_SWAP' is not valid"
+    fi
 fi
 
-new_pend=$(get_last_free_sector "$SNF_IMAGE_DEV")
+if [ -z "$swap_num" ]; then
+    swap_num=0
+fi
 
-if [ -z "$new_pend" ] ; then
-    # Nothing to do
-    exit 0
+# Most partition setups leave 2048s in the end. For GPT partitions you need at
+# least 34s for the secondary GPT header.
+new_end="-2049"
+
+if [ $swap_num -ne 0 ]; then
+    free=$(get_last_free_sector "$SNF_IMAGE_DEV" "$swap_unit")
+    free_size=$(cut -d: -f4 <<< "$free")
+    free_size_val=${free_size/$swap_unit/}
+    if [ $free_size_val -le $swap_size ]; then
+        log_error "Not enough space for swap partition"
+    fi
+
+    swap_end="$new_end"
+
+    swap_start=$((new_end - (swap_size * 2048) + 1)) # in sectors
+    new_end=$((swap_start - 1))
 fi
 
-# Extend the partition
+extended=""
+
+if [ "$table_type" != "msdos" ]; then
+    # For gpt partitions, get the partition GUID code as partition type
+    last_part_type="$($SGDISK -i "$last_part_id" "$SNF_IMAGE_DEV" | grep "^Partition GUID code:" | cut -d"(" -f1 | cut -d: -f2 | xargs echo)"
+elif [ $last_part_id -gt 4 ]; then
+    last_part_type="logical"
+    extended=$(get_extended_partition "$table")
+    last_primary=$(get_last_primary_partition "$table")
+
+    ext_id=$(cut -d':' -f1 <<< "$extended")
+    last_prim_id=$(cut -d':' -f1 <<< "$last_primary")
+
+    if [ "$ext_id" != "$last_prim_id" ]; then
+        # Mark last primary as the last partition
+        last_part="$extended"
+        last_part_id="$ext_id"
+        last_part_type="primary"
+    else
+        # Enlarge the extended partition
+        if [ $swap_num -ge 5 ]; then
+            # This is needed because logical partitions need to have at least
+            # 1 sector gap between them. We make the gap 2048 sectors to
+            # properly align them.
+            new_end=$((new_end - 2048))
+            enlarge_partition "$SNF_IMAGE_DEV" "$extended" "extended" "${swap_end}s"
+        else
+            enlarge_partition "$SNF_IMAGE_DEV" "$extended" "extended" "${new_end}s"
+        fi
+    fi
+elif [ $(is_extended_partition "$SNF_IMAGE_DEV" "$last_part_id") = "yes" ]; then
+    last_part_type="extended"
+    extended="$last_part"
+    if [ $swap_num -ge 5]; then
+        new_end=$swap_end
+    fi
+else
+    last_part_type="primary"
+    if [ $swap_num -ge 5 ]; then
+        # This is needed because the swap partition should be added inside a
+        # new extended partition. In order to align the swap partition, we
+        # need to create some extra space between the (aligned) primary and
+        # the swap.
+        new_end=$((new_end - 2048))
+    fi
+fi
 
-$PARTED -s -m "$SNF_IMAGE_DEV" rm "$id"
-$PARTED -s -m "$SNF_IMAGE_DEV" mkpart primary "$ptype" "$pstart" "$new_pend"
+enlarge_partition "$SNF_IMAGE_DEV" "$last_part" "$last_part_type" "${new_end}s"
+
+if [ $swap_num -gt 0 ]; then
+    swap_part="$swap_num:${swap_start}s:${swap_end}s:0:linux-swap(v1)::;"
+    if [ "$table_type" != "msdos" ]; then
+        swap_ptype="swap" # in gpt this is used as a partition name
+    elif [ $swap_num -ge 5 ]; then
+        if [ -z "$extended" ]; then
+            extended="0:$((swap_start - 2))s:${swap_end}s:0:::;"
+            create_partition "$SNF_IMAGE_DEV" "$extended" "extended"
+        fi
+        swap_ptype="logical"
+    else
+        swap_ptype="primary"
+    fi
+    create_partition "$SNF_IMAGE_DEV" "$swap_part" "$swap_ptype"
+fi
 
 # Inform the kernel about the changes
 partprobe "$SNF_IMAGE_DEV"