summaryrefslogblamecommitdiffstats
path: root/modules/dev.inc
blob: 6080af67f1aeac2adca6ab176b12bf551b5cd49e (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                      
                           
                                                                   
                                                    

                           
                                                      
                            
                     


                              


                                            



                                        
                                                        




                                                               
                                                                                           


                                                                                            

                  
                                     
                                               




                                                                                                    


                                             














































































































                                                                                                                                    
#!/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"
		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
}

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
	if [ -z "$label" ] && [ "$number" -lt 1000 ]  && [ "$number" -gt 0 ] \
			&& [ "$( __read_mbrsig "$dev" 0 )" = "55aa" ]; then
		# Get MBR ID
		if [ "$number" -le 4 ]; then
			# Primary
			mbrid="$( __read_mbrid "$dev" 0 "$number" )"
		else
			# 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" )"
				echo "Scanning. Primary $no is type $id" >&2
				[ "$id" = "05" ] && break
			done
			if [ "$id" != "05" ]; then
				echo "No matching extended primary partition found" >&2
				return 1
			fi
			ex_start="$( __read_mbrstart "$dev" 0 "$no" )"
			current="$ex_start"
			no=5 # Count through logical partitions
			while [ "$no" != 0 ]; do
				[ "$( __read_mbrsig "$dev" "$current" )" = "55aa" ] || break
				log_id="$( __read_mbrid "$dev" "$current" 1 )"
				if [ "$no" = "$number" ]; then
					log_start="$( __read_mbrstart "$dev" "$current" 1 )"
					log_start="$(( log_start + current ))"
					if [ "$pstart" != "$log_start" ]; then
						echo "Found partition $no, but start mismatch (want: $pstart found: $log_start)" >&2
						return 1
					fi
					mbrid="$log_id"
					break
				fi
				next_id="$( __read_mbrid "$dev" "$current" 2 )"
				next_start="$( __read_mbrstart "$dev" "$current" 2 )"
				echo "Extended id $log_id, next $next_id, $next_start" >&2
				if [ "$next_id" = "05" ] && [ "$next_start" -gt 0 ]; then
					current="$(( next_start + ex_start ))"
					no="$(( no + 1 ))"
				else
					no=0
				fi
			done
		fi
		return 0
	elif [ -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
		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
}

__read_mbrid() {
	dd if="$1" bs=1 skip=$(( 512 * $2 + 446 + ($3 - 1) * 16 + 4 )) count=1 2> /dev/null | xxd -p
}

__read_mbrstart() {
	__read_le "$1" "$(( 512 * $2 + 446 + ($3 - 1) * 16 + 8 ))" 4
}

__read_mbrsig() {
	dd if="$1" bs=1 skip=$(( 512 * $2 + 510 )) count=2 2> /dev/null | xxd -p
}

__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 ))
}