#!/bin/ash # Get all partitions with given id (list of /dev/sdXX) # Works for MBR/DOS by looking at the type (1 byte) # and for GPT by looking for the label 'OpenSLX-ID$1' # in case an id was given, or with the given UUID, # or with the given name. # The output will be a list of matching devices, # sorted from largest to smallest. dev_find_partitions() { local ID dev target # target for the scan, defaults to /dev to check everything if [ "${1:0:1}" = "/" ] && [ -b "$1" ]; then target="$1" shift elif [ "${1:0:1}" = "/" ] && [ -d "$1" ]; then target="$1/" shift else target="/dev/" fi local want_label="never match this#" local want_type="never match this#" local want_uuid="never match this#" while [ "$#" -gt 0 ]; do ID="$1" shift [ -z "$ID" ] && continue if regex_imatch "$ID" "^[0-9a-f]$"; then want_type="$want_type|0$ID" want_label="$want_label|OpenSLX-ID0$ID" elif regex_imatch "$ID" "^[0-9a-f]{2}$"; then want_type="$want_type|$ID" want_label="$want_label|OpenSLX-ID$ID" elif regex_imatch "$ID" "^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$"; then want_uuid="$want_uuid|$ID" elif [ "${#ID}" -gt 3 ]; then # Safety measure: Want label length >= 4 chars want_label="$want_label|$( regex_escape "$ID" )" fi done local label number mbrid uuid for dev in $(find $target* -type b); do dev_get_type "$dev" || continue if regex_imatch "$mbrid" "^($want_type)$" || regex_imatch "$uuid" "^($want_uuid)$" \ || regex_match "$label" "^($want_label)$"; then printf "%s\n" "$(blockdev --getsize64 "$dev") $dev" fi done | sort -n -k1 -r | cut -d' ' -f2 } # Pass partition block device. If it could be identified successfully, # fills the variables number, mbrid, uuid, label, depending on MBR/GPT. # Otherwise, return code is != 0 and contents are undefined. # This only makes sense if called within this script, or if slx-tools # was sourced, otherwise the variables will be inaccessible. dev_get_type() { local part dev partn devn pstart dev= mbrid= uuid= label= number= [ -b "$1" ] || return 1 part="$( readlink -f "$1" )" partn="${part##*/}" for devn in "${partn%p?}" "${partn%?}"; do if [ "$dev" != "$part" ] && [ -f "/sys/block/${devn}/${partn}/uevent" ]; then dev="/dev/${devn}" break fi done [ -z "$dev" ] && return 1 pstart="$( cat "/sys/block/${devn}/${partn}/start" )" # For validation label="$( grep -Po '(?<=^PARTNAME=).*$' "/sys/block/${devn}/${partn}/uevent" )" number="$( grep -Po '(?<=^PARTN=).*$' "/sys/block/${devn}/${partn}/uevent" )" local gpt= if [ "$( dd if="$dev" bs=1 count=8 skip=512 2> /dev/null )" = "EFI PART" ]; then gpt=512 elif [ "$( dd if="$dev" bs=1 count=8 skip=4096 2> /dev/null )" = "EFI PART" ]; then gpt=4096 fi # Check if mbr signatur is there, no GPT label was in uevent, and no protective MBR local t1="$( __read_mbrid "$dev" 0 1 )" if [ -z "$label" ] && [ "$number" -lt 1000 ] && [ "$number" -gt 0 ] \ && [ "$t1" != "ee" ] && [ "$( __read_mbrsig "$dev" 0 )" = "55aa" ]; then # Then we consider parsing this as MBR if [ "$number" -le 4 ]; then # 1-4 are primary partitions # Validate start LBA matches sysfs log_start="$( __read_mbrstart "$dev" 0 "$number" )" if [ "$pstart" != "$log_start" ]; then echo "Found partition $number, but start mismatch (want: $pstart found: $log_start)" >&2 else mbrid="$( __read_mbrid "$dev" 0 "$number" )" return 0 fi else # part number 5+ -- must be logical volume in extended partition # Scan for Primary type 05, scan linked list of fake MBRs from there local no id ex_start log_id log_start next_id next_start current for no in 1 2 3 4; do id="$( __read_mbrid "$dev" 0 "$no" )" [ "$id" = "05" ] && break done if [ "$id" != "05" ]; then echo "No matching extended primary partition found" >&2 else echo "Primary $no is extended partition" >&2 ex_start="$( __read_mbrstart "$dev" 0 "$no" )" current="$ex_start" no=5 # Count through logical partitions. These start at 5. while [ "$no" -le "$number" ]; do if [ "$( __read_mbrsig "$dev" "$current" )" != "55aa" ]; then echo "Unexpected end of logical partition list" >&2 break fi log_id="$( __read_mbrid "$dev" "$current" 1 )" if [ "$no" -eq "$number" ]; then log_start="$( __read_mbrstart "$dev" "$current" 1 )" # logical partition's start is relative to its fake MBR log_start="$(( log_start + current ))" if [ "$pstart" != "$log_start" ]; then echo "Found partition $no, but start mismatch (want: $pstart found: $log_start)" >&2 break fi mbrid="$log_id" return 0 fi next_id="$( __read_mbrid "$dev" "$current" 2 )" # next logical partition's fake MBR is relative to the extended partition's start next_start="$( __read_mbrstart "$dev" "$current" 2 )" echo "Extended id $log_id, next $next_id, $next_start" >&2 if [ "$next_id" = "05" ] && [ "$next_start" -ge 512 ]; then current="$(( next_start + ex_start ))" no="$(( no + 1 ))" # Increase counter for next logical partition else break fi done fi fi fi if [ -n "$gpt" ]; then # GPT local table_start current entries no entry_size log_start table_start="$( __read_le "$dev" "$(( gpt + 72 ))" 8 )" entries="$( __read_le "$dev" "$(( gpt + 80 ))" 4 )" entry_size="$( __read_le "$dev" "$(( gpt + 84 ))" 4 )" current="$(( table_start * gpt ))" if ! [ "$current" -ge "$(( gpt * 2 ))" ] || ! [ "$entries" -gt 0 ] \ || [ "$entries" -lt "$number" ] || ! [ "$entry_size" -le 4096 ]; then echo "Bad GPT table. Start: $current, Partition count: $entries. Want: $number" >&2 return 1 fi log_start="$( __read_le "$dev" "$(( current + entry_size * (number - 1) + 32 ))" 8 )" if [ "$log_start" != "$pstart" ]; then echo "Found partition $number, but start mismatch (want: $pstart found: $log_start)" >&2 return 1 fi # Convert raw hex stream to proper string representation. First 3 groups are little endian. uuid="$( dd if="$dev" bs=1 count=16 skip="$(( current + entry_size * (number - 1) ))" 2> /dev/null \ | xxd -p | sed -r 's/^(..)(..)(..)(..)(..)(..)(..)(..)(....)/\4\3\2\1-\6\5-\8\7-\9-/' )" return 0 fi # Unknown return 1 } # Get MBR type of partition (1 byte) as hex # Pass "$device" "$lba_offset" "$partition_number" __read_mbrid() { dd if="$1" bs=1 skip=$(( 512 * $2 + 446 + ($3 - 1) * 16 + 4 )) count=1 2> /dev/null | xxd -p } # Get LBA start address of MBR partition # Pass "$device" "$lba_offset" "$partition_number" __read_mbrstart() { __read_le "$1" "$(( 512 * $2 + 446 + ($3 - 1) * 16 + 8 ))" 4 } # Read the MBR signature in the given sector # Pass "$device" "$lba_offset" __read_mbrsig() { dd if="$1" bs=1 skip=$(( 512 * $2 + 510 )) count=2 2> /dev/null | xxd -p } # Read a little endian value at given byte offset # Pass "$source_path" "$byte_offset" "$size" __read_le() { local v="$( dd if="$1" bs=1 count="$3" skip="$2" 2> /dev/null | xxd -e -g "$3" | cut -d' ' -f2 )" echo $(( 0x$v )) }