summaryrefslogblamecommitdiffstats
path: root/modules/dev.inc
blob: 88c2cc98f635be8880fd544df8fff9dc6179b011 (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 rw
	# check if only interested in writable devices
	rw=
	if [ "$1" = "--rw" ]; then
		shift
		rw=1
	fi
	# 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* -maxdepth 0 -type b); do
		[ -n "$rw" ] && [ "$( cat "/sys/class/block/${dev##*/}/ro" )" = "1" ] && continue
		dev_get_type "$dev" || continue
		is_on "$SLX_DEBUG" && echo "$dev is $number - MBR=$mbrid, UUID=$uuid, label=$label" >&2
		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 devlist devn pstart
	dev=
	mbrid=
	uuid=
	label=
	number=
	[ -b "$1" ] || return 1
	part="$( readlink -f "$1" )"
	partn="${part##*/}"
	devlist="$( printf "%s" "$partn" | sed -r 's/[0-9]+$//' )"
	[ "$partn" = "$devlist" ] && return 1
	for devn in "${devlist}" "${devlist%p}"; do
		if [ -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" )"
	number="$( cat "/sys/block/${devn}/${partn}/partition" )"
	local gpt=
	if [ "$( dd if="$dev" bs=1 count=8 skip=512 )" = "EFI PART" ]; then
		gpt=512
	elif [ "$( dd if="$dev" bs=1 count=8 skip=4096 )" = "EFI PART" ]; then
		gpt=4096
	fi 2> /dev/null
	is_on "$SLX_DEBUG" && echo "Checking $partn (on $dev) GPT='$gpt', label='$label'" >&2
	# 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 on $dev, 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/0f, 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" )"
				if [ "$id" = "05" ] || [ "$id" = "0f" ]; then
					id="found"
					break
				fi
			done
			if [ "$id" != "found" ]; then
				echo "No matching extended primary partition for $number found on $dev" >&2
			else
				is_on "$SLX_DEBUG" && 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 at $no of $number on $dev" >&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 on $dev, 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 )"
					is_on "$SLX_DEBUG" && echo "Extended id $log_id, next $next_id, $next_start" >&2
					[ "$next_id" = "0f" ] && next_id="05"
					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 readoff num byte
		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. 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 \
			| __bin2hex | sed -r 's/^(..)(..)(..)(..)(..)(..)(..)(..)(....)/\4\3\2\1-\6\5-\8\7-\9-/' )"
		if [ -z "$label" ]; then
			readoff="$(( current + entry_size * (number - 1) + 56 ))"
			num=0
			while [ "$num" -lt 36 ]; do
				byte="$( __read_le "$dev" "$readoff" "1" )"
				if [ "$byte" -ge 32 ] && [ "$byte" -le 126 ]; then
					byte="$( dd if="$dev" bs=1 count="1" skip="$readoff" 2> /dev/null )"
					label="$label$byte"
				fi
				readoff=$(( readoff + 2 ))
				num=$(( num + 1 ))
			done
		fi
		return 0
	fi
	# Unknown
	return 1
}

# Get version of given swap partition. $1 will be checked for the
# swap signature, and if found, the version number will be returned.
# old-style swap will be reported as version 1,
# new-style as 1 + <version_field>.
dev_swap_version() {
	local sig version
	sig="$( dd if="$1" bs=1 count=10 skip=4086 2> /dev/null )"
	if [ "$sig" = "SWAP-SPACE" ]; then
		echo 1
		return 0
	elif [ "$sig" = "SWAPSPACE2" ]; then
		version="$( __read_le "$1" 1024 4 )"
		# I think there is only '1' currently, but try to be clever...
		if [ "$version" -gt 0 ] && [ "$version" -lt 8 ]; then
			echo $(( 1 + version ))
			return 0
		fi
	fi
	return 1
}

# stdin = binary data, stdout = raw, unformatted hex
# __hex2bin()

# stdin = binary data, stdout = raw, unformatted hex
# $1 = number of bytes to read and reverse (1, 2, 4, 8)
# __hex2bin_le()

# 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 | __bin2hex
}

# 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 | __bin2hex
}

# 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 | __bin2hex_le "$3" )"
	echo $(( 0x$v ))
}

__init() {
	local t
	# init __hex2bin
	t="7a32337465737431323374657374"
	if [ "$( printf z23test123test | xxd -p | tr -d '\n ' )" = "$t" ]; then
		#echo "Using xxd" >&2
		__bin2hex() { xxd -p | tr -d '\n '; }
	elif [ "$( printf z23test123test | od -t x1 -An | tr -d '\n ' )" = "$t" ]; then
		#echo "Using od" >&2
		__bin2hex() { od -t x1 -An | tr -d '\n '; }
	elif [ "$( printf z23test123test | hexdump -ve '/1 "%02x"' )" = "$t" ]; then
		#echo "Using hexdump" >&2
		__bin2hex() { hexdump -ve '/1 "%02x"'; }
	else
		echo "No suitable tool for converting binary to hex"
		exit 1
	fi >&2 2> /dev/null
	# init __hex2bin_le
	t="383736353433327a"
	if [ "$( printf z2345678 | xxd -e -g 8 | cut -d' ' -f2 )" = "$t" ]; then
		#echo "Using xxd" >&2
		__bin2hex_le() { xxd -e -g "$1" | cut -d' ' -f2; }
	elif [ "$( printf z2345678 | od -t x8 -An | tr -d '\n ' )" = "$t" ]; then
		#echo "Using od" >&2
		__bin2hex_le() { od -t "x$1" -An | tr -d '\n '; }
	elif [ "$( printf z2345678 | hexdump -ve '/4 "%02x"' \
			| sed 's/\(........\)\(........\)/\2\1/' )" = "$t" ]; then
		#echo "Using hexdump" >&2
		__bin2hex_le() {
			if [ "$1" -lt 8 ]; then
				hexdump -ve "/$1"' "%02x"'
			else
				hexdump -ve '/4 "%02x"' | sed 's/\(........\)\(........\)/\2\1/'
			fi
		}
	elif [ "$( printf z2345678 | xxd -g 8 | cut -d' ' -f2 | awk '{ for (i=length-1;i>0;i-=2)x=x substr($0,i,2);}END{print x}' )" = "$t" ]; then
		__bin2hex_le() { xxd -g "$1" | cut -d' ' -f2 | awk '{ for (i=length-1;i>0;i-=2)x=x substr($0,i,2);}END{print x}'; }
	else
		echo "No suitable tool for converting binary data to little endian hex"
		exit 1
	fi >&2 2> /dev/null
	unset -f __init
}

__init