Chuyển đổi ROM ZIP thành IMG để flash cho điện thoại Xiaomi

Rãnh rỗi sanh nông nổi, lấy con Xiaomi Redmi Note 3 ra vọc. Thấy thiên hạ người ta cook rom, port rom tùm lum nên cũng muốn bon chen tí. Ngặt nỗi em dế nhà mình lại chưa unlock bootloader nên chưa cài được TWRP để có thể chạy ROM cook. Nào giờ có ROM mới toàn phải đợi người ta port qua file IMG để flash với SP Flash Tool. Bực mình quá nên quyết tâm tự port ra file IMG từ file ZIP của Recovery. Cụ thể ý tưởng ở đây là tạo file ‘system.img’ từ thư mục ‘system’ có trong file ZIP của ROM.

Vì Android chạy trên nền của Linux nên mình phải sử dụng hệ điều hành Linux để làm nha. Ở đây mình đã cài đặt hệ điều hành Fedora thông qua máy ảo VMWare để làm. Cách cài đặt Fedora bạn có thể tham khảo trên Google nha.

Yêu cầu:

  • Linux (Fedora)
  • ext4-utils dùng để tạo file img với định dạng ext4fs (xem hướng dẫn compile tại đây)

Lưu ý: mặc dù mình đã thành công làm trên máy của mình nhưng không chắc chắn sẽ thành công trên máy khác. Vì vậy bạn hãy cân nhắc thật kỹ trước khi làm. Mình sẽ không chịu bất kỳ trách nhiệm nào nếu máy bạn bị lỗi hay gặp bất kỳ sự cố nào.

Phần 1 – Script dùng để chạy file updater-script của ROM

Sau 1 buổi tìm hiểu cấu trúc file ZIP Recovery, ngộ ra được file updater-script trong folder META-INF/com/google, chính là file Recovery dùng để cài đặt ROM, trong đó quan trọng là tạo các symlink và cài đặt các phân quyền (permission) cho files/directories. File script này viết theo định dạng Edify và chỉ chạy được trong Recovery, do đó mình quyết định thử viết 1 đoạn shell script để chạy file này. Đoạn script này chủ yếu tạo các symlink và set các permission cho thư mục “system”. Sau khi chạy thành công thì mình sẽ đóng gói nó lại thành file system.img để flash với SP Flash Tool. 

Trên Fedora, bạn tạo 1 file mới đặt tên là “run-updater.sh” với nội dung như sau:

#!/bin/bash
# Script created by Tran Nhuan Quang at workleast.com (goctinhtinh.wordpress.com)

