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>