[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#913431: Debian Installer Bullseye RC 2 release



Le 23/03/2023 à 12:01, Vincent Danjean a écrit :
Le 23/03/2023 à 11:41, Vincent Danjean a écrit :
   Hi,

Le 23/03/2023 à 10:06, Emanuele Rocca a écrit :
There is (now) a function in partman-base called valid_human [1] which
checks if the partition size specified by the user is valid. Probably
when you first wrote the patch this wasn't the case.

That function needs to be modified as well to accept GiB and friends.

  Here is a new patch. There are several improvments:
- when using power-of-ten units, all decimals are taken into account
  (not just the 4 first ones)
- human2longint was concatenating the 5 first arguments. Now it
  concatenates all its arguments (not sure about this part, this is
  a two-lines modification that can easily be reverted
- bigger decimal number are handled (in fact, there are no size limits)
- power-of-two units are accepted and handled
- Peta and Exa units are added

  This patch is using both 'expr' (from busybox) and bash arithmetic
that both are limited to 2^64 (signed) integers (even on 32-bits
platforms, I checked on i386). Both were already used previously.

  To be relatively sure of my code, I wrote a series of test.
It is in the tar.gz file. Just unpack it and run 'make' in it.
For each check, it compares:
- the new implementation and a (local) implementation based on
  'bc' that uses no-size-limit integer operations
  It prints 'OK' if both are the same and else NACK
  (in practice, there are no NACKs)
- if previously OK, it compares to the old implementation.
  - 'OK:' means that both (old and new) implementation returns the
    same thing
  - 'OK+': means that they differ.
    If there is no 'previously' at the end of the line,
      it means that the old implementation was not handling this case
      (all power-of-two units and the new Peta and Exa prefix)
    else
      it means that the old implementation was returning another value
      (seen just after the "previouly: " text)
      Here, you find better precision (more decimals taken into account)
      or wrong results (negative by overflow, etc.)
All these tests are run within the busybox shell and with the busybox
tools added in front of the PATH.

However, I did not rebuild all the installer packages to generate a
new installer and test it in real conditions.

  I hope the patch can be reviewed and applied for the next release.

  Regards
    Vincent

diff --git a/lib/base.sh b/lib/base.sh
index d38e101e..492732bf 100644
--- a/lib/base.sh
+++ b/lib/base.sh
@@ -313,9 +313,115 @@ longint2human () {
 	printf "%i%s%i %s\n" $int $deci $frac $suffix
 }
 
+longmult() {
+	local a="$1" # no size limit
+	local b="$2" # <= 2^30
+
+	local carry=0
+	local endres=""
+	local partres
+	local enda
+
+	while [ "${#a}" -gt 6 ]; do
+		enda="${a:$((${#a} - 6))}"
+		a="${a%$enda}"
+		partres="$(expr "$enda" '*' "$b" + "$carry")"
+		if [ "${#partres}" -gt 6 ]; then
+			carry="${partres:0:$((${#partres} - 6))}"
+			endres="${partres#$carry}$endres"
+		else
+			partres="000000$partres"
+			carry=0
+			endres="${partres:$((${#partres} - 6))}$endres"
+		fi
+	done
+	partres="$(expr "$a" '*' "$b" + "$carry")"
+	echo "$partres$endres"
+}
+
+longadd() {
+	local a="$1" # no size limit
+	local b="$2" # <= 2^60
+
+	local carry="$b"
+	local endres=""
+	local partres
+	local enda
+
+	while [ "${#a}" -gt 15 ]; do
+		enda="${a:$((${#a} - 15))}"
+		a="${a%$enda}"
+		partres="$(expr "$enda" + "$carry")"
+		if [ "${#partres}" -gt 15 ]; then
+			carry="${partres:0:$((${#partres} - 15))}"
+			endres="${partres#$carry}$endres"
+		else
+			partres="000000000000000$partres"
+			echo "${a}${partres:$((${#partres} - 15))}$endres"
+			return
+		fi
+	done 
+	partres="$(expr "$a" + "$carry")"
+	echo "$partres$endres"
+}
+
+human2longint_binary_unit() {
+	local int="$1"
+	local frac="$2"
+	local powbase="$3" # 1 <= powbase <= 6
+	# must return "$int.$frac * 1024^$powbase"
+	# contraints :
+	# - no floating point operation
+	# - no computed values above 2^63-1
+	# - expr has no exponentiation
+	# - bash arithmetics consider that 0-leading numbers are octal
+
+	# max of useful decimals when converting: powbase*10
+	# next ones, when multipled by 1024^powbase, would be <1
+	# so no need to take into account to many decimals
+	frac="${frac:0:$((powbase * 10 ))}"
+	local longint="${int}${frac}"
+	longint="${longint:$(expr "$longint" : '0*' || true)}" # remove leading 0
+	[ "$longint" ] || {
+		echo 0
+		return
+	}
+	while [ "$powbase" -gt 3 ]; do
+		longint="$(longmult "$longint" "$((1024**3))")"
+		powbase=$(( $powbase - 3 ))
+	done
+	longint="$(longmult "$longint" "$((1024**$powbase))")"
+
+	if [ -z "$frac" ]; then
+		# no fractional part, just return the result
+		echo "$longint"
+		return
+	fi
+	# non-null fractional part.
+	# longint must be divided by 10^length(frac)
+	local posfrac="$(( ${#longint} - ${#frac} ))"
+	if [ $posfrac -le 0 ]; then
+		echo 0
+		return
+	fi
+	local res="${longint:0:$posfrac}"
+	# roundup if the next decimal is >= 5
+	case "${longint:$posfrac:1}" in
+	[5-9]*) # roundup
+		longadd "$res" 1
+		;;
+	*)
+		echo "$res"
+		;;
+	esac
+}
+
 human2longint () {
-	local human orighuman gotb suffix int frac longint
-	set -- $*; human="$1$2$3$4$5" # without the spaces
+	local human orighuman gotb suffix int frac
+	local binary powbase dfrac
+	#set -- $*; human="$1$2$3$4$5" # without the spaces
+	human="$*"
+	human="${human// /}" #without the spaces
 	orighuman="$human"
 	human=${human%b} #remove last b
 	human=${human%B} #remove last B
@@ -323,9 +429,18 @@ human2longint () {
 	if [ "$human" != "$orighuman" ]; then
 		gotb=1
 	fi
+	binary=${human#${human%?}} # the last symbol of $human
+	case $binary in
+	i)
+		human=${human%$binary}
+		;;
+	*)
+		binary=''
+		;;
+	esac
 	suffix=${human#${human%?}} # the last symbol of $human
 	case $suffix in
-	k|K|m|M|g|G|t|T)
+	k|K|m|M|g|G|t|T|p|P|e|E)
 		human=${human%$suffix}
 		;;
 	*)
