Compare commits

...

36 Commits

Author SHA1 Message Date
topjohnwu
fb0c4ea838 Fallback to userspace copy if splice failed
Fix #9032
2025-09-03 16:10:18 -07:00
topjohnwu
bc89c60977 Run cargo fmt 2025-09-02 22:06:08 -07:00
topjohnwu
bd657c354c Reduce FFI across C++/Rust 2025-09-02 22:06:08 -07:00
MONA
675b5f9565 feat(i18n): Add Hinglish translation 2025-09-02 01:27:58 -07:00
MONA
1b2c43268e feat(i18n): Add Hinglish translation 2025-09-02 01:27:58 -07:00
topjohnwu
653730d75e Make cxx binding generate less code 2025-08-29 01:44:06 -07:00
topjohnwu
d472e9c36e Update cargo dependencies 2025-08-28 22:01:35 -07:00
topjohnwu
484d53ef7e Update to ONDK r29.2 2025-08-28 16:15:59 -07:00
topjohnwu
c4e2985677 Migrate resetprop to Rust 2025-08-27 22:48:48 -07:00
topjohnwu
42d9f87bc9 Cleanup resetprop code 2025-08-27 22:48:48 -07:00
topjohnwu
2e4fa6864c Make Utf8CStr a first class citizen in C++ codebase
Utf8CStr is in many cases a better string view class than
std::string_view, because it provides "view" access to a string buffer
that is guaranteed to be null terminated. It also has the additional
benefit of being UTF-8 verified and can seemlessly cross FFI boundaries.

We would want to start use more Utf8CStr in our existing C++ codebase.
2025-08-27 22:48:48 -07:00
topjohnwu
e2abb648ac Update system_properties 2025-08-27 10:12:51 -07:00
topjohnwu
3599dcedfb Make argh directly parse into Utf8CString 2025-08-27 01:26:41 -07:00
topjohnwu
ea72666df8 Only specify ADB port for tests 2025-08-25 15:34:04 -07:00
topjohnwu
bd2a47ba18 Merge libbase cpp files 2025-08-25 01:31:47 -07:00
topjohnwu
b861671391 Cleanup libbase 2025-08-25 01:31:47 -07:00
topjohnwu
e91fc75d86 Consolidate for_each implementation into Rust 2025-08-25 01:31:47 -07:00
LoveSy
78f5cd55c7 Use lzma-rust2 for xz and lzma compression and decompression 2025-08-24 00:23:55 -07:00
topjohnwu
9787a69528 Make all decoders Read instead of Write
Most libraries only implement Read for decoders
2025-08-24 00:23:55 -07:00
topjohnwu
87b8fe374d Fix magiskboot cli parsing 2025-08-23 20:31:15 -07:00
topjohnwu
7b706bb0cb Cleanup and fix compress/decompress command 2025-08-23 20:31:15 -07:00
topjohnwu
c1491b8d2b Fix LoggedResult implementation error 2025-08-23 15:25:52 -07:00
LoveSy
5cbaf2ae11 Use super let to simplify code 2025-08-22 12:05:44 -07:00
topjohnwu
8ebc6207b4 Merge headers 2025-08-22 12:03:47 -07:00
topjohnwu
7848ee616b Cleanup magiskboot main function 2025-08-22 12:03:47 -07:00
topjohnwu
fd193c3cae Simplify ResultExt implementation
Also introduce OptionExt
2025-08-22 12:03:47 -07:00
topjohnwu
36d33c7a85 Make log_err directly return LoggedResult 2025-08-22 12:03:47 -07:00
topjohnwu
5caf28d27c Hide harmless error reporting 2025-08-22 12:03:47 -07:00
topjohnwu
2c39d0234d Fix compression format detection 2025-08-21 12:21:22 -07:00
topjohnwu
c313812129 Simplify magiskboot FFI 2025-08-21 12:21:22 -07:00
topjohnwu
af51880a81 Introduce CmdArgs for argument parsing in Rust 2025-08-21 12:21:22 -07:00
LoveSy
db8d832707 Move magiskboot cli to argh 2025-08-20 21:40:34 -07:00
Wang Han
8dc23d0ead Avoid triggering magisk --zygote-restart twice
We have already used on restart keyword to inject zygote restart, so
triggering it here on prop is not needed.
2025-08-20 12:34:39 -07:00
topjohnwu
b4287700d5 Increase timeout to 15 minutes 2025-08-20 11:23:18 -07:00
topjohnwu
8d10ab89f2 Set zygisk properties in Rust 2025-08-20 11:23:18 -07:00
topjohnwu
49fdc1addb Prevent setting zygisk prop twice 2025-08-20 11:23:18 -07:00
96 changed files with 3465 additions and 3570 deletions

View File

@@ -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
View File

@@ -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

View File

@@ -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

View 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>

View 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>

View File

@@ -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 = {}

View File

@@ -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
View File

@@ -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"

View File

@@ -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"

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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)));
}

View File

@@ -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()) }
}
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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]),
})
}
}

View File

@@ -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>>;

View File

@@ -0,0 +1 @@
../../../external/cxx-rs/include/cxx.h

View File

@@ -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"]

View File

@@ -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);
}

View File

@@ -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);
}
};

View File

@@ -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) })
}
}

View File

@@ -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(

View File

@@ -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); }

View File

@@ -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

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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"] }

View File

@@ -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);
}

View File

@@ -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
View 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)
}

View File

@@ -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(())
}

View File

@@ -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)
}

View File

@@ -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)?),
}
}

View 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;
}

View File

@@ -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
View 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)
}

View File

@@ -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;
}
}

View File

@@ -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());
}

View File

@@ -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;
}

View File

@@ -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());

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -25,3 +25,4 @@ quick-protobuf = { workspace = true }
bytemuck = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
bit-set = { workspace = true }
argh = { workspace = true }

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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")]

View File

@@ -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;

View File

@@ -2,6 +2,7 @@
#include <android/log.h>
#include <sys/syscall.h>
#include <string>
#include <map>
#include <core.hpp>

View File

@@ -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;
}
}

View File

@@ -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()); }

View File

@@ -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);
}

View File

@@ -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() */

View File

@@ -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"]

View File

@@ -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();
}
}
}

View File

@@ -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

View 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 {

View File

@@ -1,4 +0,0 @@
pub use persist::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
mod persist;
mod proto;

View 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()
}

View 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() }
}
}

View File

@@ -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(())
}

View File

@@ -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);
}

View 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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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)
}
}

View File

@@ -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());
}

View File

@@ -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;
}

View File

@@ -1,3 +1,3 @@
mod daemon;
pub use daemon::zygisk_should_load_module;
pub use daemon::{ZygiskState, zygisk_should_load_module};

View File

@@ -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

View File

@@ -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 = []

View File

@@ -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;
}

Submodule native/src/external/xz deleted from 2327a461e1

View File

@@ -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();

View File

@@ -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[]);

View File

@@ -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;
}

View File

@@ -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>;

View File

@@ -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,

View File

@@ -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;
});

View File

@@ -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();

View File

@@ -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";

View File

@@ -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;

View File

@@ -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 }

View File

@@ -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>;

View File

@@ -4,7 +4,7 @@
#include <map>
#include <string_view>
#include <cxx.h>
#include <rust/cxx.h>
#include <sepol/policydb/policydb.h>

View File

@@ -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;

View File

@@ -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
});

View File

@@ -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