You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Compare commits
36 Commits
1333d3b986
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb0c4ea838 | ||
|
|
bc89c60977 | ||
|
|
bd657c354c | ||
|
|
675b5f9565 | ||
|
|
1b2c43268e | ||
|
|
653730d75e | ||
|
|
d472e9c36e | ||
|
|
484d53ef7e | ||
|
|
c4e2985677 | ||
|
|
42d9f87bc9 | ||
|
|
2e4fa6864c | ||
|
|
e2abb648ac | ||
|
|
3599dcedfb | ||
|
|
ea72666df8 | ||
|
|
bd2a47ba18 | ||
|
|
b861671391 | ||
|
|
e91fc75d86 | ||
|
|
78f5cd55c7 | ||
|
|
9787a69528 | ||
|
|
87b8fe374d | ||
|
|
7b706bb0cb | ||
|
|
c1491b8d2b | ||
|
|
5cbaf2ae11 | ||
|
|
8ebc6207b4 | ||
|
|
7848ee616b | ||
|
|
fd193c3cae | ||
|
|
36d33c7a85 | ||
|
|
5caf28d27c | ||
|
|
2c39d0234d | ||
|
|
c313812129 | ||
|
|
af51880a81 | ||
|
|
db8d832707 | ||
|
|
8dc23d0ead | ||
|
|
b4287700d5 | ||
|
|
8d10ab89f2 | ||
|
|
49fdc1addb |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -105,7 +105,7 @@ jobs:
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Run AVD test
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
AVD_TEST_LOG: 1
|
||||
run: scripts/avd.sh test ${{ matrix.version }} ${{ matrix.type }}
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: Run AVD test
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
FORCE_32_BIT: 1
|
||||
AVD_TEST_LOG: 1
|
||||
@@ -191,7 +191,7 @@ jobs:
|
||||
scripts/cuttlefish.sh download ${{ matrix.branch }} ${{ matrix.device }}
|
||||
|
||||
- name: Run Cuttlefish test
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
run: sudo -E -u $USER scripts/cuttlefish.sh test
|
||||
|
||||
- name: Upload logs on error
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,9 +4,6 @@
|
||||
[submodule "lz4"]
|
||||
path = native/src/external/lz4
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "xz"]
|
||||
path = native/src/external/xz
|
||||
url = https://github.com/xz-mirror/xz.git
|
||||
[submodule "libcxx"]
|
||||
path = native/src/external/libcxx
|
||||
url = https://github.com/topjohnwu/libcxx.git
|
||||
|
||||
@@ -55,7 +55,7 @@ fun Project.setupCommon() {
|
||||
compileSdkVersion(36)
|
||||
buildToolsVersion = "36.0.0"
|
||||
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||
ndkVersion = "28.1.13356709"
|
||||
ndkVersion = "29.0.13846066"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 23
|
||||
|
||||
249
app/core/src/main/res/values-hn/strings.xml
Normal file
249
app/core/src/main/res/values-hn/strings.xml
Normal file
@@ -0,0 +1,249 @@
|
||||
<resources>
|
||||
|
||||
<!--Sections-->
|
||||
<string name="modules">Modules</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="logs">Logs</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="section_home">Home</string>
|
||||
<string name="section_theme">Themes</string>
|
||||
<string name="denylist">DenyList</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">Net nahi chal rha hai</string>
|
||||
<string name="app_changelog">Kya naga hai</string>
|
||||
<string name="loading">Loading ho rha hai…</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="not_available">Available nahi hai</string>
|
||||
<string name="hide">Chhupao</string>
|
||||
<string name="home_package">Package</string>
|
||||
<string name="home_app_title">App</string>
|
||||
<string name="home_notice_content">Hamesha Magisk ko uske official github release source se download karein. Unofficial source ki file khatarnak ho sakti hai.</string>
|
||||
<string name="home_support_title">Humein Support karo</string>
|
||||
<string name="home_follow_title">Humein Karo</string>
|
||||
<string name="home_item_source">Source</string>
|
||||
<string name="home_support_content">Magisk hamesha free aur open source rahega. Agar aap support karna chahte ho, toh donation de sakte ho.</string>
|
||||
<string name="home_installed_version">Jo version install hai</string>
|
||||
<string name="home_latest_version">Latest</string>
|
||||
<string name="invalid_update_channel">Galat update channel</string>
|
||||
<string name="uninstall_magisk_title">Magisk uninstall karo</string>
|
||||
<string name="uninstall_magisk_msg">Saare modules disable ya remove ho jayenge!\nRoot khatam ho jayega!\nAgar Magisk ne storage ko decrypt kiya tha toh wo phir se encrypt ho jayega!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Force encryption ko preserve karo</string>
|
||||
<string name="keep_dm_verity">AVB 2.0/dm-verity ko preserve karo</string>
|
||||
<string name="recovery_mode">Recovery mode</string>
|
||||
<string name="install_options_title">Options</string>
|
||||
<string name="install_method_title">Method</string>
|
||||
<string name="install_next">Aage badho</string>
|
||||
<string name="install_start">Chalo shuru karein</string>
|
||||
<string name="manager_download_install">Download aur install karne ke liye dabao</string>
|
||||
<string name="direct_install">Direct install (Recommended)</string>
|
||||
<string name="install_inactive_slot">Inactive slot par install karo (OTA ke baad)</string>
|
||||
<string name="install_inactive_slot_msg">Reboot ke baad device ko inactive slot se boot karna padega!\nSirf tab use karo jab OTA complete ho chuka ho.\nAage badhna hai?</string>
|
||||
<string name="setup_title">Extra setup</string>
|
||||
<string name="select_patch_file">File select karke patch karo</string>
|
||||
<string name="patch_file_msg">Raw image (*.img), ODIN tar file (*.tar), ya payload.bin (*.bin) select karo</string>
|
||||
<string name="reboot_delay_toast">Phone 5 second mein reboot hoga…</string>
|
||||
<string name="flash_screen_title">Installation ho rahi hai</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">ROOT access maang rha hai</string>
|
||||
<string name="touch_filtered_warning">Ek app Superuser request ko cover kar raha hai, isliye Magisk aapka response verify nahi kar sakta.</string>
|
||||
<string name="deny">Nahi</string>
|
||||
<string name="prompt">Poocho</string>
|
||||
<string name="restrict">Restrict</string>
|
||||
<string name="grant">Haan Theek hai</string>
|
||||
<string name="su_warning">Yeh full access dega device ko.\nAgar sure nahi ho toh deny kar do!</string>
|
||||
<string name="forever">Hamesha ke liye</string>
|
||||
<string name="once">Ek baar ke liye</string>
|
||||
<string name="tenmin">10 mins</string>
|
||||
<string name="twentymin">20 mins</string>
|
||||
<string name="thirtymin">30 mins</string>
|
||||
<string name="sixtymin">60 mins</string>
|
||||
<string name="su_allow_toast">%1$s ko ROOT access de diya gaya</string>
|
||||
<string name="su_deny_toast">%1$s ko ROOT access nahi diya gaya</string>
|
||||
<string name="su_snack_grant">%1$s ko ROOT access mila</string>
|
||||
<string name="su_snack_deny">%1$s ka ROOT access deny hua</string>
|
||||
<string name="su_snack_notif_on">%1$s ke notifications ON hain</string>
|
||||
<string name="su_snack_notif_off">%1$s ke notifications OFF hain</string>
|
||||
<string name="su_snack_log_on">%1$s ka logging ON hai</string>
|
||||
<string name="su_snack_log_off">%1$s ka logging OFF hai</string>
|
||||
<string name="su_revoke_title">Access wapas lena hai?</string>
|
||||
<string name="su_revoke_msg">Kya aap confirm karte ho ki %1$s ka ROOT access hata diya jaye?</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="none">Kuch nahi</string>
|
||||
<string name="superuser_toggle_notification">Notifications</string>
|
||||
<string name="superuser_toggle_revoke">Wapas lelo</string>
|
||||
<string name="superuser_policy_none">Ab tak kisi bhi app ne ROOT access nahi manga hai</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Koi logs nahi mile. Shayad tumhe root apps ka zyada use krna chahiye.</string>
|
||||
<string name="log_data_magisk_none">Ajeeb baat hai, Yaha toh logs hain hi nahi.</string>
|
||||
<string name="menuSaveLog">Log save karo</string>
|
||||
<string name="menuClearLog">Log clear karo</string>
|
||||
<string name="logs_cleared">Logs clear ho gaye</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Target UID: %1$d</string>
|
||||
<string name="target_pid">Target PID: %s</string>
|
||||
<string name="selinux_context">SELinux context: %s</string>
|
||||
<string name="supp_group">Supplementary group: %s</string>
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">System apps dikhao</string>
|
||||
<string name="show_os_app">OS apps dikhao</string>
|
||||
<string name="hide_filter_hint">Naam ke hisaab se filter karo</string>
|
||||
<string name="hide_search">Search karo</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Koi info nahi di gayi)</string>
|
||||
<string name="reboot_userspace">Soft reboot</string>
|
||||
<string name="reboot_recovery">Recovery mode mein reboot karo</string>
|
||||
<string name="reboot_bootloader">Bootloader mode mein reboot karo</string>
|
||||
<string name="reboot_download">Download mode mein reboot karo</string>
|
||||
<string name="reboot_edl">EDL mode mein reboot karo</string>
|
||||
<string name="reboot_safe_mode">Safe mode</string>
|
||||
<string name="module_version_author">%1$s ko %2$s ne banaya hai</string>
|
||||
<string name="module_state_remove">Delete karo</string>
|
||||
<string name="module_action">Action</string>
|
||||
<string name="module_state_restore">Wapas laao</string>
|
||||
<string name="module_action_install_external">Storage se install karo</string>
|
||||
<string name="update_available">Naya update available hai</string>
|
||||
<string name="suspend_text_riru">Module suspend kiya gaya kyunki %1$s enabled hai</string>
|
||||
<string name="suspend_text_zygisk">Bhai %1$s ON kr tabb ye module chalega</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk module compatible nahi tha, isliye load nahi hua</string>
|
||||
<string name="module_empty">Koi module install nahi hai</string>
|
||||
<string name="confirm_install">>%1$s module install karna hai?</string>
|
||||
<string name="confirm_install_title">Ek baar firse soch lo</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Theme mode</string>
|
||||
<string name="settings_dark_mode_message">Apni hisaab se mode choose karo!</string>
|
||||
<string name="settings_dark_mode_light">Hamesha light theme</string>
|
||||
<string name="settings_dark_mode_system">System ke saath follow karo</string>
|
||||
<string name="settings_dark_mode_dark">Hamesha dark theme</string>
|
||||
<string name="settings_download_path_title">Download path</string>
|
||||
<string name="settings_download_path_message">Files yahan save hongi: %1$s</string>
|
||||
<string name="settings_hide_app_title">Magisk app ko hide karo</string>
|
||||
<string name="settings_hide_app_summary">Magisk app ka name aur package ID change kro</string>
|
||||
<string name="settings_restore_app_title">Magisk app ko unhide karo</string>
|
||||
<string name="settings_restore_app_summary">App ko unhide karo aur original APK wapas lao</string>
|
||||
<string name="language">Language</string>
|
||||
<string name="system_default">(System ki default)</string>
|
||||
<string name="settings_check_update_title">Updates check karo</string>
|
||||
<string name="settings_check_update_summary">Background mein updates auto check honge</string>
|
||||
<string name="settings_update_channel_title">Update channel</string>
|
||||
<string name="settings_update_stable">Stable</string>
|
||||
<string name="settings_update_beta">Beta</string>
|
||||
<string name="settings_update_debug">Debug</string>
|
||||
<string name="settings_update_custom">Custom</string>
|
||||
<string name="settings_update_custom_msg">Apna custom channel URL daaloL</string>
|
||||
<string name="settings_zygisk_summary">Magisk ke kuch parts ko Zygote daemon mein run karo</string>
|
||||
<string name="settings_denylist_title">DenyList enforce karo</string>
|
||||
<string name="settings_denylist_summary">DenyList mein jo processes hain, unpe Magisk ka effect hata diya jayega</string>
|
||||
<string name="settings_denylist_config_title">DenyList set karo</string>
|
||||
<string name="settings_denylist_config_summary">Kaunse process DenyList mein daalne hain, select karo</string>
|
||||
<string name="settings_hosts_title">Systemless hosts</string>
|
||||
<string name="settings_hosts_summary">Ad-blocking apps ke liye systemless hosts support</string>
|
||||
<string name="settings_hosts_toast">Systemless hosts module add kar diya gaya</string>
|
||||
<string name="settings_app_name_hint">Naya naam</string>
|
||||
<string name="settings_app_name_helper">App is naam ke saath repack hoga</string>
|
||||
<string name="settings_app_name_error">Naam ka format galat hai</string>
|
||||
<string name="settings_su_app_adb">Apps aur ADB</string>
|
||||
<string name="settings_su_app">Sirf apps</string>
|
||||
<string name="settings_su_adb">Sirf ADB</string>
|
||||
<string name="settings_su_disable">Disable kiya gaya</string>
|
||||
<string name="settings_su_request_10">10 seconds</string>
|
||||
<string name="settings_su_request_15">15 seconds</string>
|
||||
<string name="settings_su_request_20">20 seconds</string>
|
||||
<string name="settings_su_request_30">30 seconds</string>
|
||||
<string name="settings_su_request_45">45 seconds</string>
|
||||
<string name="settings_su_request_60">60 seconds</string>
|
||||
<string name="superuser_access">ROOT access</string>
|
||||
<string name="auto_response">Automatic response</string>
|
||||
<string name="request_timeout">Request timeout</string>
|
||||
<string name="superuser_notification">ROOT notification</string>
|
||||
<string name="settings_su_reauth_title">Upgrade ke baad dobara permission puchho</string>
|
||||
<string name="settings_su_reauth_summary">App upgrade hone ke baad Superuser permission firse maangna</string>
|
||||
<string name="settings_su_tapjack_title">Tapjacking se protection</string>
|
||||
<string name="settings_su_tapjack_summary">Agar Superuser prompt kisi aur window ya overlay ke neeche chhup gaya ho, toh uspar tap kaam nahi karega</string>
|
||||
<string name="settings_su_auth_title">User authentication</string>
|
||||
<string name="settings_su_auth_summary">Superuser request ke time user se authentication maango</string>
|
||||
<string name="settings_su_auth_insecure">Device mein koi bhi authentication method set nahi hai</string>
|
||||
<string name="settings_su_restrict_title">Root access ko limit karo</string>
|
||||
<string name="settings_su_restrict_summary">Nayeapps ko ROOT access maangne se block karega. Warning: Isse zyada tarr apps kaam karna band kar denge. Sirf tab enable karo jab aapko pata ho aap kya kar rahe ho.</string>
|
||||
<string name="settings_customization">Customization</string>
|
||||
<string name="setting_add_shortcut_summary">Agar app hide karne ke baad uska naam ya icon samajhne mein dikkat ho rahi ho, toh home screen pe ek shortcut add kar lo</string>
|
||||
<string name="settings_doh_title">DNS over HTTPS</string>
|
||||
<string name="settings_doh_description">DNS poisoning se bachne ke liye (kuch countries mein zaroori padti hai)</string>
|
||||
<string name="settings_random_name_title">Output file ka naam random karo</string>
|
||||
<string name="settings_random_name_description">Patched images aur tar files ka naam random bana ke detection se bachao</string>
|
||||
<string name="multiuser_mode">Multiuser mode</string>
|
||||
<string name="settings_owner_only">Sirf device owner</string>
|
||||
<string name="settings_owner_manage">Device owner manage karega</string>
|
||||
<string name="settings_user_independent">Har user ke liye alag</string>
|
||||
<string name="owner_only_summary">Sirf device owner ko root access milega</string>
|
||||
<string name="owner_manage_summary">Sirf owner root access manage kar sakta hai aur requests receive karega</string>
|
||||
<string name="user_independent_summary">Har user ke liye alag root rules honge</string>
|
||||
<string name="mount_namespace_mode">Mount namespace mode</string>
|
||||
<string name="settings_ns_global">Global namespace</string>
|
||||
<string name="settings_ns_requester">Inherit namespace</string>
|
||||
<string name="settings_ns_isolate">Isolated namespace</string>
|
||||
<string name="global_summary">Sabhi root sessions ek hi global mount namespace use karenge</string>
|
||||
<string name="requester_summary">Root session apne requester ka namespace inherit karega</string>
|
||||
<string name="isolate_summary">Har root session ka apna alag isolated namespace hoga</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Magisk updates</string>
|
||||
<string name="progress_channel">Progress notifications</string>
|
||||
<string name="updated_channel">Update ho gaya</string>
|
||||
<string name="download_complete">Download ho gaya</string>
|
||||
<string name="download_file_error">File download karne mein error aaya</string>
|
||||
<string name="magisk_update_title">Magisk ka naya update available hai!</string>
|
||||
<string name="updated_title">Magisk update ho gaya</string>
|
||||
<string name="updated_text">App open karne ke liye tap karo</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Haan</string>
|
||||
<string name="no">Nahi</string>
|
||||
<string name="repo_install_title">%1$s %2$s(%3$d) Install karo</string>
|
||||
<string name="download">Download karo</string>
|
||||
<string name="reboot">Reboot karo</string>
|
||||
<string name="close">Close karo</string>
|
||||
<string name="release_notes">Release notes</string>
|
||||
<string name="flashing">Flash ho raha hai…</string>
|
||||
<string name="running">Chal raha hai…</string>
|
||||
<string name="done">Ho gaya!</string>
|
||||
<string name="done_action">%1$s ka action complete ho gaya</string>
|
||||
<string name="failure">Fail ho gaya!</string>
|
||||
<string name="hide_app_title">Magisk app ko chhupa rahe hain…</string>
|
||||
<string name="open_link_failed_toast">Link kholne ke liye koi app nahi mila</string>
|
||||
<string name="complete_uninstall">Sab kuch uninstall karo</string>
|
||||
<string name="restore_img">Images restore karo</string>
|
||||
<string name="restore_img_msg">Restore kiya ja raha hai…</string>
|
||||
<string name="restore_done">Restore complete ho gaya!</string>
|
||||
<string name="restore_fail">Stock backup available nahi hai!</string>
|
||||
<string name="setup_fail">Setup fail ho gaya</string>
|
||||
<string name="env_fix_title">Iske liye thoda extra setup chahiye</string>
|
||||
<string name="env_fix_msg">Magisk ko sahi se chalane ke liye thoda aur setup karna padega. Kya aap proceed karke device reboot karna chahte ho?</string>
|
||||
<string name="env_full_fix_msg">Magisk ko properly chalane ke liye aapko usse dubara flash karna padega. App ke andar se Magisk reinstall karo, kyunki Recovery mode sahi device info nahi de pata.</string>
|
||||
<string name="setup_msg">Environment setup chal raha hai…</string>
|
||||
<string name="unsupport_magisk_title">Magisk version supported nahi hain</string>
|
||||
<string name="unsupport_magisk_msg">Is app version ko %1$s se purani Magisk versions support nahi karti.\n\nApp aise behave karega jaise Magisk install hi nahi hai. Jaldi se Magisk ko update karo.</string>
|
||||
<string name="unsupport_general_title">System thoda alag behave kar raha hai</string>
|
||||
<string name="unsupport_system_app_msg">App ko system app bana ke chalana supported nahi hai. Please app ko wapas user app bana do.</string>
|
||||
<string name="unsupport_other_su_msg">Pehle doosri root method hatao ya Magisk dobara install karo.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk external storage pe installed hai. App ko internal storage mein move karo.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Hidden Magisk app ab kaam nahi karega kyunki root chala gaya hai. Please original APK restore karo.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Iss feature ko enable karne ke liye storage permission allow karo</string>
|
||||
<string name="post_notifications_denied">Is feature ke liye notifications permission allow karo</string>
|
||||
<string name="install_unknown_denied">\"Install unknown apps\" ki permission allow karo taaki ye feature kaam kar sakey</string>
|
||||
<string name="add_shortcut_title">Shortcut home screen pe add karo</string>
|
||||
<string name="add_shortcut_msg">App ko hide karne ke baad agar uska icon ya naam pehchanna mushkil ho, toh ek shortcut home screen pe add kar lein?</string>
|
||||
<string name="app_not_found">Is action ko handle karne ke liye koi app nahi mila</string>
|
||||
<string name="reboot_apply_change">Changes apply karne ke liye reboot krna zaroori hai</string>
|
||||
<string name="restore_app_confirmation">Ye action hidden app ko original version mein wapas laayega. Kya aap sach mein ye karna chahte ho?</string>
|
||||
|
||||
</resources>
|
||||
7
app/stub/src/main/res/values-hn/strings.xml
Normal file
7
app/stub/src/main/res/values-hn/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Setup complete karne ke liye full Magisk install karna hoga. Abhi download aur install karein?</string>
|
||||
<string name="no_internet_msg">Full Magisk upgrade ke liye Internet se connect hona zaroori hai!</string>
|
||||
<string name="dling">Download ho raha hai…</string>
|
||||
<string name="relaunch_app">App ko reopen karo</string>
|
||||
</resources>
|
||||
3
build.py
3
build.py
@@ -11,7 +11,6 @@ import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import textwrap
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from zipfile import ZipFile
|
||||
@@ -71,7 +70,7 @@ default_archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
|
||||
default_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
||||
support_targets = default_targets | {"resetprop"}
|
||||
rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
||||
ondk_version = "r28.5"
|
||||
ondk_version = "r29.2"
|
||||
|
||||
# Global vars
|
||||
config = {}
|
||||
|
||||
@@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \
|
||||
core/sqlite.cpp \
|
||||
core/thread.cpp \
|
||||
core/core-rs.cpp \
|
||||
core/resetprop/resetprop.cpp \
|
||||
core/resetprop/sys.cpp \
|
||||
core/su/su.cpp \
|
||||
core/zygisk/entry.cpp \
|
||||
core/zygisk/module.cpp \
|
||||
@@ -81,14 +81,11 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := magiskboot
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libbase \
|
||||
liblzma \
|
||||
liblz4 \
|
||||
libboot-rs
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
boot/main.cpp \
|
||||
boot/bootimg.cpp \
|
||||
boot/format.cpp \
|
||||
boot/boot-rs.cpp
|
||||
|
||||
LOCAL_LDFLAGS := -static
|
||||
@@ -126,7 +123,7 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
core/applet_stub.cpp \
|
||||
core/resetprop/resetprop.cpp \
|
||||
core/resetprop/sys.cpp \
|
||||
core/core-rs.cpp
|
||||
|
||||
LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=resetprop_main
|
||||
|
||||
298
native/src/Cargo.lock
generated
298
native/src/Cargo.lock
generated
@@ -93,10 +93,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
@@ -115,18 +118,18 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.1"
|
||||
version = "1.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||
checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.10.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
|
||||
checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -150,33 +153,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.31"
|
||||
version = "1.2.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
|
||||
checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.42"
|
||||
version = "4.5.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
|
||||
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.42"
|
||||
version = "4.5.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
|
||||
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -191,10 +194,11 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
@@ -234,6 +238,21 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
@@ -257,6 +276,16 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.0-rc.3"
|
||||
@@ -279,7 +308,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.137"
|
||||
version = "1.0.170"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-cmd",
|
||||
@@ -290,9 +319,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-gen"
|
||||
version = "0.7.137"
|
||||
version = "0.7.170"
|
||||
dependencies = [
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -300,10 +330,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.137"
|
||||
version = "1.0.170"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -311,12 +342,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.137"
|
||||
version = "1.0.170"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.137"
|
||||
version = "1.0.170"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
@@ -338,9 +370,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "der_derive"
|
||||
version = "0.8.0-rc.3"
|
||||
version = "0.8.0-rc.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d184a65ac0a9db9e66e0d629bea86d0402198b2a5cc01c9c16de7a6537f0ac4f"
|
||||
checksum = "14bfffadecb79dfde429f5dcd7553780c2cea5f7d0e72ad7c37a74f1ef79230a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -356,15 +388,25 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common 0.1.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460dd7f37e4950526b54a5a6b1f41b6c8e763c58eb9a8fc8fc05ba5c2f44ca7b"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.11.0-rc.4",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"crypto-common 0.2.0-rc.3",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -375,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112839e868b3376c2066506d42331023165d687a7ed38b2ed77f28763d9a7742"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"signature",
|
||||
@@ -391,7 +433,7 @@ checksum = "c28ecec37eea07ab976cea93c7ce8b36d561cf161f6767925c1edc51024b0ad3"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
"ff",
|
||||
"group",
|
||||
"hybrid-array",
|
||||
@@ -403,6 +445,12 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "fdt"
|
||||
version = "0.1.5"
|
||||
@@ -438,9 +486,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
@@ -465,13 +523,19 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.13.0-rc.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dc6a2fcc35ab09136c6df2cdf9ca49790701420a3a6b5db0987dddbabc79b21"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -485,16 +549,26 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.1"
|
||||
name = "indexmap"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775bf80d5878ab7c2b1080b5351a48b2f737d9f6f8b383574eebcc22be0dfccb"
|
||||
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.175"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -534,16 +608,20 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzma-sys"
|
||||
version = "0.1.20"
|
||||
name = "lzma-rust2"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43aa8ef7ba6927e84cfb39120bbc06c29474346b956f7b88cc4899b1f2e86e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"crc",
|
||||
"sha2 0.10.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "magisk"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"argh",
|
||||
"base",
|
||||
"bit-set",
|
||||
"bytemuck",
|
||||
@@ -569,10 +647,11 @@ dependencies = [
|
||||
"cxx",
|
||||
"cxx-gen",
|
||||
"der",
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
"fdt",
|
||||
"flate2",
|
||||
"lz4",
|
||||
"lzma-rust2",
|
||||
"num-traits",
|
||||
"p256",
|
||||
"p384",
|
||||
@@ -581,10 +660,9 @@ dependencies = [
|
||||
"quick-protobuf",
|
||||
"rsa",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"sha2 0.11.0-rc.0",
|
||||
"size",
|
||||
"x509-cert",
|
||||
"xz2",
|
||||
"zopfli",
|
||||
]
|
||||
|
||||
@@ -669,7 +747,7 @@ dependencies = [
|
||||
"elliptic-curve",
|
||||
"primefield",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
"sha2 0.11.0-rc.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -682,7 +760,7 @@ dependencies = [
|
||||
"elliptic-curve",
|
||||
"primefield",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
"sha2 0.11.0-rc.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -697,7 +775,7 @@ dependencies = [
|
||||
"primefield",
|
||||
"primeorder",
|
||||
"rand_core",
|
||||
"sha2",
|
||||
"sha2 0.11.0-rc.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -762,9 +840,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -820,11 +898,11 @@ dependencies = [
|
||||
"const-oid",
|
||||
"crypto-bigint",
|
||||
"crypto-primes",
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"sha2",
|
||||
"sha2 0.11.0-rc.0",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
@@ -839,9 +917,9 @@ checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
@@ -894,7 +972,18 @@ checksum = "6f9318facddf9ac32a33527066936837e189b3f23ced6edc1603720ead5e2b3d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -905,7 +994,7 @@ checksum = "aa1d2e6b3cc4e43a8258a9a3b17aa5dfd2cc5186c7024bba8a64aa65b2c71a59"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -920,7 +1009,7 @@ version = "3.0.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4835c3b5ecb10171941a4998a95a3a76ecac1c5ae8e6954f2ad030acd1c7e8ab"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.11.0-rc.0",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
@@ -960,9 +1049,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -980,18 +1069,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1033,9 +1122,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -1044,38 +1133,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.3+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
@@ -1088,60 +1190,57 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
name = "wit-bindgen"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
|
||||
|
||||
[[package]]
|
||||
name = "x509-cert"
|
||||
@@ -1155,15 +1254,6 @@ dependencies = [
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xz2"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
|
||||
dependencies = [
|
||||
"lzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
|
||||
@@ -10,20 +10,20 @@ edition = "2024"
|
||||
[workspace.dependencies]
|
||||
cxx = { path = "external/cxx-rs" }
|
||||
cxx-gen = { path = "external/cxx-rs/gen/lib" }
|
||||
libc = "0.2.174"
|
||||
cfg-if = "1.0.1"
|
||||
libc = "0.2.175"
|
||||
cfg-if = "1.0.3"
|
||||
num-traits = "0.2.19"
|
||||
num-derive = "0.4.2"
|
||||
thiserror = "2.0.12"
|
||||
thiserror = "2.0.16"
|
||||
byteorder = "1.5.0"
|
||||
size = "0.5.0"
|
||||
bytemuck = "1.23.1"
|
||||
bytemuck = "1.23.2"
|
||||
fdt = "0.1.5"
|
||||
const_format = "0.2.34"
|
||||
bit-set = "0.8.0"
|
||||
syn = "2.0.104"
|
||||
syn = "2.0.106"
|
||||
quote = "1.0.40"
|
||||
proc-macro2 = "1.0.95"
|
||||
proc-macro2 = "1.0.101"
|
||||
argh = { version = "0.1.13", default-features = false }
|
||||
pb-rs = { version = "0.10.0", default-features = false }
|
||||
quick-protobuf = "0.8.1"
|
||||
@@ -31,7 +31,7 @@ flate2 = { version = "1.1.2", default-features = false }
|
||||
bzip2 = { version = "0.6.0" }
|
||||
zopfli = "0.8.2"
|
||||
lz4 = "1.28.1"
|
||||
xz2 = "0.1.7"
|
||||
lzma-rust2 = { version = "0.11.0", default-features = false, features = ["xz", "std", "encoder", "optimization"] }
|
||||
|
||||
# Rust crypto crates are tied together
|
||||
sha1 = "0.11.0-rc.0"
|
||||
@@ -40,15 +40,14 @@ digest = "0.11.0-rc.0"
|
||||
p256 = "0.14.0-pre.9"
|
||||
p384 = "0.14.0-pre.9"
|
||||
p521 = "0.14.0-pre.9"
|
||||
rsa = "0.10.0-rc.3"
|
||||
rsa = "=0.10.0-rc.3"
|
||||
x509-cert = "0.3.0-rc.1"
|
||||
der = "0.8.0-rc.7"
|
||||
der = "=0.8.0-rc.7"
|
||||
|
||||
[patch.crates-io]
|
||||
pb-rs = { git = "https://github.com/tafia/quick-protobuf.git" }
|
||||
quick-protobuf = { git = "https://github.com/tafia/quick-protobuf.git" }
|
||||
lz4-sys = { path = "external/lz4-sys" }
|
||||
lzma-sys = { path = "external/lzma-sys" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = "z"
|
||||
|
||||
@@ -7,17 +7,12 @@ LOCAL_MODULE := libbase
|
||||
LOCAL_C_INCLUDES := \
|
||||
src/include \
|
||||
$(LOCAL_PATH)/include \
|
||||
src/external/cxx-rs/include \
|
||||
out/generated
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_EXPORT_STATIC_LIBRARIES := libcxx
|
||||
LOCAL_STATIC_LIBRARIES := libcxx
|
||||
LOCAL_CFLAGS := -DRUST_CXX_NO_EXCEPTIONS
|
||||
LOCAL_SRC_FILES := \
|
||||
new.cpp \
|
||||
files.cpp \
|
||||
misc.cpp \
|
||||
logging.cpp \
|
||||
base.cpp \
|
||||
base-rs.cpp \
|
||||
../external/cxx-rs/src/cxx.cc
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <android/log.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <syscall.h>
|
||||
@@ -11,18 +13,27 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifndef __call_bypassing_fortify
|
||||
#define __call_bypassing_fortify(fn) (&fn)
|
||||
#endif
|
||||
|
||||
// Override libc++ new implementation to optimize final build size
|
||||
|
||||
void* operator new(std::size_t s) { return std::malloc(s); }
|
||||
void* operator new[](std::size_t s) { return std::malloc(s); }
|
||||
void operator delete(void *p) { std::free(p); }
|
||||
void operator delete[](void *p) { std::free(p); }
|
||||
void* operator new(std::size_t s, const std::nothrow_t&) noexcept { return std::malloc(s); }
|
||||
void* operator new[](std::size_t s, const std::nothrow_t&) noexcept { return std::malloc(s); }
|
||||
void operator delete(void *p, const std::nothrow_t&) noexcept { std::free(p); }
|
||||
void operator delete[](void *p, const std::nothrow_t&) noexcept { std::free(p); }
|
||||
|
||||
bool byte_view::contains(byte_view pattern) const {
|
||||
return _buf != nullptr && memmem(_buf, _sz, pattern._buf, pattern._sz) != nullptr;
|
||||
}
|
||||
|
||||
bool byte_view::equals(byte_view o) const {
|
||||
return _sz == o._sz && memcmp(_buf, o._buf, _sz) == 0;
|
||||
}
|
||||
|
||||
heap_data byte_view::clone() const {
|
||||
heap_data copy(_sz);
|
||||
memcpy(copy._buf, _buf, _sz);
|
||||
return copy;
|
||||
bool byte_view::operator==(byte_view rhs) const {
|
||||
return _sz == rhs._sz && memcmp(_buf, rhs._buf, _sz) == 0;
|
||||
}
|
||||
|
||||
void byte_data::swap(byte_data &o) {
|
||||
@@ -30,28 +41,25 @@ void byte_data::swap(byte_data &o) {
|
||||
std::swap(_sz, o._sz);
|
||||
}
|
||||
|
||||
rust::Vec<size_t> byte_data::patch(byte_view from, byte_view to) {
|
||||
rust::Vec<size_t> byte_data::patch(byte_view from, byte_view to) const {
|
||||
rust::Vec<size_t> v;
|
||||
if (_buf == nullptr)
|
||||
return v;
|
||||
auto p = _buf;
|
||||
auto eof = _buf + _sz;
|
||||
while (p < eof) {
|
||||
p = static_cast<uint8_t *>(memmem(p, eof - p, from.buf(), from.sz()));
|
||||
p = static_cast<uint8_t *>(memmem(p, eof - p, from.data(), from.size()));
|
||||
if (p == nullptr)
|
||||
return v;
|
||||
memset(p, 0, from.sz());
|
||||
memcpy(p, to.buf(), to.sz());
|
||||
memset(p, 0, from.size());
|
||||
memcpy(p, to.data(), to.size());
|
||||
v.push_back(p - _buf);
|
||||
p += from.sz();
|
||||
p += from.size();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
rust::Vec<size_t> mut_u8_patch(
|
||||
rust::Slice<uint8_t> buf,
|
||||
rust::Slice<const uint8_t> from,
|
||||
rust::Slice<const uint8_t> to) {
|
||||
rust::Vec<size_t> mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to) {
|
||||
byte_data data(buf);
|
||||
return data.patch(from, to);
|
||||
}
|
||||
@@ -258,18 +266,167 @@ size_t strscpy(char *dest, const char *src, size_t size) {
|
||||
return std::min(strlcpy(dest, src, size), size - 1);
|
||||
}
|
||||
|
||||
extern "C" void cxx$utf8str$new(rust::Utf8CStr *self, const void *s, size_t len);
|
||||
extern "C" const char *cxx$utf8str$ptr(const rust::Utf8CStr *self);
|
||||
extern "C" size_t cxx$utf8str$len(const rust::Utf8CStr *self);
|
||||
#undef vsnprintf
|
||||
static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) {
|
||||
constexpr int sz = 4096;
|
||||
char buf[sz];
|
||||
buf[0] = '\0';
|
||||
// Fortify logs when a fatal error occurs. Do not run through fortify again
|
||||
int len = std::min(__call_bypassing_fortify(vsnprintf)(buf, sz, fmt, ap), sz - 1);
|
||||
log_with_rs(level, Utf8CStr(buf, len + 1));
|
||||
return len;
|
||||
}
|
||||
|
||||
rust::Utf8CStr::Utf8CStr(const char *s, size_t len) {
|
||||
// Used to override external C library logging
|
||||
extern "C" int magisk_log_print(int prio, const char *tag, const char *fmt, ...) {
|
||||
LogLevel level;
|
||||
switch (prio) {
|
||||
case ANDROID_LOG_DEBUG:
|
||||
level = LogLevel::Debug;
|
||||
break;
|
||||
case ANDROID_LOG_INFO:
|
||||
level = LogLevel::Info;
|
||||
break;
|
||||
case ANDROID_LOG_WARN:
|
||||
level = LogLevel::Warn;
|
||||
break;
|
||||
case ANDROID_LOG_ERROR:
|
||||
level = LogLevel::ErrorCxx;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
char fmt_buf[4096];
|
||||
auto len = strscpy(fmt_buf, tag, sizeof(fmt_buf) - 1);
|
||||
// Prevent format specifications in the tag
|
||||
std::replace(fmt_buf, fmt_buf + len, '%', '_');
|
||||
len = ssprintf(fmt_buf + len, sizeof(fmt_buf) - len - 1, ": %s", fmt) + len;
|
||||
// Ensure the fmt string always ends with newline
|
||||
if (fmt_buf[len - 1] != '\n') {
|
||||
fmt_buf[len] = '\n';
|
||||
fmt_buf[len + 1] = '\0';
|
||||
}
|
||||
va_list argv;
|
||||
va_start(argv, fmt);
|
||||
int ret = fmt_and_log_with_rs(level, fmt_buf, argv);
|
||||
va_end(argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define LOG_BODY(level) \
|
||||
va_list argv; \
|
||||
va_start(argv, fmt); \
|
||||
fmt_and_log_with_rs(LogLevel::level, fmt, argv); \
|
||||
va_end(argv); \
|
||||
|
||||
// LTO will optimize out the NOP function
|
||||
#if MAGISK_DEBUG
|
||||
void LOGD(const char *fmt, ...) { LOG_BODY(Debug) }
|
||||
#else
|
||||
void LOGD(const char *fmt, ...) {}
|
||||
#endif
|
||||
void LOGI(const char *fmt, ...) { LOG_BODY(Info) }
|
||||
void LOGW(const char *fmt, ...) { LOG_BODY(Warn) }
|
||||
void LOGE(const char *fmt, ...) { LOG_BODY(ErrorCxx) }
|
||||
|
||||
// Export raw symbol to fortify compat
|
||||
extern "C" void __vloge(const char* fmt, va_list ap) {
|
||||
fmt_and_log_with_rs(LogLevel::ErrorCxx, fmt, ap);
|
||||
}
|
||||
|
||||
string full_read(int fd) {
|
||||
string str;
|
||||
char buf[4096];
|
||||
for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;)
|
||||
str.insert(str.end(), buf, buf + len);
|
||||
return str;
|
||||
}
|
||||
|
||||
string full_read(const char *filename) {
|
||||
string str;
|
||||
if (int fd = xopen(filename, O_RDONLY | O_CLOEXEC); fd >= 0) {
|
||||
str = full_read(fd);
|
||||
close(fd);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void write_zero(int fd, size_t size) {
|
||||
char buf[4096] = {0};
|
||||
size_t len;
|
||||
while (size > 0) {
|
||||
len = sizeof(buf) > size ? size : sizeof(buf);
|
||||
write(fd, buf, len);
|
||||
size -= len;
|
||||
}
|
||||
}
|
||||
|
||||
sDIR make_dir(DIR *dp) {
|
||||
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
|
||||
}
|
||||
|
||||
sFILE make_file(FILE *fp) {
|
||||
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; });
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(const char *name, bool rw) {
|
||||
auto slice = rust::map_file(name, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(int dirfd, const char *name, bool rw) {
|
||||
auto slice = rust::map_file_at(dirfd, name, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(int fd, size_t sz, bool rw) {
|
||||
auto slice = rust::map_fd(fd, sz, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::~mmap_data() {
|
||||
if (_buf)
|
||||
munmap(_buf, _sz);
|
||||
}
|
||||
|
||||
string resolve_preinit_dir(const char *base_dir) {
|
||||
string dir = base_dir;
|
||||
if (access((dir + "/unencrypted").data(), F_OK) == 0) {
|
||||
dir += "/unencrypted/magisk";
|
||||
} else if (access((dir + "/adb").data(), F_OK) == 0) {
|
||||
dir += "/adb";
|
||||
} else if (access((dir + "/watchdog").data(), F_OK) == 0) {
|
||||
dir += "/watchdog/magisk";
|
||||
} else {
|
||||
dir += "/magisk";
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
// FFI for Utf8CStr
|
||||
|
||||
extern "C" void cxx$utf8str$new(Utf8CStr *self, const void *s, size_t len);
|
||||
extern "C" const char *cxx$utf8str$ptr(const Utf8CStr *self);
|
||||
extern "C" size_t cxx$utf8str$len(const Utf8CStr *self);
|
||||
|
||||
Utf8CStr::Utf8CStr(const char *s, size_t len) {
|
||||
cxx$utf8str$new(this, s, len);
|
||||
}
|
||||
|
||||
const char *rust::Utf8CStr::data() const {
|
||||
const char *Utf8CStr::data() const {
|
||||
return cxx$utf8str$ptr(this);
|
||||
}
|
||||
|
||||
size_t rust::Utf8CStr::length() const {
|
||||
size_t Utf8CStr::length() const {
|
||||
return cxx$utf8str$len(this);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use std::fmt::{Debug, Display, Formatter, Write};
|
||||
use std::ops::Deref;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::Utf8Error;
|
||||
use std::str::{FromStr, Utf8Error};
|
||||
use std::{fmt, mem, slice, str};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -56,7 +56,7 @@ pub mod buf {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn wrap(buf: &mut [u8]) -> Utf8CStrBufRef {
|
||||
pub fn wrap(buf: &mut [u8]) -> Utf8CStrBufRef<'_> {
|
||||
Utf8CStrBufRef::from(buf)
|
||||
}
|
||||
|
||||
@@ -200,7 +200,18 @@ impl From<String> for Utf8CString {
|
||||
|
||||
impl From<&str> for Utf8CString {
|
||||
fn from(value: &str) -> Self {
|
||||
value.to_string().into()
|
||||
let mut s = String::with_capacity(value.len() + 1);
|
||||
s.push_str(value);
|
||||
s.nul_terminate();
|
||||
Utf8CString(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Utf8CString {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,6 +311,13 @@ impl Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn from_raw_parts<'a>(ptr: *const c_char, len: usize) -> &'a Utf8CStr {
|
||||
unsafe {
|
||||
let bytes = slice::from_raw_parts(ptr.cast(), len);
|
||||
Self::from_bytes_unchecked(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
&self.0
|
||||
@@ -351,7 +369,7 @@ impl AsRef<Utf8CStr> for Utf8CStr {
|
||||
|
||||
// Notice that we only implement ExternType on Utf8CStr *reference*
|
||||
unsafe impl ExternType for &Utf8CStr {
|
||||
type Id = type_id!("rust::Utf8CStr");
|
||||
type Id = type_id!("Utf8CStr");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
// Functions in this file are only for exporting to C++, DO NOT USE IN RUST
|
||||
|
||||
use std::os::fd::{BorrowedFd, OwnedFd, RawFd};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::fd::{BorrowedFd, FromRawFd, OwnedFd, RawFd};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use libc::{c_char, mode_t};
|
||||
use libc::{O_RDONLY, c_char, mode_t};
|
||||
|
||||
use crate::ffi::{FnBoolStr, FnBoolStrStr};
|
||||
use crate::files::map_file_at;
|
||||
pub(crate) use crate::xwrap::*;
|
||||
use crate::{
|
||||
CxxResultExt, Directory, OsResultStatic, Utf8CStr, clone_attr, cstr, fclone_attr, fd_path,
|
||||
BufReadExt, CxxResultExt, Directory, OsResultStatic, Utf8CStr, clone_attr, cstr, fclone_attr,
|
||||
map_fd, map_file, slice_from_ptr,
|
||||
};
|
||||
|
||||
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
||||
let mut buf = cstr::buf::wrap(buf);
|
||||
fd_path(fd, &mut buf)
|
||||
.log_cxx()
|
||||
.map_or(-1_isize, |_| buf.len() as isize)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
unsafe {
|
||||
@@ -112,10 +110,10 @@ pub(crate) unsafe fn readlinkat(
|
||||
#[unsafe(export_name = "cp_afc")]
|
||||
unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
return src.copy_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src)
|
||||
&& let Ok(dest) = Utf8CStr::from_ptr(dest)
|
||||
{
|
||||
return src.copy_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -124,10 +122,10 @@ unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) ->
|
||||
#[unsafe(export_name = "mv_path")]
|
||||
unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
return src.move_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src)
|
||||
&& let Ok(dest) = Utf8CStr::from_ptr(dest)
|
||||
{
|
||||
return src.move_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -136,10 +134,10 @@ unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) ->
|
||||
#[unsafe(export_name = "link_path")]
|
||||
unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
return src.link_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src)
|
||||
&& let Ok(dest) = Utf8CStr::from_ptr(dest)
|
||||
{
|
||||
return src.link_to(dest).log_cxx().is_ok();
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -148,10 +146,10 @@ unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char)
|
||||
#[unsafe(export_name = "clone_attr")]
|
||||
unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
return clone_attr(src, dest).log_cxx().is_ok();
|
||||
}
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src)
|
||||
&& let Ok(dest) = Utf8CStr::from_ptr(dest)
|
||||
{
|
||||
return clone_attr(src, dest).log_cxx().is_ok();
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -178,3 +176,14 @@ unsafe extern "C" fn str_ptr(this: &&Utf8CStr) -> *const u8 {
|
||||
unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize {
|
||||
this.len()
|
||||
}
|
||||
|
||||
pub(crate) fn parse_prop_file_rs(name: &Utf8CStr, f: &FnBoolStrStr) {
|
||||
if let Ok(file) = name.open(O_RDONLY) {
|
||||
BufReader::new(file).for_each_prop(|key, value| f.call(key, value))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn file_readline_for_cxx(fd: RawFd, f: &FnBoolStr) {
|
||||
let mut fd = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
|
||||
BufReader::new(fd.deref_mut()).for_each_line(|line| f.call(Utf8CStr::from_string(line)));
|
||||
}
|
||||
|
||||
@@ -63,16 +63,16 @@ impl DirEntry<'_> {
|
||||
self.d_type == libc::DT_SOCK
|
||||
}
|
||||
|
||||
pub fn unlink(&self) -> OsResult<()> {
|
||||
pub fn unlink(&self) -> OsResult<'_, ()> {
|
||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||
self.dir.unlink_at(self.name(), flag)
|
||||
}
|
||||
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
|
||||
self.dir.read_link_at(self.name(), buf)
|
||||
}
|
||||
|
||||
pub fn open_as_dir(&self) -> OsResult<Directory> {
|
||||
pub fn open_as_dir(&self) -> OsResult<'_, Directory> {
|
||||
if !self.is_dir() {
|
||||
return Err(OsError::with_os_error(
|
||||
libc::ENOTDIR,
|
||||
@@ -84,7 +84,7 @@ impl DirEntry<'_> {
|
||||
self.dir.open_as_dir_at(self.name())
|
||||
}
|
||||
|
||||
pub fn open_as_file(&self, flags: i32) -> OsResult<File> {
|
||||
pub fn open_as_file(&self, flags: i32) -> OsResult<'_, File> {
|
||||
if self.is_dir() {
|
||||
return Err(OsError::with_os_error(
|
||||
libc::EISDIR,
|
||||
@@ -141,7 +141,7 @@ pub enum WalkResult {
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
fn borrow(&self) -> BorrowedDirectory {
|
||||
fn borrow(&self) -> BorrowedDirectory<'_> {
|
||||
BorrowedDirectory {
|
||||
inner: self.inner,
|
||||
phantom: PhantomData,
|
||||
@@ -164,13 +164,13 @@ impl Directory {
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
pub fn open(path: &Utf8CStr) -> OsResult<Directory> {
|
||||
pub fn open(path: &Utf8CStr) -> OsResult<'_, Directory> {
|
||||
let dirp = unsafe { libc::opendir(path.as_ptr()) };
|
||||
let dirp = dirp.as_os_result("opendir", Some(path), None)?;
|
||||
Ok(Directory { inner: dirp })
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> OsResult<'static, Option<DirEntry>> {
|
||||
pub fn read(&mut self) -> OsResult<'static, Option<DirEntry<'_>>> {
|
||||
*errno() = 0;
|
||||
let e = unsafe { libc::readdir(self.inner.as_ptr()) };
|
||||
if e.is_null() {
|
||||
@@ -481,7 +481,7 @@ impl AsRawFd for Directory {
|
||||
}
|
||||
|
||||
impl AsFd for Directory {
|
||||
fn as_fd(&self) -> BorrowedFd {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <linux/fs.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int fd_pathat(int dirfd, const char *name, char *path, size_t size) {
|
||||
if (fd_path(dirfd, byte_data(path, size)) < 0)
|
||||
return -1;
|
||||
auto len = strlen(path);
|
||||
path[len] = '/';
|
||||
strscpy(path + len + 1, name, size - len - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void full_read(int fd, string &str) {
|
||||
char buf[4096];
|
||||
for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;)
|
||||
str.insert(str.end(), buf, buf + len);
|
||||
}
|
||||
|
||||
void full_read(const char *filename, string &str) {
|
||||
if (int fd = xopen(filename, O_RDONLY | O_CLOEXEC); fd >= 0) {
|
||||
full_read(fd, str);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
string full_read(int fd) {
|
||||
string str;
|
||||
full_read(fd, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
string full_read(const char *filename) {
|
||||
string str;
|
||||
full_read(filename, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void write_zero(int fd, size_t size) {
|
||||
char buf[4096] = {0};
|
||||
size_t len;
|
||||
while (size > 0) {
|
||||
len = sizeof(buf) > size ? size : sizeof(buf);
|
||||
write(fd, buf, len);
|
||||
size -= len;
|
||||
}
|
||||
}
|
||||
|
||||
void file_readline(bool trim, FILE *fp, const function<bool(string_view)> &fn) {
|
||||
size_t len = 1024;
|
||||
char *buf = (char *) malloc(len);
|
||||
char *start;
|
||||
ssize_t read;
|
||||
while ((read = getline(&buf, &len, fp)) >= 0) {
|
||||
start = buf;
|
||||
if (trim) {
|
||||
while (read && "\n\r "sv.find(buf[read - 1]) != string::npos)
|
||||
--read;
|
||||
buf[read] = '\0';
|
||||
while (*start == ' ')
|
||||
++start;
|
||||
}
|
||||
if (!fn(start))
|
||||
break;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void file_readline(bool trim, const char *file, const function<bool(string_view)> &fn) {
|
||||
if (auto fp = open_file(file, "re"))
|
||||
file_readline(trim, fp.get(), fn);
|
||||
}
|
||||
|
||||
void file_readline(const char *file, const function<bool(string_view)> &fn) {
|
||||
file_readline(false, file, fn);
|
||||
}
|
||||
|
||||
void parse_prop_file(FILE *fp, const function<bool(string_view, string_view)> &fn) {
|
||||
file_readline(true, fp, [&](string_view line_view) -> bool {
|
||||
char *line = (char *) line_view.data();
|
||||
if (line[0] == '#')
|
||||
return true;
|
||||
char *eql = strchr(line, '=');
|
||||
if (eql == nullptr || eql == line)
|
||||
return true;
|
||||
*eql = '\0';
|
||||
return fn(line, eql + 1);
|
||||
});
|
||||
}
|
||||
|
||||
void parse_prop_file(const char *file, const function<bool(string_view, string_view)> &fn) {
|
||||
if (auto fp = open_file(file, "re"))
|
||||
parse_prop_file(fp.get(), fn);
|
||||
}
|
||||
|
||||
sDIR make_dir(DIR *dp) {
|
||||
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
|
||||
}
|
||||
|
||||
sFILE make_file(FILE *fp) {
|
||||
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; });
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(const char *name, bool rw) {
|
||||
auto slice = rust::map_file(name, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(int dirfd, const char *name, bool rw) {
|
||||
auto slice = rust::map_file_at(dirfd, name, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(int fd, size_t sz, bool rw) {
|
||||
auto slice = rust::map_fd(fd, sz, rw);
|
||||
if (!slice.empty()) {
|
||||
_buf = slice.data();
|
||||
_sz = slice.size();
|
||||
}
|
||||
}
|
||||
|
||||
mmap_data::~mmap_data() {
|
||||
if (_buf)
|
||||
munmap(_buf, _sz);
|
||||
}
|
||||
|
||||
string resolve_preinit_dir(const char *base_dir) {
|
||||
string dir = base_dir;
|
||||
if (access((dir + "/unencrypted").data(), F_OK) == 0) {
|
||||
dir += "/unencrypted/magisk";
|
||||
} else if (access((dir + "/adb").data(), F_OK) == 0) {
|
||||
dir += "/adb";
|
||||
} else if (access((dir + "/watchdog").data(), F_OK) == 0) {
|
||||
dir += "/watchdog/magisk";
|
||||
} else {
|
||||
dir += "/magisk";
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
@@ -1,24 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <linux/fs.h>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include "misc.hpp"
|
||||
|
||||
template <typename T>
|
||||
static inline T align_to(T v, int a) {
|
||||
static_assert(std::is_integral<T>::value);
|
||||
return (v + a - 1) / a * a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T align_padding(T v, int a) {
|
||||
return align_to(v, a) - v;
|
||||
}
|
||||
#include "base-rs.hpp"
|
||||
|
||||
struct mmap_data : public byte_data {
|
||||
static_assert((sizeof(void *) == 8 && BLKGETSIZE64 == 0x80081272) ||
|
||||
@@ -45,24 +33,26 @@ bool fclone_attr(int src, int dest);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
|
||||
static inline ssize_t realpath(
|
||||
const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz) {
|
||||
return canonical_path(path, buf, bufsiz);
|
||||
}
|
||||
void full_read(int fd, std::string &str);
|
||||
void full_read(const char *filename, std::string &str);
|
||||
std::string full_read(int fd);
|
||||
std::string full_read(const char *filename);
|
||||
void write_zero(int fd, size_t size);
|
||||
void file_readline(bool trim, FILE *fp, const std::function<bool(std::string_view)> &fn);
|
||||
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
|
||||
void file_readline(const char *file, const std::function<bool(std::string_view)> &fn);
|
||||
void parse_prop_file(FILE *fp, const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||
void parse_prop_file(const char *file,
|
||||
const std::function<bool(std::string_view, std::string_view)> &fn);
|
||||
std::string resolve_preinit_dir(const char *base_dir);
|
||||
|
||||
// Functor = function<bool(Utf8CStr, Utf8CStr)>
|
||||
template <typename Functor>
|
||||
void parse_prop_file(const char *file, Functor &&fn) {
|
||||
parse_prop_file_rs(file, [&](rust::Str key, rust::Str val) -> bool {
|
||||
// We perform the null termination here in C++ because it's very difficult to do it
|
||||
// right in Rust due to pointer provenance. Trying to dereference a pointer without
|
||||
// the correct provenance in Rust, even in unsafe code, is undefined behavior.
|
||||
// However on the C++ side, there are fewer restrictions on pointers, so the const_cast here
|
||||
// will not trigger UB in the compiler.
|
||||
*(const_cast<char *>(key.data()) + key.size()) = '\0';
|
||||
*(const_cast<char *>(val.data()) + val.size()) = '\0';
|
||||
return fn(Utf8CStr(key.data(), key.size() + 1), Utf8CStr(val.data(), val.size() + 1));
|
||||
});
|
||||
}
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
sDIR make_dir(DIR *dp);
|
||||
|
||||
@@ -57,12 +57,12 @@ impl<T: Read + Seek> ReadSeekExt for T {
|
||||
}
|
||||
|
||||
pub trait BufReadExt {
|
||||
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, f: F);
|
||||
fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
|
||||
fn for_each_line<F: FnMut(&mut String) -> bool>(&mut self, f: F);
|
||||
fn for_each_prop<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
|
||||
}
|
||||
|
||||
impl<T: BufRead> BufReadExt for T {
|
||||
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, mut f: F) {
|
||||
fn for_each_line<F: FnMut(&mut String) -> bool>(&mut self, mut f: F) {
|
||||
let mut buf = String::new();
|
||||
loop {
|
||||
match self.read_line(&mut buf) {
|
||||
@@ -81,8 +81,11 @@ impl<T: BufRead> BufReadExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, mut f: F) {
|
||||
self.foreach_lines(|line| {
|
||||
fn for_each_prop<F: FnMut(&str, &str) -> bool>(&mut self, mut f: F) {
|
||||
self.for_each_line(|line| {
|
||||
// Reserve an additional byte, because this string will be manually
|
||||
// null terminated on the C++ side, and it may need more space.
|
||||
line.reserve(1);
|
||||
let line = line.trim();
|
||||
if line.starts_with('#') {
|
||||
return true;
|
||||
@@ -116,7 +119,27 @@ impl<T: Write> WriteExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
fn open_fd(path: &Utf8CStr, flags: i32, mode: mode_t) -> OsResult<OwnedFd> {
|
||||
pub enum FileOrStd {
|
||||
StdIn,
|
||||
StdOut,
|
||||
StdErr,
|
||||
File(File),
|
||||
}
|
||||
|
||||
impl FileOrStd {
|
||||
pub fn as_file(&self) -> &File {
|
||||
let raw_fd_ref: &'static RawFd = match self {
|
||||
FileOrStd::StdIn => &0,
|
||||
FileOrStd::StdOut => &1,
|
||||
FileOrStd::StdErr => &2,
|
||||
FileOrStd::File(file) => return file,
|
||||
};
|
||||
// SAFETY: File is guaranteed to have the same ABI as RawFd
|
||||
unsafe { mem::transmute(raw_fd_ref) }
|
||||
}
|
||||
}
|
||||
|
||||
fn open_fd(path: &Utf8CStr, flags: i32, mode: mode_t) -> OsResult<'_, OwnedFd> {
|
||||
unsafe {
|
||||
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).as_os_result(
|
||||
"open",
|
||||
@@ -201,11 +224,11 @@ impl Utf8CStr {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
|
||||
pub fn open(&self, flags: i32) -> OsResult<File> {
|
||||
pub fn open(&self, flags: i32) -> OsResult<'_, File> {
|
||||
Ok(File::from(open_fd(self, flags, 0)?))
|
||||
}
|
||||
|
||||
pub fn create(&self, flags: i32, mode: mode_t) -> OsResult<File> {
|
||||
pub fn create(&self, flags: i32, mode: mode_t) -> OsResult<'_, File> {
|
||||
Ok(File::from(open_fd(self, O_CREAT | flags, mode)?))
|
||||
}
|
||||
|
||||
@@ -226,7 +249,7 @@ impl Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&self) -> OsResult<()> {
|
||||
pub fn remove(&self) -> OsResult<'_, ()> {
|
||||
unsafe { libc::remove(self.as_ptr()).check_os_err("remove", Some(self), None) }
|
||||
}
|
||||
|
||||
@@ -240,7 +263,7 @@ impl Utf8CStr {
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
|
||||
@@ -251,7 +274,7 @@ impl Utf8CStr {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mkdir(&self, mode: mode_t) -> OsResult<()> {
|
||||
pub fn mkdir(&self, mode: mode_t) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
if libc::mkdir(self.as_ptr(), mode) < 0 {
|
||||
if *errno() == EEXIST {
|
||||
@@ -294,7 +317,7 @@ impl Utf8CStr {
|
||||
}
|
||||
|
||||
// Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp
|
||||
pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
pub fn realpath(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
|
||||
let fd = self.open(O_PATH | O_CLOEXEC)?;
|
||||
let mut st1: libc::stat;
|
||||
let mut st2: libc::stat;
|
||||
@@ -318,7 +341,7 @@ impl Utf8CStr {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> OsResult<FileAttr> {
|
||||
pub fn get_attr(&self) -> OsResult<'_, FileAttr> {
|
||||
let mut attr = FileAttr::new();
|
||||
unsafe {
|
||||
libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?;
|
||||
@@ -352,7 +375,7 @@ impl Utf8CStr {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
let sz = libc::lgetxattr(
|
||||
self.as_ptr(),
|
||||
@@ -477,7 +500,7 @@ impl Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkfifo(&self, mode: mode_t) -> OsResult<()> {
|
||||
pub fn mkfifo(&self, mode: mode_t) -> OsResult<'_, ()> {
|
||||
unsafe { libc::mkfifo(self.as_ptr(), mode).check_os_err("mkfifo", Some(self), None) }
|
||||
}
|
||||
}
|
||||
@@ -487,7 +510,7 @@ impl FsPathFollow {
|
||||
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> OsResult<FileAttr> {
|
||||
pub fn get_attr(&self) -> OsResult<'_, FileAttr> {
|
||||
let mut attr = FileAttr::new();
|
||||
unsafe {
|
||||
libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?;
|
||||
@@ -519,7 +542,7 @@ impl FsPathFollow {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
let sz = libc::getxattr(
|
||||
self.as_ptr(),
|
||||
@@ -617,7 +640,7 @@ pub fn fd_get_attr(fd: RawFd) -> OsResult<'static, FileAttr> {
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> OsResult<()> {
|
||||
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
libc::fchmod(fd, (attr.st.st_mode & 0o777).as_()).check_os_err("fchmod", None, None)?;
|
||||
libc::fchown(fd, attr.st.st_uid, attr.st.st_gid).check_os_err("fchown", None, None)?;
|
||||
@@ -649,7 +672,7 @@ pub fn fd_get_secontext(fd: RawFd, con: &mut dyn Utf8CStrBuf) -> OsResult<'stati
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fd_set_secontext(fd: RawFd, con: &Utf8CStr) -> OsResult<()> {
|
||||
pub fn fd_set_secontext(fd: RawFd, con: &Utf8CStr) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
libc::fsetxattr(
|
||||
fd,
|
||||
@@ -675,11 +698,11 @@ pub fn fclone_attr(a: RawFd, b: RawFd) -> OsResult<'static, ()> {
|
||||
pub struct MappedFile(&'static mut [u8]);
|
||||
|
||||
impl MappedFile {
|
||||
pub fn open(path: &Utf8CStr) -> OsResult<MappedFile> {
|
||||
pub fn open(path: &Utf8CStr) -> OsResult<'_, MappedFile> {
|
||||
Ok(MappedFile(map_file(path, false)?))
|
||||
}
|
||||
|
||||
pub fn open_rw(path: &Utf8CStr) -> OsResult<MappedFile> {
|
||||
pub fn open_rw(path: &Utf8CStr) -> OsResult<'_, MappedFile> {
|
||||
Ok(MappedFile(map_file(path, true)?))
|
||||
}
|
||||
|
||||
@@ -722,7 +745,7 @@ unsafe extern "C" {
|
||||
}
|
||||
|
||||
// We mark the returned slice static because it is valid until explicitly unmapped
|
||||
pub(crate) fn map_file(path: &Utf8CStr, rw: bool) -> OsResult<&'static mut [u8]> {
|
||||
pub(crate) fn map_file(path: &Utf8CStr, rw: bool) -> OsResult<'_, &'static mut [u8]> {
|
||||
unsafe { map_file_at(BorrowedFd::borrow_raw(libc::AT_FDCWD), path, rw) }
|
||||
}
|
||||
|
||||
@@ -854,7 +877,7 @@ pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
|
||||
let mut res = vec![];
|
||||
let mut path = format!("/proc/{pid}/mountinfo");
|
||||
if let Ok(file) = Utf8CStr::from_string(&mut path).open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(file).foreach_lines(|line| {
|
||||
BufReader::new(file).for_each_line(|line| {
|
||||
parse_mount_info_line(line)
|
||||
.map(|info| res.push(info))
|
||||
.is_some()
|
||||
@@ -862,3 +885,19 @@ pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub struct PipeFd {
|
||||
pub read: OwnedFd,
|
||||
pub write: OwnedFd,
|
||||
}
|
||||
|
||||
pub fn make_pipe(flags: i32) -> OsResult<'static, PipeFd> {
|
||||
let mut pipefd: [RawFd; 2] = [0; 2];
|
||||
unsafe {
|
||||
libc::pipe2(pipefd.as_mut_ptr(), flags).check_os_err("pipe2", None, None)?;
|
||||
Ok(PipeFd {
|
||||
read: OwnedFd::from_raw_fd(pipefd[0]),
|
||||
write: OwnedFd::from_raw_fd(pipefd[1]),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "../xwrap.hpp"
|
||||
#include "../files.hpp"
|
||||
#include "../misc.hpp"
|
||||
#include "../logging.hpp"
|
||||
#include "../base-rs.hpp"
|
||||
#include "../files.hpp"
|
||||
#include "../logging.hpp"
|
||||
|
||||
using rust::xpipe2;
|
||||
using rust::fd_path;
|
||||
using kv_pairs = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
1
native/src/base/include/rust/cxx.h
Symbolic link
1
native/src/base/include/rust/cxx.h
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../external/cxx-rs/include/cxx.h
|
||||
@@ -41,12 +41,17 @@ pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("misc.hpp");
|
||||
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
|
||||
|
||||
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
|
||||
fn fork_dont_care() -> i32;
|
||||
|
||||
type FnBoolStrStr;
|
||||
fn call(self: &FnBoolStrStr, key: &str, value: &str) -> bool;
|
||||
|
||||
type FnBoolStr;
|
||||
fn call(self: &FnBoolStr, key: Utf8CStrRef) -> bool;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
@@ -56,13 +61,14 @@ pub mod ffi {
|
||||
fn set_log_level_state_cxx(level: LogLevelCxx, enabled: bool);
|
||||
fn exit_on_error(b: bool);
|
||||
fn cmdline_logging();
|
||||
fn parse_prop_file_rs(name: Utf8CStrRef, f: &FnBoolStrStr);
|
||||
#[cxx_name = "file_readline"]
|
||||
fn file_readline_for_cxx(fd: i32, f: &FnBoolStr);
|
||||
}
|
||||
|
||||
#[namespace = "rust"]
|
||||
extern "Rust" {
|
||||
fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32;
|
||||
#[cxx_name = "fd_path"]
|
||||
fn fd_path_for_cxx(fd: i32, buf: &mut [u8]) -> isize;
|
||||
#[cxx_name = "map_file"]
|
||||
fn map_file_for_cxx(path: Utf8CStrRef, rw: bool) -> &'static mut [u8];
|
||||
#[cxx_name = "map_file_at"]
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <flags.h>
|
||||
#include <base.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifndef __call_bypassing_fortify
|
||||
#define __call_bypassing_fortify(fn) (&fn)
|
||||
#endif
|
||||
|
||||
#undef vsnprintf
|
||||
static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) {
|
||||
constexpr int sz = 4096;
|
||||
char buf[sz];
|
||||
buf[0] = '\0';
|
||||
// Fortify logs when a fatal error occurs. Do not run through fortify again
|
||||
int len = std::min(__call_bypassing_fortify(vsnprintf)(buf, sz, fmt, ap), sz - 1);
|
||||
log_with_rs(level, rust::Utf8CStr(buf, len + 1));
|
||||
return len;
|
||||
}
|
||||
|
||||
// Used to override external C library logging
|
||||
extern "C" int magisk_log_print(int prio, const char *tag, const char *fmt, ...) {
|
||||
LogLevel level;
|
||||
switch (prio) {
|
||||
case ANDROID_LOG_DEBUG:
|
||||
level = LogLevel::Debug;
|
||||
break;
|
||||
case ANDROID_LOG_INFO:
|
||||
level = LogLevel::Info;
|
||||
break;
|
||||
case ANDROID_LOG_WARN:
|
||||
level = LogLevel::Warn;
|
||||
break;
|
||||
case ANDROID_LOG_ERROR:
|
||||
level = LogLevel::ErrorCxx;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
char fmt_buf[4096];
|
||||
auto len = strscpy(fmt_buf, tag, sizeof(fmt_buf) - 1);
|
||||
// Prevent format specifications in the tag
|
||||
std::replace(fmt_buf, fmt_buf + len, '%', '_');
|
||||
len = ssprintf(fmt_buf + len, sizeof(fmt_buf) - len - 1, ": %s", fmt) + len;
|
||||
// Ensure the fmt string always ends with newline
|
||||
if (fmt_buf[len - 1] != '\n') {
|
||||
fmt_buf[len] = '\n';
|
||||
fmt_buf[len + 1] = '\0';
|
||||
}
|
||||
va_list argv;
|
||||
va_start(argv, fmt);
|
||||
int ret = fmt_and_log_with_rs(level, fmt_buf, argv);
|
||||
va_end(argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define LOG_BODY(level) \
|
||||
va_list argv; \
|
||||
va_start(argv, fmt); \
|
||||
fmt_and_log_with_rs(LogLevel::level, fmt, argv); \
|
||||
va_end(argv); \
|
||||
|
||||
// LTO will optimize out the NOP function
|
||||
#if MAGISK_DEBUG
|
||||
void LOGD(const char *fmt, ...) { LOG_BODY(Debug) }
|
||||
#else
|
||||
void LOGD(const char *fmt, ...) {}
|
||||
#endif
|
||||
void LOGI(const char *fmt, ...) { LOG_BODY(Info) }
|
||||
void LOGW(const char *fmt, ...) { LOG_BODY(Warn) }
|
||||
void LOGE(const char *fmt, ...) { LOG_BODY(ErrorCxx) }
|
||||
|
||||
// Export raw symbol to fortify compat
|
||||
extern "C" void __vloge(const char* fmt, va_list ap) {
|
||||
fmt_and_log_with_rs(LogLevel::ErrorCxx, fmt, ap);
|
||||
}
|
||||
@@ -5,8 +5,7 @@
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <bitset>
|
||||
#include <random>
|
||||
#include <cxx.h>
|
||||
#include <rust/cxx.h>
|
||||
|
||||
#include "xwrap.hpp"
|
||||
|
||||
@@ -46,30 +45,11 @@ private:
|
||||
Func fn;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class reversed_container {
|
||||
public:
|
||||
reversed_container(T &base) : base(base) {}
|
||||
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
|
||||
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
|
||||
decltype(std::declval<T>().crbegin()) cbegin() const { return base.crbegin(); }
|
||||
decltype(std::declval<T>().rend()) end() { return base.rend(); }
|
||||
decltype(std::declval<T>().crend()) end() const { return base.crend(); }
|
||||
decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
|
||||
private:
|
||||
T &base;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
reversed_container<T> reversed(T &base) {
|
||||
return reversed_container<T>(base);
|
||||
}
|
||||
template<class T>
|
||||
static void default_new(T *&p) { p = new T(); }
|
||||
|
||||
template<class T>
|
||||
static inline void default_new(T *&p) { p = new T(); }
|
||||
|
||||
template<class T>
|
||||
static inline void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
|
||||
static void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
|
||||
|
||||
struct StringCmp {
|
||||
using is_transparent = void;
|
||||
@@ -78,49 +58,32 @@ struct StringCmp {
|
||||
|
||||
struct heap_data;
|
||||
|
||||
using ByteSlice = rust::Slice<const uint8_t>;
|
||||
using MutByteSlice = rust::Slice<uint8_t>;
|
||||
|
||||
// Interchangeable as `&[u8]` in Rust
|
||||
struct byte_view {
|
||||
byte_view() : _buf(nullptr), _sz(0) {}
|
||||
byte_view(const void *buf, size_t sz) : _buf((uint8_t *) buf), _sz(sz) {}
|
||||
|
||||
// byte_view, or any of its subclass, can be copied as byte_view
|
||||
// byte_view, or any of its subclasses, can be copied as byte_view
|
||||
byte_view(const byte_view &o) : _buf(o._buf), _sz(o._sz) {}
|
||||
|
||||
// Bridging to Rust slice
|
||||
byte_view(rust::Slice<const uint8_t> o) : byte_view(o.data(), o.size()) {}
|
||||
operator rust::Slice<const uint8_t>() const { return rust::Slice<const uint8_t>(_buf, _sz); }
|
||||
// Transparent conversion to Rust slice
|
||||
byte_view(const ByteSlice o) : byte_view(o.data(), o.size()) {}
|
||||
operator ByteSlice() const { return {_buf, _sz}; }
|
||||
|
||||
// String as bytes
|
||||
byte_view(const char *s, bool with_nul = true)
|
||||
: byte_view(std::string_view(s), with_nul, false) {}
|
||||
byte_view(const std::string &s, bool with_nul = true)
|
||||
: byte_view(std::string_view(s), with_nul, false) {}
|
||||
byte_view(std::string_view s, bool with_nul = true)
|
||||
: byte_view(s, with_nul, true /* string_view is not guaranteed to null terminate */ ) {}
|
||||
|
||||
// Vector as bytes
|
||||
byte_view(const std::vector<uint8_t> &v) : byte_view(v.data(), v.size()) {}
|
||||
|
||||
const uint8_t *buf() const { return _buf; }
|
||||
size_t sz() const { return _sz; }
|
||||
// String as bytes, including null terminator
|
||||
byte_view(const char *s) : byte_view(s, strlen(s) + 1) {}
|
||||
|
||||
const uint8_t *data() const { return _buf; }
|
||||
size_t size() const { return _sz; }
|
||||
bool contains(byte_view pattern) const;
|
||||
bool equals(byte_view o) const;
|
||||
heap_data clone() const;
|
||||
bool operator==(byte_view rhs) const;
|
||||
|
||||
protected:
|
||||
uint8_t *_buf;
|
||||
size_t _sz;
|
||||
|
||||
private:
|
||||
byte_view(std::string_view s, bool with_nul, bool check_nul)
|
||||
: byte_view(static_cast<const void *>(s.data()), s.length()) {
|
||||
if (with_nul) {
|
||||
if (check_nul && s[s.length()] != '\0')
|
||||
return;
|
||||
++_sz;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Interchangeable as `&mut [u8]` in Rust
|
||||
@@ -128,23 +91,18 @@ struct byte_data : public byte_view {
|
||||
byte_data() = default;
|
||||
byte_data(void *buf, size_t sz) : byte_view(buf, sz) {}
|
||||
|
||||
// byte_data, or any of its subclass, can be copied as byte_data
|
||||
// byte_data, or any of its subclasses, can be copied as byte_data
|
||||
byte_data(const byte_data &o) : byte_data(o._buf, o._sz) {}
|
||||
|
||||
// Transparent conversion from common C++ types to mutable byte references
|
||||
byte_data(std::string &s, bool with_nul = true)
|
||||
: byte_data(s.data(), with_nul ? s.length() + 1 : s.length()) {}
|
||||
byte_data(std::vector<uint8_t> &v) : byte_data(v.data(), v.size()) {}
|
||||
// Transparent conversion to Rust slice
|
||||
byte_data(const MutByteSlice o) : byte_data(o.data(), o.size()) {}
|
||||
operator MutByteSlice() const { return {_buf, _sz}; }
|
||||
|
||||
// Bridging to Rust slice
|
||||
byte_data(rust::Slice<uint8_t> o) : byte_data(o.data(), o.size()) {}
|
||||
operator rust::Slice<uint8_t>() { return rust::Slice<uint8_t>(_buf, _sz); }
|
||||
|
||||
using byte_view::buf;
|
||||
uint8_t *buf() { return _buf; }
|
||||
using byte_view::data;
|
||||
uint8_t *data() const { return _buf; }
|
||||
|
||||
void swap(byte_data &o);
|
||||
rust::Vec<size_t> patch(byte_view from, byte_view to);
|
||||
rust::Vec<size_t> patch(byte_view from, byte_view to) const;
|
||||
};
|
||||
|
||||
struct heap_data : public byte_data {
|
||||
@@ -170,10 +128,7 @@ private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
rust::Vec<size_t> mut_u8_patch(
|
||||
rust::Slice<uint8_t> buf,
|
||||
rust::Slice<const uint8_t> from,
|
||||
rust::Slice<const uint8_t> to);
|
||||
rust::Vec<size_t> mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to);
|
||||
|
||||
uint32_t parse_uint32_hex(std::string_view s);
|
||||
int parse_int(std::string_view s);
|
||||
@@ -181,21 +136,6 @@ int parse_int(std::string_view s);
|
||||
using thread_entry = void *(*)(void *);
|
||||
extern "C" int new_daemon_thread(thread_entry entry, void *arg = nullptr);
|
||||
|
||||
static inline bool str_contains(std::string_view s, std::string_view ss) {
|
||||
return s.find(ss) != std::string::npos;
|
||||
}
|
||||
static inline bool str_starts(std::string_view s, std::string_view ss) {
|
||||
return s.size() >= ss.size() && s.compare(0, ss.size(), ss) == 0;
|
||||
}
|
||||
static inline bool str_ends(std::string_view s, std::string_view ss) {
|
||||
return s.size() >= ss.size() && s.compare(s.size() - ss.size(), std::string::npos, ss) == 0;
|
||||
}
|
||||
static inline std::string ltrim(std::string &&s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
return std::move(s);
|
||||
}
|
||||
static inline std::string rtrim(std::string &&s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch) && ch != '\0';
|
||||
@@ -267,8 +207,6 @@ constexpr auto operator+(T e) noexcept ->
|
||||
return static_cast<std::underlying_type_t<T>>(e);
|
||||
}
|
||||
|
||||
namespace rust {
|
||||
|
||||
struct Utf8CStr {
|
||||
const char *data() const;
|
||||
size_t length() const;
|
||||
@@ -276,14 +214,14 @@ struct Utf8CStr {
|
||||
|
||||
Utf8CStr() : Utf8CStr("", 1) {};
|
||||
Utf8CStr(const Utf8CStr &o) = default;
|
||||
Utf8CStr(Utf8CStr &&o) = default;
|
||||
Utf8CStr(const char *s) : Utf8CStr(s, strlen(s) + 1) {};
|
||||
Utf8CStr(std::string_view s) : Utf8CStr(s.data(), s.length() + 1) {};
|
||||
Utf8CStr(std::string s) : Utf8CStr(s.data(), s.length() + 1) {};
|
||||
const char *c_str() const { return this->data(); }
|
||||
size_t size() const { return this->length(); }
|
||||
bool empty() const { return this->length() == 0 ; }
|
||||
operator std::string_view() { return {data(), length()}; }
|
||||
std::string_view sv() const { return {data(), length()}; }
|
||||
operator std::string_view() const { return sv(); }
|
||||
bool operator==(std::string_view rhs) const { return sv() == rhs; }
|
||||
|
||||
private:
|
||||
#pragma clang diagnostic push
|
||||
@@ -292,4 +230,19 @@ private:
|
||||
#pragma clang diagnostic pop
|
||||
};
|
||||
|
||||
} // namespace rust
|
||||
// Bindings for std::function to be callable from Rust
|
||||
|
||||
using CxxFnBoolStrStr = std::function<bool(rust::Str, rust::Str)>;
|
||||
struct FnBoolStrStr : public CxxFnBoolStrStr {
|
||||
using CxxFnBoolStrStr::function;
|
||||
bool call(rust::Str a, rust::Str b) const {
|
||||
return operator()(a, b);
|
||||
}
|
||||
};
|
||||
using CxxFnBoolStr = std::function<bool(Utf8CStr)>;
|
||||
struct FnBoolStr : public CxxFnBoolStr {
|
||||
using CxxFnBoolStr::function;
|
||||
bool call(Utf8CStr s) const {
|
||||
return operator()(s);
|
||||
}
|
||||
};
|
||||
@@ -1,13 +1,16 @@
|
||||
use crate::{StrErr, Utf8CStr, ffi};
|
||||
use argh::EarlyExit;
|
||||
use crate::{Utf8CStr, Utf8CString, cstr, ffi};
|
||||
use argh::{EarlyExit, MissingRequirements};
|
||||
use libc::c_char;
|
||||
use std::fmt::Arguments;
|
||||
use std::io::Write;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::{fmt, slice, str};
|
||||
use std::{
|
||||
fmt,
|
||||
fmt::Arguments,
|
||||
io::Write,
|
||||
mem::ManuallyDrop,
|
||||
process::exit,
|
||||
slice, str,
|
||||
sync::Arc,
|
||||
sync::atomic::{AtomicPtr, Ordering},
|
||||
};
|
||||
|
||||
pub fn errno() -> &'static mut i32 {
|
||||
unsafe { &mut *libc::__errno() }
|
||||
@@ -76,15 +79,6 @@ impl<T: AsMut<[u8]> + ?Sized> MutBytesExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: libc guarantees argc and argv are properly setup and are static
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn map_args(argc: i32, argv: *const *const c_char) -> Result<Vec<&'static str>, StrErr> {
|
||||
unsafe { slice::from_raw_parts(argv, argc as usize) }
|
||||
.iter()
|
||||
.map(|s| unsafe { Utf8CStr::from_ptr(*s) }.map(|s| s.as_str()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub trait EarlyExitExt<T> {
|
||||
fn on_early_exit<F: FnOnce()>(self, print_help_msg: F) -> T;
|
||||
}
|
||||
@@ -108,6 +102,51 @@ impl<T> EarlyExitExt<T> for Result<T, EarlyExit> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PositionalArgParser<'a>(pub slice::Iter<'a, &'a str>);
|
||||
|
||||
impl PositionalArgParser<'_> {
|
||||
pub fn required(&mut self, field_name: &'static str) -> Result<Utf8CString, EarlyExit> {
|
||||
if let Some(next) = self.0.next() {
|
||||
Ok((*next).into())
|
||||
} else {
|
||||
let mut missing = MissingRequirements::default();
|
||||
missing.missing_positional_arg(field_name);
|
||||
missing.err_on_any()?;
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optional(&mut self) -> Option<Utf8CString> {
|
||||
self.0.next().map(|s| (*s).into())
|
||||
}
|
||||
|
||||
pub fn last_required(&mut self, field_name: &'static str) -> Result<Utf8CString, EarlyExit> {
|
||||
let r = self.required(field_name)?;
|
||||
self.ensure_end()?;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
pub fn last_optional(&mut self) -> Result<Option<Utf8CString>, EarlyExit> {
|
||||
let r = self.optional();
|
||||
if r.is_none() {
|
||||
return Ok(r);
|
||||
}
|
||||
self.ensure_end()?;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn ensure_end(&mut self) -> Result<(), EarlyExit> {
|
||||
if self.0.len() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EarlyExit::from(format!(
|
||||
"Unrecognized argument: {}\n",
|
||||
self.0.next().unwrap()
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FmtAdaptor<'a, T>(pub &'a mut T)
|
||||
where
|
||||
T: Write;
|
||||
@@ -227,3 +266,35 @@ impl Chunker {
|
||||
chunk
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmdArgs(pub Vec<&'static str>);
|
||||
|
||||
impl CmdArgs {
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn new(argc: i32, argv: *const *const c_char) -> CmdArgs {
|
||||
CmdArgs(
|
||||
// SAFETY: libc guarantees argc and argv are properly setup and are static
|
||||
unsafe { slice::from_raw_parts(argv, argc as usize) }
|
||||
.iter()
|
||||
.map(|s| unsafe { Utf8CStr::from_ptr(*s) })
|
||||
.map(|r| r.unwrap_or(cstr!("<invalid>")))
|
||||
.map(Utf8CStr::as_str)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[&'static str] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> slice::Iter<'_, &'static str> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
pub fn cstr_iter(&self) -> impl Iterator<Item = &'static Utf8CStr> {
|
||||
// SAFETY: libc guarantees null terminated strings
|
||||
self.0
|
||||
.iter()
|
||||
.map(|s| unsafe { Utf8CStr::from_raw_parts(s.as_ptr().cast(), s.len() + 1) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ impl Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_mount_point_flags(&self, flags: c_ulong) -> OsResult<()> {
|
||||
pub fn remount_mount_point_flags(&self, flags: c_ulong) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
@@ -30,7 +30,7 @@ impl Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_mount_flags(&self, flags: c_ulong) -> OsResult<()> {
|
||||
pub fn remount_mount_flags(&self, flags: c_ulong) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
@@ -43,7 +43,7 @@ impl Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<()> {
|
||||
pub fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
@@ -69,13 +69,13 @@ impl Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmount(&self) -> OsResult<()> {
|
||||
pub fn unmount(&self) -> OsResult<'_, ()> {
|
||||
unsafe {
|
||||
libc::umount2(self.as_ptr(), libc::MNT_DETACH).check_os_err("unmount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_mount_private(&self, recursive: bool) -> OsResult<()> {
|
||||
pub fn set_mount_private(&self, recursive: bool) -> OsResult<'_, ()> {
|
||||
let flag = if recursive { libc::MS_REC } else { 0 };
|
||||
unsafe {
|
||||
libc::mount(
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#include <new>
|
||||
#include <cstdlib>
|
||||
|
||||
/* Override libc++ new implementation
|
||||
* to optimize final build size */
|
||||
|
||||
void* operator new(std::size_t s) { return std::malloc(s); }
|
||||
void* operator new[](std::size_t s) { return std::malloc(s); }
|
||||
void operator delete(void *p) { std::free(p); }
|
||||
void operator delete[](void *p) { std::free(p); }
|
||||
void* operator new(std::size_t s, const std::nothrow_t&) noexcept { return std::malloc(s); }
|
||||
void* operator new[](std::size_t s, const std::nothrow_t&) noexcept { return std::malloc(s); }
|
||||
void operator delete(void *p, const std::nothrow_t&) noexcept { std::free(p); }
|
||||
void operator delete[](void *p, const std::nothrow_t&) noexcept { std::free(p); }
|
||||
@@ -23,32 +23,29 @@ pub type LoggedResult<T> = Result<T, LoggedError>;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_err {
|
||||
() => {{
|
||||
Err($crate::LoggedError::default())
|
||||
}};
|
||||
($($args:tt)+) => {{
|
||||
$crate::error!($($args)+);
|
||||
$crate::LoggedError::default()
|
||||
Err($crate::LoggedError::default())
|
||||
}};
|
||||
}
|
||||
|
||||
// Any result or option can be silenced
|
||||
pub trait SilentResultExt<T> {
|
||||
pub trait SilentLogExt<T> {
|
||||
fn silent(self) -> LoggedResult<T>;
|
||||
}
|
||||
|
||||
impl<T, E> SilentResultExt<T> for Result<T, E> {
|
||||
impl<T, E> SilentLogExt<T> for Result<T, E> {
|
||||
fn silent(self) -> LoggedResult<T> {
|
||||
match self {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => Err(LoggedError::default()),
|
||||
}
|
||||
self.map_err(|_| LoggedError::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SilentResultExt<T> for Option<T> {
|
||||
impl<T> SilentLogExt<T> for Option<T> {
|
||||
fn silent(self) -> LoggedResult<T> {
|
||||
match self {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(LoggedError::default()),
|
||||
}
|
||||
self.ok_or_else(LoggedError::default)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,142 +61,168 @@ pub(crate) trait CxxResultExt<T> {
|
||||
fn log_cxx(self) -> LoggedResult<T>;
|
||||
}
|
||||
|
||||
trait Loggable<T> {
|
||||
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedResult<T>;
|
||||
// Public API for converting Option to LoggedResult
|
||||
pub trait OptionExt<T> {
|
||||
fn ok_or_log(self) -> LoggedResult<T>;
|
||||
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
|
||||
}
|
||||
|
||||
impl<T> OptionExt<T> for Option<T> {
|
||||
#[inline(always)]
|
||||
fn ok_or_log(self) -> LoggedResult<T> {
|
||||
self.ok_or_else(LoggedError::default)
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
self.ok_or_else(|| {
|
||||
do_log_msg(LogLevel::Error, None, f);
|
||||
LoggedError::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[cfg(debug_assertions)]
|
||||
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
let caller = Some(Location::caller());
|
||||
self.ok_or_else(|| {
|
||||
do_log_msg(LogLevel::Error, caller, f);
|
||||
LoggedError::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
trait Loggable {
|
||||
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedError;
|
||||
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
|
||||
self,
|
||||
level: LogLevel,
|
||||
caller: Option<&'static Location>,
|
||||
f: F,
|
||||
) -> LoggedResult<T>;
|
||||
) -> LoggedError;
|
||||
}
|
||||
|
||||
impl<T, R: Loggable<T>> CxxResultExt<T> for R {
|
||||
impl<T, E: Loggable> CxxResultExt<T> for Result<T, E> {
|
||||
fn log_cxx(self) -> LoggedResult<T> {
|
||||
self.do_log(LogLevel::ErrorCxx, None)
|
||||
self.map_err(|e| e.do_log(LogLevel::ErrorCxx, None))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: Loggable<T>> ResultExt<T> for R {
|
||||
impl<T, E: Loggable> ResultExt<T> for Result<T, E> {
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn log(self) -> LoggedResult<T> {
|
||||
self.do_log(LogLevel::Error, None)
|
||||
self.map_err(|e| e.do_log(LogLevel::Error, None))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[cfg(debug_assertions)]
|
||||
fn log(self) -> LoggedResult<T> {
|
||||
self.do_log(LogLevel::Error, Some(Location::caller()))
|
||||
let caller = Some(Location::caller());
|
||||
self.map_err(|e| e.do_log(LogLevel::Error, caller))
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
self.do_log_msg(LogLevel::Error, None, f)
|
||||
self.map_err(|e| e.do_log_msg(LogLevel::Error, None, f))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[cfg(debug_assertions)]
|
||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
self.do_log_msg(LogLevel::Error, Some(Location::caller()), f)
|
||||
let caller = Some(Location::caller());
|
||||
self.map_err(|e| e.do_log_msg(LogLevel::Error, caller, f))
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn log_ok(self) {
|
||||
self.log().ok();
|
||||
self.map_err(|e| e.do_log(LogLevel::Error, None)).ok();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[cfg(debug_assertions)]
|
||||
fn log_ok(self) {
|
||||
self.do_log(LogLevel::Error, Some(Location::caller())).ok();
|
||||
let caller = Some(Location::caller());
|
||||
self.map_err(|e| e.do_log(LogLevel::Error, caller)).ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Loggable<T> for LoggedResult<T> {
|
||||
fn do_log(self, _: LogLevel, _: Option<&'static Location>) -> LoggedResult<T> {
|
||||
impl<T> ResultExt<T> for LoggedResult<T> {
|
||||
fn log(self) -> LoggedResult<T> {
|
||||
self
|
||||
}
|
||||
|
||||
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
|
||||
self,
|
||||
level: LogLevel,
|
||||
caller: Option<&'static Location>,
|
||||
f: F,
|
||||
) -> LoggedResult<T> {
|
||||
match self {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => {
|
||||
log_with_formatter(level, |w| {
|
||||
if let Some(caller) = caller {
|
||||
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
|
||||
}
|
||||
f(w)?;
|
||||
w.write_char('\n')
|
||||
});
|
||||
Err(LoggedError::default())
|
||||
}
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
self.inspect_err(|_| do_log_msg(LogLevel::Error, None, f))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[cfg(debug_assertions)]
|
||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
let caller = Some(Location::caller());
|
||||
self.inspect_err(|_| do_log_msg(LogLevel::Error, caller, f))
|
||||
}
|
||||
|
||||
fn log_ok(self) {}
|
||||
}
|
||||
|
||||
impl<T, E: Display> Loggable<T> for Result<T, E> {
|
||||
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedResult<T> {
|
||||
match self {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => {
|
||||
if let Some(caller) = caller {
|
||||
log_with_args!(level, "[{}:{}] {:#}", caller.file(), caller.line(), e);
|
||||
} else {
|
||||
log_with_args!(level, "{:#}", e);
|
||||
}
|
||||
Err(LoggedError::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
|
||||
self,
|
||||
level: LogLevel,
|
||||
caller: Option<&'static Location>,
|
||||
f: F,
|
||||
) -> LoggedResult<T> {
|
||||
match self {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => {
|
||||
log_with_formatter(level, |w| {
|
||||
if let Some(caller) = caller {
|
||||
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
|
||||
}
|
||||
f(w)?;
|
||||
writeln!(w, ": {e:#}")
|
||||
});
|
||||
Err(LoggedError::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically convert all printable errors to LoggedError to support `?` operator
|
||||
impl<T: Display> From<T> for LoggedError {
|
||||
// Allow converting Loggable errors to LoggedError to support `?` operator
|
||||
impl<T: Loggable> From<T> for LoggedError {
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn from(e: T) -> Self {
|
||||
log_with_args!(LogLevel::Error, "{:#}", e);
|
||||
LoggedError::default()
|
||||
e.do_log(LogLevel::Error, None)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[cfg(debug_assertions)]
|
||||
fn from(e: T) -> Self {
|
||||
let caller = Location::caller();
|
||||
log_with_args!(
|
||||
LogLevel::Error,
|
||||
"[{}:{}] {:#}",
|
||||
caller.file(),
|
||||
caller.line(),
|
||||
e
|
||||
);
|
||||
let caller = Some(Location::caller());
|
||||
e.do_log(LogLevel::Error, caller)
|
||||
}
|
||||
}
|
||||
|
||||
// Actual logging implementation
|
||||
|
||||
// Make all printable objects Loggable
|
||||
impl<T: Display> Loggable for T {
|
||||
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedError {
|
||||
if let Some(caller) = caller {
|
||||
log_with_args!(level, "[{}:{}] {:#}", caller.file(), caller.line(), self);
|
||||
} else {
|
||||
log_with_args!(level, "{:#}", self);
|
||||
}
|
||||
LoggedError::default()
|
||||
}
|
||||
|
||||
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
|
||||
self,
|
||||
level: LogLevel,
|
||||
caller: Option<&'static Location>,
|
||||
f: F,
|
||||
) -> LoggedError {
|
||||
log_with_formatter(level, |w| {
|
||||
if let Some(caller) = caller {
|
||||
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
|
||||
}
|
||||
f(w)?;
|
||||
writeln!(w, ": {self:#}")
|
||||
});
|
||||
LoggedError::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
|
||||
level: LogLevel,
|
||||
caller: Option<&'static Location>,
|
||||
f: F,
|
||||
) {
|
||||
log_with_formatter(level, |w| {
|
||||
if let Some(caller) = caller {
|
||||
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
|
||||
}
|
||||
f(w)?;
|
||||
w.write_char('\n')
|
||||
});
|
||||
}
|
||||
|
||||
// Check libc return value and map to Result
|
||||
|
||||
@@ -27,7 +27,6 @@ int xlisten(int sockfd, int backlog);
|
||||
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
|
||||
int xstat(const char *pathname, struct stat *buf);
|
||||
int xfstat(int fd, struct stat *buf);
|
||||
int xdup(int fd);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
ssize_t xreadlink(const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||
ssize_t xreadlinkat(
|
||||
@@ -42,7 +41,6 @@ int xmkdir(const char *pathname, mode_t mode);
|
||||
int xmkdirs(const char *pathname, mode_t mode);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
pid_t xfork();
|
||||
int xpoll(pollfd *fds, nfds_t nfds, int timeout);
|
||||
ssize_t xrealpath(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
|
||||
int xmknod(const char * pathname, mode_t mode, dev_t dev);
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ use crate::cxx_extern::readlinkat;
|
||||
use crate::{
|
||||
BorrowedDirectory, CxxResultExt, LibcReturn, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut,
|
||||
};
|
||||
use libc::{
|
||||
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
||||
};
|
||||
use libc::{c_char, c_uint, c_ulong, c_void, dev_t, mode_t, off_t, sockaddr, socklen_t};
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
@@ -270,16 +268,6 @@ unsafe extern "C" fn xfstat(fd: RawFd, buf: *mut libc::stat) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xdup(oldfd: RawFd) -> RawFd {
|
||||
unsafe {
|
||||
libc::dup(oldfd)
|
||||
.as_os_result("dup", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
||||
unsafe {
|
||||
@@ -381,16 +369,6 @@ extern "C" fn xfork() -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xpoll(fds: *mut pollfd, nfds: nfds_t, timeout: i32) -> i32 {
|
||||
unsafe {
|
||||
libc::poll(fds, nfds, timeout)
|
||||
.as_os_result("poll", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
||||
unsafe {
|
||||
|
||||
@@ -33,5 +33,5 @@ num-traits = { workspace = true }
|
||||
flate2 = { workspace = true, features = ["zlib-rs"] }
|
||||
bzip2 = { workspace = true }
|
||||
lz4 = { workspace = true }
|
||||
xz2 = { workspace = true }
|
||||
lzma-rust2 = { workspace = true }
|
||||
zopfli = { workspace = true, features = ["gzip"] }
|
||||
|
||||
@@ -48,6 +48,43 @@ static bool check_env(const char *name) {
|
||||
return val != nullptr && val == "true"sv;
|
||||
}
|
||||
|
||||
FileFormat check_fmt(const void *buf, size_t len) {
|
||||
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
|
||||
return FileFormat::CHROMEOS;
|
||||
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
|
||||
return FileFormat::AOSP;
|
||||
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
|
||||
return FileFormat::AOSP_VENDOR;
|
||||
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
|
||||
return FileFormat::GZIP;
|
||||
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
|
||||
return FileFormat::LZOP;
|
||||
} else if (CHECKED_MATCH(XZ_MAGIC)) {
|
||||
return FileFormat::XZ;
|
||||
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
||||
return FileFormat::LZMA;
|
||||
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
|
||||
return FileFormat::BZIP2;
|
||||
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
|
||||
return FileFormat::LZ4;
|
||||
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
|
||||
return FileFormat::LZ4_LEGACY;
|
||||
} else if (CHECKED_MATCH(MTK_MAGIC)) {
|
||||
return FileFormat::MTK;
|
||||
} else if (CHECKED_MATCH(DTB_MAGIC)) {
|
||||
return FileFormat::DTB;
|
||||
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
|
||||
return FileFormat::DHTB;
|
||||
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
|
||||
return FileFormat::BLOB;
|
||||
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
|
||||
return FileFormat::ZIMAGE;
|
||||
} else {
|
||||
return FileFormat::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
void dyn_img_hdr::print() const {
|
||||
uint32_t ver = header_version();
|
||||
fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver);
|
||||
@@ -119,7 +156,7 @@ void dyn_img_hdr::dump_hdr_file() const {
|
||||
}
|
||||
|
||||
void dyn_img_hdr::load_hdr_file() {
|
||||
parse_prop_file(HEADER_FILE, [=, this](string_view key, string_view value) -> bool {
|
||||
parse_prop_file(HEADER_FILE, [=, this](Utf8CStr key, Utf8CStr value) -> bool {
|
||||
if (key == "name" && name()) {
|
||||
memset(name(), 0, 16);
|
||||
memcpy(name(), value.data(), value.length() > 15 ? 15 : value.length());
|
||||
@@ -129,7 +166,7 @@ void dyn_img_hdr::load_hdr_file() {
|
||||
if (value.length() > BOOT_ARGS_SIZE) {
|
||||
memcpy(cmdline(), value.data(), BOOT_ARGS_SIZE);
|
||||
auto len = std::min(value.length() - BOOT_ARGS_SIZE, (size_t) BOOT_EXTRA_ARGS_SIZE);
|
||||
memcpy(extra_cmdline(), &value[BOOT_ARGS_SIZE], len);
|
||||
memcpy(extra_cmdline(), value.data() + BOOT_ARGS_SIZE, len);
|
||||
} else {
|
||||
memcpy(cmdline(), value.data(), value.length());
|
||||
}
|
||||
@@ -152,8 +189,8 @@ void dyn_img_hdr::load_hdr_file() {
|
||||
boot_img::boot_img(const char *image) :
|
||||
map(image), k_fmt(FileFormat::UNKNOWN), r_fmt(FileFormat::UNKNOWN), e_fmt(FileFormat::UNKNOWN) {
|
||||
fprintf(stderr, "Parsing boot image: [%s]\n", image);
|
||||
for (const uint8_t *addr = map.buf(); addr < map.buf() + map.sz(); ++addr) {
|
||||
FileFormat fmt = check_fmt(addr, map.sz());
|
||||
for (const uint8_t *addr = map.data(); addr < map.data() + map.size(); ++addr) {
|
||||
FileFormat fmt = check_fmt(addr, map.size());
|
||||
switch (fmt) {
|
||||
case FileFormat::CHROMEOS:
|
||||
// chromeos require external signing
|
||||
@@ -330,9 +367,9 @@ pair<const uint8_t *, dyn_img_hdr *> boot_img::create_hdr(const uint8_t *addr, F
|
||||
|
||||
auto real_hdr_sz = h->page_size - AMONET_MICROLOADER_SZ;
|
||||
heap_data copy(h->page_size);
|
||||
memcpy(copy.buf(), h, real_hdr_sz);
|
||||
memcpy(copy.data(), h, real_hdr_sz);
|
||||
|
||||
return make_pair(addr, make_hdr(copy.buf()));
|
||||
return make_pair(addr, make_hdr(copy.data()));
|
||||
}
|
||||
|
||||
if (CMD_MATCH(NOOKHD_RL_MAGIC) ||
|
||||
@@ -367,7 +404,7 @@ static const char *vendor_ramdisk_type(int type) {
|
||||
}
|
||||
|
||||
#define assert_off() \
|
||||
if ((base_addr + off) > (map.buf() + map_end)) { \
|
||||
if ((base_addr + off) > (map.data() + map_end)) { \
|
||||
fprintf(stderr, "Corrupted boot image!\n"); \
|
||||
return false; \
|
||||
}
|
||||
@@ -396,7 +433,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
|
||||
hdr->print();
|
||||
|
||||
size_t map_end = align_to(map.sz(), getpagesize());
|
||||
size_t map_end = align_to(map.size(), getpagesize());
|
||||
size_t off = hdr->hdr_space();
|
||||
get_block(kernel);
|
||||
get_block(ramdisk);
|
||||
@@ -410,13 +447,13 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
|
||||
payload = byte_view(base_addr, off);
|
||||
auto tail_addr = base_addr + off;
|
||||
tail = byte_view(tail_addr, map.buf() + map_end - tail_addr);
|
||||
tail = byte_view(tail_addr, map.data() + map_end - tail_addr);
|
||||
|
||||
if (auto size = hdr->kernel_size()) {
|
||||
if (int dtb_off = find_dtb_offset(kernel, size); dtb_off > 0) {
|
||||
kernel_dtb = byte_view(kernel + dtb_off, size - dtb_off);
|
||||
hdr->kernel_size() = dtb_off;
|
||||
fprintf(stderr, "%-*s [%zu]\n", PADDING, "KERNEL_DTB_SZ", kernel_dtb.sz());
|
||||
fprintf(stderr, "%-*s [%zu]\n", PADDING, "KERNEL_DTB_SZ", kernel_dtb.size());
|
||||
}
|
||||
|
||||
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||
@@ -472,7 +509,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
fprintf(stderr, "! Could not find zImage piggy, keeping raw kernel\n");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name(k_fmt));
|
||||
}
|
||||
if (auto size = hdr->ramdisk_size()) {
|
||||
if (hdr->vendor_ramdisk_table_size()) {
|
||||
@@ -493,7 +530,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
fprintf(stderr,
|
||||
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
|
||||
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
|
||||
it.ramdisk_size, fmt2name[fmt]);
|
||||
it.ramdisk_size, fmt2name(fmt));
|
||||
}
|
||||
} else {
|
||||
r_fmt = check_fmt_lg(ramdisk, size);
|
||||
@@ -507,38 +544,37 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
hdr->ramdisk_size() -= sizeof(mtk_hdr);
|
||||
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size());
|
||||
}
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name[r_fmt]);
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "RAMDISK_FMT", fmt2name(r_fmt));
|
||||
}
|
||||
}
|
||||
if (auto size = hdr->extra_size()) {
|
||||
e_fmt = check_fmt_lg(extra, size);
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name[e_fmt]);
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "EXTRA_FMT", fmt2name(e_fmt));
|
||||
}
|
||||
|
||||
if (tail.sz()) {
|
||||
if (tail.size()) {
|
||||
// Check special flags
|
||||
if (tail.sz() >= 16 && BUFFER_MATCH(tail.buf(), SEANDROID_MAGIC)) {
|
||||
if (tail.size() >= 16 && BUFFER_MATCH(tail.data(), SEANDROID_MAGIC)) {
|
||||
fprintf(stderr, "SAMSUNG_SEANDROID\n");
|
||||
flags[SEANDROID_FLAG] = true;
|
||||
} else if (tail.sz() >= 16 && BUFFER_MATCH(tail.buf(), LG_BUMP_MAGIC)) {
|
||||
} else if (tail.size() >= 16 && BUFFER_MATCH(tail.data(), LG_BUMP_MAGIC)) {
|
||||
fprintf(stderr, "LG_BUMP_IMAGE\n");
|
||||
flags[LG_BUMP_FLAG] = true;
|
||||
} else if (!(tail.sz() >= 4 && BUFFER_MATCH(tail.buf(), AVB_MAGIC)) && verify()) {
|
||||
// Check if the image is avb 1.0 signed
|
||||
} else if (verify()) {
|
||||
fprintf(stderr, "AVB1_SIGNED\n");
|
||||
flags[AVB1_SIGNED_FLAG] = true;
|
||||
}
|
||||
|
||||
// Find AVB footer
|
||||
const void *footer = tail.buf() + tail.sz() - sizeof(AvbFooter);
|
||||
const void *footer = tail.data() + tail.size() - sizeof(AvbFooter);
|
||||
if (BUFFER_MATCH(footer, AVB_FOOTER_MAGIC)) {
|
||||
avb_footer = reinterpret_cast<const AvbFooter*>(footer);
|
||||
avb_footer = static_cast<const AvbFooter*>(footer);
|
||||
// Double check if meta header exists
|
||||
const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset);
|
||||
if (BUFFER_MATCH(meta, AVB_MAGIC)) {
|
||||
fprintf(stderr, "VBMETA\n");
|
||||
flags[AVB_FLAG] = true;
|
||||
vbmeta = reinterpret_cast<const AvbVBMetaImageHeader*>(meta);
|
||||
vbmeta = static_cast<const AvbVBMetaImageHeader*>(meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,38 +583,34 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boot_img::verify(const char *cert) const {
|
||||
return rust::verify_boot_image(*this, cert);
|
||||
}
|
||||
int split_image_dtb(Utf8CStr filename, bool skip_decomp) {
|
||||
mmap_data img(filename.data());
|
||||
|
||||
int split_image_dtb(const char *filename, bool skip_decomp) {
|
||||
mmap_data img(filename);
|
||||
|
||||
if (size_t off = find_dtb_offset(img.buf(), img.sz()); off > 0) {
|
||||
FileFormat fmt = check_fmt_lg(img.buf(), img.sz());
|
||||
if (!skip_decomp && COMPRESSED(fmt)) {
|
||||
if (size_t off = find_dtb_offset(img.data(), img.size()); off > 0) {
|
||||
FileFormat fmt = check_fmt_lg(img.data(), img.size());
|
||||
if (!skip_decomp && fmt_compressed(fmt)) {
|
||||
int fd = creat(KERNEL_FILE, 0644);
|
||||
decompress(fmt, fd, img.buf(), off);
|
||||
decompress(fmt, fd, img.data(), off);
|
||||
close(fd);
|
||||
} else {
|
||||
dump(img.buf(), off, KERNEL_FILE);
|
||||
dump(img.data(), off, KERNEL_FILE);
|
||||
}
|
||||
dump(img.buf() + off, img.sz() - off, KER_DTB_FILE);
|
||||
dump(img.data() + off, img.size() - off, KER_DTB_FILE);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Cannot find DTB in %s\n", filename);
|
||||
fprintf(stderr, "Cannot find DTB in %s\n", filename.data());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int unpack(const char *image, bool skip_decomp, bool hdr) {
|
||||
const boot_img boot(image);
|
||||
int unpack(Utf8CStr image, bool skip_decomp, bool hdr) {
|
||||
const boot_img boot(image.data());
|
||||
|
||||
if (hdr)
|
||||
boot.hdr->dump_hdr_file();
|
||||
|
||||
// Dump kernel
|
||||
if (!skip_decomp && COMPRESSED(boot.k_fmt)) {
|
||||
if (!skip_decomp && fmt_compressed(boot.k_fmt)) {
|
||||
if (boot.hdr->kernel_size() != 0) {
|
||||
int fd = creat(KERNEL_FILE, 0644);
|
||||
decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size());
|
||||
@@ -589,7 +621,7 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
|
||||
}
|
||||
|
||||
// Dump kernel_dtb
|
||||
dump(boot.kernel_dtb.buf(), boot.kernel_dtb.sz(), KER_DTB_FILE);
|
||||
dump(boot.kernel_dtb.data(), boot.kernel_dtb.size(), KER_DTB_FILE);
|
||||
|
||||
// Dump ramdisk
|
||||
if (boot.hdr->vendor_ramdisk_table_size()) {
|
||||
@@ -609,13 +641,13 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
|
||||
}
|
||||
owned_fd fd = xopenat(dirfd, file_name, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644);
|
||||
FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
if (!skip_decomp && COMPRESSED(fmt)) {
|
||||
if (!skip_decomp && fmt_compressed(fmt)) {
|
||||
decompress(fmt, fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
} else {
|
||||
xwrite(fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
}
|
||||
}
|
||||
} else if (!skip_decomp && COMPRESSED(boot.r_fmt)) {
|
||||
} else if (!skip_decomp && fmt_compressed(boot.r_fmt)) {
|
||||
if (boot.hdr->ramdisk_size() != 0) {
|
||||
int fd = creat(RAMDISK_FILE, 0644);
|
||||
decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size());
|
||||
@@ -629,7 +661,7 @@ int unpack(const char *image, bool skip_decomp, bool hdr) {
|
||||
dump(boot.second, boot.hdr->second_size(), SECOND_FILE);
|
||||
|
||||
// Dump extra
|
||||
if (!skip_decomp && COMPRESSED(boot.e_fmt)) {
|
||||
if (!skip_decomp && fmt_compressed(boot.e_fmt)) {
|
||||
if (boot.hdr->extra_size() != 0) {
|
||||
int fd = creat(EXTRA_FILE, 0644);
|
||||
decompress(boot.e_fmt, fd, boot.extra, boot.hdr->extra_size());
|
||||
@@ -656,9 +688,9 @@ write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR) - off.header, page_size))
|
||||
|
||||
#define file_align() file_align_with(boot.hdr->page_size())
|
||||
|
||||
void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
const boot_img boot(src_img);
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n", out_img);
|
||||
void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) {
|
||||
const boot_img boot(src_img.data());
|
||||
fprintf(stderr, "Repack to boot image: [%s]\n", out_img.data());
|
||||
|
||||
struct {
|
||||
uint32_t header;
|
||||
@@ -687,22 +719,22 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
***************/
|
||||
|
||||
// Create new image
|
||||
int fd = creat(out_img, 0644);
|
||||
int fd = creat(out_img.data(), 0644);
|
||||
|
||||
if (boot.flags[DHTB_FLAG]) {
|
||||
// Skip DHTB header
|
||||
write_zero(fd, sizeof(dhtb_hdr));
|
||||
} else if (boot.flags[BLOB_FLAG]) {
|
||||
xwrite(fd, boot.map.buf(), sizeof(blob_hdr));
|
||||
xwrite(fd, boot.map.data(), sizeof(blob_hdr));
|
||||
} else if (boot.flags[NOOKHD_FLAG]) {
|
||||
xwrite(fd, boot.map.buf(), NOOKHD_PRE_HEADER_SZ);
|
||||
xwrite(fd, boot.map.data(), NOOKHD_PRE_HEADER_SZ);
|
||||
} else if (boot.flags[ACCLAIM_FLAG]) {
|
||||
xwrite(fd, boot.map.buf(), ACCLAIM_PRE_HEADER_SZ);
|
||||
xwrite(fd, boot.map.data(), ACCLAIM_PRE_HEADER_SZ);
|
||||
}
|
||||
|
||||
// Copy raw header
|
||||
off.header = lseek(fd, 0, SEEK_CUR);
|
||||
xwrite(fd, boot.payload.buf(), hdr->hdr_space());
|
||||
xwrite(fd, boot.payload.data(), hdr->hdr_space());
|
||||
|
||||
// kernel
|
||||
off.kernel = lseek(fd, 0, SEEK_CUR);
|
||||
@@ -716,12 +748,12 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
}
|
||||
if (access(KERNEL_FILE, R_OK) == 0) {
|
||||
mmap_data m(KERNEL_FILE);
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(boot.k_fmt)) {
|
||||
if (!skip_comp && !fmt_compressed_any(check_fmt(m.data(), m.size())) && fmt_compressed(boot.k_fmt)) {
|
||||
// Always use zopfli for zImage compression
|
||||
auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == FileFormat::GZIP) ? FileFormat::ZOPFLI : boot.k_fmt;
|
||||
hdr->kernel_size() = compress_len(fmt, m, fd);
|
||||
} else {
|
||||
hdr->kernel_size() = xwrite(fd, m.buf(), m.sz());
|
||||
hdr->kernel_size() = xwrite(fd, m.data(), m.size());
|
||||
}
|
||||
|
||||
if (boot.flags[ZIMAGE_KERNEL]) {
|
||||
@@ -732,7 +764,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
} else if (!skip_comp) {
|
||||
// Pad zeros to make sure the zImage file size does not change
|
||||
// Also ensure the last 4 bytes are the uncompressed vmlinux size
|
||||
uint32_t sz = m.sz();
|
||||
uint32_t sz = m.size();
|
||||
write_zero(fd, boot.hdr->kernel_size() - hdr->kernel_size() - sizeof(sz));
|
||||
xwrite(fd, &sz, sizeof(sz));
|
||||
}
|
||||
@@ -747,7 +779,7 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
if (boot.flags[ZIMAGE_KERNEL]) {
|
||||
// Copy zImage tail and adjust size accordingly
|
||||
hdr->kernel_size() += boot.z_info.hdr_sz;
|
||||
hdr->kernel_size() += xwrite(fd, boot.z_info.tail.buf(), boot.z_info.tail.sz());
|
||||
hdr->kernel_size() += xwrite(fd, boot.z_info.tail.data(), boot.z_info.tail.size());
|
||||
}
|
||||
|
||||
// kernel dtb
|
||||
@@ -784,10 +816,10 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
mmap_data m(dirfd, file_name);
|
||||
FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
|
||||
it.ramdisk_offset = ramdisk_offset;
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(fmt)) {
|
||||
if (!skip_comp && !fmt_compressed_any(check_fmt(m.data(), m.size())) && fmt_compressed(fmt)) {
|
||||
it.ramdisk_size = compress_len(fmt, m, fd);
|
||||
} else {
|
||||
it.ramdisk_size = xwrite(fd, m.buf(), m.sz());
|
||||
it.ramdisk_size = xwrite(fd, m.data(), m.size());
|
||||
}
|
||||
ramdisk_offset += it.ramdisk_size;
|
||||
}
|
||||
@@ -801,13 +833,13 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
// A v4 boot image ramdisk will have to be merged with other vendor ramdisks,
|
||||
// and they have to use the exact same compression method. v4 GKIs are required to
|
||||
// use lz4 (legacy), so hardcode the format here.
|
||||
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name[r_fmt], fmt2name[FileFormat::LZ4_LEGACY]);
|
||||
fprintf(stderr, "RAMDISK_FMT: [%s] -> [%s]\n", fmt2name(r_fmt), fmt2name(FileFormat::LZ4_LEGACY));
|
||||
r_fmt = FileFormat::LZ4_LEGACY;
|
||||
}
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(r_fmt)) {
|
||||
if (!skip_comp && !fmt_compressed_any(check_fmt(m.data(), m.size())) && fmt_compressed(r_fmt)) {
|
||||
hdr->ramdisk_size() = compress_len(r_fmt, m, fd);
|
||||
} else {
|
||||
hdr->ramdisk_size() = xwrite(fd, m.buf(), m.sz());
|
||||
hdr->ramdisk_size() = xwrite(fd, m.data(), m.size());
|
||||
}
|
||||
file_align();
|
||||
}
|
||||
@@ -823,10 +855,10 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
off.extra = lseek(fd, 0, SEEK_CUR);
|
||||
if (access(EXTRA_FILE, R_OK) == 0) {
|
||||
mmap_data m(EXTRA_FILE);
|
||||
if (!skip_comp && !COMPRESSED_ANY(check_fmt(m.buf(), m.sz())) && COMPRESSED(boot.e_fmt)) {
|
||||
if (!skip_comp && !fmt_compressed_any(check_fmt(m.data(), m.size())) && fmt_compressed(boot.e_fmt)) {
|
||||
hdr->extra_size() = compress_len(boot.e_fmt, m, fd);
|
||||
} else {
|
||||
hdr->extra_size() = xwrite(fd, m.buf(), m.sz());
|
||||
hdr->extra_size() = xwrite(fd, m.data(), m.size());
|
||||
}
|
||||
file_align();
|
||||
}
|
||||
@@ -889,8 +921,8 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
// Pad image to original size if not chromeos (as it requires post processing)
|
||||
if (!boot.flags[CHROMEOS_FLAG]) {
|
||||
off_t current = lseek(fd, 0, SEEK_CUR);
|
||||
if (current < boot.map.sz()) {
|
||||
write_zero(fd, boot.map.sz() - current);
|
||||
if (current < boot.map.size()) {
|
||||
write_zero(fd, boot.map.size() - current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,16 +931,16 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
******************/
|
||||
|
||||
// Map output image as rw
|
||||
mmap_data out(out_img, true);
|
||||
mmap_data out(out_img.data(), true);
|
||||
|
||||
// MTK headers
|
||||
if (boot.flags[MTK_KERNEL]) {
|
||||
auto m_hdr = reinterpret_cast<mtk_hdr *>(out.buf() + off.kernel);
|
||||
auto m_hdr = reinterpret_cast<mtk_hdr *>(out.data() + off.kernel);
|
||||
m_hdr->size = hdr->kernel_size();
|
||||
hdr->kernel_size() += sizeof(mtk_hdr);
|
||||
}
|
||||
if (boot.flags[MTK_RAMDISK]) {
|
||||
auto m_hdr = reinterpret_cast<mtk_hdr *>(out.buf() + off.ramdisk);
|
||||
auto m_hdr = reinterpret_cast<mtk_hdr *>(out.data() + off.ramdisk);
|
||||
m_hdr->size = hdr->ramdisk_size();
|
||||
hdr->ramdisk_size() += sizeof(mtk_hdr);
|
||||
}
|
||||
@@ -920,28 +952,28 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
if (char *id = hdr->id()) {
|
||||
auto ctx = get_sha(!boot.flags[SHA256_FLAG]);
|
||||
uint32_t size = hdr->kernel_size();
|
||||
ctx->update(byte_view(out.buf() + off.kernel, size));
|
||||
ctx->update(byte_view(out.data() + off.kernel, size));
|
||||
ctx->update(byte_view(&size, sizeof(size)));
|
||||
size = hdr->ramdisk_size();
|
||||
ctx->update(byte_view(out.buf() + off.ramdisk, size));
|
||||
ctx->update(byte_view(out.data() + off.ramdisk, size));
|
||||
ctx->update(byte_view(&size, sizeof(size)));
|
||||
size = hdr->second_size();
|
||||
ctx->update(byte_view(out.buf() + off.second, size));
|
||||
ctx->update(byte_view(out.data() + off.second, size));
|
||||
ctx->update(byte_view(&size, sizeof(size)));
|
||||
size = hdr->extra_size();
|
||||
if (size) {
|
||||
ctx->update(byte_view(out.buf() + off.extra, size));
|
||||
ctx->update(byte_view(out.data() + off.extra, size));
|
||||
ctx->update(byte_view(&size, sizeof(size)));
|
||||
}
|
||||
uint32_t ver = hdr->header_version();
|
||||
if (ver == 1 || ver == 2) {
|
||||
size = hdr->recovery_dtbo_size();
|
||||
ctx->update(byte_view(out.buf() + hdr->recovery_dtbo_offset(), size));
|
||||
ctx->update(byte_view(out.data() + hdr->recovery_dtbo_offset(), size));
|
||||
ctx->update(byte_view(&size, sizeof(size)));
|
||||
}
|
||||
if (ver == 2) {
|
||||
size = hdr->dtb_size();
|
||||
ctx->update(byte_view(out.buf() + off.dtb, size));
|
||||
ctx->update(byte_view(out.data() + off.dtb, size));
|
||||
ctx->update(byte_view(&size, sizeof(size)));
|
||||
}
|
||||
memset(id, 0, BOOT_ID_SIZE);
|
||||
@@ -954,40 +986,40 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
// Copy main header
|
||||
if (boot.flags[AMONET_FLAG]) {
|
||||
auto real_hdr_sz = std::min(hdr->hdr_space() - AMONET_MICROLOADER_SZ, hdr->hdr_size());
|
||||
memcpy(out.buf() + off.header + AMONET_MICROLOADER_SZ, hdr->raw_hdr(), real_hdr_sz);
|
||||
memcpy(out.data() + off.header + AMONET_MICROLOADER_SZ, hdr->raw_hdr(), real_hdr_sz);
|
||||
} else {
|
||||
memcpy(out.buf() + off.header, hdr->raw_hdr(), hdr->hdr_size());
|
||||
memcpy(out.data() + off.header, hdr->raw_hdr(), hdr->hdr_size());
|
||||
}
|
||||
|
||||
if (boot.flags[AVB_FLAG]) {
|
||||
// Copy and patch AVB structures
|
||||
auto footer = reinterpret_cast<AvbFooter*>(out.buf() + out.sz() - sizeof(AvbFooter));
|
||||
auto footer = reinterpret_cast<AvbFooter*>(out.data() + out.size() - sizeof(AvbFooter));
|
||||
memcpy(footer, boot.avb_footer, sizeof(AvbFooter));
|
||||
footer->original_image_size = __builtin_bswap64(off.total);
|
||||
footer->vbmeta_offset = __builtin_bswap64(off.vbmeta);
|
||||
if (check_env("PATCHVBMETAFLAG")) {
|
||||
auto vbmeta = reinterpret_cast<AvbVBMetaImageHeader*>(out.buf() + off.vbmeta);
|
||||
auto vbmeta = reinterpret_cast<AvbVBMetaImageHeader*>(out.data() + off.vbmeta);
|
||||
vbmeta->flags = __builtin_bswap32(3);
|
||||
}
|
||||
}
|
||||
|
||||
if (boot.flags[DHTB_FLAG]) {
|
||||
// DHTB header
|
||||
auto d_hdr = reinterpret_cast<dhtb_hdr *>(out.buf());
|
||||
auto d_hdr = reinterpret_cast<dhtb_hdr *>(out.data());
|
||||
memcpy(d_hdr, DHTB_MAGIC, 8);
|
||||
d_hdr->size = off.total - sizeof(dhtb_hdr);
|
||||
sha256_hash(byte_view(out.buf() + sizeof(dhtb_hdr), d_hdr->size),
|
||||
sha256_hash(byte_view(out.data() + sizeof(dhtb_hdr), d_hdr->size),
|
||||
byte_data(d_hdr->checksum, 32));
|
||||
} else if (boot.flags[BLOB_FLAG]) {
|
||||
// Blob header
|
||||
auto b_hdr = reinterpret_cast<blob_hdr *>(out.buf());
|
||||
auto b_hdr = reinterpret_cast<blob_hdr *>(out.data());
|
||||
b_hdr->size = off.total - sizeof(blob_hdr);
|
||||
}
|
||||
|
||||
// Sign the image after we finish patching the boot image
|
||||
if (boot.flags[AVB1_SIGNED_FLAG]) {
|
||||
byte_view payload(out.buf() + off.header, off.total - off.header);
|
||||
auto sig = rust::sign_boot_image(payload, "/boot", nullptr, nullptr);
|
||||
byte_view payload(out.data() + off.header, off.total - off.header);
|
||||
auto sig = sign_payload(payload);
|
||||
if (!sig.empty()) {
|
||||
lseek(fd, off.total, SEEK_SET);
|
||||
xwrite(fd, sig.data(), sig.size());
|
||||
@@ -997,33 +1029,15 @@ void repack(const char *src_img, const char *out_img, bool skip_comp) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int verify(const char *image, const char *cert) {
|
||||
const boot_img boot(image);
|
||||
if (cert == nullptr) {
|
||||
// Boot image parsing already checks if the image is signed
|
||||
return boot.flags[AVB1_SIGNED_FLAG] ? 0 : 1;
|
||||
} else {
|
||||
// Provide a custom certificate and re-verify
|
||||
return boot.verify(cert) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
int sign(const char *image, const char *name, const char *cert, const char *key) {
|
||||
const boot_img boot(image);
|
||||
auto sig = rust::sign_boot_image(boot.payload, name, cert, key);
|
||||
if (sig.empty())
|
||||
return 1;
|
||||
|
||||
auto eof = boot.tail.buf() - boot.map.buf();
|
||||
int fd = xopen(image, O_WRONLY | O_CLOEXEC);
|
||||
if (lseek(fd, eof, SEEK_SET) != eof || xwrite(fd, sig.data(), sig.size()) != sig.size()) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
if (auto off = lseek(fd, 0, SEEK_CUR); off < boot.map.sz()) {
|
||||
// Wipe out rest of tail
|
||||
write_zero(fd, boot.map.sz() - off);
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
void cleanup() {
|
||||
unlink(HEADER_FILE);
|
||||
unlink(KERNEL_FILE);
|
||||
unlink(RAMDISK_FILE);
|
||||
unlink(SECOND_FILE);
|
||||
unlink(KER_DTB_FILE);
|
||||
unlink(EXTRA_FILE);
|
||||
unlink(RECV_DTBO_FILE);
|
||||
unlink(DTB_FILE);
|
||||
unlink(BOOTCONFIG_FILE);
|
||||
rm_rf(VND_RAMDISK_DIR);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <bitset>
|
||||
#include <cxx.h>
|
||||
|
||||
#include "format.hpp"
|
||||
#include <rust/cxx.h>
|
||||
|
||||
/******************
|
||||
* Special Headers
|
||||
@@ -347,6 +345,17 @@ struct vendor_ramdisk_table_entry_v4 {
|
||||
* Polymorphic Universal Header
|
||||
*******************************/
|
||||
|
||||
template <typename T>
|
||||
static T align_to(T v, int a) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
return (v + a - 1) / a * a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T align_padding(T v, int a) {
|
||||
return align_to(v, a) - v;
|
||||
}
|
||||
|
||||
#define decl_val(name, len) \
|
||||
virtual uint##len##_t name() const { return 0; }
|
||||
|
||||
@@ -676,7 +685,12 @@ struct boot_img {
|
||||
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
|
||||
|
||||
// Rust FFI
|
||||
static std::unique_ptr<boot_img> create(Utf8CStr name) { return std::make_unique<boot_img>(name.c_str()); }
|
||||
rust::Slice<const uint8_t> get_payload() const { return payload; }
|
||||
rust::Slice<const uint8_t> get_tail() const { return tail; }
|
||||
bool verify(const char *cert = nullptr) const;
|
||||
bool is_signed() const { return flags[AVB1_SIGNED_FLAG]; }
|
||||
uint64_t tail_off() const { return tail.data() - map.data(); }
|
||||
|
||||
// Implemented in Rust
|
||||
bool verify() const noexcept;
|
||||
};
|
||||
|
||||
463
native/src/boot/cli.rs
Normal file
463
native/src/boot/cli.rs
Normal file
@@ -0,0 +1,463 @@
|
||||
use crate::compress::{compress_cmd, decompress_cmd};
|
||||
use crate::cpio::{cpio_commands, print_cpio_usage};
|
||||
use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage};
|
||||
use crate::ffi::{BootImage, FileFormat, cleanup, repack, split_image_dtb, unpack};
|
||||
use crate::patch::hexpatch;
|
||||
use crate::payload::extract_boot_from_payload;
|
||||
use crate::sign::{sha1_hash, sign_boot_image};
|
||||
use argh::{CommandInfo, EarlyExit, FromArgs, SubCommand};
|
||||
use base::{
|
||||
CmdArgs, EarlyExitExt, LoggedResult, MappedFile, PositionalArgParser, ResultExt, Utf8CStr,
|
||||
Utf8CString, WriteExt, cmdline_logging, cstr, libc, libc::umask, log_err,
|
||||
};
|
||||
use std::ffi::c_char;
|
||||
use std::io::{Seek, SeekFrom, Write};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct Cli {
|
||||
#[argh(subcommand)]
|
||||
action: Action,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand)]
|
||||
enum Action {
|
||||
Unpack(Unpack),
|
||||
Repack(Repack),
|
||||
Verify(Verify),
|
||||
Sign(Sign),
|
||||
Extract(Extract),
|
||||
HexPatch(HexPatch),
|
||||
Cpio(Cpio),
|
||||
Dtb(Dtb),
|
||||
Split(Split),
|
||||
Sha1(Sha1),
|
||||
Cleanup(Cleanup),
|
||||
Compress(Compress),
|
||||
Decompress(Decompress),
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "unpack")]
|
||||
struct Unpack {
|
||||
#[argh(switch, short = 'n')]
|
||||
no_decompress: bool,
|
||||
#[argh(switch, short = 'h')]
|
||||
dump_header: bool,
|
||||
#[argh(positional)]
|
||||
img: Utf8CString,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "repack")]
|
||||
struct Repack {
|
||||
#[argh(switch, short = 'n')]
|
||||
no_compress: bool,
|
||||
#[argh(positional)]
|
||||
img: Utf8CString,
|
||||
#[argh(positional, default = r#"Utf8CString::from("new-boot.img")"#)]
|
||||
out: Utf8CString,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "verify")]
|
||||
struct Verify {
|
||||
#[argh(positional)]
|
||||
img: Utf8CString,
|
||||
#[argh(positional)]
|
||||
cert: Option<Utf8CString>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "sign")]
|
||||
struct Sign {
|
||||
#[argh(positional)]
|
||||
img: Utf8CString,
|
||||
#[argh(positional)]
|
||||
args: Vec<Utf8CString>,
|
||||
}
|
||||
|
||||
struct Extract {
|
||||
payload: Utf8CString,
|
||||
partition: Option<Utf8CString>,
|
||||
outfile: Option<Utf8CString>,
|
||||
}
|
||||
|
||||
impl FromArgs for Extract {
|
||||
fn from_args(_command_name: &[&str], args: &[&str]) -> Result<Self, EarlyExit> {
|
||||
let mut parse = PositionalArgParser(args.iter());
|
||||
Ok(Extract {
|
||||
payload: parse.required("payload.bin")?,
|
||||
partition: parse.optional(),
|
||||
outfile: parse.last_optional()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SubCommand for Extract {
|
||||
const COMMAND: &'static CommandInfo = &CommandInfo {
|
||||
name: "extract",
|
||||
description: "",
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "hexpatch")]
|
||||
struct HexPatch {
|
||||
#[argh(positional)]
|
||||
file: Utf8CString,
|
||||
#[argh(positional)]
|
||||
src: Utf8CString,
|
||||
#[argh(positional)]
|
||||
dest: Utf8CString,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "cpio")]
|
||||
struct Cpio {
|
||||
#[argh(positional)]
|
||||
file: Utf8CString,
|
||||
#[argh(positional)]
|
||||
cmds: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "dtb")]
|
||||
struct Dtb {
|
||||
#[argh(positional)]
|
||||
file: Utf8CString,
|
||||
#[argh(subcommand)]
|
||||
action: DtbAction,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "split")]
|
||||
struct Split {
|
||||
#[argh(switch, short = 'n')]
|
||||
no_decompress: bool,
|
||||
#[argh(positional)]
|
||||
file: Utf8CString,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "sha1")]
|
||||
struct Sha1 {
|
||||
#[argh(positional)]
|
||||
file: Utf8CString,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "cleanup")]
|
||||
struct Cleanup {}
|
||||
|
||||
struct Compress {
|
||||
format: FileFormat,
|
||||
file: Utf8CString,
|
||||
out: Option<Utf8CString>,
|
||||
}
|
||||
|
||||
impl FromArgs for Compress {
|
||||
fn from_args(command_name: &[&str], args: &[&str]) -> Result<Self, EarlyExit> {
|
||||
let cmd = command_name.last().copied().unwrap_or_default();
|
||||
let fmt = cmd.strip_prefix("compress=").unwrap_or("gzip");
|
||||
|
||||
let Ok(fmt) = FileFormat::from_str(fmt) else {
|
||||
return Err(EarlyExit::from(format!(
|
||||
"Unsupported or unknown compression format: {fmt}\n"
|
||||
)));
|
||||
};
|
||||
|
||||
let mut iter = PositionalArgParser(args.iter());
|
||||
Ok(Compress {
|
||||
format: fmt,
|
||||
file: iter.required("infile")?,
|
||||
out: iter.last_optional()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SubCommand for Compress {
|
||||
const COMMAND: &'static CommandInfo = &CommandInfo {
|
||||
name: "compress",
|
||||
description: "",
|
||||
};
|
||||
}
|
||||
|
||||
struct Decompress {
|
||||
file: Utf8CString,
|
||||
out: Option<Utf8CString>,
|
||||
}
|
||||
|
||||
impl FromArgs for Decompress {
|
||||
fn from_args(_command_name: &[&str], args: &[&str]) -> Result<Self, EarlyExit> {
|
||||
let mut iter = PositionalArgParser(args.iter());
|
||||
Ok(Decompress {
|
||||
file: iter.required("infile")?,
|
||||
out: iter.last_optional()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SubCommand for Decompress {
|
||||
const COMMAND: &'static CommandInfo = &CommandInfo {
|
||||
name: "decompress",
|
||||
description: "",
|
||||
};
|
||||
}
|
||||
|
||||
fn print_usage(cmd: &str) {
|
||||
eprintln!(
|
||||
r#"MagiskBoot - Boot Image Modification Tool
|
||||
|
||||
Usage: {0} <action> [args...]
|
||||
|
||||
Supported actions:
|
||||
unpack [-n] [-h] <bootimg>
|
||||
Unpack <bootimg> to its individual components, each component to
|
||||
a file with its corresponding file name in the current directory.
|
||||
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
|
||||
dtb, extra, and recovery_dtbo.
|
||||
By default, each component will be decompressed on-the-fly.
|
||||
If '-n' is provided, all decompression operations will be skipped;
|
||||
each component will remain untouched, dumped in its original format.
|
||||
If '-h' is provided, the boot image header information will be
|
||||
dumped to the file 'header', which can be used to modify header
|
||||
configurations during repacking.
|
||||
Return values:
|
||||
0:valid 1:error 2:chromeos
|
||||
|
||||
repack [-n] <origbootimg> [outbootimg]
|
||||
Repack boot image components using files from the current directory
|
||||
to [outbootimg], or 'new-boot.img' if not specified. Current directory
|
||||
should only contain required files for [outbootimg], or incorrect
|
||||
[outbootimg] may be produced.
|
||||
<origbootimg> is the original boot image used to unpack the components.
|
||||
By default, each component will be automatically compressed using its
|
||||
corresponding format detected in <origbootimg>. If a component file
|
||||
in the current directory is already compressed, then no addition
|
||||
compression will be performed for that specific component.
|
||||
If '-n' is provided, all compression operations will be skipped.
|
||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
|
||||
the boot image's vbmeta header will be set.
|
||||
|
||||
verify <bootimg> [x509.pem]
|
||||
Check whether the boot image is signed with AVB 1.0 signature.
|
||||
Optionally provide a certificate to verify whether the image is
|
||||
signed by the public key certificate.
|
||||
Return value:
|
||||
0:valid 1:error
|
||||
|
||||
sign <bootimg> [name] [x509.pem pk8]
|
||||
Sign <bootimg> with AVB 1.0 signature.
|
||||
Optionally provide the name of the image (default: '/boot').
|
||||
Optionally provide the certificate/private key pair for signing.
|
||||
If the certificate/private key pair is not provided, the AOSP
|
||||
verity key bundled in the executable will be used.
|
||||
|
||||
extract <payload.bin> [partition] [outfile]
|
||||
Extract [partition] from <payload.bin> to [outfile].
|
||||
If [outfile] is not specified, then output to '[partition].img'.
|
||||
If [partition] is not specified, then attempt to extract either
|
||||
'init_boot' or 'boot'. Which partition was chosen can be determined
|
||||
by whichever 'init_boot.img' or 'boot.img' exists.
|
||||
<payload.bin> can be '-' to be STDIN.
|
||||
|
||||
hexpatch <file> <hexpattern1> <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace it with <hexpattern2>
|
||||
|
||||
cpio <incpio> [commands...]
|
||||
Do cpio commands to <incpio> (modifications are done in-place).
|
||||
Each command is a single argument; add quotes for each command.
|
||||
See "cpio --help" for supported commands.
|
||||
|
||||
dtb <file> <action> [args...]
|
||||
Do dtb related actions to <file>.
|
||||
See "dtb --help" for supported actions.
|
||||
|
||||
split [-n] <file>
|
||||
Split image.*-dtb into kernel + kernel_dtb.
|
||||
If '-n' is provided, decompression operations will be skipped;
|
||||
the kernel will remain untouched, split in its original format.
|
||||
|
||||
sha1 <file>
|
||||
Print the SHA1 checksum for <file>
|
||||
|
||||
cleanup
|
||||
Cleanup the current working directory
|
||||
|
||||
compress[=format] <infile> [outfile]
|
||||
Compress <infile> with [format] to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [format] is not specified, then gzip will be used.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file suffixed with a matching file extension.
|
||||
Supported formats:
|
||||
{1}
|
||||
|
||||
decompress <infile> [outfile]
|
||||
Detect format and decompress <infile> to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file removing its archive format file extension.
|
||||
Supported formats:
|
||||
{1}
|
||||
"#,
|
||||
cmd,
|
||||
FileFormat::formats()
|
||||
);
|
||||
}
|
||||
|
||||
fn verify_cmd(image: &Utf8CStr, cert: Option<&Utf8CStr>) -> bool {
|
||||
let image = BootImage::new(image);
|
||||
match cert {
|
||||
None => {
|
||||
// Boot image parsing already checks if the image is signed
|
||||
image.is_signed()
|
||||
}
|
||||
Some(_) => {
|
||||
// Provide a custom certificate and re-verify
|
||||
image.verify(cert).is_ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sign_cmd(
|
||||
image: &Utf8CStr,
|
||||
name: Option<&Utf8CStr>,
|
||||
cert: Option<&Utf8CStr>,
|
||||
key: Option<&Utf8CStr>,
|
||||
) -> LoggedResult<()> {
|
||||
let img = BootImage::new(image);
|
||||
let name = name.unwrap_or(cstr!("/boot"));
|
||||
let sig = sign_boot_image(img.payload(), name, cert, key)?;
|
||||
let tail_off = img.tail_off();
|
||||
drop(img);
|
||||
let mut fd = image.open(libc::O_WRONLY | libc::O_CLOEXEC)?;
|
||||
fd.seek(SeekFrom::Start(tail_off))?;
|
||||
fd.write_all(&sig)?;
|
||||
let current = fd.stream_position()?;
|
||||
let eof = fd.seek(SeekFrom::End(0))?;
|
||||
if eof > current {
|
||||
// Zero out rest of the file
|
||||
fd.seek(SeekFrom::Start(current))?;
|
||||
fd.write_zeros((eof - current) as usize)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn boot_main(cmds: CmdArgs) -> LoggedResult<i32> {
|
||||
let mut cmds = cmds.0;
|
||||
if cmds.len() < 2 {
|
||||
print_usage(cmds.first().unwrap_or(&"magiskboot"));
|
||||
return log_err!();
|
||||
}
|
||||
|
||||
if cmds[1].starts_with("--") {
|
||||
cmds[1] = &cmds[1][2..];
|
||||
}
|
||||
|
||||
let cli = if cmds[1].starts_with("compress=") {
|
||||
// Skip the main parser, directly parse the subcommand
|
||||
Compress::from_args(&cmds[..2], &cmds[2..]).map(|compress| Cli {
|
||||
action: Action::Compress(compress),
|
||||
})
|
||||
} else {
|
||||
Cli::from_args(&[cmds[0]], &cmds[1..])
|
||||
}
|
||||
.on_early_exit(|| match cmds[1] {
|
||||
"dtb" => print_dtb_usage(),
|
||||
"cpio" => print_cpio_usage(),
|
||||
_ => print_usage(cmds[0]),
|
||||
});
|
||||
|
||||
match cli.action {
|
||||
Action::Unpack(Unpack {
|
||||
no_decompress,
|
||||
dump_header,
|
||||
img,
|
||||
}) => {
|
||||
return Ok(unpack(&img, no_decompress, dump_header));
|
||||
}
|
||||
Action::Repack(Repack {
|
||||
no_compress,
|
||||
img,
|
||||
out,
|
||||
}) => {
|
||||
repack(&img, &out, no_compress);
|
||||
}
|
||||
Action::Verify(Verify { img, cert }) => {
|
||||
if !verify_cmd(&img, cert.as_deref()) {
|
||||
return log_err!();
|
||||
}
|
||||
}
|
||||
Action::Sign(Sign { img, args }) => {
|
||||
let mut iter = args.iter();
|
||||
sign_cmd(
|
||||
&img,
|
||||
iter.next().map(AsRef::as_ref),
|
||||
iter.next().map(AsRef::as_ref),
|
||||
iter.next().map(AsRef::as_ref),
|
||||
)?;
|
||||
}
|
||||
Action::Extract(Extract {
|
||||
payload,
|
||||
partition,
|
||||
outfile,
|
||||
}) => {
|
||||
extract_boot_from_payload(
|
||||
&payload,
|
||||
partition.as_ref().map(AsRef::as_ref),
|
||||
outfile.as_ref().map(AsRef::as_ref),
|
||||
)
|
||||
.log_with_msg(|w| w.write_str("Failed to extract from payload"))?;
|
||||
}
|
||||
Action::HexPatch(HexPatch { file, src, dest }) => {
|
||||
if !hexpatch(&file, &src, &dest) {
|
||||
log_err!("Failed to patch")?;
|
||||
}
|
||||
}
|
||||
Action::Cpio(Cpio { file, cmds }) => {
|
||||
cpio_commands(&file, &cmds).log_with_msg(|w| w.write_str("Failed to process cpio"))?;
|
||||
}
|
||||
Action::Dtb(Dtb { file, action }) => {
|
||||
return dtb_commands(&file, &action)
|
||||
.map(|b| if b { 0 } else { 1 })
|
||||
.log_with_msg(|w| w.write_str("Failed to process dtb"));
|
||||
}
|
||||
Action::Split(Split {
|
||||
no_decompress,
|
||||
file,
|
||||
}) => {
|
||||
return Ok(split_image_dtb(&file, no_decompress));
|
||||
}
|
||||
Action::Sha1(Sha1 { file }) => {
|
||||
let file = MappedFile::open(&file)?;
|
||||
let mut sha1 = [0u8; 20];
|
||||
sha1_hash(file.as_ref(), &mut sha1);
|
||||
for byte in &sha1 {
|
||||
print!("{byte:02x}");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
Action::Cleanup(_) => {
|
||||
eprintln!("Cleaning up...");
|
||||
cleanup();
|
||||
}
|
||||
Action::Decompress(Decompress { file, out }) => {
|
||||
decompress_cmd(&file, out.as_deref())?;
|
||||
}
|
||||
Action::Compress(Compress { format, file, out }) => {
|
||||
compress_cmd(format, &file, out.as_deref())?;
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 {
|
||||
cmdline_logging();
|
||||
unsafe { umask(0) };
|
||||
let cmds = CmdArgs::new(argc, argv);
|
||||
boot_main(cmds).unwrap_or(1)
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
use crate::ffi::FileFormat;
|
||||
use base::{Chunker, LoggedResult, WriteExt};
|
||||
use bytemuck::bytes_of_mut;
|
||||
use bzip2::{Compression as BzCompression, write::BzDecoder, write::BzEncoder};
|
||||
use flate2::{Compression as GzCompression, write::GzEncoder, write::MultiGzDecoder};
|
||||
use crate::ffi::{FileFormat, check_fmt};
|
||||
use base::libc::{O_RDONLY, O_TRUNC, O_WRONLY};
|
||||
use base::{
|
||||
Chunker, FileOrStd, LoggedResult, ReadExt, ResultExt, Utf8CStr, Utf8CString, WriteExt, log_err,
|
||||
};
|
||||
use bzip2::{Compression as BzCompression, read::BzDecoder, write::BzEncoder};
|
||||
use flate2::{Compression as GzCompression, read::MultiGzDecoder, write::GzEncoder};
|
||||
use lz4::{
|
||||
BlockMode, BlockSize, ContentChecksum, Encoder as LZ4FrameEncoder,
|
||||
BlockMode, BlockSize, ContentChecksum, Decoder as LZ4FrameDecoder, Encoder as LZ4FrameEncoder,
|
||||
EncoderBuilder as LZ4FrameEncoderBuilder, block::CompressionMode, liblz4::BlockChecksum,
|
||||
};
|
||||
use std::cell::Cell;
|
||||
use lzma_rust2::{CheckType, LZMAOptions, LZMAReader, LZMAWriter, XZOptions, XZReader, XZWriter};
|
||||
use std::cmp::min;
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Read, Write};
|
||||
use std::io::{BufWriter, Cursor, Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
use xz2::{
|
||||
stream::{Check as LzmaCheck, Filters as LzmaFilters, LzmaOptions, Stream as LzmaStream},
|
||||
write::{XzDecoder, XzEncoder},
|
||||
};
|
||||
use zopfli::{BlockType, GzipEncoder as ZopFliEncoder, Options as ZopfliOptions};
|
||||
|
||||
pub trait WriteFinish<W: Write>: Write {
|
||||
@@ -36,19 +36,7 @@ macro_rules! finish_impl {
|
||||
)*}
|
||||
}
|
||||
|
||||
finish_impl!(GzEncoder<W>, MultiGzDecoder<W>, BzEncoder<W>, XzEncoder<W>);
|
||||
|
||||
macro_rules! finish_impl_ref {
|
||||
($($t:ty),*) => {$(
|
||||
impl<W: Write> WriteFinish<W> for $t {
|
||||
fn finish(mut self: Box<Self>) -> std::io::Result<W> {
|
||||
Self::finish(self.as_mut())
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
finish_impl_ref!(BzDecoder<W>, XzDecoder<W>);
|
||||
finish_impl!(GzEncoder<W>, BzEncoder<W>, XZWriter<W>, LZMAWriter<W>);
|
||||
|
||||
impl<W: Write> WriteFinish<W> for BufWriter<ZopFliEncoder<W>> {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W> {
|
||||
@@ -65,89 +53,6 @@ impl<W: Write> WriteFinish<W> for LZ4FrameEncoder<W> {
|
||||
}
|
||||
}
|
||||
|
||||
// Adapt Reader to Writer
|
||||
|
||||
// In case some decoders don't support the Write trait, instead of pushing data into the
|
||||
// decoder, we have no choice but to pull data out of it. So first, we create a "fake" reader
|
||||
// that does not own any data as a placeholder. In the Writer adapter struct, when data
|
||||
// is fed in, we call FakeReader::set_data to forward this data as the "source" of the
|
||||
// decoder. Next, we pull data out of the decoder, and finally, forward the decoded data to output.
|
||||
|
||||
struct FakeReader(Cell<&'static [u8]>);
|
||||
|
||||
impl FakeReader {
|
||||
fn new() -> FakeReader {
|
||||
FakeReader(Cell::new(&[]))
|
||||
}
|
||||
|
||||
// SAFETY: the lifetime of the buffer is between the invocation of
|
||||
// this method and the invocation of FakeReader::clear. There is currently
|
||||
// no way to represent this with Rust's lifetime marker, so we transmute all
|
||||
// lifetimes away and make the users of this struct manually manage the lifetime.
|
||||
// It is the responsibility of the caller to ensure the underlying reference does not
|
||||
// live longer than it should.
|
||||
unsafe fn set_data(&self, data: &[u8]) {
|
||||
let buf: &'static [u8] = unsafe { std::mem::transmute(data) };
|
||||
self.0.set(buf)
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
self.0.set(&[])
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FakeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let data = self.0.get();
|
||||
let len = std::cmp::min(buf.len(), data.len());
|
||||
buf[..len].copy_from_slice(&data[..len]);
|
||||
self.0.set(&data[len..]);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
// LZ4FrameDecoder
|
||||
|
||||
struct LZ4FrameDecoder<W: Write> {
|
||||
write: W,
|
||||
decoder: lz4::Decoder<FakeReader>,
|
||||
}
|
||||
|
||||
impl<W: Write> LZ4FrameDecoder<W> {
|
||||
fn new(write: W) -> Self {
|
||||
let fake = FakeReader::new();
|
||||
let decoder = lz4::Decoder::new(fake).unwrap();
|
||||
LZ4FrameDecoder { write, decoder }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LZ4FrameDecoder<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write_all(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
// SAFETY: buf is removed from the reader immediately after usage
|
||||
unsafe { self.decoder.reader().set_data(buf) };
|
||||
std::io::copy(&mut self.decoder, &mut self.write)?;
|
||||
self.decoder.reader().clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteFinish<W> for LZ4FrameDecoder<W> {
|
||||
fn finish(self: Box<Self>) -> std::io::Result<W> {
|
||||
let (_, r) = self.decoder.finish();
|
||||
r?;
|
||||
Ok(self.write)
|
||||
}
|
||||
}
|
||||
|
||||
// LZ4BlockArchive format
|
||||
//
|
||||
// len: | 4 | 4 | n | ... | 4 |
|
||||
@@ -157,7 +62,7 @@ impl<W: Write> WriteFinish<W> for LZ4FrameDecoder<W> {
|
||||
|
||||
const LZ4_BLOCK_SIZE: usize = 0x800000;
|
||||
const LZ4HC_CLEVEL_MAX: i32 = 12;
|
||||
const LZ4_MAGIC: &[u8] = b"\x02\x21\x4c\x18";
|
||||
const LZ4_MAGIC: u32 = 0x184c2102;
|
||||
|
||||
struct LZ4BlockEncoder<W: Write> {
|
||||
write: W,
|
||||
@@ -206,7 +111,7 @@ impl<W: Write> Write for LZ4BlockEncoder<W> {
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> std::io::Result<()> {
|
||||
if self.total == 0 {
|
||||
// Write header
|
||||
self.write.write_all(LZ4_MAGIC)?;
|
||||
self.write.write_pod(&LZ4_MAGIC)?;
|
||||
}
|
||||
|
||||
self.total += buf.len() as u32;
|
||||
@@ -236,88 +141,72 @@ impl<W: Write> WriteFinish<W> for LZ4BlockEncoder<W> {
|
||||
|
||||
// LZ4BlockDecoder
|
||||
|
||||
struct LZ4BlockDecoder<W: Write> {
|
||||
write: W,
|
||||
chunker: Chunker,
|
||||
struct LZ4BlockDecoder<R: Read> {
|
||||
read: R,
|
||||
in_buf: Box<[u8]>,
|
||||
out_buf: Box<[u8]>,
|
||||
curr_block_size: usize,
|
||||
out_len: usize,
|
||||
out_pos: usize,
|
||||
}
|
||||
|
||||
impl<W: Write> LZ4BlockDecoder<W> {
|
||||
fn new(write: W) -> Self {
|
||||
LZ4BlockDecoder {
|
||||
write,
|
||||
chunker: Chunker::new(size_of::<u32>()),
|
||||
// SAFETY: all bytes will be initialized before it is used
|
||||
impl<R: Read> LZ4BlockDecoder<R> {
|
||||
fn new(read: R) -> Self {
|
||||
let compressed_sz = lz4::block::compress_bound(LZ4_BLOCK_SIZE).unwrap_or(LZ4_BLOCK_SIZE);
|
||||
Self {
|
||||
read,
|
||||
in_buf: unsafe { Box::new_uninit_slice(compressed_sz).assume_init() },
|
||||
out_buf: unsafe { Box::new_uninit_slice(LZ4_BLOCK_SIZE).assume_init() },
|
||||
curr_block_size: 0,
|
||||
out_len: 0,
|
||||
out_pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_block(write: &mut W, out_buf: &mut [u8], chunk: &[u8]) -> std::io::Result<()> {
|
||||
let decompressed_size =
|
||||
lz4::block::decompress_to_buffer(chunk, Some(LZ4_BLOCK_SIZE as i32), out_buf)?;
|
||||
write.write_all(&out_buf[..decompressed_size])
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LZ4BlockDecoder<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.write_all(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> std::io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
let (b, chunk) = self.chunker.add_data(buf);
|
||||
buf = b;
|
||||
if let Some(chunk) = chunk {
|
||||
if chunk == LZ4_MAGIC {
|
||||
// Skip magic, read next u32
|
||||
continue;
|
||||
}
|
||||
if self.curr_block_size == 0 {
|
||||
// We haven't got the current block size yet, try read it
|
||||
let mut next_u32: u32 = 0;
|
||||
bytes_of_mut(&mut next_u32).copy_from_slice(chunk);
|
||||
|
||||
if next_u32 > lz4::block::compress_bound(LZ4_BLOCK_SIZE)? as u32 {
|
||||
// This is the LG format trailer, EOF
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update chunker to read next block
|
||||
self.curr_block_size = next_u32 as usize;
|
||||
self.chunker.set_chunk_size(self.curr_block_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Actually decode the block
|
||||
Self::decode_block(&mut self.write, &mut self.out_buf, chunk)?;
|
||||
|
||||
// Reset for the next block
|
||||
self.curr_block_size = 0;
|
||||
self.chunker.set_chunk_size(size_of::<u32>());
|
||||
impl<R: Read> Read for LZ4BlockDecoder<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
if self.out_pos == self.out_len {
|
||||
let mut block_size: u32 = 0;
|
||||
if let Err(e) = self.read.read_pod(&mut block_size) {
|
||||
return if e.kind() == std::io::ErrorKind::UnexpectedEof {
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(e)
|
||||
};
|
||||
}
|
||||
if block_size == LZ4_MAGIC {
|
||||
self.read.read_pod(&mut block_size)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteFinish<W> for LZ4BlockDecoder<W> {
|
||||
fn finish(mut self: Box<Self>) -> std::io::Result<W> {
|
||||
let chunk = self.chunker.get_available();
|
||||
if !chunk.is_empty() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Interrupted,
|
||||
"Finish ran before end of compressed stream",
|
||||
));
|
||||
let block_size = block_size as usize;
|
||||
|
||||
if block_size > self.in_buf.len() {
|
||||
// This may be the LG format trailer, EOF
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Read the entire compressed block
|
||||
let compressed_block = &mut self.in_buf[..block_size];
|
||||
if let Ok(len) = self.read.read(compressed_block) {
|
||||
if len == 0 {
|
||||
// We hit EOF, that's fine
|
||||
return Ok(0);
|
||||
} else if len != block_size {
|
||||
let remain = &mut compressed_block[len..];
|
||||
self.read.read_exact(remain)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.out_len = lz4::block::decompress_to_buffer(
|
||||
compressed_block,
|
||||
Some(LZ4_BLOCK_SIZE as i32),
|
||||
&mut self.out_buf,
|
||||
)?;
|
||||
self.out_pos = 0;
|
||||
}
|
||||
Ok(self.write)
|
||||
let copy_len = min(buf.len(), self.out_len - self.out_pos);
|
||||
buf[..copy_len].copy_from_slice(&self.out_buf[self.out_pos..self.out_pos + copy_len]);
|
||||
self.out_pos += copy_len;
|
||||
Ok(copy_len)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,16 +215,12 @@ impl<W: Write> WriteFinish<W> for LZ4BlockDecoder<W> {
|
||||
pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn WriteFinish<W> + 'a> {
|
||||
match format {
|
||||
FileFormat::XZ => {
|
||||
let opt = LzmaOptions::new_preset(9).unwrap();
|
||||
let stream =
|
||||
LzmaStream::new_stream_encoder(LzmaFilters::new().lzma2(&opt), LzmaCheck::Crc32)
|
||||
.unwrap();
|
||||
Box::new(XzEncoder::new_stream(w, stream))
|
||||
let mut opt = XZOptions::with_preset(9);
|
||||
opt.set_check_sum_type(CheckType::Crc32);
|
||||
Box::new(XZWriter::new(w, opt).unwrap())
|
||||
}
|
||||
FileFormat::LZMA => {
|
||||
let opt = LzmaOptions::new_preset(9).unwrap();
|
||||
let stream = LzmaStream::new_lzma_encoder(&opt).unwrap();
|
||||
Box::new(XzEncoder::new_stream(w, stream))
|
||||
Box::new(LZMAWriter::new_use_header(w, &LZMAOptions::with_preset(9), None).unwrap())
|
||||
}
|
||||
FileFormat::BZIP2 => Box::new(BzEncoder::new(w, BzCompression::best())),
|
||||
FileFormat::LZ4 => {
|
||||
@@ -366,45 +251,20 @@ pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn Write
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_decoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn WriteFinish<W> + 'a> {
|
||||
pub fn get_decoder<'a, R: Read + 'a>(format: FileFormat, r: R) -> Box<dyn Read + 'a> {
|
||||
match format {
|
||||
FileFormat::XZ | FileFormat::LZMA => {
|
||||
let stream = LzmaStream::new_auto_decoder(u64::MAX, 0).unwrap();
|
||||
Box::new(XzDecoder::new_stream(w, stream))
|
||||
}
|
||||
FileFormat::BZIP2 => Box::new(BzDecoder::new(w)),
|
||||
FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(w)),
|
||||
FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(w)),
|
||||
FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(w)),
|
||||
FileFormat::XZ => Box::new(XZReader::new(r, true)),
|
||||
FileFormat::LZMA => Box::new(LZMAReader::new_mem_limit(r, u32::MAX, None).unwrap()),
|
||||
FileFormat::BZIP2 => Box::new(BzDecoder::new(r)),
|
||||
FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(r).unwrap()),
|
||||
FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(r)),
|
||||
FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(r)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// C++ FFI
|
||||
|
||||
pub fn compress_fd(format: FileFormat, in_fd: RawFd, out_fd: RawFd) {
|
||||
let mut in_file = unsafe { ManuallyDrop::new(File::from_raw_fd(in_fd)) };
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut encoder = get_encoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
std::io::copy(in_file.deref_mut(), encoder.as_mut())?;
|
||||
encoder.finish()?;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn decompress_bytes_fd(format: FileFormat, in_bytes: &[u8], in_fd: RawFd, out_fd: RawFd) {
|
||||
let mut in_file = unsafe { ManuallyDrop::new(File::from_raw_fd(in_fd)) };
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut decoder = get_decoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
decoder.write_all(in_bytes)?;
|
||||
std::io::copy(in_file.deref_mut(), decoder.as_mut())?;
|
||||
decoder.finish()?;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
@@ -418,9 +278,112 @@ pub fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
|
||||
pub fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
|
||||
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
|
||||
|
||||
let mut decoder = get_decoder(format, out_file.deref_mut());
|
||||
let _: LoggedResult<()> = try {
|
||||
decoder.write_all(in_bytes)?;
|
||||
decoder.finish()?;
|
||||
};
|
||||
let mut decoder = get_decoder(format, in_bytes);
|
||||
std::io::copy(decoder.as_mut(), out_file.deref_mut()).log_ok();
|
||||
}
|
||||
|
||||
// Command-line entry points
|
||||
|
||||
pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> LoggedResult<()> {
|
||||
let in_std = infile == "-";
|
||||
let mut rm_in = false;
|
||||
|
||||
let mut buf = [0u8; 4096];
|
||||
|
||||
let input = if in_std {
|
||||
FileOrStd::StdIn
|
||||
} else {
|
||||
FileOrStd::File(infile.open(O_RDONLY)?)
|
||||
};
|
||||
|
||||
// First read some bytes for format detection
|
||||
let len = input.as_file().read(&mut buf)?;
|
||||
let buf = &buf[..len];
|
||||
|
||||
let format = check_fmt(buf);
|
||||
|
||||
eprintln!("Detected format: {format}");
|
||||
|
||||
if !format.is_compressed() {
|
||||
return log_err!("Input file is not a supported type!");
|
||||
}
|
||||
|
||||
// If user did not provide outfile, infile has to be either
|
||||
// <path>.[ext], or "-". Outfile will be either <path> or "-".
|
||||
// If the input does not have proper format, abort.
|
||||
|
||||
let output = if let Some(outfile) = outfile {
|
||||
if outfile == "-" {
|
||||
FileOrStd::StdOut
|
||||
} else {
|
||||
FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?)
|
||||
}
|
||||
} else if in_std {
|
||||
FileOrStd::StdOut
|
||||
} else {
|
||||
// Strip out extension and remove input
|
||||
let outfile = if let Some((outfile, ext)) = infile.rsplit_once('.')
|
||||
&& ext == format.ext()
|
||||
{
|
||||
Utf8CString::from(outfile)
|
||||
} else {
|
||||
return log_err!("Input file is not a supported type!");
|
||||
};
|
||||
|
||||
rm_in = true;
|
||||
eprintln!("Decompressing to [{outfile}]");
|
||||
FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?)
|
||||
};
|
||||
|
||||
let mut decoder = get_decoder(format, Cursor::new(buf).chain(input.as_file()));
|
||||
std::io::copy(decoder.as_mut(), &mut output.as_file())?;
|
||||
|
||||
if rm_in {
|
||||
infile.remove()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compress_cmd(
|
||||
method: FileFormat,
|
||||
infile: &Utf8CStr,
|
||||
outfile: Option<&Utf8CStr>,
|
||||
) -> LoggedResult<()> {
|
||||
let in_std = infile == "-";
|
||||
let mut rm_in = false;
|
||||
|
||||
let input = if in_std {
|
||||
FileOrStd::StdIn
|
||||
} else {
|
||||
FileOrStd::File(infile.open(O_RDONLY)?)
|
||||
};
|
||||
|
||||
let output = if let Some(outfile) = outfile {
|
||||
if outfile == "-" {
|
||||
FileOrStd::StdOut
|
||||
} else {
|
||||
FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?)
|
||||
}
|
||||
} else if in_std {
|
||||
FileOrStd::StdOut
|
||||
} else {
|
||||
let mut outfile = Utf8CString::default();
|
||||
outfile.write_str(infile).ok();
|
||||
outfile.write_char('.').ok();
|
||||
outfile.write_str(method.ext()).ok();
|
||||
eprintln!("Compressing to [{outfile}]");
|
||||
rm_in = true;
|
||||
let outfile = outfile.create(O_WRONLY | O_TRUNC, 0o644)?;
|
||||
FileOrStd::File(outfile)
|
||||
};
|
||||
|
||||
let mut encoder = get_encoder(method, output.as_file());
|
||||
std::io::copy(&mut input.as_file(), encoder.as_mut())?;
|
||||
encoder.finish()?;
|
||||
|
||||
if rm_in {
|
||||
infile.remove()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::{Cursor, Read, Write};
|
||||
use std::mem::size_of;
|
||||
use std::process::exit;
|
||||
use std::str;
|
||||
@@ -17,11 +17,11 @@ use size::{Base, Size, Style};
|
||||
use base::libc::{
|
||||
O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT,
|
||||
S_IFREG, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
||||
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t,
|
||||
dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t,
|
||||
};
|
||||
use base::{
|
||||
BytesExt, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||
cstr, log_err, map_args,
|
||||
BytesExt, EarlyExitExt, LoggedResult, MappedFile, OptionExt, ResultExt, Utf8CStr, Utf8CStrBuf,
|
||||
WriteExt, cstr, log_err,
|
||||
};
|
||||
|
||||
use crate::check_env;
|
||||
@@ -29,14 +29,6 @@ use crate::compress::{get_decoder, get_encoder};
|
||||
use crate::ffi::FileFormat;
|
||||
use crate::patch::{patch_encryption, patch_verity};
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct CpioCli {
|
||||
#[argh(positional)]
|
||||
file: String,
|
||||
#[argh(positional)]
|
||||
commands: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct CpioCommand {
|
||||
#[argh(subcommand)]
|
||||
@@ -151,7 +143,7 @@ struct List {
|
||||
recursive: bool,
|
||||
}
|
||||
|
||||
fn print_cpio_usage() {
|
||||
pub(crate) fn print_cpio_usage() {
|
||||
eprintln!(
|
||||
r#"Usage: magiskboot cpio <incpio> [commands...]
|
||||
|
||||
@@ -235,7 +227,7 @@ impl Cpio {
|
||||
let hdr_sz = size_of::<CpioHeader>();
|
||||
let hdr = from_bytes::<CpioHeader>(&data[pos..(pos + hdr_sz)]);
|
||||
if &hdr.magic != b"070701" {
|
||||
return Err(log_err!("invalid cpio magic"));
|
||||
return log_err!("invalid cpio magic");
|
||||
}
|
||||
pos += hdr_sz;
|
||||
let name_sz = x8u(&hdr.namesize)? as usize;
|
||||
@@ -339,7 +331,7 @@ impl Cpio {
|
||||
let entry = self
|
||||
.entries
|
||||
.get(path)
|
||||
.ok_or_else(|| log_err!("No such file"))?;
|
||||
.ok_or_log_msg(|w| w.write_str("No such file"))?;
|
||||
eprintln!("Extracting entry [{path}] to [{out}]");
|
||||
|
||||
let out = Utf8CStr::from_string(out);
|
||||
@@ -370,7 +362,7 @@ impl Cpio {
|
||||
unsafe { mknod(out.as_ptr().cast(), entry.mode, dev) };
|
||||
}
|
||||
_ => {
|
||||
return Err(log_err!("unknown entry type"));
|
||||
return log_err!("unknown entry type");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -397,7 +389,7 @@ impl Cpio {
|
||||
|
||||
fn add(&mut self, mode: mode_t, path: &str, file: &mut String) -> LoggedResult<()> {
|
||||
if path.ends_with('/') {
|
||||
return Err(log_err!("path cannot end with / for add"));
|
||||
return log_err!("path cannot end with / for add");
|
||||
}
|
||||
let file = Utf8CStr::from_string(file);
|
||||
let attr = file.get_attr()?;
|
||||
@@ -420,7 +412,7 @@ impl Cpio {
|
||||
} else if attr.is_char_device() {
|
||||
mode | S_IFCHR
|
||||
} else {
|
||||
return Err(log_err!("unsupported file type"));
|
||||
return log_err!("unsupported file type");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -473,7 +465,7 @@ impl Cpio {
|
||||
let entry = self
|
||||
.entries
|
||||
.remove(&norm_path(from))
|
||||
.ok_or_else(|| log_err!("no such entry {}", from))?;
|
||||
.ok_or_log_msg(|w| w.write_fmt(format_args!("No such entry {from}")))?;
|
||||
self.entries.insert(norm_path(to), entry);
|
||||
eprintln!("Move [{from}] -> [{to}]");
|
||||
Ok(())
|
||||
@@ -568,7 +560,7 @@ impl Cpio {
|
||||
let mut backups = HashMap::<String, Box<CpioEntry>>::new();
|
||||
let mut rm_list = String::new();
|
||||
self.entries
|
||||
.extract_if(|name, _| name.starts_with(".backup/"))
|
||||
.extract_if(.., |name, _| name.starts_with(".backup/"))
|
||||
.for_each(|(name, mut entry)| {
|
||||
if name == ".backup/.rmlist" {
|
||||
if let Ok(data) = str::from_utf8(&entry.data) {
|
||||
@@ -713,10 +705,11 @@ impl CpioEntry {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut decoder = get_decoder(FileFormat::XZ, Vec::new());
|
||||
let Ok(data): std::io::Result<Vec<u8>> = (try {
|
||||
decoder.write_all(&self.data)?;
|
||||
decoder.finish()?
|
||||
let mut decoder = get_decoder(FileFormat::XZ, Cursor::new(&self.data));
|
||||
let mut data = Vec::new();
|
||||
std::io::copy(decoder.as_mut(), &mut data)?;
|
||||
data
|
||||
}) else {
|
||||
eprintln!("xz compression failed");
|
||||
return false;
|
||||
@@ -761,74 +754,61 @@ impl Display for CpioEntry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
if argc < 1 {
|
||||
Err(log_err!("No arguments"))?;
|
||||
}
|
||||
|
||||
let cmds = map_args(argc, argv)?;
|
||||
|
||||
let mut cli =
|
||||
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
|
||||
|
||||
let file = Utf8CStr::from_string(&mut cli.file);
|
||||
let mut cpio = if file.exists() {
|
||||
Cpio::load_from_file(file)?
|
||||
} else {
|
||||
Cpio::new()
|
||||
};
|
||||
|
||||
for cmd in cli.commands {
|
||||
if cmd.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
let mut cli = CpioCommand::from_args(
|
||||
&["magiskboot", "cpio", file],
|
||||
cmd.split(' ')
|
||||
.filter(|x| !x.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.on_early_exit(print_cpio_usage);
|
||||
|
||||
match &mut cli.action {
|
||||
CpioAction::Test(_) => exit(cpio.test()),
|
||||
CpioAction::Restore(_) => cpio.restore()?,
|
||||
CpioAction::Patch(_) => cpio.patch(),
|
||||
CpioAction::Exists(Exists { path }) => {
|
||||
if cpio.exists(path) {
|
||||
exit(0);
|
||||
} else {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
CpioAction::Backup(Backup {
|
||||
origin,
|
||||
skip_compress,
|
||||
}) => cpio.backup(origin, *skip_compress)?,
|
||||
CpioAction::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive),
|
||||
CpioAction::Move(Move { from, to }) => cpio.mv(from, to)?,
|
||||
CpioAction::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(*mode, dir),
|
||||
CpioAction::Link(Link { src, dst }) => cpio.ln(src, dst),
|
||||
CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
|
||||
CpioAction::Extract(Extract { paths }) => {
|
||||
if !paths.is_empty() && paths.len() != 2 {
|
||||
Err(log_err!("invalid arguments"))?;
|
||||
}
|
||||
let mut it = paths.iter_mut();
|
||||
cpio.extract(it.next(), it.next())?;
|
||||
}
|
||||
CpioAction::List(List { path, recursive }) => {
|
||||
cpio.ls(path.as_str(), *recursive);
|
||||
exit(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
cpio.dump(file)?;
|
||||
pub(crate) fn cpio_commands(file: &Utf8CStr, cmds: &Vec<String>) -> LoggedResult<()> {
|
||||
let mut cpio = if file.exists() {
|
||||
Cpio::load_from_file(file)?
|
||||
} else {
|
||||
Cpio::new()
|
||||
};
|
||||
res.log_with_msg(|w| w.write_str("Failed to process cpio"))
|
||||
.is_ok()
|
||||
|
||||
for cmd in cmds {
|
||||
if cmd.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
let mut cmd = CpioCommand::from_args(
|
||||
&["magiskboot", "cpio", file],
|
||||
cmd.split(' ')
|
||||
.filter(|x| !x.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.on_early_exit(print_cpio_usage);
|
||||
|
||||
match &mut cmd.action {
|
||||
CpioAction::Test(_) => exit(cpio.test()),
|
||||
CpioAction::Restore(_) => cpio.restore()?,
|
||||
CpioAction::Patch(_) => cpio.patch(),
|
||||
CpioAction::Exists(Exists { path }) => {
|
||||
return if cpio.exists(path) {
|
||||
Ok(())
|
||||
} else {
|
||||
log_err!()
|
||||
};
|
||||
}
|
||||
CpioAction::Backup(Backup {
|
||||
origin,
|
||||
skip_compress,
|
||||
}) => cpio.backup(origin, *skip_compress)?,
|
||||
CpioAction::Remove(Remove { path, recursive }) => cpio.rm(path, *recursive),
|
||||
CpioAction::Move(Move { from, to }) => cpio.mv(from, to)?,
|
||||
CpioAction::MakeDir(MakeDir { mode, dir }) => cpio.mkdir(*mode, dir),
|
||||
CpioAction::Link(Link { src, dst }) => cpio.ln(src, dst),
|
||||
CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
|
||||
CpioAction::Extract(Extract { paths }) => {
|
||||
if !paths.is_empty() && paths.len() != 2 {
|
||||
log_err!("invalid arguments")?;
|
||||
}
|
||||
let mut it = paths.iter_mut();
|
||||
cpio.extract(it.next(), it.next())?;
|
||||
}
|
||||
CpioAction::List(List { path, recursive }) => {
|
||||
cpio.ls(path.as_str(), *recursive);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
}
|
||||
cpio.dump(file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn x8u(x: &[u8; 8]) -> LoggedResult<u32> {
|
||||
@@ -836,7 +816,9 @@ fn x8u(x: &[u8; 8]) -> LoggedResult<u32> {
|
||||
let mut ret = 0u32;
|
||||
let s = str::from_utf8(x).log_with_msg(|w| w.write_str("bad cpio header"))?;
|
||||
for c in s.chars() {
|
||||
ret = ret * 16 + c.to_digit(16).ok_or_else(|| log_err!("bad cpio header"))?;
|
||||
ret = ret * 16
|
||||
+ c.to_digit(16)
|
||||
.ok_or_log_msg(|w| w.write_str("bad cpio header"))?;
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{cell::UnsafeCell, process::exit};
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
use argh::FromArgs;
|
||||
use fdt::{
|
||||
@@ -6,23 +6,13 @@ use fdt::{
|
||||
node::{FdtNode, NodeProperty},
|
||||
};
|
||||
|
||||
use base::{
|
||||
EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, libc::c_char, log_err, map_args,
|
||||
};
|
||||
use base::{LoggedResult, MappedFile, Utf8CStr};
|
||||
|
||||
use crate::{check_env, patch::patch_verity};
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct DtbCli {
|
||||
#[argh(positional)]
|
||||
file: String,
|
||||
#[argh(subcommand)]
|
||||
action: DtbAction,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand)]
|
||||
enum DtbAction {
|
||||
pub(crate) enum DtbAction {
|
||||
Print(Print),
|
||||
Patch(Patch),
|
||||
Test(Test),
|
||||
@@ -30,20 +20,20 @@ enum DtbAction {
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "print")]
|
||||
struct Print {
|
||||
pub(crate) struct Print {
|
||||
#[argh(switch, short = 'f')]
|
||||
fstab: bool,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "patch")]
|
||||
struct Patch {}
|
||||
pub(crate) struct Patch {}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[argh(subcommand, name = "test")]
|
||||
struct Test {}
|
||||
pub(crate) struct Test {}
|
||||
|
||||
fn print_dtb_usage() {
|
||||
pub(crate) fn print_dtb_usage() {
|
||||
eprintln!(
|
||||
r#"Usage: magiskboot dtb <file> <action> [args...]
|
||||
Do dtb related actions to <file>.
|
||||
@@ -220,11 +210,11 @@ fn dtb_test(file: &Utf8CStr) -> LoggedResult<bool> {
|
||||
if child.name != "system" {
|
||||
continue;
|
||||
}
|
||||
if let Some(mount_point) = child.property("mnt_point") {
|
||||
if mount_point.value == b"/system_root\0" {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
if let Some(mount_point) = child.property("mnt_point")
|
||||
&& mount_point.value == b"/system_root\0"
|
||||
{
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,34 +264,13 @@ fn dtb_patch(file: &Utf8CStr) -> LoggedResult<bool> {
|
||||
Ok(patched)
|
||||
}
|
||||
|
||||
pub fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
if argc < 1 {
|
||||
Err(log_err!("No arguments"))?;
|
||||
pub(crate) fn dtb_commands(file: &Utf8CStr, action: &DtbAction) -> LoggedResult<bool> {
|
||||
match action {
|
||||
DtbAction::Print(Print { fstab }) => {
|
||||
dtb_print(file, *fstab)?;
|
||||
Ok(true)
|
||||
}
|
||||
let cmds = map_args(argc, argv)?;
|
||||
|
||||
let mut cli =
|
||||
DtbCli::from_args(&["magiskboot", "dtb"], &cmds).on_early_exit(print_dtb_usage);
|
||||
|
||||
let file = Utf8CStr::from_string(&mut cli.file);
|
||||
|
||||
match cli.action {
|
||||
DtbAction::Print(Print { fstab }) => {
|
||||
dtb_print(file, fstab)?;
|
||||
}
|
||||
DtbAction::Test(_) => {
|
||||
if !dtb_test(file)? {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
DtbAction::Patch(_) => {
|
||||
if !dtb_patch(file)? {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
res.log_with_msg(|w| w.write_str("Failed to process dtb"))
|
||||
.is_ok()
|
||||
DtbAction::Test(_) => Ok(dtb_test(file)?),
|
||||
DtbAction::Patch(_) => Ok(dtb_patch(file)?),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#include "boot-rs.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
Name2Fmt name2fmt;
|
||||
Fmt2Name fmt2name;
|
||||
Fmt2Ext fmt2ext;
|
||||
|
||||
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
|
||||
|
||||
FileFormat check_fmt(const void *buf, size_t len) {
|
||||
if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
|
||||
return FileFormat::CHROMEOS;
|
||||
} else if (CHECKED_MATCH(BOOT_MAGIC)) {
|
||||
return FileFormat::AOSP;
|
||||
} else if (CHECKED_MATCH(VENDOR_BOOT_MAGIC)) {
|
||||
return FileFormat::AOSP_VENDOR;
|
||||
} else if (CHECKED_MATCH(GZIP1_MAGIC) || CHECKED_MATCH(GZIP2_MAGIC)) {
|
||||
return FileFormat::GZIP;
|
||||
} else if (CHECKED_MATCH(LZOP_MAGIC)) {
|
||||
return FileFormat::LZOP;
|
||||
} else if (CHECKED_MATCH(XZ_MAGIC)) {
|
||||
return FileFormat::XZ;
|
||||
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
|
||||
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
|
||||
return FileFormat::LZMA;
|
||||
} else if (CHECKED_MATCH(BZIP_MAGIC)) {
|
||||
return FileFormat::BZIP2;
|
||||
} else if (CHECKED_MATCH(LZ41_MAGIC) || CHECKED_MATCH(LZ42_MAGIC)) {
|
||||
return FileFormat::LZ4;
|
||||
} else if (CHECKED_MATCH(LZ4_LEG_MAGIC)) {
|
||||
return FileFormat::LZ4_LEGACY;
|
||||
} else if (CHECKED_MATCH(MTK_MAGIC)) {
|
||||
return FileFormat::MTK;
|
||||
} else if (CHECKED_MATCH(DTB_MAGIC)) {
|
||||
return FileFormat::DTB;
|
||||
} else if (CHECKED_MATCH(DHTB_MAGIC)) {
|
||||
return FileFormat::DHTB;
|
||||
} else if (CHECKED_MATCH(TEGRABLOB_MAGIC)) {
|
||||
return FileFormat::BLOB;
|
||||
} else if (len >= 0x28 && memcmp(&((char *)buf)[0x24], ZIMAGE_MAGIC, 4) == 0) {
|
||||
return FileFormat::ZIMAGE;
|
||||
} else {
|
||||
return FileFormat::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
const char *Fmt2Name::operator[](FileFormat fmt) {
|
||||
switch (fmt) {
|
||||
case FileFormat::GZIP:
|
||||
return "gzip";
|
||||
case FileFormat::ZOPFLI:
|
||||
return "zopfli";
|
||||
case FileFormat::LZOP:
|
||||
return "lzop";
|
||||
case FileFormat::XZ:
|
||||
return "xz";
|
||||
case FileFormat::LZMA:
|
||||
return "lzma";
|
||||
case FileFormat::BZIP2:
|
||||
return "bzip2";
|
||||
case FileFormat::LZ4:
|
||||
return "lz4";
|
||||
case FileFormat::LZ4_LEGACY:
|
||||
return "lz4_legacy";
|
||||
case FileFormat::LZ4_LG:
|
||||
return "lz4_lg";
|
||||
case FileFormat::DTB:
|
||||
return "dtb";
|
||||
case FileFormat::ZIMAGE:
|
||||
return "zimage";
|
||||
default:
|
||||
return "raw";
|
||||
}
|
||||
}
|
||||
|
||||
const char *Fmt2Ext::operator[](FileFormat fmt) {
|
||||
switch (fmt) {
|
||||
case FileFormat::GZIP:
|
||||
case FileFormat::ZOPFLI:
|
||||
return ".gz";
|
||||
case FileFormat::LZOP:
|
||||
return ".lzo";
|
||||
case FileFormat::XZ:
|
||||
return ".xz";
|
||||
case FileFormat::LZMA:
|
||||
return ".lzma";
|
||||
case FileFormat::BZIP2:
|
||||
return ".bz2";
|
||||
case FileFormat::LZ4:
|
||||
case FileFormat::LZ4_LEGACY:
|
||||
case FileFormat::LZ4_LG:
|
||||
return ".lz4";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK(s, f) else if (name == s) return f;
|
||||
|
||||
FileFormat Name2Fmt::operator[](std::string_view name) {
|
||||
if (0) {}
|
||||
CHECK("gzip", FileFormat::GZIP)
|
||||
CHECK("zopfli", FileFormat::ZOPFLI)
|
||||
CHECK("xz", FileFormat::XZ)
|
||||
CHECK("lzma", FileFormat::LZMA)
|
||||
CHECK("bzip2", FileFormat::BZIP2)
|
||||
CHECK("lz4", FileFormat::LZ4)
|
||||
CHECK("lz4_legacy", FileFormat::LZ4_LEGACY)
|
||||
CHECK("lz4_lg", FileFormat::LZ4_LG)
|
||||
else return FileFormat::UNKNOWN;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
enum class FileFormat : ::std::uint8_t;
|
||||
|
||||
#define COMPRESSED(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) < +FileFormat::LZOP)
|
||||
#define COMPRESSED_ANY(fmt) ((+fmt) >= +FileFormat::GZIP && (+fmt) <= +FileFormat::LZOP)
|
||||
|
||||
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
|
||||
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
|
||||
|
||||
#define BOOT_MAGIC "ANDROID!"
|
||||
#define VENDOR_BOOT_MAGIC "VNDRBOOT"
|
||||
#define CHROMEOS_MAGIC "CHROMEOS"
|
||||
#define GZIP1_MAGIC "\x1f\x8b"
|
||||
#define GZIP2_MAGIC "\x1f\x9e"
|
||||
#define LZOP_MAGIC "\x89""LZO"
|
||||
#define XZ_MAGIC "\xfd""7zXZ"
|
||||
#define BZIP_MAGIC "BZh"
|
||||
#define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
|
||||
#define LZ41_MAGIC "\x03\x21\x4c\x18"
|
||||
#define LZ42_MAGIC "\x04\x22\x4d\x18"
|
||||
#define MTK_MAGIC "\x88\x16\x88\x58"
|
||||
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
|
||||
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
|
||||
#define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00"
|
||||
#define SEANDROID_MAGIC "SEANDROIDENFORCE"
|
||||
#define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-"
|
||||
#define NOOKHD_RL_MAGIC "Red Loader"
|
||||
#define NOOKHD_GL_MAGIC "Green Loader"
|
||||
#define NOOKHD_GR_MAGIC "Green Recovery"
|
||||
#define NOOKHD_EB_MAGIC "eMMC boot.img+secondloader"
|
||||
#define NOOKHD_ER_MAGIC "eMMC recovery.img+secondloader"
|
||||
#define NOOKHD_PRE_HEADER_SZ 1048576
|
||||
#define ACCLAIM_MAGIC "BauwksBoot"
|
||||
#define ACCLAIM_PRE_HEADER_SZ 262144
|
||||
#define AMONET_MICROLOADER_MAGIC "microloader"
|
||||
#define AMONET_MICROLOADER_SZ 1024
|
||||
#define AVB_FOOTER_MAGIC "AVBf"
|
||||
#define AVB_MAGIC "AVB0"
|
||||
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
|
||||
|
||||
class Fmt2Name {
|
||||
public:
|
||||
const char *operator[](FileFormat fmt);
|
||||
};
|
||||
|
||||
class Fmt2Ext {
|
||||
public:
|
||||
const char *operator[](FileFormat fmt);
|
||||
};
|
||||
|
||||
class Name2Fmt {
|
||||
public:
|
||||
FileFormat operator[](std::string_view name);
|
||||
};
|
||||
|
||||
FileFormat check_fmt(const void *buf, size_t len);
|
||||
|
||||
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
|
||||
return check_fmt(bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
extern Name2Fmt name2fmt;
|
||||
extern Fmt2Name fmt2name;
|
||||
extern Fmt2Ext fmt2ext;
|
||||
104
native/src/boot/format.rs
Normal file
104
native/src/boot/format.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use crate::ffi::FileFormat;
|
||||
use base::{Utf8CStr, cstr, libc};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
impl FromStr for FileFormat {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"gzip" => Ok(Self::GZIP),
|
||||
"zopfli" => Ok(Self::ZOPFLI),
|
||||
"xz" => Ok(Self::XZ),
|
||||
"lzma" => Ok(Self::LZMA),
|
||||
"bzip2" => Ok(Self::BZIP2),
|
||||
"lz4" => Ok(Self::LZ4),
|
||||
"lz4_legacy" => Ok(Self::LZ4_LEGACY),
|
||||
"lz4_lg" => Ok(Self::LZ4_LG),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FileFormat {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.as_cstr())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileFormat {
|
||||
fn as_cstr(&self) -> &'static Utf8CStr {
|
||||
match *self {
|
||||
Self::GZIP => cstr!("gzip"),
|
||||
Self::ZOPFLI => cstr!("zopfli"),
|
||||
Self::LZOP => cstr!("lzop"),
|
||||
Self::XZ => cstr!("xz"),
|
||||
Self::LZMA => cstr!("lzma"),
|
||||
Self::BZIP2 => cstr!("bzip2"),
|
||||
Self::LZ4 => cstr!("lz4"),
|
||||
Self::LZ4_LEGACY => cstr!("lz4_legacy"),
|
||||
Self::LZ4_LG => cstr!("lz4_lg"),
|
||||
Self::DTB => cstr!("dtb"),
|
||||
Self::ZIMAGE => cstr!("zimage"),
|
||||
_ => cstr!("raw"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileFormat {
|
||||
pub fn ext(&self) -> &'static str {
|
||||
match *self {
|
||||
Self::GZIP | Self::ZOPFLI => "gz",
|
||||
Self::LZOP => "lzo",
|
||||
Self::XZ => "xz",
|
||||
Self::LZMA => "lzma",
|
||||
Self::BZIP2 => "bz2",
|
||||
Self::LZ4 | Self::LZ4_LEGACY | Self::LZ4_LG => "lz4",
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_compressed(&self) -> bool {
|
||||
matches!(
|
||||
*self,
|
||||
Self::GZIP
|
||||
| Self::ZOPFLI
|
||||
| Self::XZ
|
||||
| Self::LZMA
|
||||
| Self::BZIP2
|
||||
| Self::LZ4
|
||||
| Self::LZ4_LEGACY
|
||||
| Self::LZ4_LG
|
||||
)
|
||||
}
|
||||
|
||||
pub fn formats() -> String {
|
||||
[
|
||||
Self::GZIP,
|
||||
Self::ZOPFLI,
|
||||
Self::XZ,
|
||||
Self::LZMA,
|
||||
Self::BZIP2,
|
||||
Self::LZ4,
|
||||
Self::LZ4_LEGACY,
|
||||
Self::LZ4_LG,
|
||||
]
|
||||
.map(|f| f.to_string())
|
||||
.join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
// C++ FFI
|
||||
|
||||
pub fn fmt2name(fmt: FileFormat) -> *const libc::c_char {
|
||||
fmt.as_cstr().as_ptr()
|
||||
}
|
||||
|
||||
pub fn fmt_compressed(fmt: FileFormat) -> bool {
|
||||
fmt.is_compressed()
|
||||
}
|
||||
|
||||
pub fn fmt_compressed_any(fmt: FileFormat) -> bool {
|
||||
fmt.is_compressed() || matches!(fmt, FileFormat::LZOP)
|
||||
}
|
||||
@@ -4,17 +4,16 @@
|
||||
#![feature(try_blocks)]
|
||||
|
||||
pub use base;
|
||||
use compress::{compress_bytes, compress_fd, decompress_bytes, decompress_bytes_fd};
|
||||
use cpio::cpio_commands;
|
||||
use dtb::dtb_commands;
|
||||
use patch::hexpatch;
|
||||
use payload::extract_boot_from_payload;
|
||||
use sign::{SHA, get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image};
|
||||
use compress::{compress_bytes, decompress_bytes};
|
||||
use format::{fmt_compressed, fmt_compressed_any, fmt2name};
|
||||
use sign::{SHA, get_sha, sha256_hash, sign_payload_for_cxx};
|
||||
use std::env;
|
||||
|
||||
mod cli;
|
||||
mod compress;
|
||||
mod cpio;
|
||||
mod dtb;
|
||||
mod format;
|
||||
mod patch;
|
||||
mod payload;
|
||||
// Suppress warnings in generated code
|
||||
@@ -50,24 +49,16 @@ pub mod ffi {
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("../base/include/base.hpp");
|
||||
include!("magiskboot.hpp");
|
||||
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("format.hpp");
|
||||
fn cleanup();
|
||||
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
|
||||
fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool);
|
||||
fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32;
|
||||
fn check_fmt(buf: &[u8]) -> FileFormat;
|
||||
|
||||
include!("bootimg.hpp");
|
||||
#[cxx_name = "boot_img"]
|
||||
type BootImage;
|
||||
#[cxx_name = "get_payload"]
|
||||
fn payload(self: &BootImage) -> &[u8];
|
||||
#[cxx_name = "get_tail"]
|
||||
fn tail(self: &BootImage) -> &[u8];
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
@@ -76,33 +67,38 @@ pub mod ffi {
|
||||
fn update(self: &mut SHA, data: &[u8]);
|
||||
fn finalize_into(self: &mut SHA, out: &mut [u8]);
|
||||
fn output_size(self: &SHA) -> usize;
|
||||
fn sha1_hash(data: &[u8], out: &mut [u8]);
|
||||
fn sha256_hash(data: &[u8], out: &mut [u8]);
|
||||
|
||||
fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool;
|
||||
fn compress_fd(format: FileFormat, in_fd: i32, out_fd: i32);
|
||||
fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
||||
fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: i32);
|
||||
fn decompress_bytes_fd(format: FileFormat, in_bytes: &[u8], in_fd: i32, out_fd: i32);
|
||||
fn fmt2name(fmt: FileFormat) -> *const c_char;
|
||||
fn fmt_compressed(fmt: FileFormat) -> bool;
|
||||
fn fmt_compressed_any(fmt: FileFormat) -> bool;
|
||||
|
||||
#[cxx_name = "sign_payload"]
|
||||
fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8>;
|
||||
}
|
||||
|
||||
#[namespace = "rust"]
|
||||
#[allow(unused_unsafe)]
|
||||
// BootImage FFI
|
||||
unsafe extern "C++" {
|
||||
include!("bootimg.hpp");
|
||||
#[cxx_name = "boot_img"]
|
||||
type BootImage;
|
||||
|
||||
#[cxx_name = "get_payload"]
|
||||
fn payload(self: &BootImage) -> &[u8];
|
||||
#[cxx_name = "get_tail"]
|
||||
fn tail(self: &BootImage) -> &[u8];
|
||||
fn is_signed(self: &BootImage) -> bool;
|
||||
fn tail_off(self: &BootImage) -> u64;
|
||||
|
||||
#[Self = BootImage]
|
||||
#[cxx_name = "create"]
|
||||
fn new(img: Utf8CStrRef) -> UniquePtr<BootImage>;
|
||||
}
|
||||
extern "Rust" {
|
||||
fn extract_boot_from_payload(
|
||||
partition: Utf8CStrRef,
|
||||
in_path: Utf8CStrRef,
|
||||
out_path: Utf8CStrRef,
|
||||
) -> bool;
|
||||
unsafe fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool;
|
||||
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
|
||||
unsafe fn sign_boot_image(
|
||||
payload: &[u8],
|
||||
name: *const c_char,
|
||||
cert: *const c_char,
|
||||
key: *const c_char,
|
||||
) -> Vec<u8>;
|
||||
unsafe fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool;
|
||||
#[cxx_name = "verify"]
|
||||
fn verify_for_cxx(self: &BootImage) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
#define HEADER_FILE "header"
|
||||
#define KERNEL_FILE "kernel"
|
||||
#define RAMDISK_FILE "ramdisk.cpio"
|
||||
@@ -12,8 +14,49 @@
|
||||
#define BOOTCONFIG_FILE "bootconfig"
|
||||
#define NEW_BOOT "new-boot.img"
|
||||
|
||||
int unpack(const char *image, bool skip_decomp = false, bool hdr = false);
|
||||
void repack(const char *src_img, const char *out_img, bool skip_comp = false);
|
||||
int verify(const char *image, const char *cert);
|
||||
int sign(const char *image, const char *name, const char *cert, const char *key);
|
||||
int split_image_dtb(const char *filename, bool skip_decomp = false);
|
||||
#define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
|
||||
#define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
|
||||
#define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
|
||||
|
||||
#define BOOT_MAGIC "ANDROID!"
|
||||
#define VENDOR_BOOT_MAGIC "VNDRBOOT"
|
||||
#define CHROMEOS_MAGIC "CHROMEOS"
|
||||
#define GZIP1_MAGIC "\x1f\x8b"
|
||||
#define GZIP2_MAGIC "\x1f\x9e"
|
||||
#define LZOP_MAGIC "\x89""LZO"
|
||||
#define XZ_MAGIC "\xfd""7zXZ"
|
||||
#define BZIP_MAGIC "BZh"
|
||||
#define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
|
||||
#define LZ41_MAGIC "\x03\x21\x4c\x18"
|
||||
#define LZ42_MAGIC "\x04\x22\x4d\x18"
|
||||
#define MTK_MAGIC "\x88\x16\x88\x58"
|
||||
#define DTB_MAGIC "\xd0\x0d\xfe\xed"
|
||||
#define LG_BUMP_MAGIC "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79"
|
||||
#define DHTB_MAGIC "\x44\x48\x54\x42\x01\x00\x00\x00"
|
||||
#define SEANDROID_MAGIC "SEANDROIDENFORCE"
|
||||
#define TEGRABLOB_MAGIC "-SIGNED-BY-SIGNBLOB-"
|
||||
#define NOOKHD_RL_MAGIC "Red Loader"
|
||||
#define NOOKHD_GL_MAGIC "Green Loader"
|
||||
#define NOOKHD_GR_MAGIC "Green Recovery"
|
||||
#define NOOKHD_EB_MAGIC "eMMC boot.img+secondloader"
|
||||
#define NOOKHD_ER_MAGIC "eMMC recovery.img+secondloader"
|
||||
#define NOOKHD_PRE_HEADER_SZ 1048576
|
||||
#define ACCLAIM_MAGIC "BauwksBoot"
|
||||
#define ACCLAIM_PRE_HEADER_SZ 262144
|
||||
#define AMONET_MICROLOADER_MAGIC "microloader"
|
||||
#define AMONET_MICROLOADER_SZ 1024
|
||||
#define AVB_FOOTER_MAGIC "AVBf"
|
||||
#define AVB_MAGIC "AVB0"
|
||||
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
|
||||
|
||||
enum class FileFormat : uint8_t;
|
||||
|
||||
int unpack(Utf8CStr image, bool skip_decomp = false, bool hdr = false);
|
||||
void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp = false);
|
||||
int split_image_dtb(Utf8CStr filename, bool skip_decomp = false);
|
||||
void cleanup();
|
||||
FileFormat check_fmt(const void *buf, size_t len);
|
||||
|
||||
static inline FileFormat check_fmt(rust::Slice<const uint8_t> bytes) {
|
||||
return check_fmt(bytes.data(), bytes.size());
|
||||
}
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
#include <base.hpp>
|
||||
|
||||
#include "boot-rs.hpp"
|
||||
#include "magiskboot.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void print_formats() {
|
||||
for (int fmt = +FileFormat::GZIP; fmt < +FileFormat::LZOP; ++fmt) {
|
||||
fprintf(stderr, "%s ", fmt2name[(FileFormat) fmt]);
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(char *arg0) {
|
||||
fprintf(stderr,
|
||||
R"EOF(MagiskBoot - Boot Image Modification Tool
|
||||
|
||||
Usage: %s <action> [args...]
|
||||
|
||||
Supported actions:
|
||||
unpack [-n] [-h] <bootimg>
|
||||
Unpack <bootimg> to its individual components, each component to
|
||||
a file with its corresponding file name in the current directory.
|
||||
Supported components: kernel, kernel_dtb, ramdisk.cpio, second,
|
||||
dtb, extra, and recovery_dtbo.
|
||||
By default, each component will be decompressed on-the-fly.
|
||||
If '-n' is provided, all decompression operations will be skipped;
|
||||
each component will remain untouched, dumped in its original format.
|
||||
If '-h' is provided, the boot image header information will be
|
||||
dumped to the file 'header', which can be used to modify header
|
||||
configurations during repacking.
|
||||
Return values:
|
||||
0:valid 1:error 2:chromeos
|
||||
|
||||
repack [-n] <origbootimg> [outbootimg]
|
||||
Repack boot image components using files from the current directory
|
||||
to [outbootimg], or 'new-boot.img' if not specified. Current directory
|
||||
should only contain required files for [outbootimg], or incorrect
|
||||
[outbootimg] may be produced.
|
||||
<origbootimg> is the original boot image used to unpack the components.
|
||||
By default, each component will be automatically compressed using its
|
||||
corresponding format detected in <origbootimg>. If a component file
|
||||
in the current directory is already compressed, then no addition
|
||||
compression will be performed for that specific component.
|
||||
If '-n' is provided, all compression operations will be skipped.
|
||||
If env variable PATCHVBMETAFLAG is set to true, all disable flags in
|
||||
the boot image's vbmeta header will be set.
|
||||
|
||||
verify <bootimg> [x509.pem]
|
||||
Check whether the boot image is signed with AVB 1.0 signature.
|
||||
Optionally provide a certificate to verify whether the image is
|
||||
signed by the public key certificate.
|
||||
Return value:
|
||||
0:valid 1:error
|
||||
|
||||
sign <bootimg> [name] [x509.pem pk8]
|
||||
Sign <bootimg> with AVB 1.0 signature.
|
||||
Optionally provide the name of the image (default: '/boot').
|
||||
Optionally provide the certificate/private key pair for signing.
|
||||
If the certificate/private key pair is not provided, the AOSP
|
||||
verity key bundled in the executable will be used.
|
||||
|
||||
extract <payload.bin> [partition] [outfile]
|
||||
Extract [partition] from <payload.bin> to [outfile].
|
||||
If [outfile] is not specified, then output to '[partition].img'.
|
||||
If [partition] is not specified, then attempt to extract either
|
||||
'init_boot' or 'boot'. Which partition was chosen can be determined
|
||||
by whichever 'init_boot.img' or 'boot.img' exists.
|
||||
<payload.bin> can be '-' to be STDIN.
|
||||
|
||||
hexpatch <file> <hexpattern1> <hexpattern2>
|
||||
Search <hexpattern1> in <file>, and replace it with <hexpattern2>
|
||||
|
||||
cpio <incpio> [commands...]
|
||||
Do cpio commands to <incpio> (modifications are done in-place).
|
||||
Each command is a single argument; add quotes for each command.
|
||||
See "cpio --help" for supported commands.
|
||||
|
||||
dtb <file> <action> [args...]
|
||||
Do dtb related actions to <file>.
|
||||
See "dtb --help" for supported actions.
|
||||
|
||||
split [-n] <file>
|
||||
Split image.*-dtb into kernel + kernel_dtb.
|
||||
If '-n' is provided, decompression operations will be skipped;
|
||||
the kernel will remain untouched, split in its original format.
|
||||
|
||||
sha1 <file>
|
||||
Print the SHA1 checksum for <file>
|
||||
|
||||
cleanup
|
||||
Cleanup the current working directory
|
||||
|
||||
compress[=format] <infile> [outfile]
|
||||
Compress <infile> with [format] to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [format] is not specified, then gzip will be used.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file suffixed with a matching file extension.
|
||||
Supported formats: )EOF", arg0);
|
||||
|
||||
print_formats();
|
||||
|
||||
fprintf(stderr, R"EOF(
|
||||
|
||||
decompress <infile> [outfile]
|
||||
Detect format and decompress <infile> to [outfile].
|
||||
<infile>/[outfile] can be '-' to be STDIN/STDOUT.
|
||||
If [outfile] is not specified, then <infile> will be replaced
|
||||
with another file removing its archive format file extension.
|
||||
Supported formats: )EOF");
|
||||
|
||||
print_formats();
|
||||
|
||||
fprintf(stderr, "\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void decompress(char *infile, const char *outfile) {
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
|
||||
int out_fd = -1;
|
||||
|
||||
uint8_t buf[4096];
|
||||
size_t len = read(in_fd, buf, sizeof(buf));
|
||||
FileFormat type = check_fmt(buf, len);
|
||||
|
||||
fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]);
|
||||
|
||||
if (!COMPRESSED(type))
|
||||
LOGE("Input file is not a supported compressed type!\n");
|
||||
|
||||
// If user does not provide outfile, infile has to be either
|
||||
// <path>.[ext], or '-'. Outfile will be either <path> or '-'.
|
||||
// If the input does not have proper format, abort.
|
||||
|
||||
char *ext = nullptr;
|
||||
if (outfile == nullptr) {
|
||||
outfile = infile;
|
||||
if (!in_std) {
|
||||
ext = strrchr(infile, '.');
|
||||
if (ext == nullptr || strcmp(ext, fmt2ext[type]) != 0)
|
||||
LOGE("Input file is not a supported type!\n");
|
||||
|
||||
// Strip out extension and remove input
|
||||
*ext = '\0';
|
||||
rm_in = true;
|
||||
fprintf(stderr, "Decompressing to [%s]\n", outfile);
|
||||
}
|
||||
}
|
||||
|
||||
out_fd = outfile == "-"sv ?
|
||||
STDOUT_FILENO :
|
||||
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (ext) *ext = '.';
|
||||
|
||||
decompress_bytes_fd(type, byte_view{ buf, len }, in_fd, out_fd);
|
||||
|
||||
if (in_fd != STDIN_FILENO) close(in_fd);
|
||||
if (out_fd != STDOUT_FILENO) close(out_fd);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
|
||||
static void compress(const char *method, const char *infile, const char *outfile) {
|
||||
FileFormat fmt = name2fmt[method];
|
||||
if (fmt == FileFormat::UNKNOWN)
|
||||
LOGE("Unknown compression method: [%s]\n", method);
|
||||
|
||||
bool in_std = infile == "-"sv;
|
||||
bool rm_in = false;
|
||||
|
||||
int in_fd = in_std ? STDIN_FILENO : xopen(infile, O_RDONLY);
|
||||
int out_fd = -1;
|
||||
|
||||
if (outfile == nullptr) {
|
||||
if (in_std) {
|
||||
out_fd = STDOUT_FILENO;
|
||||
} else {
|
||||
// If user does not provide outfile and infile is not
|
||||
// STDIN, output to <infile>.[ext]
|
||||
string tmp(infile);
|
||||
tmp += fmt2ext[fmt];
|
||||
out_fd = xopen(tmp.data(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
fprintf(stderr, "Compressing to [%s]\n", tmp.data());
|
||||
rm_in = true;
|
||||
}
|
||||
} else {
|
||||
out_fd = outfile == "-"sv ?
|
||||
STDOUT_FILENO :
|
||||
xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
}
|
||||
|
||||
compress_fd(fmt, in_fd, out_fd);
|
||||
|
||||
if (in_fd != STDIN_FILENO) close(in_fd);
|
||||
if (out_fd != STDOUT_FILENO) close(out_fd);
|
||||
|
||||
if (rm_in)
|
||||
unlink(infile);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
cmdline_logging();
|
||||
umask(0);
|
||||
|
||||
if (argc < 2)
|
||||
usage(argv[0]);
|
||||
|
||||
// Skip '--' for backwards compatibility
|
||||
string_view action(argv[1]);
|
||||
if (str_starts(action, "--"))
|
||||
action = argv[1] + 2;
|
||||
|
||||
if (action == "cleanup") {
|
||||
fprintf(stderr, "Cleaning up...\n");
|
||||
unlink(HEADER_FILE);
|
||||
unlink(KERNEL_FILE);
|
||||
unlink(RAMDISK_FILE);
|
||||
unlink(SECOND_FILE);
|
||||
unlink(KER_DTB_FILE);
|
||||
unlink(EXTRA_FILE);
|
||||
unlink(RECV_DTBO_FILE);
|
||||
unlink(DTB_FILE);
|
||||
unlink(BOOTCONFIG_FILE);
|
||||
rm_rf(VND_RAMDISK_DIR);
|
||||
} else if (argc > 2 && action == "sha1") {
|
||||
uint8_t sha1[20];
|
||||
{
|
||||
mmap_data m(argv[2]);
|
||||
sha1_hash(m, byte_data(sha1, sizeof(sha1)));
|
||||
}
|
||||
for (uint8_t i : sha1)
|
||||
printf("%02x", i);
|
||||
printf("\n");
|
||||
} else if (argc > 2 && action == "split") {
|
||||
if (argv[2] == "-n"sv) {
|
||||
if (argc == 3)
|
||||
usage(argv[0]);
|
||||
return split_image_dtb(argv[3], true);
|
||||
} else {
|
||||
return split_image_dtb(argv[2]);
|
||||
}
|
||||
} else if (argc > 2 && action == "unpack") {
|
||||
int idx = 2;
|
||||
bool nodecomp = false;
|
||||
bool hdr = false;
|
||||
for (;;) {
|
||||
if (idx >= argc)
|
||||
usage(argv[0]);
|
||||
if (argv[idx][0] != '-')
|
||||
break;
|
||||
for (char *flag = &argv[idx][1]; *flag; ++flag) {
|
||||
if (*flag == 'n')
|
||||
nodecomp = true;
|
||||
else if (*flag == 'h')
|
||||
hdr = true;
|
||||
else
|
||||
usage(argv[0]);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return unpack(argv[idx], nodecomp, hdr);
|
||||
} else if (argc > 2 && action == "repack") {
|
||||
if (argv[2] == "-n"sv) {
|
||||
if (argc == 3)
|
||||
usage(argv[0]);
|
||||
repack(argv[3], argv[4] ? argv[4] : NEW_BOOT, true);
|
||||
} else {
|
||||
repack(argv[2], argv[3] ? argv[3] : NEW_BOOT);
|
||||
}
|
||||
} else if (argc > 2 && action == "verify") {
|
||||
return verify(argv[2], argv[3]);
|
||||
} else if (argc > 2 && action == "sign") {
|
||||
if (argc == 5) usage(argv[0]);
|
||||
return sign(
|
||||
argv[2],
|
||||
argc > 3 ? argv[3] : "/boot",
|
||||
argc > 5 ? argv[4] : nullptr,
|
||||
argc > 5 ? argv[5] : nullptr);
|
||||
} else if (argc > 2 && action == "decompress") {
|
||||
decompress(argv[2], argv[3]);
|
||||
} else if (argc > 2 && str_starts(action, "compress")) {
|
||||
compress(action[8] == '=' ? &action[9] : "gzip", argv[2], argv[3]);
|
||||
} else if (argc > 4 && action == "hexpatch") {
|
||||
return hexpatch(byte_view(argv[2]), byte_view(argv[3]), byte_view(argv[4])) ? 0 : 1;
|
||||
} else if (argc > 2 && action == "cpio") {
|
||||
return rust::cpio_commands(argc - 2, argv + 2) ? 0 : 1;
|
||||
} else if (argc > 2 && action == "dtb") {
|
||||
return rust::dtb_commands(argc - 2, argv + 2) ? 0 : 1;
|
||||
} else if (argc > 2 && action == "extract") {
|
||||
return rust::extract_boot_from_payload(
|
||||
argv[2],
|
||||
argc > 3 ? argv[3] : "",
|
||||
argc > 4 ? argv[4] : ""
|
||||
) ? 0 : 1;
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -102,12 +102,8 @@ fn hex2byte(hex: &[u8]) -> Vec<u8> {
|
||||
v
|
||||
}
|
||||
|
||||
pub fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool {
|
||||
pub fn hexpatch(file: &Utf8CStr, from: &Utf8CStr, to: &Utf8CStr) -> bool {
|
||||
let res: LoggedResult<bool> = try {
|
||||
let file = Utf8CStr::from_bytes(file)?;
|
||||
let from = Utf8CStr::from_bytes(from)?;
|
||||
let to = Utf8CStr::from_bytes(to)?;
|
||||
|
||||
let mut map = MappedFile::open_rw(file)?;
|
||||
let pattern = hex2byte(from.as_bytes());
|
||||
let patch = hex2byte(to.as_bytes());
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
use crate::compress::get_decoder;
|
||||
use crate::ffi::check_fmt;
|
||||
use crate::proto::update_metadata::{DeltaArchiveManifest, mod_InstallOperation::Type};
|
||||
use base::{LoggedError, LoggedResult, ReadSeekExt, ResultExt, WriteExt, error};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use quick_protobuf::{BytesReader, MessageRead};
|
||||
use std::io::Cursor;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, Read, Seek, SeekFrom, Write},
|
||||
os::fd::FromRawFd,
|
||||
};
|
||||
|
||||
use crate::compress::get_decoder;
|
||||
use crate::ffi::check_fmt;
|
||||
use crate::proto::update_metadata::{DeltaArchiveManifest, mod_InstallOperation::Type};
|
||||
use base::{
|
||||
LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt, error, ffi::Utf8CStrRef,
|
||||
};
|
||||
|
||||
macro_rules! bad_payload {
|
||||
($msg:literal) => {{
|
||||
error!(concat!("Invalid payload: ", $msg));
|
||||
@@ -26,10 +24,10 @@ macro_rules! bad_payload {
|
||||
|
||||
const PAYLOAD_MAGIC: &str = "CrAU";
|
||||
|
||||
fn do_extract_boot_from_payload(
|
||||
in_path: &Utf8CStr,
|
||||
partition_name: Option<&Utf8CStr>,
|
||||
out_path: Option<&Utf8CStr>,
|
||||
pub fn extract_boot_from_payload(
|
||||
in_path: &str,
|
||||
partition_name: Option<&str>,
|
||||
out_path: Option<&str>,
|
||||
) -> LoggedResult<()> {
|
||||
let mut reader = BufReader::new(if in_path == "-" {
|
||||
unsafe { File::from_raw_fd(0) }
|
||||
@@ -166,9 +164,11 @@ fn do_extract_boot_from_payload(
|
||||
}
|
||||
Type::REPLACE_BZ | Type::REPLACE_XZ => {
|
||||
out_file.seek(SeekFrom::Start(out_offset))?;
|
||||
let mut decoder = get_decoder(check_fmt(data), &mut out_file);
|
||||
let fmt = check_fmt(data);
|
||||
|
||||
let mut decoder = get_decoder(fmt, Cursor::new(data));
|
||||
let Ok(_): std::io::Result<()> = (try {
|
||||
decoder.write_all(data)?;
|
||||
std::io::copy(decoder.as_mut(), &mut out_file)?;
|
||||
}) else {
|
||||
return Err(bad_payload!("decompression failed"));
|
||||
};
|
||||
@@ -179,25 +179,3 @@ fn do_extract_boot_from_payload(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn extract_boot_from_payload(
|
||||
in_path: Utf8CStrRef,
|
||||
partition: Utf8CStrRef,
|
||||
out_path: Utf8CStrRef,
|
||||
) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
let partition = if partition.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(partition)
|
||||
};
|
||||
let out_path = if out_path.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(out_path)
|
||||
};
|
||||
do_extract_boot_from_payload(in_path, partition, out_path)?
|
||||
};
|
||||
res.log_with_msg(|w| w.write_str("Failed to extract from payload"))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ use x509_cert::der::Any;
|
||||
use x509_cert::der::asn1::{OctetString, PrintableString};
|
||||
use x509_cert::spki::AlgorithmIdentifier;
|
||||
|
||||
use base::libc::c_char;
|
||||
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
|
||||
use base::{LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, log_err};
|
||||
|
||||
use crate::ffi::BootImage;
|
||||
|
||||
@@ -117,7 +116,7 @@ impl Verifier {
|
||||
digest = Box::<Sha512>::default();
|
||||
VerifyingKey::SHA521withECDSA(ec)
|
||||
} else {
|
||||
return Err(log_err!("Unsupported private key"));
|
||||
return log_err!("Unsupported private key");
|
||||
};
|
||||
Ok(Verifier { digest, key })
|
||||
}
|
||||
@@ -178,7 +177,7 @@ impl Signer {
|
||||
SigningKey::SHA521withECDSA(ec)
|
||||
}
|
||||
_ => {
|
||||
return Err(log_err!("Unsupported private key"));
|
||||
return log_err!("Unsupported private key");
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -249,7 +248,7 @@ struct BootSignature {
|
||||
impl BootSignature {
|
||||
fn verify(self, payload: &[u8]) -> LoggedResult<()> {
|
||||
if self.authenticated_attributes.length as usize != payload.len() {
|
||||
return Err(log_err!("Invalid image size"));
|
||||
return log_err!("Invalid image size");
|
||||
}
|
||||
let mut verifier = Verifier::from_public_key(
|
||||
self.certificate
|
||||
@@ -265,23 +264,27 @@ impl BootSignature {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
let tail = img.tail();
|
||||
impl BootImage {
|
||||
pub fn verify(&self, cert: Option<&Utf8CStr>) -> LoggedResult<()> {
|
||||
let tail = self.tail();
|
||||
if tail.starts_with(b"AVB0") {
|
||||
return log_err!();
|
||||
}
|
||||
|
||||
// Don't use BootSignature::from_der because tail might have trailing zeros
|
||||
let mut reader = SliceReader::new(tail)?;
|
||||
let mut sig = BootSignature::decode(&mut reader)?;
|
||||
match unsafe { Utf8CStr::from_ptr(cert) } {
|
||||
Ok(s) => {
|
||||
let pem = MappedFile::open(s)?;
|
||||
sig.certificate = Certificate::from_pem(pem)?;
|
||||
}
|
||||
Err(StrErr::NullPointerError) => {}
|
||||
Err(e) => Err(e)?,
|
||||
if let Some(s) = cert {
|
||||
let pem = MappedFile::open(s)?;
|
||||
sig.certificate = Certificate::from_pem(pem)?;
|
||||
};
|
||||
sig.verify(img.payload())?;
|
||||
};
|
||||
res.is_ok()
|
||||
|
||||
sig.verify(self.payload()).log()
|
||||
}
|
||||
|
||||
pub fn verify_for_cxx(&self) -> bool {
|
||||
self.verify(None).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
enum Bytes {
|
||||
@@ -303,47 +306,44 @@ const VERITY_PK8: &[u8] = include_bytes!("../../../tools/keys/verity.pk8");
|
||||
|
||||
pub fn sign_boot_image(
|
||||
payload: &[u8],
|
||||
name: *const c_char,
|
||||
cert: *const c_char,
|
||||
key: *const c_char,
|
||||
) -> Vec<u8> {
|
||||
let res: LoggedResult<Vec<u8>> = try {
|
||||
// Process arguments
|
||||
let name = unsafe { Utf8CStr::from_ptr(name) }?;
|
||||
let cert = match unsafe { Utf8CStr::from_ptr(cert) } {
|
||||
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PEM),
|
||||
Err(e) => Err(e)?,
|
||||
};
|
||||
let key = match unsafe { Utf8CStr::from_ptr(key) } {
|
||||
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PK8),
|
||||
Err(e) => Err(e)?,
|
||||
};
|
||||
|
||||
// Parse cert and private key
|
||||
let cert = Certificate::from_pem(cert)?;
|
||||
let mut signer = Signer::from_private_key(key.as_ref())?;
|
||||
|
||||
// Sign image
|
||||
let attr = AuthenticatedAttributes {
|
||||
target: PrintableString::new(name.as_bytes())?,
|
||||
length: payload.len() as u64,
|
||||
};
|
||||
signer.update(payload);
|
||||
signer.update(attr.to_der()?.as_slice());
|
||||
let sig = signer.sign()?;
|
||||
|
||||
// Create BootSignature DER
|
||||
let alg_id = cert.signature_algorithm().clone();
|
||||
let sig = BootSignature {
|
||||
format_version: 1,
|
||||
certificate: cert,
|
||||
algorithm_identifier: alg_id,
|
||||
authenticated_attributes: attr,
|
||||
signature: OctetString::new(sig)?,
|
||||
};
|
||||
sig.to_der()?
|
||||
name: &Utf8CStr,
|
||||
cert: Option<&Utf8CStr>,
|
||||
key: Option<&Utf8CStr>,
|
||||
) -> LoggedResult<Vec<u8>> {
|
||||
let cert = match cert {
|
||||
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||
None => Bytes::Slice(VERITY_PEM),
|
||||
};
|
||||
res.unwrap_or_default()
|
||||
let key = match key {
|
||||
Some(s) => Bytes::Mapped(MappedFile::open(s)?),
|
||||
None => Bytes::Slice(VERITY_PK8),
|
||||
};
|
||||
|
||||
// Parse cert and private key
|
||||
let cert = Certificate::from_pem(cert)?;
|
||||
let mut signer = Signer::from_private_key(key.as_ref())?;
|
||||
|
||||
// Sign image
|
||||
let attr = AuthenticatedAttributes {
|
||||
target: PrintableString::new(name.as_bytes())?,
|
||||
length: payload.len() as u64,
|
||||
};
|
||||
signer.update(payload);
|
||||
signer.update(attr.to_der()?.as_slice());
|
||||
let sig = signer.sign()?;
|
||||
|
||||
// Create BootSignature DER
|
||||
let alg_id = cert.signature_algorithm().clone();
|
||||
let sig = BootSignature {
|
||||
format_version: 1,
|
||||
certificate: cert,
|
||||
algorithm_identifier: alg_id,
|
||||
authenticated_attributes: attr,
|
||||
signature: OctetString::new(sig)?,
|
||||
};
|
||||
sig.to_der().log()
|
||||
}
|
||||
|
||||
pub fn sign_payload_for_cxx(payload: &[u8]) -> Vec<u8> {
|
||||
sign_boot_image(payload, cstr!("/boot"), None, None).unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -25,3 +25,4 @@ quick-protobuf = { workspace = true }
|
||||
bytemuck = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
bit-set = { workspace = true }
|
||||
argh = { workspace = true }
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 1)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#include <libgen.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -28,7 +26,7 @@ int main(int argc, char *argv[]) {
|
||||
cmdline_logging();
|
||||
init_argv0(argc, argv);
|
||||
|
||||
string_view argv0 = basename(argv[0]);
|
||||
Utf8CStr argv0 = basename(argv[0]);
|
||||
|
||||
umask(0);
|
||||
|
||||
@@ -63,6 +61,6 @@ int main(int argc, char *argv[]) {
|
||||
return app.fn(argc, argv);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%s: applet not found\n", argv0.data());
|
||||
fprintf(stderr, "%s: applet not found\n", argv0.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <linux/input.h>
|
||||
#include <map>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
@@ -160,76 +161,6 @@ void write_string(int fd, string_view str) {
|
||||
xwrite(fd, str.data(), str.size());
|
||||
}
|
||||
|
||||
static void handle_request_async(int client, int code, const sock_cred &cred) {
|
||||
auto &daemon = MagiskD::Get();
|
||||
switch (code) {
|
||||
case +RequestCode::DENYLIST:
|
||||
denylist_handler(client, &cred);
|
||||
break;
|
||||
case +RequestCode::SUPERUSER:
|
||||
daemon.su_daemon_handler(client, cred);
|
||||
break;
|
||||
case +RequestCode::ZYGOTE_RESTART: {
|
||||
LOGI("** zygote restarted\n");
|
||||
daemon.prune_su_access();
|
||||
scan_deny_apps();
|
||||
daemon.zygisk_reset(false);
|
||||
close(client);
|
||||
break;
|
||||
}
|
||||
case +RequestCode::SQLITE_CMD:
|
||||
daemon.db_exec(client);
|
||||
break;
|
||||
case +RequestCode::REMOVE_MODULES: {
|
||||
int do_reboot = read_int(client);
|
||||
remove_modules();
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
if (do_reboot) {
|
||||
daemon.reboot();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case +RequestCode::ZYGISK:
|
||||
daemon.zygisk_handler(client);
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_request_sync(int client, int code) {
|
||||
switch (code) {
|
||||
case +RequestCode::CHECK_VERSION:
|
||||
#if MAGISK_DEBUG
|
||||
write_string(client, MAGISK_VERSION ":MAGISK:D");
|
||||
#else
|
||||
write_string(client, MAGISK_VERSION ":MAGISK:R");
|
||||
#endif
|
||||
break;
|
||||
case +RequestCode::CHECK_VERSION_CODE:
|
||||
write_int(client, MAGISK_VER_CODE);
|
||||
break;
|
||||
case +RequestCode::START_DAEMON:
|
||||
setup_logfile();
|
||||
break;
|
||||
case +RequestCode::STOP_DAEMON: {
|
||||
// Unmount all overlays
|
||||
denylist_handler(-1, nullptr);
|
||||
|
||||
// Restore native bridge property
|
||||
restore_zygisk_prop();
|
||||
|
||||
write_int(client, 0);
|
||||
|
||||
// Terminate the daemon!
|
||||
exit(0);
|
||||
}
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_client(pid_t pid) {
|
||||
// Verify caller is the same as server
|
||||
char path[32];
|
||||
@@ -243,16 +174,13 @@ static void handle_request(pollfd *pfd) {
|
||||
|
||||
// Verify client credentials
|
||||
sock_cred cred;
|
||||
bool is_root;
|
||||
bool is_zygote;
|
||||
int code;
|
||||
|
||||
if (!get_client_cred(client, &cred)) {
|
||||
// Client died
|
||||
return;
|
||||
}
|
||||
is_root = cred.uid == AID_ROOT;
|
||||
is_zygote = cred.context == "u:r:zygote:s0";
|
||||
bool is_root = cred.uid == AID_ROOT;
|
||||
bool is_zygote = cred.context == "u:r:zygote:s0";
|
||||
|
||||
if (!is_root && !is_zygote && !is_client(cred.pid)) {
|
||||
// Unsupported client state
|
||||
@@ -260,7 +188,7 @@ static void handle_request(pollfd *pfd) {
|
||||
return;
|
||||
}
|
||||
|
||||
code = read_int(client);
|
||||
int code = read_int(client);
|
||||
if (code < 0 || code >= +RequestCode::END ||
|
||||
code == +RequestCode::_SYNC_BARRIER_ ||
|
||||
code == +RequestCode::_STAGE_BARRIER_) {
|
||||
@@ -302,9 +230,11 @@ static void handle_request(pollfd *pfd) {
|
||||
write_int(client, +RespondCode::OK);
|
||||
|
||||
if (code < +RequestCode::_SYNC_BARRIER_) {
|
||||
handle_request_sync(client, code);
|
||||
MagiskD::Get().handle_request_sync(client.release(), code);
|
||||
} else if (code < +RequestCode::_STAGE_BARRIER_) {
|
||||
exec_task([=, fd = client.release()] { handle_request_async(fd, code, cred); });
|
||||
exec_task([=, fd = client.release()] {
|
||||
MagiskD::Get().handle_request_async(fd, code, cred);
|
||||
});
|
||||
} else {
|
||||
exec_task([=, fd = client.release()] {
|
||||
MagiskD::Get().boot_stage_handler(fd, code);
|
||||
@@ -389,7 +319,7 @@ int connect_daemon(int req, bool create) {
|
||||
|
||||
char buf[64];
|
||||
xreadlink("/proc/self/exe", buf, sizeof(buf));
|
||||
if (tmp[0] == '\0' || !str_starts(buf, tmp)) {
|
||||
if (tmp[0] == '\0' || !string_view(buf).starts_with(tmp)) {
|
||||
LOGE("Start daemon on magisk tmpfs\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
@@ -498,7 +428,7 @@ void unlock_blocks() {
|
||||
|
||||
bool check_key_combo() {
|
||||
uint8_t bitmask[(KEY_MAX + 1) / 8];
|
||||
vector<int> events;
|
||||
vector<owned_fd> events;
|
||||
constexpr char name[] = "/dev/.ev";
|
||||
|
||||
// First collect candidate events that accepts volume down
|
||||
@@ -512,19 +442,17 @@ bool check_key_combo() {
|
||||
memset(bitmask, 0, sizeof(bitmask));
|
||||
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
|
||||
if (test_bit(KEY_VOLUMEDOWN, bitmask))
|
||||
events.push_back(fd);
|
||||
events.emplace_back(fd);
|
||||
else
|
||||
close(fd);
|
||||
}
|
||||
if (events.empty())
|
||||
return false;
|
||||
|
||||
run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); });
|
||||
|
||||
// Check if volume down key is held continuously for more than 3 seconds
|
||||
for (int i = 0; i < 300; ++i) {
|
||||
bool pressed = false;
|
||||
for (const int &fd : events) {
|
||||
for (int fd : events) {
|
||||
memset(bitmask, 0, sizeof(bitmask));
|
||||
ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
|
||||
if (test_bit(KEY_VOLUMEDOWN, bitmask)) {
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOTOVL, SECURE_DIR};
|
||||
use crate::UCred;
|
||||
use crate::consts::{
|
||||
MAGISK_FULL_VER, MAGISK_PROC_CON, MAGISK_VER_CODE, MAGISK_VERSION, MAIN_CONFIG, ROOTMNT,
|
||||
ROOTOVL, SECURE_DIR,
|
||||
};
|
||||
use crate::db::Sqlite3;
|
||||
use crate::ffi::{
|
||||
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, exec_common_scripts, exec_module_scripts,
|
||||
get_magisk_tmp, get_prop, initialize_denylist, set_prop, setup_magisk_env,
|
||||
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, denylist_handler, exec_common_scripts,
|
||||
exec_module_scripts, get_magisk_tmp, initialize_denylist, scan_deny_apps, setup_magisk_env,
|
||||
};
|
||||
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
|
||||
use crate::module::disable_modules;
|
||||
use crate::module::{disable_modules, remove_modules};
|
||||
use crate::mount::{clean_mounts, setup_preinit_dir};
|
||||
use crate::package::ManagerInfo;
|
||||
use crate::resetprop::{get_prop, set_prop};
|
||||
use crate::selinux::restore_tmpcon;
|
||||
use crate::socket::IpcWrite;
|
||||
use crate::su::SuInfo;
|
||||
use crate::zygisk::ZygiskState;
|
||||
use base::const_format::concatcp;
|
||||
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
|
||||
use base::{
|
||||
AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, error, info, libc,
|
||||
AtomicArc, BufReadExt, FsPathBuilder, ReadExt, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||
cstr, error, info, libc,
|
||||
};
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd};
|
||||
use std::process::{Command, exit};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
// Global magiskd singleton
|
||||
@@ -66,9 +76,8 @@ pub struct MagiskD {
|
||||
pub manager_info: Mutex<ManagerInfo>,
|
||||
boot_stage_lock: Mutex<BootStateFlags>,
|
||||
pub module_list: OnceLock<Vec<ModuleInfo>>,
|
||||
pub zygiskd_sockets: Mutex<(Option<UnixStream>, Option<UnixStream>)>,
|
||||
pub zygisk_enabled: AtomicBool,
|
||||
pub zygote_start_count: AtomicU32,
|
||||
pub zygisk: Mutex<ZygiskState>,
|
||||
pub cached_su_info: AtomicArc<SuInfo>,
|
||||
sdk_int: i32,
|
||||
pub is_emulator: bool,
|
||||
@@ -80,10 +89,6 @@ impl MagiskD {
|
||||
unsafe { MAGISKD.get().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
pub fn zygisk_enabled(&self) -> bool {
|
||||
self.zygisk_enabled.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn sdk_int(&self) -> i32 {
|
||||
self.sdk_int
|
||||
}
|
||||
@@ -126,8 +131,8 @@ impl MagiskD {
|
||||
.log()
|
||||
.ok();
|
||||
let safe_mode = boot_cnt >= 2
|
||||
|| get_prop(cstr!("persist.sys.safemode"), true) == "1"
|
||||
|| get_prop(cstr!("ro.sys.safemode"), false) == "1"
|
||||
|| get_prop(cstr!("persist.sys.safemode")) == "1"
|
||||
|| get_prop(cstr!("ro.sys.safemode")) == "1"
|
||||
|| check_key_combo();
|
||||
|
||||
if safe_mode {
|
||||
@@ -175,10 +180,12 @@ impl MagiskD {
|
||||
|
||||
setup_preinit_dir();
|
||||
self.ensure_manager();
|
||||
self.zygisk_reset(true)
|
||||
self.zygisk.lock().unwrap().reset(true);
|
||||
}
|
||||
|
||||
pub fn boot_stage_handler(&self, client: i32, code: i32) {
|
||||
// Take ownership
|
||||
let client = unsafe { OwnedFd::from_raw_fd(client) };
|
||||
// Make sure boot stage execution is always serialized
|
||||
let mut state = self.boot_stage_lock.lock().unwrap();
|
||||
|
||||
@@ -191,10 +198,9 @@ impl MagiskD {
|
||||
}
|
||||
state.set(BootState::PostFsDataDone);
|
||||
}
|
||||
unsafe { libc::close(client) };
|
||||
}
|
||||
RequestCode::LATE_START => {
|
||||
unsafe { libc::close(client) };
|
||||
drop(client);
|
||||
if state.contains(BootState::PostFsDataDone) && !state.contains(BootState::SafeMode)
|
||||
{
|
||||
self.late_start();
|
||||
@@ -202,15 +208,85 @@ impl MagiskD {
|
||||
}
|
||||
}
|
||||
RequestCode::BOOT_COMPLETE => {
|
||||
unsafe { libc::close(client) };
|
||||
drop(client);
|
||||
if state.contains(BootState::PostFsDataDone) {
|
||||
state.set(BootState::BootComplete);
|
||||
self.boot_complete()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unsafe { libc::close(client) };
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_request_sync(&self, client: i32, code: i32) {
|
||||
// Take ownership
|
||||
let mut client = unsafe { File::from_raw_fd(client) };
|
||||
let code = RequestCode { repr: code };
|
||||
match code {
|
||||
RequestCode::CHECK_VERSION => {
|
||||
#[cfg(debug_assertions)]
|
||||
let s = concatcp!(MAGISK_VERSION, ":MAGISK:D");
|
||||
#[cfg(not(debug_assertions))]
|
||||
let s = concatcp!(MAGISK_VERSION, ":MAGISK:R");
|
||||
|
||||
client.write_encodable(s).log_ok();
|
||||
}
|
||||
RequestCode::CHECK_VERSION_CODE => {
|
||||
client.write_pod(&MAGISK_VER_CODE).log_ok();
|
||||
}
|
||||
RequestCode::START_DAEMON => {
|
||||
setup_logfile();
|
||||
}
|
||||
RequestCode::STOP_DAEMON => {
|
||||
// Unmount all overlays
|
||||
denylist_handler(-1);
|
||||
|
||||
// Restore native bridge property
|
||||
self.zygisk.lock().unwrap().restore_prop();
|
||||
|
||||
client.write_pod(&0).log_ok();
|
||||
|
||||
// Terminate the daemon!
|
||||
exit(0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_request_async(&self, client: i32, code: i32, cred: &UCred) {
|
||||
// Take ownership
|
||||
let client = unsafe { OwnedFd::from_raw_fd(client) };
|
||||
let code = RequestCode { repr: code };
|
||||
match code {
|
||||
RequestCode::DENYLIST => {
|
||||
denylist_handler(client.into_raw_fd());
|
||||
}
|
||||
RequestCode::SUPERUSER => {
|
||||
self.su_daemon_handler(client, cred);
|
||||
}
|
||||
RequestCode::ZYGOTE_RESTART => {
|
||||
info!("** zygote restarted");
|
||||
self.prune_su_access();
|
||||
scan_deny_apps();
|
||||
self.zygisk.lock().unwrap().reset(false);
|
||||
}
|
||||
RequestCode::SQLITE_CMD => {
|
||||
self.db_exec_for_cli(client).ok();
|
||||
}
|
||||
RequestCode::REMOVE_MODULES => {
|
||||
let mut file = File::from(client);
|
||||
let mut do_reboot: i32 = 0;
|
||||
file.read_pod(&mut do_reboot).log_ok();
|
||||
remove_modules();
|
||||
file.write_pod(&0).log_ok();
|
||||
if do_reboot != 0 {
|
||||
self.reboot();
|
||||
}
|
||||
}
|
||||
RequestCode::ZYGISK => {
|
||||
self.zygisk_handler(client);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,11 +311,11 @@ pub fn daemon_entry() {
|
||||
|
||||
start_log_daemon();
|
||||
magisk_logging();
|
||||
info!("Magisk {} daemon started", MAGISK_FULL_VER);
|
||||
info!("Magisk {MAGISK_FULL_VER} daemon started");
|
||||
|
||||
let is_emulator = get_prop(cstr!("ro.kernel.qemu"), false) == "1"
|
||||
|| get_prop(cstr!("ro.boot.qemu"), false) == "1"
|
||||
|| get_prop(cstr!("ro.product.device"), false).contains("vsoc");
|
||||
let is_emulator = get_prop(cstr!("ro.kernel.qemu")) == "1"
|
||||
|| get_prop(cstr!("ro.boot.qemu")) == "1"
|
||||
|| get_prop(cstr!("ro.product.device")).contains("vsoc");
|
||||
|
||||
// Load config status
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
@@ -248,7 +324,7 @@ pub fn daemon_entry() {
|
||||
.join_path(MAIN_CONFIG);
|
||||
let mut is_recovery = false;
|
||||
if let Ok(main_config) = tmp_path.open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(main_config).foreach_props(|key, val| {
|
||||
BufReader::new(main_config).for_each_prop(|key, val| {
|
||||
if key == "RECOVERYMODE" {
|
||||
is_recovery = val == "true";
|
||||
return false;
|
||||
@@ -260,7 +336,7 @@ pub fn daemon_entry() {
|
||||
|
||||
let mut sdk_int = -1;
|
||||
if let Ok(build_prop) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(build_prop).foreach_props(|key, val| {
|
||||
BufReader::new(build_prop).for_each_prop(|key, val| {
|
||||
if key == "ro.build.version.sdk" {
|
||||
sdk_int = val.parse::<i32>().unwrap_or(-1);
|
||||
return false;
|
||||
@@ -270,7 +346,7 @@ pub fn daemon_entry() {
|
||||
}
|
||||
if sdk_int < 0 {
|
||||
// In case some devices do not store this info in build.prop, fallback to getprop
|
||||
sdk_int = get_prop(cstr!("ro.build.version.sdk"), false)
|
||||
sdk_int = get_prop(cstr!("ro.build.version.sdk"))
|
||||
.parse::<i32>()
|
||||
.unwrap_or(-1);
|
||||
}
|
||||
@@ -283,19 +359,19 @@ pub fn daemon_entry() {
|
||||
switch_cgroup("/acct", pid);
|
||||
switch_cgroup("/dev/cg2_bpf", pid);
|
||||
switch_cgroup("/sys/fs/cgroup", pid);
|
||||
if get_prop(cstr!("ro.config.per_app_memcg"), false) != "false" {
|
||||
if get_prop(cstr!("ro.config.per_app_memcg")) != "false" {
|
||||
switch_cgroup("/dev/memcg/apps", pid);
|
||||
}
|
||||
|
||||
// Samsung workaround #7887
|
||||
if cstr!("/system_ext/app/mediatek-res/mediatek-res.apk").exists() {
|
||||
set_prop(cstr!("ro.vendor.mtk_model"), cstr!("0"), false);
|
||||
set_prop(cstr!("ro.vendor.mtk_model"), cstr!("0"));
|
||||
}
|
||||
|
||||
// Cleanup pre-init mounts
|
||||
tmp_path.append_path(ROOTMNT);
|
||||
if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(mount_list).foreach_lines(|line| {
|
||||
BufReader::new(mount_list).for_each_line(|line| {
|
||||
line.truncate(line.trim_end().len());
|
||||
let item = Utf8CStr::from_string(line);
|
||||
item.unmount().log_ok();
|
||||
@@ -312,14 +388,13 @@ pub fn daemon_entry() {
|
||||
|
||||
// Remove all pre-init overlay files to free-up memory
|
||||
tmp_path.append_path(ROOTOVL);
|
||||
tmp_path.remove_all().log_ok();
|
||||
tmp_path.remove_all().ok();
|
||||
tmp_path.truncate(magisk_tmp.len());
|
||||
|
||||
let magiskd = MagiskD {
|
||||
sdk_int,
|
||||
is_emulator,
|
||||
is_recovery,
|
||||
zygote_start_count: AtomicU32::new(1),
|
||||
..Default::default()
|
||||
};
|
||||
MAGISKD.set(magiskd).ok();
|
||||
@@ -342,7 +417,7 @@ fn switch_cgroup(cgroup: &str, pid: i32) {
|
||||
fn check_data() -> bool {
|
||||
if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut mnt = false;
|
||||
BufReader::new(file).foreach_lines(|line| {
|
||||
BufReader::new(file).for_each_line(|line| {
|
||||
if line.contains(" /data ") && !line.contains("tmpfs") {
|
||||
mnt = true;
|
||||
return false;
|
||||
@@ -352,14 +427,14 @@ fn check_data() -> bool {
|
||||
if !mnt {
|
||||
return false;
|
||||
}
|
||||
let crypto = get_prop(cstr!("ro.crypto.state"), false);
|
||||
let crypto = get_prop(cstr!("ro.crypto.state"));
|
||||
return if !crypto.is_empty() {
|
||||
if crypto != "encrypted" {
|
||||
// Unencrypted, we can directly access data
|
||||
true
|
||||
} else {
|
||||
// Encrypted, check whether vold is started
|
||||
!get_prop(cstr!("init.svc.vold"), false).is_empty()
|
||||
!get_prop(cstr!("init.svc.vold")).is_empty()
|
||||
}
|
||||
} else {
|
||||
// ro.crypto.state is not set, assume it's unencrypted
|
||||
|
||||
@@ -11,7 +11,7 @@ use num_traits::FromPrimitive;
|
||||
use std::ffi::c_void;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter};
|
||||
use std::os::fd::{FromRawFd, OwnedFd, RawFd};
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::ptr::NonNull;
|
||||
@@ -302,7 +302,7 @@ impl MagiskD {
|
||||
.sql_result()
|
||||
}
|
||||
|
||||
fn db_exec_for_client(&self, fd: OwnedFd) -> LoggedResult<()> {
|
||||
pub fn db_exec_for_cli(&self, fd: OwnedFd) -> LoggedResult<()> {
|
||||
let mut file = File::from(fd);
|
||||
let mut reader = BufReader::new(&mut file);
|
||||
let sql: String = reader.read_decodable()?;
|
||||
@@ -328,12 +328,6 @@ impl MagiskD {
|
||||
pub fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool {
|
||||
self.set_db_setting(key, value).log().is_ok()
|
||||
}
|
||||
|
||||
pub fn db_exec_for_cxx(&self, client_fd: RawFd) {
|
||||
// Take ownership
|
||||
let fd = unsafe { OwnedFd::from_raw_fd(client_fd) };
|
||||
self.db_exec_for_client(fd).ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "sql_exec_rs")]
|
||||
|
||||
@@ -26,7 +26,7 @@ Actions:
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void denylist_handler(int client, const sock_cred *cred) {
|
||||
void denylist_handler(int client) {
|
||||
if (client < 0) {
|
||||
revert_unmount();
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <android/log.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <core.hpp>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <sqlite.hpp>
|
||||
@@ -90,9 +91,10 @@ static void crawl_procfs(const F &fn) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool str_eql(string_view a, string_view b) { return a == b; }
|
||||
static bool str_eql(string_view a, string_view b) { return a == b; }
|
||||
static bool str_starts_with(string_view a, string_view b) { return a.starts_with(b); }
|
||||
|
||||
template<bool str_op(string_view, string_view) = &str_eql>
|
||||
template<bool str_op(string_view, string_view) = str_eql>
|
||||
static bool proc_name_match(int pid, string_view name) {
|
||||
char buf[4019];
|
||||
sprintf(buf, "/proc/%d/cmdline", pid);
|
||||
@@ -111,7 +113,7 @@ bool proc_context_match(int pid, string_view context) {
|
||||
|
||||
sprintf(buf, "/proc/%d", pid);
|
||||
if (lgetfilecon(buf, byte_data{ con, sizeof(con) })) {
|
||||
return str_starts(con, context);
|
||||
return string_view(con).starts_with(context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -173,7 +175,7 @@ static bool add_hide_set(const char *pkg, const char *proc) {
|
||||
return true;
|
||||
if (str_eql(pkg, ISOLATED_MAGIC)) {
|
||||
// Kill all matching isolated processes
|
||||
kill_process<&proc_name_match<str_starts>>(proc, true);
|
||||
kill_process<&proc_name_match<str_starts_with>>(proc, true);
|
||||
} else {
|
||||
kill_process(proc);
|
||||
}
|
||||
@@ -411,7 +413,7 @@ bool is_deny_target(int uid, string_view process) {
|
||||
if (app_id >= 90000) {
|
||||
if (auto it = pkg_to_procs.find(ISOLATED_MAGIC); it != pkg_to_procs.end()) {
|
||||
for (const auto &s : it->second) {
|
||||
if (str_starts(process, s))
|
||||
if (process.starts_with(s))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
#define to_app_id(uid) (uid % AID_USER_OFFSET)
|
||||
#define to_user_id(uid) (uid / AID_USER_OFFSET)
|
||||
|
||||
// Multi-call entrypoints
|
||||
int magisk_main(int argc, char *argv[]);
|
||||
int su_client_main(int argc, char *argv[]);
|
||||
int zygisk_main(int argc, char *argv[]);
|
||||
|
||||
// Return codes for daemon
|
||||
enum class RespondCode : int {
|
||||
ERROR = -1,
|
||||
@@ -35,11 +40,6 @@ void unlock_blocks();
|
||||
bool setup_magisk_env();
|
||||
bool check_key_combo();
|
||||
|
||||
// Zygisk daemon
|
||||
rust::Str get_zygisk_lib_name();
|
||||
void set_zygisk_prop();
|
||||
void restore_zygisk_prop();
|
||||
|
||||
// Sockets
|
||||
struct sock_cred : public ucred {
|
||||
std::string context;
|
||||
@@ -90,13 +90,13 @@ void init_thread_pool();
|
||||
void exec_task(std::function<void()> &&task);
|
||||
|
||||
// Daemon handlers
|
||||
void denylist_handler(int client, const sock_cred *cred);
|
||||
void denylist_handler(int client);
|
||||
|
||||
// Scripting
|
||||
void install_apk(rust::Utf8CStr apk);
|
||||
void uninstall_pkg(rust::Utf8CStr pkg);
|
||||
void exec_common_scripts(rust::Utf8CStr stage);
|
||||
void exec_module_scripts(rust::Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list);
|
||||
void install_apk(Utf8CStr apk);
|
||||
void uninstall_pkg(Utf8CStr pkg);
|
||||
void exec_common_scripts(Utf8CStr stage);
|
||||
void exec_module_scripts(Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list);
|
||||
void exec_script(const char *script);
|
||||
void clear_pkg(const char *pkg, int user_id);
|
||||
[[noreturn]] void install_module(const char *file);
|
||||
@@ -114,8 +114,8 @@ void update_deny_flags(int uid, rust::Str process, uint32_t &flags);
|
||||
void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode);
|
||||
|
||||
// Rust bindings
|
||||
static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
|
||||
static inline rust::String resolve_preinit_dir_rs(rust::Utf8CStr base_dir) {
|
||||
static inline Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
|
||||
static inline rust::String resolve_preinit_dir_rs(Utf8CStr base_dir) {
|
||||
return resolve_preinit_dir(base_dir.c_str());
|
||||
}
|
||||
static inline void exec_script_rs(rust::Utf8CStr script) { exec_script(script.data()); }
|
||||
static inline void exec_script_rs(Utf8CStr script) { exec_script(script.data()); }
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <cxx.h>
|
||||
|
||||
struct prop_cb {
|
||||
virtual void exec(const char *name, const char *value, uint32_t serial) = 0;
|
||||
};
|
||||
|
||||
using prop_list = std::map<std::string, std::string>;
|
||||
|
||||
struct prop_collector : prop_cb {
|
||||
explicit prop_collector(prop_list &list) : list(list) {}
|
||||
void exec(const char *name, const char *value, uint32_t) override {
|
||||
list.insert({name, value});
|
||||
}
|
||||
private:
|
||||
prop_list &list;
|
||||
};
|
||||
|
||||
// System properties
|
||||
std::string get_prop(const char *name, bool persist = false);
|
||||
int delete_prop(const char *name, bool persist = false);
|
||||
int set_prop(const char *name, const char *value, bool skip_svc = false);
|
||||
void load_prop_file(const char *filename, bool skip_svc = false);
|
||||
|
||||
// Rust bindings
|
||||
rust::String get_prop_rs(rust::Utf8CStr name, bool persist);
|
||||
static inline int set_prop_rs(rust::Utf8CStr name, rust::Utf8CStr value, bool skip_svc) {
|
||||
return set_prop(name.data(), value.data(), skip_svc);
|
||||
}
|
||||
static inline void load_prop_file_rs(rust::Utf8CStr filename, bool skip_svc) {
|
||||
load_prop_file(filename.data(), skip_svc);
|
||||
}
|
||||
static inline void prop_cb_exec(prop_cb &cb, rust::Utf8CStr name, rust::Utf8CStr value, uint32_t serial) {
|
||||
cb.exec(name.data(), value.data(), serial);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <cxx.h>
|
||||
#include <rust/cxx.h>
|
||||
|
||||
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
|
||||
#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#![feature(fn_traits)]
|
||||
#![feature(unix_socket_ancillary_data)]
|
||||
#![feature(unix_socket_peek)]
|
||||
#![feature(default_field_values)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use crate::ffi::SuRequest;
|
||||
@@ -11,12 +12,11 @@ use base::libc;
|
||||
use cxx::{ExternType, type_id};
|
||||
use daemon::{MagiskD, daemon_entry};
|
||||
use derive::Decodable;
|
||||
use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
|
||||
use module::remove_modules;
|
||||
use logging::{android_logging, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
|
||||
use mount::{find_preinit_device, revert_unmount};
|
||||
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
||||
use resetprop::{get_prop, resetprop_main};
|
||||
use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon};
|
||||
use socket::{recv_fd, recv_fds, send_fd, send_fds};
|
||||
use socket::{recv_fd, recv_fds, send_fd};
|
||||
use std::fs::File;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::DerefMut;
|
||||
@@ -126,7 +126,6 @@ pub mod ffi {
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
|
||||
#[cxx_name = "ucred"]
|
||||
@@ -148,12 +147,13 @@ pub mod ffi {
|
||||
fn uninstall_pkg(apk: Utf8CStrRef);
|
||||
fn update_deny_flags(uid: i32, process: &str, flags: &mut u32);
|
||||
fn initialize_denylist();
|
||||
fn get_zygisk_lib_name() -> &'static str;
|
||||
fn set_zygisk_prop();
|
||||
fn restore_zygisk_prop();
|
||||
fn switch_mnt_ns(pid: i32) -> i32;
|
||||
fn exec_root_shell(client: i32, pid: i32, req: &mut SuRequest, mode: MntNsMode);
|
||||
|
||||
// Denylist
|
||||
fn denylist_handler(client: i32);
|
||||
fn scan_deny_apps();
|
||||
|
||||
include!("include/sqlite.hpp");
|
||||
|
||||
type sqlite3;
|
||||
@@ -167,18 +167,6 @@ pub mod ffi {
|
||||
fn get_text(self: &DbValues, index: i32) -> &str;
|
||||
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32;
|
||||
fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> i32;
|
||||
|
||||
include!("include/resetprop.hpp");
|
||||
|
||||
#[cxx_name = "prop_cb"]
|
||||
type PropCb;
|
||||
#[cxx_name = "get_prop_rs"]
|
||||
fn get_prop(name: Utf8CStrRef, persist: bool) -> String;
|
||||
#[cxx_name = "set_prop_rs"]
|
||||
fn set_prop(name: Utf8CStrRef, value: Utf8CStrRef, skip_svc: bool) -> i32;
|
||||
#[cxx_name = "load_prop_file_rs"]
|
||||
fn load_prop_file(filename: Utf8CStrRef, skip_svc: bool);
|
||||
fn prop_cb_exec(cb: Pin<&mut PropCb>, name: Utf8CStrRef, value: Utf8CStrRef, serial: u32);
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
@@ -186,17 +174,10 @@ pub mod ffi {
|
||||
fn zygisk_logging();
|
||||
fn zygisk_close_logd();
|
||||
fn zygisk_get_logd() -> i32;
|
||||
fn setup_logfile();
|
||||
fn find_preinit_device() -> String;
|
||||
fn revert_unmount(pid: i32);
|
||||
fn remove_modules();
|
||||
fn zygisk_should_load_module(flags: u32) -> bool;
|
||||
unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCb>);
|
||||
unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>);
|
||||
unsafe fn persist_delete_prop(name: Utf8CStrRef) -> bool;
|
||||
unsafe fn persist_set_prop(name: Utf8CStrRef, value: Utf8CStrRef) -> bool;
|
||||
fn send_fd(socket: i32, fd: i32) -> bool;
|
||||
fn send_fds(socket: i32, fds: &[i32]) -> bool;
|
||||
fn recv_fd(socket: i32) -> i32;
|
||||
fn recv_fds(socket: i32) -> Vec<i32>;
|
||||
fn write_to_fd(self: &SuRequest, fd: i32);
|
||||
@@ -208,6 +189,9 @@ pub mod ffi {
|
||||
fn setfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool;
|
||||
fn lsetfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool;
|
||||
|
||||
fn get_prop(name: Utf8CStrRef) -> String;
|
||||
unsafe fn resetprop_main(argc: i32, argv: *mut *mut c_char) -> i32;
|
||||
|
||||
#[namespace = "rust"]
|
||||
fn daemon_entry();
|
||||
}
|
||||
@@ -222,22 +206,14 @@ pub mod ffi {
|
||||
// FFI for MagiskD
|
||||
extern "Rust" {
|
||||
type MagiskD;
|
||||
fn reboot(&self);
|
||||
fn sdk_int(&self) -> i32;
|
||||
fn zygisk_enabled(&self) -> bool;
|
||||
fn boot_stage_handler(&self, client: i32, code: i32);
|
||||
fn zygisk_handler(&self, client: i32);
|
||||
fn zygisk_reset(&self, restore: bool);
|
||||
fn prune_su_access(&self);
|
||||
fn su_daemon_handler(&self, client: i32, cred: &UCred);
|
||||
#[cxx_name = "get_manager"]
|
||||
unsafe fn get_manager_for_cxx(&self, user: i32, ptr: *mut CxxString, install: bool) -> i32;
|
||||
|
||||
fn handle_request_sync(&self, client: i32, code: i32);
|
||||
fn handle_request_async(&self, client: i32, code: i32, cred: &UCred);
|
||||
fn get_db_setting(&self, key: DbEntryKey) -> i32;
|
||||
#[cxx_name = "set_db_setting"]
|
||||
fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool;
|
||||
#[cxx_name = "db_exec"]
|
||||
fn db_exec_for_cxx(&self, client_fd: i32);
|
||||
|
||||
#[Self = MagiskD]
|
||||
#[cxx_name = "Get"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::consts::{MODULEMNT, MODULEROOT, MODULEUPGRADE, WORKERDIR};
|
||||
use crate::daemon::MagiskD;
|
||||
use crate::ffi::{
|
||||
ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp, get_zygisk_lib_name,
|
||||
load_prop_file, set_zygisk_prop,
|
||||
};
|
||||
use crate::ffi::{ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp};
|
||||
use crate::mount::setup_module_mount;
|
||||
use crate::resetprop::load_prop_file;
|
||||
use base::{
|
||||
DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResultStatic, ResultExt,
|
||||
Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc,
|
||||
raw_cstr, warn,
|
||||
SilentLogExt, Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error,
|
||||
info, libc, raw_cstr, warn,
|
||||
};
|
||||
use libc::{AT_REMOVEDIR, MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY};
|
||||
use std::collections::BTreeMap;
|
||||
@@ -72,7 +70,7 @@ impl PathTracker<'_> {
|
||||
PathTracker { path, len }
|
||||
}
|
||||
|
||||
fn append(&mut self, name: &str) -> PathTracker {
|
||||
fn append(&mut self, name: &str) -> PathTracker<'_> {
|
||||
let len = self.path.len();
|
||||
self.path.append_path(name);
|
||||
PathTracker {
|
||||
@@ -81,7 +79,7 @@ impl PathTracker<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow(&mut self) -> PathTracker {
|
||||
fn reborrow(&mut self) -> PathTracker<'_> {
|
||||
Self::from(self.path)
|
||||
}
|
||||
}
|
||||
@@ -101,7 +99,7 @@ struct FilePaths<'a> {
|
||||
}
|
||||
|
||||
impl FilePaths<'_> {
|
||||
fn append(&mut self, name: &str) -> FilePaths {
|
||||
fn append(&mut self, name: &str) -> FilePaths<'_> {
|
||||
FilePaths {
|
||||
real: self.real.append(name),
|
||||
worker: self.worker.append(name),
|
||||
@@ -110,7 +108,7 @@ impl FilePaths<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow(&mut self) -> FilePaths {
|
||||
fn reborrow(&mut self) -> FilePaths<'_> {
|
||||
FilePaths {
|
||||
real: self.real.reborrow(),
|
||||
worker: self.worker.reborrow(),
|
||||
@@ -502,9 +500,7 @@ fn inject_magisk_bins(system: &mut FsNode, is_emulator: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn inject_zygisk_bins(system: &mut FsNode) {
|
||||
let name = get_zygisk_lib_name();
|
||||
|
||||
fn inject_zygisk_bins(name: &str, system: &mut FsNode) {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let has_32_bit = cstr!("/system/bin/linker").exists();
|
||||
|
||||
@@ -560,116 +556,8 @@ fn inject_zygisk_bins(system: &mut FsNode) {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_modules(zygisk: bool, module_list: &[ModuleInfo], is_emulator: bool) {
|
||||
let mut system = FsNode::new_dir();
|
||||
|
||||
// Build all the base "prefix" paths
|
||||
let mut root = cstr::buf::default().join_path("/");
|
||||
|
||||
let mut module_dir = cstr::buf::default().join_path(MODULEROOT);
|
||||
|
||||
let mut module_mnt = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(MODULEMNT);
|
||||
|
||||
let mut worker = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(WORKERDIR);
|
||||
|
||||
// Create a collection of all relevant paths
|
||||
let mut root_paths = FilePaths {
|
||||
real: PathTracker::from(&mut root),
|
||||
worker: PathTracker::from(&mut worker),
|
||||
module_mnt: PathTracker::from(&mut module_mnt),
|
||||
module_root: PathTracker::from(&mut module_dir),
|
||||
};
|
||||
|
||||
// Step 1: Create virtual filesystem tree
|
||||
//
|
||||
// In this step, there is zero logic applied during tree construction; we simply collect and
|
||||
// record the union of all module filesystem trees under each of their /system directory.
|
||||
|
||||
for info in module_list {
|
||||
let mut module_paths = root_paths.append(&info.name);
|
||||
{
|
||||
// Read props
|
||||
let prop = module_paths.append("system.prop");
|
||||
if prop.module().exists() {
|
||||
// Do NOT go through property service as it could cause boot lock
|
||||
load_prop_file(prop.module(), true);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Check whether skip mounting
|
||||
let skip = module_paths.append("skip_mount");
|
||||
if skip.module().exists() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
// Double check whether the system folder exists
|
||||
let sys = module_paths.append("system");
|
||||
if sys.module().exists() {
|
||||
info!("{}: loading module files", &info.name);
|
||||
system.collect(sys).log_ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Inject custom files
|
||||
//
|
||||
// Magisk provides some built-in functionality that requires augmenting the filesystem.
|
||||
// We expose several cmdline tools (e.g. su) into PATH, and the zygisk shared library
|
||||
// has to also be added into the default LD_LIBRARY_PATH for code injection.
|
||||
// We directly inject file nodes into the virtual filesystem tree we built in the previous
|
||||
// step, treating Magisk just like a special "module".
|
||||
|
||||
if get_magisk_tmp() != "/sbin" || get_path_env().split(":").all(|s| s != "/sbin") {
|
||||
inject_magisk_bins(&mut system, is_emulator);
|
||||
}
|
||||
if zygisk {
|
||||
inject_zygisk_bins(&mut system);
|
||||
}
|
||||
|
||||
// Step 3: Extract all supported read-only partition roots
|
||||
//
|
||||
// For simplicity and backwards compatibility on older Android versions, when constructing
|
||||
// Magisk modules, we always assume that there is only a single read-only partition mounted
|
||||
// at /system. However, on modern Android there are actually multiple read-only partitions
|
||||
// mounted at their respective paths. We need to extract these subtrees out of the main
|
||||
// tree and treat them as individual trees.
|
||||
|
||||
let mut roots = BTreeMap::new(); /* mapOf(partition_name -> FsNode) */
|
||||
if let FsNode::Directory { children } = &mut system {
|
||||
for dir in SECONDARY_READ_ONLY_PARTITIONS {
|
||||
// Only treat these nodes as root iff it is actually a directory in rootdir
|
||||
if let Ok(attr) = dir.get_attr()
|
||||
&& attr.is_dir()
|
||||
{
|
||||
let name = dir.trim_start_matches('/');
|
||||
if let Some(root) = children.remove(name) {
|
||||
roots.insert(name, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
roots.insert("system", system);
|
||||
|
||||
for (dir, mut root) in roots {
|
||||
// Step 4: Convert virtual filesystem tree into concrete operations
|
||||
//
|
||||
// Compare the virtual filesystem tree we constructed against the real filesystem
|
||||
// structure on-device to generate a series of "operations".
|
||||
// The "core" of the logic is to decide which directories need to be rebuilt in the
|
||||
// tmpfs worker directory, and real sub-nodes need to be mirrored inside it.
|
||||
|
||||
let path = root_paths.append(dir);
|
||||
root.commit(path, true).log_ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade_modules() -> LoggedResult<()> {
|
||||
let mut upgrade = Directory::open(cstr!(MODULEUPGRADE))?;
|
||||
let mut upgrade = Directory::open(cstr!(MODULEUPGRADE)).silent()?;
|
||||
let ufd = upgrade.as_raw_fd();
|
||||
let root = Directory::open(cstr!(MODULEROOT))?;
|
||||
while let Some(e) = upgrade.read()? {
|
||||
@@ -877,11 +765,119 @@ impl MagiskD {
|
||||
|
||||
// Recollect modules (module scripts could remove itself)
|
||||
let modules = collect_modules(zygisk, true);
|
||||
if zygisk {
|
||||
set_zygisk_prop();
|
||||
}
|
||||
apply_modules(zygisk, &modules, self.is_emulator);
|
||||
self.apply_modules(&modules);
|
||||
|
||||
self.module_list.set(modules).ok();
|
||||
}
|
||||
|
||||
fn apply_modules(&self, module_list: &[ModuleInfo]) {
|
||||
let mut system = FsNode::new_dir();
|
||||
|
||||
// Build all the base "prefix" paths
|
||||
let mut root = cstr::buf::default().join_path("/");
|
||||
|
||||
let mut module_dir = cstr::buf::default().join_path(MODULEROOT);
|
||||
|
||||
let mut module_mnt = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(MODULEMNT);
|
||||
|
||||
let mut worker = cstr::buf::default()
|
||||
.join_path(get_magisk_tmp())
|
||||
.join_path(WORKERDIR);
|
||||
|
||||
// Create a collection of all relevant paths
|
||||
let mut root_paths = FilePaths {
|
||||
real: PathTracker::from(&mut root),
|
||||
worker: PathTracker::from(&mut worker),
|
||||
module_mnt: PathTracker::from(&mut module_mnt),
|
||||
module_root: PathTracker::from(&mut module_dir),
|
||||
};
|
||||
|
||||
// Step 1: Create virtual filesystem tree
|
||||
//
|
||||
// In this step, there is zero logic applied during tree construction; we simply collect and
|
||||
// record the union of all module filesystem trees under each of their /system directory.
|
||||
|
||||
for info in module_list {
|
||||
let mut module_paths = root_paths.append(&info.name);
|
||||
{
|
||||
// Read props
|
||||
let prop = module_paths.append("system.prop");
|
||||
if prop.module().exists() {
|
||||
load_prop_file(prop.module());
|
||||
}
|
||||
}
|
||||
{
|
||||
// Check whether skip mounting
|
||||
let skip = module_paths.append("skip_mount");
|
||||
if skip.module().exists() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
// Double check whether the system folder exists
|
||||
let sys = module_paths.append("system");
|
||||
if sys.module().exists() {
|
||||
info!("{}: loading module files", &info.name);
|
||||
system.collect(sys).log_ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Inject custom files
|
||||
//
|
||||
// Magisk provides some built-in functionality that requires augmenting the filesystem.
|
||||
// We expose several cmdline tools (e.g. su) into PATH, and the zygisk shared library
|
||||
// has to also be added into the default LD_LIBRARY_PATH for code injection.
|
||||
// We directly inject file nodes into the virtual filesystem tree we built in the previous
|
||||
// step, treating Magisk just like a special "module".
|
||||
|
||||
if get_magisk_tmp() != "/sbin" || get_path_env().split(":").all(|s| s != "/sbin") {
|
||||
inject_magisk_bins(&mut system, self.is_emulator);
|
||||
}
|
||||
|
||||
// Handle zygisk
|
||||
if self.zygisk_enabled.load(Ordering::Acquire) {
|
||||
let mut zygisk = self.zygisk.lock().unwrap();
|
||||
zygisk.set_prop();
|
||||
inject_zygisk_bins(&zygisk.lib_name, &mut system);
|
||||
}
|
||||
|
||||
// Step 3: Extract all supported read-only partition roots
|
||||
//
|
||||
// For simplicity and backwards compatibility on older Android versions, when constructing
|
||||
// Magisk modules, we always assume that there is only a single read-only partition mounted
|
||||
// at /system. However, on modern Android there are actually multiple read-only partitions
|
||||
// mounted at their respective paths. We need to extract these subtrees out of the main
|
||||
// tree and treat them as individual trees.
|
||||
|
||||
let mut roots = BTreeMap::new(); /* mapOf(partition_name -> FsNode) */
|
||||
if let FsNode::Directory { children } = &mut system {
|
||||
for dir in SECONDARY_READ_ONLY_PARTITIONS {
|
||||
// Only treat these nodes as root iff it is actually a directory in rootdir
|
||||
if let Ok(attr) = dir.get_attr()
|
||||
&& attr.is_dir()
|
||||
{
|
||||
let name = dir.trim_start_matches('/');
|
||||
if let Some(root) = children.remove(name) {
|
||||
roots.insert(name, root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
roots.insert("system", system);
|
||||
|
||||
for (dir, mut root) in roots {
|
||||
// Step 4: Convert virtual filesystem tree into concrete operations
|
||||
//
|
||||
// Compare the virtual filesystem tree we constructed against the real filesystem
|
||||
// structure on-device to generate a series of "operations".
|
||||
// The "core" of the logic is to decide which directories need to be rebuilt in the
|
||||
// tmpfs worker directory, and real sub-nodes need to be mirrored inside it.
|
||||
|
||||
let path = root_paths.append(dir);
|
||||
root.commit(path, true).log_ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ use base::{
|
||||
};
|
||||
|
||||
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
||||
use crate::ffi::{get_magisk_tmp, get_prop, resolve_preinit_dir, switch_mnt_ns};
|
||||
use crate::ffi::{get_magisk_tmp, resolve_preinit_dir, switch_mnt_ns};
|
||||
use crate::resetprop::get_prop;
|
||||
|
||||
pub fn setup_preinit_dir() {
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
@@ -109,11 +110,11 @@ enum EncryptType {
|
||||
}
|
||||
|
||||
pub fn find_preinit_device() -> String {
|
||||
let encrypt_type = if get_prop(cstr!("ro.crypto.state"), false) != "encrypted" {
|
||||
let encrypt_type = if get_prop(cstr!("ro.crypto.state")) != "encrypted" {
|
||||
EncryptType::None
|
||||
} else if get_prop(cstr!("ro.crypto.type"), false) == "block" {
|
||||
} else if get_prop(cstr!("ro.crypto.type")) == "block" {
|
||||
EncryptType::Block
|
||||
} else if get_prop(cstr!("ro.crypto.metadata.enabled"), false) == "true" {
|
||||
} else if get_prop(cstr!("ro.crypto.metadata.enabled")) == "true" {
|
||||
EncryptType::Metadata
|
||||
} else {
|
||||
EncryptType::File
|
||||
|
||||
@@ -8,13 +8,11 @@ use base::{
|
||||
Utf8CString, cstr, error, fd_get_attr, warn,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
use cxx::CxxString;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
|
||||
const EOCD_MAGIC: u32 = 0x06054B50;
|
||||
@@ -88,7 +86,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
apk.read_exact(&mut comment)?;
|
||||
let mut comment = Cursor::new(&comment);
|
||||
let mut apk_ver = 0;
|
||||
comment.foreach_props(|k, v| {
|
||||
comment.for_each_prop(|k, v| {
|
||||
if k == "versionCode" {
|
||||
apk_ver = v.parse::<i32>().unwrap_or(0);
|
||||
false
|
||||
@@ -474,19 +472,6 @@ impl MagiskD {
|
||||
let _ = info.get_manager(self, 0, true);
|
||||
}
|
||||
|
||||
pub unsafe fn get_manager_for_cxx(&self, user: i32, ptr: *mut CxxString, install: bool) -> i32 {
|
||||
unsafe {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let (uid, pkg) = info.get_manager(self, user, install);
|
||||
if let Some(str) = ptr.as_mut() {
|
||||
let mut str = Pin::new_unchecked(str);
|
||||
str.as_mut().clear();
|
||||
str.push_str(pkg);
|
||||
}
|
||||
uid
|
||||
}
|
||||
}
|
||||
|
||||
// app_id = app_no + AID_APP_START
|
||||
// app_no range: [0, 9999]
|
||||
pub fn get_app_no_list(&self) -> BitSet {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
pub use persist::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
||||
|
||||
mod persist;
|
||||
mod proto;
|
||||
325
native/src/core/resetprop/cli.rs
Normal file
325
native/src/core/resetprop/cli.rs
Normal file
@@ -0,0 +1,325 @@
|
||||
use super::{
|
||||
PropInfo, PropReader, SYS_PROP,
|
||||
persist::{persist_delete_prop, persist_get_all_props, persist_get_prop, persist_set_prop},
|
||||
};
|
||||
use argh::{EarlyExit, FromArgs, MissingRequirements};
|
||||
use base::libc::PROP_VALUE_MAX;
|
||||
use base::{
|
||||
BufReadExt, CmdArgs, EarlyExitExt, LogLevel, LoggedResult, ResultExt, Utf8CStr, Utf8CStrBuf,
|
||||
Utf8CString, cstr, debug, libc, log_err, set_log_level_state,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::c_char;
|
||||
use std::io::BufReader;
|
||||
|
||||
#[derive(FromArgs, Default)]
|
||||
struct ResetProp {
|
||||
#[argh(switch, short = 'v')]
|
||||
verbose: bool,
|
||||
#[argh(switch, short = 'w')]
|
||||
wait_mode: bool,
|
||||
#[argh(switch, short = 'p')]
|
||||
persist: bool,
|
||||
#[argh(switch, short = 'P')]
|
||||
persist_only: bool,
|
||||
#[argh(switch, short = 'Z')]
|
||||
context: bool,
|
||||
#[argh(switch, short = 'n')]
|
||||
skip_svc: bool,
|
||||
#[argh(option, short = 'f')]
|
||||
file: Option<Utf8CString>,
|
||||
#[argh(option, long = "delete", short = 'd')]
|
||||
delete_key: Option<Utf8CString>,
|
||||
#[argh(positional)]
|
||||
args: Vec<Utf8CString>,
|
||||
}
|
||||
|
||||
fn print_usage(cmd: &str) {
|
||||
eprintln!(
|
||||
r#"resetprop - System Property Manipulation Tool
|
||||
|
||||
Usage: {cmd} [flags] [arguments...]
|
||||
|
||||
Read mode arguments:
|
||||
(no arguments) print all properties
|
||||
NAME get property of NAME
|
||||
|
||||
Write mode arguments:
|
||||
NAME VALUE set property NAME as VALUE
|
||||
-f,--file FILE load and set properties from FILE
|
||||
-d,--delete NAME delete property
|
||||
|
||||
Wait mode arguments (toggled with -w):
|
||||
NAME wait until property NAME changes
|
||||
NAME OLD_VALUE if value of property NAME is not OLD_VALUE, get value
|
||||
or else wait until property NAME changes
|
||||
|
||||
General flags:
|
||||
-h,--help show this message
|
||||
-v print verbose output to stderr
|
||||
-w switch to wait mode
|
||||
|
||||
Read mode flags:
|
||||
-p also read persistent properties from storage
|
||||
-P only read persistent properties from storage
|
||||
-Z get property context instead of value
|
||||
|
||||
Write mode flags:
|
||||
-n set properties bypassing property_service
|
||||
-p always write persistent prop changes to storage
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
impl ResetProp {
|
||||
fn get(&self, key: &Utf8CStr) -> Option<String> {
|
||||
if self.context {
|
||||
return Some(SYS_PROP.get_context(key).to_string());
|
||||
}
|
||||
|
||||
let mut val = if !self.persist_only {
|
||||
SYS_PROP.find(key).map(|info| {
|
||||
let mut v = String::new();
|
||||
info.read(&mut PropReader::Value(&mut v));
|
||||
debug!("resetprop: get prop [{key}]=[{v}]");
|
||||
v
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if val.is_none() && (self.persist || self.persist_only) && key.starts_with("persist.") {
|
||||
val = persist_get_prop(key).ok();
|
||||
}
|
||||
|
||||
if val.is_none() {
|
||||
debug!("resetprop: prop [{key}] does not exist");
|
||||
}
|
||||
|
||||
val
|
||||
}
|
||||
|
||||
fn print_all(&self) {
|
||||
let mut map: BTreeMap<String, String> = BTreeMap::new();
|
||||
if !self.persist_only {
|
||||
SYS_PROP.for_each(&mut PropReader::List(&mut map));
|
||||
}
|
||||
if self.persist || self.persist_only {
|
||||
persist_get_all_props(&mut PropReader::List(&mut map)).log_ok();
|
||||
}
|
||||
for (mut k, v) in map.into_iter() {
|
||||
if self.context {
|
||||
println!(
|
||||
"[{k}]: [{}]",
|
||||
SYS_PROP.get_context(Utf8CStr::from_string(&mut k))
|
||||
);
|
||||
} else {
|
||||
println!("[{k}]: [{v}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&self, key: &Utf8CStr, val: &Utf8CStr) {
|
||||
let mut skip_svc = self.skip_svc;
|
||||
let mut info = SYS_PROP.find_mut(key);
|
||||
|
||||
// Delete existing read-only properties if they are or will be long properties,
|
||||
// which cannot directly go through __system_property_update
|
||||
if key.starts_with("ro.") {
|
||||
skip_svc = true;
|
||||
if let Some(pi) = &info
|
||||
&& (pi.is_long() || val.len() >= PROP_VALUE_MAX as usize)
|
||||
{
|
||||
// Skip pruning nodes as we will add it back ASAP
|
||||
SYS_PROP.delete(key, false);
|
||||
info = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
let msg = if skip_svc {
|
||||
"direct modification"
|
||||
} else {
|
||||
"property_service"
|
||||
};
|
||||
|
||||
if let Some(pi) = info {
|
||||
if skip_svc {
|
||||
pi.update(val);
|
||||
} else {
|
||||
SYS_PROP.set(key, val);
|
||||
}
|
||||
debug!("resetprop: update prop [{key}]=[{val}] by {msg}");
|
||||
} else {
|
||||
if skip_svc {
|
||||
SYS_PROP.add(key, val);
|
||||
} else {
|
||||
SYS_PROP.set(key, val);
|
||||
}
|
||||
debug!("resetprop: create prop [{key}]=[{val}] by {msg}");
|
||||
}
|
||||
|
||||
// When bypassing property_service, persistent props won't be stored in storage.
|
||||
// Explicitly handle this situation.
|
||||
if skip_svc && self.persist && key.starts_with("persist.") {
|
||||
persist_set_prop(key, val).log_ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(&self, key: &Utf8CStr) -> bool {
|
||||
debug!("resetprop: delete prop [{key}]");
|
||||
let mut ret = false;
|
||||
ret |= SYS_PROP.delete(key, true);
|
||||
if self.persist && key.starts_with("persist.") {
|
||||
ret |= persist_delete_prop(key).is_ok()
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn wait(&self) {
|
||||
let key = &self.args[0];
|
||||
let val = self.args.get(1).map(|s| &**s);
|
||||
|
||||
// Find PropInfo
|
||||
let info: &PropInfo;
|
||||
loop {
|
||||
let i = SYS_PROP.find(key);
|
||||
if let Some(i) = i {
|
||||
info = i;
|
||||
break;
|
||||
} else {
|
||||
debug!("resetprop: waiting for prop [{key}] to exist");
|
||||
let mut serial = SYS_PROP.area_serial();
|
||||
SYS_PROP.wait(None, serial, &mut serial);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(val) = val {
|
||||
let mut curr_val = String::new();
|
||||
let mut serial = 0;
|
||||
loop {
|
||||
let mut r = PropReader::ValueSerial(&mut curr_val, &mut serial);
|
||||
SYS_PROP.read(info, &mut r);
|
||||
if *val != *curr_val {
|
||||
debug!("resetprop: get prop [{key}]=[{curr_val}]");
|
||||
break;
|
||||
}
|
||||
debug!("resetprop: waiting for prop [{key}]!=[{val}]");
|
||||
SYS_PROP.wait(Some(info), serial, &mut serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_file(&self, file: &Utf8CStr) -> LoggedResult<()> {
|
||||
let fd = file.open(libc::O_RDONLY | libc::O_CLOEXEC)?;
|
||||
let mut key = cstr::buf::dynamic(128);
|
||||
let mut val = cstr::buf::dynamic(128);
|
||||
BufReader::new(fd).for_each_prop(|k, v| {
|
||||
key.clear();
|
||||
val.clear();
|
||||
key.push_str(k);
|
||||
val.push_str(v);
|
||||
self.set(&key, &val);
|
||||
true
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(self) -> LoggedResult<()> {
|
||||
if self.wait_mode {
|
||||
self.wait();
|
||||
} else if let Some(file) = &self.file {
|
||||
self.load_file(file)?;
|
||||
} else if let Some(key) = &self.delete_key {
|
||||
if !self.delete(key) {
|
||||
return log_err!();
|
||||
}
|
||||
} else {
|
||||
match self.args.len() {
|
||||
0 => self.print_all(),
|
||||
1 => {
|
||||
if let Some(val) = self.get(&self.args[0]) {
|
||||
println!("{val}");
|
||||
} else {
|
||||
return log_err!();
|
||||
}
|
||||
}
|
||||
2 => self.set(&self.args[0], &self.args[1]),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resetprop_main(argc: i32, argv: *mut *mut c_char) -> i32 {
|
||||
set_log_level_state(LogLevel::Debug, false);
|
||||
let cmds = CmdArgs::new(argc, argv.cast());
|
||||
let cmds = cmds.as_slice();
|
||||
|
||||
let cli = ResetProp::from_args(&[cmds[0]], &cmds[1..])
|
||||
.and_then(|cli| {
|
||||
let mut special_mode = 0;
|
||||
if cli.wait_mode {
|
||||
if cli.args.is_empty() {
|
||||
let mut missing = MissingRequirements::default();
|
||||
missing.missing_positional_arg("NAME");
|
||||
missing.err_on_any()?;
|
||||
}
|
||||
special_mode += 1;
|
||||
}
|
||||
if cli.file.is_some() {
|
||||
special_mode += 1;
|
||||
}
|
||||
if cli.delete_key.is_some() {
|
||||
special_mode += 1;
|
||||
}
|
||||
if special_mode > 1 {
|
||||
return Err(EarlyExit::from(
|
||||
"Multiple operation mode detected!\n".to_string(),
|
||||
));
|
||||
}
|
||||
if cli.args.len() > 2 {
|
||||
return Err(EarlyExit::from(format!(
|
||||
"Unrecognized argument: {}\n",
|
||||
cli.args[2]
|
||||
)));
|
||||
}
|
||||
Ok(cli)
|
||||
})
|
||||
.on_early_exit(|| print_usage(cmds[0]));
|
||||
|
||||
if cli.verbose {
|
||||
set_log_level_state(LogLevel::Debug, true);
|
||||
}
|
||||
|
||||
if cli.run().is_ok() { 0 } else { 1 }
|
||||
}
|
||||
|
||||
// Magisk's own helper functions
|
||||
|
||||
pub fn set_prop(key: &Utf8CStr, val: &Utf8CStr) {
|
||||
let prop = ResetProp {
|
||||
// All Magisk's internal usage should skip property_service
|
||||
skip_svc: true,
|
||||
..Default::default()
|
||||
};
|
||||
prop.set(key, val);
|
||||
}
|
||||
|
||||
pub fn load_prop_file(file: &Utf8CStr) {
|
||||
let prop = ResetProp {
|
||||
// All Magisk's internal usage should skip property_service
|
||||
skip_svc: true,
|
||||
..Default::default()
|
||||
};
|
||||
prop.load_file(file).ok();
|
||||
}
|
||||
|
||||
pub fn get_prop(key: &Utf8CStr) -> String {
|
||||
let prop = ResetProp {
|
||||
persist: key.starts_with("persist."),
|
||||
..Default::default()
|
||||
};
|
||||
prop.get(key).unwrap_or_default()
|
||||
}
|
||||
181
native/src/core/resetprop/mod.rs
Normal file
181
native/src/core/resetprop/mod.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use base::libc::c_char;
|
||||
use base::{Utf8CStr, libc};
|
||||
pub use cli::{get_prop, load_prop_file, resetprop_main, set_prop};
|
||||
use libc::timespec;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
mod cli;
|
||||
mod persist;
|
||||
mod proto;
|
||||
|
||||
static SYS_PROP: LazyLock<SysProp> = LazyLock::new(|| unsafe { get_sys_prop() });
|
||||
|
||||
#[repr(C)]
|
||||
struct PropInfo {
|
||||
_private: cxx::private::Opaque,
|
||||
}
|
||||
|
||||
type CharPtr = *const c_char;
|
||||
type ReadCallback = unsafe extern "C" fn(&mut PropReader, CharPtr, CharPtr, u32);
|
||||
type ForEachCallback = unsafe extern "C" fn(&PropInfo, &mut PropReader);
|
||||
|
||||
enum PropReader<'a> {
|
||||
Value(&'a mut String),
|
||||
ValueSerial(&'a mut String, &'a mut u32),
|
||||
List(&'a mut BTreeMap<String, String>),
|
||||
}
|
||||
|
||||
impl PropReader<'_> {
|
||||
fn put_cstr(&mut self, key: CharPtr, val: CharPtr, serial: u32) {
|
||||
let key = unsafe { CStr::from_ptr(key) };
|
||||
let val = unsafe { CStr::from_ptr(val) };
|
||||
match self {
|
||||
PropReader::Value(v) => {
|
||||
**v = String::from_utf8_lossy(val.to_bytes()).into_owned();
|
||||
}
|
||||
PropReader::ValueSerial(v, s) => {
|
||||
**v = String::from_utf8_lossy(val.to_bytes()).into_owned();
|
||||
**s = serial;
|
||||
}
|
||||
PropReader::List(map) => {
|
||||
map.insert(
|
||||
String::from_utf8_lossy(key.to_bytes()).into_owned(),
|
||||
String::from_utf8_lossy(val.to_bytes()).into_owned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn put_str(&mut self, key: String, val: String, serial: u32) {
|
||||
match self {
|
||||
PropReader::Value(v) => {
|
||||
**v = val;
|
||||
}
|
||||
PropReader::ValueSerial(v, s) => {
|
||||
**v = val;
|
||||
**s = serial;
|
||||
}
|
||||
PropReader::List(map) => {
|
||||
map.insert(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
// SAFETY: the improper_ctypes warning is about PropReader. We only pass PropReader
|
||||
// to C functions as raw pointers, and all actual usage happens on the Rust side.
|
||||
#[allow(improper_ctypes)]
|
||||
fn get_sys_prop() -> SysProp;
|
||||
|
||||
fn prop_info_is_long(info: &PropInfo) -> bool;
|
||||
#[link_name = "__system_property_find2"]
|
||||
fn sys_prop_find(key: CharPtr) -> Option<&'static mut PropInfo>;
|
||||
#[link_name = "__system_property_update2"]
|
||||
fn sys_prop_update(info: &mut PropInfo, val: CharPtr, val_len: u32) -> i32;
|
||||
#[link_name = "__system_property_add2"]
|
||||
fn sys_prop_add(key: CharPtr, key_len: u32, val: CharPtr, val_len: u32) -> i32;
|
||||
#[link_name = "__system_property_delete"]
|
||||
fn sys_prop_delete(key: CharPtr, prune: bool) -> i32;
|
||||
#[link_name = "__system_property_get_context"]
|
||||
fn sys_prop_get_context(key: CharPtr) -> CharPtr;
|
||||
#[link_name = "__system_property_area_serial2"]
|
||||
fn sys_prop_area_serial() -> u32;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct SysProp {
|
||||
set: unsafe extern "C" fn(CharPtr, CharPtr) -> i32,
|
||||
find: unsafe extern "C" fn(CharPtr) -> Option<&'static PropInfo>,
|
||||
read_callback: unsafe extern "C" fn(&PropInfo, ReadCallback, &mut PropReader) -> i32,
|
||||
foreach: unsafe extern "C" fn(ForEachCallback, &mut PropReader) -> i32,
|
||||
wait: unsafe extern "C" fn(Option<&PropInfo>, u32, &mut u32, *const timespec) -> i32,
|
||||
}
|
||||
|
||||
// Safe abstractions over raw C APIs
|
||||
|
||||
impl PropInfo {
|
||||
fn read(&self, reader: &mut PropReader) {
|
||||
SYS_PROP.read(self, reader);
|
||||
}
|
||||
|
||||
fn update(&mut self, val: &Utf8CStr) {
|
||||
SYS_PROP.update(self, val);
|
||||
}
|
||||
|
||||
fn is_long(&self) -> bool {
|
||||
unsafe { prop_info_is_long(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl SysProp {
|
||||
fn read(&self, info: &PropInfo, reader: &mut PropReader) {
|
||||
unsafe extern "C" fn read_fn(r: &mut PropReader, key: CharPtr, val: CharPtr, serial: u32) {
|
||||
r.put_cstr(key, val, serial);
|
||||
}
|
||||
unsafe {
|
||||
(self.read_callback)(info, read_fn, reader);
|
||||
}
|
||||
}
|
||||
|
||||
fn find(&self, key: &Utf8CStr) -> Option<&'static PropInfo> {
|
||||
unsafe { (self.find)(key.as_ptr()) }
|
||||
}
|
||||
|
||||
fn find_mut(&self, key: &Utf8CStr) -> Option<&'static mut PropInfo> {
|
||||
unsafe { sys_prop_find(key.as_ptr()) }
|
||||
}
|
||||
|
||||
fn set(&self, key: &Utf8CStr, val: &Utf8CStr) {
|
||||
unsafe {
|
||||
(self.set)(key.as_ptr(), val.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&self, key: &Utf8CStr, val: &Utf8CStr) {
|
||||
unsafe {
|
||||
sys_prop_add(
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
val.as_ptr(),
|
||||
val.len() as u32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&self, info: &mut PropInfo, val: &Utf8CStr) {
|
||||
unsafe {
|
||||
sys_prop_update(info, val.as_ptr(), val.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(&self, key: &Utf8CStr, prune: bool) -> bool {
|
||||
unsafe { sys_prop_delete(key.as_ptr(), prune) == 0 }
|
||||
}
|
||||
|
||||
fn for_each(&self, reader: &mut PropReader) {
|
||||
unsafe extern "C" fn for_each_fn(info: &PropInfo, vals: &mut PropReader) {
|
||||
SYS_PROP.read(info, vals);
|
||||
}
|
||||
unsafe {
|
||||
(self.foreach)(for_each_fn, reader);
|
||||
}
|
||||
}
|
||||
|
||||
fn wait(&self, info: Option<&PropInfo>, old_serial: u32, new_serial: &mut u32) {
|
||||
unsafe {
|
||||
(self.wait)(info, old_serial, new_serial, ptr::null());
|
||||
}
|
||||
}
|
||||
|
||||
fn get_context(&self, key: &Utf8CStr) -> &'static Utf8CStr {
|
||||
unsafe { Utf8CStr::from_ptr_unchecked(sys_prop_get_context(key.as_ptr())) }
|
||||
}
|
||||
|
||||
fn area_serial(&self) -> u32 {
|
||||
unsafe { sys_prop_area_serial() }
|
||||
}
|
||||
}
|
||||
@@ -2,64 +2,39 @@ use std::io::Read;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufWriter, Write},
|
||||
ops::{Deref, DerefMut},
|
||||
os::fd::FromRawFd,
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
|
||||
|
||||
use crate::ffi::{PropCb, prop_cb_exec};
|
||||
use crate::resetprop::PropReader;
|
||||
use crate::resetprop::proto::persistent_properties::{
|
||||
PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord,
|
||||
};
|
||||
use base::const_format::concatcp;
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{
|
||||
Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentResultExt, Utf8CStr,
|
||||
Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp,
|
||||
Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentLogExt, Utf8CStr,
|
||||
Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, log_err,
|
||||
};
|
||||
|
||||
const PERSIST_PROP_DIR: &str = "/data/property";
|
||||
const PERSIST_PROP: &str = concatcp!(PERSIST_PROP_DIR, "/persistent_properties");
|
||||
|
||||
trait PropCbExec {
|
||||
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr);
|
||||
}
|
||||
|
||||
impl PropCbExec for Pin<&mut PropCb> {
|
||||
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr) {
|
||||
prop_cb_exec(self.as_mut(), name, value, u32::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PersistentProperties {
|
||||
type Target = Vec<PersistentPropertyRecord>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.properties
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for PersistentProperties {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.properties
|
||||
}
|
||||
}
|
||||
|
||||
trait PropExt {
|
||||
fn find_index(&self, name: &Utf8CStr) -> Result<usize, usize>;
|
||||
fn find(&mut self, name: &Utf8CStr) -> LoggedResult<&mut PersistentPropertyRecord>;
|
||||
fn find(self, name: &Utf8CStr) -> Option<PersistentPropertyRecord>;
|
||||
}
|
||||
|
||||
impl PropExt for PersistentProperties {
|
||||
fn find_index(&self, name: &Utf8CStr) -> Result<usize, usize> {
|
||||
self.binary_search_by(|p| p.name.as_deref().cmp(&Some(name.deref())))
|
||||
self.properties
|
||||
.binary_search_by(|p| p.name.as_deref().cmp(&Some(name.as_str())))
|
||||
}
|
||||
|
||||
fn find(&mut self, name: &Utf8CStr) -> LoggedResult<&mut PersistentPropertyRecord> {
|
||||
let idx = self.find_index(name).silent()?;
|
||||
Ok(&mut self[idx])
|
||||
fn find(self, name: &Utf8CStr) -> Option<PersistentPropertyRecord> {
|
||||
let idx = self.find_index(name).ok()?;
|
||||
self.properties.into_iter().nth(idx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +85,9 @@ fn proto_read_props() -> LoggedResult<PersistentProperties> {
|
||||
let mut r = BytesReader::from_bytes(m);
|
||||
let mut props = PersistentProperties::from_reader(&mut r, m)?;
|
||||
// Keep the list sorted for binary search
|
||||
props.sort_unstable_by(|a, b| a.name.cmp(&b.name));
|
||||
props
|
||||
.properties
|
||||
.sort_unstable_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(props)
|
||||
}
|
||||
|
||||
@@ -130,88 +107,80 @@ fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn persist_get_prop(name: &Utf8CStr, mut prop_cb: Pin<&mut PropCb>) {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
let prop = props.find(name)?;
|
||||
pub(super) fn persist_get_prop(key: &Utf8CStr) -> LoggedResult<String> {
|
||||
if check_proto() {
|
||||
let props = proto_read_props()?;
|
||||
let prop = props.find(key).silent()?;
|
||||
if let PersistentPropertyRecord {
|
||||
name: Some(_),
|
||||
value: Some(v),
|
||||
} = prop
|
||||
{
|
||||
return Ok(v);
|
||||
}
|
||||
} else {
|
||||
let value = file_get_prop(key)?;
|
||||
debug!("resetprop: get persist prop [{}]=[{}]", key, value);
|
||||
return Ok(value);
|
||||
}
|
||||
log_err!()
|
||||
}
|
||||
|
||||
pub(super) fn persist_get_all_props(reader: &mut PropReader) -> LoggedResult<()> {
|
||||
if check_proto() {
|
||||
let props = proto_read_props()?;
|
||||
props.properties.into_iter().for_each(|prop| {
|
||||
if let PersistentPropertyRecord {
|
||||
name: Some(n),
|
||||
value: Some(v),
|
||||
} = prop
|
||||
{
|
||||
prop_cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v));
|
||||
reader.put_str(n, v, 0);
|
||||
}
|
||||
} else {
|
||||
let mut value = file_get_prop(name)?;
|
||||
prop_cb.exec(name, Utf8CStr::from_string(&mut value));
|
||||
debug!("resetprop: found prop [{}] = [{}]", name, value);
|
||||
}
|
||||
};
|
||||
res.ok();
|
||||
}
|
||||
|
||||
pub fn persist_get_props(mut prop_cb: Pin<&mut PropCb>) {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
props.iter_mut().for_each(|prop| {
|
||||
if let PersistentPropertyRecord {
|
||||
name: Some(n),
|
||||
value: Some(v),
|
||||
} = prop
|
||||
{
|
||||
prop_cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?;
|
||||
dir.pre_order_walk(|e| {
|
||||
if e.is_file()
|
||||
&& let Ok(mut value) = file_get_prop(e.name())
|
||||
{
|
||||
prop_cb.exec(e.name(), Utf8CStr::from_string(&mut value));
|
||||
}
|
||||
// Do not traverse recursively
|
||||
Ok(WalkResult::Skip)
|
||||
})?;
|
||||
}
|
||||
};
|
||||
res.ok();
|
||||
}
|
||||
|
||||
pub fn persist_delete_prop(name: &Utf8CStr) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
let idx = props.find_index(name).silent()?;
|
||||
props.remove(idx);
|
||||
proto_write_props(&props)?;
|
||||
} else {
|
||||
file_set_prop(name, None)?;
|
||||
}
|
||||
};
|
||||
res.is_ok()
|
||||
}
|
||||
|
||||
pub fn persist_set_prop(name: &Utf8CStr, value: &Utf8CStr) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
match props.find_index(name) {
|
||||
Ok(idx) => props[idx].value = Some(value.to_string()),
|
||||
Err(idx) => props.insert(
|
||||
idx,
|
||||
PersistentPropertyRecord {
|
||||
name: Some(name.to_string()),
|
||||
value: Some(value.to_string()),
|
||||
},
|
||||
),
|
||||
});
|
||||
} else {
|
||||
let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?;
|
||||
dir.pre_order_walk(|e| {
|
||||
if e.is_file()
|
||||
&& let Ok(value) = file_get_prop(e.name())
|
||||
{
|
||||
reader.put_str(e.name().to_string(), value, 0);
|
||||
}
|
||||
proto_write_props(&props)?;
|
||||
} else {
|
||||
file_set_prop(name, Some(value))?;
|
||||
}
|
||||
};
|
||||
res.is_ok()
|
||||
// Do not traverse recursively
|
||||
Ok(WalkResult::Skip)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn persist_delete_prop(key: &Utf8CStr) -> LoggedResult<()> {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
let idx = props.find_index(key).silent()?;
|
||||
props.properties.remove(idx);
|
||||
proto_write_props(&props)?;
|
||||
} else {
|
||||
file_set_prop(key, None)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn persist_set_prop(key: &Utf8CStr, val: &Utf8CStr) -> LoggedResult<()> {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
match props.find_index(key) {
|
||||
Ok(idx) => props.properties[idx].value = Some(val.to_string()),
|
||||
Err(idx) => props.properties.insert(
|
||||
idx,
|
||||
PersistentPropertyRecord {
|
||||
name: Some(key.to_string()),
|
||||
value: Some(val.to_string()),
|
||||
},
|
||||
),
|
||||
}
|
||||
proto_write_props(&props)?;
|
||||
} else {
|
||||
file_set_prop(key, Some(val))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,459 +0,0 @@
|
||||
#include <dlfcn.h>
|
||||
#include <sys/types.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
#include <resetprop.hpp>
|
||||
|
||||
#include <api/system_properties.h>
|
||||
#include <system_properties/prop_info.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef APPLET_STUB_MAIN
|
||||
#define system_property_set __system_property_set
|
||||
#define system_property_read(...)
|
||||
#define system_property_find __system_property_find
|
||||
#define system_property_read_callback __system_property_read_callback
|
||||
#define system_property_foreach __system_property_foreach
|
||||
#define system_property_wait __system_property_wait
|
||||
#else
|
||||
static int (*system_property_set)(const char*, const char*);
|
||||
static int (*system_property_read)(const prop_info*, char*, char*);
|
||||
static const prop_info *(*system_property_find)(const char*);
|
||||
static void (*system_property_read_callback)(
|
||||
const prop_info*, void (*)(void*, const char*, const char*, uint32_t), void*);
|
||||
static int (*system_property_foreach)(void (*)(const prop_info*, void*), void*);
|
||||
static bool (*system_property_wait)(const prop_info*, uint32_t, uint32_t*, const struct timespec*);
|
||||
#endif
|
||||
|
||||
struct PropFlags {
|
||||
void setSkipSvc() { flags |= 1; }
|
||||
void setPersist() { flags |= (1 << 1); }
|
||||
void setContext() { flags |= (1 << 2); }
|
||||
void setPersistOnly() { flags |= (1 << 3); setPersist(); }
|
||||
void setWait() { flags |= (1 << 4); }
|
||||
bool isSkipSvc() const { return flags & 1; }
|
||||
bool isPersist() const { return flags & (1 << 1); }
|
||||
bool isContext() const { return flags & (1 << 2); }
|
||||
bool isPersistOnly() const { return flags & (1 << 3); }
|
||||
bool isWait() const { return flags & (1 << 4); }
|
||||
private:
|
||||
uint32_t flags = 0;
|
||||
};
|
||||
|
||||
[[noreturn]] static void usage(char* arg0) {
|
||||
fprintf(stderr,
|
||||
R"EOF(resetprop - System Property Manipulation Tool
|
||||
|
||||
Usage: %s [flags] [arguments...]
|
||||
|
||||
Read mode arguments:
|
||||
(no arguments) print all properties
|
||||
NAME get property of NAME
|
||||
|
||||
Write mode arguments:
|
||||
NAME VALUE set property NAME as VALUE
|
||||
-f,--file FILE load and set properties from FILE
|
||||
-d,--delete NAME delete property
|
||||
|
||||
Wait mode arguments (toggled with -w):
|
||||
NAME wait until property NAME changes
|
||||
NAME OLD_VALUE if value of property NAME is not OLD_VALUE, get value
|
||||
or else wait until property NAME changes
|
||||
|
||||
General flags:
|
||||
-h,--help show this message
|
||||
-v print verbose output to stderr
|
||||
-w switch to wait mode
|
||||
|
||||
Read mode flags:
|
||||
-p also read persistent props from storage
|
||||
-P only read persistent props from storage
|
||||
-Z get property context instead of value
|
||||
|
||||
Write mode flags:
|
||||
-n set properties bypassing property_service
|
||||
-p always write persistent prop changes to storage
|
||||
|
||||
)EOF", arg0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static bool check_legal_property_name(const char *name) {
|
||||
int namelen = strlen(name);
|
||||
|
||||
if (namelen < 1) goto illegal;
|
||||
if (name[0] == '.') goto illegal;
|
||||
if (name[namelen - 1] == '.') goto illegal;
|
||||
|
||||
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
|
||||
/* Don't allow ".." to appear in a property name */
|
||||
for (size_t i = 0; i < namelen; i++) {
|
||||
if (name[i] == '.') {
|
||||
// i=0 is guaranteed to never have a dot. See above.
|
||||
if (name[i-1] == '.') goto illegal;
|
||||
continue;
|
||||
}
|
||||
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
|
||||
if (name[i] >= 'a' && name[i] <= 'z') continue;
|
||||
if (name[i] >= 'A' && name[i] <= 'Z') continue;
|
||||
if (name[i] >= '0' && name[i] <= '9') continue;
|
||||
goto illegal;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
illegal:
|
||||
LOGE("Illegal property name: [%s]\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void read_prop_with_cb(const prop_info *pi, void *cb) {
|
||||
if (system_property_read_callback) {
|
||||
auto callback = [](void *cb, const char *name, const char *value, uint32_t serial) {
|
||||
static_cast<prop_cb*>(cb)->exec(name, value, serial);
|
||||
};
|
||||
system_property_read_callback(pi, callback, cb);
|
||||
} else {
|
||||
char name[PROP_NAME_MAX];
|
||||
char value[PROP_VALUE_MAX];
|
||||
name[0] = '\0';
|
||||
value[0] = '\0';
|
||||
system_property_read(pi, name, value);
|
||||
static_cast<prop_cb*>(cb)->exec(name, value, pi->serial);
|
||||
}
|
||||
}
|
||||
|
||||
template<class StringType>
|
||||
struct prop_to_string : prop_cb {
|
||||
void exec(const char *, const char *value, uint32_t s) override {
|
||||
val = value;
|
||||
serial = s;
|
||||
}
|
||||
StringType val;
|
||||
uint32_t serial;
|
||||
};
|
||||
|
||||
template<> void prop_to_string<rust::String>::exec(const char *, const char *value, uint32_t s) {
|
||||
// We do not want to crash when values are not UTF-8
|
||||
val = rust::String::lossy(value);
|
||||
serial = s;
|
||||
}
|
||||
|
||||
static int set_prop(const char *name, const char *value, PropFlags flags) {
|
||||
if (!check_legal_property_name(name))
|
||||
return 1;
|
||||
|
||||
auto pi = const_cast<prop_info *>(__system_property_find(name));
|
||||
|
||||
// Delete existing read-only properties if they are or will be long properties,
|
||||
// which cannot directly go through __system_property_update
|
||||
if (str_starts(name, "ro.")) {
|
||||
if (pi != nullptr && (pi->is_long() || strlen(value) >= PROP_VALUE_MAX)) {
|
||||
// Skip pruning nodes as we will add it back ASAP
|
||||
__system_property_delete(name, false);
|
||||
pi = nullptr;
|
||||
}
|
||||
flags.setSkipSvc();
|
||||
}
|
||||
|
||||
const char *msg = flags.isSkipSvc() ? "direct modification" : "property_service";
|
||||
|
||||
int ret;
|
||||
if (pi != nullptr) {
|
||||
if (flags.isSkipSvc()) {
|
||||
ret = __system_property_update(pi, value, strlen(value));
|
||||
} else {
|
||||
ret = system_property_set(name, value);
|
||||
}
|
||||
LOGD("resetprop: update prop [%s]: [%s] by %s\n", name, value, msg);
|
||||
} else {
|
||||
if (flags.isSkipSvc()) {
|
||||
ret = __system_property_add(name, strlen(name), value, strlen(value));
|
||||
} else {
|
||||
ret = system_property_set(name, value);
|
||||
}
|
||||
LOGD("resetprop: create prop [%s]: [%s] by %s\n", name, value, msg);
|
||||
}
|
||||
|
||||
// When bypassing property_service, persistent props won't be stored in storage.
|
||||
// Explicitly handle this situation.
|
||||
if (ret == 0 && flags.isSkipSvc() && flags.isPersist() && str_starts(name, "persist.")) {
|
||||
ret = persist_set_prop(name, value) ? 0 : 1;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
LOGW("resetprop: set prop error\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<class StringType>
|
||||
static StringType get_prop(const char *name, PropFlags flags) {
|
||||
if (!check_legal_property_name(name))
|
||||
return {};
|
||||
|
||||
prop_to_string<StringType> cb;
|
||||
|
||||
if (flags.isContext()) {
|
||||
auto context = __system_property_get_context(name) ?: "";
|
||||
LOGD("resetprop: prop context [%s]: [%s]\n", name, context);
|
||||
cb.exec(name, context, -1);
|
||||
return cb.val;
|
||||
}
|
||||
|
||||
if (!flags.isPersistOnly()) {
|
||||
if (auto pi = system_property_find(name)) {
|
||||
read_prop_with_cb(pi, &cb);
|
||||
LOGD("resetprop: get prop [%s]: [%s]\n", name, cb.val.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (cb.val.empty() && flags.isPersist() && str_starts(name, "persist."))
|
||||
persist_get_prop(name, cb);
|
||||
if (cb.val.empty())
|
||||
LOGD("resetprop: prop [%s] does not exist\n", name);
|
||||
|
||||
return cb.val;
|
||||
}
|
||||
|
||||
template<class StringType>
|
||||
static StringType wait_prop(const char *name, const char *old_value) {
|
||||
if (!check_legal_property_name(name))
|
||||
return {};
|
||||
|
||||
const prop_info *pi;
|
||||
auto serial = __system_property_area_serial();
|
||||
while (!(pi = system_property_find(name))) {
|
||||
LOGD("resetprop: waiting for prop [%s] to exist\n", name);
|
||||
system_property_wait(nullptr, serial, &serial, nullptr);
|
||||
}
|
||||
|
||||
prop_to_string<StringType> cb;
|
||||
read_prop_with_cb(pi, &cb);
|
||||
|
||||
while (old_value == nullptr || cb.val == old_value) {
|
||||
LOGD("resetprop: waiting for prop [%s]\n", name);
|
||||
uint32_t new_serial;
|
||||
system_property_wait(pi, cb.serial, &new_serial, nullptr);
|
||||
read_prop_with_cb(pi, &cb);
|
||||
if (old_value == nullptr) break;
|
||||
}
|
||||
|
||||
LOGD("resetprop: get prop [%s]: [%s]\n", name, cb.val.c_str());
|
||||
return cb.val;
|
||||
}
|
||||
|
||||
static void print_props(PropFlags flags) {
|
||||
prop_list list;
|
||||
prop_collector collector(list);
|
||||
if (!flags.isPersistOnly())
|
||||
system_property_foreach(read_prop_with_cb, &collector);
|
||||
if (flags.isPersist())
|
||||
persist_get_props(collector);
|
||||
for (auto &[key, val] : list) {
|
||||
const char *v = flags.isContext() ?
|
||||
(__system_property_get_context(key.data()) ?: "") :
|
||||
val.data();
|
||||
printf("[%s]: [%s]\n", key.data(), v);
|
||||
}
|
||||
}
|
||||
|
||||
static int delete_prop(const char *name, PropFlags flags) {
|
||||
if (!check_legal_property_name(name))
|
||||
return 1;
|
||||
|
||||
LOGD("resetprop: delete prop [%s]\n", name);
|
||||
|
||||
int ret = __system_property_delete(name, true);
|
||||
if (flags.isPersist() && str_starts(name, "persist.")) {
|
||||
if (persist_delete_prop(name))
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void load_file(const char *filename, PropFlags flags) {
|
||||
LOGD("resetprop: Parse prop file [%s]\n", filename);
|
||||
parse_prop_file(filename, [=](auto key, auto val) -> bool {
|
||||
set_prop(key.data(), val.data(), flags);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
struct Initialize {
|
||||
Initialize() {
|
||||
#ifndef APPLET_STUB_MAIN
|
||||
#define DLOAD(name) (*(void **) &name = dlsym(RTLD_DEFAULT, "__" #name))
|
||||
// Load platform implementations
|
||||
DLOAD(system_property_set);
|
||||
DLOAD(system_property_read);
|
||||
DLOAD(system_property_find);
|
||||
DLOAD(system_property_read_callback);
|
||||
DLOAD(system_property_foreach);
|
||||
DLOAD(system_property_wait);
|
||||
#undef DLOAD
|
||||
if (system_property_wait == nullptr) {
|
||||
// The platform API only exist on API 26+
|
||||
system_property_wait = __system_property_wait;
|
||||
}
|
||||
#endif
|
||||
if (__system_properties_init()) {
|
||||
LOGE("resetprop: __system_properties_init error\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void InitOnce() {
|
||||
static struct Initialize init;
|
||||
}
|
||||
|
||||
#define consume_next(val) \
|
||||
if (argc != 2) usage(argv0); \
|
||||
val = argv[1]; \
|
||||
stop_parse = true; \
|
||||
|
||||
int resetprop_main(int argc, char *argv[]) {
|
||||
PropFlags flags;
|
||||
char *argv0 = argv[0];
|
||||
set_log_level_state(LogLevel::Debug, false);
|
||||
|
||||
const char *prop_file = nullptr;
|
||||
const char *prop_to_rm = nullptr;
|
||||
|
||||
--argc;
|
||||
++argv;
|
||||
|
||||
// Parse flags and -- options
|
||||
while (argc && argv[0][0] == '-') {
|
||||
bool stop_parse = false;
|
||||
for (int idx = 1; true; ++idx) {
|
||||
switch (argv[0][idx]) {
|
||||
case '-':
|
||||
if (argv[0] == "--file"sv) {
|
||||
consume_next(prop_file);
|
||||
} else if (argv[0] == "--delete"sv) {
|
||||
consume_next(prop_to_rm);
|
||||
} else {
|
||||
usage(argv0);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
consume_next(prop_to_rm);
|
||||
continue;
|
||||
case 'f':
|
||||
consume_next(prop_file);
|
||||
continue;
|
||||
case 'n':
|
||||
flags.setSkipSvc();
|
||||
continue;
|
||||
case 'p':
|
||||
flags.setPersist();
|
||||
continue;
|
||||
case 'P':
|
||||
flags.setPersistOnly();
|
||||
continue;
|
||||
case 'v':
|
||||
set_log_level_state(LogLevel::Debug, true);
|
||||
continue;
|
||||
case 'Z':
|
||||
flags.setContext();
|
||||
continue;
|
||||
case 'w':
|
||||
flags.setWait();
|
||||
continue;
|
||||
case '\0':
|
||||
break;
|
||||
default:
|
||||
usage(argv0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
--argc;
|
||||
++argv;
|
||||
if (stop_parse)
|
||||
break;
|
||||
}
|
||||
|
||||
InitOnce();
|
||||
|
||||
if (prop_to_rm) {
|
||||
return delete_prop(prop_to_rm, flags);
|
||||
}
|
||||
|
||||
if (prop_file) {
|
||||
load_file(prop_file, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags.isWait()) {
|
||||
if (argc == 0) usage(argv0);
|
||||
auto val = wait_prop<string>(argv[0], argv[1]);
|
||||
if (val.empty())
|
||||
return 1;
|
||||
printf("%s\n", val.data());
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (argc) {
|
||||
case 0:
|
||||
print_props(flags);
|
||||
return 0;
|
||||
case 1: {
|
||||
auto val = get_prop<string>(argv[0], flags);
|
||||
if (val.empty())
|
||||
return 1;
|
||||
printf("%s\n", val.data());
|
||||
return 0;
|
||||
}
|
||||
case 2:
|
||||
return set_prop(argv[0], argv[1], flags);
|
||||
default:
|
||||
usage(argv0);
|
||||
}
|
||||
}
|
||||
|
||||
/***************
|
||||
* Public APIs
|
||||
****************/
|
||||
|
||||
template<class StringType>
|
||||
static StringType get_prop_impl(const char *name, bool persist) {
|
||||
InitOnce();
|
||||
PropFlags flags;
|
||||
if (persist) flags.setPersist();
|
||||
return get_prop<StringType>(name, flags);
|
||||
}
|
||||
|
||||
rust::String get_prop_rs(rust::Utf8CStr name, bool persist) {
|
||||
return get_prop_impl<rust::String>(name.data(), persist);
|
||||
}
|
||||
|
||||
string get_prop(const char *name, bool persist) {
|
||||
return get_prop_impl<string>(name, persist);
|
||||
}
|
||||
|
||||
int delete_prop(const char *name, bool persist) {
|
||||
InitOnce();
|
||||
PropFlags flags;
|
||||
if (persist) flags.setPersist();
|
||||
return delete_prop(name, flags);
|
||||
}
|
||||
|
||||
int set_prop(const char *name, const char *value, bool skip_svc) {
|
||||
InitOnce();
|
||||
PropFlags flags;
|
||||
if (skip_svc) flags.setSkipSvc();
|
||||
return set_prop(name, value, flags);
|
||||
}
|
||||
|
||||
void load_prop_file(const char *filename, bool skip_svc) {
|
||||
InitOnce();
|
||||
PropFlags flags;
|
||||
if (skip_svc) flags.setSkipSvc();
|
||||
load_file(filename, flags);
|
||||
}
|
||||
55
native/src/core/resetprop/sys.cpp
Normal file
55
native/src/core/resetprop/sys.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
#include <api/system_properties.h>
|
||||
#include <system_properties/prop_info.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// This has to keep in sync with SysProp in mod.rs
|
||||
struct SysProp {
|
||||
int (*set)(const char*, const char*);
|
||||
const prop_info *(*find)(const char*);
|
||||
void (*read_callback)(const prop_info*, void (*)(void*, const char*, const char*, uint32_t), void*);
|
||||
int (*foreach)(void (*)(const prop_info*, void*), void*);
|
||||
bool (*wait)(const prop_info*, uint32_t, uint32_t*, const timespec*);
|
||||
};
|
||||
|
||||
extern "C" bool prop_info_is_long(const prop_info &info) {
|
||||
return info.is_long();
|
||||
}
|
||||
|
||||
extern "C" SysProp get_sys_prop() {
|
||||
SysProp prop{};
|
||||
#ifdef APPLET_STUB_MAIN
|
||||
// Use internal implementation
|
||||
prop.set = __system_property_set;
|
||||
prop.find = __system_property_find;
|
||||
prop.read_callback = __system_property_read_callback;
|
||||
prop.foreach = __system_property_foreach;
|
||||
prop.wait = __system_property_wait;
|
||||
#else
|
||||
#define DLOAD(name) (*(void **) &prop.name = dlsym(RTLD_DEFAULT, "__system_property_" #name))
|
||||
// Dynamic load platform implementation
|
||||
DLOAD(set);
|
||||
DLOAD(find);
|
||||
DLOAD(read_callback);
|
||||
DLOAD(foreach);
|
||||
DLOAD(wait);
|
||||
#undef DLOAD
|
||||
if (prop.wait == nullptr) {
|
||||
// This platform API only exist on API 26+
|
||||
prop.wait = __system_property_wait;
|
||||
}
|
||||
if (prop.read_callback == nullptr) {
|
||||
// This platform API only exist on API 26+
|
||||
prop.read_callback = __system_property_read_callback;
|
||||
}
|
||||
#endif
|
||||
if (__system_properties_init()) {
|
||||
LOGE("resetprop: __system_properties_init error\n");
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ if (pfs) { \
|
||||
exit(0); \
|
||||
}
|
||||
|
||||
void exec_common_scripts(rust::Utf8CStr stage) {
|
||||
void exec_common_scripts(Utf8CStr stage) {
|
||||
LOGI("* Running %s.d scripts\n", stage.c_str());
|
||||
char path[4096];
|
||||
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage.c_str());
|
||||
@@ -116,12 +116,12 @@ static bool operator>(const timespec &a, const timespec &b) {
|
||||
return a.tv_nsec > b.tv_nsec;
|
||||
}
|
||||
|
||||
void exec_module_scripts(rust::Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list) {
|
||||
void exec_module_scripts(Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list) {
|
||||
LOGI("* Running module %s scripts\n", stage.c_str());
|
||||
if (module_list.empty())
|
||||
return;
|
||||
|
||||
bool pfs = (string_view) stage == "post-fs-data";
|
||||
bool pfs = stage == "post-fs-data";
|
||||
if (pfs) {
|
||||
timespec now{};
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
@@ -157,7 +157,7 @@ appops set %s REQUEST_INSTALL_PACKAGES allow
|
||||
rm -f $APK
|
||||
)EOF";
|
||||
|
||||
void install_apk(rust::Utf8CStr apk) {
|
||||
void install_apk(Utf8CStr apk) {
|
||||
setfilecon(apk.c_str(), MAGISK_FILE_CON);
|
||||
char cmds[sizeof(install_script) + 4096];
|
||||
ssprintf(cmds, sizeof(cmds), install_script, apk.c_str(), JAVA_PACKAGE_NAME);
|
||||
@@ -170,7 +170,7 @@ log -t Magisk "pm_uninstall: $PKG"
|
||||
log -t Magisk "pm_uninstall: $(pm uninstall $PKG 2>&1)"
|
||||
)EOF";
|
||||
|
||||
void uninstall_pkg(rust::Utf8CStr pkg) {
|
||||
void uninstall_pkg(Utf8CStr pkg) {
|
||||
char cmds[sizeof(uninstall_script) + 256];
|
||||
ssprintf(cmds, sizeof(cmds), uninstall_script, pkg.c_str());
|
||||
exec_command_async("/system/bin/sh", "-c", cmds);
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use std::os::unix::net::{AncillaryData, SocketAncillary, UnixStream};
|
||||
|
||||
pub trait Encodable {
|
||||
#[allow(dead_code)]
|
||||
fn encoded_len(&self) -> usize;
|
||||
fn encode(&self, w: &mut impl Write) -> io::Result<()>;
|
||||
}
|
||||
@@ -235,11 +236,6 @@ pub fn send_fd(socket: RawFd, fd: RawFd) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_fds(socket: RawFd, fds: &[RawFd]) -> bool {
|
||||
let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) });
|
||||
socket.send_fds(fds).log().is_ok()
|
||||
}
|
||||
|
||||
pub fn recv_fd(socket: RawFd) -> RawFd {
|
||||
let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) });
|
||||
socket
|
||||
|
||||
@@ -6,11 +6,14 @@ use crate::db::{DbSettings, MultiuserMode, RootAccess};
|
||||
use crate::ffi::{SuPolicy, SuRequest, exec_root_shell};
|
||||
use crate::socket::IpcRead;
|
||||
use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn};
|
||||
use std::os::fd::{FromRawFd, IntoRawFd};
|
||||
use std::os::fd::{IntoRawFd, OwnedFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
const DEFAULT_SHELL: &str = "/system/bin/sh";
|
||||
|
||||
impl Default for SuRequest {
|
||||
@@ -111,14 +114,16 @@ impl AccessInfo {
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn su_daemon_handler(&self, client: i32, cred: &UCred) {
|
||||
pub fn su_daemon_handler(&self, client: OwnedFd, cred: &UCred) {
|
||||
let cred = cred.0;
|
||||
debug!(
|
||||
"su: request from uid=[{}], pid=[{}], client=[{}]",
|
||||
cred.uid, cred.pid, client
|
||||
cred.uid,
|
||||
cred.pid,
|
||||
client.as_raw_fd()
|
||||
);
|
||||
|
||||
let mut client = unsafe { UnixStream::from_raw_fd(client) };
|
||||
let mut client = UnixStream::from(client);
|
||||
|
||||
let mut req = match client.read_decodable::<SuRequest>().log() {
|
||||
Ok(req) => req,
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
use base::{error, libc, warn};
|
||||
use libc::{
|
||||
POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH,
|
||||
TIOCGWINSZ, TIOCSWINSZ, cfmakeraw, close, pipe, poll, pollfd, raise, read, sigaddset,
|
||||
sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, splice, tcgetattr, tcsetattr,
|
||||
termios, winsize,
|
||||
use base::libc::ssize_t;
|
||||
use base::{
|
||||
LibcReturn, LoggedResult, OsResult, PipeFd, ReadExt, ResultExt, error, libc, log_err,
|
||||
make_pipe, warn,
|
||||
};
|
||||
use std::{ffi::c_int, mem::MaybeUninit, ptr::null_mut};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use libc::{
|
||||
O_CLOEXEC, POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN,
|
||||
TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, cfmakeraw, close, poll, pollfd, raise, sigaddset,
|
||||
sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, tcgetattr, tcsetattr, termios,
|
||||
winsize,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::{ManuallyDrop, MaybeUninit};
|
||||
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{ffi::c_int, ptr::null_mut};
|
||||
|
||||
static mut OLD_STDIN: Option<termios> = None;
|
||||
static SHOULD_USE_SPLICE: AtomicBool = AtomicBool::new(true);
|
||||
const TIOCGPTN: u32 = 0x80045430;
|
||||
|
||||
unsafe extern "C" {
|
||||
@@ -71,34 +82,55 @@ fn resize_pty(outfd: i32) {
|
||||
}
|
||||
}
|
||||
|
||||
fn pump_via_pipe(infd: i32, outfd: i32, pipe: &[c_int; 2]) -> bool {
|
||||
// usize::MAX will EINVAL in some kernels, use i32::MAX in case
|
||||
let s = unsafe { splice(infd, null_mut(), pipe[1], null_mut(), i32::MAX as _, 0) };
|
||||
if s < 0 {
|
||||
error!("splice error");
|
||||
return false;
|
||||
}
|
||||
if s == 0 {
|
||||
return true;
|
||||
}
|
||||
let s = unsafe { splice(pipe[0], null_mut(), outfd, null_mut(), s as usize, 0) };
|
||||
if s < 0 {
|
||||
error!("splice error");
|
||||
return false;
|
||||
}
|
||||
true
|
||||
fn splice(fd_in: RawFd, fd_out: RawFd, len: usize, flags: u32) -> OsResult<'static, ssize_t> {
|
||||
unsafe { libc::splice(fd_in, null_mut(), fd_out, null_mut(), len, flags) }
|
||||
.as_os_result("splice", None, None)
|
||||
}
|
||||
|
||||
fn pump_via_copy(infd: RawFd, outfd: RawFd) -> LoggedResult<()> {
|
||||
let mut buf = MaybeUninit::<[u8; 4096]>::uninit();
|
||||
let buf = unsafe { buf.assume_init_mut() };
|
||||
let mut infd = ManuallyDrop::new(unsafe { File::from_raw_fd(infd) });
|
||||
let mut outfd = ManuallyDrop::new(unsafe { File::from_raw_fd(outfd) });
|
||||
let len = infd.read(buf)?;
|
||||
outfd.write_all(&buf[..len])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pump_via_splice(infd: RawFd, outfd: RawFd, pipe: &PipeFd) -> LoggedResult<()> {
|
||||
if !SHOULD_USE_SPLICE.load(Ordering::Acquire) {
|
||||
return pump_via_copy(infd, outfd);
|
||||
}
|
||||
|
||||
// The pipe capacity is by default 16 pages, let's just use 65536
|
||||
let Ok(len) = splice(infd, pipe.write.as_raw_fd(), 65536_usize, 0) else {
|
||||
// If splice failed, stop using splice and fallback to userspace copy
|
||||
SHOULD_USE_SPLICE.store(false, Ordering::Release);
|
||||
return pump_via_copy(infd, outfd);
|
||||
};
|
||||
if len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
splice(pipe.read.as_raw_fd(), outfd, len as usize, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
struct SignalFdInfo(signalfd_siginfo);
|
||||
unsafe impl Zeroable for SignalFdInfo {}
|
||||
unsafe impl Pod for SignalFdInfo {}
|
||||
|
||||
pub fn pump_tty(infd: i32, outfd: i32) {
|
||||
set_stdin_raw();
|
||||
|
||||
let sfd = unsafe {
|
||||
let signal_fd = unsafe {
|
||||
let mut mask: sigset_t = std::mem::zeroed();
|
||||
sigemptyset(&mut mask);
|
||||
sigaddset(&mut mask, SIGWINCH);
|
||||
if sigprocmask(SIG_BLOCK, &mask, null_mut()) < 0 {
|
||||
error!("sigprocmask");
|
||||
}
|
||||
sigprocmask(SIG_BLOCK, &mask, null_mut())
|
||||
.check_os_err("sigprocmask", None, None)
|
||||
.log_ok();
|
||||
signalfd(-1, &mask, SFD_CLOEXEC)
|
||||
};
|
||||
|
||||
@@ -116,17 +148,16 @@ pub fn pump_tty(infd: i32, outfd: i32) {
|
||||
revents: 0,
|
||||
},
|
||||
pollfd {
|
||||
fd: sfd,
|
||||
fd: signal_fd,
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
},
|
||||
];
|
||||
|
||||
let mut p: [c_int; 2] = [0; 2];
|
||||
if unsafe { pipe(&mut p as *mut c_int) } < 0 {
|
||||
error!("pipe error");
|
||||
let Ok(pipe_fd) = make_pipe(O_CLOEXEC).log() else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
'poll: loop {
|
||||
let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) };
|
||||
|
||||
@@ -138,22 +169,18 @@ pub fn pump_tty(infd: i32, outfd: i32) {
|
||||
for pfd in &pfds {
|
||||
if pfd.revents & POLLIN != 0 {
|
||||
let res = if pfd.fd == STDIN_FILENO {
|
||||
pump_via_pipe(pfd.fd, outfd, &p)
|
||||
pump_via_splice(STDIN_FILENO, outfd, &pipe_fd)
|
||||
} else if pfd.fd == infd {
|
||||
pump_via_pipe(pfd.fd, STDOUT_FILENO, &p)
|
||||
} else if pfd.fd == sfd {
|
||||
pump_via_splice(infd, STDOUT_FILENO, &pipe_fd)
|
||||
} else if pfd.fd == signal_fd {
|
||||
resize_pty(outfd);
|
||||
let mut buf = [MaybeUninit::<u8>::uninit(); size_of::<signalfd_siginfo>()];
|
||||
if unsafe { read(pfd.fd, buf.as_mut_ptr() as *mut _, buf.len()) } < 0 {
|
||||
error!("read error");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
let mut info = SignalFdInfo::zeroed();
|
||||
let mut fd = ManuallyDrop::new(unsafe { File::from_raw_fd(signal_fd) });
|
||||
fd.read_pod(&mut info).log()
|
||||
} else {
|
||||
false
|
||||
log_err!()
|
||||
};
|
||||
if !res {
|
||||
if res.is_err() {
|
||||
break 'poll;
|
||||
}
|
||||
} else if pfd.revents != 0 && pfd.fd == infd {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 - 2023, John Wu (@topjohnwu)
|
||||
* Copyright 2017 - 2025, John Wu (@topjohnwu)
|
||||
* Copyright 2015, Pierre-Hugues Husson <phh@phh.me>
|
||||
* Copyright 2010, Adam Shanks (@ChainsDD)
|
||||
* Copyright 2008, Zinx Verituse (@zinxv)
|
||||
@@ -76,9 +76,7 @@ static void sighandler(int sig) {
|
||||
close(STDERR_FILENO);
|
||||
|
||||
// Put back all the default handlers
|
||||
struct sigaction act;
|
||||
|
||||
memset(&act, 0, sizeof(act));
|
||||
struct sigaction act{};
|
||||
act.sa_handler = SIG_DFL;
|
||||
for (int i = 0; quit_signals[i]; ++i) {
|
||||
sigaction(quit_signals[i], &act, nullptr);
|
||||
@@ -86,8 +84,7 @@ static void sighandler(int sig) {
|
||||
}
|
||||
|
||||
static void setup_sighandlers(void (*handler)(int)) {
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
struct sigaction act{};
|
||||
act.sa_handler = handler;
|
||||
for (int i = 0; quit_signals[i]; ++i) {
|
||||
sigaction(quit_signals[i], &act, nullptr);
|
||||
@@ -95,8 +92,7 @@ static void setup_sighandlers(void (*handler)(int)) {
|
||||
}
|
||||
|
||||
int su_client_main(int argc, char *argv[]) {
|
||||
int c;
|
||||
struct option long_opts[] = {
|
||||
option long_opts[] = {
|
||||
{ "command", required_argument, nullptr, 'c' },
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "login", no_argument, nullptr, 'l' },
|
||||
@@ -126,6 +122,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
|
||||
bool interactive = false;
|
||||
|
||||
int c;
|
||||
while ((c = getopt_long(argc, argv, "c:hlimpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
|
||||
switch (c) {
|
||||
case 'c': {
|
||||
@@ -191,7 +188,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
fprintf(stderr, "Invalid GID: %s\n", optarg);
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
std::copy(gids.begin(), gids.end(), std::back_inserter(req.gids));
|
||||
ranges::copy(gids, std::back_inserter(req.gids));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -207,19 +204,15 @@ int su_client_main(int argc, char *argv[]) {
|
||||
}
|
||||
/* username or uid */
|
||||
if (optind < argc) {
|
||||
struct passwd *pw;
|
||||
pw = getpwnam(argv[optind]);
|
||||
if (pw)
|
||||
if (const passwd *pw = getpwnam(argv[optind]))
|
||||
req.target_uid = pw->pw_uid;
|
||||
else
|
||||
req.target_uid = parse_int(argv[optind]);
|
||||
optind++;
|
||||
}
|
||||
|
||||
int ptmx, fd;
|
||||
|
||||
// Connect to client
|
||||
fd = connect_daemon(+RequestCode::SUPERUSER);
|
||||
owned_fd fd = connect_daemon(+RequestCode::SUPERUSER);
|
||||
|
||||
// Send request
|
||||
req.write_to_fd(fd);
|
||||
@@ -248,23 +241,17 @@ int su_client_main(int argc, char *argv[]) {
|
||||
if (atty) {
|
||||
// We need a PTY. Get one.
|
||||
write_int(fd, 1);
|
||||
ptmx = recv_fd(fd);
|
||||
} else {
|
||||
write_int(fd, 0);
|
||||
}
|
||||
|
||||
if (atty) {
|
||||
int ptmx = recv_fd(fd);
|
||||
setup_sighandlers(sighandler);
|
||||
// if stdin is not a tty, if we pump to ptmx, our process may intercept the input to ptmx and
|
||||
// output to stdout, which cause the target process lost input.
|
||||
pump_tty(ptmx, (atty & ATTY_IN) ? ptmx : -1);
|
||||
} else {
|
||||
write_int(fd, 0);
|
||||
}
|
||||
|
||||
// Get the exit code
|
||||
int code = read_int(fd);
|
||||
close(fd);
|
||||
|
||||
return code;
|
||||
return read_int(fd);
|
||||
}
|
||||
|
||||
static void drop_caps() {
|
||||
@@ -300,7 +287,9 @@ static bool proc_is_restricted(pid_t pid) {
|
||||
auto bnd = "CapBnd:"sv;
|
||||
uint32_t data[_LINUX_CAPABILITY_U32S_3] = {};
|
||||
ssprintf(buf, sizeof(buf), "/proc/%d/status", pid);
|
||||
file_readline(buf, [&](string_view line) -> bool {
|
||||
owned_fd status_fd = xopen(buf, O_RDONLY | O_CLOEXEC);
|
||||
file_readline(status_fd, [&](Utf8CStr s) -> bool {
|
||||
string_view line = s;
|
||||
if (line.starts_with(bnd)) {
|
||||
auto p = line.begin();
|
||||
advance(p, bnd.size());
|
||||
@@ -445,7 +434,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
|
||||
char path[32];
|
||||
ssprintf(path, sizeof(path), "/proc/%d/cwd", pid);
|
||||
char cwd[4096];
|
||||
if (realpath(path, cwd, sizeof(cwd)) > 0)
|
||||
if (canonical_path(path, cwd, sizeof(cwd)) > 0)
|
||||
chdir(cwd);
|
||||
ssprintf(path, sizeof(path), "/proc/%d/environ", pid);
|
||||
auto env = full_read(path);
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
use crate::consts::MODULEROOT;
|
||||
use crate::daemon::{MagiskD, to_user_id};
|
||||
use crate::ffi::{
|
||||
ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, restore_zygisk_prop, set_zygisk_prop, update_deny_flags,
|
||||
};
|
||||
use crate::ffi::{ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, update_deny_flags};
|
||||
use crate::resetprop::{get_prop, set_prop};
|
||||
use crate::socket::{IpcRead, UnixSocketExt};
|
||||
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO};
|
||||
use base::{
|
||||
Directory, FsPathBuilder, LoggedError, LoggedResult, ResultExt, WriteExt, cstr, error,
|
||||
fork_dont_care, libc, raw_cstr, warn,
|
||||
Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, WriteExt, cstr, fork_dont_care,
|
||||
libc, log_err, raw_cstr, warn,
|
||||
};
|
||||
use std::fmt::Write;
|
||||
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
const NBPROP: &Utf8CStr = cstr!("ro.dalvik.vm.native.bridge");
|
||||
const ZYGISKLDR: &str = "libzygisk.so";
|
||||
const UNMOUNT_MASK: u32 =
|
||||
ZygiskStateFlags::ProcessOnDenyList.repr | ZygiskStateFlags::DenyListEnforced.repr;
|
||||
|
||||
@@ -56,44 +57,126 @@ fn exec_zygiskd(is_64_bit: bool, remote: UnixStream) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ZygiskState {
|
||||
pub lib_name: String,
|
||||
sockets: (Option<UnixStream>, Option<UnixStream>),
|
||||
start_count: u32 = 1,
|
||||
}
|
||||
|
||||
impl ZygiskState {
|
||||
fn connect_zygiskd(&mut self, mut client: UnixStream, daemon: &MagiskD) -> LoggedResult<()> {
|
||||
let is_64_bit: bool = client.read_decodable()?;
|
||||
let socket = if is_64_bit {
|
||||
&mut self.sockets.1
|
||||
} else {
|
||||
&mut self.sockets.0
|
||||
};
|
||||
|
||||
if let Some(fd) = socket {
|
||||
// Make sure the socket is still valid
|
||||
let mut pfd = libc::pollfd {
|
||||
fd: fd.as_raw_fd(),
|
||||
events: 0,
|
||||
revents: 0,
|
||||
};
|
||||
if unsafe { libc::poll(&mut pfd, 1, 0) } != 0 || pfd.revents != 0 {
|
||||
// Any revent means error
|
||||
*socket = None;
|
||||
}
|
||||
}
|
||||
|
||||
let socket = if let Some(fd) = socket {
|
||||
fd
|
||||
} else {
|
||||
// Create a new socket pair and fork zygiskd process
|
||||
let (local, remote) = UnixStream::pair()?;
|
||||
if fork_dont_care() == 0 {
|
||||
exec_zygiskd(is_64_bit, remote);
|
||||
}
|
||||
*socket = Some(local);
|
||||
let local = socket.as_mut().unwrap();
|
||||
if let Some(module_fds) = daemon.get_module_fds(is_64_bit) {
|
||||
local.send_fds(&module_fds)?;
|
||||
}
|
||||
if local.read_decodable::<i32>()? != 0 {
|
||||
return log_err!();
|
||||
}
|
||||
local
|
||||
};
|
||||
socket.send_fds(&[client.as_raw_fd()])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, mut restore: bool) {
|
||||
if restore {
|
||||
self.start_count = 1;
|
||||
} else {
|
||||
self.sockets = (None, None);
|
||||
self.start_count += 1;
|
||||
if self.start_count > 3 {
|
||||
warn!("zygote crashed too many times, rolling-back");
|
||||
restore = true;
|
||||
}
|
||||
}
|
||||
|
||||
if restore {
|
||||
self.restore_prop();
|
||||
} else {
|
||||
self.set_prop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_prop(&mut self) {
|
||||
if !self.lib_name.is_empty() {
|
||||
return;
|
||||
}
|
||||
let orig = get_prop(NBPROP);
|
||||
self.lib_name = if orig.is_empty() || orig == "0" {
|
||||
ZYGISKLDR.to_string()
|
||||
} else {
|
||||
orig + ZYGISKLDR
|
||||
};
|
||||
set_prop(NBPROP, Utf8CStr::from_string(&mut self.lib_name));
|
||||
// Whether Huawei's Maple compiler is enabled.
|
||||
// If so, system server will be created by a special Zygote which ignores the native bridge
|
||||
// and make system server out of our control. Avoid it by disabling.
|
||||
if get_prop(cstr!("ro.maple.enable")) == "1" {
|
||||
set_prop(cstr!("ro.maple.enable"), cstr!("0"));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restore_prop(&mut self) {
|
||||
let mut orig = "0".to_string();
|
||||
if self.lib_name.len() > ZYGISKLDR.len() {
|
||||
orig = self.lib_name[ZYGISKLDR.len()..].to_string();
|
||||
}
|
||||
set_prop(NBPROP, Utf8CStr::from_string(&mut orig));
|
||||
self.lib_name.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn zygisk_handler(&self, client: i32) {
|
||||
let mut client = unsafe { UnixStream::from_raw_fd(client) };
|
||||
pub fn zygisk_handler(&self, client: OwnedFd) {
|
||||
let mut client = UnixStream::from(client);
|
||||
let _: LoggedResult<()> = try {
|
||||
let code = ZygiskRequest {
|
||||
repr: client.read_decodable()?,
|
||||
};
|
||||
match code {
|
||||
ZygiskRequest::GetInfo => self.get_process_info(client)?,
|
||||
ZygiskRequest::ConnectCompanion => self.connect_zygiskd(client),
|
||||
ZygiskRequest::ConnectCompanion => self
|
||||
.zygisk
|
||||
.lock()
|
||||
.unwrap()
|
||||
.connect_zygiskd(client, self)
|
||||
.log_with_msg(|w| w.write_str("zygiskd startup error"))?,
|
||||
ZygiskRequest::GetModDir => self.get_mod_dir(client)?,
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn zygisk_reset(&self, mut restore: bool) {
|
||||
if !self.zygisk_enabled.load(Ordering::Acquire) {
|
||||
return;
|
||||
}
|
||||
|
||||
if restore {
|
||||
self.zygote_start_count.store(1, Ordering::Release);
|
||||
} else {
|
||||
*self.zygiskd_sockets.lock().unwrap() = (None, None);
|
||||
if self.zygote_start_count.fetch_add(1, Ordering::AcqRel) > 3 {
|
||||
warn!("zygote crashes too many times, rolling-back");
|
||||
restore = true;
|
||||
}
|
||||
}
|
||||
|
||||
if restore {
|
||||
restore_zygisk_prop();
|
||||
} else {
|
||||
set_zygisk_prop();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_module_fds(&self, is_64_bit: bool) -> Option<Vec<RawFd>> {
|
||||
self.module_list.get().map(|module_list| {
|
||||
module_list
|
||||
@@ -107,54 +190,6 @@ impl MagiskD {
|
||||
})
|
||||
}
|
||||
|
||||
fn connect_zygiskd(&self, mut client: UnixStream) {
|
||||
let mut zygiskd_sockets = self.zygiskd_sockets.lock().unwrap();
|
||||
let result: LoggedResult<()> = try {
|
||||
let is_64_bit: bool = client.read_decodable()?;
|
||||
let socket = if is_64_bit {
|
||||
&mut zygiskd_sockets.1
|
||||
} else {
|
||||
&mut zygiskd_sockets.0
|
||||
};
|
||||
|
||||
if let Some(fd) = socket {
|
||||
// Make sure the socket is still valid
|
||||
let mut pfd = libc::pollfd {
|
||||
fd: fd.as_raw_fd(),
|
||||
events: 0,
|
||||
revents: 0,
|
||||
};
|
||||
if unsafe { libc::poll(&mut pfd, 1, 0) } != 0 || pfd.revents != 0 {
|
||||
// Any revent means error
|
||||
*socket = None;
|
||||
}
|
||||
}
|
||||
|
||||
let socket = if let Some(fd) = socket {
|
||||
fd
|
||||
} else {
|
||||
// Create a new socket pair and fork zygiskd process
|
||||
let (local, remote) = UnixStream::pair()?;
|
||||
if fork_dont_care() == 0 {
|
||||
exec_zygiskd(is_64_bit, remote);
|
||||
}
|
||||
*socket = Some(local);
|
||||
let local = socket.as_mut().unwrap();
|
||||
if let Some(module_fds) = self.get_module_fds(is_64_bit) {
|
||||
local.send_fds(&module_fds)?;
|
||||
}
|
||||
if local.read_decodable::<i32>()? != 0 {
|
||||
Err(LoggedError::default())?;
|
||||
}
|
||||
local
|
||||
};
|
||||
socket.send_fds(&[client.as_raw_fd()])?;
|
||||
};
|
||||
if result.is_err() {
|
||||
error!("zygiskd startup error");
|
||||
}
|
||||
}
|
||||
|
||||
fn get_process_info(&self, mut client: UnixStream) -> LoggedResult<()> {
|
||||
let uid: i32 = client.read_decodable()?;
|
||||
let process: String = client.read_decodable()?;
|
||||
@@ -214,3 +249,10 @@ impl MagiskD {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// FFI to C++
|
||||
impl MagiskD {
|
||||
pub fn zygisk_enabled(&self) -> bool {
|
||||
self.zygisk_enabled.load(Ordering::Acquire)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <android/dlext.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
@@ -10,8 +9,6 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
static string zygisk_lib_name = "0";
|
||||
|
||||
static void zygiskd(int socket) {
|
||||
if (getuid() != 0 || fcntl(socket, F_GETFD) < 0)
|
||||
exit(-1);
|
||||
@@ -109,30 +106,3 @@ extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
rust::Str get_zygisk_lib_name() {
|
||||
return zygisk_lib_name;
|
||||
}
|
||||
|
||||
void set_zygisk_prop() {
|
||||
string native_bridge_orig = get_prop(NBPROP);
|
||||
if (native_bridge_orig.empty()) {
|
||||
native_bridge_orig = "0";
|
||||
}
|
||||
zygisk_lib_name = native_bridge_orig == "0" ? ZYGISKLDR : ZYGISKLDR + native_bridge_orig;
|
||||
set_prop(NBPROP, zygisk_lib_name.data());
|
||||
// Whether Huawei's Maple compiler is enabled.
|
||||
// If so, system server will be created by a special Zygote which ignores the native bridge
|
||||
// and make system server out of our control. Avoid it by disabling.
|
||||
if (get_prop("ro.maple.enable") == "1") {
|
||||
set_prop("ro.maple.enable", "0");
|
||||
}
|
||||
}
|
||||
|
||||
void restore_zygisk_prop() {
|
||||
string native_bridge_orig = "0";
|
||||
if (zygisk_lib_name.length() > strlen(ZYGISKLDR)) {
|
||||
native_bridge_orig = zygisk_lib_name.substr(strlen(ZYGISKLDR));
|
||||
}
|
||||
set_prop(NBPROP, native_bridge_orig.data());
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ void HookContext::post_native_bridge_load(void *handle) {
|
||||
auto nb = get_prop(NBPROP);
|
||||
auto len = sizeof(ZYGISKLDR) - 1;
|
||||
if (nb.size() > len) {
|
||||
arg.load_native_bridge(nb.data() + len, arg.callbacks);
|
||||
arg.load_native_bridge(nb.c_str() + len, arg.callbacks);
|
||||
}
|
||||
runtime_callbacks = arg.callbacks;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
mod daemon;
|
||||
|
||||
pub use daemon::zygisk_should_load_module;
|
||||
pub use daemon::{ZygiskState, zygisk_should_load_module};
|
||||
|
||||
100
native/src/external/Android.mk
vendored
100
native/src/external/Android.mk
vendored
@@ -23,106 +23,6 @@ LOCAL_SRC_FILES := \
|
||||
lz4/lib/xxhash.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# liblzma.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := liblzma
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_PATH)/xz_config \
|
||||
$(LOCAL_PATH)/xz/src/common \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/api \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/check \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/common \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/delta \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/lz \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/lzma \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/rangecoder \
|
||||
$(LOCAL_PATH)/xz/src/liblzma/simple \
|
||||
$(LOCAL_PATH)/xz/src/liblzma
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/xz/src/liblzma/api
|
||||
LOCAL_SRC_FILES := \
|
||||
xz/src/common/tuklib_cpucores.c \
|
||||
xz/src/common/tuklib_exit.c \
|
||||
xz/src/common/tuklib_mbstr_fw.c \
|
||||
xz/src/common/tuklib_mbstr_width.c \
|
||||
xz/src/common/tuklib_open_stdxxx.c \
|
||||
xz/src/common/tuklib_physmem.c \
|
||||
xz/src/common/tuklib_progname.c \
|
||||
xz/src/liblzma/check/check.c \
|
||||
xz/src/liblzma/check/crc32_fast.c \
|
||||
xz/src/liblzma/check/crc32_table.c \
|
||||
xz/src/liblzma/check/crc64_fast.c \
|
||||
xz/src/liblzma/check/crc64_table.c \
|
||||
xz/src/liblzma/check/sha256.c \
|
||||
xz/src/liblzma/common/alone_decoder.c \
|
||||
xz/src/liblzma/common/alone_encoder.c \
|
||||
xz/src/liblzma/common/auto_decoder.c \
|
||||
xz/src/liblzma/common/block_buffer_decoder.c \
|
||||
xz/src/liblzma/common/block_buffer_encoder.c \
|
||||
xz/src/liblzma/common/block_decoder.c \
|
||||
xz/src/liblzma/common/block_encoder.c \
|
||||
xz/src/liblzma/common/block_header_decoder.c \
|
||||
xz/src/liblzma/common/block_header_encoder.c \
|
||||
xz/src/liblzma/common/block_util.c \
|
||||
xz/src/liblzma/common/common.c \
|
||||
xz/src/liblzma/common/easy_buffer_encoder.c \
|
||||
xz/src/liblzma/common/easy_decoder_memusage.c \
|
||||
xz/src/liblzma/common/easy_encoder.c \
|
||||
xz/src/liblzma/common/easy_encoder_memusage.c \
|
||||
xz/src/liblzma/common/easy_preset.c \
|
||||
xz/src/liblzma/common/filter_buffer_decoder.c \
|
||||
xz/src/liblzma/common/filter_buffer_encoder.c \
|
||||
xz/src/liblzma/common/filter_common.c \
|
||||
xz/src/liblzma/common/filter_decoder.c \
|
||||
xz/src/liblzma/common/filter_encoder.c \
|
||||
xz/src/liblzma/common/filter_flags_decoder.c \
|
||||
xz/src/liblzma/common/filter_flags_encoder.c \
|
||||
xz/src/liblzma/common/hardware_cputhreads.c \
|
||||
xz/src/liblzma/common/hardware_physmem.c \
|
||||
xz/src/liblzma/common/index.c \
|
||||
xz/src/liblzma/common/index_decoder.c \
|
||||
xz/src/liblzma/common/index_encoder.c \
|
||||
xz/src/liblzma/common/index_hash.c \
|
||||
xz/src/liblzma/common/outqueue.c \
|
||||
xz/src/liblzma/common/stream_buffer_decoder.c \
|
||||
xz/src/liblzma/common/stream_buffer_encoder.c \
|
||||
xz/src/liblzma/common/stream_decoder.c \
|
||||
xz/src/liblzma/common/stream_encoder.c \
|
||||
xz/src/liblzma/common/stream_encoder_mt.c \
|
||||
xz/src/liblzma/common/stream_flags_common.c \
|
||||
xz/src/liblzma/common/stream_flags_decoder.c \
|
||||
xz/src/liblzma/common/stream_flags_encoder.c \
|
||||
xz/src/liblzma/common/vli_decoder.c \
|
||||
xz/src/liblzma/common/vli_encoder.c \
|
||||
xz/src/liblzma/common/vli_size.c \
|
||||
xz/src/liblzma/delta/delta_common.c \
|
||||
xz/src/liblzma/delta/delta_decoder.c \
|
||||
xz/src/liblzma/delta/delta_encoder.c \
|
||||
xz/src/liblzma/lz/lz_decoder.c \
|
||||
xz/src/liblzma/lz/lz_encoder.c \
|
||||
xz/src/liblzma/lz/lz_encoder_mf.c \
|
||||
xz/src/liblzma/lzma/fastpos_table.c \
|
||||
xz/src/liblzma/lzma/fastpos_tablegen.c \
|
||||
xz/src/liblzma/lzma/lzma2_decoder.c \
|
||||
xz/src/liblzma/lzma/lzma2_encoder.c \
|
||||
xz/src/liblzma/lzma/lzma_decoder.c \
|
||||
xz/src/liblzma/lzma/lzma_encoder.c \
|
||||
xz/src/liblzma/lzma/lzma_encoder_optimum_fast.c \
|
||||
xz/src/liblzma/lzma/lzma_encoder_optimum_normal.c \
|
||||
xz/src/liblzma/lzma/lzma_encoder_presets.c \
|
||||
xz/src/liblzma/rangecoder/price_table.c \
|
||||
xz/src/liblzma/rangecoder/price_tablegen.c \
|
||||
xz/src/liblzma/simple/arm.c \
|
||||
xz/src/liblzma/simple/armthumb.c \
|
||||
xz/src/liblzma/simple/ia64.c \
|
||||
xz/src/liblzma/simple/powerpc.c \
|
||||
xz/src/liblzma/simple/simple_coder.c \
|
||||
xz/src/liblzma/simple/simple_decoder.c \
|
||||
xz/src/liblzma/simple/simple_encoder.c \
|
||||
xz/src/liblzma/simple/sparc.c \
|
||||
xz/src/liblzma/simple/x86.c
|
||||
LOCAL_CFLAGS := -DHAVE_CONFIG_H -Wno-implicit-function-declaration
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
SE_PATH := $(LOCAL_PATH)/selinux
|
||||
|
||||
# libsepol.a
|
||||
|
||||
2
native/src/external/cxx-rs
vendored
2
native/src/external/cxx-rs
vendored
Submodule native/src/external/cxx-rs updated: 0ea6d507ce...667377297a
22
native/src/external/lzma-sys/Cargo.toml
vendored
22
native/src/external/lzma-sys/Cargo.toml
vendored
@@ -1,22 +0,0 @@
|
||||
[package]
|
||||
name = "lzma-sys"
|
||||
version = "0.1.20"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/alexcrichton/xz2-rs"
|
||||
homepage = "https://github.com/alexcrichton/xz2-rs"
|
||||
documentation = "https://docs.rs/lzma-sys"
|
||||
description = """
|
||||
Raw bindings to liblzma which contains an implementation of LZMA and xz stream
|
||||
encoding/decoding.
|
||||
|
||||
High level Rust bindings are available in the `xz2` crate.
|
||||
"""
|
||||
categories = ["external-ffi-bindings"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.51"
|
||||
|
||||
[features]
|
||||
static = []
|
||||
359
native/src/external/lzma-sys/src/lib.rs
vendored
359
native/src/external/lzma-sys/src/lib.rs
vendored
@@ -1,359 +0,0 @@
|
||||
#![allow(bad_style)]
|
||||
#![doc(html_root_url = "https://docs.rs/lzma-sys/0.1")]
|
||||
|
||||
use libc::{c_char, c_uchar, c_void, size_t};
|
||||
use std::u64;
|
||||
|
||||
#[cfg(target_env = "msvc")]
|
||||
#[doc(hidden)]
|
||||
pub type __enum_ty = libc::c_int;
|
||||
#[cfg(not(target_env = "msvc"))]
|
||||
#[doc(hidden)]
|
||||
pub type __enum_ty = libc::c_uint;
|
||||
pub type lzma_bool = c_uchar;
|
||||
pub type lzma_ret = __enum_ty;
|
||||
pub type lzma_action = __enum_ty;
|
||||
type lzma_reserved_enum = __enum_ty;
|
||||
pub type lzma_check = __enum_ty;
|
||||
pub type lzma_vli = u64;
|
||||
pub type lzma_mode = __enum_ty;
|
||||
pub type lzma_match_finder = __enum_ty;
|
||||
|
||||
pub const LZMA_OK: lzma_ret = 0;
|
||||
pub const LZMA_STREAM_END: lzma_ret = 1;
|
||||
pub const LZMA_NO_CHECK: lzma_ret = 2;
|
||||
pub const LZMA_UNSUPPORTED_CHECK: lzma_ret = 3;
|
||||
pub const LZMA_GET_CHECK: lzma_ret = 4;
|
||||
pub const LZMA_MEM_ERROR: lzma_ret = 5;
|
||||
pub const LZMA_MEMLIMIT_ERROR: lzma_ret = 6;
|
||||
pub const LZMA_FORMAT_ERROR: lzma_ret = 7;
|
||||
pub const LZMA_OPTIONS_ERROR: lzma_ret = 8;
|
||||
pub const LZMA_DATA_ERROR: lzma_ret = 9;
|
||||
pub const LZMA_BUF_ERROR: lzma_ret = 10;
|
||||
pub const LZMA_PROG_ERROR: lzma_ret = 11;
|
||||
|
||||
pub const LZMA_RUN: lzma_action = 0;
|
||||
pub const LZMA_SYNC_FLUSH: lzma_action = 1;
|
||||
pub const LZMA_FULL_FLUSH: lzma_action = 2;
|
||||
pub const LZMA_FULL_BARRIER: lzma_action = 4;
|
||||
pub const LZMA_FINISH: lzma_action = 3;
|
||||
|
||||
pub const LZMA_CHECK_NONE: lzma_check = 0;
|
||||
pub const LZMA_CHECK_CRC32: lzma_check = 1;
|
||||
pub const LZMA_CHECK_CRC64: lzma_check = 4;
|
||||
pub const LZMA_CHECK_SHA256: lzma_check = 10;
|
||||
|
||||
pub const LZMA_MODE_FAST: lzma_mode = 1;
|
||||
pub const LZMA_MODE_NORMAL: lzma_mode = 2;
|
||||
|
||||
pub const LZMA_MF_HC3: lzma_match_finder = 0x03;
|
||||
pub const LZMA_MF_HC4: lzma_match_finder = 0x04;
|
||||
pub const LZMA_MF_BT2: lzma_match_finder = 0x12;
|
||||
pub const LZMA_MF_BT3: lzma_match_finder = 0x13;
|
||||
pub const LZMA_MF_BT4: lzma_match_finder = 0x14;
|
||||
|
||||
pub const LZMA_TELL_NO_CHECK: u32 = 0x01;
|
||||
pub const LZMA_TELL_UNSUPPORTED_CHECK: u32 = 0x02;
|
||||
pub const LZMA_TELL_ANY_CHECK: u32 = 0x04;
|
||||
pub const LZMA_IGNORE_CHECK: u32 = 0x10;
|
||||
pub const LZMA_CONCATENATED: u32 = 0x08;
|
||||
|
||||
pub const LZMA_PRESET_DEFAULT: u32 = 6;
|
||||
pub const LZMA_PRESET_LEVEL_MASK: u32 = 0x1f;
|
||||
pub const LZMA_PRESET_EXTREME: u32 = 1 << 31;
|
||||
|
||||
pub const LZMA_DICT_SIZE_MIN: u32 = 4096;
|
||||
pub const LZMA_DICT_SIZE_DEFAULT: u32 = 1 << 23;
|
||||
|
||||
pub const LZMA_LCLP_MIN: u32 = 0;
|
||||
pub const LZMA_LCLP_MAX: u32 = 4;
|
||||
pub const LZMA_LC_DEFAULT: u32 = 3;
|
||||
|
||||
pub const LZMA_LP_DEFAULT: u32 = 0;
|
||||
|
||||
pub const LZMA_PB_MIN: u32 = 0;
|
||||
pub const LZMA_PB_MAX: u32 = 4;
|
||||
pub const LZMA_PB_DEFAULT: u32 = 2;
|
||||
|
||||
pub const LZMA_BACKWARD_SIZE_MIN: lzma_vli = 4;
|
||||
pub const LZMA_BACKWARD_SIZE_MAX: lzma_vli = 1 << 34;
|
||||
|
||||
pub const LZMA_VLI_MAX: lzma_vli = u64::MAX / 2;
|
||||
pub const LZMA_VLI_UNKNOWN: lzma_vli = u64::MAX;
|
||||
pub const LZMA_VLI_BYTES_MAX: usize = 9;
|
||||
|
||||
pub const LZMA_FILTER_X86: lzma_vli = 0x04;
|
||||
pub const LZMA_FILTER_POWERPC: lzma_vli = 0x05;
|
||||
pub const LZMA_FILTER_IA64: lzma_vli = 0x06;
|
||||
pub const LZMA_FILTER_ARM: lzma_vli = 0x07;
|
||||
pub const LZMA_FILTER_ARMTHUMB: lzma_vli = 0x08;
|
||||
pub const LZMA_FILTER_SPARC: lzma_vli = 0x09;
|
||||
pub const LZMA_FILTER_LZMA1: lzma_vli = 0x4000000000000001;
|
||||
pub const LZMA_FILTER_LZMA2: lzma_vli = 0x21;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lzma_allocator {
|
||||
pub alloc: Option<extern "C" fn(*mut c_void, size_t, size_t) -> *mut c_void>,
|
||||
pub free: Option<extern "C" fn(*mut c_void, *mut c_void)>,
|
||||
pub opaque: *mut c_void,
|
||||
}
|
||||
|
||||
pub enum lzma_internal {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lzma_stream {
|
||||
pub next_in: *const u8,
|
||||
pub avail_in: size_t,
|
||||
pub total_in: u64,
|
||||
pub next_out: *mut u8,
|
||||
pub avail_out: size_t,
|
||||
pub total_out: u64,
|
||||
pub allocator: *const lzma_allocator,
|
||||
|
||||
internal: *mut lzma_internal,
|
||||
reserved_ptr1: *mut c_void,
|
||||
reserved_ptr2: *mut c_void,
|
||||
reserved_ptr3: *mut c_void,
|
||||
reserved_ptr4: *mut c_void,
|
||||
reserved_int1: u64,
|
||||
reserved_int2: u64,
|
||||
reserved_int3: size_t,
|
||||
reserved_int4: size_t,
|
||||
reserved_enum1: lzma_reserved_enum,
|
||||
reserved_enum2: lzma_reserved_enum,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lzma_filter {
|
||||
pub id: lzma_vli,
|
||||
pub options: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lzma_mt {
|
||||
pub flags: u32,
|
||||
pub threads: u32,
|
||||
pub block_size: u64,
|
||||
pub timeout: u32,
|
||||
pub preset: u32,
|
||||
pub filters: *const lzma_filter,
|
||||
pub check: lzma_check,
|
||||
|
||||
reserved_enum1: lzma_reserved_enum,
|
||||
reserved_enum2: lzma_reserved_enum,
|
||||
reserved_enum3: lzma_reserved_enum,
|
||||
reserved_int1: u32,
|
||||
reserved_int2: u32,
|
||||
reserved_int3: u32,
|
||||
reserved_int4: u32,
|
||||
reserved_int5: u64,
|
||||
reserved_int6: u64,
|
||||
reserved_int7: u64,
|
||||
reserved_int8: u64,
|
||||
reserved_ptr1: *mut c_void,
|
||||
reserved_ptr2: *mut c_void,
|
||||
reserved_ptr3: *mut c_void,
|
||||
reserved_ptr4: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct lzma_options_lzma {
|
||||
pub dict_size: u32,
|
||||
pub preset_dict: *const u8,
|
||||
pub preset_dict_size: u32,
|
||||
pub lc: u32,
|
||||
pub lp: u32,
|
||||
pub pb: u32,
|
||||
pub mode: lzma_mode,
|
||||
pub nice_len: u32,
|
||||
pub mf: lzma_match_finder,
|
||||
pub depth: u32,
|
||||
|
||||
reserved_int1: u32,
|
||||
reserved_int2: u32,
|
||||
reserved_int3: u32,
|
||||
reserved_int4: u32,
|
||||
reserved_int5: u32,
|
||||
reserved_int6: u32,
|
||||
reserved_int7: u32,
|
||||
reserved_int8: u32,
|
||||
reserved_enum1: lzma_reserved_enum,
|
||||
reserved_enum2: lzma_reserved_enum,
|
||||
reserved_enum3: lzma_reserved_enum,
|
||||
reserved_enum4: lzma_reserved_enum,
|
||||
reserved_ptr1: *mut c_void,
|
||||
reserved_ptr2: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lzma_stream_flags {
|
||||
pub version: u32,
|
||||
pub backward_size: lzma_vli,
|
||||
pub check: lzma_check,
|
||||
|
||||
reserved_enum1: lzma_reserved_enum,
|
||||
reserved_enum2: lzma_reserved_enum,
|
||||
reserved_enum3: lzma_reserved_enum,
|
||||
reserved_enum4: lzma_reserved_enum,
|
||||
reserved_bool1: lzma_bool,
|
||||
reserved_bool2: lzma_bool,
|
||||
reserved_bool3: lzma_bool,
|
||||
reserved_bool4: lzma_bool,
|
||||
reserved_bool5: lzma_bool,
|
||||
reserved_bool6: lzma_bool,
|
||||
reserved_bool7: lzma_bool,
|
||||
reserved_bool8: lzma_bool,
|
||||
reserved_int1: u32,
|
||||
reserved_int2: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct lzma_options_bcj {
|
||||
pub start_offset: u32,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn lzma_code(strm: *mut lzma_stream, action: lzma_action) -> lzma_ret;
|
||||
pub fn lzma_end(strm: *mut lzma_stream);
|
||||
pub fn lzma_get_progress(strm: *mut lzma_stream, progress_in: *mut u64, progress_out: *mut u64);
|
||||
pub fn lzma_memusage(strm: *const lzma_stream) -> u64;
|
||||
pub fn lzma_memlimit_get(strm: *const lzma_stream) -> u64;
|
||||
pub fn lzma_memlimit_set(strm: *mut lzma_stream, memlimit: u64) -> lzma_ret;
|
||||
|
||||
pub fn lzma_easy_encoder_memusage(preset: u32) -> u64;
|
||||
pub fn lzma_easy_decoder_memusage(preset: u32) -> u64;
|
||||
pub fn lzma_easy_encoder(strm: *mut lzma_stream, preset: u32, check: lzma_check) -> lzma_ret;
|
||||
pub fn lzma_easy_buffer_encode(
|
||||
preset: u32,
|
||||
check: lzma_check,
|
||||
allocator: *const lzma_allocator,
|
||||
input: *const u8,
|
||||
in_size: size_t,
|
||||
out: *mut u8,
|
||||
out_pos: *mut size_t,
|
||||
out_size: size_t,
|
||||
) -> lzma_ret;
|
||||
|
||||
pub fn lzma_stream_encoder(
|
||||
strm: *mut lzma_stream,
|
||||
filters: *const lzma_filter,
|
||||
check: lzma_check,
|
||||
) -> lzma_ret;
|
||||
pub fn lzma_stream_encoder_mt_memusage(options: *const lzma_mt) -> u64;
|
||||
pub fn lzma_stream_encoder_mt(strm: *mut lzma_stream, options: *const lzma_mt) -> lzma_ret;
|
||||
|
||||
pub fn lzma_alone_encoder(
|
||||
strm: *mut lzma_stream,
|
||||
options: *const lzma_options_lzma,
|
||||
) -> lzma_ret;
|
||||
|
||||
pub fn lzma_stream_buffer_bound(uncompressed_size: size_t) -> size_t;
|
||||
pub fn lzma_stream_buffer_encode(
|
||||
filters: *mut lzma_filter,
|
||||
check: lzma_check,
|
||||
allocator: *const lzma_allocator,
|
||||
input: *const u8,
|
||||
in_size: size_t,
|
||||
out: *mut u8,
|
||||
out_pos: *mut size_t,
|
||||
out_size: size_t,
|
||||
) -> lzma_ret;
|
||||
|
||||
pub fn lzma_stream_decoder(strm: *mut lzma_stream, memlimit: u64, flags: u32) -> lzma_ret;
|
||||
pub fn lzma_auto_decoder(strm: *mut lzma_stream, memlimit: u64, flags: u32) -> lzma_ret;
|
||||
pub fn lzma_alone_decoder(strm: *mut lzma_stream, memlimit: u64) -> lzma_ret;
|
||||
pub fn lzma_stream_buffer_decode(
|
||||
memlimit: *mut u64,
|
||||
flags: u32,
|
||||
allocator: *const lzma_allocator,
|
||||
input: *const u8,
|
||||
in_pos: *mut size_t,
|
||||
in_size: size_t,
|
||||
out: *mut u8,
|
||||
out_pos: *mut size_t,
|
||||
out_size: size_t,
|
||||
) -> lzma_ret;
|
||||
|
||||
pub fn lzma_check_is_supported(check: lzma_check) -> lzma_bool;
|
||||
pub fn lzma_check_size(check: lzma_check) -> u32;
|
||||
|
||||
pub fn lzma_crc32(buf: *const u8, size: size_t, crc: u32) -> u32;
|
||||
pub fn lzma_crc64(buf: *const u8, size: size_t, crc: u64) -> u64;
|
||||
pub fn lzma_get_check(strm: *const lzma_stream) -> lzma_check;
|
||||
|
||||
pub fn lzma_filter_encoder_is_supported(id: lzma_vli) -> lzma_bool;
|
||||
pub fn lzma_filter_decoder_is_supported(id: lzma_vli) -> lzma_bool;
|
||||
pub fn lzma_filters_copy(
|
||||
src: *const lzma_filter,
|
||||
dest: *mut lzma_filter,
|
||||
allocator: *const lzma_allocator,
|
||||
) -> lzma_ret;
|
||||
pub fn lzma_raw_encoder_memusage(filters: *const lzma_filter) -> u64;
|
||||
pub fn lzma_raw_decoder_memusage(filters: *const lzma_filter) -> u64;
|
||||
pub fn lzma_raw_encoder(strm: *mut lzma_stream, filters: *const lzma_filter) -> lzma_ret;
|
||||
pub fn lzma_raw_decoder(strm: *mut lzma_stream, filters: *const lzma_filter) -> lzma_ret;
|
||||
pub fn lzma_filters_update(strm: *mut lzma_stream, filters: *const lzma_filter) -> lzma_ret;
|
||||
pub fn lzma_raw_buffer_encode(
|
||||
filters: *const lzma_filter,
|
||||
allocator: *const lzma_allocator,
|
||||
input: *const u8,
|
||||
in_size: size_t,
|
||||
out: *mut u8,
|
||||
out_pos: *mut size_t,
|
||||
out_size: size_t,
|
||||
) -> lzma_ret;
|
||||
pub fn lzma_raw_buffer_decode(
|
||||
filters: *const lzma_filter,
|
||||
allocator: *const lzma_allocator,
|
||||
input: *const u8,
|
||||
in_pos: *mut size_t,
|
||||
in_size: size_t,
|
||||
out: *mut u8,
|
||||
out_pos: *mut size_t,
|
||||
out_size: size_t,
|
||||
) -> lzma_ret;
|
||||
pub fn lzma_properties_size(size: *mut u32, filter: *const lzma_filter) -> lzma_ret;
|
||||
pub fn lzma_properties_encode(filter: *const lzma_filter, props: *mut u8) -> lzma_ret;
|
||||
pub fn lzma_properties_decode(
|
||||
filter: *mut lzma_filter,
|
||||
allocator: *const lzma_allocator,
|
||||
props: *const u8,
|
||||
props_size: size_t,
|
||||
) -> lzma_ret;
|
||||
pub fn lzma_physmem() -> u64;
|
||||
pub fn lzma_cputhreads() -> u32;
|
||||
|
||||
pub fn lzma_stream_header_encode(options: *const lzma_stream_flags, out: *mut u8) -> lzma_ret;
|
||||
pub fn lzma_stream_footer_encode(options: *const lzma_stream_flags, out: *mut u8) -> lzma_ret;
|
||||
pub fn lzma_stream_header_decode(options: *mut lzma_stream_flags, input: *const u8)
|
||||
-> lzma_ret;
|
||||
pub fn lzma_stream_footer_decode(options: *mut lzma_stream_flags, input: *const u8)
|
||||
-> lzma_ret;
|
||||
pub fn lzma_stream_flags_compare(
|
||||
a: *const lzma_stream_flags,
|
||||
b: *const lzma_stream_flags,
|
||||
) -> lzma_ret;
|
||||
|
||||
pub fn lzma_version_number() -> u32;
|
||||
pub fn lzma_version_string() -> *const c_char;
|
||||
|
||||
pub fn lzma_vli_encode(
|
||||
vli: lzma_vli,
|
||||
vli_pos: *mut size_t,
|
||||
out: *mut u8,
|
||||
out_pos: *mut size_t,
|
||||
out_size: size_t,
|
||||
) -> lzma_ret;
|
||||
pub fn lzma_vli_decode(
|
||||
vli: *mut lzma_vli,
|
||||
vli_pos: *mut size_t,
|
||||
input: *const u8,
|
||||
in_pos: *mut size_t,
|
||||
in_size: size_t,
|
||||
) -> lzma_ret;
|
||||
pub fn lzma_vli_size(vli: lzma_vli) -> u32;
|
||||
|
||||
pub fn lzma_lzma_preset(options: *mut lzma_options_lzma, preset: u32) -> lzma_bool;
|
||||
pub fn lzma_mf_is_supported(mf: lzma_match_finder) -> lzma_bool;
|
||||
}
|
||||
2
native/src/external/system_properties
vendored
2
native/src/external/system_properties
vendored
Submodule native/src/external/system_properties updated: 3420071fe2...f081b497cd
1
native/src/external/xz
vendored
1
native/src/external/xz
vendored
Submodule native/src/external/xz deleted from 2327a461e1
@@ -6,7 +6,7 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::{fs, io, process};
|
||||
|
||||
use cxx_gen::Opt;
|
||||
use cxx_gen::{Include, IncludeKind, Opt};
|
||||
|
||||
trait ResultExt<T> {
|
||||
fn ok_or_exit(self) -> T;
|
||||
@@ -38,7 +38,12 @@ fn write_if_diff<P: AsRef<Path>>(path: P, bytes: &[u8]) -> io::Result<()> {
|
||||
|
||||
pub fn gen_cxx_binding(name: &str) {
|
||||
println!("cargo:rerun-if-changed=lib.rs");
|
||||
let opt = Opt::default();
|
||||
let mut opt = Opt::default();
|
||||
opt.cxx_impl_annotations = Some("[[gnu::always_inline]]".to_string());
|
||||
opt.include.push(Include {
|
||||
path: "rust/cxx.h".to_string(),
|
||||
kind: IncludeKind::Bracketed,
|
||||
});
|
||||
let code = cxx_gen::generate_header_and_cc_with_path("lib.rs", &opt);
|
||||
write_if_diff(format!("{name}.cpp"), code.implementation.as_slice()).ok_or_exit();
|
||||
write_if_diff(format!("{name}.hpp"), code.header.as_slice()).ok_or_exit();
|
||||
|
||||
@@ -37,9 +37,3 @@ constexpr const char *applet_names[] = { "su", "resetprop", nullptr };
|
||||
|
||||
extern int SDK_INT;
|
||||
#define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user")
|
||||
|
||||
// Multi-call entrypoints
|
||||
int magisk_main(int argc, char *argv[]);
|
||||
int su_client_main(int argc, char *argv[]);
|
||||
int resetprop_main(int argc, char *argv[]);
|
||||
int zygisk_main(int argc, char *argv[]);
|
||||
|
||||
@@ -15,23 +15,23 @@
|
||||
#include "init-rs.hpp"
|
||||
|
||||
int magisk_proxy_main(int, char *argv[]);
|
||||
rust::Utf8CStr backup_init();
|
||||
Utf8CStr backup_init();
|
||||
|
||||
// Expose some constants to Rust
|
||||
|
||||
static inline rust::Utf8CStr split_plat_cil() {
|
||||
static inline Utf8CStr split_plat_cil() {
|
||||
return SPLIT_PLAT_CIL;
|
||||
};
|
||||
|
||||
static inline rust::Utf8CStr preload_lib() {
|
||||
static inline Utf8CStr preload_lib() {
|
||||
return PRELOAD_LIB;
|
||||
}
|
||||
|
||||
static inline rust::Utf8CStr preload_policy() {
|
||||
static inline Utf8CStr preload_policy() {
|
||||
return PRELOAD_POLICY;
|
||||
}
|
||||
|
||||
static inline rust::Utf8CStr preload_ack() {
|
||||
static inline Utf8CStr preload_ack() {
|
||||
return PRELOAD_ACK;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("init.hpp");
|
||||
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use base::{
|
||||
LOGGER, LogLevel, Logger, SilentResultExt, Utf8CStr, cstr,
|
||||
LOGGER, LogLevel, Logger, SilentLogExt, Utf8CStr, cstr,
|
||||
libc::{
|
||||
O_CLOEXEC, O_RDWR, O_WRONLY, S_IFCHR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, SYS_dup3,
|
||||
makedev, mknod, syscall,
|
||||
|
||||
@@ -30,15 +30,15 @@ static void parse_device(devinfo *dev, const char *uevent) {
|
||||
dev->devpath[0] = '\0';
|
||||
dev->dmname[0] = '\0';
|
||||
dev->devname[0] = '\0';
|
||||
parse_prop_file(uevent, [=](string_view key, string_view value) -> bool {
|
||||
parse_prop_file(uevent, [=](Utf8CStr key, Utf8CStr value) -> bool {
|
||||
if (key == "MAJOR")
|
||||
dev->major = parse_int(value.data());
|
||||
dev->major = parse_int(value);
|
||||
else if (key == "MINOR")
|
||||
dev->minor = parse_int(value.data());
|
||||
dev->minor = parse_int(value);
|
||||
else if (key == "DEVNAME")
|
||||
strscpy(dev->devname, value.data(), sizeof(dev->devname));
|
||||
strscpy(dev->devname, value.c_str(), sizeof(dev->devname));
|
||||
else if (key == "PARTNAME")
|
||||
strscpy(dev->partname, value.data(), sizeof(dev->devname));
|
||||
strscpy(dev->partname, value.c_str(), sizeof(dev->devname));
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -29,10 +29,9 @@ pub(crate) fn switch_root(path: &Utf8CStr) {
|
||||
if let Some(last_mount) = mounts
|
||||
.range::<String, _>((Unbounded, Excluded(&info.target)))
|
||||
.last()
|
||||
&& info.target.starts_with(&format!("{}/", *last_mount))
|
||||
{
|
||||
if info.target.starts_with(&format!("{}/", *last_mount)) {
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut target = info.target.clone();
|
||||
|
||||
@@ -60,75 +60,77 @@ static bool patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr
|
||||
|
||||
// First patch init.rc
|
||||
{
|
||||
auto src = xopen_file(xopenat(src_fd, INIT_RC, O_RDONLY | O_CLOEXEC, 0), "re");
|
||||
if (!src) return false;
|
||||
owned_fd src_rc = xopenat(src_fd, INIT_RC, O_RDONLY | O_CLOEXEC, 0);
|
||||
if (src_rc < 0) return false;
|
||||
if (writable) unlinkat(src_fd, INIT_RC, 0);
|
||||
auto dest = xopen_file(
|
||||
auto dest_rc = xopen_file(
|
||||
xopenat(dest_fd, INIT_RC, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we");
|
||||
if (!dest) return false;
|
||||
if (!dest_rc) return false;
|
||||
LOGD("Patching " INIT_RC " in %s\n", src_path);
|
||||
file_readline(false, src.get(), [&dest](string_view line) -> bool {
|
||||
file_readline(src_rc, [&dest_rc](Utf8CStr line) -> bool {
|
||||
// Do not start vaultkeeper
|
||||
if (str_contains(line, "start vaultkeeper")) {
|
||||
if (line.sv().contains("start vaultkeeper")) {
|
||||
LOGD("Remove vaultkeeper\n");
|
||||
return true;
|
||||
}
|
||||
// Do not run flash_recovery
|
||||
if (line.starts_with("service flash_recovery")) {
|
||||
if (line.sv().starts_with("service flash_recovery")) {
|
||||
LOGD("Remove flash_recovery\n");
|
||||
fprintf(dest.get(), "service flash_recovery /system/bin/true\n");
|
||||
fprintf(dest_rc.get(), "service flash_recovery /system/bin/true\n");
|
||||
return true;
|
||||
}
|
||||
// Samsung's persist.sys.zygote.early will cause Zygote to start before post-fs-data
|
||||
if (line.starts_with("on property:persist.sys.zygote.early=")) {
|
||||
if (line.sv().starts_with("on property:persist.sys.zygote.early=")) {
|
||||
LOGD("Invalidate persist.sys.zygote.early\n");
|
||||
fprintf(dest.get(), "on property:persist.sys.zygote.early.xxxxx=true\n");
|
||||
fprintf(dest_rc.get(), "on property:persist.sys.zygote.early.xxxxx=true\n");
|
||||
return true;
|
||||
}
|
||||
// Else just write the line
|
||||
fprintf(dest.get(), "%s", line.data());
|
||||
fprintf(dest_rc.get(), "%s", line.c_str());
|
||||
return true;
|
||||
});
|
||||
|
||||
fprintf(dest.get(), "\n");
|
||||
fprintf(dest_rc.get(), "\n");
|
||||
|
||||
// Inject custom rc scripts
|
||||
for (auto &script : rc_list) {
|
||||
// Replace template arguments of rc scripts with dynamic paths
|
||||
replace_all(script, "${MAGISKTMP}", tmp_path);
|
||||
fprintf(dest.get(), "\n%s\n", script.data());
|
||||
fprintf(dest_rc.get(), "\n%s\n", script.data());
|
||||
}
|
||||
rc_list.clear();
|
||||
|
||||
// Inject Magisk rc scripts
|
||||
rust::inject_magisk_rc(fileno(dest.get()), tmp_path);
|
||||
rust::inject_magisk_rc(fileno(dest_rc.get()), tmp_path);
|
||||
|
||||
fclone_attr(fileno(src.get()), fileno(dest.get()));
|
||||
fclone_attr(src_rc, fileno(dest_rc.get()));
|
||||
}
|
||||
|
||||
// Then patch init.zygote*.rc
|
||||
for (dirent *entry; (entry = readdir(src_dir.get()));) {
|
||||
auto name = std::string_view(entry->d_name);
|
||||
if (!name.starts_with("init.zygote") || !name.ends_with(".rc")) continue;
|
||||
auto src = xopen_file(xopenat(src_fd, name.data(), O_RDONLY | O_CLOEXEC, 0), "re");
|
||||
if (!src) continue;
|
||||
if (writable) unlinkat(src_fd, name.data(), 0);
|
||||
auto dest = xopen_file(
|
||||
xopenat(dest_fd, name.data(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we");
|
||||
if (!dest) continue;
|
||||
LOGD("Patching %s in %s\n", name.data(), src_path);
|
||||
file_readline(false, src.get(), [&dest, &tmp_path](string_view line) -> bool {
|
||||
if (line.starts_with("service zygote ")) {
|
||||
{
|
||||
auto name = std::string_view(entry->d_name);
|
||||
if (!name.starts_with("init.zygote") || !name.ends_with(".rc")) continue;
|
||||
}
|
||||
owned_fd src_rc = xopenat(src_fd, entry->d_name, O_RDONLY | O_CLOEXEC, 0);
|
||||
if (src_rc < 0) continue;
|
||||
if (writable) unlinkat(src_fd, entry->d_name, 0);
|
||||
auto dest_rc = xopen_file(
|
||||
xopenat(dest_fd, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we");
|
||||
if (!dest_rc) continue;
|
||||
LOGD("Patching %s in %s\n", entry->d_name, src_path);
|
||||
file_readline(src_rc, [&dest_rc, &tmp_path](Utf8CStr line) -> bool {
|
||||
if (line.sv().starts_with("service zygote ")) {
|
||||
LOGD("Inject zygote restart\n");
|
||||
fprintf(dest.get(), "%s", line.data());
|
||||
fprintf(dest.get(),
|
||||
fprintf(dest_rc.get(), "%s", line.c_str());
|
||||
fprintf(dest_rc.get(),
|
||||
" onrestart exec " MAGISK_PROC_CON " 0 0 -- %s/magisk --zygote-restart\n", tmp_path);
|
||||
return true;
|
||||
}
|
||||
fprintf(dest.get(), "%s", line.data());
|
||||
fprintf(dest_rc.get(), "%s", line.c_str());
|
||||
return true;
|
||||
});
|
||||
fclone_attr(fileno(src.get()), fileno(dest.get()));
|
||||
fclone_attr(src_rc, fileno(dest_rc.get()));
|
||||
}
|
||||
|
||||
return faccessat(src_fd, "init.fission_host.rc", F_OK, 0) == 0;
|
||||
@@ -147,7 +149,7 @@ void MagiskInit::patch_fissiond(const char *tmp_path) noexcept {
|
||||
}
|
||||
mkdirs(ROOTOVL "/system/bin", 0755);
|
||||
if (auto target_fissiond = xopen_file(ROOTOVL "/system/bin/fissiond", "we")) {
|
||||
fwrite(fissiond.buf(), 1, fissiond.sz(), target_fissiond.get());
|
||||
fwrite(fissiond.data(), 1, fissiond.size(), target_fissiond.get());
|
||||
clone_attr("/system/bin/fissiond", ROOTOVL "/system/bin/fissiond");
|
||||
}
|
||||
}
|
||||
@@ -160,8 +162,7 @@ void MagiskInit::patch_fissiond(const char *tmp_path) noexcept {
|
||||
LOGD("hijacked isolated\n");
|
||||
xumount2("/sys/devices/system/cpu/isolated", MNT_DETACH);
|
||||
unlink(INTLROOT "/isolated");
|
||||
string content;
|
||||
full_read(fileno(hijack.get()), content);
|
||||
string content = full_read(fileno(hijack.get()));
|
||||
{
|
||||
string target = "/dev/cells/cell2"s + tmp_path;
|
||||
xmkdirs(target.data(), 0);
|
||||
@@ -185,7 +186,7 @@ static void load_overlay_rc(const char *overlay) {
|
||||
char buf[NAME_MAX + 2];
|
||||
buf[0] = '/';
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (!str_ends(entry->d_name, ".rc")) {
|
||||
if (!string_view(entry->d_name).ends_with(".rc")) {
|
||||
continue;
|
||||
}
|
||||
strscpy(buf + 1, entry->d_name, sizeof(buf) - 1);
|
||||
@@ -299,7 +300,7 @@ void MagiskInit::patch_ro_root() noexcept {
|
||||
LOGD("Patch @ %08zX [android,fstab] -> [xxx]\n", off);
|
||||
}
|
||||
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC, 0);
|
||||
xwrite(dest, init.buf(), init.sz());
|
||||
xwrite(dest, init.data(), init.size());
|
||||
fclone_attr(src, dest);
|
||||
close(src);
|
||||
close(dest);
|
||||
@@ -408,7 +409,7 @@ static void unxz_init(const char *init_xz, const char *init) {
|
||||
unlink(init_xz);
|
||||
}
|
||||
|
||||
rust::Utf8CStr backup_init() {
|
||||
Utf8CStr backup_init() {
|
||||
if (access("/.backup/init.xz", F_OK) == 0)
|
||||
unxz_init("/.backup/init.xz", "/.backup/init");
|
||||
return "/.backup/init";
|
||||
|
||||
@@ -32,9 +32,6 @@ on nonencrypted
|
||||
|
||||
on property:sys.boot_completed=1
|
||||
exec {0} 0 0 -- {1}/magisk --boot-complete
|
||||
|
||||
on property:init.svc.zygote=stopped
|
||||
exec {0} 0 0 -- {1}/magisk --zygote-restart
|
||||
"#,
|
||||
"u:r:magisk:s0", tmp_dir
|
||||
)
|
||||
@@ -49,7 +46,7 @@ impl MagiskInit {
|
||||
pub(crate) fn parse_config_file(&mut self) {
|
||||
if let Ok(fd) = cstr!("/data/.backup/.magisk").open(O_RDONLY) {
|
||||
let mut reader = BufReader::new(fd);
|
||||
reader.foreach_props(|key, val| {
|
||||
reader.for_each_prop(|key, val| {
|
||||
if key == "PREINITDEVICE" {
|
||||
self.preinit_dev = val.to_string();
|
||||
return false;
|
||||
|
||||
@@ -2,8 +2,8 @@ use crate::ffi::SePolicy;
|
||||
use crate::statement::format_statement_help;
|
||||
use argh::FromArgs;
|
||||
use base::{
|
||||
EarlyExitExt, FmtAdaptor, LoggedResult, Utf8CStr, cmdline_logging, cstr, libc::umask, log_err,
|
||||
map_args,
|
||||
CmdArgs, EarlyExitExt, FmtAdaptor, LoggedResult, Utf8CString, cmdline_logging, cstr,
|
||||
libc::umask, log_err,
|
||||
};
|
||||
use std::ffi::c_char;
|
||||
use std::io::stderr;
|
||||
@@ -26,13 +26,13 @@ struct Cli {
|
||||
print_rules: bool,
|
||||
|
||||
#[argh(option)]
|
||||
load: Option<String>,
|
||||
load: Option<Utf8CString>,
|
||||
|
||||
#[argh(option)]
|
||||
save: Option<String>,
|
||||
save: Option<Utf8CString>,
|
||||
|
||||
#[argh(option)]
|
||||
apply: Vec<String>,
|
||||
apply: Vec<Utf8CString>,
|
||||
|
||||
#[argh(positional)]
|
||||
polices: Vec<String>,
|
||||
@@ -79,22 +79,23 @@ pub unsafe extern "C" fn main(
|
||||
}
|
||||
|
||||
let res: LoggedResult<()> = try {
|
||||
let cmds = map_args(argc, argv)?;
|
||||
let cmds = CmdArgs::new(argc, argv);
|
||||
let cmds = cmds.as_slice();
|
||||
if argc < 2 {
|
||||
print_usage(cmds.first().unwrap_or(&"magiskpolicy"));
|
||||
return 1;
|
||||
}
|
||||
let mut cli = Cli::from_args(&[cmds[0]], &cmds[1..]).on_early_exit(|| print_usage(cmds[0]));
|
||||
let cli = Cli::from_args(&[cmds[0]], &cmds[1..]).on_early_exit(|| print_usage(cmds[0]));
|
||||
|
||||
let mut sepol = match (&mut cli.load, cli.load_split, cli.compile_split) {
|
||||
(Some(file), false, false) => SePolicy::from_file(Utf8CStr::from_string(file)),
|
||||
let mut sepol = match (cli.load, cli.load_split, cli.compile_split) {
|
||||
(Some(file), false, false) => SePolicy::from_file(&file),
|
||||
(None, true, false) => SePolicy::from_split(),
|
||||
(None, false, true) => SePolicy::compile_split(),
|
||||
(None, false, false) => SePolicy::from_file(cstr!("/sys/fs/selinux/policy")),
|
||||
_ => Err(log_err!("Multiple load source supplied"))?,
|
||||
_ => log_err!("Multiple load source supplied")?,
|
||||
};
|
||||
if sepol._impl.is_null() {
|
||||
Err(log_err!("Cannot load policy"))?;
|
||||
log_err!("Cannot load policy")?;
|
||||
}
|
||||
|
||||
if cli.print_rules {
|
||||
@@ -104,7 +105,7 @@ pub unsafe extern "C" fn main(
|
||||
|| cli.live
|
||||
|| cli.save.is_some()
|
||||
{
|
||||
Err(log_err!("Cannot print rules with other options"))?;
|
||||
log_err!("Cannot print rules with other options")?;
|
||||
}
|
||||
sepol.print_rules();
|
||||
return 0;
|
||||
@@ -114,8 +115,8 @@ pub unsafe extern "C" fn main(
|
||||
sepol.magisk_rules();
|
||||
}
|
||||
|
||||
for file in &mut cli.apply {
|
||||
sepol.load_rule_file(Utf8CStr::from_string(file));
|
||||
for file in cli.apply {
|
||||
sepol.load_rule_file(&file);
|
||||
}
|
||||
|
||||
for statement in &cli.polices {
|
||||
@@ -123,13 +124,13 @@ pub unsafe extern "C" fn main(
|
||||
}
|
||||
|
||||
if cli.live && !sepol.to_file(cstr!("/sys/fs/selinux/load")) {
|
||||
Err(log_err!("Cannot apply policy"))?;
|
||||
log_err!("Cannot apply policy")?;
|
||||
}
|
||||
|
||||
if let Some(file) = &mut cli.save {
|
||||
if !sepol.to_file(Utf8CStr::from_string(file)) {
|
||||
Err(log_err!("Cannot dump policy to {}", file))?;
|
||||
}
|
||||
if let Some(file) = cli.save
|
||||
&& !sepol.to_file(&file)
|
||||
{
|
||||
log_err!("Cannot dump policy to {}", file)?;
|
||||
}
|
||||
};
|
||||
if res.is_ok() { 0 } else { 1 }
|
||||
|
||||
@@ -30,7 +30,6 @@ pub mod ffi {
|
||||
include!("policy.hpp");
|
||||
include!("../base/include/base.hpp");
|
||||
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <cxx.h>
|
||||
#include <rust/cxx.h>
|
||||
|
||||
#include <sepol/policydb/policydb.h>
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ static bool check_precompiled(const char *precompiled) {
|
||||
|
||||
static void load_cil(struct cil_db *db, const char *file) {
|
||||
mmap_data d(file);
|
||||
cil_add_file(db, file, (const char *) d.buf(), d.sz());
|
||||
cil_add_file(db, file, (const char *) d.data(), d.size());
|
||||
LOGD("cil_add [%s]\n", file);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ SePolicy SePolicy::from_data(rust::Slice<const uint8_t> data) noexcept {
|
||||
return {std::make_unique<sepol_impl>(db)};
|
||||
}
|
||||
|
||||
SePolicy SePolicy::from_file(::rust::Utf8CStr file) noexcept {
|
||||
SePolicy SePolicy::from_file(::Utf8CStr file) noexcept {
|
||||
LOGD("Load policy from: %.*s\n", static_cast<int>(file.size()), file.data());
|
||||
|
||||
policy_file_t pf;
|
||||
@@ -235,7 +235,7 @@ static int vec_write(void *v, const char *buf, int len) {
|
||||
return len;
|
||||
}
|
||||
|
||||
bool SePolicy::to_file(::rust::Utf8CStr file) const noexcept {
|
||||
bool SePolicy::to_file(::Utf8CStr file) const noexcept {
|
||||
// No partial writes are allowed to /sys/fs/selinux/load, thus the reason why we
|
||||
// first dump everything into memory, then directly call write system call
|
||||
vector<char> out;
|
||||
|
||||
@@ -202,10 +202,10 @@ fn parse_xperms<'a>(tokens: &mut Tokens<'a>) -> ParseResult<'a, Vec<Xperm>> {
|
||||
}
|
||||
|
||||
fn match_string<'a>(tokens: &mut Tokens<'a>, pattern: &str) -> ParseResult<'a, ()> {
|
||||
if let Some(Token::ID(s)) = tokens.next() {
|
||||
if s == pattern {
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(Token::ID(s)) = tokens.next()
|
||||
&& s == pattern
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
Err(ParseError::General)
|
||||
}
|
||||
@@ -263,7 +263,7 @@ fn extract_token<'a>(s: &'a str, tokens: &mut Vec<Token<'a>>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn tokenize_statement(statement: &str) -> Vec<Token> {
|
||||
fn tokenize_statement(statement: &str) -> Vec<Token<'_>> {
|
||||
let mut tokens = Vec::new();
|
||||
for s in statement.split_whitespace() {
|
||||
extract_token(s, &mut tokens);
|
||||
@@ -287,7 +287,7 @@ impl SePolicy {
|
||||
}
|
||||
|
||||
fn load_rules_from_reader<T: BufRead>(&mut self, reader: &mut T) {
|
||||
reader.foreach_lines(|line| {
|
||||
reader.for_each_line(|line| {
|
||||
self.parse_statement(line);
|
||||
true
|
||||
});
|
||||
|
||||
@@ -4,10 +4,7 @@ set -e
|
||||
shopt -s extglob
|
||||
. scripts/test_common.sh
|
||||
|
||||
emu_port=5682
|
||||
export ANDROID_SERIAL="emulator-$emu_port"
|
||||
|
||||
emu_args_base="-no-window -no-audio -no-boot-anim -gpu swiftshader_indirect -read-only -no-snapshot -port $emu_port -cores $core_count"
|
||||
emu_args_base="-no-window -no-audio -no-boot-anim -gpu swiftshader_indirect -read-only -no-snapshot -cores $core_count"
|
||||
log_args="-show-kernel -logcat '' -logcat-output logcat.log"
|
||||
emu_args=
|
||||
emu_pid=
|
||||
@@ -175,6 +172,11 @@ test_main() {
|
||||
local avd_pkg ramdisk
|
||||
eval $(resolve_vars "emu_args avd_pkg ramdisk" $1 $2)
|
||||
|
||||
# Specify an explicit port so that tests can run with other emulators running at the same time
|
||||
local emu_port=5682
|
||||
emu_args="$emu_args -port $emu_port"
|
||||
export ANDROID_SERIAL="emulator-$emu_port"
|
||||
|
||||
setup_emu "$avd_pkg"
|
||||
|
||||
# Restart ADB daemon just in case
|
||||
|
||||
Reference in New Issue
Block a user