@@ -337,49 +452,61 @@ human2longint () {
 		;;
 	esac
 	int="${human%[.,]*}"
-	[ "$int" ] || int=0
 	frac=${human#$int}
-	frac="${frac#[.,]}0000" # to be sure there are at least 4 digits
-	frac=${frac%${frac#????}} # only the first 4 digits of $frac
-	longint=$(expr "$int" \* 10000 + "$frac")
+	int="${int:$(expr "$int" : '0*' || true)}" # remove leading 0
+	[ "$int" ] || int=0
+	frac="${frac#[.,]}"
+	# to be sure there are at least 18 digits, the maximum possible precision for decimal units
+	dfrac="${frac}0000000000000000000"
 	case $suffix in
 	b|B)
-		longint=${longint%????}
-		[ "$longint" ] || longint=0
+		dfrac=
+		binary=''
 		;;
 	k|K)
-		longint=${longint%?}
+		powbase=1
+		dfrac=${dfrac:0:3} # only the first 3 digits of $dfrac
 		;;
-	m|M)
-		longint=${longint}00
+	m|M|'') # without units, suppose MB
+		powbase=2
+		dfrac=${dfrac:0:6} # only the first 6 digits of $dfrac
 		;;
 	g|G)
-		longint=${longint}00000
+		powbase=3
+		dfrac=${dfrac:0:9} # only the first 9 digits of $dfrac
 		;;
 	t|T)
-		longint=${longint}00000000
+		powbase=4
+		dfrac=${dfrac:0:12} # only the first 12 digits of $dfrac
 		;;
-	*) # no suffix:
-		# bytes
-		#longint=${longint%????}
-		#[ "$longint" ] || longint=0
-		# megabytes
-		longint=${longint}00
+	p|P)
+		powbase=5
+		dfrac=${dfrac:0:15} # only the first 15 digits of $dfrac
+		;;
+	e|E)
+		powbase=6
+		dfrac=${dfrac:0:18} # only the first 18 digits of $dfrac
+		;;
+	esac
+	case "$binary" in
+	'')
+		echo "${int}${dfrac}"
 		;;
+	i)
+		human2longint_binary_unit "$int" "$frac" "$powbase"
 	esac
-	echo $longint
 }
 
 valid_human () {
 	local IFS patterns
 	patterns='[0-9][0-9]* *$
 [0-9][0-9]* *[bB] *$
-[0-9][0-9]* *[kKmMgGtT] *$
-[0-9][0-9]* *[kKmMgGtT][bB] *$
+[0-9][0-9]* *[kKmMgGtTpPeE] *$
+[0-9][0-9]* *[kKmMgGtTpPeE]i\?[bB] *$
 [0-9]*[.,][0-9]* *$
 [0-9]*[.,][0-9]* *[bB] *$
-[0-9]*[.,][0-9]* *[kKmMgGtT] *$
-[0-9]*[.,][0-9]* *[kKmMgGtT][bB] *$'
+[0-9]*[.,][0-9]* *[kKmMgGtTpPeE] *$
+[0-9]*[.,][0-9]* *[kKmMgGtTpPeE]i\?[bB] *$'
 	IFS="$NL"
 	for regex in $patterns; do
 		if expr "$1" : "$regex" >/dev/null; then return 0; fi

Attachment: binary-units-tests.tar.gz
Description: application/gzip


Reply to: