├── README.md ├── bin ├── Linux │ ├── aarch64 │ │ ├── extract.erofs │ │ ├── img2simg │ │ ├── lpmake │ │ ├── magiskboot │ │ ├── make_ext4fs │ │ ├── mkfs.erofs │ │ ├── payload-dumper-go │ │ ├── vbmeta-disable-verification │ │ └── zstd │ └── x86_64 │ │ ├── extract.erofs │ │ ├── img2simg │ │ ├── lpmake │ │ ├── magiskboot │ │ ├── make_ext4fs │ │ ├── mkfs.erofs │ │ ├── payload-dumper-go │ │ ├── vbmeta-disable-verification │ │ └── zstd ├── apktool │ ├── baksmali.jar │ └── smali.jar ├── flash │ ├── flash_and_format.bat │ ├── flash_update.bat │ ├── platform-tools-windows │ │ ├── AdbWinApi.dll │ │ ├── AdbWinUsbApi.dll │ │ ├── NOTICE.txt │ │ ├── adb.exe │ │ ├── awk.exe │ │ ├── curl.exe │ │ ├── cut.exe │ │ ├── dmtracedump.exe │ │ ├── etc1tool.exe │ │ ├── fastboot.exe │ │ ├── hprof-conv.exe │ │ ├── libwinpthread-1.dll │ │ ├── make_f2fs.exe │ │ ├── make_f2fs_casefold.exe │ │ ├── mke2fs.conf │ │ ├── mke2fs.exe │ │ ├── source.properties │ │ ├── sqlite3.exe │ │ └── zstd.exe │ ├── update-binary │ └── zstd ├── getSuperSize.sh ├── imgextractor │ ├── __pycache__ │ │ └── ext4.cpython-310.pyc │ ├── ext4.py │ └── imgextractor.py └── port_config ├── port.sh └── setup.sh /README.md: -------------------------------------------------------------------------------- 1 | # MIUI V-A/B 机型移植项目 2 | 3 | ## 简介 4 | - MIUI14安卓13移植一键自动完成 5 | 6 | ## 测试机型及版本 7 | - 测试机型小米10S 底包 (V14.0.6.0.TGACNXM) 8 | - 测试版本 小米13 Android 13 V14.0.23.9.18.DEV 9 | - 测试版本 小米13Pro Android 13 V14.0.23.9.18.DEV 10 | - 测试版本 小米13Ultra Android 13 V14.0.23.9.18.DEV 11 | 12 | ## 正常工作 13 | - NFC 14 | - 人脸 15 | - 挖孔 16 | - 相机 17 | - 指纹 18 | - 自动亮度 19 | - 通话息屏 20 | - 应用双开 21 | - 护眼模式 22 | - 带壳截屏 23 | 24 | ## BUG 25 | - DPI偏小 26 | - 等你发现 27 | 28 | ## 说明 29 | - 以上均基于小米10S正式版(V14.0.6.0.TGACNXM)测试 30 | - 联发科未测试 31 | 32 | ## 如何使用 33 | - 在WSL、ubuntu、deepin等Linux下 34 | ```shell 35 | sudo apt update 36 | sudo apt upgrade 37 | sudo apt install git -y 38 | # 克隆项目 39 | git clone https://github.com/ljc-fight/miui_port.git 40 | cd miui_port 41 | # 安装依赖 42 | sudo bash setup.sh 43 | # 开始移植 44 | sudo bash miui_port.sh <底包路径> <移植包路径> 45 | ``` 46 | 47 | - 在Termux上 48 | ```shell 49 | pkg update 50 | pkg upgrade 51 | pkg install git tsu -y 52 | # 克隆项目 53 | git clone https://github.com/ljc-fight/miui_port.git 54 | cd miui_port/ 55 | # 安装依赖 56 | bash setup.sh 57 | # 进入root模式 58 | tsu 59 | bash miui_port.sh <底包路径> <移植包路径> 60 | ``` 61 | - 上述代码中,底包路径和移植包路径可以替换为链接 -------------------------------------------------------------------------------- /bin/Linux/aarch64/extract.erofs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/extract.erofs -------------------------------------------------------------------------------- /bin/Linux/aarch64/img2simg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/img2simg -------------------------------------------------------------------------------- /bin/Linux/aarch64/lpmake: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/lpmake -------------------------------------------------------------------------------- /bin/Linux/aarch64/magiskboot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/magiskboot -------------------------------------------------------------------------------- /bin/Linux/aarch64/make_ext4fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/make_ext4fs -------------------------------------------------------------------------------- /bin/Linux/aarch64/mkfs.erofs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/mkfs.erofs -------------------------------------------------------------------------------- /bin/Linux/aarch64/payload-dumper-go: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/payload-dumper-go -------------------------------------------------------------------------------- /bin/Linux/aarch64/vbmeta-disable-verification: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/vbmeta-disable-verification -------------------------------------------------------------------------------- /bin/Linux/aarch64/zstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/aarch64/zstd -------------------------------------------------------------------------------- /bin/Linux/x86_64/extract.erofs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/extract.erofs -------------------------------------------------------------------------------- /bin/Linux/x86_64/img2simg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/img2simg -------------------------------------------------------------------------------- /bin/Linux/x86_64/lpmake: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/lpmake -------------------------------------------------------------------------------- /bin/Linux/x86_64/magiskboot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/magiskboot -------------------------------------------------------------------------------- /bin/Linux/x86_64/make_ext4fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/make_ext4fs -------------------------------------------------------------------------------- /bin/Linux/x86_64/mkfs.erofs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/mkfs.erofs -------------------------------------------------------------------------------- /bin/Linux/x86_64/payload-dumper-go: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/payload-dumper-go -------------------------------------------------------------------------------- /bin/Linux/x86_64/vbmeta-disable-verification: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/vbmeta-disable-verification -------------------------------------------------------------------------------- /bin/Linux/x86_64/zstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/Linux/x86_64/zstd -------------------------------------------------------------------------------- /bin/apktool/baksmali.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/apktool/baksmali.jar -------------------------------------------------------------------------------- /bin/apktool/smali.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/apktool/smali.jar -------------------------------------------------------------------------------- /bin/flash/flash_and_format.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd %~dp0 3 | if exist images\super.img.zst META-INF\platform-tools\zstd --rm -d images\super.img.zst -o images\super.img 4 | 5 | rem 6 | 7 | if exist images\cust.img META-INF\platform-tools\fastboot flash super images\cust.img 8 | if exist images\super.img META-INF\platform-tools\fastboot flash super images\super.img 9 | META-INF\platform-tools\fastboot erase userdata 10 | META-INF\platform-tools\fastboot erase metadata 11 | META-INF\platform-tools\fastboot set_active a 12 | META-INF\platform-tools\fastboot reboot 13 | pause -------------------------------------------------------------------------------- /bin/flash/flash_update.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd %~dp0 3 | 4 | if exist images\super.img.zst META-INF\platform-tools\zstd --rm -d images\super.img.zst -o images\super.img 5 | 6 | rem 7 | 8 | if exist images\cust.img META-INF\platform-tools\fastboot flash super images\cust.img 9 | if exist images\super.img META-INF\platform-tools\fastboot flash super images\super.img 10 | META-INF\platform-tools\fastboot set_active a 11 | META-INF\platform-tools\fastboot reboot 12 | pause -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/AdbWinApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/AdbWinApi.dll -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/AdbWinUsbApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/AdbWinUsbApi.dll -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/NOTICE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/NOTICE.txt -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/adb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/adb.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/awk.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/awk.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/curl.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/curl.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/cut.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/cut.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/dmtracedump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/dmtracedump.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/etc1tool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/etc1tool.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/fastboot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/fastboot.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/hprof-conv.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/hprof-conv.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/libwinpthread-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/libwinpthread-1.dll -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/make_f2fs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/make_f2fs.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/make_f2fs_casefold.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/make_f2fs_casefold.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/mke2fs.conf: -------------------------------------------------------------------------------- 1 | [defaults] 2 | base_features = sparse_super,large_file,filetype,dir_index,ext_attr 3 | default_mntopts = acl,user_xattr 4 | enable_periodic_fsck = 0 5 | blocksize = 4096 6 | inode_size = 256 7 | inode_ratio = 16384 8 | reserved_ratio = 1.0 9 | 10 | [fs_types] 11 | ext3 = { 12 | features = has_journal 13 | } 14 | ext4 = { 15 | features = has_journal,extent,huge_file,dir_nlink,extra_isize,uninit_bg 16 | inode_size = 256 17 | } 18 | ext4dev = { 19 | features = has_journal,extent,huge_file,flex_bg,inline_data,64bit,dir_nlink,extra_isize 20 | inode_size = 256 21 | options = test_fs=1 22 | } 23 | small = { 24 | blocksize = 1024 25 | inode_size = 128 26 | inode_ratio = 4096 27 | } 28 | floppy = { 29 | blocksize = 1024 30 | inode_size = 128 31 | inode_ratio = 8192 32 | } 33 | big = { 34 | inode_ratio = 32768 35 | } 36 | huge = { 37 | inode_ratio = 65536 38 | } 39 | news = { 40 | inode_ratio = 4096 41 | } 42 | largefile = { 43 | inode_ratio = 1048576 44 | blocksize = -1 45 | } 46 | largefile4 = { 47 | inode_ratio = 4194304 48 | blocksize = -1 49 | } 50 | hurd = { 51 | blocksize = 4096 52 | inode_size = 128 53 | } 54 | -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/mke2fs.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/mke2fs.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/source.properties: -------------------------------------------------------------------------------- 1 | Pkg.UserSrc=false 2 | Pkg.Revision=33.0.2 -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/sqlite3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/sqlite3.exe -------------------------------------------------------------------------------- /bin/flash/platform-tools-windows/zstd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/platform-tools-windows/zstd.exe -------------------------------------------------------------------------------- /bin/flash/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | OUTFD=/proc/self/fd/$2 4 | ZIPFILE="$3" 5 | 6 | ui_print() { 7 | echo -e "ui_print $1\nui_print" >>$OUTFD 8 | } 9 | 10 | package_extract_file() { 11 | ui_print "- 正在刷写分区 $(echo $2 | cut -d '/' -f 6) " 12 | unzip -p "$ZIPFILE" $1 >$2 13 | } 14 | 15 | package_extract_zstd() { 16 | ui_print "- 正在刷写分区 $(echo $2 | cut -d '/' -f 6) " 17 | unzip -p "$ZIPFILE" $1 | /tmp/META-INF/zstd -c -d >$2 18 | } 19 | 20 | getVolumeKey() { 21 | ui_print "- 监听音量键 按[+]选择"是" 按[-]选择"否"" 22 | keyInfo=true 23 | while $keyInfo;do 24 | keyInfo=$(getevent -qlc 1 |grep KEY_VOLUME) 25 | if [ "$keyInfo" == "" ];then 26 | continue 27 | else 28 | isUpKey=$(echo $keyInfo |grep KEY_VOLUMEUP) 29 | [ "$isUpKey" != "" ] && return 0 || return 1 30 | break 31 | fi 32 | done 33 | } 34 | 35 | checkDevice() { 36 | myDevice=$(getprop ro.product.name) 37 | romDevice=deviceCode 38 | ui_print "=========================" 39 | ui_print " " 40 | ui_print " 设备代号:$myDevice" 41 | ui_print " " 42 | ui_print " ROM 代号:$romDevice" 43 | ui_print " " 44 | ui_print " 底包版本:baseversion" 45 | ui_print " " 46 | ui_print " 移植版本:portversion" 47 | ui_print " " 48 | ui_print " 安卓版本:andVersion" 49 | ui_print " " 50 | ui_print "=========================" 51 | if [ "$myDevice" != "$romDevice" ];then 52 | ui_print "- 设备代号检验不通过,请再次核实此包是否对应你的机型" 53 | ui_print "- 如果刷错包导致变砖,你将自己承担其后果,是否继续刷入?" 54 | if ! getVolumeKey ;then 55 | ui_print "- 你选择了终止刷入" 56 | exit 1 57 | else 58 | ui_print "- 你选择了继续刷入" 59 | fi 60 | fi 61 | } 62 | 63 | [ -d /tmp ] && rm -rf /tmp 64 | mkdir -p /tmp 65 | unzip "$ZIPFILE" META-INF/zstd -d /tmp 66 | chmod -R 0755 /tmp 67 | ui_print " " 68 | ui_print "=========================" 69 | ui_print " MIUI 追更计划" 70 | ui_print " Github网址: https://github.com/ljc-fight/miui_port" 71 | ui_print "=========================" 72 | ui_print " " 73 | 74 | checkDevice 75 | 76 | # 检查是否存在打包错误 77 | 78 | isDamaged=$(unzip -l $ZIPFILE |grep "patcherror.txt") 79 | 80 | if [ "$isDamaged" != "" ];then 81 | ui_print "该ROM存在打包错误,终止刷入" 82 | exit 1 83 | fi 84 | 85 | 86 | 87 | #firmware 88 | 89 | 90 | 91 | 92 | # unzip preloader 93 | unzip -o $ZIPFILE "images/preloader*.img" -d /tmp >/dev/null 2>&1 94 | 95 | # 部分机型 刷 preloader 时 检测部分机型的闪存类型 UFS or EMMC 96 | 97 | # UFS 98 | if [ -e /dev/block/sda ] && [ -f tmp/images/preloader_ufs.img ] ;then 99 | unzip -p $ZIPFILE images/preloader_ufs.img | /tmp/META-INF/zstd -c -d >/dev/block/by-name/sda 100 | fi 101 | 102 | #EMMC 103 | if [ -e /dev/block/mmcblk0boot0 ] && [ -f tmp/images/preloader_emmc.img ] ;then 104 | unzip -p $ZIPFILE images/preloader_emmc.img | /tmp/META-INF/zstd -c -d >/dev/block/by-name/mmcblk0boot0 105 | fi 106 | 107 | if [ -e /dev/block/mmcblk0boot1 ] && [ -f tmp/images/preloader_emmc.img ] ;then 108 | unzip -p $ZIPFILE images/preloader_emmc.img | /tmp/META-INF/zstd -c -d >/dev/block/by-name/mmcblk0boot1 109 | fi 110 | 111 | # 有 preloader_raw.img 时 获取分区名称 112 | if [ -f tmp/images/preloader_raw.img ] ;then 113 | for preloaderPartition in $(ls /dev/block/by-name |grep preloader);do 114 | unzip -p $ZIPFILE images/preloader_raw.img | /tmp/META-INF/zstd -c -d >/dev/block/by-name/"$preloaderPartition" 115 | done 116 | fi 117 | 118 | 119 | 120 | 121 | #super cust image 122 | package_extract_file "images/cust.img" "/dev/block/bootdevice/by-name/cust" 123 | package_extract_zstd "images/super.img.zst" "/dev/block/bootdevice/by-name/super" 124 | 125 | 126 | #remap 127 | [ -e /dev/block/mapper/odm_a ] && lptools unmap odm_a && lptools map odm_a 128 | [ -e /dev/block/mapper/mi_ext_a ] && lptools unmap mi_ext_a && lptools map mi_ext_a 129 | [ -e /dev/block/mapper/system_a ] && lptools unmap system_a && lptools map system_a 130 | [ -e /dev/block/mapper/vendor_a ] && lptools unmap vendor_a && lptools map vendor_a 131 | [ -e /dev/block/mapper/product_a ] && lptools unmap product_a && lptools map product_a 132 | [ -e /dev/block/mapper/odm_dlkm_a ] && lptools unmap odm_dlkm_a && lptools map odm_dlkm_a 133 | [ -e /dev/block/mapper/system_ext_a ] && lptools unmap system_ext_a && lptools map system_ext_a 134 | [ -e /dev/block/mapper/vendor_dlkm_a ] && lptools unmap vendor_dlkm_a && lptools map vendor_dlkm_a 135 | [ -e /dev/block/mapper/system_dlkm_a ] && lptools unmap system_dlkm_a && lptools map system_dlkm_a 136 | 137 | 138 | ui_print "- 清除缓存" 139 | rm -rf /data/dalvik-cache/arm/* 140 | rm -rf /data/dalvik-cache/arm64/* 141 | rm -rf /data/system/package_cache/* 142 | ui_print " " 143 | ui_print " " 144 | ui_print "- 刷机完毕" 145 | ui_print " " 146 | ui_print " " 147 | ui_print "- 如有红色挂载报错请直接忽略" 148 | ui_print "- 如有红色挂载报错请直接忽略" 149 | ui_print "- 如有红色挂载报错请直接忽略" 150 | ui_print " " 151 | ui_print " " 152 | 153 | exit 0 154 | -------------------------------------------------------------------------------- /bin/flash/zstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/flash/zstd -------------------------------------------------------------------------------- /bin/getSuperSize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | deviceCode=$1 3 | case $deviceCode in 4 | #13 13Pro 13Ultra 5 | FUXI | NUWA |ISHTAR) size=9663676416;; 6 | #RedmiNote12Turbo |K60Pro |MIXFold 7 | MARBLE |SOCRATES|BABYLON) size=9663676416;; 8 | #Redmi Note 12 5G 9 | SUNSTONE) size=9122611200;; 10 | #PAD6Max 11 | YUDI) size=11811160064;; 12 | #Others 13 | *) size=9126805504;; 14 | esac 15 | echo $size 16 | 17 | #pipa 9126805504 |Pad6 18 | #liuqin 9126805504 |Pad6Pro 19 | #sunstone 9126805504 or 9122611200 |Note 12 5G 20 | #rembrandt 9126805504 |K60E 21 | #redwood 9126805504 |Note12ProSpeed 22 | #mondrian 9126805504 |K60 23 | #yunluo 9126805504 |RedmiPad 24 | #ruby 9126805504 |Note 12 Pro -------------------------------------------------------------------------------- /bin/imgextractor/__pycache__/ext4.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc-fight/miui_port/6fa71826f028536afb3ebcca994ddb2beb40e3b4/bin/imgextractor/__pycache__/ext4.cpython-310.pyc -------------------------------------------------------------------------------- /bin/imgextractor/ext4.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import functools 3 | import io 4 | import math 5 | import queue 6 | 7 | def wcscmp(str_a, str_b): 8 | for a, b in zip(str_a, str_b): 9 | tmp = ord(a) - ord(b) 10 | if tmp != 0: return -1 if tmp < 0 else 1 11 | 12 | tmp = len(str_a) - len(str_b) 13 | return -1 if tmp < 0 else 1 if tmp > 0 else 0 14 | 15 | 16 | class Ext4Error(Exception): 17 | pass 18 | 19 | 20 | class BlockMapError(Ext4Error): 21 | pass 22 | 23 | 24 | class EndOfStreamError(Ext4Error): 25 | pass 26 | 27 | 28 | class MagicError(Ext4Error): 29 | pass 30 | 31 | 32 | # ----------------------------- LOW LEVEL ------------------------------ 33 | 34 | class ext4_struct(ctypes.LittleEndianStructure): 35 | def __getattr__(self, name): 36 | try: 37 | # Combining *_lo and *_hi fields 38 | lo_field = ctypes.LittleEndianStructure.__getattribute__(type(self), name + "_lo") 39 | size = lo_field.size 40 | 41 | lo = lo_field.__get__(self) 42 | hi = ctypes.LittleEndianStructure.__getattribute__(self, name + "_hi") 43 | 44 | return (hi << (8 * size)) | lo 45 | except AttributeError: 46 | return ctypes.LittleEndianStructure.__getattribute__(self, name) 47 | 48 | def __setattr__(self, name, value): 49 | try: 50 | # Combining *_lo and *_hi fields 51 | lo_field = lo_field = ctypes.LittleEndianStructure.__getattribute__(type(self), name + "_lo") 52 | size = lo_field.size 53 | 54 | lo_field.__set__(self, value & ((1 << (8 * size)) - 1)) 55 | ctypes.LittleEndianStructure.__setattr__(self, name + "_hi", value >> (8 * size)) 56 | except AttributeError: 57 | ctypes.LittleEndianStructure.__setattr__(self, name, value) 58 | 59 | 60 | class ext4_dir_entry_2(ext4_struct): 61 | _fields_ = [ 62 | ("inode", ctypes.c_uint), # 0x0 63 | ("rec_len", ctypes.c_ushort), # 0x4 64 | ("name_len", ctypes.c_ubyte), # 0x6 65 | ("file_type", ctypes.c_ubyte) # 0x7 66 | # Variable length field "name" missing at 0x8 67 | ] 68 | 69 | def _from_buffer_copy(raw, offset=0, platform64=True): 70 | struct = ext4_dir_entry_2.from_buffer_copy(raw, offset) 71 | struct.name = raw[offset + 0x8: offset + 0x8 + struct.name_len] 72 | return struct 73 | 74 | 75 | class ext4_extent(ext4_struct): 76 | _fields_ = [ 77 | ("ee_block", ctypes.c_uint), # 0x0000 78 | ("ee_len", ctypes.c_ushort), # 0x0004 79 | ("ee_start_hi", ctypes.c_ushort), # 0x0006 80 | ("ee_start_lo", ctypes.c_uint) # 0x0008 81 | ] 82 | 83 | 84 | class ext4_extent_header(ext4_struct): 85 | _fields_ = [ 86 | ("eh_magic", ctypes.c_ushort), # 0x0000, Must be 0xF30A 87 | ("eh_entries", ctypes.c_ushort), # 0x0002 88 | ("eh_max", ctypes.c_ushort), # 0x0004 89 | ("eh_depth", ctypes.c_ushort), # 0x0006 90 | ("eh_generation", ctypes.c_uint) # 0x0008 91 | ] 92 | 93 | 94 | class ext4_extent_idx(ext4_struct): 95 | _fields_ = [ 96 | ("ei_block", ctypes.c_uint), # 0x0000 97 | ("ei_leaf_lo", ctypes.c_uint), # 0x0004 98 | ("ei_leaf_hi", ctypes.c_ushort), # 0x0008 99 | ("ei_unused", ctypes.c_ushort) # 0x000A 100 | ] 101 | 102 | 103 | class ext4_group_descriptor(ext4_struct): 104 | _fields_ = [ 105 | ("bg_block_bitmap_lo", ctypes.c_uint), # 0x0000 106 | ("bg_inode_bitmap_lo", ctypes.c_uint), # 0x0004 107 | ("bg_inode_table_lo", ctypes.c_uint), # 0x0008 108 | ("bg_free_blocks_count_lo", ctypes.c_ushort), # 0x000C 109 | ("bg_free_inodes_count_lo", ctypes.c_ushort), # 0x000E 110 | ("bg_used_dirs_count_lo", ctypes.c_ushort), # 0x0010 111 | ("bg_flags", ctypes.c_ushort), # 0x0012 112 | ("bg_exclude_bitmap_lo", ctypes.c_uint), # 0x0014 113 | ("bg_block_bitmap_csum_lo", ctypes.c_ushort), # 0x0018 114 | ("bg_inode_bitmap_csum_lo", ctypes.c_ushort), # 0x001A 115 | ("bg_itable_unused_lo", ctypes.c_ushort), # 0x001C 116 | ("bg_checksum", ctypes.c_ushort), # 0x001E 117 | 118 | # 64-bit fields 119 | ("bg_block_bitmap_hi", ctypes.c_uint), # 0x0020 120 | ("bg_inode_bitmap_hi", ctypes.c_uint), # 0x0024 121 | ("bg_inode_table_hi", ctypes.c_uint), # 0x0028 122 | ("bg_free_blocks_count_hi", ctypes.c_ushort), # 0x002C 123 | ("bg_free_inodes_count_hi", ctypes.c_ushort), # 0x002E 124 | ("bg_used_dirs_count_hi", ctypes.c_ushort), # 0x0030 125 | ("bg_itable_unused_hi", ctypes.c_ushort), # 0x0032 126 | ("bg_exclude_bitmap_hi", ctypes.c_uint), # 0x0034 127 | ("bg_block_bitmap_csum_hi", ctypes.c_ushort), # 0x0038 128 | ("bg_inode_bitmap_csum_hi", ctypes.c_ushort), # 0x003A 129 | ("bg_reserved", ctypes.c_uint), # 0x003C 130 | ] 131 | 132 | def _from_buffer_copy(raw, platform64=True): 133 | struct = ext4_group_descriptor.from_buffer_copy(raw) 134 | 135 | if not platform64: 136 | struct.bg_block_bitmap_hi = 0 137 | struct.bg_inode_bitmap_hi = 0 138 | struct.bg_inode_table_hi = 0 139 | struct.bg_free_blocks_count_hi = 0 140 | struct.bg_free_inodes_count_hi = 0 141 | struct.bg_used_dirs_count_hi = 0 142 | struct.bg_itable_unused_hi = 0 143 | struct.bg_exclude_bitmap_hi = 0 144 | struct.bg_block_bitmap_csum_hi = 0 145 | struct.bg_inode_bitmap_csum_hi = 0 146 | struct.bg_reserved = 0 147 | 148 | return struct 149 | 150 | 151 | class ext4_inode(ext4_struct): 152 | EXT2_GOOD_OLD_INODE_SIZE = 128 # Every field passing 128 bytes is "additional data", whose size is specified by i_extra_isize. 153 | 154 | # i_mode 155 | S_IXOTH = 0x1 # Others can execute 156 | S_IWOTH = 0x2 # Others can write 157 | S_IROTH = 0x4 # Others can read 158 | S_IXGRP = 0x8 # Group can execute 159 | S_IWGRP = 0x10 # Group can write 160 | S_IRGRP = 0x20 # Group can read 161 | S_IXUSR = 0x40 # Owner can execute 162 | S_IWUSR = 0x80 # Owner can write 163 | S_IRUSR = 0x100 # Owner can read 164 | S_ISVTX = 0x200 # Sticky bit (only owner can delete) 165 | S_ISGID = 0x400 # Set GID (execute with privileges of group owner of the file's group) 166 | S_ISUID = 0x800 # Set UID (execute with privileges of the file's owner) 167 | S_IFIFO = 0x1000 # FIFO device (named pipe) 168 | S_IFCHR = 0x2000 # Character device (raw, unbuffered, aligned, direct access to hardware storage) 169 | S_IFDIR = 0x4000 # Directory 170 | S_IFBLK = 0x6000 # Block device (buffered, arbitrary access to storage) 171 | S_IFREG = 0x8000 # Regular file 172 | S_IFLNK = 0xA000 # Symbolic link 173 | S_IFSOCK = 0xC000 # Socket 174 | 175 | # i_flags 176 | EXT4_INDEX_FL = 0x1000 # Uses hash trees 177 | EXT4_EXTENTS_FL = 0x80000 # Uses extents 178 | EXT4_EA_INODE_FL = 0x200000 # Inode stores large xattr 179 | EXT4_INLINE_DATA_FL = 0x10000000 # Has inline data 180 | 181 | _fields_ = [ 182 | ("i_mode", ctypes.c_ushort), # 0x0000 183 | ("i_uid_lo", ctypes.c_ushort), # 0x0002, Originally named i_uid 184 | ("i_size_lo", ctypes.c_uint), # 0x0004 185 | ("i_atime", ctypes.c_uint), # 0x0008 186 | ("i_ctime", ctypes.c_uint), # 0x000C 187 | ("i_mtime", ctypes.c_uint), # 0x0010 188 | ("i_dtime", ctypes.c_uint), # 0x0014 189 | ("i_gid_lo", ctypes.c_ushort), # 0x0018, Originally named i_gid 190 | ("i_links_count", ctypes.c_ushort), # 0x001A 191 | ("i_blocks_lo", ctypes.c_uint), # 0x001C 192 | ("i_flags", ctypes.c_uint), # 0x0020 193 | ("osd1", ctypes.c_uint), # 0x0024 194 | ("i_block", ctypes.c_uint * 15), # 0x0028 195 | ("i_generation", ctypes.c_uint), # 0x0064 196 | ("i_file_acl_lo", ctypes.c_uint), # 0x0068 197 | ("i_size_hi", ctypes.c_uint), # 0x006C, Originally named i_size_high 198 | ("i_obso_faddr", ctypes.c_uint), # 0x0070 199 | ("i_osd2_blocks_high", ctypes.c_ushort), # 0x0074, Originally named i_osd2.linux2.l_i_blocks_high 200 | ("i_file_acl_hi", ctypes.c_ushort), # 0x0076, Originally named i_osd2.linux2.l_i_file_acl_high 201 | ("i_uid_hi", ctypes.c_ushort), # 0x0078, Originally named i_osd2.linux2.l_i_uid_high 202 | ("i_gid_hi", ctypes.c_ushort), # 0x007A, Originally named i_osd2.linux2.l_i_gid_high 203 | ("i_osd2_checksum_lo", ctypes.c_ushort), # 0x007C, Originally named i_osd2.linux2.l_i_checksum_lo 204 | ("i_osd2_reserved", ctypes.c_ushort), # 0x007E, Originally named i_osd2.linux2.l_i_reserved 205 | ("i_extra_isize", ctypes.c_ushort), # 0x0080 206 | ("i_checksum_hi", ctypes.c_ushort), # 0x0082 207 | ("i_ctime_extra", ctypes.c_uint), # 0x0084 208 | ("i_mtime_extra", ctypes.c_uint), # 0x0088 209 | ("i_atime_extra", ctypes.c_uint), # 0x008C 210 | ("i_crtime", ctypes.c_uint), # 0x0090 211 | ("i_crtime_extra", ctypes.c_uint), # 0x0094 212 | ("i_version_hi", ctypes.c_uint), # 0x0098 213 | ("i_projid", ctypes.c_uint), # 0x009C 214 | ] 215 | 216 | 217 | class ext4_superblock(ext4_struct): 218 | EXT2_DESC_SIZE = 0x20 # Default value for s_desc_size, if INCOMPAT_64BIT is not set (NEEDS CONFIRMATION) 219 | 220 | # s_feature_incompat 221 | INCOMPAT_64BIT = 0x80 # Uses 64-bit features (e.g. *_hi structure fields in ext4_group_descriptor) 222 | INCOMPAT_FILETYPE = 0x2 # Directory entries record file type (instead of inode flags) 223 | _fields_ = [ 224 | ("s_inodes_count", ctypes.c_uint), # 0x0000 225 | ("s_blocks_count_lo", ctypes.c_uint), # 0x0004 226 | ("s_r_blocks_count_lo", ctypes.c_uint), # 0x0008 227 | ("s_free_blocks_count_lo", ctypes.c_uint), # 0x000C 228 | ("s_free_inodes_count", ctypes.c_uint), # 0x0010 229 | ("s_first_data_block", ctypes.c_uint), # 0x0014 230 | ("s_log_block_size", ctypes.c_uint), # 0x0018 231 | ("s_log_cluster_size", ctypes.c_uint), # 0x001C 232 | ("s_blocks_per_group", ctypes.c_uint), # 0x0020 233 | ("s_clusters_per_group", ctypes.c_uint), # 0x0024 234 | ("s_inodes_per_group", ctypes.c_uint), # 0x0028 235 | ("s_mtime", ctypes.c_uint), # 0x002C 236 | ("s_wtime", ctypes.c_uint), # 0x0030 237 | ("s_mnt_count", ctypes.c_ushort), # 0x0034 238 | ("s_max_mnt_count", ctypes.c_ushort), # 0x0036 239 | ("s_magic", ctypes.c_ushort), # 0x0038, Must be 0xEF53 240 | ("s_state", ctypes.c_ushort), # 0x003A 241 | ("s_errors", ctypes.c_ushort), # 0x003C 242 | ("s_minor_rev_level", ctypes.c_ushort), # 0x003E 243 | ("s_lastcheck", ctypes.c_uint), # 0x0040 244 | ("s_checkinterval", ctypes.c_uint), # 0x0044 245 | ("s_creator_os", ctypes.c_uint), # 0x0048 246 | ("s_rev_level", ctypes.c_uint), # 0x004C 247 | ("s_def_resuid", ctypes.c_ushort), # 0x0050 248 | ("s_def_resgid", ctypes.c_ushort), # 0x0052 249 | ("s_first_ino", ctypes.c_uint), # 0x0054 250 | ("s_inode_size", ctypes.c_ushort), # 0x0058 251 | ("s_block_group_nr", ctypes.c_ushort), # 0x005A 252 | ("s_feature_compat", ctypes.c_uint), # 0x005C 253 | ("s_feature_incompat", ctypes.c_uint), # 0x0060 254 | ("s_feature_ro_compat", ctypes.c_uint), # 0x0064 255 | ("s_uuid", ctypes.c_ubyte * 16), # 0x0068 256 | ("s_volume_name", ctypes.c_char * 16), # 0x0078 257 | ("s_last_mounted", ctypes.c_char * 64), # 0x0088 258 | ("s_algorithm_usage_bitmap", ctypes.c_uint), # 0x00C8 259 | ("s_prealloc_blocks", ctypes.c_ubyte), # 0x00CC 260 | ("s_prealloc_dir_blocks", ctypes.c_ubyte), # 0x00CD 261 | ("s_reserved_gdt_blocks", ctypes.c_ushort), # 0x00CE 262 | ("s_journal_uuid", ctypes.c_ubyte * 16), # 0x00D0 263 | ("s_journal_inum", ctypes.c_uint), # 0x00E0 264 | ("s_journal_dev", ctypes.c_uint), # 0x00E4 265 | ("s_last_orphan", ctypes.c_uint), # 0x00E8 266 | ("s_hash_seed", ctypes.c_uint * 4), # 0x00EC 267 | ("s_def_hash_version", ctypes.c_ubyte), # 0x00FC 268 | ("s_jnl_backup_type", ctypes.c_ubyte), # 0x00FD 269 | ("s_desc_size", ctypes.c_ushort), # 0x00FE 270 | ("s_default_mount_opts", ctypes.c_uint), # 0x0100 271 | ("s_first_meta_bg", ctypes.c_uint), # 0x0104 272 | ("s_mkfs_time", ctypes.c_uint), # 0x0108 273 | ("s_jnl_blocks", ctypes.c_uint * 17), # 0x010C 274 | 275 | # 64-bit fields 276 | ("s_blocks_count_hi", ctypes.c_uint), # 0x0150 277 | ("s_r_blocks_count_hi", ctypes.c_uint), # 0x0154 278 | ("s_free_blocks_count_hi", ctypes.c_uint), # 0x0158 279 | ("s_min_extra_isize", ctypes.c_ushort), # 0x015C 280 | ("s_want_extra_isize", ctypes.c_ushort), # 0x015E 281 | ("s_flags", ctypes.c_uint), # 0x0160 282 | ("s_raid_stride", ctypes.c_ushort), # 0x0164 283 | ("s_mmp_interval", ctypes.c_ushort), # 0x0166 284 | ("s_mmp_block", ctypes.c_ulonglong), # 0x0168 285 | ("s_raid_stripe_width", ctypes.c_uint), # 0x0170 286 | ("s_log_groups_per_flex", ctypes.c_ubyte), # 0x0174 287 | ("s_checksum_type", ctypes.c_ubyte), # 0x0175 288 | ("s_reserved_pad", ctypes.c_ushort), # 0x0176 289 | ("s_kbytes_written", ctypes.c_ulonglong), # 0x0178 290 | ("s_snapshot_inum", ctypes.c_uint), # 0x0180 291 | ("s_snapshot_id", ctypes.c_uint), # 0x0184 292 | ("s_snapshot_r_blocks_count", ctypes.c_ulonglong), # 0x0188 293 | ("s_snapshot_list", ctypes.c_uint), # 0x0190 294 | ("s_error_count", ctypes.c_uint), # 0x0194 295 | ("s_first_error_time", ctypes.c_uint), # 0x0198 296 | ("s_first_error_ino", ctypes.c_uint), # 0x019C 297 | ("s_first_error_block", ctypes.c_ulonglong), # 0x01A0 298 | ("s_first_error_func", ctypes.c_ubyte * 32), # 0x01A8 299 | ("s_first_error_line", ctypes.c_uint), # 0x01C8 300 | ("s_last_error_time", ctypes.c_uint), # 0x01CC 301 | ("s_last_error_ino", ctypes.c_uint), # 0x01D0 302 | ("s_last_error_line", ctypes.c_uint), # 0x01D4 303 | ("s_last_error_block", ctypes.c_ulonglong), # 0x01D8 304 | ("s_last_error_func", ctypes.c_ubyte * 32), # 0x01E0 305 | ("s_mount_opts", ctypes.c_ubyte * 64), # 0x0200 306 | ("s_usr_quota_inum", ctypes.c_uint), # 0x0240 307 | ("s_grp_quota_inum", ctypes.c_uint), # 0x0244 308 | ("s_overhead_blocks", ctypes.c_uint), # 0x0248 309 | ("s_backup_bgs", ctypes.c_uint * 2), # 0x024C 310 | ("s_encrypt_algos", ctypes.c_ubyte * 4), # 0x0254 311 | ("s_encrypt_pw_salt", ctypes.c_ubyte * 16), # 0x0258 312 | ("s_lpf_ino", ctypes.c_uint), # 0x0268 313 | ("s_prj_quota_inum", ctypes.c_uint), # 0x026C 314 | ("s_checksum_seed", ctypes.c_uint), # 0x0270 315 | ("s_reserved", ctypes.c_uint * 98), # 0x0274 316 | ("s_checksum", ctypes.c_uint) # 0x03FC 317 | ] 318 | 319 | def _from_buffer_copy(raw, platform64=True): 320 | struct = ext4_superblock.from_buffer_copy(raw) 321 | 322 | if not platform64: 323 | struct.s_blocks_count_hi = 0 324 | struct.s_r_blocks_count_hi = 0 325 | struct.s_free_blocks_count_hi = 0 326 | struct.s_min_extra_isize = 0 327 | struct.s_want_extra_isize = 0 328 | struct.s_flags = 0 329 | struct.s_raid_stride = 0 330 | struct.s_mmp_interval = 0 331 | struct.s_mmp_block = 0 332 | struct.s_raid_stripe_width = 0 333 | struct.s_log_groups_per_flex = 0 334 | struct.s_checksum_type = 0 335 | struct.s_reserved_pad = 0 336 | struct.s_kbytes_written = 0 337 | struct.s_snapshot_inum = 0 338 | struct.s_snapshot_id = 0 339 | struct.s_snapshot_r_blocks_count = 0 340 | struct.s_snapshot_list = 0 341 | struct.s_error_count = 0 342 | struct.s_first_error_time = 0 343 | struct.s_first_error_ino = 0 344 | struct.s_first_error_block = 0 345 | struct.s_first_error_func = 0 346 | struct.s_first_error_line = 0 347 | struct.s_last_error_time = 0 348 | struct.s_last_error_ino = 0 349 | struct.s_last_error_line = 0 350 | struct.s_last_error_block = 0 351 | struct.s_last_error_func = 0 352 | struct.s_mount_opts = 0 353 | struct.s_usr_quota_inum = 0 354 | struct.s_grp_quota_inum = 0 355 | struct.s_overhead_blocks = 0 356 | struct.s_backup_bgs = 0 357 | struct.s_encrypt_algos = 0 358 | struct.s_encrypt_pw_salt = 0 359 | struct.s_lpf_ino = 0 360 | struct.s_prj_quota_inum = 0 361 | struct.s_checksum_seed = 0 362 | struct.s_reserved = 0 363 | struct.s_checksum = 0 364 | 365 | if (struct.s_feature_incompat & ext4_superblock.INCOMPAT_64BIT) == 0: 366 | struct.s_desc_size = ext4_superblock.EXT2_DESC_SIZE 367 | 368 | return struct 369 | 370 | 371 | class ext4_xattr_entry(ext4_struct): 372 | _fields_ = [ 373 | ("e_name_len", ctypes.c_ubyte), # 0x00 374 | ("e_name_index", ctypes.c_ubyte), # 0x01 375 | ("e_value_offs", ctypes.c_ushort), # 0x02 376 | ("e_value_inum", ctypes.c_uint), # 0x04 377 | ("e_value_size", ctypes.c_uint), # 0x08 378 | ("e_hash", ctypes.c_uint) # 0x0C 379 | # Variable length field "e_name" missing at 0x10 380 | ] 381 | 382 | def _from_buffer_copy(raw, offset=0, platform64=True): 383 | struct = ext4_xattr_entry.from_buffer_copy(raw, offset) 384 | struct.e_name = raw[offset + 0x10: offset + 0x10 + struct.e_name_len] 385 | return struct 386 | 387 | @property 388 | def _size(self): return 4 * ((ctypes.sizeof(type(self)) + self.e_name_len + 3) // 4) # 4-byte alignment 389 | 390 | 391 | class ext4_xattr_header(ext4_struct): 392 | _fields_ = [ 393 | ("h_magic", ctypes.c_uint), # 0x0, Must be 0xEA020000 394 | ("h_refcount", ctypes.c_uint), # 0x4 395 | ("h_blocks", ctypes.c_uint), # 0x8 396 | ("h_hash", ctypes.c_uint), # 0xC 397 | ("h_checksum", ctypes.c_uint), # 0x10 398 | ("h_reserved", ctypes.c_uint * 3), # 0x14 399 | ] 400 | 401 | 402 | class ext4_xattr_ibody_header(ext4_struct): 403 | _fields_ = [ 404 | ("h_magic", ctypes.c_uint) # 0x0, Must be 0xEA020000 405 | ] 406 | 407 | 408 | class InodeType: 409 | UNKNOWN = 0x0 # Unknown file type 410 | FILE = 0x1 # Regular file 411 | DIRECTORY = 0x2 # Directory 412 | CHARACTER_DEVICE = 0x3 # Character device 413 | BLOCK_DEVICE = 0x4 # Block device 414 | FIFO = 0x5 # FIFO 415 | SOCKET = 0x6 # Socket 416 | SYMBOLIC_LINK = 0x7 # Symbolic link 417 | CHECKSUM = 0xDE # Checksum entry; not really a file type, but a type of directory entry 418 | 419 | 420 | # ----------------------------- HIGH LEVEL ------------------------------ 421 | 422 | class MappingEntry: 423 | def __init__(self, file_block_idx, disk_block_idx, block_count=1): 424 | self.file_block_idx = file_block_idx 425 | self.disk_block_idx = disk_block_idx 426 | self.block_count = block_count 427 | 428 | def __iter__(self): 429 | yield self.file_block_idx 430 | yield self.disk_block_idx 431 | yield self.block_count 432 | 433 | def __repr__(self): 434 | return "{type:s}({file_block_idx!r:s}, {disk_block_idx!r:s}, {blocK_count!r:s})".format( 435 | blocK_count=self.block_count, 436 | disk_block_idx=self.disk_block_idx, 437 | file_block_idx=self.file_block_idx, 438 | type=type(self).__name__ 439 | ) 440 | 441 | def copy(self): 442 | return MappingEntry(self.file_block_idx, self.disk_block_idx, self.block_count) 443 | 444 | def create_mapping(*entries): 445 | file_block_idx = 0 446 | result = [None] * len(entries) 447 | 448 | for i, entry in enumerate(entries): 449 | disk_block_idx, block_count = entry 450 | result[i] = MappingEntry(file_block_idx, disk_block_idx, block_count) 451 | file_block_idx += block_count 452 | 453 | return result 454 | 455 | def optimize(entries): 456 | entries.sort(key=lambda entry: entry.file_block_idx) 457 | 458 | idx = 0 459 | while idx < len(entries): 460 | while idx + 1 < len(entries) \ 461 | and entries[idx].file_block_idx + entries[idx].block_count == entries[idx + 1].file_block_idx \ 462 | and entries[idx].disk_block_idx + entries[idx].block_count == entries[idx + 1].disk_block_idx: 463 | tmp = entries.pop(idx + 1) 464 | entries[idx].block_count += tmp.block_count 465 | 466 | idx += 1 467 | 468 | class Volume: 469 | ROOT_INODE = 2 470 | 471 | def __init__(self, stream, offset=0, ignore_flags=False, ignore_magic=False): 472 | self.ignore_flags = ignore_flags 473 | self.ignore_magic = ignore_magic 474 | self.offset = offset 475 | self.platform64 = True # Initial value needed for Volume.read_struct 476 | self.stream = stream 477 | 478 | # Superblock 479 | self.superblock = self.read_struct(ext4_superblock, 0x400) 480 | self.platform64 = (self.superblock.s_feature_incompat & ext4_superblock.INCOMPAT_64BIT) != 0 481 | 482 | if not ignore_magic and self.superblock.s_magic != 0xEF53: 483 | raise MagicError("Invalid magic value in superblock: 0x{magic:04X} (expected 0xEF53)".format( 484 | magic=self.superblock.s_magic)) 485 | 486 | # Group descriptors 487 | self.group_descriptors = [None] * (self.superblock.s_inodes_count // self.superblock.s_inodes_per_group) 488 | 489 | group_desc_table_offset = (0x400 // self.block_size + 1) * self.block_size # First block after superblock 490 | for group_desc_idx in range(len(self.group_descriptors)): 491 | group_desc_offset = group_desc_table_offset + group_desc_idx * self.superblock.s_desc_size 492 | self.group_descriptors[group_desc_idx] = self.read_struct(ext4_group_descriptor, group_desc_offset) 493 | 494 | def __repr__(self): 495 | return "{type_name:s}(volume_name = {volume_name!r:s}, uuid = {uuid!r:s}, last_mounted = {last_mounted!r:s})".format( 496 | last_mounted=self.superblock.s_last_mounted, 497 | type_name=type(self).__name__, 498 | uuid=self.uuid, 499 | volume_name=self.superblock.s_volume_name 500 | ) 501 | 502 | @property 503 | def block_size(self): 504 | return 1 << (10 + self.superblock.s_log_block_size) 505 | 506 | def get_inode(self, inode_idx, file_type=InodeType.UNKNOWN): 507 | group_idx, inode_table_entry_idx = self.get_inode_group(inode_idx) 508 | 509 | inode_table_offset = self.group_descriptors[group_idx].bg_inode_table * self.block_size 510 | inode_offset = inode_table_offset + inode_table_entry_idx * self.superblock.s_inode_size 511 | 512 | return Inode(self, inode_offset, inode_idx, file_type) 513 | 514 | def get_inode_group(self, inode_idx): 515 | group_idx = (inode_idx - 1) // self.superblock.s_inodes_per_group 516 | inode_table_entry_idx = (inode_idx - 1) % self.superblock.s_inodes_per_group 517 | return (group_idx, inode_table_entry_idx) 518 | 519 | def read(self, offset, byte_len): 520 | if self.offset + offset != self.stream.tell(): 521 | self.stream.seek(self.offset + offset, io.SEEK_SET) 522 | 523 | return self.stream.read(byte_len) 524 | 525 | def read_struct(self, structure, offset, platform64=None): 526 | raw = self.read(offset, ctypes.sizeof(structure)) 527 | 528 | if hasattr(structure, "_from_buffer_copy"): 529 | return structure._from_buffer_copy(raw, platform64=platform64 if platform64 != None else self.platform64) 530 | else: 531 | return structure.from_buffer_copy(raw) 532 | 533 | @property 534 | def root(self): 535 | return self.get_inode(Volume.ROOT_INODE, InodeType.DIRECTORY) 536 | 537 | @property 538 | def uuid(self): 539 | uuid = self.superblock.s_uuid 540 | uuid = [uuid[:4], uuid[4: 6], uuid[6: 8], uuid[8: 10], uuid[10:]] 541 | return "-".join("".join("{0:02X}".format(c) for c in part) for part in uuid) 542 | 543 | 544 | class Inode: 545 | def __init__(self, volume, offset, inode_idx, file_type=InodeType.UNKNOWN): 546 | self.inode_idx = inode_idx 547 | self.offset = offset 548 | self.volume = volume 549 | 550 | self.file_type = file_type 551 | self.inode = volume.read_struct(ext4_inode, offset) 552 | 553 | def __len__(self): 554 | return self.inode.i_size 555 | 556 | def __repr__(self): 557 | if self.inode_idx != None: 558 | return "{type_name:s}(inode_idx = {inode!r:s}, offset = 0x{offset:X}, volume_uuid = {uuid!r:s})".format( 559 | inode=self.inode_idx, 560 | offset=self.offset, 561 | type_name=type(self).__name__, 562 | uuid=self.volume.uuid 563 | ) 564 | else: 565 | return "{type_name:s}(offset = 0x{offset:X}, volume_uuid = {uuid!r:s})".format( 566 | offset=self.offset, 567 | type_name=type(self).__name__, 568 | uuid=self.volume.uuid 569 | ) 570 | 571 | def _parse_xattrs(self, raw_data, offset, prefix_override={}): 572 | prefixes = { 573 | 0: "", 574 | 1: "user.", 575 | 2: "system.posix_acl_access", 576 | 3: "system.posix_acl_default", 577 | 4: "trusted.", 578 | 6: "security.", 579 | 7: "system.", 580 | 8: "system.richacl" 581 | } 582 | prefixes.update(prefixes) 583 | 584 | # Iterator over ext4_xattr_entry structures 585 | i = 0 586 | while i < len(raw_data): 587 | xattr_entry = ext4_xattr_entry._from_buffer_copy(raw_data, i, platform64=self.volume.platform64) 588 | 589 | if ( 590 | xattr_entry.e_name_len | xattr_entry.e_name_index | xattr_entry.e_value_offs | xattr_entry.e_value_inum) == 0: 591 | # End of ext4_xattr_entry list 592 | break 593 | 594 | if not xattr_entry.e_name_index in prefixes: 595 | raise Ext4Error("Unknown attribute prefix {prefix:d} in inode {inode:d}".format( 596 | inode=self.inode_idx, 597 | prefix=xattr_entry.e_name_index 598 | )) 599 | 600 | xattr_name = prefixes[xattr_entry.e_name_index] + xattr_entry.e_name.decode("iso-8859-2") 601 | 602 | if xattr_entry.e_value_inum != 0: 603 | # external xattr 604 | xattr_inode = self.volume.get_inode(xattr.e_value_inum, InodeType.FILE) 605 | 606 | if not self.volume.ignore_flags and (xattr_inode.inode.i_flags & ext4_inode.EXT4_EA_INODE_FL) != 0: 607 | raise Ext4Error( 608 | "Inode {value_indoe:d} associated with the extended attribute {xattr_name!r:s} of inode {inode:d} is not marked as large extended attribute value.".format( 609 | inode=self.inode_idx, 610 | value_inode=xattr_inode.inode_idx, 611 | xattr_name=xattr_name 612 | )) 613 | 614 | # TODO Use xattr_entry.e_value_size or xattr_inode.inode.i_size? 615 | xattr_value = xattr_inode.open_read().read() 616 | else: 617 | # internal xattr 618 | xattr_value = raw_data[ 619 | xattr_entry.e_value_offs + offset: xattr_entry.e_value_offs + offset + xattr_entry.e_value_size] 620 | 621 | yield (xattr_name, xattr_value) 622 | 623 | i += xattr_entry._size 624 | 625 | def directory_entry_comparator(dir_a, dir_b): 626 | file_name_a, _, file_type_a = dir_a 627 | file_name_b, _, file_type_b = dir_b 628 | 629 | if file_type_a == InodeType.DIRECTORY == file_type_b or file_type_a != InodeType.DIRECTORY != file_type_b: 630 | tmp = wcscmp(file_name_a.lower(), file_name_b.lower()) 631 | return tmp if tmp != 0 else wcscmp(file_name_a, file_name_b) 632 | else: 633 | return -1 if file_type_a == InodeType.DIRECTORY else 1 634 | 635 | directory_entry_key = functools.cmp_to_key(directory_entry_comparator) 636 | 637 | def get_inode(self, *relative_path, decode_name=None): 638 | if not self.is_dir: 639 | raise Ext4Error("Inode {inode:d} is not a directory.".format(inode=self.inode_idx)) 640 | 641 | current_inode = self 642 | 643 | for i, part in enumerate(relative_path): 644 | if not self.volume.ignore_flags and not current_inode.is_dir: 645 | current_path = "/".join(relative_path[:i]) 646 | raise Ext4Error("{current_path!r:s} (Inode {inode:d}) is not a directory.".format( 647 | current_path=current_path, 648 | inode=inode_idx 649 | )) 650 | 651 | file_name, inode_idx, file_type = next( 652 | filter(lambda entry: entry[0] == part, current_inode.open_dir(decode_name)), (None, None, None)) 653 | 654 | if inode_idx == None: 655 | current_path = "/".join(relative_path[:i]) 656 | raise FileNotFoundError("{part!r:s} not found in {current_path!r:s} (Inode {inode:d}).".format( 657 | current_path=current_path, 658 | inode=current_inode.inode_idx, 659 | part=part 660 | )) 661 | 662 | current_inode = current_inode.volume.get_inode(inode_idx, file_type) 663 | 664 | return current_inode 665 | 666 | @property 667 | def is_dir(self): 668 | if (self.volume.superblock.s_feature_incompat & ext4_superblock.INCOMPAT_FILETYPE) == 0: 669 | return (self.inode.i_mode & ext4_inode.S_IFDIR) != 0 670 | else: 671 | return self.file_type == InodeType.DIRECTORY 672 | 673 | @property 674 | def is_file(self): 675 | if (self.volume.superblock.s_feature_incompat & ext4_superblock.INCOMPAT_FILETYPE) == 0: 676 | return (self.inode.i_mode & ext4_inode.S_IFREG) != 0 677 | else: 678 | return self.file_type == InodeType.FILE 679 | 680 | @property 681 | def is_symlink(self): 682 | if (self.volume.superblock.s_feature_incompat & ext4_superblock.INCOMPAT_FILETYPE) == 0: 683 | return (self.inode.i_mode & ext4_inode.S_IFLNK) != 0 684 | else: 685 | return self.file_type == InodeType.SYMBOLIC_LINK 686 | 687 | @property 688 | def is_in_use(self): 689 | group_idx, bitmap_bit = self.volume.get_inode_group(self.inode_idx) 690 | 691 | inode_usage_bitmap_offset = self.volume.group_descriptors[group_idx].bg_inode_bitmap * self.volume.block_size 692 | inode_usage_byte = self.volume.read(inode_usage_bitmap_offset + bitmap_bit // 8, 1)[0] 693 | 694 | return ((inode_usage_byte >> (7 - bitmap_bit % 8)) & 1) != 0 695 | 696 | @property 697 | def mode_str(self): 698 | special_flag = lambda letter, execute, special: { 699 | (False, False): "-", 700 | (False, True): letter.upper(), 701 | (True, False): "x", 702 | (True, True): letter.lower() 703 | }[(execute, special)] 704 | 705 | try: 706 | if (self.volume.superblock.s_feature_incompat & ext4_superblock.INCOMPAT_FILETYPE) == 0: 707 | device_type = { 708 | ext4_inode.S_IFIFO: "p", 709 | ext4_inode.S_IFCHR: "c", 710 | ext4_inode.S_IFDIR: "d", 711 | ext4_inode.S_IFBLK: "b", 712 | ext4_inode.S_IFREG: "-", 713 | ext4_inode.S_IFLNK: "l", 714 | ext4_inode.S_IFSOCK: "s", 715 | }[self.inode.i_mode & 0xF000] 716 | else: 717 | device_type = { 718 | InodeType.FILE: "-", 719 | InodeType.DIRECTORY: "d", 720 | InodeType.CHARACTER_DEVICE: "c", 721 | InodeType.BLOCK_DEVICE: "b", 722 | InodeType.FIFO: "p", 723 | InodeType.SOCKET: "s", 724 | InodeType.SYMBOLIC_LINK: "l" 725 | }[self.file_type] 726 | except KeyError: 727 | device_type = "?" 728 | 729 | return "".join([ 730 | device_type, 731 | 732 | "r" if (self.inode.i_mode & ext4_inode.S_IRUSR) != 0 else "-", 733 | "w" if (self.inode.i_mode & ext4_inode.S_IWUSR) != 0 else "-", 734 | special_flag("s", (self.inode.i_mode & ext4_inode.S_IXUSR) != 0, 735 | (self.inode.i_mode & ext4_inode.S_ISUID) != 0), 736 | 737 | "r" if (self.inode.i_mode & ext4_inode.S_IRGRP) != 0 else "-", 738 | "w" if (self.inode.i_mode & ext4_inode.S_IWGRP) != 0 else "-", 739 | special_flag("s", (self.inode.i_mode & ext4_inode.S_IXGRP) != 0, 740 | (self.inode.i_mode & ext4_inode.S_ISGID) != 0), 741 | 742 | "r" if (self.inode.i_mode & ext4_inode.S_IROTH) != 0 else "-", 743 | "w" if (self.inode.i_mode & ext4_inode.S_IWOTH) != 0 else "-", 744 | special_flag("t", (self.inode.i_mode & ext4_inode.S_IXOTH) != 0, 745 | (self.inode.i_mode & ext4_inode.S_ISVTX) != 0), 746 | ]) 747 | 748 | def open_dir(self, decode_name=None): 749 | # Parse args 750 | if decode_name == None: 751 | decode_name = lambda raw: raw.decode("utf8") 752 | 753 | if not self.volume.ignore_flags and not self.is_dir: 754 | raise Ext4Error("Inode ({inode:d}) is not a directory.".format(inode=self.inode_idx)) 755 | 756 | # # Hash trees are compatible with linear arrays 757 | if (self.inode.i_flags & ext4_inode.EXT4_INDEX_FL) != 0: 758 | pass 759 | 760 | # Read raw directory content 761 | raw_data = self.open_read().read() 762 | offset = 0 763 | 764 | while offset < len(raw_data): 765 | dirent = ext4_dir_entry_2._from_buffer_copy(raw_data, offset, platform64=self.volume.platform64) 766 | 767 | if dirent.file_type != InodeType.CHECKSUM: 768 | yield (decode_name(dirent.name), dirent.inode, dirent.file_type) 769 | 770 | offset += dirent.rec_len 771 | 772 | def open_read(self): 773 | if (self.inode.i_flags & ext4_inode.EXT4_EXTENTS_FL) != 0: 774 | # Obtain mapping from extents 775 | mapping = [] # List of MappingEntry instances 776 | 777 | nodes = queue.Queue() 778 | nodes.put_nowait(self.offset + ext4_inode.i_block.offset) 779 | 780 | while nodes.qsize() != 0: 781 | header_offset = nodes.get_nowait() 782 | header = self.volume.read_struct(ext4_extent_header, header_offset) 783 | 784 | if not self.volume.ignore_magic and header.eh_magic != 0xF30A: 785 | raise MagicError( 786 | "Invalid magic value in extent header at offset 0x{header_offset:X} of inode {inode:d}: 0x{header_magic:04X} (expected 0xF30A)".format( 787 | header_magic=header.eh_magic, 788 | header_offset=self.inode_idx, 789 | inode=self.inode_idx 790 | )) 791 | 792 | if header.eh_depth != 0: 793 | indices = self.volume.read_struct(ext4_extent_idx * header.eh_entries, 794 | header_offset + ctypes.sizeof(ext4_extent_header)) 795 | for idx in indices: nodes.put_nowait(idx.ei_leaf * self.volume.block_size) 796 | else: 797 | extents = self.volume.read_struct(ext4_extent * header.eh_entries, 798 | header_offset + ctypes.sizeof(ext4_extent_header)) 799 | for extent in extents: 800 | mapping.append(MappingEntry(extent.ee_block, extent.ee_start, extent.ee_len)) 801 | 802 | MappingEntry.optimize(mapping) 803 | return BlockReader(self.volume, len(self), mapping) 804 | else: 805 | # Inode uses inline data 806 | i_block = self.volume.read(self.offset + ext4_inode.i_block.offset, ext4_inode.i_block.size) 807 | return io.BytesIO(i_block[:self.inode.i_size]) 808 | 809 | @property 810 | def size_readable(self): 811 | if self.inode.i_size < 1024: 812 | return "{0:d} bytes".format(self.inode.i_size) if self.inode.i_size != 1 else "1 byte" 813 | else: 814 | units = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] 815 | unit_idx = min(int(math.log(self.inode.i_size, 1024)), len(units)) 816 | 817 | return "{size:.2f} {unit:s}".format( 818 | size=self.inode.i_size / (1024 ** unit_idx), 819 | unit=units[unit_idx - 1] 820 | ) 821 | 822 | def xattrs(self, check_inline=True, check_block=True, force_inline=False, prefix_override={}): 823 | # Inline xattrs 824 | inline_data_offset = self.offset + ext4_inode.EXT2_GOOD_OLD_INODE_SIZE + self.inode.i_extra_isize 825 | inline_data_length = self.offset + self.volume.superblock.s_inode_size - inline_data_offset 826 | 827 | if check_inline and inline_data_length > ctypes.sizeof(ext4_xattr_ibody_header): 828 | inline_data = self.volume.read(inline_data_offset, inline_data_length) 829 | xattrs_header = ext4_xattr_ibody_header.from_buffer_copy(inline_data) 830 | 831 | # TODO Find way to detect inline xattrs without checking the h_magic field to enable error detection with the h_magic field. 832 | if force_inline or xattrs_header.h_magic == 0xEA020000: 833 | offset = 4 * ((ctypes.sizeof( 834 | ext4_xattr_ibody_header) + 3) // 4) # The ext4_xattr_entry following the header is aligned on a 4-byte boundary 835 | try: 836 | for xattr_name, xattr_value in self._parse_xattrs(inline_data[offset:], 0, 837 | prefix_override=prefix_override): 838 | yield (xattr_name, xattr_value) 839 | except: 840 | pass 841 | # xattr block(s) 842 | if check_block and self.inode.i_file_acl != 0: 843 | xattrs_block_start = self.inode.i_file_acl * self.volume.block_size 844 | xattrs_block = self.volume.read(xattrs_block_start, self.volume.block_size) 845 | 846 | xattrs_header = ext4_xattr_header.from_buffer_copy(xattrs_block) 847 | if not self.volume.ignore_magic and xattrs_header.h_magic != 0xEA020000: 848 | try: 849 | raise MagicError( 850 | "Invalid magic value in xattrs block header at offset 0x{xattrs_block_start:X} of inode {inode:d}: 0x{xattrs_header} (expected 0xEA020000)".format( 851 | inode=self.inode_idx, 852 | xattrs_block_start=xattrs_block_start, 853 | xattrs_header=xattrs_header.h_magic 854 | )) 855 | except: 856 | pass 857 | 858 | if xattrs_header.h_blocks != 1: 859 | raise Ext4Error( 860 | "Invalid number of xattr blocks at offset 0x{xattrs_block_start:X} of inode {inode:d}: {xattrs_header:d} (expected 1)".format( 861 | inode=self.inode_idx, 862 | xattrs_header=xattrs_header.h_blocks, 863 | xattrs_block_start=xattrs_block_start 864 | )) 865 | 866 | offset = 4 * ((ctypes.sizeof( 867 | ext4_xattr_header) + 3) // 4) # The ext4_xattr_entry following the header is aligned on a 4-byte boundary 868 | for xattr_name, xattr_value in self._parse_xattrs(xattrs_block[offset:], -offset, 869 | prefix_override=prefix_override): 870 | yield (xattr_name, xattr_value) 871 | 872 | 873 | class BlockReader: 874 | # OSError 875 | EINVAL = 22 876 | 877 | def __init__(self, volume, byte_size, block_map): 878 | self.byte_size = byte_size 879 | self.volume = volume 880 | 881 | self.cursor = 0 882 | 883 | block_map = list(map(MappingEntry.copy, block_map)) 884 | 885 | # Optimize mapping (stich together) 886 | MappingEntry.optimize(block_map) 887 | self.block_map = block_map 888 | 889 | def __repr__(self): 890 | return "{type_name:s}(byte_size = {size!r:s}, block_map = {block_map!r:s}, volume_uuid = {uuid!r:s})".format( 891 | block_map=self.block_map, 892 | size=self.byte_size, 893 | type_name=type(self).__name__, 894 | uuid=self.volume.uuid 895 | ) 896 | 897 | def get_block_mapping(self, file_block_idx): 898 | disk_block_idx = None 899 | 900 | # Find disk block 901 | for entry in self.block_map: 902 | if entry.file_block_idx <= file_block_idx < entry.file_block_idx + entry.block_count: 903 | block_diff = file_block_idx - entry.file_block_idx 904 | disk_block_idx = entry.disk_block_idx + block_diff 905 | break 906 | 907 | return disk_block_idx 908 | 909 | def read(self, byte_len=-1): 910 | # Parse args 911 | if byte_len < -1: raise ValueError("byte_len must be non-negative or -1") 912 | 913 | bytes_remaining = self.byte_size - self.cursor 914 | byte_len = bytes_remaining if byte_len == -1 else max(0, min(byte_len, bytes_remaining)) 915 | 916 | if byte_len == 0: return b"" 917 | 918 | # Reading blocks 919 | start_block_idx = self.cursor // self.volume.block_size 920 | end_block_idx = (self.cursor + byte_len - 1) // self.volume.block_size 921 | end_of_stream_check = byte_len 922 | 923 | blocks = [self.read_block(i) for i in range(start_block_idx, end_block_idx - start_block_idx + 1)] 924 | 925 | start_offset = self.cursor % self.volume.block_size 926 | if start_offset != 0: blocks[0] = blocks[0][start_offset:] 927 | byte_len = (byte_len + start_offset - self.volume.block_size - 1) % self.volume.block_size + 1 928 | blocks[-1] = blocks[-1][:byte_len] 929 | 930 | result = b"".join(blocks) 931 | 932 | # Check read 933 | if len(result) != end_of_stream_check: 934 | raise EndOfStreamError( 935 | "The volume's underlying stream ended {0:d} bytes before EOF.".format(byte_len - len(result))) 936 | 937 | self.cursor += len(result) 938 | return result 939 | 940 | def read_block(self, file_block_idx): 941 | disk_block_idx = self.get_block_mapping(file_block_idx) 942 | 943 | if disk_block_idx != None: 944 | return self.volume.read(disk_block_idx * self.volume.block_size, self.volume.block_size) 945 | else: 946 | return bytes([0] * self.volume.block_size) 947 | 948 | def seek(self, seek, seek_mode=io.SEEK_SET): 949 | if seek_mode == io.SEEK_CUR: 950 | seek += self.cursor 951 | elif seek_mode == io.SEEK_END: 952 | seek += self.byte_size 953 | # elif seek_mode == io.SEEK_SET: 954 | # seek += 0 955 | 956 | if seek < 0: 957 | raise OSError(BlockReader.EINVAL, "Invalid argument") # Exception behavior copied from IOBase.seek 958 | 959 | self.cursor = seek 960 | return seek 961 | 962 | def tell(self): 963 | return self.cursor 964 | -------------------------------------------------------------------------------- /bin/imgextractor/imgextractor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import struct 4 | import traceback 5 | import shutil 6 | import re 7 | import mmap 8 | 9 | EXT4_HEADER_MAGIC = 0xED26FF3A 10 | EXT4_SPARSE_HEADER_LEN = 28 11 | EXT4_CHUNK_HEADER_SIZE = 12 12 | 13 | 14 | class ext4_file_header(object): 15 | def __init__(self, buf): 16 | (self.magic, 17 | self.major, 18 | self.minor, 19 | self.file_header_size, 20 | self.chunk_header_size, 21 | self.block_size, 22 | self.total_blocks, 23 | self.total_chunks, 24 | self.crc32) = struct.unpack(' 10: 77 | return 78 | if len(arg) > 8: 79 | arg = arg[1:] 80 | oor, ow, ox, gr, gw, gx, wr, ww, wx = list(arg) 81 | o, g, w, s = 0, 0, 0, 0 82 | if oor == 'r': o += 4 83 | if ow == 'w': o += 2 84 | if ox == 'x': o += 1 85 | if ox == 'S': s += 4 86 | if ox == 's': s += 4; o += 1 87 | if gr == 'r': g += 4 88 | if gw == 'w': g += 2 89 | if gx == 'x': g += 1 90 | if gx == 'S': s += 2 91 | if gx == 's': s += 2; g += 1 92 | if wr == 'r': w += 4 93 | if ww == 'w': w += 2 94 | if wx == 'x': w += 1 95 | if wx == 'T': s += 1 96 | if wx == 't': s += 1; w += 1 97 | return str(s) + str(o) + str(g) + str(w) 98 | 99 | def __ext4extractor(self): 100 | import ext4, string, struct 101 | fs_config_file = self.FileName + '_fs_config' 102 | fuking_symbols='\\^$.|?*+(){}[]' 103 | contexts = self.CONFING_DIR + os.sep + self.FileName + "_file_contexts" #08.05.18 104 | def scan_dir(root_inode, root_path=""): 105 | for entry_name, entry_inode_idx, entry_type in root_inode.open_dir(): 106 | if entry_name in ['.', '..'] or entry_name.endswith(' (2)'): 107 | continue 108 | entry_inode = root_inode.volume.get_inode(entry_inode_idx, entry_type) 109 | entry_inode_path = root_path + '/' + entry_name 110 | mode = self.__getperm(entry_inode.mode_str) 111 | uid = entry_inode.inode.i_uid 112 | gid = entry_inode.inode.i_gid 113 | con = '' 114 | cap = '' 115 | for i in list(entry_inode.xattrs()): 116 | if i[0] == 'security.selinux': 117 | con = i[1].decode('utf8')[:-1] 118 | elif i[0] == 'security.capability': 119 | raw_cap = struct.unpack("<5I", i[1]) 120 | if raw_cap[1] > 65535: 121 | cap = '' + str(hex(int('%04x%04x' % (raw_cap[3], raw_cap[1]), 16))) 122 | else: 123 | cap = '' + str(hex(int('%04x%04x%04x' % (raw_cap[3], raw_cap[2], raw_cap[1]), 16))) 124 | cap = ' capabilities={cap}'.format(cap=cap) 125 | if entry_inode.is_dir: 126 | dir_target = self.EXTRACT_DIR + entry_inode_path.replace(' ','_').replace('"','') 127 | if not os.path.isdir(dir_target): 128 | os.makedirs(dir_target) 129 | if os.name == 'posix': 130 | os.chmod(dir_target, int(mode, 8)) 131 | os.chown(dir_target, uid, gid) 132 | scan_dir(entry_inode, entry_inode_path) 133 | if cap == '' and con == '': 134 | tmppath=self.DIR + entry_inode_path 135 | if (tmppath).find(' ',1,len(tmppath))>0: 136 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 137 | if not os.path.isfile(spaces_file): 138 | f = open(spaces_file, 'tw', encoding='utf-8') 139 | self.__appendf(tmppath, spaces_file) 140 | f.close 141 | else: 142 | self.__appendf(tmppath, spaces_file) 143 | tmppath=tmppath.replace(' ', '_') 144 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode)) 145 | else: 146 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode)) 147 | else: 148 | if cap == '': 149 | tmppath=self.DIR + entry_inode_path 150 | if (tmppath).find(' ',1,len(tmppath))>0: 151 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 152 | if not os.path.isfile(spaces_file): 153 | f = open(spaces_file, 'tw', encoding='utf-8') 154 | self.__appendf(tmppath, spaces_file) 155 | f.close 156 | else: 157 | self.__appendf(tmppath, spaces_file) 158 | tmppath=tmppath.replace(' ', '_') 159 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode)) 160 | else: 161 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode)) 162 | for fuk_symb in fuking_symbols: 163 | tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 164 | self.context.append('/%s %s' % (tmppath, con)) 165 | else: 166 | if con == '': 167 | tmppath=self.DIR + entry_inode_path 168 | if (tmppath).find(' ',1,len(tmppath))>0: 169 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 170 | if not os.path.isfile(spaces_file): 171 | f = open(spaces_file, 'tw', encoding='utf-8') 172 | self.__appendf(tmppath, spaces_file) 173 | f.close 174 | else: 175 | self.__appendf(tmppath, spaces_file) 176 | tmppath=tmppath.replace(' ', '_') 177 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode + cap)) 178 | else: 179 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap)) 180 | else: 181 | tmppath=self.DIR + entry_inode_path 182 | if (tmppath).find(' ',1,len(tmppath))>0: 183 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 184 | if not os.path.isfile(spaces_file): 185 | f = open(spaces_file, 'tw', encoding='utf-8') 186 | self.__appendf(tmppath, spaces_file) 187 | f.close 188 | else: 189 | self.__appendf(tmppath, spaces_file) 190 | tmppath=tmppath.replace(' ', '_') 191 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode + cap)) 192 | else: 193 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap)) 194 | for fuk_symb in fuking_symbols: 195 | tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 196 | self.context.append('/%s %s' % (tmppath, con)) 197 | elif entry_inode.is_file: 198 | raw = entry_inode.open_read().read() 199 | wdone = None 200 | if os.name == 'nt': 201 | if entry_name.endswith('/'): 202 | entry_name = entry_name[:-1] 203 | file_target = self.EXTRACT_DIR + entry_inode_path.replace('/', os.sep).replace(' ','_').replace('"','') 204 | if not os.path.isdir(os.path.dirname(file_target)): 205 | os.makedirs(os.path.dirname(file_target)) 206 | with open(file_target, 'wb') as out: 207 | out.write(raw) 208 | if os.name == 'posix': 209 | file_target = self.EXTRACT_DIR + entry_inode_path.replace(' ','_').replace('"','') 210 | if not os.path.isdir(os.path.dirname(file_target)): 211 | os.makedirs(os.path.dirname(file_target)) 212 | with open(file_target, 'wb') as out: 213 | out.write(raw) 214 | os.chmod(file_target, int(mode, 8)) 215 | os.chown(file_target, uid, gid) 216 | if cap == '' and con == '': 217 | tmppath=self.DIR + entry_inode_path 218 | if (tmppath).find(' ',1,len(tmppath))>0: 219 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 220 | if not os.path.isfile(spaces_file): 221 | f = open(spaces_file, 'tw', encoding='utf-8') 222 | self.__appendf(tmppath, spaces_file) 223 | f.close 224 | else: 225 | self.__appendf(tmppath, spaces_file) 226 | tmppath=tmppath.replace(' ', '_') 227 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode)) 228 | else: 229 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode)) 230 | else: 231 | if cap == '': 232 | tmppath=self.DIR + entry_inode_path 233 | if (tmppath).find(' ',1,len(tmppath))>0: 234 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 235 | if not os.path.isfile(spaces_file): 236 | f = open(spaces_file, 'tw', encoding='utf-8') 237 | self.__appendf(tmppath, spaces_file) 238 | f.close 239 | else: 240 | self.__appendf(tmppath, spaces_file) 241 | tmppath=tmppath.replace(' ', '_') 242 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode)) 243 | else: 244 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode)) 245 | for fuk_symb in fuking_symbols: 246 | tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 247 | self.context.append('/%s %s' % (tmppath, con)) 248 | else: 249 | if con == '': 250 | tmppath=self.DIR + entry_inode_path 251 | if (tmppath).find(' ',1,len(tmppath))>0: 252 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 253 | if not os.path.isfile(spaces_file): 254 | f = open(spaces_file, 'tw', encoding='utf-8') 255 | self.__appendf(tmppath, spaces_file) 256 | f.close 257 | else: 258 | self.__appendf(tmppath, spaces_file) 259 | tmppath=tmppath.replace(' ', '_') 260 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode + cap)) 261 | else: 262 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap)) 263 | else: 264 | tmppath=self.DIR + entry_inode_path 265 | if (tmppath).find(' ',1,len(tmppath))>0: 266 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 267 | if not os.path.isfile(spaces_file): 268 | f = open(spaces_file, 'tw', encoding='utf-8') 269 | self.__appendf(tmppath, spaces_file) 270 | f.close 271 | else: 272 | self.__appendf(tmppath, spaces_file) 273 | tmppath=tmppath.replace(' ', '_') 274 | self.fsconfig.append('%s %s %s %s' % (tmppath, uid, gid, mode + cap)) 275 | else: 276 | self.fsconfig.append('%s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap)) 277 | for fuk_symb in fuking_symbols: 278 | tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 279 | self.context.append('/%s %s' % (tmppath, con)) 280 | elif entry_inode.is_symlink: 281 | try: 282 | link_target = entry_inode.open_read().read().decode("utf8") 283 | target = self.EXTRACT_DIR + entry_inode_path.replace(' ', '_') 284 | if cap == '' and con == '': 285 | tmppath=self.DIR + entry_inode_path 286 | if (tmppath).find(' ',1,len(tmppath))>0: 287 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 288 | if not os.path.isfile(spaces_file): 289 | f = open(spaces_file, 'tw', encoding='utf-8') 290 | self.__appendf(tmppath, spaces_file) 291 | f.close 292 | else: 293 | self.__appendf(tmppath, spaces_file) 294 | tmppath=tmppath.replace(' ', '_') 295 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode, link_target)) 296 | else: 297 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode, link_target)) 298 | else: 299 | if cap == '': 300 | tmppath=self.DIR + entry_inode_path 301 | if (tmppath).find(' ',1,len(tmppath))>0: 302 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 303 | if not os.path.isfile(spaces_file): 304 | f = open(spaces_file, 'tw', encoding='utf-8') 305 | self.__appendf(tmppath, spaces_file) 306 | f.close 307 | else: 308 | self.__appendf(tmppath, spaces_file) 309 | tmppath=tmppath.replace(' ', '_') 310 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode, link_target)) 311 | else: 312 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode, link_target)) 313 | for fuk_symb in fuking_symbols: 314 | tmppath=tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 315 | self.context.append('/%s %s' % (tmppath, con)) 316 | else: 317 | if con == '': 318 | tmppath=self.DIR + entry_inode_path 319 | if (tmppath).find(' ',1,len(tmppath))>0: 320 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 321 | if not os.path.isfile(spaces_file): 322 | f = open(spaces_file, 'tw', encoding='utf-8') 323 | self.__appendf(tmppath, spaces_file) 324 | f.close 325 | else: 326 | self.__appendf(tmppath, spaces_file) 327 | tmppath=tmppath.replace(' ', '_') 328 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode + cap, link_target)) 329 | else: 330 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap, link_target)) 331 | else: 332 | tmppath=self.DIR + entry_inode_path 333 | if (tmppath).find(' ',1,len(tmppath))>0: 334 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 335 | if not os.path.isfile(spaces_file): 336 | f = open(spaces_file, 'tw', encoding='utf-8') 337 | self.__appendf(tmppath, spaces_file) 338 | f.close 339 | else: 340 | self.__appendf(tmppath, spaces_file) 341 | tmppath=tmppath.replace(' ', '_') 342 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode + cap, link_target)) 343 | else: 344 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap, link_target)) 345 | for fuk_symb in fuking_symbols: 346 | tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 347 | self.context.append('/%s %s' % (tmppath, con)) 348 | if os.path.islink(target): 349 | try: 350 | os.remove(target) 351 | except: 352 | pass 353 | if os.path.isfile(target): 354 | try: 355 | os.remove(target) 356 | except: 357 | pass 358 | if os.name == 'posix': 359 | os.symlink(link_target, target) 360 | if os.name == 'nt': 361 | with open(target.replace('/', os.sep), 'wb') as out: 362 | tmp = bytes.fromhex('213C73796D6C696E6B3EFFFE') 363 | for index in list(link_target): 364 | tmp = tmp + struct.pack('>sx', index.encode('utf-8')) 365 | out.write(tmp + struct.pack('xx')) 366 | os.system('attrib +s "%s"' % target.replace('/', os.sep)) 367 | if not all(c in string.printable for c in link_target): 368 | pass 369 | if entry_inode_path[1:] == entry_name or link_target[1:] == entry_name: 370 | self.symlinks.append('%s %s' % (link_target, entry_inode_path[1:])) 371 | else: 372 | self.symlinks.append('%s %s' % (link_target, self.DIR + entry_inode_path)) 373 | except: 374 | try: 375 | link_target_block = int.from_bytes(entry_inode.open_read().read(), "little") 376 | link_target = root_inode.volume.read(link_target_block * root_inode.volume.block_size, entry_inode.inode.i_size).decode("utf8") 377 | target = self.EXTRACT_DIR + entry_inode_path.replace(' ', '_') 378 | if link_target and all(c in string.printable for c in link_target): 379 | if cap == '' and con == '': 380 | tmppath=self.DIR + entry_inode_path 381 | if (tmppath).find(' ',1,len(tmppath))>0: 382 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 383 | if not os.path.isfile(spaces_file): 384 | f = open(spaces_file, 'tw', encoding='utf-8') 385 | self.__appendf(tmppath, spaces_file) 386 | f.close 387 | else: 388 | self.__appendf(tmppath, spaces_file) 389 | tmppath=tmppath.replace(' ', '_') 390 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode, link_target)) 391 | else: 392 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode, link_target)) 393 | else: 394 | if cap == '': 395 | tmppath=self.DIR + entry_inode_path 396 | if (tmppath).find(' ',1,len(tmppath))>0: 397 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 398 | if not os.path.isfile(spaces_file): 399 | f = open(spaces_file, 'tw', encoding='utf-8') 400 | self.__appendf(tmppath, spaces_file) 401 | f.close 402 | else: 403 | self.__appendf(tmppath, spaces_file) 404 | tmppath=tmppath.replace(' ', '_') 405 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode, link_target)) 406 | else: 407 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode, link_target)) 408 | for fuk_symb in fuking_symbols: 409 | tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 410 | self.context.append('/%s %s' % (tmppath, con)) 411 | else: 412 | if con == '': 413 | tmppath=self.DIR + entry_inode_path 414 | if (tmppath).find(' ',1,len(tmppath))>0: 415 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 416 | if not os.path.isfile(spaces_file): 417 | f = open(spaces_file, 'tw', encoding='utf-8') 418 | self.__appendf(tmppath, spaces_file) 419 | f.close 420 | else: 421 | self.__appendf(tmppath, spaces_file) 422 | tmppath=tmppath.replace(' ', '_') 423 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode + cap, link_target)) 424 | else: 425 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap, link_target)) 426 | else: 427 | tmppath=self.DIR + entry_inode_path 428 | if (tmppath).find(' ',1,len(tmppath))>0: 429 | spaces_file=self.BASE_MYDIR + 'config' + os.sep + self.FileName + '_space.txt' 430 | if not os.path.isfile(spaces_file): 431 | f = open(spaces_file, 'tw', encoding='utf-8') 432 | self.__appendf(tmppath, spaces_file) 433 | f.close 434 | else: 435 | self.__appendf(tmppath, spaces_file) 436 | tmppath=tmppath.replace(' ', '_') 437 | self.fsconfig.append('%s %s %s %s %s' % (tmppath, uid, gid, mode + cap, link_target)) 438 | else: 439 | self.fsconfig.append('%s %s %s %s %s' % (self.DIR + entry_inode_path, uid, gid, mode + cap, link_target)) 440 | for fuk_symb in fuking_symbols: 441 | tmppath=tmppath.replace(fuk_symb, '\\'+fuk_symb) 442 | self.context.append('/%s %s' % (tmppath, con)) 443 | if os.name == 'posix': 444 | os.symlink(link_target, target) 445 | if os.name == 'nt': 446 | with open(target.replace('/', os.sep), 'wb') as out: 447 | tmp = bytes.fromhex('213C73796D6C696E6B3EFFFE') 448 | for index in list(link_target): 449 | tmp = tmp + struct.pack('>sx', index.encode('utf-8')) 450 | out.write(tmp + struct.pack('xx')) 451 | os.system('attrib +s %s' % target.replace('/', os.sep)) 452 | else: 453 | pass 454 | except: 455 | pass 456 | 457 | dir_my = self.CONFING_DIR + os.sep 458 | if not os.path.isdir(dir_my): 459 | os.makedirs(dir_my) 460 | # f = open(dir_my + self.FileName + '_pack.sh', 'tw', encoding='utf-8') 461 | # self.__appendf('make_ext4fs -T -1 -S ./file_contexts -C ./fs_config -l ' +str(os.path.getsize(self.OUTPUT_IMAGE_FILE))+ ' -a /'+self.FileName+' "$outdir"/'+self.FileName+'.new.img '+self.FileName+'', dir_my + self.FileName + '_pack.sh') 462 | # f.close() 463 | # f = open(dir_my + self.FileName + '_pack_sparse.sh', 'tw', encoding='utf-8') 464 | # self.__appendf('make_ext4fs -s -T -1 -S ./file_contexts -C ./fs_config -l ' +str(os.path.getsize(self.OUTPUT_IMAGE_FILE))+ ' -a /'+self.FileName+' "$outdir"/'+self.FileName+'.new.img '+self.FileName+'', dir_my + self.FileName + '_pack_sparse.sh') 465 | # f.close() 466 | f = open(dir_my + self.FileName + '_size.txt', 'tw', encoding='utf-8') 467 | self.__appendf(os.path.getsize(self.OUTPUT_IMAGE_FILE), dir_my + self.FileName + '_size.txt') 468 | f.close() 469 | # f = open(dir_my + self.FileName + '_name.txt', 'tw', encoding='utf-8') 470 | # self.__appendf(os.path.basename(self.OUTPUT_IMAGE_FILE).replace(".img", ""), dir_my + self.FileName + '_name.txt') 471 | # f.close() 472 | with open(self.OUTPUT_IMAGE_FILE, 'rb') as file: 473 | root = ext4.Volume(file).root 474 | dirlist = [] 475 | for file_name, inode_idx, file_type in root.open_dir(): 476 | dirlist.append(file_name) 477 | dirr = self.__file_name(os.path.basename(self.OUTPUT_IMAGE_FILE).split('.')[0]) #11.05.18 478 | setattr(self, 'DIR', dirr) 479 | scan_dir(root) 480 | for c in self.fsconfig: 481 | if dirr == 'vendor': 482 | self.fsconfig.insert(0, '/' + ' 0 2000 0755') 483 | self.fsconfig.insert(1, dirr + ' 0 2000 0755') 484 | elif dirr == 'system': 485 | self.fsconfig.insert(0, '/' + ' 0 0 0755') 486 | self.fsconfig.insert(1, '/' + 'lost+found' + ' 0 0 0700') 487 | self.fsconfig.insert(2, dirr + ' 0 0 0755') 488 | else: 489 | self.fsconfig.insert(0, '/' + ' 0 0 0755') 490 | self.fsconfig.insert(1, dirr + ' 0 0 0755') 491 | break 492 | 493 | self.__appendf('\n'.join(self.fsconfig), self.CONFING_DIR + os.sep + fs_config_file) 494 | if self.context: #11.05.18 495 | self.context.sort() #11.05.18 496 | for c in self.context: 497 | if re.search('lost..found', c): 498 | self.context.insert(0, '/' + ' ' + c.split(" ")[1]) 499 | self.context.insert(1, '/' + dirr +'(/.*)? ' + c.split(" ")[1]) 500 | self.context.insert(2, '/' + dirr + ' ' + c.split(" ")[1]) 501 | self.context.insert(3, '/' + dirr + '/lost\+found' + ' ' + c.split(" ")[1]) 502 | break 503 | 504 | for c in self.context: 505 | if re.search('/system/system/build..prop ', c): 506 | self.context.insert(3, '/lost\+found' + ' u:object_r:rootfs:s0') 507 | self.context.insert(4, '/' + dirr + '/' + dirr + '(/.*)? ' + c.split(" ")[1]) 508 | break 509 | self.__appendf('\n'.join(self.context), contexts) #11.05.18 510 | 511 | def __converSimgToImg(self, target): 512 | with open(target, "rb") as img_file: 513 | if self.sign_offset > 0: 514 | img_file.seek(self.sign_offset, 0) 515 | header = ext4_file_header(img_file.read(28)) 516 | total_chunks = header.total_chunks 517 | if header.file_header_size > EXT4_SPARSE_HEADER_LEN: 518 | img_file.seek(header.file_header_size - EXT4_SPARSE_HEADER_LEN, 1) 519 | with open(target.replace(".img", ".raw.img"), "wb") as raw_img_file: 520 | sector_base = 82528 521 | output_len = 0 522 | while total_chunks > 0: 523 | chunk_header = ext4_chunk_header(img_file.read(EXT4_CHUNK_HEADER_SIZE)) 524 | sector_size = (chunk_header.chunk_size * header.block_size) >> 9 525 | chunk_data_size = chunk_header.total_size - header.chunk_header_size 526 | if chunk_header.type == 0xCAC1: # CHUNK_TYPE_RAW 527 | if header.chunk_header_size > EXT4_CHUNK_HEADER_SIZE: 528 | img_file.seek(header.chunk_header_size - EXT4_CHUNK_HEADER_SIZE, 1) 529 | data = img_file.read(chunk_data_size) 530 | len_data = len(data) 531 | if len_data == (sector_size << 9): 532 | raw_img_file.write(data) 533 | output_len += len_data 534 | sector_base += sector_size 535 | else: 536 | if chunk_header.type == 0xCAC2: # CHUNK_TYPE_FILL 537 | if header.chunk_header_size > EXT4_CHUNK_HEADER_SIZE: 538 | img_file.seek(header.chunk_header_size - EXT4_CHUNK_HEADER_SIZE, 1) 539 | data = img_file.read(chunk_data_size) 540 | len_data = sector_size << 9 541 | raw_img_file.write(struct.pack("B", 0) * len_data) 542 | output_len += len(data) 543 | sector_base += sector_size 544 | else: 545 | if chunk_header.type == 0xCAC3: # CHUNK_TYPE_DONT_CARE 546 | if header.chunk_header_size > EXT4_CHUNK_HEADER_SIZE: 547 | img_file.seek(header.chunk_header_size - EXT4_CHUNK_HEADER_SIZE, 1) 548 | data = img_file.read(chunk_data_size) 549 | len_data = sector_size << 9 550 | raw_img_file.write(struct.pack("B", 0) * len_data) 551 | output_len += len(data) 552 | sector_base += sector_size 553 | else: 554 | len_data = sector_size << 9 555 | raw_img_file.write(struct.pack("B", 0) * len_data) 556 | sector_base += sector_size 557 | total_chunks -= 1 558 | self.OUTPUT_IMAGE_FILE = target.replace(".img", ".raw.img") 559 | 560 | def fixmoto(self, input_file): 561 | if os.path.exists(input_file) == False: 562 | return 563 | output_file=input_file + "_" 564 | if os.path.exists(output_file) == True: 565 | try: 566 | os.remove(output_file) 567 | except: 568 | pass 569 | with open(input_file, 'rb') as f: 570 | data = f.read(500000) 571 | moto = re.search(b'\x4d\x4f\x54\x4f', data) 572 | if not moto: 573 | return 574 | result = [] 575 | for i in re.finditer(b'\x53\xEF', data): 576 | result.append(i.start() - 1080) 577 | offset = 0 578 | for i in result: 579 | if data[i] == 0: 580 | offset = i 581 | break 582 | if offset > 0: 583 | with open(output_file, 'wb') as o, open(input_file, 'rb') as f: 584 | data = f.seek(offset) 585 | data = f.read(15360) 586 | if data: 587 | devnull = o.write(data) 588 | try: 589 | os.remove(input_file) 590 | os.rename(output_file, input_file) 591 | except: 592 | pass 593 | 594 | def checkSignOffset(self, file): 595 | size=os.stat(file.name).st_size 596 | if size <= 52428800: 597 | mm = mmap.mmap(file.fileno(),0 , access=mmap.ACCESS_READ) 598 | else: 599 | mm = mmap.mmap(file.fileno(),52428800 , access=mmap.ACCESS_READ) # 52428800=50Mb 600 | offset = mm.find(struct.pack(' 0: 609 | img_file.seek(self.sign_offset, 0) 610 | header = ext4_file_header(img_file.read(28)) 611 | if header.magic != EXT4_HEADER_MAGIC: 612 | return 'img' 613 | else: 614 | return 'simg' 615 | 616 | def main(self, target, output_dir): 617 | self.BASE_DIR = (os.path.realpath(os.path.dirname(target)) + os.sep) 618 | self.BASE_MYDIR = output_dir + os.sep 619 | self.EXTRACT_DIR = os.path.realpath(os.path.dirname(output_dir)) + os.sep + self.__file_name(os.path.basename(output_dir)) #output_dir 620 | self.OUTPUT_IMAGE_FILE = self.BASE_DIR + os.path.basename(target) 621 | self.OUTPUT_MYIMAGE_FILE = os.path.basename(target) 622 | self.MYFileName = os.path.basename(self.OUTPUT_IMAGE_FILE).replace(".img", "") 623 | self.FileName = self.__file_name(os.path.basename(target)) 624 | #target_type = self.__getTypeTarget(target) 625 | target_type = 'img' 626 | if sys.argv.__len__() == 3: 627 | self.CONFING_DIR = sys.argv[2] + os.sep + 'config' 628 | else: 629 | self.CONFING_DIR = '.' + os.sep + 'config' 630 | if target_type == 'simg': 631 | print("Convert %s to %s" % (os.path.basename(target), os.path.basename(target).replace(".img", ".raw.img"))) 632 | self.__converSimgToImg(target) 633 | with open(os.path.abspath(self.OUTPUT_IMAGE_FILE), 'rb') as f: 634 | data = f.read(500000) 635 | moto = re.search(b'\x4d\x4f\x54\x4f', data) 636 | if moto: 637 | print("Finding MOTO structure! Fixing.....") 638 | self.fixmoto(os.path.abspath(self.OUTPUT_IMAGE_FILE)) 639 | print("Extraction from %s to %s" % (os.path.basename(target), os.path.basename(self.EXTRACT_DIR))) 640 | self.__ext4extractor() 641 | print("Done! All extraction in %s" % (os.path.basename(self.EXTRACT_DIR))) 642 | if target_type == 'img': 643 | with open(os.path.abspath(self.OUTPUT_IMAGE_FILE), 'rb') as f: 644 | data = f.read(500000) 645 | moto = re.search(b'\x4d\x4f\x54\x4f', data) 646 | if moto: 647 | print("Finding MOTO structure! Fixing.....") 648 | self.fixmoto(os.path.abspath(self.OUTPUT_IMAGE_FILE)) 649 | print("Extraction from %s to %s" % (os.path.basename(target), os.path.basename(self.EXTRACT_DIR))) 650 | self.__ext4extractor() 651 | print("Done! All extraction in %s" % (os.path.basename(self.EXTRACT_DIR))) 652 | 653 | if __name__ == '__main__': 654 | if sys.argv.__len__() == 3: 655 | Extractor().main(sys.argv[1], (sys.argv[2] + os.sep + os.path.basename(sys.argv[1]).split('.')[0])) 656 | else: 657 | if sys.argv.__len__() == 2: 658 | 659 | Extractor().main(sys.argv[1], "." + os.sep + os.path.basename(sys.argv[1]).split('.')[0]) 660 | -------------------------------------------------------------------------------- /bin/port_config: -------------------------------------------------------------------------------- 1 | # 需要移植的分区 2 | partition_to_port=system product system_ext 3 | 4 | 5 | # 可能的super子分区列表 6 | super_list=vendor mi_ext odm odm_dlkm system system_dlkm vendor_dlkm product product_dlkm system_ext 7 | 8 | 9 | # 打包为 ext4 文件系统(如果底包是erofs) true:是 false:否 意义:解锁system 10 | repack_with_ext4=true 11 | 12 | 13 | # 去除data加密 true:是 false:否 14 | remove_data_encryption=true -------------------------------------------------------------------------------- /port.sh: -------------------------------------------------------------------------------- 1 | # miui_port project 2 | 3 | # Only For V-A/B Device 4 | 5 | # Based on Android 13 6 | 7 | # Test Base ROM: Mi 10S (V14.0.6) 8 | 9 | # Test Port ROM: Mi13、Mi13Pro、Mi13Ultra 10 | 11 | # 底包和移植包为外部参数传入 12 | BASEROM="$1" 13 | PORTROM="$2" 14 | 15 | export PATH=$(pwd)/bin/$(uname)/$(uname -m)/:$PATH 16 | 17 | # 定义颜色输出函数 18 | Error() { 19 | echo -e \[$(date +%m%d-%T)\] "\e[1;31m"$@"\e[0m" 20 | } 21 | 22 | Yellow() { 23 | echo -e \[$(date +%m%d-%T)\] "\e[1;33m"$@"\e[0m" 24 | } 25 | 26 | Green() { 27 | echo -e \[$(date +%m%d-%T)\] "\e[1;32m"$@"\e[0m" 28 | } 29 | 30 | # 移植的分区,可在 bin/port_config 中更改 31 | PORT_PARTITION=$(grep "partition_to_port" bin/port_config |cut -d '=' -f 2) 32 | SUPERLIST=$(grep "super_list" bin/port_config |cut -d '=' -f 2) 33 | REPACKEXT4=$(grep "repack_with_ext4" bin/port_config |cut -d '=' -f 2) 34 | 35 | # 检查为本地包还是链接 36 | if [ ! -f "${BASEROM}" ] && [ "$(echo $BASEROM |grep http)" != "" ];then 37 | Yellow "底包为一个链接,正在尝试下载" 38 | aria2c --max-download-limit=1024M --file-allocation=none -s10 -x10 -j10 ${BASEROM} 39 | BASEROM=$(basename ${BASEROM}) 40 | if [ ! -f "${BASEROM}" ];then 41 | Error "下载错误" 42 | fi 43 | elif [ -f "${BASEROM}" ];then 44 | Green "底包: ${BASEROM}" 45 | else 46 | Error "底包参数错误" 47 | exit 48 | fi 49 | 50 | if [ ! -f "${PORTROM}" ] && [ "$(echo ${PORTROM} |grep http)" != "" ];then 51 | Yellow "移植包为一个链接,正在尝试下载" 52 | aria2c --max-download-limit=1024M --file-allocation=none -s10 -x10 -j10 ${PORTROM} 53 | BASEROM=$(basename ${PORTROM}) 54 | if [ ! -f "${PORTROM}" ];then 55 | Error "下载错误" 56 | fi 57 | elif [ -f "${PORTROM}" ];then 58 | Green "移植包: ${PORTROM}" 59 | else 60 | Error "移植包参数错误" 61 | exit 62 | fi 63 | 64 | if [ "$(echo $BASEROM |grep miui_)" != "" ];then 65 | deviceCode=$(basename $BASEROM |cut -d '_' -f 2) 66 | else 67 | deviceCode="YourDevice" 68 | fi 69 | 70 | 71 | 72 | # 检查ROM为包含 payload.bin 的包,否则无法处理 73 | Yellow "正在检测ROM包" 74 | unzip -l ${BASEROM}|grep "payload.bin" 1>/dev/null 2>&1 ||Error "底包没有payload.bin,请用MIUI官方包作为底包" 75 | unzip -l ${BASEROM} |grep "payload.bin" 1>/dev/null 2>&1 ||Error "目标移植包没有payload.bin,请用MIUI官方包作为移植包" 76 | 77 | Green "ROM初步检测通过" 78 | 79 | 80 | # 清理文件 81 | Yellow "正在清理文件" 82 | for i in ${PORT_PARTITION};do 83 | [ -d ./${i} ] && rm -rf ./${i} 84 | done 85 | rm -rf app 86 | rm -rf config 87 | rm -rf BASEROM/ 88 | rm -rf PORTROM/ 89 | find . -type d -name 'PORT_*' |xargs rm -rf 90 | mkdir -p BASEROM/images/ 91 | mkdir -p BASEROM/config/ 92 | mkdir -p PORTROM/images/ 93 | Green "文件清理完毕" 94 | 95 | 96 | # 提取分区 97 | Yellow "正在提取底包 [payload.bin]" 98 | unzip ${BASEROM} payload.bin -d BASEROM ||Error "解压底包 [payload.bin] 时出错" 99 | Green "底包 [payload.bin] 提取完毕" 100 | Yellow "正在提取移植包 [payload.bin]" 101 | unzip ${PORTROM} payload.bin -d PORTROM ||Error "解压移植包 [payload.bin] 时出错" 102 | Green "移植包 [payload.bin] 提取完毕" 103 | 104 | Yellow "开始分解底包 [payload.bin]" 105 | payload-dumper-go -o BASEROM/images/ BASEROM/payload.bin >/dev/null 2>&1 ||Error "分解底包 [payload.bin] 时出错" 106 | 107 | 108 | for part in ${PORT_PARTITION};do 109 | payload-dumper-go -l PORTROM/payload.bin |sed "s/,/\n/g" |grep -v "vbmeta" |grep "${part} (" 110 | if [ $? -eq 0 ];then 111 | Yellow "底包 [${part}.img] 已重命名为 [${part}_bak.img]" 112 | mv BASEROM/images/${part}.img BASEROM/images/${part}_bak.img 113 | Yellow "正在分解底包 [${part}_bak.img]" 114 | python3 bin/imgextractor/imgextractor.py BASEROM/images/${part}_bak.img 2>/dev/null 115 | if [ -d "${part}_bak" ];then 116 | mv ${part}_bak BASEROM/images/ 117 | sed -i '/+found/d' config/${part}_bak_file_contexts 118 | rm -rf BASEROM/images/${part}_bak.img 119 | else 120 | extract.erofs -x -i BASEROM/images/${part}_bak.img 121 | mv ${part}_bak BASEROM/images/ 122 | rm -rf BASEROM/images/${part}_bak.img 123 | fi 124 | mv config/${part}_bak_size.txt BASEROM/config/ 125 | mv config/${part}_bak_fs_config BASEROM/config/ 126 | mv config/${part}_bak_file_contexts BASEROM/config/ 127 | Yellow "正在提取移植包 [${part}] 分区" 128 | payload-dumper-go -p ${part} -o BASEROM/images/ PORTROM/payload.bin >/dev/null 2>&1 ||Error "提取移植包 [${part}] 分区时出错" 129 | fi 130 | done 131 | 132 | rm -rf PORTROM 133 | 134 | # 分解镜像 135 | Green 开始提取逻辑分区镜像 136 | for pname in ${SUPERLIST};do 137 | if [ -f "BASEROM/images/${pname}.img" ];then 138 | Yellow 正在提取 ${pname}.img 139 | python3 bin/imgextractor/imgextractor.py BASEROM/images/${pname}.img 2>/dev/null 140 | if [ -d "${pname}" ];then 141 | mv ${pname} BASEROM/images/ 142 | mv config/*${pname}* BASEROM/config/ 143 | sed -i '/+found/d' BASEROM/config/${pname}_file_contexts 144 | rm -rf BASEROM/images/${pname}.img 145 | # 测试原包分区文件系统类型 146 | if [ "${pname}" == "vendor" ];then 147 | packType=ext4 148 | Green "底包为 [ext4] 文件系统" 149 | fi 150 | else 151 | extract.erofs -x -i BASEROM/images/${pname}.img 152 | mv ${pname} BASEROM/images/ 153 | mv config/*${pname}* BASEROM/config/ 154 | rm -rf BASEROM/images/${pname}.img 155 | # 测试原包分区文件系统类型 156 | if [ "${pname}" == "vendor" ];then 157 | packType=erofs 158 | Green "底包为 [erofs] 文件系统" 159 | [ "${REPACKEXT4}" == "true" ] && packType=ext4 160 | fi 161 | fi 162 | Green "提取 [${pname}] 镜像完毕" 163 | fi 164 | done 165 | rm -rf config 166 | 167 | 168 | # 获取ROM参数 169 | 170 | Yellow "正在获取ROM参数" 171 | # 安卓版本 172 | base_android_version=$(cat BASEROM/images/vendor/build.prop |grep "ro.vendor.build.version.release" |awk 'NR==1' |cut -d '=' -f 2) 173 | port_android_version=$(cat BASEROM/images/system/system/build.prop |grep "ro.system.build.version.release" |awk 'NR==1' |cut -d '=' -f 2) 174 | Green "安卓版本: 底包为[Android ${base_android_version}], 移植包为 [Android ${port_android_version}]" 175 | 176 | # SDK版本 177 | base_android_sdk=$(cat BASEROM/images/vendor/build.prop |grep "ro.vendor.build.version.sdk" |awk 'NR==1' |cut -d '=' -f 2) 178 | port_android_sdk=$(cat BASEROM/images/system/system/build.prop |grep "ro.system.build.version.sdk" |awk 'NR==1' |cut -d '=' -f 2) 179 | Green "SDK 版本: 底包为 [SDK ${base_android_sdk}], 移植包为 [SDK ${port_android_sdk}]" 180 | 181 | # ROM版本 182 | base_rom_version=$(cat BASEROM/images/vendor/build.prop |grep "ro.vendor.build.version.incremental" |awk 'NR==1' |cut -d '=' -f 2) 183 | port_rom_version=$(cat BASEROM/images/system/system/build.prop |grep "ro.system.build.version.incremental" |awk 'NR==1' |cut -d '=' -f 2) 184 | Green "ROM 版本: 底包为 [${base_rom_version}], 移植包为 [${port_rom_version}]" 185 | 186 | # MIUI版本 187 | base_miui_version=$(cat BASEROM/images/product_bak/etc/build.prop |grep "ro.miui.ui.version.code" |awk 'NR==1' |cut -d '=' -f 2) 188 | port_miui_version=$(cat BASEROM/images/product/etc/build.prop |grep "ro.miui.ui.version.code" |awk 'NR==1' |cut -d '=' -f 2) 189 | Green "MIUI版本: 底包为 [${base_miui_version}], 移植包为 [${port_miui_version}]" 190 | 191 | # 代号 192 | base_rom_code=$(cat BASEROM/images/vendor/build.prop |grep "ro.product.vendor.device" |awk 'NR==1' |cut -d '=' -f 2) 193 | port_rom_code=$(cat BASEROM/images/system/system/build.prop |grep "ro.product.system.device" |awk 'NR==1' |cut -d '=' -f 2) 194 | Green "机型代号: 底包为 [${base_rom_code}], 移植包为 [${port_rom_code}]" 195 | 196 | # 机型名称 197 | base_rom_marketname=$(cat BASEROM/images/vendor/build.prop |grep "ro.product.vendor.marketname" |awk 'NR==1' |cut -d '=' -f 2) 198 | port_rom_marketname=$(cat BASEROM/images/system/system/build.prop |grep "ro.product.system.marketname" |awk 'NR==1' |cut -d '=' -f 2) # 这个很可能是空的 199 | Green "机型名称: 底包为 [${base_rom_marketname}], 移植包为 [${port_rom_marketname}]" 200 | 201 | # 修改ROM包 202 | 203 | # 去除avb校验 204 | Yellow "去除avb校验" 205 | for fstab in $(find BASEROM/images/ -type f -name "fstab.*");do 206 | Yellow "Target: $fstab" 207 | sed -i "s/,avb_keys=.*avbpubkey//g" $fstab 208 | sed -i "s/,avb=vbmeta_system//g" $fstab 209 | sed -i "s/,avb=vbmeta_vendor//g" $fstab 210 | sed -i "s/,avb=vbmeta//g" $fstab 211 | sed -i "s/,avb//g" $fstab 212 | done 213 | 214 | # data 加密 215 | remove_data_encrypt=$(grep "remove_data_encryption" bin/port_config |cut -d '=' -f 2) 216 | if [ "${remove_data_encrypt}" == "true" ];then 217 | Yellow "去除data加密" 218 | for fstab in $(find BASEROM/images/ -type f -name "fstab.*");do 219 | Yellow "Target: $fstab" 220 | sed -i "s/,fileencryption=aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized+wrappedkey_v0//g" $fstab 221 | sed -i "s/,fileencryption=aes-256-xts:aes-256-cts:v2+emmc_optimized+wrappedkey_v0//g" $fstab 222 | sed -i "s/,fileencryption=aes-256-xts:aes-256-cts:v2//g" $fstab 223 | sed -i "s/,metadata_encryption=aes-256-xts:wrappedkey_v0//g" $fstab 224 | sed -i "s/,fileencryption=aes-256-xts:wrappedkey_v0//g" $fstab 225 | sed -i "s/,metadata_encryption=aes-256-xts//g" $fstab 226 | sed -i "s/,fileencryption=aes-256-xts//g" $fstab 227 | sed -i "s/fileencryption/encryptable/g" $fstab 228 | sed -i "s/,fileencryption=ice//g" $fstab 229 | done 230 | fi 231 | 232 | baseAospFrameworkResOverlay=$(find BASEROM/images/product_bak/ -type f -name "AospFrameworkResOverlay.apk") 233 | portAospFrameworkResOverlay=$(find BASEROM/images/product/ -type f -name "AospFrameworkResOverlay.apk") 234 | if [ -f "${baseAospFrameworkResOverlay}" ] && [ -f "${portAospFrameworkResOverlay}" ];then 235 | Yellow "正在替换 [AospFrameworkResOverlay.apk]" 236 | cp -rf ${baseAospFrameworkResOverlay} ${portAospFrameworkResOverlay} 237 | fi 238 | 239 | baseMiuiFrameworkResOverlay=$(find BASEROM/images/product_bak/ -type f -name "MiuiFrameworkResOverlay.apk") 240 | portMiuiFrameworkResOverlay=$(find BASEROM/images/product/ -type f -name "MiuiFrameworkResOverlay.apk") 241 | if [ -f "${baseMiuiFrameworkResOverlay}" ] && [ -f "${portMiuiFrameworkResOverlay}" ];then 242 | Yellow "正在替换 [MiuiFrameworkResOverlay.apk]" 243 | cp -rf ${baseMiuiFrameworkResOverlay} ${portMiuiFrameworkResOverlay} 244 | fi 245 | 246 | baseAospWifiResOverlay=$(find BASEROM/images/product_bak/ -type f -name "AospWifiResOverlay.apk") 247 | portAospWifiResOverlay=$(find BASEROM/images/product/ -type f -name "AospWifiResOverlay.apk") 248 | if [ -f "${baseAospWifiResOverlay}" ] && [ -f "${portAospWifiResOverlay}" ];then 249 | Yellow "正在替换 [AospWifiResOverlay.apk]" 250 | cp -rf ${baseAospWifiResOverlay} ${portAospWifiResOverlay} 251 | fi 252 | 253 | baseDevicesAndroidOverlay=$(find BASEROM/images/product_bak/ -type f -name "DevicesAndroidOverlay.apk") 254 | portDevicesAndroidOverlay=$(find BASEROM/images/product/ -type f -name "DevicesAndroidOverlay.apk") 255 | if [ -f "${baseDevicesAndroidOverlay}" ] && [ -f "${portDevicesAndroidOverlay}" ];then 256 | Yellow "正在替换 [DevicesAndroidOverlay.apk]" 257 | cp -rf ${baseDevicesAndroidOverlay} ${portDevicesAndroidOverlay} 258 | fi 259 | 260 | baseDevicesOverlay=$(find BASEROM/images/product_bak/ -type f -name "DevicesOverlay.apk") 261 | portDevicesOverlay=$(find BASEROM/images/product/ -type f -name "DevicesOverlay.apk") 262 | if [ -f "${baseDevicesOverlay}" ] && [ -f "${portDevicesOverlay}" ];then 263 | Yellow "正在替换 [DevicesOverlay.apk]" 264 | cp -rf ${baseDevicesOverlay} ${portDevicesOverlay} 265 | fi 266 | 267 | baseMiuiBiometricResOverlay=$(find BASEROM/images/product_bak/ -type f -name "MiuiBiometricResOverlay.apk") 268 | portMiuiBiometricResOverlay=$(find BASEROM/images/product/ -type f -name "MiuiBiometricResOverlay.apk") 269 | if [ -f "${baseMiuiBiometricResOverlay}" ] && [ -f "${portMiuiBiometricResOverlay}" ];then 270 | Yellow "正在替换 [MiuiBiometricResOverlay.apk]" 271 | cp -rf ${baseMiuiBiometricResOverlay} ${portMiuiBiometricResOverlay} 272 | fi 273 | 274 | # radio lib 275 | # Yellow "信号相关" 276 | # for radiolib in $(find BASEROM/images/system_bak/system/lib/ -maxdepth 1 -type f -name "*radio*");do 277 | # cp -rf $radiolib BASEROM/images/system/system/lib/ 278 | # done 279 | 280 | # for radiolib in $(find BASEROM/images/system_bak/system/lib64/ -maxdepth 1 -type f -name "*radio*");do 281 | # cp -rf $radiolib BASEROM/images/system/system/lib64/ 282 | # done 283 | 284 | 285 | # audio lib 286 | # Yellow "音频相关" 287 | # for audiolib in $(find BASEROM/images/system_bak/system/lib/ -maxdepth 1 -type f -name "*audio*");do 288 | # cp -rf $audiolib BASEROM/images/system/system/lib/ 289 | # done 290 | 291 | # for audiolib in $(find BASEROM/images/system_bak/system/lib64/ -maxdepth 1 -type f -name "*audio*");do 292 | # cp -rf $audiolib BASEROM/images/system/system/lib64/ 293 | # done 294 | 295 | # # bt lib 296 | # Yellow "蓝牙相关" 297 | # for btlib in $(find BASEROM/images/system_bak/system/lib/ -maxdepth 1 -type f -name "*bluetooth*");do 298 | # cp -rf $btlib BASEROM/images/system/system/lib/ 299 | # done 300 | 301 | # for btlib in $(find BASEROM/images/system_bak/system/lib64/ -maxdepth 1 -type f -name "*bluetooth*");do 302 | # cp -rf $btlib BASEROM/images/system/system/lib64/ 303 | # done 304 | 305 | 306 | # displayconfig id 307 | Yellow "正在替换 displayconfig" 308 | rm -rf BASEROM/images/product/etc/displayconfig/* 309 | cp -rf BASEROM/images/product_bak/etc/displayconfig/* BASEROM/images/product/etc/displayconfig/ 310 | for context in $(find BASEROM/images/product/etc/displayconfig/ -type f);do 311 | echo>>BASEROM/config/product_file_contexts 312 | echo>>BASEROM/config/product_fs_config 313 | echo "${context} u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/product_file_contexts 314 | echo "${context} 0 0 0644" |sed 's/BASEROM\/images\///g' >>BASEROM/config/product_fs_config 315 | done 316 | 317 | # device_features 318 | Yellow "正在替换 device_features" 319 | rm -rf BASEROM/images/product/etc/device_features/* 320 | cp -rf BASEROM/images/product_bak/etc/device_features/* BASEROM/images/product/etc/device_features/ 321 | for context in $(find BASEROM/images/product/etc/device_features/ -type f);do 322 | echo>>BASEROM/config/product_file_contexts 323 | echo>>BASEROM/config/product_fs_config 324 | echo "${context} u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/product_file_contexts 325 | echo "${context} 0 0 0644" |sed 's/BASEROM\/images\///g' >>BASEROM/config/product_fs_config 326 | done 327 | 328 | 329 | # 相机 330 | baseMiuiCamera=$(find BASEROM/images/product_bak/ -type d -name "MiuiCamera") 331 | portMiuiCamera=$(find BASEROM/images/product/ -type d -name "MiuiCamera") 332 | if [ -d "${baseMiuiCamera}" ] && [ -d "${portMiuiCamera}" ];then 333 | Yellow "正在替换 相机" 334 | rm -rf ./${portMiuiCamera}/* 335 | cp -rf ./${baseMiuiCamera}/* ${portMiuiCamera}/ 336 | fi 337 | 338 | 339 | # MiSound 340 | baseMiSound=$(find BASEROM/images/product_bak/ -type d -name "MiSound") 341 | portMiSound=$(find BASEROM/images/product/ -type d -name "MiSound") 342 | if [ -d "${baseMiSound}" ] && [ -d "${portMiSound}" ];then 343 | Yellow "正在替换 MiSound" 344 | rm -rf ./${portMiSound}/* 345 | cp -rf ./${baseMiSound}/* ${portMiSound}/ 346 | fi 347 | 348 | # MusicFX 349 | baseMusicFX=$(find BASEROM/images/product_bak/ BASEROM/images/system_bak/ -type d -name "MusicFX") 350 | portMusicFX=$(find BASEROM/images/product/ BASEROM/images/system/ -type d -name "MusicFX") 351 | if [ -d "${baseMusicFX}" ] && [ -d "${portMusicFX}" ];then 352 | Yellow "正在替换 MusicFX" 353 | rm -rf ./${portMusicFX}/* 354 | cp -rf ./${baseMusicFX}/* ${portMusicFX}/ 355 | fi 356 | 357 | # 人脸 358 | baseMiuiBiometric=$(find BASEROM/images/product_bak/app -type d -name "MiuiBiometric*") 359 | portMiuiBiometric=$(find BASEROM/images/product/app -type d -name "MiuiBiometric*") 360 | if [ -d "${baseMiuiBiometric}" ] && [ -d "${portMiuiBiometric}" ];then 361 | Yellow "正在替换人脸识别" 362 | rm -rf ./${portMiuiBiometric}/* 363 | cp -rf ./${baseMiuiBiometric}/* ${portMiuiBiometric}/ 364 | else 365 | if [ -d "${baseMiuiBiometric}" ] && [ ! -d "${portMiuiBiometric}" ];then 366 | Yellow "Port MiuiBiometric not found, copying..." 367 | cp -rf ${baseMiuiBiometric} BASEROM/images/product/app/ 368 | fi 369 | fi 370 | 371 | 372 | # 修复NFC 373 | Yellow "正在修复 NFC" 374 | cp -rf BASEROM/images/product_bak/pangu/system ./system 375 | for file in $(find system -type d |sed "1d");do 376 | echo>>BASEROM/config/system_file_contexts 377 | echo>>BASEROM/config/system_fs_config 378 | echo "$file u:object_r:system_file:s0" |sed 's/system\//\/system\/system\//g' |sed 's/\./\\\./g' >>BASEROM/config/system_file_contexts 379 | echo "$file 0 0 0755" |sed 's/system/system\/system/g' >>BASEROM/config/system_fs_config 380 | done 381 | 382 | for file in $(find system/ -type f);do 383 | echo>>BASEROM/config/system_file_contexts 384 | echo>>BASEROM/config/system_fs_config 385 | echo "$file u:object_r:system_file:s0" |sed 's/system\//\/system\/system\//g' |sed 's/\./\\\./g' >>BASEROM/config/system_file_contexts 386 | echo "$file 0 0 0644" |sed 's/system/system\/system/g' >>BASEROM/config/system_fs_config 387 | done 388 | cp -rf system/* BASEROM/images/system/system/ 389 | rm -rf system/ 390 | if [ -f BASEROM/images/system_bak/system/etc/permissions/com.android.nfc_extras.xml ] && [ -f BASEROM/images/system/system/etc/permissions/com.android.nfc_extras.xml ];then 391 | cp -rf BASEROM/images/system_bak/system/etc/permissions/com.android.nfc_extras.xml BASEROM/images/system/system/etc/permissions/com.android.nfc_extras.xml 392 | fi 393 | if [ -f BASEROM/images/system_bak/system/framework/com.android.nfc_extras.jar ] && [ -f BASEROM/images/system/system/framework/com.android.nfc_extras.jar ];then 394 | cp -rf BASEROM/images/system_bak/system/framework/com.android.nfc_extras.jar BASEROM/images/system/system/framework/com.android.nfc_extras.jar 395 | fi 396 | 397 | 398 | # App context 修复 399 | Yellow "正在补全 contexts" 400 | for file in $(find BASEROM/images/system/system/app BASEROM/images/system/system/priv-app -type d);do 401 | echo>>BASEROM/config/system_file_contexts 402 | echo>>BASEROM/config/system_fs_config 403 | echo "$file u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/system_file_contexts 404 | echo "$file 0 0 0755" |sed 's/BASEROM\/images\///g' >>BASEROM/config/system_fs_config 405 | done 406 | 407 | for file in $(find BASEROM/images/system/system/app BASEROM/images/system/system/priv-app -type f);do 408 | echo>>BASEROM/config/system_file_contexts 409 | echo>>BASEROM/config/system_fs_config 410 | echo "$file u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/system_file_contexts 411 | echo "$file 0 0 0644" |sed 's/BASEROM\/images\///g' >>BASEROM/config/system_fs_config 412 | done 413 | 414 | for file in $(find BASEROM/images/product/app BASEROM/images/product/priv-app -type d);do 415 | echo>>BASEROM/config/product_file_contexts 416 | echo>>BASEROM/config/product_fs_config 417 | echo "$file u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/product_file_contexts 418 | echo "$file 0 0 0755" |sed 's/BASEROM\/images\///g' >>BASEROM/config/product_fs_config 419 | done 420 | 421 | for file in $(find BASEROM/images/product/app BASEROM/images/product/priv-app -type f);do 422 | echo>>BASEROM/config/product_file_contexts 423 | echo>>BASEROM/config/product_fs_config 424 | echo "$file u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/product_file_contexts 425 | echo "$file 0 0 644" |sed 's/BASEROM\/images\///g' >>BASEROM/config/product_fs_config 426 | done 427 | 428 | 429 | # lib file u:object_r:system_lib_file:s0 430 | 431 | for lib in $(find BASEROM/images/system/system/lib/ BASEROM/images/system/system/lib64/ -maxdepth 1 -type f);do 432 | echo>>BASEROM/config/system_file_contexts 433 | echo>>BASEROM/config/system_fs_config 434 | echo "$lib u:object_r:system_lib_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/system_file_contexts 435 | echo "$lib 0 0 0644" |sed 's/BASEROM\/images\///g' >>BASEROM/config/system_fs_config 436 | done 437 | 438 | cp -rf BASEROM/images/product_bak/overlay/* BASEROM/images/product/overlay/ 439 | for file in $(find BASEROM/images/product/overlay -type d);do 440 | echo>>BASEROM/config/product_file_contexts 441 | echo>>BASEROM/config/product_fs_config 442 | echo "$file u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/product_file_contexts 443 | echo "$file 0 0 0755" |sed 's/BASEROM\/images\///g' >>BASEROM/config/product_fs_config 444 | done 445 | 446 | for file in $(find BASEROM/images/product/overlay -type f);do 447 | echo>>BASEROM/config/product_file_contexts 448 | echo>>BASEROM/config/product_fs_config 449 | echo "$file u:object_r:system_file:s0" |sed 's/BASEROM\/images//g' |sed 's/\./\\\./g' >>BASEROM/config/product_file_contexts 450 | echo "$file 0 0 644" |sed 's/BASEROM\/images\///g' >>BASEROM/config/product_fs_config 451 | done 452 | 453 | # 签名验证 454 | frameworkjar=$(find BASEROM/images/system/system -type f -name framework.jar) 455 | if [ -f "$frameworkjar" ] && [ ${port_android_version} -ge 13 ];then 456 | Yellow "正在去除安卓应用签名限制" 457 | rm -rf tmp/framework/ 458 | mkdir -p tmp/framework/ 459 | cp -rf ${frameworkjar} tmp/framework/framework.jar 460 | 7z x -y tmp/framework/framework.jar *.dex -otmp/framework >/dev/null 461 | for dexfile in $(ls tmp/framework/*.dex);do 462 | echo I: Baksmaling ${dexfile}... 463 | fname=${dexfile%%.*} 464 | fname=$(echo $fname |cut -d "/" -f 3) 465 | java -jar bin/apktool/baksmali.jar d --api ${port_android_sdk} ${dexfile} -o tmp/framework/${fname} 466 | rm -rf ${dexfile} 467 | done 468 | targetSmali=$(find tmp/framework/ -type f -name ApkSignatureVerifier.smali) 469 | if [ -f "$targetSmali" ];then 470 | echo I: Target ${targetSmali} 471 | targetdir=$(echo $targetSmali |cut -d "/" -f 3) 472 | sed -i "s/const\/4 v0, 0x2/const\/4 v0, 0x1/g" $targetSmali 473 | rm -rf ${frameworkjar} 474 | echo I: Smaling smali_${targetdir} folder into ${targetdir}.dex 475 | java -jar bin/apktool/smali.jar a --api ${port_android_sdk} tmp/framework/${targetdir} -o tmp/framework/${targetdir}.dex 476 | cd tmp/framework/ 477 | 7z a -y framework.jar ${targetdir}.dex >/dev/null 478 | cd ../../ 479 | cp -rf tmp/framework/framework.jar ${frameworkjar} 480 | rm -rf tmp/framework/ 481 | mkdir -p tmp/framework/arm tmp/framework/arm64 482 | mv BASEROM/images/system/system/framework/boot-framework.vdex tmp/framework/ 483 | mv BASEROM/images/system/system/framework/arm/boot-framework.* tmp/framework/arm/ 484 | mv BASEROM/images/system/system/framework/arm64/boot-framework.* tmp/framework/arm64/ 485 | rm -rf BASEROM/images/system/system/framework/*.vdex BASEROM/images/system/system/framework/arm/* BASEROM/images/system/system/framework/arm64/* 486 | find BASEROM/images/system -type d -name "oat" |xargs rm -rf 487 | find BASEROM/images/vendor -type d -name "oat" |xargs rm -rf 488 | find BASEROM/images/system_ext -type d -name "oat" |xargs rm -rf 489 | find BASEROM/images/product -type d -name "oat" |xargs rm -rf 490 | mv tmp/framework/* BASEROM/images/system/system/framework/ 491 | rm -rf tmp/ 492 | else 493 | echo I: Skipping modify framework.jar 494 | rm -rf tmp/ 495 | fi 496 | else 497 | echo I: Skipping modify framework.jar 498 | fi 499 | 500 | 501 | # 主题防恢复 502 | if [ -f BASEROM/images/system/system/etc/init/hw/init.rc ];then 503 | sed -i '/on boot/a\ chmod 0731 \/data\/system\/theme' BASEROM/images/system/system/etc/init/hw/init.rc 504 | fi 505 | 506 | # 删除多余的App 507 | rm -rf BASEROM/images/product/app/MSA 508 | rm -rf BASEROM/images/product/priv-app/MSA 509 | rm -rf BASEROM/images/product/app/mab 510 | rm -rf BASEROM/images/product/priv-app/mab 511 | rm -rf BASEROM/images/product/app/Updater 512 | rm -rf BASEROM/images/product/priv-app/Updater 513 | rm -rf BASEROM/images/product/app/MiuiUpdater 514 | rm -rf BASEROM/images/product/priv-app/MiuiUpdater 515 | rm -rf BASEROM/images/product/app/MIUIUpdater 516 | rm -rf BASEROM/images/product/priv-app/MIUIUpdater 517 | rm -rf BASEROM/images/product/app/MiService 518 | rm -rf BASEROM/images/product/app/MIService 519 | rm -rf BASEROM/images/product/priv-app/MiService 520 | rm -rf BASEROM/images/product/priv-app/MIService 521 | rm -rf BASEROM/images/product/app/*Hybrid* 522 | rm -rf BASEROM/images/product/priv-app/*Hybrid* 523 | rm -rf BASEROM/images/product/etc/auto-install* 524 | rm -rf BASEROM/images/product/app/AnalyticsCore/* 525 | rm -rf BASEROM/images/product/priv-app/AnalyticsCore/* 526 | rm -rf BASEROM/images/product/data-app/*GalleryLockscreen* >/dev/null 2>&1 527 | mkdir -p app 528 | mv BASEROM/images/product/data-app/*Weather* app/ >/dev/null 2>&1 529 | mv BASEROM/images/product/data-app/*DeskClock* app/ >/dev/null 2>&1 530 | mv BASEROM/images/product/data-app/*Gallery* app/ >/dev/null 2>&1 531 | mv BASEROM/images/product/data-app/*SoundRecorder* app/ >/dev/null 2>&1 532 | mv BASEROM/images/product/data-app/*ScreenRecorder* app/ >/dev/null 2>&1 533 | mv BASEROM/images/product/data-app/*Calculator* app/ >/dev/null 2>&1 534 | mv BASEROM/images/product/data-app/*Calendar* app/ >/dev/null 2>&1 535 | rm -rf BASEROM/images/product/data-app/* 536 | cp -rf app/* BASEROM/images/product/data-app 537 | rm -rf app 538 | rm -rf BASEROM/images/system/verity_key 539 | rm -rf BASEROM/images/vendor/verity_key 540 | rm -rf BASEROM/images/product/verity_key 541 | rm -rf BASEROM/images/system/recovery-from-boot.p 542 | rm -rf BASEROM/images/vendor/recovery-from-boot.p 543 | rm -rf BASEROM/images/product/recovery-from-boot.p 544 | rm -rf BASEROM/images/product/media/theme/miui_mod_icons/com.google.android.apps.nbu* 545 | rm -rf BASEROM/images/product/media/theme/miui_mod_icons/dynamic/com.google.android.apps.nbu* 546 | 547 | # build.prop 修改 548 | Yellow "正在修改 build.prop" 549 | buildDate=$(date -u +"%a %b %d %H:%M:%S UTC %Y") 550 | buildUtc=$(date +%s) 551 | for i in $(find BASEROM/images/ -type f -name "build.prop");do 552 | Yellow "正在处理 ${i}" 553 | sed -i "s/ro.odm.build.date=.*/ro.odm.build.date=${buildDate}/g" ${i} 554 | sed -i "s/ro.odm.build.date.utc=.*/ro.odm.build.date.utc=${buildUtc}/g" ${i} 555 | sed -i "s/ro.vendor.build.date=.*/ro.vendor.build.date=${buildDate}/g" ${i} 556 | sed -i "s/ro.vendor.build.date.utc=.*/ro.vendor.build.date.utc=${buildUtc}/g" ${i} 557 | sed -i "s/ro.system.build.date=.*/ro.system.build.date=${buildDate}/g" ${i} 558 | sed -i "s/ro.system.build.date.utc=.*/ro.system.build.date.utc=${buildUtc}/g" ${i} 559 | sed -i "s/ro.product.build.date=.*/ro.product.build.date=${buildDate}/g" ${i} 560 | sed -i "s/ro.product.build.date.utc=.*/ro.product.build.date.utc=${buildUtc}/g" ${i} 561 | sed -i "s/ro.system_ext.build.date=.*/ro.system_ext.build.date=${buildDate}/g" ${i} 562 | sed -i "s/ro.system_ext.build.date.utc=.*/ro.system_ext.build.date.utc=${buildUtc}/g" ${i} 563 | sed -i "s/ro.build.date=.*/ro.build.date=${buildDate}/g" ${i} 564 | sed -i "s/ro.build.date.utc=.*/ro.build.date.utc=${buildUtc}/g" ${i} 565 | sed -i "s/ro.odm.build.version.incremental=.*/ro.odm.build.version.incremental=${port_rom_version}/g" ${i} 566 | sed -i "s/ro.vendor.build.version.incremental=.*/ro.vendor.build.version.incremental=${port_rom_version}/g" ${i} 567 | sed -i "s/ro.system.build.version.incremental=.*/ro.system.build.version.incremental=${port_rom_version}/g" ${i} 568 | sed -i "s/ro.product.build.version.incremental=.*/ro.product.build.version.incremental=${port_rom_version}/g" ${i} 569 | sed -i "s/ro.system_ext.build.version.incremental=.*/ro.system_ext.build.version.incremental=${port_rom_version}/g" ${i} 570 | sed -i "s/ro.product.device=.*/ro.product.device=${base_rom_code}/g" ${i} 571 | sed -i "s/ro.product.odm.device=.*/ro.product.odm.device=${base_rom_code}/g" ${i} 572 | sed -i "s/ro.product.vendor.device=.*/ro.product.vendor.device=${base_rom_code}/g" ${i} 573 | sed -i "s/ro.product.system.device=.*/ro.product.system.device=${base_rom_code}/g" ${i} 574 | sed -i "s/ro.product.board=.*/ro.product.board=${base_rom_code}/g" ${i} 575 | sed -i "s/ro.product.system_ext.device=.*/ro.product.system_ext.device=${base_rom_code}/g" ${i} 576 | sed -i "s/persist.sys.timezone=.*/persist.sys.timezone=Asia\/Shanghai/g" ${i} 577 | sed -i "s/ro.product.mod_device=.*/ro.product.mod_device=${base_rom_code}/g" ${i} 578 | done 579 | 580 | sed -i '$a\persist.adb.notify=0' BASEROM/images/system/system/build.prop 581 | sed -i '$a\persist.sys.usb.config=mtp,adb' BASEROM/images/system/system/build.prop 582 | sed -i '$a\persist.sys.disable_rescue=true' BASEROM/images/system/system/build.prop 583 | sed -i '$a\persist.miui.extm.enable=0' BASEROM/images/system/system/build.prop 584 | 585 | # 屏幕密度修修改 586 | for prop in $(find BASEROM/images/product_bak BASEROM/images/system_bak -type f -name "build.prop");do 587 | base_rom_density=$(cat $prop |grep "ro.sf.lcd_density" |awk 'NR==1' |cut -d '=' -f 2) 588 | if [ "${base_rom_density}" != "" ];then 589 | Green "底包屏幕密度值 ${base_rom_density}" 590 | break 591 | fi 592 | done 593 | 594 | # 未在底包找到则默认440,如果是其他值可自己修改 595 | [ -z ${base_rom_density} ] && base_rom_density=440 596 | 597 | for prop in $(find BASEROM/images/product_bak BASEROM/images/system_bak -type f -name "build.prop");do 598 | sed -i "s/ro.sf.lcd_density=.*/ro.sf.lcd_density=${base_rom_density}/g" ${prop} 599 | sed -i "s/persist.miui.density_v2=.*/persist.miui.density_v2=${base_rom_density}/g" ${prop} 600 | done 601 | 602 | 603 | vendorprop=$(find BASEROM/images/vendor/ -type f -name "build.prop") 604 | odmprop=$(find BASEROM/images/odm/ -type f -name "build.prop" |awk 'NR==1') 605 | if [ "$(cat $vendorprop |grep "sys.haptic" |awk 'NR==1')" != "" ];then 606 | Yellow "复制 haptic prop 到 odm" 607 | cat $vendorprop |grep "sys.haptic" >>${odmprop} 608 | fi 609 | 610 | # 处理 contexts 去重 611 | Yellow "contexts 去重" 612 | cat BASEROM/config/system_file_contexts |sort |uniq >system_file_contexts 613 | cat BASEROM/config/product_file_contexts |sort |uniq >product_file_contexts 614 | cat BASEROM/config/system_fs_config |sort |uniq >system_fs_config 615 | cat BASEROM/config/product_fs_config |sort |uniq >product_fs_config 616 | mv -f system_file_contexts BASEROM/config/system_file_contexts 617 | mv -f product_file_contexts BASEROM/config/product_file_contexts 618 | mv -f system_fs_config BASEROM/config/system_fs_config 619 | mv -f product_fs_config BASEROM/config/product_fs_config 620 | sed -i "1d" BASEROM/config/system_file_contexts 621 | sed -i "1d" BASEROM/config/product_file_contexts 622 | sed -i "1d" BASEROM/config/system_fs_config 623 | sed -i "1d" BASEROM/config/product_fs_config 624 | 625 | 626 | # 重新打包镜像 627 | rm -rf BASEROM/images/system_bak* 628 | rm -rf BASEROM/images/product_bak* 629 | rm -rf BASEROM/images/system_ext_bak* 630 | for pname in ${PORT_PARTITION};do 631 | rm -rf BASEROM/images/${pname}.img 632 | done 633 | echo "${packType}">fstype.txt 634 | superSize=$(bash bin/getSuperSize.sh $deviceCode) 635 | Green 开始打包镜像 636 | for pname in ${SUPERLIST};do 637 | if [ -d "BASEROM/images/$pname" ];then 638 | thisSize=$(du -sb BASEROM/images/${pname} |tr -cd 0-9) 639 | case $pname in 640 | mi_ext) addSize=4194304 ;; 641 | odm) addSize=134217728 ;; 642 | system|vendor|system_ext) addSize=154217728 ;; 643 | product) addSize=204217728 ;; 644 | *) addSize=8554432 ;; 645 | esac 646 | if [ "$packType" = "ext4" ];then 647 | Yellow "$pname"为EXT4文件系统多分配大小$addSize 648 | for fstab in $(find BASEROM/images/${pname}/ -type f -name "fstab.*");do 649 | sed -i '/overlay/d' $fstab 650 | sed -i '/system * erofs/d' $fstab 651 | sed -i '/system_ext * erofs/d' $fstab 652 | sed -i '/vendor * erofs/d' $fstab 653 | sed -i '/product * erofs/d' $fstab 654 | done 655 | thisSize=$(echo "$thisSize + $addSize" |bc) 656 | Yellow 以[$packType]文件系统打包[${pname}.img]大小[$thisSize] 657 | make_ext4fs -J -T $(date +%s) -S BASEROM/config/${pname}_file_contexts -l $thisSize -C BASEROM/config/${pname}_fs_config -L ${pname} -a ${pname} BASEROM/images/${pname}.img BASEROM/images/${pname} 658 | if [ -f "BASEROM/images/${pname}.img" ];then 659 | Green "成功以大小 [$thisSize] 打包 [${pname}.img] [${packType}] 文件系统" 660 | rm -rf BASEROM/images/${pname} 661 | else 662 | Error "以 [${packType}] 文件系统打包 [${pname}] 分区失败" 663 | fi 664 | else 665 | Yellow 以[$packType]文件系统打包[${pname}.img] 666 | mkfs.erofs --mount-point ${pname} --fs-config-file BASEROM/config/${pname}_fs_config --file-contexts BASEROM/config/${pname}_file_contexts BASEROM/images/${pname}.img BASEROM/images/${pname} 667 | if [ -f "BASEROM/images/${pname}.img" ];then 668 | Green "成功以 [erofs] 文件系统打包 [${pname}.img]" 669 | rm -rf BASEROM/images/${pname} 670 | else 671 | Error "以 [${packType}] 文件系统打包 [${pname}] 分区失败" 672 | fi 673 | fi 674 | unset fsType 675 | unset thisSize 676 | fi 677 | done 678 | rm fstype.txt 679 | 680 | 681 | 682 | # 打包 super.img 683 | Yellow 开始打包Super.img 684 | 685 | lpargs="-F --virtual-ab --output BASEROM/images/super.img --metadata-size 65536 --super-name super --metadata-slots 3 --device super:$superSize --group=qti_dynamic_partitions_a:$superSize --group=qti_dynamic_partitions_b:$superSize" 686 | 687 | for pname in ${SUPERLIST};do 688 | if [ -f "BASEROM/images/${pname}.img" ];then 689 | subsize=$(du -sb BASEROM/images/${pname}.img |tr -cd 0-9) 690 | Green Super 子分区 [$pname] 大小 [$subsize] 691 | args="--partition ${pname}_a:none:${subsize}:qti_dynamic_partitions_a --image ${pname}_a=BASEROM/images/${pname}.img --partition ${pname}_b:none:0:qti_dynamic_partitions_b" 692 | lpargs="$lpargs $args" 693 | unset subsize 694 | unset args 695 | fi 696 | done 697 | lpmake $lpargs 698 | if [ -f "BASEROM/images/super.img" ];then 699 | Green 成功打包 Super.img 700 | else 701 | Error 无法打包 Super.img 702 | fi 703 | for pname in ${SUPERLIST};do 704 | rm -rf BASEROM/images/${pname}.img 705 | done 706 | 707 | Yellow "正在压缩 super.img" 708 | zstd --rm BASEROM/images/super.img 709 | 710 | # disable vbmeta 711 | for img in $(find BASEROM/images/ -type f -name "vbmeta*.img");do 712 | vbmeta-disable-verification $img 713 | done 714 | 715 | 716 | mkdir -p PORT_${deviceCode}_${port_rom_version}/images 717 | mkdir -p PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/ 718 | cp -rf bin/flash/update-binary PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/ 719 | cp -rf bin/flash/platform-tools-windows PORT_${deviceCode}_${port_rom_version}/META-INF/ 720 | cp -rf bin/flash/flash_update.bat PORT_${deviceCode}_${port_rom_version}/ 721 | cp -rf bin/flash/flash_and_format.bat PORT_${deviceCode}_${port_rom_version}/ 722 | mv -f BASEROM/images/super.img.zst PORT_${deviceCode}_${port_rom_version}/images/ 723 | mv -f BASEROM/images/*.img PORT_${deviceCode}_${port_rom_version}/images/ 724 | cp -rf bin/flash/zstd PORT_${deviceCode}_${port_rom_version}/META-INF/ 725 | 726 | # 生成刷机脚本 727 | Yellow "正在生成刷机脚本" 728 | for fwImg in $(ls PORT_${deviceCode}_${port_rom_version}/images/ |cut -d "." -f 1 |grep -vE "super|cust|preloader");do 729 | if [ "$(echo $fwImg |grep vbmeta)" != "" ];then 730 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot --disable-verity --disable-verification flash "$fwImg"_b images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_update.bat 731 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot --disable-verity --disable-verification flash "$fwImg"_a images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_update.bat 732 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot --disable-verity --disable-verification flash "$fwImg"_b images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_and_format.bat 733 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot --disable-verity --disable-verification flash "$fwImg"_a images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_and_format.bat 734 | sed -i "/#firmware/a package_extract_file \"images/"$fwImg".img\" \"/dev/block/bootdevice/by-name/"$fwImg"_b\"" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 735 | sed -i "/#firmware/a package_extract_file \"images/"$fwImg".img\" \"/dev/block/bootdevice/by-name/"$fwImg"_a\"" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 736 | else 737 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot flash "$fwImg"_b images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_update.bat 738 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot flash "$fwImg"_a images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_update.bat 739 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot flash "$fwImg"_b images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_and_format.bat 740 | sed -i "/rem/a META-INF\\\platform-tools-windows\\\fastboot flash "$fwImg"_a images\/"$fwImg".img" PORT_${deviceCode}_${port_rom_version}/flash_and_format.bat 741 | sed -i "/#firmware/a package_extract_file \"images/"$fwImg".img\" \"/dev/block/bootdevice/by-name/"$fwImg"_b\"" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 742 | sed -i "/#firmware/a package_extract_file \"images/"$fwImg".img\" \"/dev/block/bootdevice/by-name/"$fwImg"_a\"" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 743 | fi 744 | done 745 | 746 | sed -i "s/portversion/${port_rom_version}/g" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 747 | sed -i "s/baseversion/${base_rom_version}/g" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 748 | sed -i "s/andVersion/${port_android_version}/g" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 749 | sed -i "s/deviceCode/${base_rom_code}/g" PORT_${deviceCode}_${port_rom_version}/META-INF/com/google/android/update-binary 750 | 751 | busybox unix2dosPORT_${deviceCode}_${port_rom_version}/flash_update.bat 752 | busybox unix2dos PORT_${deviceCode}_${port_rom_version}/flash_and_format.bat 753 | 754 | find PORT_${deviceCode}_${port_rom_version}/ |xargs touch 755 | 756 | cd PORT_${deviceCode}_${port_rom_version}/ 757 | zip -r PORT_${deviceCode}_${port_rom_version}.zip ./* 758 | mv PORT_${deviceCode}_${port_rom_version}.zip ../ 759 | cd ../ 760 | hash=$(md5sum PORT_${deviceCode}_${port_rom_version}.zip |head -c 10) 761 | mv PORT_${deviceCode}_${port_rom_version}.zip PORT_${deviceCode}_${port_rom_version}_${hash}_${port_android_version}.zip 762 | Green "移植完毕" 763 | Green "输出包为 $(pwd)/PORT_${deviceCode}_${port_rom_version}_${hash}_${port_android_version}.zip" 764 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | # port requirements 2 | 3 | if [ "$(id -u)" != "0" ] && [ "$(uname -m)" == "x86_64" ];then 4 | echo "请以 root 用户运行" 5 | exit 6 | fi 7 | 8 | if [ "$(uname -m)" == "x86_64" ];then 9 | echo "Device arch: x86_64" 10 | apt update -y 11 | apt upgrade -y 12 | apt install -y aria2 python3 busybox zip unzip p7zip-full openjdk-8-jre 13 | if [ $? -ne 0 ];then 14 | echo "安装可能出错,请手动执行:apt install -y python3 busybox zip unzip p7zip-full" 15 | fi 16 | fi 17 | 18 | if [ "$(uname -m)" == "aarch64" ];then 19 | echo "Device arch: aarch64" 20 | apt update -y 21 | apt upgrade -y 22 | apt install -y python busybox zip unzip p7zip openjdk-17 23 | fi 24 | --------------------------------------------------------------------------------