if [ $# -eq 0 ]
then
	echo "Please input ROM folder"
	exit 1
fi

# State = Dev / Deploy
# On Dev, no command is executed. Instead, only output to the console
STATE=Deploy

# Mode = Mock / Real
# On Mock, a test.txt file will be used
MODE=Real

XATTR_SECURITY_PREFIX=security.
XATTR_SELINUX_SUFFIX=selinux
XATTR_NAME_SELINUX=$XATTR_SECURITY_PREFIX$XATTR_SELINUX_SUFFIX
XATTR_CAPS_SUFFIX=capability
XATTR_NAME_CAPS=$XATTR_SECURITY_PREFIX$XATTR_CAPS_SUFFIX

DESC="$1"
# Remove back-slash if exist
if [[ ${DESC:$((${#DESC}-1)):1} == / ]]; then
	DESC=${DESC%/}
	# echo "$DESC"
fi

if [ "$MODE" == Real ]; then
	SCRIPT_PATH=/META-INF/com/google/android/updater-script
else
	SCRIPT_PATH="/test.txt"
fi

EXTRAS_FILE="extras"

Process_script () {
func_name=
temp_cmd=
# sym_target=
# sym_array=
# sym_count=0

while IFS='' read -r line || [[ -n "$line" ]]; do
	line=${line//[[:blank:]]/}

	# echo "$func_name $temp_cmd $sym_array $sym_count";

	# SYMLINK
	if [[ "$line" =~ ^symlink ]]; then
		func_name=symlink
		temp=${line/);/}
		# temp=${temp//[[:blank:]]/}
		temp=${temp/symlink(/}
		# temp=${temp//,/ }
		temp=${temp//\"}
		temp_cmd=${temp}
		# echo "$temp_cmd";

	# SYMLINK CONTINUES (NEXT LINES)
	elif [[ "$line" =~ ^\" ]]; then
		if [ "$func_name" == symlink ]; then
			temp=${line/);/}
			# temp=${temp/,/ }
			temp=${temp//\"}
			temp_cmd="$temp_cmd$temp"
			
			#IFS=',' read -r -a array <<< "$temp"
			#for index in "${!array[@]}"
			#do
			#	temp_cmd="$temp_cmd ${array[$index]}"
			#done

		fi

	# SET METADATA
	elif [[ "$line" =~ ^set_metadata\( ]]; then
		# temp_cmd=
		func_name=metadata
		temp=${line/set_metadata(/}
		# temp=${temp//[[:blank:]]/}
		temp=${temp/);/}
		temp=${temp//\"}
		IFS=',' read -r -a array <<< "$temp"
		file="${array[0]}"
		unset "array[0]"
		chuser=""
		chgroup=""
		for index in "${!array[@]}"
		do
			if [ $(($index % 2)) != 0 ]; then
				cmd=""

				if [ ${array[$index]} == uid ]; then
					chuser="${array[(($index+1))]}"
				
				elif [ ${array[$index]} == gid ]; then
					chgroup=":${array[(($index+1))]}"
					
				elif [ ${array[$index]} == mode ]; then
					cmd="chmod ${array[(($index+1))]} $DESC$file"

				elif [ ${array[$index]} == capabilities ]; then
					# TODO
					# this will simulate function setxattr() from Recovery
					# Remove the content/attribute first, then set the new one
					caps=$((${array[(($index+1))]}))
					if [[ $caps == 0 ]]; then
						cmd="setfattr -x $XATTR_NAME_CAPS -h $DESC$file"
					else
						cmd="setfattr -n $XATTR_NAME_CAPS -v \\\"${array[(($index+1))]}\\\" -h $DESC$file"
					fi

				elif [ ${array[$index]} == selabel ]; then
					#TODO
					# this will simulate function lsetfilecon() from Recovery
					# setfattr is Fedora's setxattr equivalent
					# Edit: We do not want to remove selinux lable to avoid permission denied problem. Old: Remove the attr first, then set the new one
					#cmd2="setfattr -x $XATTR_NAME_SELINUX -h $DESC$file"
					cmd="setfattr -n $XATTR_NAME_SELINUX -v ${array[(($index+1))]} -h $DESC$file"
					#cmd=""

				fi

				# execute cmd here
				if [ "$cmd" != "" ]; then
					if [ "$STATE" == Deploy ]; then
						echo `$cmd`
					else
						echo "$cmd";
					fi
				fi
			fi
		done
		# execute chown here
		if [[ $chuser$chgroup != "" ]]; then
			if [ "$STATE" == Deploy ]; then
				echo `chown -h $chuser$chgroup $DESC$file`
			else
				echo "chown -h $chuser$chgroup $DESC$file";
			fi
		fi
		# echo $file;

	# SET_METADATA_RECURSIVE
	elif [[ "$line" =~ ^set_metadata_recursive  ]]; then
		# tmp_cmd=
		func_name=metadata
		temp=${line/set_metadata_recursive(/}
		temp=${temp/);/}
		temp=${temp//\"}
		# temp=${temp//[[:blank:]]/}
		IFS=',' read -r -a array <<< "$temp"
		file="${array[0]}"
		unset "array[0]"
		chuser=""
		chgroup=""
		esc="{} +"
		for index in "${!array[@]}"
		do
			# Travel through the name1, name2, nam3.... and get value1, value2, value3 by index+1
			if [ $(($index % 2)) != 0 ]; then
				cmd=""
				cmd2=""
				cmd3=""

				if [ ${array[$index]} == uid ]; then
					chuser="${array[(($index+1))]}"
				
				elif [ ${array[$index]} == gid ]; then
					chgroup=":${array[(($index+1))]}"
					
				elif [ ${array[$index]} == dmode ]; then
					cmd="find $DESC$file -type d -exec chmod ${array[(($index+1))]} $esc"

				elif [ ${array[$index]} == fmode ]; then
					cmd="find $DESC$file -type f -exec chmod ${array[(($index+1))]} $esc"
					#cmd2="find $DESC$file -type l -exec chmod -h ${array[(($index+1))]} $esc"


				elif [ ${array[$index]} == capabilities ]; then

					caps=$((${array[(($index+1))]}))
					if [[ $caps == 0 ]]; then
						cmd="find $DESC$file -type f -exec setfattr -x $XATTR_NAME_CAPS -h $esc"
						cmd2="find $DESC$file -type d -exec setfattr -x $XATTR_NAME_CAPS -h $esc"
						cmd3="find $DESC$file -type l -exec setfattr -x $XATTR_NAME_CAPS -h $esc"
					else
						cmd="find $DESC$file -type f -exec setfattr -n $XATTR_NAME_CAPS -v \\\"${array[(($index+1))]}\\\" -h $esc"
						cmd2="find $DESC$file -type d -exec setfattr -n $XATTR_NAME_CAPS -v \\\"${array[(($index+1))]}\\\" -h $esc"
						cmd3="find $DESC$file -type l -exec setfattr -n $XATTR_NAME_CAPS -v \\\"${array[(($index+1))]}\\\" -h $esc"
					fi

				elif [ ${array[$index]} == selabel ]; then
					#cmd="setlabel -R ${array[(($index+1))]} $DESC$file"
					#cmd2="find $DESC$file -type f -or -type d -exec setfattr -x $XATTR_NAME_SELINUX -h $esc"
					cmd="find $DESC$file -type f -exec setfattr -n $XATTR_NAME_SELINUX -v ${array[(($index+1))]} -h $esc"
					cmd2="find $DESC$file -type d -exec setfattr -n $XATTR_NAME_SELINUX -v ${array[(($index+1))]} -h $esc"
					cmd3="find $DESC$file -type l -exec setfattr -n $XATTR_NAME_SELINUX -v ${array[(($index+1))]} -h $esc"
					#cmd=""

				fi

				# execute cmd here
				if [ "$cmd" != "" ]; then
					if [ "$STATE" == Deploy ]; then
						echo `$cmd`
					else
						echo "$cmd";
					fi
				fi
				# execute cmd here
				if [ "$cmd2" != "" ]; then
					if [ "$STATE" == Deploy ]; then
						echo `$cmd2`
					else
						echo "$cmd2";
					fi
				fi
				if [ "$cmd3" != "" ]; then
					if [ "$STATE" == Deploy ]; then
						echo `$cmd3`
					else
						echo "$cmd3";
					fi
				fi

			fi
		done
		# execute chown here
		if [[ $chuser$chgroup != "" ]]; then
			if [ "$STATE" == Deploy ]; then
				echo `chown -R -h $chuser$chgroup $DESC$file`
			else
				echo "chown -R -h $chuser$chgroup $DESC$file";
			fi
		fi
	else
		func_name=
		temp_cmd=
	fi


	# IF this is the last line of symlink, execute the command
	if [ "$func_name" == symlink ]; then
		if [[ $line =~ .*\).* ]]; then
			if [ "$temp_cmd" != "" ]; then
				# sym_array[${sym_count}]=${temp_cmd}
				IFS=',' read -r -a array <<< "$temp_cmd"
				file=${array[0]}
				unset "array[0]"

				cmd="ln -sf $file"

				for element in "${array[@]}"
				do
					if [ "$STATE" == Deploy ]; then
						echo `mkdir -p $DESC${element%/*}`
						echo `$cmd $DESC$element`
					else
						echo "Create $DESC${element%/*}"
						echo "$cmd $DESC$element";
					fi
				done
				# sym_count=$((sym_count+1))
				# echo "$sym_count";
				temp_cmd=
			fi
		fi
	fi

done < "$1"
}

echo "Process the $DESC$SCRIPT_PATH"
Process_script $DESC$SCRIPT_PATH

echo "Process any extra scripts...";
Process_script $EXTRAS_FILE

Phần 2 – Script dùng để tạo file system.img

Sau khi chạy file updater, mình sẽ phải chuyển thư mục /system trong thư mục chứa ROM thành file system.img để có thể flash với SP Flash Tool.

Bạn tạo 1 file mới với têm “create-system-img.sh” với nội dụng sau:

#!/bin/bash
# Script created by Tran Nhuan Quang at workleast.com (goctinhtinh.wordpress.com)

if [ $# -eq 0 ]
then
	echo "Please input ROM folder"
	exit 1
fi

make_ext4fs -s -l 2048M -a /system -S $1file_contexts system.img $1system 

mkdir tmp_dir

mount -o loop system.img tmp_dir

cp -v -r -p --preserve=context $1system/* tmp_dir/ && sync

umount tmp_dir/

rm -Rf tmp_dir/

Sau khi tạo 2 file này, bắt đầu tiến hành chuyển đổi file bằng các lệnh sau:

$chmod +x run-updater.sh
$chmod +x create-system-img.sh
$./run-updater.sh <ROM Folder>
$./create-system-img.sh <ROM Folder>

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *