Compare commits

...

31 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
93 changed files with 2819 additions and 3135 deletions

3
.gitmodules vendored
View File

@@ -4,9 +4,6 @@
[submodule "lz4"] [submodule "lz4"]
path = native/src/external/lz4 path = native/src/external/lz4
url = https://github.com/lz4/lz4.git url = https://github.com/lz4/lz4.git
[submodule "xz"]
path = native/src/external/xz
url = https://github.com/xz-mirror/xz.git
[submodule "libcxx"] [submodule "libcxx"]
path = native/src/external/libcxx path = native/src/external/libcxx
url = https://github.com/topjohnwu/libcxx.git url = https://github.com/topjohnwu/libcxx.git

View File

@@ -55,7 +55,7 @@ fun Project.setupCommon() {
compileSdkVersion(36) compileSdkVersion(36)
buildToolsVersion = "36.0.0" buildToolsVersion = "36.0.0"
ndkPath = "$sdkDirectory/ndk/magisk" ndkPath = "$sdkDirectory/ndk/magisk"
ndkVersion = "28.1.13356709" ndkVersion = "29.0.13846066"
defaultConfig { defaultConfig {
minSdk = 23 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 subprocess
import sys import sys
import tarfile import tarfile
import textwrap
import urllib.request import urllib.request
from pathlib import Path from pathlib import Path
from zipfile import ZipFile from zipfile import ZipFile
@@ -71,7 +70,7 @@ default_archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
default_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"} default_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
support_targets = default_targets | {"resetprop"} support_targets = default_targets | {"resetprop"}
rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"} rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
ondk_version = "r28.5" ondk_version = "r29.2"
# Global vars # Global vars
config = {} config = {}

View File

@@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \
core/sqlite.cpp \ core/sqlite.cpp \
core/thread.cpp \ core/thread.cpp \
core/core-rs.cpp \ core/core-rs.cpp \
core/resetprop/resetprop.cpp \ core/resetprop/sys.cpp \
core/su/su.cpp \ core/su/su.cpp \
core/zygisk/entry.cpp \ core/zygisk/entry.cpp \
core/zygisk/module.cpp \ core/zygisk/module.cpp \
@@ -81,13 +81,11 @@ include $(CLEAR_VARS)
LOCAL_MODULE := magiskboot LOCAL_MODULE := magiskboot
LOCAL_STATIC_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \
libbase \ libbase \
liblzma \
liblz4 \ liblz4 \
libboot-rs libboot-rs
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
boot/bootimg.cpp \ boot/bootimg.cpp \
boot/format.cpp \
boot/boot-rs.cpp boot/boot-rs.cpp
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static
@@ -125,7 +123,7 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
core/applet_stub.cpp \ core/applet_stub.cpp \
core/resetprop/resetprop.cpp \ core/resetprop/sys.cpp \
core/core-rs.cpp core/core-rs.cpp
LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=resetprop_main 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" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]] [[package]]
name = "bitflags" name = "block-buffer"
version = "2.9.1" version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@@ -115,18 +118,18 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.23.1" version = "1.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
dependencies = [ dependencies = [
"bytemuck_derive", "bytemuck_derive",
] ]
[[package]] [[package]]
name = "bytemuck_derive" name = "bytemuck_derive"
version = "1.10.0" version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -150,33 +153,33 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.31" version = "1.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
dependencies = [ dependencies = [
"shlex", "shlex",
] ]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.1" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.42" version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
] ]
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.42" version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"clap_lex", "clap_lex",
@@ -191,10 +194,11 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
[[package]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [ dependencies = [
"serde",
"termcolor", "termcolor",
"unicode-width", "unicode-width",
] ]
@@ -234,6 +238,21 @@ dependencies = [
"libc", "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]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.0"
@@ -257,6 +276,16 @@ dependencies = [
"zeroize", "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]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.2.0-rc.3" version = "0.2.0-rc.3"
@@ -279,7 +308,7 @@ dependencies = [
[[package]] [[package]]
name = "cxx" name = "cxx"
version = "1.0.137" version = "1.0.170"
dependencies = [ dependencies = [
"cc", "cc",
"cxxbridge-cmd", "cxxbridge-cmd",
@@ -290,9 +319,10 @@ dependencies = [
[[package]] [[package]]
name = "cxx-gen" name = "cxx-gen"
version = "0.7.137" version = "0.7.170"
dependencies = [ dependencies = [
"codespan-reporting", "codespan-reporting",
"indexmap",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@@ -300,10 +330,11 @@ dependencies = [
[[package]] [[package]]
name = "cxxbridge-cmd" name = "cxxbridge-cmd"
version = "1.0.137" version = "1.0.170"
dependencies = [ dependencies = [
"clap", "clap",
"codespan-reporting", "codespan-reporting",
"indexmap",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@@ -311,12 +342,13 @@ dependencies = [
[[package]] [[package]]
name = "cxxbridge-flags" name = "cxxbridge-flags"
version = "1.0.137" version = "1.0.170"
[[package]] [[package]]
name = "cxxbridge-macro" name = "cxxbridge-macro"
version = "1.0.137" version = "1.0.170"
dependencies = [ dependencies = [
"indexmap",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
@@ -338,9 +370,9 @@ dependencies = [
[[package]] [[package]]
name = "der_derive" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d184a65ac0a9db9e66e0d629bea86d0402198b2a5cc01c9c16de7a6537f0ac4f" checksum = "14bfffadecb79dfde429f5dcd7553780c2cea5f7d0e72ad7c37a74f1ef79230a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -356,15 +388,25 @@ dependencies = [
"syn", "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]] [[package]]
name = "digest" name = "digest"
version = "0.11.0-rc.0" version = "0.11.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460dd7f37e4950526b54a5a6b1f41b6c8e763c58eb9a8fc8fc05ba5c2f44ca7b" checksum = "460dd7f37e4950526b54a5a6b1f41b6c8e763c58eb9a8fc8fc05ba5c2f44ca7b"
dependencies = [ dependencies = [
"block-buffer", "block-buffer 0.11.0-rc.4",
"const-oid", "const-oid",
"crypto-common", "crypto-common 0.2.0-rc.3",
"subtle", "subtle",
] ]
@@ -375,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112839e868b3376c2066506d42331023165d687a7ed38b2ed77f28763d9a7742" checksum = "112839e868b3376c2066506d42331023165d687a7ed38b2ed77f28763d9a7742"
dependencies = [ dependencies = [
"der", "der",
"digest", "digest 0.11.0-rc.0",
"elliptic-curve", "elliptic-curve",
"rfc6979", "rfc6979",
"signature", "signature",
@@ -391,7 +433,7 @@ checksum = "c28ecec37eea07ab976cea93c7ce8b36d561cf161f6767925c1edc51024b0ad3"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"crypto-bigint", "crypto-bigint",
"digest", "digest 0.11.0-rc.0",
"ff", "ff",
"group", "group",
"hybrid-array", "hybrid-array",
@@ -403,6 +445,12 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]] [[package]]
name = "fdt" name = "fdt"
version = "0.1.5" version = "0.1.5"
@@ -438,9 +486,19 @@ dependencies = [
[[package]] [[package]]
name = "foldhash" name = "foldhash"
version = "0.1.5" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "getrandom" name = "getrandom"
@@ -465,13 +523,19 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
[[package]] [[package]]
name = "hmac" name = "hmac"
version = "0.13.0-rc.0" version = "0.13.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dc6a2fcc35ab09136c6df2cdf9ca49790701420a3a6b5db0987dddbabc79b21" checksum = "8dc6a2fcc35ab09136c6df2cdf9ca49790701420a3a6b5db0987dddbabc79b21"
dependencies = [ dependencies = [
"digest", "digest 0.11.0-rc.0",
] ]
[[package]] [[package]]
@@ -485,16 +549,26 @@ dependencies = [
] ]
[[package]] [[package]]
name = "libbz2-rs-sys" name = "indexmap"
version = "0.2.1" version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "libc" name = "libc"
version = "0.2.174" version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]] [[package]]
name = "libm" name = "libm"
@@ -534,16 +608,20 @@ dependencies = [
] ]
[[package]] [[package]]
name = "lzma-sys" name = "lzma-rust2"
version = "0.1.20" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43aa8ef7ba6927e84cfb39120bbc06c29474346b956f7b88cc4899b1f2e86e"
dependencies = [ dependencies = [
"libc", "crc",
"sha2 0.10.9",
] ]
[[package]] [[package]]
name = "magisk" name = "magisk"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"argh",
"base", "base",
"bit-set", "bit-set",
"bytemuck", "bytemuck",
@@ -569,10 +647,11 @@ dependencies = [
"cxx", "cxx",
"cxx-gen", "cxx-gen",
"der", "der",
"digest", "digest 0.11.0-rc.0",
"fdt", "fdt",
"flate2", "flate2",
"lz4", "lz4",
"lzma-rust2",
"num-traits", "num-traits",
"p256", "p256",
"p384", "p384",
@@ -581,10 +660,9 @@ dependencies = [
"quick-protobuf", "quick-protobuf",
"rsa", "rsa",
"sha1", "sha1",
"sha2", "sha2 0.11.0-rc.0",
"size", "size",
"x509-cert", "x509-cert",
"xz2",
"zopfli", "zopfli",
] ]
@@ -669,7 +747,7 @@ dependencies = [
"elliptic-curve", "elliptic-curve",
"primefield", "primefield",
"primeorder", "primeorder",
"sha2", "sha2 0.11.0-rc.0",
] ]
[[package]] [[package]]
@@ -682,7 +760,7 @@ dependencies = [
"elliptic-curve", "elliptic-curve",
"primefield", "primefield",
"primeorder", "primeorder",
"sha2", "sha2 0.11.0-rc.0",
] ]
[[package]] [[package]]
@@ -697,7 +775,7 @@ dependencies = [
"primefield", "primefield",
"primeorder", "primeorder",
"rand_core", "rand_core",
"sha2", "sha2 0.11.0-rc.0",
] ]
[[package]] [[package]]
@@ -762,9 +840,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.95" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -820,11 +898,11 @@ dependencies = [
"const-oid", "const-oid",
"crypto-bigint", "crypto-bigint",
"crypto-primes", "crypto-primes",
"digest", "digest 0.11.0-rc.0",
"pkcs1", "pkcs1",
"pkcs8", "pkcs8",
"rand_core", "rand_core",
"sha2", "sha2 0.11.0-rc.0",
"signature", "signature",
"spki", "spki",
"subtle", "subtle",
@@ -839,9 +917,9 @@ checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.21" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]] [[package]]
name = "sec1" name = "sec1"
@@ -894,7 +972,18 @@ checksum = "6f9318facddf9ac32a33527066936837e189b3f23ced6edc1603720ead5e2b3d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "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]] [[package]]
@@ -905,7 +994,7 @@ checksum = "aa1d2e6b3cc4e43a8258a9a3b17aa5dfd2cc5186c7024bba8a64aa65b2c71a59"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"digest", "digest 0.11.0-rc.0",
] ]
[[package]] [[package]]
@@ -920,7 +1009,7 @@ version = "3.0.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4835c3b5ecb10171941a4998a95a3a76ecac1c5ae8e6954f2ad030acd1c7e8ab" checksum = "4835c3b5ecb10171941a4998a95a3a76ecac1c5ae8e6954f2ad030acd1c7e8ab"
dependencies = [ dependencies = [
"digest", "digest 0.11.0-rc.0",
"rand_core", "rand_core",
] ]
@@ -960,9 +1049,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.104" version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -980,18 +1069,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.12" version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.12" version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1033,9 +1122,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.14" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
@@ -1044,38 +1133,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]] [[package]]
name = "wasi" name = "version_check"
version = "0.14.2+wasi-0.2.4" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"wit-bindgen-rt", "wit-bindgen",
] ]
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22"
dependencies = [ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]] [[package]]
name = "windows-sys" name = "windows-link"
version = "0.59.0" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [ dependencies = [
"windows-link",
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm",
"windows_aarch64_msvc", "windows_aarch64_msvc",
"windows_i686_gnu", "windows_i686_gnu",
@@ -1088,60 +1190,57 @@ dependencies = [
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]] [[package]]
name = "windows_i686_gnullvm" name = "windows_i686_gnullvm"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]] [[package]]
name = "wit-bindgen-rt" name = "wit-bindgen"
version = "0.39.0" version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "x509-cert" name = "x509-cert"
@@ -1155,15 +1254,6 @@ dependencies = [
"tls_codec", "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]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.8.1" version = "1.8.1"

View File

@@ -10,20 +10,20 @@ edition = "2024"
[workspace.dependencies] [workspace.dependencies]
cxx = { path = "external/cxx-rs" } cxx = { path = "external/cxx-rs" }
cxx-gen = { path = "external/cxx-rs/gen/lib" } cxx-gen = { path = "external/cxx-rs/gen/lib" }
libc = "0.2.174" libc = "0.2.175"
cfg-if = "1.0.1" cfg-if = "1.0.3"
num-traits = "0.2.19" num-traits = "0.2.19"
num-derive = "0.4.2" num-derive = "0.4.2"
thiserror = "2.0.12" thiserror = "2.0.16"
byteorder = "1.5.0" byteorder = "1.5.0"
size = "0.5.0" size = "0.5.0"
bytemuck = "1.23.1" bytemuck = "1.23.2"
fdt = "0.1.5" fdt = "0.1.5"
const_format = "0.2.34" const_format = "0.2.34"
bit-set = "0.8.0" bit-set = "0.8.0"
syn = "2.0.104" syn = "2.0.106"
quote = "1.0.40" quote = "1.0.40"
proc-macro2 = "1.0.95" proc-macro2 = "1.0.101"
argh = { version = "0.1.13", default-features = false } argh = { version = "0.1.13", default-features = false }
pb-rs = { version = "0.10.0", default-features = false } pb-rs = { version = "0.10.0", default-features = false }
quick-protobuf = "0.8.1" quick-protobuf = "0.8.1"
@@ -31,7 +31,7 @@ flate2 = { version = "1.1.2", default-features = false }
bzip2 = { version = "0.6.0" } bzip2 = { version = "0.6.0" }
zopfli = "0.8.2" zopfli = "0.8.2"
lz4 = "1.28.1" 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 # Rust crypto crates are tied together
sha1 = "0.11.0-rc.0" sha1 = "0.11.0-rc.0"
@@ -40,15 +40,14 @@ digest = "0.11.0-rc.0"
p256 = "0.14.0-pre.9" p256 = "0.14.0-pre.9"
p384 = "0.14.0-pre.9" p384 = "0.14.0-pre.9"
p521 = "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" x509-cert = "0.3.0-rc.1"
der = "0.8.0-rc.7" der = "=0.8.0-rc.7"
[patch.crates-io] [patch.crates-io]
pb-rs = { git = "https://github.com/tafia/quick-protobuf.git" } pb-rs = { git = "https://github.com/tafia/quick-protobuf.git" }
quick-protobuf = { git = "https://github.com/tafia/quick-protobuf.git" } quick-protobuf = { git = "https://github.com/tafia/quick-protobuf.git" }
lz4-sys = { path = "external/lz4-sys" } lz4-sys = { path = "external/lz4-sys" }
lzma-sys = { path = "external/lzma-sys" }
[profile.dev] [profile.dev]
opt-level = "z" opt-level = "z"

View File

@@ -7,17 +7,12 @@ LOCAL_MODULE := libbase
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
src/include \ src/include \
$(LOCAL_PATH)/include \ $(LOCAL_PATH)/include \
src/external/cxx-rs/include \
out/generated out/generated
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_EXPORT_STATIC_LIBRARIES := libcxx LOCAL_EXPORT_STATIC_LIBRARIES := libcxx
LOCAL_STATIC_LIBRARIES := libcxx LOCAL_STATIC_LIBRARIES := libcxx
LOCAL_CFLAGS := -DRUST_CXX_NO_EXCEPTIONS
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
new.cpp \ base.cpp \
files.cpp \
misc.cpp \
logging.cpp \
base-rs.cpp \ base-rs.cpp \
../external/cxx-rs/src/cxx.cc ../external/cxx-rs/src/cxx.cc
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)

View File

@@ -1,6 +1,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/mman.h>
#include <android/log.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <syscall.h> #include <syscall.h>
@@ -11,18 +13,27 @@
using namespace std; 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 { bool byte_view::contains(byte_view pattern) const {
return _buf != nullptr && memmem(_buf, _sz, pattern._buf, pattern._sz) != nullptr; return _buf != nullptr && memmem(_buf, _sz, pattern._buf, pattern._sz) != nullptr;
} }
bool byte_view::equals(byte_view o) const { bool byte_view::operator==(byte_view rhs) const {
return _sz == o._sz && memcmp(_buf, o._buf, _sz) == 0; return _sz == rhs._sz && memcmp(_buf, rhs._buf, _sz) == 0;
}
heap_data byte_view::clone() const {
heap_data copy(_sz);
memcpy(copy._buf, _buf, _sz);
return copy;
} }
void byte_data::swap(byte_data &o) { void byte_data::swap(byte_data &o) {
@@ -30,28 +41,25 @@ void byte_data::swap(byte_data &o) {
std::swap(_sz, o._sz); 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; rust::Vec<size_t> v;
if (_buf == nullptr) if (_buf == nullptr)
return v; return v;
auto p = _buf; auto p = _buf;
auto eof = _buf + _sz; auto eof = _buf + _sz;
while (p < eof) { 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) if (p == nullptr)
return v; return v;
memset(p, 0, from.sz()); memset(p, 0, from.size());
memcpy(p, to.buf(), to.sz()); memcpy(p, to.data(), to.size());
v.push_back(p - _buf); v.push_back(p - _buf);
p += from.sz(); p += from.size();
} }
return v; return v;
} }
rust::Vec<size_t> mut_u8_patch( rust::Vec<size_t> mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to) {
rust::Slice<uint8_t> buf,
rust::Slice<const uint8_t> from,
rust::Slice<const uint8_t> to) {
byte_data data(buf); byte_data data(buf);
return data.patch(from, to); 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); return std::min(strlcpy(dest, src, size), size - 1);
} }
extern "C" void cxx$utf8str$new(rust::Utf8CStr *self, const void *s, size_t len); #undef vsnprintf
extern "C" const char *cxx$utf8str$ptr(const rust::Utf8CStr *self); static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) {
extern "C" size_t cxx$utf8str$len(const rust::Utf8CStr *self); 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); cxx$utf8str$new(this, s, len);
} }
const char *rust::Utf8CStr::data() const { const char *Utf8CStr::data() const {
return cxx$utf8str$ptr(this); return cxx$utf8str$ptr(this);
} }
size_t rust::Utf8CStr::length() const { size_t Utf8CStr::length() const {
return cxx$utf8str$len(this); return cxx$utf8str$len(this);
} }

View File

@@ -7,7 +7,7 @@ use std::fmt::{Debug, Display, Formatter, Write};
use std::ops::Deref; use std::ops::Deref;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::Utf8Error; use std::str::{FromStr, Utf8Error};
use std::{fmt, mem, slice, str}; use std::{fmt, mem, slice, str};
use thiserror::Error; use thiserror::Error;
@@ -56,7 +56,7 @@ pub mod buf {
} }
#[inline(always)] #[inline(always)]
pub fn wrap(buf: &mut [u8]) -> Utf8CStrBufRef { pub fn wrap(buf: &mut [u8]) -> Utf8CStrBufRef<'_> {
Utf8CStrBufRef::from(buf) Utf8CStrBufRef::from(buf)
} }
@@ -200,7 +200,18 @@ impl From<String> for Utf8CString {
impl From<&str> for Utf8CString { impl From<&str> for Utf8CString {
fn from(value: &str) -> Self { 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)] #[inline(always)]
pub fn as_bytes_with_nul(&self) -> &[u8] { pub fn as_bytes_with_nul(&self) -> &[u8] {
&self.0 &self.0
@@ -351,7 +369,7 @@ impl AsRef<Utf8CStr> for Utf8CStr {
// Notice that we only implement ExternType on Utf8CStr *reference* // Notice that we only implement ExternType on Utf8CStr *reference*
unsafe impl ExternType for &Utf8CStr { unsafe impl ExternType for &Utf8CStr {
type Id = type_id!("rust::Utf8CStr"); type Id = type_id!("Utf8CStr");
type Kind = cxx::kind::Trivial; 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 // 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 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; use crate::files::map_file_at;
pub(crate) use crate::xwrap::*; pub(crate) use crate::xwrap::*;
use crate::{ 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, 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(no_mangle)]
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
unsafe { unsafe {
@@ -112,10 +110,10 @@ pub(crate) unsafe fn readlinkat(
#[unsafe(export_name = "cp_afc")] #[unsafe(export_name = "cp_afc")]
unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) -> bool { unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
unsafe { unsafe {
if let Ok(src) = Utf8CStr::from_ptr(src) { if let Ok(src) = Utf8CStr::from_ptr(src)
if let Ok(dest) = Utf8CStr::from_ptr(dest) { && let Ok(dest) = Utf8CStr::from_ptr(dest)
return src.copy_to(dest).log_cxx().is_ok(); {
} return src.copy_to(dest).log_cxx().is_ok();
} }
false 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(export_name = "mv_path")]
unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool { unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
unsafe { unsafe {
if let Ok(src) = Utf8CStr::from_ptr(src) { if let Ok(src) = Utf8CStr::from_ptr(src)
if let Ok(dest) = Utf8CStr::from_ptr(dest) { && let Ok(dest) = Utf8CStr::from_ptr(dest)
return src.move_to(dest).log_cxx().is_ok(); {
} return src.move_to(dest).log_cxx().is_ok();
} }
false 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(export_name = "link_path")]
unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool { unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
unsafe { unsafe {
if let Ok(src) = Utf8CStr::from_ptr(src) { if let Ok(src) = Utf8CStr::from_ptr(src)
if let Ok(dest) = Utf8CStr::from_ptr(dest) { && let Ok(dest) = Utf8CStr::from_ptr(dest)
return src.link_to(dest).log_cxx().is_ok(); {
} return src.link_to(dest).log_cxx().is_ok();
} }
false 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(export_name = "clone_attr")]
unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char) -> bool { unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
unsafe { unsafe {
if let Ok(src) = Utf8CStr::from_ptr(src) { if let Ok(src) = Utf8CStr::from_ptr(src)
if let Ok(dest) = Utf8CStr::from_ptr(dest) { && let Ok(dest) = Utf8CStr::from_ptr(dest)
return clone_attr(src, dest).log_cxx().is_ok(); {
} return clone_attr(src, dest).log_cxx().is_ok();
} }
false false
} }
@@ -178,3 +176,14 @@ unsafe extern "C" fn str_ptr(this: &&Utf8CStr) -> *const u8 {
unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize { unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize {
this.len() 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 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 }; let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
self.dir.unlink_at(self.name(), flag) 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) 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() { if !self.is_dir() {
return Err(OsError::with_os_error( return Err(OsError::with_os_error(
libc::ENOTDIR, libc::ENOTDIR,
@@ -84,7 +84,7 @@ impl DirEntry<'_> {
self.dir.open_as_dir_at(self.name()) 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() { if self.is_dir() {
return Err(OsError::with_os_error( return Err(OsError::with_os_error(
libc::EISDIR, libc::EISDIR,
@@ -141,7 +141,7 @@ pub enum WalkResult {
} }
impl Directory { impl Directory {
fn borrow(&self) -> BorrowedDirectory { fn borrow(&self) -> BorrowedDirectory<'_> {
BorrowedDirectory { BorrowedDirectory {
inner: self.inner, inner: self.inner,
phantom: PhantomData, phantom: PhantomData,
@@ -164,13 +164,13 @@ impl Directory {
} }
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 = unsafe { libc::opendir(path.as_ptr()) };
let dirp = dirp.as_os_result("opendir", Some(path), None)?; let dirp = dirp.as_os_result("opendir", Some(path), None)?;
Ok(Directory { inner: dirp }) Ok(Directory { inner: dirp })
} }
pub fn read(&mut self) -> OsResult<'static, Option<DirEntry>> { pub fn read(&mut self) -> OsResult<'static, Option<DirEntry<'_>>> {
*errno() = 0; *errno() = 0;
let e = unsafe { libc::readdir(self.inner.as_ptr()) }; let e = unsafe { libc::readdir(self.inner.as_ptr()) };
if e.is_null() { if e.is_null() {
@@ -481,7 +481,7 @@ impl AsRawFd for Directory {
} }
impl AsFd for Directory { impl AsFd for Directory {
fn as_fd(&self) -> BorrowedFd { fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } 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 #pragma once
#include <sys/stat.h> #include <sys/stat.h>
#include <linux/fs.h>
#include <functional> #include <functional>
#include <string_view> #include <string_view>
#include <string> #include <string>
#include <vector>
#include <linux/fs.h> #include "base-rs.hpp"
#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;
}
struct mmap_data : public byte_data { struct mmap_data : public byte_data {
static_assert((sizeof(void *) == 8 && BLKGETSIZE64 == 0x80081272) || static_assert((sizeof(void *) == 8 && BLKGETSIZE64 == 0x80081272) ||
@@ -45,24 +33,26 @@ bool fclone_attr(int src, int dest);
} // extern "C" } // 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(int fd);
std::string full_read(const char *filename); std::string full_read(const char *filename);
void write_zero(int fd, size_t size); 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); 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 sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>; using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
sDIR make_dir(DIR *dp); sDIR make_dir(DIR *dp);

View File

@@ -57,12 +57,12 @@ impl<T: Read + Seek> ReadSeekExt for T {
} }
pub trait BufReadExt { pub trait BufReadExt {
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, f: F); fn for_each_line<F: FnMut(&mut String) -> bool>(&mut self, f: F);
fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, f: F); fn for_each_prop<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
} }
impl<T: BufRead> BufReadExt for T { 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(); let mut buf = String::new();
loop { loop {
match self.read_line(&mut buf) { 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) { fn for_each_prop<F: FnMut(&str, &str) -> bool>(&mut self, mut f: F) {
self.foreach_lines(|line| { 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(); let line = line.trim();
if line.starts_with('#') { if line.starts_with('#') {
return true; 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 { unsafe {
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).as_os_result( let fd = libc::open(path.as_ptr(), flags, mode as c_uint).as_os_result(
"open", "open",
@@ -201,11 +224,11 @@ impl Utf8CStr {
unsafe { mem::transmute(self) } 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)?)) 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)?)) 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) } unsafe { libc::remove(self.as_ptr()).check_os_err("remove", Some(self), None) }
} }
@@ -240,7 +263,7 @@ impl Utf8CStr {
} }
#[allow(clippy::unnecessary_cast)] #[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(); buf.clear();
unsafe { unsafe {
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1) let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
@@ -251,7 +274,7 @@ impl Utf8CStr {
Ok(()) Ok(())
} }
pub fn mkdir(&self, mode: mode_t) -> OsResult<()> { pub fn mkdir(&self, mode: mode_t) -> OsResult<'_, ()> {
unsafe { unsafe {
if libc::mkdir(self.as_ptr(), mode) < 0 { if libc::mkdir(self.as_ptr(), mode) < 0 {
if *errno() == EEXIST { if *errno() == EEXIST {
@@ -294,7 +317,7 @@ impl Utf8CStr {
} }
// Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp // 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 fd = self.open(O_PATH | O_CLOEXEC)?;
let mut st1: libc::stat; let mut st1: libc::stat;
let mut st2: libc::stat; let mut st2: libc::stat;
@@ -318,7 +341,7 @@ impl Utf8CStr {
Ok(()) Ok(())
} }
pub fn get_attr(&self) -> OsResult<FileAttr> { pub fn get_attr(&self) -> OsResult<'_, FileAttr> {
let mut attr = FileAttr::new(); let mut attr = FileAttr::new();
unsafe { unsafe {
libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?; libc::lstat(self.as_ptr(), &mut attr.st).check_os_err("lstat", Some(self), None)?;
@@ -352,7 +375,7 @@ impl Utf8CStr {
Ok(()) Ok(())
} }
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> { pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
unsafe { unsafe {
let sz = libc::lgetxattr( let sz = libc::lgetxattr(
self.as_ptr(), 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) } 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 } 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(); let mut attr = FileAttr::new();
unsafe { unsafe {
libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?; libc::stat(self.as_ptr(), &mut attr.st).check_os_err("stat", Some(self), None)?;
@@ -519,7 +542,7 @@ impl FsPathFollow {
Ok(()) Ok(())
} }
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<()> { pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> OsResult<'_, ()> {
unsafe { unsafe {
let sz = libc::getxattr( let sz = libc::getxattr(
self.as_ptr(), self.as_ptr(),
@@ -617,7 +640,7 @@ pub fn fd_get_attr(fd: RawFd) -> OsResult<'static, FileAttr> {
Ok(attr) Ok(attr)
} }
pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> OsResult<()> { pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> OsResult<'_, ()> {
unsafe { unsafe {
libc::fchmod(fd, (attr.st.st_mode & 0o777).as_()).check_os_err("fchmod", None, None)?; 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)?; 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(()) Ok(())
} }
pub fn fd_set_secontext(fd: RawFd, con: &Utf8CStr) -> OsResult<()> { pub fn fd_set_secontext(fd: RawFd, con: &Utf8CStr) -> OsResult<'_, ()> {
unsafe { unsafe {
libc::fsetxattr( libc::fsetxattr(
fd, fd,
@@ -675,11 +698,11 @@ pub fn fclone_attr(a: RawFd, b: RawFd) -> OsResult<'static, ()> {
pub struct MappedFile(&'static mut [u8]); pub struct MappedFile(&'static mut [u8]);
impl MappedFile { impl MappedFile {
pub fn open(path: &Utf8CStr) -> OsResult<MappedFile> { pub fn open(path: &Utf8CStr) -> OsResult<'_, MappedFile> {
Ok(MappedFile(map_file(path, false)?)) 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)?)) 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 // 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) } 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 res = vec![];
let mut path = format!("/proc/{pid}/mountinfo"); let mut path = format!("/proc/{pid}/mountinfo");
if let Ok(file) = Utf8CStr::from_string(&mut path).open(O_RDONLY | O_CLOEXEC) { 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) parse_mount_info_line(line)
.map(|info| res.push(info)) .map(|info| res.push(info))
.is_some() .is_some()
@@ -862,3 +885,19 @@ pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
} }
res 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 #pragma once
#include "../xwrap.hpp" #include "../xwrap.hpp"
#include "../files.hpp"
#include "../misc.hpp" #include "../misc.hpp"
#include "../logging.hpp"
#include "../base-rs.hpp" #include "../base-rs.hpp"
#include "../files.hpp"
#include "../logging.hpp"
using rust::xpipe2; using rust::xpipe2;
using rust::fd_path;
using kv_pairs = std::vector<std::pair<std::string, std::string>>; 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++" { unsafe extern "C++" {
include!("misc.hpp"); include!("misc.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"] #[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr; type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>; fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
fn fork_dont_care() -> i32; 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" { extern "Rust" {
@@ -56,13 +61,14 @@ pub mod ffi {
fn set_log_level_state_cxx(level: LogLevelCxx, enabled: bool); fn set_log_level_state_cxx(level: LogLevelCxx, enabled: bool);
fn exit_on_error(b: bool); fn exit_on_error(b: bool);
fn cmdline_logging(); 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"] #[namespace = "rust"]
extern "Rust" { extern "Rust" {
fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32; 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"] #[cxx_name = "map_file"]
fn map_file_for_cxx(path: Utf8CStrRef, rw: bool) -> &'static mut [u8]; fn map_file_for_cxx(path: Utf8CStrRef, rw: bool) -> &'static mut [u8];
#[cxx_name = "map_file_at"] #[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 <functional>
#include <string_view> #include <string_view>
#include <bitset> #include <bitset>
#include <random> #include <rust/cxx.h>
#include <cxx.h>
#include "xwrap.hpp" #include "xwrap.hpp"
@@ -46,30 +45,11 @@ private:
Func fn; Func fn;
}; };
template <typename T> template<class T>
class reversed_container { static void default_new(T *&p) { p = new T(); }
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> template<class T>
static inline void default_new(T *&p) { p = new T(); } static void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
template<class T>
static inline void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
struct StringCmp { struct StringCmp {
using is_transparent = void; using is_transparent = void;
@@ -78,49 +58,32 @@ struct StringCmp {
struct heap_data; struct heap_data;
using ByteSlice = rust::Slice<const uint8_t>;
using MutByteSlice = rust::Slice<uint8_t>;
// Interchangeable as `&[u8]` in Rust // Interchangeable as `&[u8]` in Rust
struct byte_view { struct byte_view {
byte_view() : _buf(nullptr), _sz(0) {} byte_view() : _buf(nullptr), _sz(0) {}
byte_view(const void *buf, size_t sz) : _buf((uint8_t *) buf), _sz(sz) {} 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) {} byte_view(const byte_view &o) : _buf(o._buf), _sz(o._sz) {}
// Bridging to Rust slice // Transparent conversion to Rust slice
byte_view(rust::Slice<const uint8_t> o) : byte_view(o.data(), o.size()) {} byte_view(const ByteSlice o) : byte_view(o.data(), o.size()) {}
operator rust::Slice<const uint8_t>() const { return rust::Slice<const uint8_t>(_buf, _sz); } operator ByteSlice() const { return {_buf, _sz}; }
// String as bytes // String as bytes, including null terminator
byte_view(const char *s, bool with_nul = true) byte_view(const char *s) : byte_view(s, strlen(s) + 1) {}
: 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; }
const uint8_t *data() const { return _buf; }
size_t size() const { return _sz; }
bool contains(byte_view pattern) const; bool contains(byte_view pattern) const;
bool equals(byte_view o) const; bool operator==(byte_view rhs) const;
heap_data clone() const;
protected: protected:
uint8_t *_buf; uint8_t *_buf;
size_t _sz; 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 // Interchangeable as `&mut [u8]` in Rust
@@ -128,23 +91,18 @@ struct byte_data : public byte_view {
byte_data() = default; byte_data() = default;
byte_data(void *buf, size_t sz) : byte_view(buf, sz) {} 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) {} byte_data(const byte_data &o) : byte_data(o._buf, o._sz) {}
// Transparent conversion from common C++ types to mutable byte references // Transparent conversion to Rust slice
byte_data(std::string &s, bool with_nul = true) byte_data(const MutByteSlice o) : byte_data(o.data(), o.size()) {}
: byte_data(s.data(), with_nul ? s.length() + 1 : s.length()) {} operator MutByteSlice() const { return {_buf, _sz}; }
byte_data(std::vector<uint8_t> &v) : byte_data(v.data(), v.size()) {}
// Bridging to Rust slice using byte_view::data;
byte_data(rust::Slice<uint8_t> o) : byte_data(o.data(), o.size()) {} uint8_t *data() const { return _buf; }
operator rust::Slice<uint8_t>() { return rust::Slice<uint8_t>(_buf, _sz); }
using byte_view::buf;
uint8_t *buf() { return _buf; }
void swap(byte_data &o); 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 { struct heap_data : public byte_data {
@@ -170,10 +128,7 @@ private:
int fd; int fd;
}; };
rust::Vec<size_t> mut_u8_patch( rust::Vec<size_t> mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to);
rust::Slice<uint8_t> buf,
rust::Slice<const uint8_t> from,
rust::Slice<const uint8_t> to);
uint32_t parse_uint32_hex(std::string_view s); uint32_t parse_uint32_hex(std::string_view s);
int parse_int(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 *); using thread_entry = void *(*)(void *);
extern "C" int new_daemon_thread(thread_entry entry, void *arg = nullptr); 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) { static inline std::string rtrim(std::string &&s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch) && ch != '\0'; 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); return static_cast<std::underlying_type_t<T>>(e);
} }
namespace rust {
struct Utf8CStr { struct Utf8CStr {
const char *data() const; const char *data() const;
size_t length() const; size_t length() const;
@@ -276,14 +214,14 @@ struct Utf8CStr {
Utf8CStr() : Utf8CStr("", 1) {}; Utf8CStr() : Utf8CStr("", 1) {};
Utf8CStr(const Utf8CStr &o) = default; Utf8CStr(const Utf8CStr &o) = default;
Utf8CStr(Utf8CStr &&o) = default;
Utf8CStr(const char *s) : Utf8CStr(s, strlen(s) + 1) {}; 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) {}; Utf8CStr(std::string s) : Utf8CStr(s.data(), s.length() + 1) {};
const char *c_str() const { return this->data(); } const char *c_str() const { return this->data(); }
size_t size() const { return this->length(); } size_t size() const { return this->length(); }
bool empty() const { return this->length() == 0 ; } 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: private:
#pragma clang diagnostic push #pragma clang diagnostic push
@@ -292,4 +230,19 @@ private:
#pragma clang diagnostic pop #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 crate::{Utf8CStr, Utf8CString, cstr, ffi};
use argh::EarlyExit; use argh::{EarlyExit, MissingRequirements};
use libc::c_char; use libc::c_char;
use std::fmt::Arguments; use std::{
use std::io::Write; fmt,
use std::mem::ManuallyDrop; fmt::Arguments,
use std::process::exit; io::Write,
use std::sync::Arc; mem::ManuallyDrop,
use std::sync::atomic::{AtomicPtr, Ordering}; process::exit,
use std::{fmt, slice, str}; slice, str,
sync::Arc,
sync::atomic::{AtomicPtr, Ordering},
};
pub fn errno() -> &'static mut i32 { pub fn errno() -> &'static mut i32 {
unsafe { &mut *libc::__errno() } 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> { pub trait EarlyExitExt<T> {
fn on_early_exit<F: FnOnce()>(self, print_help_msg: F) -> 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) pub struct FmtAdaptor<'a, T>(pub &'a mut T)
where where
T: Write; T: Write;
@@ -227,3 +266,35 @@ impl Chunker {
chunk 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 { unsafe {
libc::mount( libc::mount(
ptr::null(), 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 { unsafe {
libc::mount( libc::mount(
ptr::null(), 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 { unsafe {
libc::mount( libc::mount(
ptr::null(), ptr::null(),
@@ -69,13 +69,13 @@ impl Utf8CStr {
} }
} }
pub fn unmount(&self) -> OsResult<()> { pub fn unmount(&self) -> OsResult<'_, ()> {
unsafe { unsafe {
libc::umount2(self.as_ptr(), libc::MNT_DETACH).check_os_err("unmount", Some(self), None) 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 }; let flag = if recursive { libc::MS_REC } else { 0 };
unsafe { unsafe {
libc::mount( 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_export]
macro_rules! log_err { macro_rules! log_err {
() => {{
Err($crate::LoggedError::default())
}};
($($args:tt)+) => {{ ($($args:tt)+) => {{
$crate::error!($($args)+); $crate::error!($($args)+);
$crate::LoggedError::default() Err($crate::LoggedError::default())
}}; }};
} }
// Any result or option can be silenced // Any result or option can be silenced
pub trait SilentResultExt<T> { pub trait SilentLogExt<T> {
fn silent(self) -> LoggedResult<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> { fn silent(self) -> LoggedResult<T> {
match self { self.map_err(|_| LoggedError::default())
Ok(v) => Ok(v),
Err(_) => Err(LoggedError::default()),
}
} }
} }
impl<T> SilentResultExt<T> for Option<T> { impl<T> SilentLogExt<T> for Option<T> {
fn silent(self) -> LoggedResult<T> { fn silent(self) -> LoggedResult<T> {
match self { self.ok_or_else(LoggedError::default)
Some(v) => Ok(v),
None => Err(LoggedError::default()),
}
} }
} }
@@ -64,142 +61,168 @@ pub(crate) trait CxxResultExt<T> {
fn log_cxx(self) -> LoggedResult<T>; fn log_cxx(self) -> LoggedResult<T>;
} }
trait Loggable<T> { // Public API for converting Option to LoggedResult
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedResult<T>; 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>( fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
self, self,
level: LogLevel, level: LogLevel,
caller: Option<&'static Location>, caller: Option<&'static Location>,
f: F, 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> { 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))] #[cfg(not(debug_assertions))]
fn log(self) -> LoggedResult<T> { fn log(self) -> LoggedResult<T> {
self.do_log(LogLevel::Error, None) self.map_err(|e| e.do_log(LogLevel::Error, None))
} }
#[track_caller] #[track_caller]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn log(self) -> LoggedResult<T> { 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))] #[cfg(not(debug_assertions))]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> { 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] #[track_caller]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> { 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))] #[cfg(not(debug_assertions))]
fn log_ok(self) { fn log_ok(self) {
self.log().ok(); self.map_err(|e| e.do_log(LogLevel::Error, None)).ok();
} }
#[track_caller] #[track_caller]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn log_ok(self) { 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> { impl<T> ResultExt<T> for LoggedResult<T> {
fn do_log(self, _: LogLevel, _: Option<&'static Location>) -> LoggedResult<T> { fn log(self) -> LoggedResult<T> {
self self
} }
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>( #[cfg(not(debug_assertions))]
self, fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
level: LogLevel, self.inspect_err(|_| do_log_msg(LogLevel::Error, None, f))
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())
}
}
} }
#[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> { // Allow converting Loggable errors to LoggedError to support `?` operator
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedResult<T> { impl<T: Loggable> From<T> for LoggedError {
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 {
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
fn from(e: T) -> Self { fn from(e: T) -> Self {
log_with_args!(LogLevel::Error, "{:#}", e); e.do_log(LogLevel::Error, None)
LoggedError::default()
} }
#[track_caller] #[track_caller]
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn from(e: T) -> Self { fn from(e: T) -> Self {
let caller = Location::caller(); let caller = Some(Location::caller());
log_with_args!( e.do_log(LogLevel::Error, caller)
LogLevel::Error, }
"[{}:{}] {:#}", }
caller.file(),
caller.line(), // Actual logging implementation
e
); // 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() 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 // 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 xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
int xstat(const char *pathname, struct stat *buf); int xstat(const char *pathname, struct stat *buf);
int xfstat(int fd, struct stat *buf); int xfstat(int fd, struct stat *buf);
int xdup(int fd);
int xdup2(int oldfd, int newfd); int xdup2(int oldfd, int newfd);
ssize_t xreadlink(const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz); ssize_t xreadlink(const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
ssize_t xreadlinkat( ssize_t xreadlinkat(
@@ -42,7 +41,6 @@ int xmkdir(const char *pathname, mode_t mode);
int xmkdirs(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); ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
pid_t xfork(); 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); ssize_t xrealpath(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz);
int xmknod(const char * pathname, mode_t mode, dev_t dev); int xmknod(const char * pathname, mode_t mode, dev_t dev);

View File

@@ -4,9 +4,7 @@ use crate::cxx_extern::readlinkat;
use crate::{ use crate::{
BorrowedDirectory, CxxResultExt, LibcReturn, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut, BorrowedDirectory, CxxResultExt, LibcReturn, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut,
}; };
use libc::{ use libc::{c_char, c_uint, c_ulong, c_void, dev_t, mode_t, off_t, sockaddr, socklen_t};
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
};
use std::ffi::CStr; use std::ffi::CStr;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; 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)] #[unsafe(no_mangle)]
extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd { extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
unsafe { 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(no_mangle)]
unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 { unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
unsafe { unsafe {

View File

@@ -33,5 +33,5 @@ num-traits = { workspace = true }
flate2 = { workspace = true, features = ["zlib-rs"] } flate2 = { workspace = true, features = ["zlib-rs"] }
bzip2 = { workspace = true } bzip2 = { workspace = true }
lz4 = { workspace = true } lz4 = { workspace = true }
xz2 = { workspace = true } lzma-rust2 = { workspace = true }
zopfli = { workspace = true, features = ["gzip"] } zopfli = { workspace = true, features = ["gzip"] }

View File

@@ -48,6 +48,43 @@ static bool check_env(const char *name) {
return val != nullptr && val == "true"sv; 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 { void dyn_img_hdr::print() const {
uint32_t ver = header_version(); uint32_t ver = header_version();
fprintf(stderr, "%-*s [%u]\n", PADDING, "HEADER_VER", ver); 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() { 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()) { if (key == "name" && name()) {
memset(name(), 0, 16); memset(name(), 0, 16);
memcpy(name(), value.data(), value.length() > 15 ? 15 : value.length()); 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) { if (value.length() > BOOT_ARGS_SIZE) {
memcpy(cmdline(), value.data(), 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); 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 { } else {
memcpy(cmdline(), value.data(), value.length()); memcpy(cmdline(), value.data(), value.length());
} }
@@ -152,8 +189,8 @@ void dyn_img_hdr::load_hdr_file() {
boot_img::boot_img(const char *image) : boot_img::boot_img(const char *image) :
map(image), k_fmt(FileFormat::UNKNOWN), r_fmt(FileFormat::UNKNOWN), e_fmt(FileFormat::UNKNOWN) { map(image), k_fmt(FileFormat::UNKNOWN), r_fmt(FileFormat::UNKNOWN), e_fmt(FileFormat::UNKNOWN) {
fprintf(stderr, "Parsing boot image: [%s]\n", image); fprintf(stderr, "Parsing boot image: [%s]\n", image);
for (const uint8_t *addr = map.buf(); addr < map.buf() + map.sz(); ++addr) { for (const uint8_t *addr = map.data(); addr < map.data() + map.size(); ++addr) {
FileFormat fmt = check_fmt(addr, map.sz()); FileFormat fmt = check_fmt(addr, map.size());
switch (fmt) { switch (fmt) {
case FileFormat::CHROMEOS: case FileFormat::CHROMEOS:
// chromeos require external signing // 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; auto real_hdr_sz = h->page_size - AMONET_MICROLOADER_SZ;
heap_data copy(h->page_size); 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) || if (CMD_MATCH(NOOKHD_RL_MAGIC) ||
@@ -367,7 +404,7 @@ static const char *vendor_ramdisk_type(int type) {
} }
#define assert_off() \ #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"); \ fprintf(stderr, "Corrupted boot image!\n"); \
return false; \ return false; \
} }
@@ -396,7 +433,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
hdr->print(); 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(); size_t off = hdr->hdr_space();
get_block(kernel); get_block(kernel);
get_block(ramdisk); get_block(ramdisk);
@@ -410,13 +447,13 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
payload = byte_view(base_addr, off); payload = byte_view(base_addr, off);
auto tail_addr = 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 (auto size = hdr->kernel_size()) {
if (int dtb_off = find_dtb_offset(kernel, size); dtb_off > 0) { if (int dtb_off = find_dtb_offset(kernel, size); dtb_off > 0) {
kernel_dtb = byte_view(kernel + dtb_off, size - dtb_off); kernel_dtb = byte_view(kernel + dtb_off, size - dtb_off);
hdr->kernel_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()); 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, "! 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 (auto size = hdr->ramdisk_size()) {
if (hdr->vendor_ramdisk_table_size()) { if (hdr->vendor_ramdisk_table_size()) {
@@ -493,7 +530,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
fprintf(stderr, fprintf(stderr,
"%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK", "%-*s name=[%s] type=[%s] size=[%u] fmt=[%s]\n", PADDING, "VND_RAMDISK",
it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type), it.ramdisk_name, vendor_ramdisk_type(it.ramdisk_type),
it.ramdisk_size, fmt2name[fmt]); it.ramdisk_size, fmt2name(fmt));
} }
} else { } else {
r_fmt = check_fmt_lg(ramdisk, size); 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); hdr->ramdisk_size() -= sizeof(mtk_hdr);
r_fmt = check_fmt_lg(ramdisk, hdr->ramdisk_size()); 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()) { if (auto size = hdr->extra_size()) {
e_fmt = check_fmt_lg(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 // 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"); fprintf(stderr, "SAMSUNG_SEANDROID\n");
flags[SEANDROID_FLAG] = true; 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"); fprintf(stderr, "LG_BUMP_IMAGE\n");
flags[LG_BUMP_FLAG] = true; flags[LG_BUMP_FLAG] = true;
} else if (!(tail.sz() >= 4 && BUFFER_MATCH(tail.buf(), AVB_MAGIC)) && verify()) { } else if (verify()) {
// Check if the image is avb 1.0 signed
fprintf(stderr, "AVB1_SIGNED\n"); fprintf(stderr, "AVB1_SIGNED\n");
flags[AVB1_SIGNED_FLAG] = true; flags[AVB1_SIGNED_FLAG] = true;
} }
// Find AVB footer // 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)) { 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 // Double check if meta header exists
const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset); const void *meta = base_addr + __builtin_bswap64(avb_footer->vbmeta_offset);
if (BUFFER_MATCH(meta, AVB_MAGIC)) { if (BUFFER_MATCH(meta, AVB_MAGIC)) {
fprintf(stderr, "VBMETA\n"); fprintf(stderr, "VBMETA\n");
flags[AVB_FLAG] = true; flags[AVB_FLAG] = true;
vbmeta = reinterpret_cast<const AvbVBMetaImageHeader*>(meta); vbmeta = static_cast<const AvbVBMetaImageHeader*>(meta);
} }
} }
} }
@@ -547,23 +583,19 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
return true; return true;
} }
bool boot_img::verify(const char *cert) const { int split_image_dtb(Utf8CStr filename, bool skip_decomp) {
return rust::verify_boot_image(*this, cert);
}
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
mmap_data img(filename.data()); mmap_data img(filename.data());
if (size_t off = find_dtb_offset(img.buf(), img.sz()); off > 0) { if (size_t off = find_dtb_offset(img.data(), img.size()); off > 0) {
FileFormat fmt = check_fmt_lg(img.buf(), img.sz()); FileFormat fmt = check_fmt_lg(img.data(), img.size());
if (!skip_decomp && COMPRESSED(fmt)) { if (!skip_decomp && fmt_compressed(fmt)) {
int fd = creat(KERNEL_FILE, 0644); int fd = creat(KERNEL_FILE, 0644);
decompress(fmt, fd, img.buf(), off); decompress(fmt, fd, img.data(), off);
close(fd); close(fd);
} else { } 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; return 0;
} else { } else {
fprintf(stderr, "Cannot find DTB in %s\n", filename.data()); fprintf(stderr, "Cannot find DTB in %s\n", filename.data());
@@ -571,14 +603,14 @@ int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
} }
} }
int unpack(rust::Utf8CStr image, bool skip_decomp, bool hdr) { int unpack(Utf8CStr image, bool skip_decomp, bool hdr) {
const boot_img boot(image.data()); const boot_img boot(image.data());
if (hdr) if (hdr)
boot.hdr->dump_hdr_file(); boot.hdr->dump_hdr_file();
// Dump kernel // Dump kernel
if (!skip_decomp && COMPRESSED(boot.k_fmt)) { if (!skip_decomp && fmt_compressed(boot.k_fmt)) {
if (boot.hdr->kernel_size() != 0) { if (boot.hdr->kernel_size() != 0) {
int fd = creat(KERNEL_FILE, 0644); int fd = creat(KERNEL_FILE, 0644);
decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size()); decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size());
@@ -589,7 +621,7 @@ int unpack(rust::Utf8CStr image, bool skip_decomp, bool hdr) {
} }
// Dump kernel_dtb // 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 // Dump ramdisk
if (boot.hdr->vendor_ramdisk_table_size()) { if (boot.hdr->vendor_ramdisk_table_size()) {
@@ -609,13 +641,13 @@ int unpack(rust::Utf8CStr image, bool skip_decomp, bool hdr) {
} }
owned_fd fd = xopenat(dirfd, file_name, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644); 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); 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); decompress(fmt, fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
} else { } else {
xwrite(fd, boot.ramdisk + it.ramdisk_offset, it.ramdisk_size); 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) { if (boot.hdr->ramdisk_size() != 0) {
int fd = creat(RAMDISK_FILE, 0644); int fd = creat(RAMDISK_FILE, 0644);
decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size()); decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size());
@@ -629,7 +661,7 @@ int unpack(rust::Utf8CStr image, bool skip_decomp, bool hdr) {
dump(boot.second, boot.hdr->second_size(), SECOND_FILE); dump(boot.second, boot.hdr->second_size(), SECOND_FILE);
// Dump extra // Dump extra
if (!skip_decomp && COMPRESSED(boot.e_fmt)) { if (!skip_decomp && fmt_compressed(boot.e_fmt)) {
if (boot.hdr->extra_size() != 0) { if (boot.hdr->extra_size() != 0) {
int fd = creat(EXTRA_FILE, 0644); int fd = creat(EXTRA_FILE, 0644);
decompress(boot.e_fmt, fd, boot.extra, boot.hdr->extra_size()); decompress(boot.e_fmt, fd, boot.extra, boot.hdr->extra_size());
@@ -656,7 +688,7 @@ write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR) - off.header, page_size))
#define file_align() file_align_with(boot.hdr->page_size()) #define file_align() file_align_with(boot.hdr->page_size())
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) { void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) {
const boot_img boot(src_img.data()); const boot_img boot(src_img.data());
fprintf(stderr, "Repack to boot image: [%s]\n", out_img.data()); fprintf(stderr, "Repack to boot image: [%s]\n", out_img.data());
@@ -693,16 +725,16 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
// Skip DHTB header // Skip DHTB header
write_zero(fd, sizeof(dhtb_hdr)); write_zero(fd, sizeof(dhtb_hdr));
} else if (boot.flags[BLOB_FLAG]) { } 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]) { } 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]) { } 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 // Copy raw header
off.header = lseek(fd, 0, SEEK_CUR); off.header = lseek(fd, 0, SEEK_CUR);
xwrite(fd, boot.payload.buf(), hdr->hdr_space()); xwrite(fd, boot.payload.data(), hdr->hdr_space());
// kernel // kernel
off.kernel = lseek(fd, 0, SEEK_CUR); off.kernel = lseek(fd, 0, SEEK_CUR);
@@ -716,12 +748,12 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
} }
if (access(KERNEL_FILE, R_OK) == 0) { if (access(KERNEL_FILE, R_OK) == 0) {
mmap_data m(KERNEL_FILE); 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 // Always use zopfli for zImage compression
auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == FileFormat::GZIP) ? FileFormat::ZOPFLI : boot.k_fmt; auto fmt = (boot.flags[ZIMAGE_KERNEL] && boot.k_fmt == FileFormat::GZIP) ? FileFormat::ZOPFLI : boot.k_fmt;
hdr->kernel_size() = compress_len(fmt, m, fd); hdr->kernel_size() = compress_len(fmt, m, fd);
} else { } else {
hdr->kernel_size() = xwrite(fd, m.buf(), m.sz()); hdr->kernel_size() = xwrite(fd, m.data(), m.size());
} }
if (boot.flags[ZIMAGE_KERNEL]) { if (boot.flags[ZIMAGE_KERNEL]) {
@@ -732,7 +764,7 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
} else if (!skip_comp) { } else if (!skip_comp) {
// Pad zeros to make sure the zImage file size does not change // Pad zeros to make sure the zImage file size does not change
// Also ensure the last 4 bytes are the uncompressed vmlinux size // 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)); write_zero(fd, boot.hdr->kernel_size() - hdr->kernel_size() - sizeof(sz));
xwrite(fd, &sz, sizeof(sz)); xwrite(fd, &sz, sizeof(sz));
} }
@@ -747,7 +779,7 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
if (boot.flags[ZIMAGE_KERNEL]) { if (boot.flags[ZIMAGE_KERNEL]) {
// Copy zImage tail and adjust size accordingly // Copy zImage tail and adjust size accordingly
hdr->kernel_size() += boot.z_info.hdr_sz; 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 // kernel dtb
@@ -784,10 +816,10 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
mmap_data m(dirfd, file_name); mmap_data m(dirfd, file_name);
FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size); FileFormat fmt = check_fmt_lg(boot.ramdisk + it.ramdisk_offset, it.ramdisk_size);
it.ramdisk_offset = ramdisk_offset; 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); it.ramdisk_size = compress_len(fmt, m, fd);
} else { } else {
it.ramdisk_size = xwrite(fd, m.buf(), m.sz()); it.ramdisk_size = xwrite(fd, m.data(), m.size());
} }
ramdisk_offset += it.ramdisk_size; ramdisk_offset += it.ramdisk_size;
} }
@@ -801,13 +833,13 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
// A v4 boot image ramdisk will have to be merged with other vendor ramdisks, // 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 // and they have to use the exact same compression method. v4 GKIs are required to
// use lz4 (legacy), so hardcode the format here. // 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; 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); hdr->ramdisk_size() = compress_len(r_fmt, m, fd);
} else { } else {
hdr->ramdisk_size() = xwrite(fd, m.buf(), m.sz()); hdr->ramdisk_size() = xwrite(fd, m.data(), m.size());
} }
file_align(); file_align();
} }
@@ -823,10 +855,10 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
off.extra = lseek(fd, 0, SEEK_CUR); off.extra = lseek(fd, 0, SEEK_CUR);
if (access(EXTRA_FILE, R_OK) == 0) { if (access(EXTRA_FILE, R_OK) == 0) {
mmap_data m(EXTRA_FILE); 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); hdr->extra_size() = compress_len(boot.e_fmt, m, fd);
} else { } else {
hdr->extra_size() = xwrite(fd, m.buf(), m.sz()); hdr->extra_size() = xwrite(fd, m.data(), m.size());
} }
file_align(); file_align();
} }
@@ -889,8 +921,8 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
// Pad image to original size if not chromeos (as it requires post processing) // Pad image to original size if not chromeos (as it requires post processing)
if (!boot.flags[CHROMEOS_FLAG]) { if (!boot.flags[CHROMEOS_FLAG]) {
off_t current = lseek(fd, 0, SEEK_CUR); off_t current = lseek(fd, 0, SEEK_CUR);
if (current < boot.map.sz()) { if (current < boot.map.size()) {
write_zero(fd, boot.map.sz() - current); write_zero(fd, boot.map.size() - current);
} }
} }
@@ -903,12 +935,12 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
// MTK headers // MTK headers
if (boot.flags[MTK_KERNEL]) { 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(); m_hdr->size = hdr->kernel_size();
hdr->kernel_size() += sizeof(mtk_hdr); hdr->kernel_size() += sizeof(mtk_hdr);
} }
if (boot.flags[MTK_RAMDISK]) { 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(); m_hdr->size = hdr->ramdisk_size();
hdr->ramdisk_size() += sizeof(mtk_hdr); hdr->ramdisk_size() += sizeof(mtk_hdr);
} }
@@ -920,28 +952,28 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
if (char *id = hdr->id()) { if (char *id = hdr->id()) {
auto ctx = get_sha(!boot.flags[SHA256_FLAG]); auto ctx = get_sha(!boot.flags[SHA256_FLAG]);
uint32_t size = hdr->kernel_size(); 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))); ctx->update(byte_view(&size, sizeof(size)));
size = hdr->ramdisk_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))); ctx->update(byte_view(&size, sizeof(size)));
size = hdr->second_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))); ctx->update(byte_view(&size, sizeof(size)));
size = hdr->extra_size(); size = hdr->extra_size();
if (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))); ctx->update(byte_view(&size, sizeof(size)));
} }
uint32_t ver = hdr->header_version(); uint32_t ver = hdr->header_version();
if (ver == 1 || ver == 2) { if (ver == 1 || ver == 2) {
size = hdr->recovery_dtbo_size(); 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))); ctx->update(byte_view(&size, sizeof(size)));
} }
if (ver == 2) { if (ver == 2) {
size = hdr->dtb_size(); 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))); ctx->update(byte_view(&size, sizeof(size)));
} }
memset(id, 0, BOOT_ID_SIZE); memset(id, 0, BOOT_ID_SIZE);
@@ -954,40 +986,40 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
// Copy main header // Copy main header
if (boot.flags[AMONET_FLAG]) { if (boot.flags[AMONET_FLAG]) {
auto real_hdr_sz = std::min(hdr->hdr_space() - AMONET_MICROLOADER_SZ, hdr->hdr_size()); 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 { } 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]) { if (boot.flags[AVB_FLAG]) {
// Copy and patch AVB structures // 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)); memcpy(footer, boot.avb_footer, sizeof(AvbFooter));
footer->original_image_size = __builtin_bswap64(off.total); footer->original_image_size = __builtin_bswap64(off.total);
footer->vbmeta_offset = __builtin_bswap64(off.vbmeta); footer->vbmeta_offset = __builtin_bswap64(off.vbmeta);
if (check_env("PATCHVBMETAFLAG")) { 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); vbmeta->flags = __builtin_bswap32(3);
} }
} }
if (boot.flags[DHTB_FLAG]) { if (boot.flags[DHTB_FLAG]) {
// DHTB header // 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); memcpy(d_hdr, DHTB_MAGIC, 8);
d_hdr->size = off.total - sizeof(dhtb_hdr); 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)); byte_data(d_hdr->checksum, 32));
} else if (boot.flags[BLOB_FLAG]) { } else if (boot.flags[BLOB_FLAG]) {
// Blob header // 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); b_hdr->size = off.total - sizeof(blob_hdr);
} }
// Sign the image after we finish patching the boot image // Sign the image after we finish patching the boot image
if (boot.flags[AVB1_SIGNED_FLAG]) { if (boot.flags[AVB1_SIGNED_FLAG]) {
byte_view payload(out.buf() + off.header, off.total - off.header); byte_view payload(out.data() + off.header, off.total - off.header);
auto sig = rust::sign_boot_image(payload, "/boot", nullptr, nullptr); auto sig = sign_payload(payload);
if (!sig.empty()) { if (!sig.empty()) {
lseek(fd, off.total, SEEK_SET); lseek(fd, off.total, SEEK_SET);
xwrite(fd, sig.data(), sig.size()); xwrite(fd, sig.data(), sig.size());
@@ -997,33 +1029,15 @@ void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
close(fd); close(fd);
} }
int verify(rust::Utf8CStr image, const char *cert) { void cleanup() {
const boot_img boot(image.data()); unlink(HEADER_FILE);
if (cert == nullptr) { unlink(KERNEL_FILE);
// Boot image parsing already checks if the image is signed unlink(RAMDISK_FILE);
return boot.flags[AVB1_SIGNED_FLAG] ? 0 : 1; unlink(SECOND_FILE);
} else { unlink(KER_DTB_FILE);
// Provide a custom certificate and re-verify unlink(EXTRA_FILE);
return boot.verify(cert) ? 0 : 1; unlink(RECV_DTBO_FILE);
} unlink(DTB_FILE);
} unlink(BOOTCONFIG_FILE);
rm_rf(VND_RAMDISK_DIR);
int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key) {
const boot_img boot(image.data());
auto sig = rust::sign_boot_image(boot.payload, name.data(), cert, key);
if (sig.empty())
return 1;
auto eof = boot.tail.buf() - boot.map.buf();
int fd = xopen(image.data(), 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;
} }

View File

@@ -3,9 +3,7 @@
#include <cstdint> #include <cstdint>
#include <utility> #include <utility>
#include <bitset> #include <bitset>
#include <cxx.h> #include <rust/cxx.h>
#include "format.hpp"
/****************** /******************
* Special Headers * Special Headers
@@ -347,6 +345,17 @@ struct vendor_ramdisk_table_entry_v4 {
* Polymorphic Universal Header * 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) \ #define decl_val(name, len) \
virtual uint##len##_t name() const { return 0; } 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); std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
// Rust FFI // 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_payload() const { return payload; }
rust::Slice<const uint8_t> get_tail() const { return tail; } 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;
}; };

View File

@@ -1,16 +1,17 @@
use crate::compress::{compress, decompress}; use crate::compress::{compress_cmd, decompress_cmd};
use crate::cpio::{cpio_commands, print_cpio_usage}; use crate::cpio::{cpio_commands, print_cpio_usage};
use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage}; use crate::dtb::{DtbAction, dtb_commands, print_dtb_usage};
use crate::ffi::{FileFormat, cleanup, repack, sign, split_image_dtb, unpack, verify}; use crate::ffi::{BootImage, FileFormat, cleanup, repack, split_image_dtb, unpack};
use crate::patch::hexpatch; use crate::patch::hexpatch;
use crate::payload::extract_boot_from_payload; use crate::payload::extract_boot_from_payload;
use crate::sign::sha1_hash; use crate::sign::{sha1_hash, sign_boot_image};
use argh::FromArgs; use argh::{CommandInfo, EarlyExit, FromArgs, SubCommand};
use base::{ use base::{
EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, cmdline_logging, cstr, CmdArgs, EarlyExitExt, LoggedResult, MappedFile, PositionalArgParser, ResultExt, Utf8CStr,
libc::umask, log_err, map_args, Utf8CString, WriteExt, cmdline_logging, cstr, libc, libc::umask, log_err,
}; };
use std::ffi::c_char; use std::ffi::c_char;
use std::io::{Seek, SeekFrom, Write};
use std::str::FromStr; use std::str::FromStr;
#[derive(FromArgs)] #[derive(FromArgs)]
@@ -45,7 +46,7 @@ struct Unpack {
#[argh(switch, short = 'h')] #[argh(switch, short = 'h')]
dump_header: bool, dump_header: bool,
#[argh(positional)] #[argh(positional)]
img: String, img: Utf8CString,
} }
#[derive(FromArgs)] #[derive(FromArgs)]
@@ -54,54 +55,69 @@ struct Repack {
#[argh(switch, short = 'n')] #[argh(switch, short = 'n')]
no_compress: bool, no_compress: bool,
#[argh(positional)] #[argh(positional)]
img: String, img: Utf8CString,
#[argh(positional, default = r#""new-boot.img".to_string()"#)] #[argh(positional, default = r#"Utf8CString::from("new-boot.img")"#)]
out: String, out: Utf8CString,
} }
#[derive(FromArgs)] #[derive(FromArgs)]
#[argh(subcommand, name = "verify")] #[argh(subcommand, name = "verify")]
struct Verify { struct Verify {
#[argh(positional)] #[argh(positional)]
img: String, img: Utf8CString,
#[argh(positional)] #[argh(positional)]
cert: Option<String>, cert: Option<Utf8CString>,
} }
#[derive(FromArgs)] #[derive(FromArgs)]
#[argh(subcommand, name = "sign")] #[argh(subcommand, name = "sign")]
struct Sign { struct Sign {
#[argh(positional)] #[argh(positional)]
img: String, img: Utf8CString,
#[argh(positional)] #[argh(positional)]
args: Vec<String>, args: Vec<Utf8CString>,
} }
#[derive(FromArgs)]
#[argh(subcommand, name = "extract")]
struct Extract { struct Extract {
#[argh(positional)] payload: Utf8CString,
payload: String, partition: Option<Utf8CString>,
#[argh(positional)] outfile: Option<Utf8CString>,
args: Vec<String>, }
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)] #[derive(FromArgs)]
#[argh(subcommand, name = "hexpatch")] #[argh(subcommand, name = "hexpatch")]
struct HexPatch { struct HexPatch {
#[argh(positional)] #[argh(positional)]
file: String, file: Utf8CString,
#[argh(positional)] #[argh(positional)]
src: String, src: Utf8CString,
#[argh(positional)] #[argh(positional)]
dest: String, dest: Utf8CString,
} }
#[derive(FromArgs)] #[derive(FromArgs)]
#[argh(subcommand, name = "cpio")] #[argh(subcommand, name = "cpio")]
struct Cpio { struct Cpio {
#[argh(positional)] #[argh(positional)]
file: String, file: Utf8CString,
#[argh(positional)] #[argh(positional)]
cmds: Vec<String>, cmds: Vec<String>,
} }
@@ -110,7 +126,7 @@ struct Cpio {
#[argh(subcommand, name = "dtb")] #[argh(subcommand, name = "dtb")]
struct Dtb { struct Dtb {
#[argh(positional)] #[argh(positional)]
file: String, file: Utf8CString,
#[argh(subcommand)] #[argh(subcommand)]
action: DtbAction, action: DtbAction,
} }
@@ -121,45 +137,80 @@ struct Split {
#[argh(switch, short = 'n')] #[argh(switch, short = 'n')]
no_decompress: bool, no_decompress: bool,
#[argh(positional)] #[argh(positional)]
file: String, file: Utf8CString,
} }
#[derive(FromArgs)] #[derive(FromArgs)]
#[argh(subcommand, name = "sha1")] #[argh(subcommand, name = "sha1")]
struct Sha1 { struct Sha1 {
#[argh(positional)] #[argh(positional)]
file: String, file: Utf8CString,
} }
#[derive(FromArgs)] #[derive(FromArgs)]
#[argh(subcommand, name = "cleanup")] #[argh(subcommand, name = "cleanup")]
struct Cleanup {} struct Cleanup {}
#[derive(FromArgs)]
#[argh(subcommand, name = "compress")]
struct Compress { struct Compress {
#[argh(option, short = 'f', default = r#""gzip".to_string()"#)] format: FileFormat,
format: String, file: Utf8CString,
#[argh(positional)] out: Option<Utf8CString>,
file: String, }
#[argh(positional)]
out: Option<String>, 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: "",
};
} }
#[derive(FromArgs)]
#[argh(subcommand, name = "decompress")]
struct Decompress { struct Decompress {
#[argh(positional)] file: Utf8CString,
file: String, out: Option<Utf8CString>,
#[argh(positional)] }
out: Option<String>,
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) { fn print_usage(cmd: &str) {
eprintln!( eprintln!(
r#"MagiskBoot - Boot Image Modification Tool r#"MagiskBoot - Boot Image Modification Tool
Usage: {} <action> [args...] Usage: {0} <action> [args...]
Supported actions: Supported actions:
unpack [-n] [-h] <bootimg> unpack [-n] [-h] <bootimg>
@@ -242,7 +293,6 @@ Supported actions:
If [outfile] is not specified, then <infile> will be replaced If [outfile] is not specified, then <infile> will be replaced
with another file suffixed with a matching file extension. with another file suffixed with a matching file extension.
Supported formats: Supported formats:
{1} {1}
decompress <infile> [outfile] decompress <infile> [outfile]
@@ -251,7 +301,6 @@ Supported actions:
If [outfile] is not specified, then <infile> will be replaced If [outfile] is not specified, then <infile> will be replaced
with another file removing its archive format file extension. with another file removing its archive format file extension.
Supported formats: Supported formats:
{1} {1}
"#, "#,
cmd, cmd,
@@ -259,172 +308,156 @@ Supported actions:
); );
} }
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)] #[unsafe(no_mangle)]
pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 { pub extern "C" fn main(argc: i32, argv: *const *const c_char, _envp: *const *const c_char) -> i32 {
cmdline_logging(); cmdline_logging();
unsafe { umask(0) }; unsafe { umask(0) };
let res: LoggedResult<()> = try { let cmds = CmdArgs::new(argc, argv);
let mut cmds = map_args(argc, argv)?; boot_main(cmds).unwrap_or(1)
if argc < 2 {
print_usage(cmds.first().unwrap_or(&"magiskboot"));
return 1;
}
if cmds[1].starts_with("--") {
cmds[1] = &cmds[1][2..];
}
if let Some(fmt) = str::strip_prefix(cmds[1], "compress=") {
cmds.insert(1, "compress");
cmds.insert(2, "-f");
cmds[3] = fmt;
}
let mut cli = Cli::from_args(&[cmds[0]], &cmds[1..]).on_early_exit(|| match cmds.get(1) {
Some(&"dtb") => print_dtb_usage(),
Some(&"cpio") => print_cpio_usage(),
_ => print_usage(cmds[0]),
});
match cli.action {
Action::Unpack(Unpack {
no_decompress,
dump_header,
ref mut img,
}) => return unpack(Utf8CStr::from_string(img), no_decompress, dump_header),
Action::Repack(Repack {
no_compress,
ref mut img,
ref mut out,
}) => {
repack(
Utf8CStr::from_string(img),
Utf8CStr::from_string(out),
no_compress,
);
}
Action::Verify(Verify {
ref mut img,
ref mut cert,
}) => {
return unsafe {
verify(
Utf8CStr::from_string(img),
cert.as_mut()
.map(|x| Utf8CStr::from_string(x).as_ptr())
.unwrap_or(std::ptr::null()),
)
};
}
Action::Sign(Sign {
ref mut img,
ref mut args,
}) => {
let (pem, pk8) = match args.get_mut(1..=2) {
Some([pem, pk8]) => (
Utf8CStr::from_string(pem).as_ptr(),
Utf8CStr::from_string(pk8).as_ptr(),
),
_ => (std::ptr::null(), std::ptr::null()),
};
return unsafe {
sign(
Utf8CStr::from_string(img),
args.first_mut()
.map(Utf8CStr::from_string)
.unwrap_or(cstr!("/boot")),
pem,
pk8,
)
};
}
Action::Extract(Extract {
ref payload,
ref args,
}) => {
if args.len() > 2 {
Err(log_err!("Too many arguments"))?;
}
extract_boot_from_payload(
payload,
args.first().map(|x| x.as_str()),
args.get(1).map(|x| x.as_str()),
)
.log_with_msg(|w| w.write_str("Failed to extract from payload"))?;
}
Action::HexPatch(HexPatch {
ref mut file,
ref mut src,
ref mut dest,
}) => {
if !hexpatch(
file,
Utf8CStr::from_string(src),
Utf8CStr::from_string(dest),
) {
Err(log_err!("Failed to patch"))?;
}
}
Action::Cpio(Cpio {
ref mut file,
ref mut cmds,
}) => {
return if cpio_commands(file, cmds)
.log_with_msg(|w| w.write_str("Failed to process cpio"))?
{
0
} else {
1
};
}
Action::Dtb(Dtb {
ref mut file,
ref action,
}) => {
return if dtb_commands(file, action)
.log_with_msg(|w| w.write_str("Failed to process dtb"))?
{
0
} else {
1
};
}
Action::Split(Split {
no_decompress,
ref mut file,
}) => {
return split_image_dtb(Utf8CStr::from_string(file), no_decompress);
}
Action::Sha1(Sha1 { ref mut file }) => {
let file = MappedFile::open(Utf8CStr::from_string(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 {
ref mut file,
ref mut out,
}) => {
decompress(file, out.as_mut())?;
}
Action::Compress(Compress {
ref mut file,
ref format,
ref mut out,
}) => {
compress(
FileFormat::from_str(format).unwrap_or(FileFormat::UNKNOWN),
file,
out.as_mut(),
)?;
}
}
};
if res.is_ok() { 0 } else { 1 }
} }

View File

@@ -1,24 +1,23 @@
use crate::ffi::{FileFormat, check_fmt}; use crate::ffi::{FileFormat, check_fmt};
use base::libc::{O_RDONLY, O_TRUNC, O_WRONLY}; use base::libc::{O_RDONLY, O_TRUNC, O_WRONLY};
use base::{Chunker, LoggedResult, Utf8CStr, WriteExt, error, log_err}; use base::{
use bytemuck::bytes_of_mut; Chunker, FileOrStd, LoggedResult, ReadExt, ResultExt, Utf8CStr, Utf8CString, WriteExt, log_err,
use bzip2::{Compression as BzCompression, write::BzDecoder, write::BzEncoder}; };
use flate2::{Compression as GzCompression, write::GzEncoder, write::MultiGzDecoder}; use bzip2::{Compression as BzCompression, read::BzDecoder, write::BzEncoder};
use flate2::{Compression as GzCompression, read::MultiGzDecoder, write::GzEncoder};
use lz4::{ use lz4::{
BlockMode, BlockSize, ContentChecksum, Encoder as LZ4FrameEncoder, BlockMode, BlockSize, ContentChecksum, Decoder as LZ4FrameDecoder, Encoder as LZ4FrameEncoder,
EncoderBuilder as LZ4FrameEncoderBuilder, block::CompressionMode, liblz4::BlockChecksum, 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::fs::File;
use std::io::{BufWriter, Read, Stdin, Stdout, Write, stdin, stdout}; use std::io::{BufWriter, Cursor, Read, Write};
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::num::NonZeroU64; use std::num::NonZeroU64;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::os::fd::{AsRawFd, FromRawFd, RawFd}; 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}; use zopfli::{BlockType, GzipEncoder as ZopFliEncoder, Options as ZopfliOptions};
pub trait WriteFinish<W: Write>: Write { pub trait WriteFinish<W: Write>: Write {
@@ -37,19 +36,7 @@ macro_rules! finish_impl {
)*} )*}
} }
finish_impl!(GzEncoder<W>, MultiGzDecoder<W>, BzEncoder<W>, XzEncoder<W>); finish_impl!(GzEncoder<W>, BzEncoder<W>, XZWriter<W>, LZMAWriter<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>);
impl<W: Write> WriteFinish<W> for BufWriter<ZopFliEncoder<W>> { impl<W: Write> WriteFinish<W> for BufWriter<ZopFliEncoder<W>> {
fn finish(self: Box<Self>) -> std::io::Result<W> { fn finish(self: Box<Self>) -> std::io::Result<W> {
@@ -66,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 // LZ4BlockArchive format
// //
// len: | 4 | 4 | n | ... | 4 | // len: | 4 | 4 | n | ... | 4 |
@@ -158,7 +62,7 @@ impl<W: Write> WriteFinish<W> for LZ4FrameDecoder<W> {
const LZ4_BLOCK_SIZE: usize = 0x800000; const LZ4_BLOCK_SIZE: usize = 0x800000;
const LZ4HC_CLEVEL_MAX: i32 = 12; const LZ4HC_CLEVEL_MAX: i32 = 12;
const LZ4_MAGIC: &[u8] = b"\x02\x21\x4c\x18"; const LZ4_MAGIC: u32 = 0x184c2102;
struct LZ4BlockEncoder<W: Write> { struct LZ4BlockEncoder<W: Write> {
write: W, write: W,
@@ -207,7 +111,7 @@ impl<W: Write> Write for LZ4BlockEncoder<W> {
fn write_all(&mut self, mut buf: &[u8]) -> std::io::Result<()> { fn write_all(&mut self, mut buf: &[u8]) -> std::io::Result<()> {
if self.total == 0 { if self.total == 0 {
// Write header // Write header
self.write.write_all(LZ4_MAGIC)?; self.write.write_pod(&LZ4_MAGIC)?;
} }
self.total += buf.len() as u32; self.total += buf.len() as u32;
@@ -237,88 +141,72 @@ impl<W: Write> WriteFinish<W> for LZ4BlockEncoder<W> {
// LZ4BlockDecoder // LZ4BlockDecoder
struct LZ4BlockDecoder<W: Write> { struct LZ4BlockDecoder<R: Read> {
write: W, read: R,
chunker: Chunker, in_buf: Box<[u8]>,
out_buf: Box<[u8]>, out_buf: Box<[u8]>,
curr_block_size: usize, out_len: usize,
out_pos: usize,
} }
impl<W: Write> LZ4BlockDecoder<W> { impl<R: Read> LZ4BlockDecoder<R> {
fn new(write: W) -> Self { fn new(read: R) -> Self {
LZ4BlockDecoder { let compressed_sz = lz4::block::compress_bound(LZ4_BLOCK_SIZE).unwrap_or(LZ4_BLOCK_SIZE);
write, Self {
chunker: Chunker::new(size_of::<u32>()), read,
// SAFETY: all bytes will be initialized before it is used in_buf: unsafe { Box::new_uninit_slice(compressed_sz).assume_init() },
out_buf: unsafe { Box::new_uninit_slice(LZ4_BLOCK_SIZE).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> { impl<R: Read> Read for LZ4BlockDecoder<R> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.write_all(buf)?; if self.out_pos == self.out_len {
Ok(buf.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 {
fn flush(&mut self) -> std::io::Result<()> { Ok(0)
Ok(()) } else {
} Err(e)
};
fn write_all(&mut self, mut buf: &[u8]) -> std::io::Result<()> { }
while !buf.is_empty() { if block_size == LZ4_MAGIC {
let (b, chunk) = self.chunker.add_data(buf); self.read.read_pod(&mut block_size)?;
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>());
} }
}
Ok(())
}
}
impl<W: Write> WriteFinish<W> for LZ4BlockDecoder<W> { let block_size = block_size as usize;
fn finish(mut self: Box<Self>) -> std::io::Result<W> {
let chunk = self.chunker.get_available(); if block_size > self.in_buf.len() {
if !chunk.is_empty() { // This may be the LG format trailer, EOF
return Err(std::io::Error::new( return Ok(0);
std::io::ErrorKind::Interrupted, }
"Finish ran before end of compressed stream",
)); // 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)
} }
} }
@@ -327,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> { pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn WriteFinish<W> + 'a> {
match format { match format {
FileFormat::XZ => { FileFormat::XZ => {
let opt = LzmaOptions::new_preset(9).unwrap(); let mut opt = XZOptions::with_preset(9);
let stream = opt.set_check_sum_type(CheckType::Crc32);
LzmaStream::new_stream_encoder(LzmaFilters::new().lzma2(&opt), LzmaCheck::Crc32) Box::new(XZWriter::new(w, opt).unwrap())
.unwrap();
Box::new(XzEncoder::new_stream(w, stream))
} }
FileFormat::LZMA => { FileFormat::LZMA => {
let opt = LzmaOptions::new_preset(9).unwrap(); Box::new(LZMAWriter::new_use_header(w, &LZMAOptions::with_preset(9), None).unwrap())
let stream = LzmaStream::new_lzma_encoder(&opt).unwrap();
Box::new(XzEncoder::new_stream(w, stream))
} }
FileFormat::BZIP2 => Box::new(BzEncoder::new(w, BzCompression::best())), FileFormat::BZIP2 => Box::new(BzEncoder::new(w, BzCompression::best())),
FileFormat::LZ4 => { FileFormat::LZ4 => {
@@ -367,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 { match format {
FileFormat::XZ | FileFormat::LZMA => { FileFormat::XZ => Box::new(XZReader::new(r, true)),
let stream = LzmaStream::new_auto_decoder(u64::MAX, 0).unwrap(); FileFormat::LZMA => Box::new(LZMAReader::new_mem_limit(r, u32::MAX, None).unwrap()),
Box::new(XzDecoder::new_stream(w, stream)) FileFormat::BZIP2 => Box::new(BzDecoder::new(r)),
} FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(r).unwrap()),
FileFormat::BZIP2 => Box::new(BzDecoder::new(w)), FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(r)),
FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(w)), FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(r)),
FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(w)),
FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(w)),
_ => unreachable!(), _ => unreachable!(),
} }
} }
// C++ FFI // 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) { 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)) }; let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
@@ -419,122 +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) { 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 out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
let mut decoder = get_decoder(format, out_file.deref_mut()); let mut decoder = get_decoder(format, in_bytes);
let _: LoggedResult<()> = try { std::io::copy(decoder.as_mut(), out_file.deref_mut()).log_ok();
decoder.write_all(in_bytes)?;
decoder.finish()?;
};
} }
enum AsRawFdFile { // Command-line entry points
Stdin(Stdin),
Stdout(Stdout),
File(File),
}
impl AsRawFd for AsRawFdFile { pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> LoggedResult<()> {
fn as_raw_fd(&self) -> RawFd {
match self {
AsRawFdFile::Stdin(stdin) => stdin.as_raw_fd(),
AsRawFdFile::Stdout(stdout) => stdout.as_raw_fd(),
AsRawFdFile::File(file) => file.as_raw_fd(),
}
}
}
pub(crate) fn decompress(infile: &mut String, outfile: Option<&mut String>) -> LoggedResult<()> {
let in_std = infile == "-"; let in_std = infile == "-";
let mut rm_in = false; let mut rm_in = false;
let raw_in = if in_std {
AsRawFdFile::Stdin(stdin())
} else {
AsRawFdFile::File(Utf8CStr::from_string(infile).open(O_RDONLY)?)
};
let mut buf = [0u8; 4096]; let mut buf = [0u8; 4096];
let mut in_file = unsafe { File::from_raw_fd(raw_in.as_raw_fd()) }; let input = if in_std {
let _ = in_file.read(&mut buf)?; FileOrStd::StdIn
} else {
FileOrStd::File(infile.open(O_RDONLY)?)
};
let format = check_fmt(&buf); // 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}"); eprintln!("Detected format: {format}");
if !format.is_compressed() { if !format.is_compressed() {
return Err(log_err!("Input file is not a supported type!")); return log_err!("Input file is not a supported type!");
} }
let raw_out = if let Some(outfile) = outfile { // 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 == "-" { if outfile == "-" {
AsRawFdFile::Stdout(stdout()) FileOrStd::StdOut
} else { } else {
AsRawFdFile::File(Utf8CStr::from_string(outfile).create(O_WRONLY | O_TRUNC, 0o644)?) FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?)
} }
} else if in_std { } else if in_std {
AsRawFdFile::Stdout(stdout()) FileOrStd::StdOut
} else { } else {
// strip the extension // Strip out extension and remove input
rm_in = true; let outfile = if let Some((outfile, ext)) = infile.rsplit_once('.')
let mut outfile = if let Some((outfile, ext)) = infile.rsplit_once('.') { && ext == format.ext()
if ext != format.ext() { {
Err(log_err!("Input file is not a supported type!"))?; Utf8CString::from(outfile)
}
outfile.to_owned()
} else { } else {
infile.clone() return log_err!("Input file is not a supported type!");
}; };
eprintln!("Decompressing to [{outfile}]");
AsRawFdFile::File(Utf8CStr::from_string(&mut outfile).create(O_WRONLY | O_TRUNC, 0o644)?) rm_in = true;
eprintln!("Decompressing to [{outfile}]");
FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?)
}; };
decompress_bytes_fd(format, &buf, raw_in.as_raw_fd(), raw_out.as_raw_fd()); 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 { if rm_in {
Utf8CStr::from_string(infile).remove()?; infile.remove()?;
} }
Ok(()) Ok(())
} }
pub(crate) fn compress( pub(crate) fn compress_cmd(
method: FileFormat, method: FileFormat,
infile: &mut String, infile: &Utf8CStr,
outfile: Option<&mut String>, outfile: Option<&Utf8CStr>,
) -> LoggedResult<()> { ) -> LoggedResult<()> {
if method == FileFormat::UNKNOWN {
error!("Unsupported compression format");
}
let in_std = infile == "-"; let in_std = infile == "-";
let mut rm_in = false; let mut rm_in = false;
let raw_in = if in_std { let input = if in_std {
AsRawFdFile::Stdin(stdin()) FileOrStd::StdIn
} else { } else {
AsRawFdFile::File(Utf8CStr::from_string(infile).open(O_RDONLY)?) FileOrStd::File(infile.open(O_RDONLY)?)
}; };
let raw_out = if let Some(outfile) = outfile { let output = if let Some(outfile) = outfile {
if outfile == "-" { if outfile == "-" {
AsRawFdFile::Stdout(stdout()) FileOrStd::StdOut
} else { } else {
AsRawFdFile::File(Utf8CStr::from_string(outfile).create(O_WRONLY | O_TRUNC, 0o644)?) FileOrStd::File(outfile.create(O_WRONLY | O_TRUNC, 0o644)?)
} }
} else if in_std { } else if in_std {
AsRawFdFile::Stdout(stdout()) FileOrStd::StdOut
} else { } else {
let mut outfile = format!("{infile}.{}", method.ext()); let mut outfile = Utf8CString::default();
outfile.write_str(infile).ok();
outfile.write_char('.').ok();
outfile.write_str(method.ext()).ok();
eprintln!("Compressing to [{outfile}]"); eprintln!("Compressing to [{outfile}]");
rm_in = true; rm_in = true;
AsRawFdFile::File(Utf8CStr::from_string(&mut outfile).create(O_WRONLY | O_TRUNC, 0o644)?) let outfile = outfile.create(O_WRONLY | O_TRUNC, 0o644)?;
FileOrStd::File(outfile)
}; };
compress_fd(method, raw_in.as_raw_fd(), raw_out.as_raw_fd()); let mut encoder = get_encoder(method, output.as_file());
std::io::copy(&mut input.as_file(), encoder.as_mut())?;
encoder.finish()?;
if rm_in { if rm_in {
Utf8CStr::from_string(infile).remove()?; infile.remove()?;
} }
Ok(()) Ok(())
} }

View File

@@ -4,7 +4,7 @@ use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Cursor, Read, Write};
use std::mem::size_of; use std::mem::size_of;
use std::process::exit; use std::process::exit;
use std::str; use std::str;
@@ -20,8 +20,8 @@ use base::libc::{
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::{ use base::{
BytesExt, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt, BytesExt, EarlyExitExt, LoggedResult, MappedFile, OptionExt, ResultExt, Utf8CStr, Utf8CStrBuf,
cstr, log_err, WriteExt, cstr, log_err,
}; };
use crate::check_env; use crate::check_env;
@@ -227,7 +227,7 @@ impl Cpio {
let hdr_sz = size_of::<CpioHeader>(); let hdr_sz = size_of::<CpioHeader>();
let hdr = from_bytes::<CpioHeader>(&data[pos..(pos + hdr_sz)]); let hdr = from_bytes::<CpioHeader>(&data[pos..(pos + hdr_sz)]);
if &hdr.magic != b"070701" { if &hdr.magic != b"070701" {
return Err(log_err!("invalid cpio magic")); return log_err!("invalid cpio magic");
} }
pos += hdr_sz; pos += hdr_sz;
let name_sz = x8u(&hdr.namesize)? as usize; let name_sz = x8u(&hdr.namesize)? as usize;
@@ -331,7 +331,7 @@ impl Cpio {
let entry = self let entry = self
.entries .entries
.get(path) .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}]"); eprintln!("Extracting entry [{path}] to [{out}]");
let out = Utf8CStr::from_string(out); let out = Utf8CStr::from_string(out);
@@ -362,7 +362,7 @@ impl Cpio {
unsafe { mknod(out.as_ptr().cast(), entry.mode, dev) }; unsafe { mknod(out.as_ptr().cast(), entry.mode, dev) };
} }
_ => { _ => {
return Err(log_err!("unknown entry type")); return log_err!("unknown entry type");
} }
} }
Ok(()) Ok(())
@@ -389,7 +389,7 @@ impl Cpio {
fn add(&mut self, mode: mode_t, path: &str, file: &mut String) -> LoggedResult<()> { fn add(&mut self, mode: mode_t, path: &str, file: &mut String) -> LoggedResult<()> {
if path.ends_with('/') { 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 file = Utf8CStr::from_string(file);
let attr = file.get_attr()?; let attr = file.get_attr()?;
@@ -412,7 +412,7 @@ impl Cpio {
} else if attr.is_char_device() { } else if attr.is_char_device() {
mode | S_IFCHR mode | S_IFCHR
} else { } else {
return Err(log_err!("unsupported file type")); return log_err!("unsupported file type");
} }
}; };
@@ -465,7 +465,7 @@ impl Cpio {
let entry = self let entry = self
.entries .entries
.remove(&norm_path(from)) .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); self.entries.insert(norm_path(to), entry);
eprintln!("Move [{from}] -> [{to}]"); eprintln!("Move [{from}] -> [{to}]");
Ok(()) Ok(())
@@ -560,7 +560,7 @@ impl Cpio {
let mut backups = HashMap::<String, Box<CpioEntry>>::new(); let mut backups = HashMap::<String, Box<CpioEntry>>::new();
let mut rm_list = String::new(); let mut rm_list = String::new();
self.entries self.entries
.extract_if(|name, _| name.starts_with(".backup/")) .extract_if(.., |name, _| name.starts_with(".backup/"))
.for_each(|(name, mut entry)| { .for_each(|(name, mut entry)| {
if name == ".backup/.rmlist" { if name == ".backup/.rmlist" {
if let Ok(data) = str::from_utf8(&entry.data) { if let Ok(data) = str::from_utf8(&entry.data) {
@@ -705,10 +705,11 @@ impl CpioEntry {
return false; return false;
} }
let mut decoder = get_decoder(FileFormat::XZ, Vec::new());
let Ok(data): std::io::Result<Vec<u8>> = (try { let Ok(data): std::io::Result<Vec<u8>> = (try {
decoder.write_all(&self.data)?; let mut decoder = get_decoder(FileFormat::XZ, Cursor::new(&self.data));
decoder.finish()? let mut data = Vec::new();
std::io::copy(decoder.as_mut(), &mut data)?;
data
}) else { }) else {
eprintln!("xz compression failed"); eprintln!("xz compression failed");
return false; return false;
@@ -753,8 +754,7 @@ impl Display for CpioEntry {
} }
} }
pub(crate) fn cpio_commands(file: &mut String, cmds: &mut Vec<String>) -> LoggedResult<bool> { pub(crate) fn cpio_commands(file: &Utf8CStr, cmds: &Vec<String>) -> LoggedResult<()> {
let file = Utf8CStr::from_string(file);
let mut cpio = if file.exists() { let mut cpio = if file.exists() {
Cpio::load_from_file(file)? Cpio::load_from_file(file)?
} else { } else {
@@ -779,7 +779,11 @@ pub(crate) fn cpio_commands(file: &mut String, cmds: &mut Vec<String>) -> Logged
CpioAction::Restore(_) => cpio.restore()?, CpioAction::Restore(_) => cpio.restore()?,
CpioAction::Patch(_) => cpio.patch(), CpioAction::Patch(_) => cpio.patch(),
CpioAction::Exists(Exists { path }) => { CpioAction::Exists(Exists { path }) => {
return Ok(cpio.exists(path)); return if cpio.exists(path) {
Ok(())
} else {
log_err!()
};
} }
CpioAction::Backup(Backup { CpioAction::Backup(Backup {
origin, origin,
@@ -792,19 +796,19 @@ pub(crate) fn cpio_commands(file: &mut String, cmds: &mut Vec<String>) -> Logged
CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?, CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
CpioAction::Extract(Extract { paths }) => { CpioAction::Extract(Extract { paths }) => {
if !paths.is_empty() && paths.len() != 2 { if !paths.is_empty() && paths.len() != 2 {
Err(log_err!("invalid arguments"))?; log_err!("invalid arguments")?;
} }
let mut it = paths.iter_mut(); let mut it = paths.iter_mut();
cpio.extract(it.next(), it.next())?; cpio.extract(it.next(), it.next())?;
} }
CpioAction::List(List { path, recursive }) => { CpioAction::List(List { path, recursive }) => {
cpio.ls(path.as_str(), *recursive); cpio.ls(path.as_str(), *recursive);
return Ok(true); return Ok(());
} }
}; };
} }
cpio.dump(file)?; cpio.dump(file)?;
Ok(true) Ok(())
} }
fn x8u(x: &[u8; 8]) -> LoggedResult<u32> { fn x8u(x: &[u8; 8]) -> LoggedResult<u32> {
@@ -812,7 +816,9 @@ fn x8u(x: &[u8; 8]) -> LoggedResult<u32> {
let mut ret = 0u32; let mut ret = 0u32;
let s = str::from_utf8(x).log_with_msg(|w| w.write_str("bad cpio header"))?; let s = str::from_utf8(x).log_with_msg(|w| w.write_str("bad cpio header"))?;
for c in s.chars() { 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) Ok(ret)
} }

View File

@@ -210,11 +210,11 @@ fn dtb_test(file: &Utf8CStr) -> LoggedResult<bool> {
if child.name != "system" { if child.name != "system" {
continue; continue;
} }
if let Some(mount_point) = child.property("mnt_point") { if let Some(mount_point) = child.property("mnt_point")
if mount_point.value == b"/system_root\0" { && mount_point.value == b"/system_root\0"
ret = false; {
break; ret = false;
} break;
} }
} }
} }
@@ -264,8 +264,7 @@ fn dtb_patch(file: &Utf8CStr) -> LoggedResult<bool> {
Ok(patched) Ok(patched)
} }
pub(crate) fn dtb_commands(file: &mut String, action: &DtbAction) -> LoggedResult<bool> { pub(crate) fn dtb_commands(file: &Utf8CStr, action: &DtbAction) -> LoggedResult<bool> {
let file = Utf8CStr::from_string(file);
match action { match action {
DtbAction::Print(Print { fstab }) => { DtbAction::Print(Print { fstab }) => {
dtb_print(file, *fstab)?; dtb_print(file, *fstab)?;

View File

@@ -1,72 +0,0 @@
#include "boot-rs.hpp"
#include "format.hpp"
Fmt2Name fmt2name;
#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";
}
}

View File

@@ -1,60 +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 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 Fmt2Name fmt2name;

View File

@@ -1,4 +1,5 @@
use crate::ffi::FileFormat; use crate::ffi::FileFormat;
use base::{Utf8CStr, cstr, libc};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
@@ -22,19 +23,25 @@ impl FromStr for FileFormat {
impl Display for FileFormat { impl Display for FileFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 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 { match *self {
Self::GZIP => write!(f, "gzip"), Self::GZIP => cstr!("gzip"),
Self::ZOPFLI => write!(f, "zopfli"), Self::ZOPFLI => cstr!("zopfli"),
Self::LZOP => write!(f, "lzop"), Self::LZOP => cstr!("lzop"),
Self::XZ => write!(f, "xz"), Self::XZ => cstr!("xz"),
Self::LZMA => write!(f, "lzma"), Self::LZMA => cstr!("lzma"),
Self::BZIP2 => write!(f, "bzip2"), Self::BZIP2 => cstr!("bzip2"),
Self::LZ4 => write!(f, "lz4"), Self::LZ4 => cstr!("lz4"),
Self::LZ4_LEGACY => write!(f, "lz4_legacy"), Self::LZ4_LEGACY => cstr!("lz4_legacy"),
Self::LZ4_LG => write!(f, "lz4_lg"), Self::LZ4_LG => cstr!("lz4_lg"),
Self::DTB => write!(f, "dtb"), Self::DTB => cstr!("dtb"),
Self::ZIMAGE => write!(f, "zimage"), Self::ZIMAGE => cstr!("zimage"),
_ => write!(f, "raw"), _ => cstr!("raw"),
} }
} }
} }
@@ -42,12 +49,12 @@ impl Display for FileFormat {
impl FileFormat { impl FileFormat {
pub fn ext(&self) -> &'static str { pub fn ext(&self) -> &'static str {
match *self { match *self {
Self::GZIP | Self::ZOPFLI => ".gz", Self::GZIP | Self::ZOPFLI => "gz",
Self::LZOP => ".lzo", Self::LZOP => "lzo",
Self::XZ => ".xz", Self::XZ => "xz",
Self::LZMA => ".lzma", Self::LZMA => "lzma",
Self::BZIP2 => ".bz2", Self::BZIP2 => "bz2",
Self::LZ4 | Self::LZ4_LEGACY | Self::LZ4_LG => ".lz4", Self::LZ4 | Self::LZ4_LEGACY | Self::LZ4_LG => "lz4",
_ => "", _ => "",
} }
} }
@@ -57,7 +64,6 @@ impl FileFormat {
*self, *self,
Self::GZIP Self::GZIP
| Self::ZOPFLI | Self::ZOPFLI
| Self::LZOP
| Self::XZ | Self::XZ
| Self::LZMA | Self::LZMA
| Self::BZIP2 | Self::BZIP2
@@ -77,9 +83,22 @@ impl FileFormat {
Self::LZ4, Self::LZ4,
Self::LZ4_LEGACY, Self::LZ4_LEGACY,
Self::LZ4_LG, Self::LZ4_LG,
Self::LZOP,
] ]
.map(|f| f.to_string()) .map(|f| f.to_string())
.join(" ") .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

@@ -5,17 +5,18 @@
pub use base; pub use base;
use compress::{compress_bytes, decompress_bytes}; use compress::{compress_bytes, decompress_bytes};
use sign::{SHA, get_sha, sha256_hash, sign_boot_image, verify_boot_image}; use format::{fmt_compressed, fmt_compressed_any, fmt2name};
use sign::{SHA, get_sha, sha256_hash, sign_payload_for_cxx};
use std::env; use std::env;
mod cli;
mod compress; mod compress;
mod cpio; mod cpio;
mod dtb; mod dtb;
mod format;
mod patch; mod patch;
mod payload; mod payload;
// Suppress warnings in generated code // Suppress warnings in generated code
mod cli;
mod format;
#[allow(warnings)] #[allow(warnings)]
mod proto; mod proto;
mod sign; mod sign;
@@ -48,37 +49,16 @@ pub mod ffi {
} }
unsafe extern "C++" { unsafe extern "C++" {
include!("../base/include/base.hpp"); include!("magiskboot.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"] #[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>; type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
}
unsafe extern "C++" {
include!("format.hpp");
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];
include!("magiskboot.hpp");
fn cleanup(); fn cleanup();
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32; fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool); fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool);
unsafe fn verify(image: Utf8CStrRef, cert: *const c_char) -> i32;
unsafe fn sign(
image: Utf8CStrRef,
name: Utf8CStrRef,
cert: *const c_char,
key: *const c_char,
) -> i32;
fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32; fn split_image_dtb(filename: Utf8CStrRef, skip_decomp: bool) -> i32;
fn check_fmt(buf: &[u8]) -> FileFormat;
} }
extern "Rust" { extern "Rust" {
@@ -91,18 +71,34 @@ pub mod ffi {
fn compress_bytes(format: FileFormat, in_bytes: &[u8], 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(format: FileFormat, in_bytes: &[u8], 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"] // BootImage FFI
#[allow(unused_unsafe)] 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" { extern "Rust" {
unsafe fn sign_boot_image( #[cxx_name = "verify"]
payload: &[u8], fn verify_for_cxx(self: &BootImage) -> bool;
name: *const c_char,
cert: *const c_char,
key: *const c_char,
) -> Vec<u8>;
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
} }
} }

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <base.hpp>
#define HEADER_FILE "header" #define HEADER_FILE "header"
#define KERNEL_FILE "kernel" #define KERNEL_FILE "kernel"
#define RAMDISK_FILE "ramdisk.cpio" #define RAMDISK_FILE "ramdisk.cpio"
@@ -12,21 +14,49 @@
#define BOOTCONFIG_FILE "bootconfig" #define BOOTCONFIG_FILE "bootconfig"
#define NEW_BOOT "new-boot.img" #define NEW_BOOT "new-boot.img"
int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false); #define BUFFER_MATCH(buf, s) (memcmp(buf, s, sizeof(s) - 1) == 0)
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false); #define BUFFER_CONTAIN(buf, sz, s) (memmem(buf, sz, s, sizeof(s) - 1) != nullptr)
int verify(rust::Utf8CStr image, const char *cert); #define CHECKED_MATCH(s) (len >= (sizeof(s) - 1) && BUFFER_MATCH(buf, s))
int sign(rust::Utf8CStr image, rust::Utf8CStr name, const char *cert, const char *key);
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false);
inline void cleanup() { #define BOOT_MAGIC "ANDROID!"
unlink(HEADER_FILE); #define VENDOR_BOOT_MAGIC "VNDRBOOT"
unlink(KERNEL_FILE); #define CHROMEOS_MAGIC "CHROMEOS"
unlink(RAMDISK_FILE); #define GZIP1_MAGIC "\x1f\x8b"
unlink(SECOND_FILE); #define GZIP2_MAGIC "\x1f\x9e"
unlink(KER_DTB_FILE); #define LZOP_MAGIC "\x89""LZO"
unlink(EXTRA_FILE); #define XZ_MAGIC "\xfd""7zXZ"
unlink(RECV_DTBO_FILE); #define BZIP_MAGIC "BZh"
unlink(DTB_FILE); #define LZ4_LEG_MAGIC "\x02\x21\x4c\x18"
unlink(BOOTCONFIG_FILE); #define LZ41_MAGIC "\x03\x21\x4c\x18"
rm_rf(VND_RAMDISK_DIR); #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

@@ -102,9 +102,9 @@ fn hex2byte(hex: &[u8]) -> Vec<u8> {
v v
} }
pub fn hexpatch(file: &mut String, from: &Utf8CStr, to: &Utf8CStr) -> bool { pub fn hexpatch(file: &Utf8CStr, from: &Utf8CStr, to: &Utf8CStr) -> bool {
let res: LoggedResult<bool> = try { let res: LoggedResult<bool> = try {
let mut map = MappedFile::open_rw(Utf8CStr::from_string(file))?; let mut map = MappedFile::open_rw(file)?;
let pattern = hex2byte(from.as_bytes()); let pattern = hex2byte(from.as_bytes());
let patch = hex2byte(to.as_bytes()); let patch = hex2byte(to.as_bytes());

View File

@@ -1,16 +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 byteorder::{BigEndian, ReadBytesExt};
use quick_protobuf::{BytesReader, MessageRead}; use quick_protobuf::{BytesReader, MessageRead};
use std::io::Cursor;
use std::{ use std::{
fs::File, fs::File,
io::{BufReader, Read, Seek, SeekFrom, Write}, io::{BufReader, Read, Seek, SeekFrom, Write},
os::fd::FromRawFd, 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, WriteExt, error};
macro_rules! bad_payload { macro_rules! bad_payload {
($msg:literal) => {{ ($msg:literal) => {{
error!(concat!("Invalid payload: ", $msg)); error!(concat!("Invalid payload: ", $msg));
@@ -164,9 +164,11 @@ pub fn extract_boot_from_payload(
} }
Type::REPLACE_BZ | Type::REPLACE_XZ => { Type::REPLACE_BZ | Type::REPLACE_XZ => {
out_file.seek(SeekFrom::Start(out_offset))?; 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 { let Ok(_): std::io::Result<()> = (try {
decoder.write_all(data)?; std::io::copy(decoder.as_mut(), &mut out_file)?;
}) else { }) else {
return Err(bad_payload!("decompression failed")); return Err(bad_payload!("decompression failed"));
}; };

View File

@@ -25,8 +25,7 @@ use x509_cert::der::Any;
use x509_cert::der::asn1::{OctetString, PrintableString}; use x509_cert::der::asn1::{OctetString, PrintableString};
use x509_cert::spki::AlgorithmIdentifier; use x509_cert::spki::AlgorithmIdentifier;
use base::libc::c_char; use base::{LoggedResult, MappedFile, ResultExt, Utf8CStr, cstr, log_err};
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
use crate::ffi::BootImage; use crate::ffi::BootImage;
@@ -117,7 +116,7 @@ impl Verifier {
digest = Box::<Sha512>::default(); digest = Box::<Sha512>::default();
VerifyingKey::SHA521withECDSA(ec) VerifyingKey::SHA521withECDSA(ec)
} else { } else {
return Err(log_err!("Unsupported private key")); return log_err!("Unsupported private key");
}; };
Ok(Verifier { digest, key }) Ok(Verifier { digest, key })
} }
@@ -178,7 +177,7 @@ impl Signer {
SigningKey::SHA521withECDSA(ec) SigningKey::SHA521withECDSA(ec)
} }
_ => { _ => {
return Err(log_err!("Unsupported private key")); return log_err!("Unsupported private key");
} }
}, },
}, },
@@ -249,7 +248,7 @@ struct BootSignature {
impl BootSignature { impl BootSignature {
fn verify(self, payload: &[u8]) -> LoggedResult<()> { fn verify(self, payload: &[u8]) -> LoggedResult<()> {
if self.authenticated_attributes.length as usize != payload.len() { 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( let mut verifier = Verifier::from_public_key(
self.certificate self.certificate
@@ -265,23 +264,27 @@ impl BootSignature {
} }
} }
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool { impl BootImage {
let res: LoggedResult<()> = try { pub fn verify(&self, cert: Option<&Utf8CStr>) -> LoggedResult<()> {
let tail = img.tail(); 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 // Don't use BootSignature::from_der because tail might have trailing zeros
let mut reader = SliceReader::new(tail)?; let mut reader = SliceReader::new(tail)?;
let mut sig = BootSignature::decode(&mut reader)?; let mut sig = BootSignature::decode(&mut reader)?;
match unsafe { Utf8CStr::from_ptr(cert) } { if let Some(s) = cert {
Ok(s) => { let pem = MappedFile::open(s)?;
let pem = MappedFile::open(s)?; sig.certificate = Certificate::from_pem(pem)?;
sig.certificate = Certificate::from_pem(pem)?;
}
Err(StrErr::NullPointerError) => {}
Err(e) => Err(e)?,
}; };
sig.verify(img.payload())?;
}; sig.verify(self.payload()).log()
res.is_ok() }
pub fn verify_for_cxx(&self) -> bool {
self.verify(None).is_ok()
}
} }
enum Bytes { enum Bytes {
@@ -303,47 +306,44 @@ const VERITY_PK8: &[u8] = include_bytes!("../../../tools/keys/verity.pk8");
pub fn sign_boot_image( pub fn sign_boot_image(
payload: &[u8], payload: &[u8],
name: *const c_char, name: &Utf8CStr,
cert: *const c_char, cert: Option<&Utf8CStr>,
key: *const c_char, key: Option<&Utf8CStr>,
) -> Vec<u8> { ) -> LoggedResult<Vec<u8>> {
let res: LoggedResult<Vec<u8>> = try { let cert = match cert {
// Process arguments Some(s) => Bytes::Mapped(MappedFile::open(s)?),
let name = unsafe { Utf8CStr::from_ptr(name) }?; None => Bytes::Slice(VERITY_PEM),
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()?
}; };
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"] } bytemuck = { workspace = true, features = ["derive"] }
thiserror = { workspace = true } thiserror = { workspace = true }
bit-set = { workspace = true } bit-set = { workspace = true }
argh = { workspace = true }

View File

@@ -1,7 +1,4 @@
#include <sys/stat.h> #include <core.hpp>
#include <consts.hpp>
#include <base.hpp>
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (argc < 1) if (argc < 1)

View File

@@ -1,9 +1,7 @@
#include <libgen.h> #include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <consts.hpp> #include <core.hpp>
#include <base.hpp>
using namespace std; using namespace std;
@@ -28,7 +26,7 @@ int main(int argc, char *argv[]) {
cmdline_logging(); cmdline_logging();
init_argv0(argc, argv); init_argv0(argc, argv);
string_view argv0 = basename(argv[0]); Utf8CStr argv0 = basename(argv[0]);
umask(0); umask(0);
@@ -63,6 +61,6 @@ int main(int argc, char *argv[]) {
return app.fn(argc, 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; return 1;
} }

View File

@@ -4,6 +4,7 @@
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#include <linux/input.h> #include <linux/input.h>
#include <map>
#include <consts.hpp> #include <consts.hpp>
#include <base.hpp> #include <base.hpp>
@@ -160,76 +161,6 @@ void write_string(int fd, string_view str) {
xwrite(fd, str.data(), str.size()); 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
MagiskD::Get().restore_zygisk_prop();
write_int(client, 0);
// Terminate the daemon!
exit(0);
}
default:
__builtin_unreachable();
}
}
static bool is_client(pid_t pid) { static bool is_client(pid_t pid) {
// Verify caller is the same as server // Verify caller is the same as server
char path[32]; char path[32];
@@ -243,16 +174,13 @@ static void handle_request(pollfd *pfd) {
// Verify client credentials // Verify client credentials
sock_cred cred; sock_cred cred;
bool is_root;
bool is_zygote;
int code;
if (!get_client_cred(client, &cred)) { if (!get_client_cred(client, &cred)) {
// Client died // Client died
return; return;
} }
is_root = cred.uid == AID_ROOT; bool is_root = cred.uid == AID_ROOT;
is_zygote = cred.context == "u:r:zygote:s0"; bool is_zygote = cred.context == "u:r:zygote:s0";
if (!is_root && !is_zygote && !is_client(cred.pid)) { if (!is_root && !is_zygote && !is_client(cred.pid)) {
// Unsupported client state // Unsupported client state
@@ -260,7 +188,7 @@ static void handle_request(pollfd *pfd) {
return; return;
} }
code = read_int(client); int code = read_int(client);
if (code < 0 || code >= +RequestCode::END || if (code < 0 || code >= +RequestCode::END ||
code == +RequestCode::_SYNC_BARRIER_ || code == +RequestCode::_SYNC_BARRIER_ ||
code == +RequestCode::_STAGE_BARRIER_) { code == +RequestCode::_STAGE_BARRIER_) {
@@ -302,9 +230,11 @@ static void handle_request(pollfd *pfd) {
write_int(client, +RespondCode::OK); write_int(client, +RespondCode::OK);
if (code < +RequestCode::_SYNC_BARRIER_) { if (code < +RequestCode::_SYNC_BARRIER_) {
handle_request_sync(client, code); MagiskD::Get().handle_request_sync(client.release(), code);
} else if (code < +RequestCode::_STAGE_BARRIER_) { } 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 { } else {
exec_task([=, fd = client.release()] { exec_task([=, fd = client.release()] {
MagiskD::Get().boot_stage_handler(fd, code); MagiskD::Get().boot_stage_handler(fd, code);
@@ -389,7 +319,7 @@ int connect_daemon(int req, bool create) {
char buf[64]; char buf[64];
xreadlink("/proc/self/exe", buf, sizeof(buf)); 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"); LOGE("Start daemon on magisk tmpfs\n");
close(fd); close(fd);
return -1; return -1;
@@ -498,7 +428,7 @@ void unlock_blocks() {
bool check_key_combo() { bool check_key_combo() {
uint8_t bitmask[(KEY_MAX + 1) / 8]; uint8_t bitmask[(KEY_MAX + 1) / 8];
vector<int> events; vector<owned_fd> events;
constexpr char name[] = "/dev/.ev"; constexpr char name[] = "/dev/.ev";
// First collect candidate events that accepts volume down // First collect candidate events that accepts volume down
@@ -512,19 +442,17 @@ bool check_key_combo() {
memset(bitmask, 0, sizeof(bitmask)); memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask); ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEDOWN, bitmask)) if (test_bit(KEY_VOLUMEDOWN, bitmask))
events.push_back(fd); events.emplace_back(fd);
else else
close(fd); close(fd);
} }
if (events.empty()) if (events.empty())
return false; 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 // Check if volume down key is held continuously for more than 3 seconds
for (int i = 0; i < 300; ++i) { for (int i = 0; i < 300; ++i) {
bool pressed = false; bool pressed = false;
for (const int &fd : events) { for (int fd : events) {
memset(bitmask, 0, sizeof(bitmask)); memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask); ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEDOWN, bitmask)) { if (test_bit(KEY_VOLUMEDOWN, bitmask)) {

View File

@@ -1,23 +1,33 @@
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::db::Sqlite3;
use crate::ffi::{ use crate::ffi::{
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, exec_common_scripts, exec_module_scripts, DbEntryKey, ModuleInfo, RequestCode, check_key_combo, denylist_handler, exec_common_scripts,
get_magisk_tmp, get_prop, initialize_denylist, set_prop, setup_magisk_env, 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::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::mount::{clean_mounts, setup_preinit_dir};
use crate::package::ManagerInfo; use crate::package::ManagerInfo;
use crate::resetprop::{get_prop, set_prop};
use crate::selinux::restore_tmpcon; use crate::selinux::restore_tmpcon;
use crate::socket::IpcWrite;
use crate::su::SuInfo; use crate::su::SuInfo;
use crate::zygisk::ZygiskState; use crate::zygisk::ZygiskState;
use base::const_format::concatcp;
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY}; use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
use base::{ 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::fmt::Write as FmtWrite;
use std::fs::File;
use std::io::{BufReader, Write}; use std::io::{BufReader, Write};
use std::process::Command; use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd};
use std::process::{Command, exit};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, OnceLock}; use std::sync::{Mutex, OnceLock};
@@ -121,8 +131,8 @@ impl MagiskD {
.log() .log()
.ok(); .ok();
let safe_mode = boot_cnt >= 2 let safe_mode = boot_cnt >= 2
|| get_prop(cstr!("persist.sys.safemode"), true) == "1" || get_prop(cstr!("persist.sys.safemode")) == "1"
|| get_prop(cstr!("ro.sys.safemode"), false) == "1" || get_prop(cstr!("ro.sys.safemode")) == "1"
|| check_key_combo(); || check_key_combo();
if safe_mode { if safe_mode {
@@ -174,6 +184,8 @@ impl MagiskD {
} }
pub fn boot_stage_handler(&self, client: i32, code: i32) { 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 // Make sure boot stage execution is always serialized
let mut state = self.boot_stage_lock.lock().unwrap(); let mut state = self.boot_stage_lock.lock().unwrap();
@@ -186,10 +198,9 @@ impl MagiskD {
} }
state.set(BootState::PostFsDataDone); state.set(BootState::PostFsDataDone);
} }
unsafe { libc::close(client) };
} }
RequestCode::LATE_START => { RequestCode::LATE_START => {
unsafe { libc::close(client) }; drop(client);
if state.contains(BootState::PostFsDataDone) && !state.contains(BootState::SafeMode) if state.contains(BootState::PostFsDataDone) && !state.contains(BootState::SafeMode)
{ {
self.late_start(); self.late_start();
@@ -197,15 +208,85 @@ impl MagiskD {
} }
} }
RequestCode::BOOT_COMPLETE => { RequestCode::BOOT_COMPLETE => {
unsafe { libc::close(client) }; drop(client);
if state.contains(BootState::PostFsDataDone) { if state.contains(BootState::PostFsDataDone) {
state.set(BootState::BootComplete); state.set(BootState::BootComplete);
self.boot_complete() 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);
}
_ => {}
} }
} }
@@ -230,11 +311,11 @@ pub fn daemon_entry() {
start_log_daemon(); start_log_daemon();
magisk_logging(); 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" let is_emulator = get_prop(cstr!("ro.kernel.qemu")) == "1"
|| get_prop(cstr!("ro.boot.qemu"), false) == "1" || get_prop(cstr!("ro.boot.qemu")) == "1"
|| get_prop(cstr!("ro.product.device"), false).contains("vsoc"); || get_prop(cstr!("ro.product.device")).contains("vsoc");
// Load config status // Load config status
let magisk_tmp = get_magisk_tmp(); let magisk_tmp = get_magisk_tmp();
@@ -243,7 +324,7 @@ pub fn daemon_entry() {
.join_path(MAIN_CONFIG); .join_path(MAIN_CONFIG);
let mut is_recovery = false; let mut is_recovery = false;
if let Ok(main_config) = tmp_path.open(O_RDONLY | O_CLOEXEC) { 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" { if key == "RECOVERYMODE" {
is_recovery = val == "true"; is_recovery = val == "true";
return false; return false;
@@ -255,7 +336,7 @@ pub fn daemon_entry() {
let mut sdk_int = -1; let mut sdk_int = -1;
if let Ok(build_prop) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) { 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" { if key == "ro.build.version.sdk" {
sdk_int = val.parse::<i32>().unwrap_or(-1); sdk_int = val.parse::<i32>().unwrap_or(-1);
return false; return false;
@@ -265,7 +346,7 @@ pub fn daemon_entry() {
} }
if sdk_int < 0 { if sdk_int < 0 {
// In case some devices do not store this info in build.prop, fallback to getprop // 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>() .parse::<i32>()
.unwrap_or(-1); .unwrap_or(-1);
} }
@@ -278,19 +359,19 @@ pub fn daemon_entry() {
switch_cgroup("/acct", pid); switch_cgroup("/acct", pid);
switch_cgroup("/dev/cg2_bpf", pid); switch_cgroup("/dev/cg2_bpf", pid);
switch_cgroup("/sys/fs/cgroup", 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); switch_cgroup("/dev/memcg/apps", pid);
} }
// Samsung workaround #7887 // Samsung workaround #7887
if cstr!("/system_ext/app/mediatek-res/mediatek-res.apk").exists() { 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 // Cleanup pre-init mounts
tmp_path.append_path(ROOTMNT); tmp_path.append_path(ROOTMNT);
if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) { 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()); line.truncate(line.trim_end().len());
let item = Utf8CStr::from_string(line); let item = Utf8CStr::from_string(line);
item.unmount().log_ok(); item.unmount().log_ok();
@@ -307,7 +388,7 @@ pub fn daemon_entry() {
// Remove all pre-init overlay files to free-up memory // Remove all pre-init overlay files to free-up memory
tmp_path.append_path(ROOTOVL); tmp_path.append_path(ROOTOVL);
tmp_path.remove_all().log_ok(); tmp_path.remove_all().ok();
tmp_path.truncate(magisk_tmp.len()); tmp_path.truncate(magisk_tmp.len());
let magiskd = MagiskD { let magiskd = MagiskD {
@@ -336,7 +417,7 @@ fn switch_cgroup(cgroup: &str, pid: i32) {
fn check_data() -> bool { fn check_data() -> bool {
if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) { if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) {
let mut mnt = false; let mut mnt = false;
BufReader::new(file).foreach_lines(|line| { BufReader::new(file).for_each_line(|line| {
if line.contains(" /data ") && !line.contains("tmpfs") { if line.contains(" /data ") && !line.contains("tmpfs") {
mnt = true; mnt = true;
return false; return false;
@@ -346,14 +427,14 @@ fn check_data() -> bool {
if !mnt { if !mnt {
return false; return false;
} }
let crypto = get_prop(cstr!("ro.crypto.state"), false); let crypto = get_prop(cstr!("ro.crypto.state"));
return if !crypto.is_empty() { return if !crypto.is_empty() {
if crypto != "encrypted" { if crypto != "encrypted" {
// Unencrypted, we can directly access data // Unencrypted, we can directly access data
true true
} else { } else {
// Encrypted, check whether vold is started // Encrypted, check whether vold is started
!get_prop(cstr!("init.svc.vold"), false).is_empty() !get_prop(cstr!("init.svc.vold")).is_empty()
} }
} else { } else {
// ro.crypto.state is not set, assume it's unencrypted // 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::ffi::c_void;
use std::fs::File; use std::fs::File;
use std::io::{BufReader, BufWriter}; use std::io::{BufReader, BufWriter};
use std::os::fd::{FromRawFd, OwnedFd, RawFd}; use std::os::fd::OwnedFd;
use std::pin::Pin; use std::pin::Pin;
use std::ptr; use std::ptr;
use std::ptr::NonNull; use std::ptr::NonNull;
@@ -302,7 +302,7 @@ impl MagiskD {
.sql_result() .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 file = File::from(fd);
let mut reader = BufReader::new(&mut file); let mut reader = BufReader::new(&mut file);
let sql: String = reader.read_decodable()?; 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 { pub fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool {
self.set_db_setting(key, value).log().is_ok() 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")] #[unsafe(export_name = "sql_exec_rs")]

View File

@@ -26,7 +26,7 @@ Actions:
exit(1); exit(1);
} }
void denylist_handler(int client, const sock_cred *cred) { void denylist_handler(int client) {
if (client < 0) { if (client < 0) {
revert_unmount(); revert_unmount();
return; return;

View File

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

View File

@@ -5,6 +5,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#include <set> #include <set>
#include <map>
#include <consts.hpp> #include <consts.hpp>
#include <sqlite.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) { static bool proc_name_match(int pid, string_view name) {
char buf[4019]; char buf[4019];
sprintf(buf, "/proc/%d/cmdline", pid); sprintf(buf, "/proc/%d/cmdline", pid);
@@ -111,7 +113,7 @@ bool proc_context_match(int pid, string_view context) {
sprintf(buf, "/proc/%d", pid); sprintf(buf, "/proc/%d", pid);
if (lgetfilecon(buf, byte_data{ con, sizeof(con) })) { if (lgetfilecon(buf, byte_data{ con, sizeof(con) })) {
return str_starts(con, context); return string_view(con).starts_with(context);
} }
return false; return false;
} }
@@ -173,7 +175,7 @@ static bool add_hide_set(const char *pkg, const char *proc) {
return true; return true;
if (str_eql(pkg, ISOLATED_MAGIC)) { if (str_eql(pkg, ISOLATED_MAGIC)) {
// Kill all matching isolated processes // 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 { } else {
kill_process(proc); kill_process(proc);
} }
@@ -411,7 +413,7 @@ bool is_deny_target(int uid, string_view process) {
if (app_id >= 90000) { if (app_id >= 90000) {
if (auto it = pkg_to_procs.find(ISOLATED_MAGIC); it != pkg_to_procs.end()) { if (auto it = pkg_to_procs.find(ISOLATED_MAGIC); it != pkg_to_procs.end()) {
for (const auto &s : it->second) { for (const auto &s : it->second) {
if (str_starts(process, s)) if (process.starts_with(s))
return true; return true;
} }
} }

View File

@@ -17,6 +17,11 @@
#define to_app_id(uid) (uid % AID_USER_OFFSET) #define to_app_id(uid) (uid % AID_USER_OFFSET)
#define to_user_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 // Return codes for daemon
enum class RespondCode : int { enum class RespondCode : int {
ERROR = -1, ERROR = -1,
@@ -85,13 +90,13 @@ void init_thread_pool();
void exec_task(std::function<void()> &&task); void exec_task(std::function<void()> &&task);
// Daemon handlers // Daemon handlers
void denylist_handler(int client, const sock_cred *cred); void denylist_handler(int client);
// Scripting // Scripting
void install_apk(rust::Utf8CStr apk); void install_apk(Utf8CStr apk);
void uninstall_pkg(rust::Utf8CStr pkg); void uninstall_pkg(Utf8CStr pkg);
void exec_common_scripts(rust::Utf8CStr stage); void exec_common_scripts(Utf8CStr stage);
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);
void exec_script(const char *script); void exec_script(const char *script);
void clear_pkg(const char *pkg, int user_id); void clear_pkg(const char *pkg, int user_id);
[[noreturn]] void install_module(const char *file); [[noreturn]] void install_module(const char *file);
@@ -109,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); void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode);
// Rust bindings // Rust bindings
static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); } static inline Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
static inline rust::String resolve_preinit_dir_rs(rust::Utf8CStr base_dir) { static inline rust::String resolve_preinit_dir_rs(Utf8CStr base_dir) {
return resolve_preinit_dir(base_dir.c_str()); 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 <functional>
#include <cxx.h> #include <rust/cxx.h>
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */

View File

@@ -12,12 +12,11 @@ use base::libc;
use cxx::{ExternType, type_id}; use cxx::{ExternType, type_id};
use daemon::{MagiskD, daemon_entry}; use daemon::{MagiskD, daemon_entry};
use derive::Decodable; use derive::Decodable;
use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging}; use logging::{android_logging, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
use module::remove_modules;
use mount::{find_preinit_device, revert_unmount}; 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 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::fs::File;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::ops::DerefMut; use std::ops::DerefMut;
@@ -127,7 +126,6 @@ pub mod ffi {
} }
unsafe extern "C++" { unsafe extern "C++" {
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"] #[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>; type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
#[cxx_name = "ucred"] #[cxx_name = "ucred"]
@@ -152,6 +150,10 @@ pub mod ffi {
fn switch_mnt_ns(pid: i32) -> i32; fn switch_mnt_ns(pid: i32) -> i32;
fn exec_root_shell(client: i32, pid: i32, req: &mut SuRequest, mode: MntNsMode); 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"); include!("include/sqlite.hpp");
type sqlite3; type sqlite3;
@@ -165,18 +167,6 @@ pub mod ffi {
fn get_text(self: &DbValues, index: i32) -> &str; fn get_text(self: &DbValues, index: i32) -> &str;
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32; fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32;
fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> 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" { extern "Rust" {
@@ -184,17 +174,10 @@ pub mod ffi {
fn zygisk_logging(); fn zygisk_logging();
fn zygisk_close_logd(); fn zygisk_close_logd();
fn zygisk_get_logd() -> i32; fn zygisk_get_logd() -> i32;
fn setup_logfile();
fn find_preinit_device() -> String; fn find_preinit_device() -> String;
fn revert_unmount(pid: i32); fn revert_unmount(pid: i32);
fn remove_modules();
fn zygisk_should_load_module(flags: u32) -> bool; 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_fd(socket: i32, fd: i32) -> bool;
fn send_fds(socket: i32, fds: &[i32]) -> bool;
fn recv_fd(socket: i32) -> i32; fn recv_fd(socket: i32) -> i32;
fn recv_fds(socket: i32) -> Vec<i32>; fn recv_fds(socket: i32) -> Vec<i32>;
fn write_to_fd(self: &SuRequest, fd: i32); fn write_to_fd(self: &SuRequest, fd: i32);
@@ -206,6 +189,9 @@ pub mod ffi {
fn setfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool; fn setfilecon(path: Utf8CStrRef, con: Utf8CStrRef) -> bool;
fn lsetfilecon(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"] #[namespace = "rust"]
fn daemon_entry(); fn daemon_entry();
} }
@@ -220,23 +206,14 @@ pub mod ffi {
// FFI for MagiskD // FFI for MagiskD
extern "Rust" { extern "Rust" {
type MagiskD; type MagiskD;
fn reboot(&self);
fn sdk_int(&self) -> i32; fn sdk_int(&self) -> i32;
fn zygisk_enabled(&self) -> bool; fn zygisk_enabled(&self) -> bool;
fn boot_stage_handler(&self, client: i32, code: i32); fn boot_stage_handler(&self, client: i32, code: i32);
fn zygisk_handler(&self, client: i32); fn handle_request_sync(&self, client: i32, code: i32);
fn zygisk_reset(&self, restore: bool); fn handle_request_async(&self, client: i32, code: i32, cred: &UCred);
fn restore_zygisk_prop(&self);
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 get_db_setting(&self, key: DbEntryKey) -> i32; fn get_db_setting(&self, key: DbEntryKey) -> i32;
#[cxx_name = "set_db_setting"] #[cxx_name = "set_db_setting"]
fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool; 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] #[Self = MagiskD]
#[cxx_name = "Get"] #[cxx_name = "Get"]

View File

@@ -1,11 +1,12 @@
use crate::consts::{MODULEMNT, MODULEROOT, MODULEUPGRADE, WORKERDIR}; use crate::consts::{MODULEMNT, MODULEROOT, MODULEUPGRADE, WORKERDIR};
use crate::daemon::MagiskD; use crate::daemon::MagiskD;
use crate::ffi::{ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp, load_prop_file}; use crate::ffi::{ModuleInfo, exec_module_scripts, exec_script, get_magisk_tmp};
use crate::mount::setup_module_mount; use crate::mount::setup_module_mount;
use crate::resetprop::load_prop_file;
use base::{ use base::{
DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResultStatic, ResultExt, DirEntry, Directory, FsPathBuilder, LibcReturn, LoggedResult, OsResultStatic, ResultExt,
Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error, info, libc, SilentLogExt, Utf8CStr, Utf8CStrBuf, Utf8CString, WalkResult, clone_attr, cstr, debug, error,
raw_cstr, warn, info, libc, raw_cstr, warn,
}; };
use libc::{AT_REMOVEDIR, MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY}; use libc::{AT_REMOVEDIR, MS_RDONLY, O_CLOEXEC, O_CREAT, O_RDONLY};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@@ -69,7 +70,7 @@ impl PathTracker<'_> {
PathTracker { path, len } PathTracker { path, len }
} }
fn append(&mut self, name: &str) -> PathTracker { fn append(&mut self, name: &str) -> PathTracker<'_> {
let len = self.path.len(); let len = self.path.len();
self.path.append_path(name); self.path.append_path(name);
PathTracker { PathTracker {
@@ -78,7 +79,7 @@ impl PathTracker<'_> {
} }
} }
fn reborrow(&mut self) -> PathTracker { fn reborrow(&mut self) -> PathTracker<'_> {
Self::from(self.path) Self::from(self.path)
} }
} }
@@ -98,7 +99,7 @@ struct FilePaths<'a> {
} }
impl FilePaths<'_> { impl FilePaths<'_> {
fn append(&mut self, name: &str) -> FilePaths { fn append(&mut self, name: &str) -> FilePaths<'_> {
FilePaths { FilePaths {
real: self.real.append(name), real: self.real.append(name),
worker: self.worker.append(name), worker: self.worker.append(name),
@@ -107,7 +108,7 @@ impl FilePaths<'_> {
} }
} }
fn reborrow(&mut self) -> FilePaths { fn reborrow(&mut self) -> FilePaths<'_> {
FilePaths { FilePaths {
real: self.real.reborrow(), real: self.real.reborrow(),
worker: self.worker.reborrow(), worker: self.worker.reborrow(),
@@ -556,7 +557,7 @@ fn inject_zygisk_bins(name: &str, system: &mut FsNode) {
} }
fn upgrade_modules() -> LoggedResult<()> { 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 ufd = upgrade.as_raw_fd();
let root = Directory::open(cstr!(MODULEROOT))?; let root = Directory::open(cstr!(MODULEROOT))?;
while let Some(e) = upgrade.read()? { while let Some(e) = upgrade.read()? {
@@ -804,8 +805,7 @@ impl MagiskD {
// Read props // Read props
let prop = module_paths.append("system.prop"); let prop = module_paths.append("system.prop");
if prop.module().exists() { if prop.module().exists() {
// Do NOT go through property service as it could cause boot lock load_prop_file(prop.module());
load_prop_file(prop.module(), true);
} }
} }
{ {

View File

@@ -12,7 +12,8 @@ use base::{
}; };
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR}; 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() { pub fn setup_preinit_dir() {
let magisk_tmp = get_magisk_tmp(); let magisk_tmp = get_magisk_tmp();
@@ -109,11 +110,11 @@ enum EncryptType {
} }
pub fn find_preinit_device() -> String { 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 EncryptType::None
} else if get_prop(cstr!("ro.crypto.type"), false) == "block" { } else if get_prop(cstr!("ro.crypto.type")) == "block" {
EncryptType::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 EncryptType::Metadata
} else { } else {
EncryptType::File EncryptType::File

View File

@@ -8,13 +8,11 @@ use base::{
Utf8CString, cstr, error, fd_get_attr, warn, Utf8CString, cstr, error, fd_get_attr, warn,
}; };
use bit_set::BitSet; use bit_set::BitSet;
use cxx::CxxString;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::io::{Cursor, Read, Seek, SeekFrom}; use std::io::{Cursor, Read, Seek, SeekFrom};
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
const EOCD_MAGIC: u32 = 0x06054B50; const EOCD_MAGIC: u32 = 0x06054B50;
@@ -88,7 +86,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
apk.read_exact(&mut comment)?; apk.read_exact(&mut comment)?;
let mut comment = Cursor::new(&comment); let mut comment = Cursor::new(&comment);
let mut apk_ver = 0; let mut apk_ver = 0;
comment.foreach_props(|k, v| { comment.for_each_prop(|k, v| {
if k == "versionCode" { if k == "versionCode" {
apk_ver = v.parse::<i32>().unwrap_or(0); apk_ver = v.parse::<i32>().unwrap_or(0);
false false
@@ -474,19 +472,6 @@ impl MagiskD {
let _ = info.get_manager(self, 0, true); 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_id = app_no + AID_APP_START
// app_no range: [0, 9999] // app_no range: [0, 9999]
pub fn get_app_no_list(&self) -> BitSet { 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::{ use std::{
fs::File, fs::File,
io::{BufWriter, Write}, io::{BufWriter, Write},
ops::{Deref, DerefMut},
os::fd::FromRawFd, os::fd::FromRawFd,
pin::Pin,
}; };
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
use crate::ffi::{PropCb, prop_cb_exec}; use crate::resetprop::PropReader;
use crate::resetprop::proto::persistent_properties::{ use crate::resetprop::proto::persistent_properties::{
PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord, PersistentProperties, mod_PersistentProperties::PersistentPropertyRecord,
}; };
use base::const_format::concatcp; use base::const_format::concatcp;
use base::libc::{O_CLOEXEC, O_RDONLY}; use base::libc::{O_CLOEXEC, O_RDONLY};
use base::{ use base::{
Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentResultExt, Utf8CStr, Directory, FsPathBuilder, LibcReturn, LoggedResult, MappedFile, SilentLogExt, Utf8CStr,
Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, Utf8CStrBuf, WalkResult, clone_attr, cstr, debug, libc::mkstemp, log_err,
}; };
const PERSIST_PROP_DIR: &str = "/data/property"; const PERSIST_PROP_DIR: &str = "/data/property";
const PERSIST_PROP: &str = concatcp!(PERSIST_PROP_DIR, "/persistent_properties"); 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 { trait PropExt {
fn find_index(&self, name: &Utf8CStr) -> Result<usize, usize>; 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 { impl PropExt for PersistentProperties {
fn find_index(&self, name: &Utf8CStr) -> Result<usize, usize> { 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> { fn find(self, name: &Utf8CStr) -> Option<PersistentPropertyRecord> {
let idx = self.find_index(name).silent()?; let idx = self.find_index(name).ok()?;
Ok(&mut self[idx]) 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 r = BytesReader::from_bytes(m);
let mut props = PersistentProperties::from_reader(&mut r, m)?; let mut props = PersistentProperties::from_reader(&mut r, m)?;
// Keep the list sorted for binary search // 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) Ok(props)
} }
@@ -130,88 +107,80 @@ fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> {
Ok(()) Ok(())
} }
pub fn persist_get_prop(name: &Utf8CStr, mut prop_cb: Pin<&mut PropCb>) { pub(super) fn persist_get_prop(key: &Utf8CStr) -> LoggedResult<String> {
let res: LoggedResult<()> = try { if check_proto() {
if check_proto() { let props = proto_read_props()?;
let mut props = proto_read_props()?; let prop = props.find(key).silent()?;
let prop = props.find(name)?; 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 { if let PersistentPropertyRecord {
name: Some(n), name: Some(n),
value: Some(v), value: Some(v),
} = prop } = 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)?; } else {
prop_cb.exec(name, Utf8CStr::from_string(&mut value)); let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?;
debug!("resetprop: found prop [{}] = [{}]", name, value); dir.pre_order_walk(|e| {
} if e.is_file()
}; && let Ok(value) = file_get_prop(e.name())
res.ok(); {
} reader.put_str(e.name().to_string(), value, 0);
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()),
},
),
} }
proto_write_props(&props)?; // Do not traverse recursively
} else { Ok(WalkResult::Skip)
file_set_prop(name, Some(value))?; })?;
} }
}; Ok(())
res.is_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); \ exit(0); \
} }
void exec_common_scripts(rust::Utf8CStr stage) { void exec_common_scripts(Utf8CStr stage) {
LOGI("* Running %s.d scripts\n", stage.c_str()); LOGI("* Running %s.d scripts\n", stage.c_str());
char path[4096]; char path[4096];
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage.c_str()); 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; 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()); LOGI("* Running module %s scripts\n", stage.c_str());
if (module_list.empty()) if (module_list.empty())
return; return;
bool pfs = (string_view) stage == "post-fs-data"; bool pfs = stage == "post-fs-data";
if (pfs) { if (pfs) {
timespec now{}; timespec now{};
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
@@ -157,7 +157,7 @@ appops set %s REQUEST_INSTALL_PACKAGES allow
rm -f $APK rm -f $APK
)EOF"; )EOF";
void install_apk(rust::Utf8CStr apk) { void install_apk(Utf8CStr apk) {
setfilecon(apk.c_str(), MAGISK_FILE_CON); setfilecon(apk.c_str(), MAGISK_FILE_CON);
char cmds[sizeof(install_script) + 4096]; char cmds[sizeof(install_script) + 4096];
ssprintf(cmds, sizeof(cmds), install_script, apk.c_str(), JAVA_PACKAGE_NAME); 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)" log -t Magisk "pm_uninstall: $(pm uninstall $PKG 2>&1)"
)EOF"; )EOF";
void uninstall_pkg(rust::Utf8CStr pkg) { void uninstall_pkg(Utf8CStr pkg) {
char cmds[sizeof(uninstall_script) + 256]; char cmds[sizeof(uninstall_script) + 256];
ssprintf(cmds, sizeof(cmds), uninstall_script, pkg.c_str()); ssprintf(cmds, sizeof(cmds), uninstall_script, pkg.c_str());
exec_command_async("/system/bin/sh", "-c", cmds); 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}; use std::os::unix::net::{AncillaryData, SocketAncillary, UnixStream};
pub trait Encodable { pub trait Encodable {
#[allow(dead_code)]
fn encoded_len(&self) -> usize; fn encoded_len(&self) -> usize;
fn encode(&self, w: &mut impl Write) -> io::Result<()>; 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 { pub fn recv_fd(socket: RawFd) -> RawFd {
let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) });
socket socket

View File

@@ -6,11 +6,14 @@ use crate::db::{DbSettings, MultiuserMode, RootAccess};
use crate::ffi::{SuPolicy, SuRequest, exec_root_shell}; use crate::ffi::{SuPolicy, SuRequest, exec_root_shell};
use crate::socket::IpcRead; use crate::socket::IpcRead;
use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn}; 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::os::unix::net::UnixStream;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[allow(unused_imports)]
use std::os::fd::AsRawFd;
const DEFAULT_SHELL: &str = "/system/bin/sh"; const DEFAULT_SHELL: &str = "/system/bin/sh";
impl Default for SuRequest { impl Default for SuRequest {
@@ -111,14 +114,16 @@ impl AccessInfo {
} }
impl MagiskD { 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; let cred = cred.0;
debug!( debug!(
"su: request from uid=[{}], pid=[{}], client=[{}]", "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() { let mut req = match client.read_decodable::<SuRequest>().log() {
Ok(req) => req, Ok(req) => req,

View File

@@ -1,13 +1,24 @@
use base::{error, libc, warn}; use base::libc::ssize_t;
use libc::{ use base::{
POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TCSAFLUSH, LibcReturn, LoggedResult, OsResult, PipeFd, ReadExt, ResultExt, error, libc, log_err,
TIOCGWINSZ, TIOCSWINSZ, cfmakeraw, close, pipe, poll, pollfd, raise, read, sigaddset, make_pipe, warn,
sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, splice, tcgetattr, tcsetattr,
termios, winsize,
}; };
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 mut OLD_STDIN: Option<termios> = None;
static SHOULD_USE_SPLICE: AtomicBool = AtomicBool::new(true);
const TIOCGPTN: u32 = 0x80045430; const TIOCGPTN: u32 = 0x80045430;
unsafe extern "C" { 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 { fn splice(fd_in: RawFd, fd_out: RawFd, len: usize, flags: u32) -> OsResult<'static, ssize_t> {
// usize::MAX will EINVAL in some kernels, use i32::MAX in case unsafe { libc::splice(fd_in, null_mut(), fd_out, null_mut(), len, flags) }
let s = unsafe { splice(infd, null_mut(), pipe[1], null_mut(), i32::MAX as _, 0) }; .as_os_result("splice", None, None)
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 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) { pub fn pump_tty(infd: i32, outfd: i32) {
set_stdin_raw(); set_stdin_raw();
let sfd = unsafe { let signal_fd = unsafe {
let mut mask: sigset_t = std::mem::zeroed(); let mut mask: sigset_t = std::mem::zeroed();
sigemptyset(&mut mask); sigemptyset(&mut mask);
sigaddset(&mut mask, SIGWINCH); sigaddset(&mut mask, SIGWINCH);
if sigprocmask(SIG_BLOCK, &mask, null_mut()) < 0 { sigprocmask(SIG_BLOCK, &mask, null_mut())
error!("sigprocmask"); .check_os_err("sigprocmask", None, None)
} .log_ok();
signalfd(-1, &mask, SFD_CLOEXEC) signalfd(-1, &mask, SFD_CLOEXEC)
}; };
@@ -116,17 +148,16 @@ pub fn pump_tty(infd: i32, outfd: i32) {
revents: 0, revents: 0,
}, },
pollfd { pollfd {
fd: sfd, fd: signal_fd,
events: POLLIN, events: POLLIN,
revents: 0, revents: 0,
}, },
]; ];
let mut p: [c_int; 2] = [0; 2]; let Ok(pipe_fd) = make_pipe(O_CLOEXEC).log() else {
if unsafe { pipe(&mut p as *mut c_int) } < 0 {
error!("pipe error");
return; return;
} };
'poll: loop { 'poll: loop {
let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) }; 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 { for pfd in &pfds {
if pfd.revents & POLLIN != 0 { if pfd.revents & POLLIN != 0 {
let res = if pfd.fd == STDIN_FILENO { 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 { } else if pfd.fd == infd {
pump_via_pipe(pfd.fd, STDOUT_FILENO, &p) pump_via_splice(infd, STDOUT_FILENO, &pipe_fd)
} else if pfd.fd == sfd { } else if pfd.fd == signal_fd {
resize_pty(outfd); resize_pty(outfd);
let mut buf = [MaybeUninit::<u8>::uninit(); size_of::<signalfd_siginfo>()]; let mut info = SignalFdInfo::zeroed();
if unsafe { read(pfd.fd, buf.as_mut_ptr() as *mut _, buf.len()) } < 0 { let mut fd = ManuallyDrop::new(unsafe { File::from_raw_fd(signal_fd) });
error!("read error"); fd.read_pod(&mut info).log()
false
} else {
true
}
} else { } else {
false log_err!()
}; };
if !res { if res.is_err() {
break 'poll; break 'poll;
} }
} else if pfd.revents != 0 && pfd.fd == infd { } 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 2015, Pierre-Hugues Husson <phh@phh.me>
* Copyright 2010, Adam Shanks (@ChainsDD) * Copyright 2010, Adam Shanks (@ChainsDD)
* Copyright 2008, Zinx Verituse (@zinxv) * Copyright 2008, Zinx Verituse (@zinxv)
@@ -76,9 +76,7 @@ static void sighandler(int sig) {
close(STDERR_FILENO); close(STDERR_FILENO);
// Put back all the default handlers // Put back all the default handlers
struct sigaction act; struct sigaction act{};
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_DFL; act.sa_handler = SIG_DFL;
for (int i = 0; quit_signals[i]; ++i) { for (int i = 0; quit_signals[i]; ++i) {
sigaction(quit_signals[i], &act, nullptr); sigaction(quit_signals[i], &act, nullptr);
@@ -86,8 +84,7 @@ static void sighandler(int sig) {
} }
static void setup_sighandlers(void (*handler)(int)) { static void setup_sighandlers(void (*handler)(int)) {
struct sigaction act; struct sigaction act{};
memset(&act, 0, sizeof(act));
act.sa_handler = handler; act.sa_handler = handler;
for (int i = 0; quit_signals[i]; ++i) { for (int i = 0; quit_signals[i]; ++i) {
sigaction(quit_signals[i], &act, nullptr); 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 su_client_main(int argc, char *argv[]) {
int c; option long_opts[] = {
struct option long_opts[] = {
{ "command", required_argument, nullptr, 'c' }, { "command", required_argument, nullptr, 'c' },
{ "help", no_argument, nullptr, 'h' }, { "help", no_argument, nullptr, 'h' },
{ "login", no_argument, nullptr, 'l' }, { "login", no_argument, nullptr, 'l' },
@@ -126,6 +122,7 @@ int su_client_main(int argc, char *argv[]) {
bool interactive = false; bool interactive = false;
int c;
while ((c = getopt_long(argc, argv, "c:hlimpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) { while ((c = getopt_long(argc, argv, "c:hlimpds:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
switch (c) { switch (c) {
case 'c': { case 'c': {
@@ -191,7 +188,7 @@ int su_client_main(int argc, char *argv[]) {
fprintf(stderr, "Invalid GID: %s\n", optarg); fprintf(stderr, "Invalid GID: %s\n", optarg);
usage(EXIT_FAILURE); usage(EXIT_FAILURE);
} }
std::copy(gids.begin(), gids.end(), std::back_inserter(req.gids)); ranges::copy(gids, std::back_inserter(req.gids));
break; break;
} }
default: default:
@@ -207,19 +204,15 @@ int su_client_main(int argc, char *argv[]) {
} }
/* username or uid */ /* username or uid */
if (optind < argc) { if (optind < argc) {
struct passwd *pw; if (const passwd *pw = getpwnam(argv[optind]))
pw = getpwnam(argv[optind]);
if (pw)
req.target_uid = pw->pw_uid; req.target_uid = pw->pw_uid;
else else
req.target_uid = parse_int(argv[optind]); req.target_uid = parse_int(argv[optind]);
optind++; optind++;
} }
int ptmx, fd;
// Connect to client // Connect to client
fd = connect_daemon(+RequestCode::SUPERUSER); owned_fd fd = connect_daemon(+RequestCode::SUPERUSER);
// Send request // Send request
req.write_to_fd(fd); req.write_to_fd(fd);
@@ -248,23 +241,17 @@ int su_client_main(int argc, char *argv[]) {
if (atty) { if (atty) {
// We need a PTY. Get one. // We need a PTY. Get one.
write_int(fd, 1); write_int(fd, 1);
ptmx = recv_fd(fd); int ptmx = recv_fd(fd);
} else {
write_int(fd, 0);
}
if (atty) {
setup_sighandlers(sighandler); setup_sighandlers(sighandler);
// if stdin is not a tty, if we pump to ptmx, our process may intercept the input to ptmx and // 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. // output to stdout, which cause the target process lost input.
pump_tty(ptmx, (atty & ATTY_IN) ? ptmx : -1); pump_tty(ptmx, (atty & ATTY_IN) ? ptmx : -1);
} else {
write_int(fd, 0);
} }
// Get the exit code // Get the exit code
int code = read_int(fd); return read_int(fd);
close(fd);
return code;
} }
static void drop_caps() { static void drop_caps() {
@@ -300,7 +287,9 @@ static bool proc_is_restricted(pid_t pid) {
auto bnd = "CapBnd:"sv; auto bnd = "CapBnd:"sv;
uint32_t data[_LINUX_CAPABILITY_U32S_3] = {}; uint32_t data[_LINUX_CAPABILITY_U32S_3] = {};
ssprintf(buf, sizeof(buf), "/proc/%d/status", pid); 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)) { if (line.starts_with(bnd)) {
auto p = line.begin(); auto p = line.begin();
advance(p, bnd.size()); advance(p, bnd.size());
@@ -445,7 +434,7 @@ void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
char path[32]; char path[32];
ssprintf(path, sizeof(path), "/proc/%d/cwd", pid); ssprintf(path, sizeof(path), "/proc/%d/cwd", pid);
char cwd[4096]; char cwd[4096];
if (realpath(path, cwd, sizeof(cwd)) > 0) if (canonical_path(path, cwd, sizeof(cwd)) > 0)
chdir(cwd); chdir(cwd);
ssprintf(path, sizeof(path), "/proc/%d/environ", pid); ssprintf(path, sizeof(path), "/proc/%d/environ", pid);
auto env = full_read(path); auto env = full_read(path);

View File

@@ -1,16 +1,15 @@
use crate::consts::MODULEROOT; use crate::consts::MODULEROOT;
use crate::daemon::{MagiskD, to_user_id}; use crate::daemon::{MagiskD, to_user_id};
use crate::ffi::{ use crate::ffi::{ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, update_deny_flags};
ZygiskRequest, ZygiskStateFlags, get_magisk_tmp, get_prop, set_prop, update_deny_flags, use crate::resetprop::{get_prop, set_prop};
};
use crate::socket::{IpcRead, UnixSocketExt}; use crate::socket::{IpcRead, UnixSocketExt};
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO}; use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO};
use base::{ use base::{
Directory, FsPathBuilder, LoggedError, LoggedResult, ResultExt, Utf8CStr, WriteExt, cstr, Directory, FsPathBuilder, LoggedResult, ResultExt, Utf8CStr, WriteExt, cstr, fork_dont_care,
fork_dont_care, libc, raw_cstr, warn, libc, log_err, raw_cstr, warn,
}; };
use std::fmt::Write; 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::os::unix::net::UnixStream;
use std::ptr; use std::ptr;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@@ -101,7 +100,7 @@ impl ZygiskState {
local.send_fds(&module_fds)?; local.send_fds(&module_fds)?;
} }
if local.read_decodable::<i32>()? != 0 { if local.read_decodable::<i32>()? != 0 {
Err(LoggedError::default())?; return log_err!();
} }
local local
}; };
@@ -132,34 +131,34 @@ impl ZygiskState {
if !self.lib_name.is_empty() { if !self.lib_name.is_empty() {
return; return;
} }
let orig = get_prop(NBPROP, false); let orig = get_prop(NBPROP);
self.lib_name = if orig.is_empty() || orig == "0" { self.lib_name = if orig.is_empty() || orig == "0" {
ZYGISKLDR.to_string() ZYGISKLDR.to_string()
} else { } else {
orig + ZYGISKLDR orig + ZYGISKLDR
}; };
set_prop(NBPROP, Utf8CStr::from_string(&mut self.lib_name), false); set_prop(NBPROP, Utf8CStr::from_string(&mut self.lib_name));
// Whether Huawei's Maple compiler is enabled. // Whether Huawei's Maple compiler is enabled.
// If so, system server will be created by a special Zygote which ignores the native bridge // 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. // and make system server out of our control. Avoid it by disabling.
if get_prop(cstr!("ro.maple.enable"), false) == "1" { if get_prop(cstr!("ro.maple.enable")) == "1" {
set_prop(cstr!("ro.maple.enable"), cstr!("0"), false); set_prop(cstr!("ro.maple.enable"), cstr!("0"));
} }
} }
fn restore_prop(&mut self) { pub fn restore_prop(&mut self) {
let mut orig = "0".to_string(); let mut orig = "0".to_string();
if self.lib_name.len() > ZYGISKLDR.len() { if self.lib_name.len() > ZYGISKLDR.len() {
orig = self.lib_name[ZYGISKLDR.len()..].to_string(); orig = self.lib_name[ZYGISKLDR.len()..].to_string();
} }
set_prop(NBPROP, Utf8CStr::from_string(&mut orig), false); set_prop(NBPROP, Utf8CStr::from_string(&mut orig));
self.lib_name.clear(); self.lib_name.clear();
} }
} }
impl MagiskD { impl MagiskD {
pub fn zygisk_handler(&self, client: i32) { pub fn zygisk_handler(&self, client: OwnedFd) {
let mut client = unsafe { UnixStream::from_raw_fd(client) }; let mut client = UnixStream::from(client);
let _: LoggedResult<()> = try { let _: LoggedResult<()> = try {
let code = ZygiskRequest { let code = ZygiskRequest {
repr: client.read_decodable()?, repr: client.read_decodable()?,
@@ -256,12 +255,4 @@ impl MagiskD {
pub fn zygisk_enabled(&self) -> bool { pub fn zygisk_enabled(&self) -> bool {
self.zygisk_enabled.load(Ordering::Acquire) self.zygisk_enabled.load(Ordering::Acquire)
} }
pub fn zygisk_reset(&self, restore: bool) {
self.zygisk.lock().unwrap().reset(restore);
}
pub fn restore_zygisk_prop(&self) {
self.zygisk.lock().unwrap().restore_prop();
}
} }

View File

@@ -2,7 +2,6 @@
#include <android/dlext.h> #include <android/dlext.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <consts.hpp>
#include <base.hpp> #include <base.hpp>
#include <core.hpp> #include <core.hpp>

View File

@@ -366,7 +366,7 @@ void HookContext::post_native_bridge_load(void *handle) {
auto nb = get_prop(NBPROP); auto nb = get_prop(NBPROP);
auto len = sizeof(ZYGISKLDR) - 1; auto len = sizeof(ZYGISKLDR) - 1;
if (nb.size() > len) { 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; runtime_callbacks = arg.callbacks;
} }

View File

@@ -23,106 +23,6 @@ LOCAL_SRC_FILES := \
lz4/lib/xxhash.c lz4/lib/xxhash.c
include $(BUILD_STATIC_LIBRARY) 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 SE_PATH := $(LOCAL_PATH)/selinux
# libsepol.a # 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::path::Path;
use std::{fs, io, process}; use std::{fs, io, process};
use cxx_gen::Opt; use cxx_gen::{Include, IncludeKind, Opt};
trait ResultExt<T> { trait ResultExt<T> {
fn ok_or_exit(self) -> 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) { pub fn gen_cxx_binding(name: &str) {
println!("cargo:rerun-if-changed=lib.rs"); 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); 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}.cpp"), code.implementation.as_slice()).ok_or_exit();
write_if_diff(format!("{name}.hpp"), code.header.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; extern int SDK_INT;
#define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user") #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" #include "init-rs.hpp"
int magisk_proxy_main(int, char *argv[]); int magisk_proxy_main(int, char *argv[]);
rust::Utf8CStr backup_init(); Utf8CStr backup_init();
// Expose some constants to Rust // Expose some constants to Rust
static inline rust::Utf8CStr split_plat_cil() { static inline Utf8CStr split_plat_cil() {
return SPLIT_PLAT_CIL; return SPLIT_PLAT_CIL;
}; };
static inline rust::Utf8CStr preload_lib() { static inline Utf8CStr preload_lib() {
return PRELOAD_LIB; return PRELOAD_LIB;
} }
static inline rust::Utf8CStr preload_policy() { static inline Utf8CStr preload_policy() {
return PRELOAD_POLICY; return PRELOAD_POLICY;
} }
static inline rust::Utf8CStr preload_ack() { static inline Utf8CStr preload_ack() {
return PRELOAD_ACK; return PRELOAD_ACK;
} }

View File

@@ -49,7 +49,6 @@ pub mod ffi {
unsafe extern "C++" { unsafe extern "C++" {
include!("init.hpp"); include!("init.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"] #[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>; type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;

View File

@@ -1,5 +1,5 @@
use base::{ use base::{
LOGGER, LogLevel, Logger, SilentResultExt, Utf8CStr, cstr, LOGGER, LogLevel, Logger, SilentLogExt, Utf8CStr, cstr,
libc::{ libc::{
O_CLOEXEC, O_RDWR, O_WRONLY, S_IFCHR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, SYS_dup3, O_CLOEXEC, O_RDWR, O_WRONLY, S_IFCHR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, SYS_dup3,
makedev, mknod, syscall, makedev, mknod, syscall,

View File

@@ -30,15 +30,15 @@ static void parse_device(devinfo *dev, const char *uevent) {
dev->devpath[0] = '\0'; dev->devpath[0] = '\0';
dev->dmname[0] = '\0'; dev->dmname[0] = '\0';
dev->devname[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") if (key == "MAJOR")
dev->major = parse_int(value.data()); dev->major = parse_int(value);
else if (key == "MINOR") else if (key == "MINOR")
dev->minor = parse_int(value.data()); dev->minor = parse_int(value);
else if (key == "DEVNAME") 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") else if (key == "PARTNAME")
strscpy(dev->partname, value.data(), sizeof(dev->devname)); strscpy(dev->partname, value.c_str(), sizeof(dev->devname));
return true; return true;
}); });

View File

@@ -29,10 +29,9 @@ pub(crate) fn switch_root(path: &Utf8CStr) {
if let Some(last_mount) = mounts if let Some(last_mount) = mounts
.range::<String, _>((Unbounded, Excluded(&info.target))) .range::<String, _>((Unbounded, Excluded(&info.target)))
.last() .last()
&& info.target.starts_with(&format!("{}/", *last_mount))
{ {
if info.target.starts_with(&format!("{}/", *last_mount)) { continue;
continue;
}
} }
let mut target = info.target.clone(); 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 // First patch init.rc
{ {
auto src = xopen_file(xopenat(src_fd, INIT_RC, O_RDONLY | O_CLOEXEC, 0), "re"); owned_fd src_rc = xopenat(src_fd, INIT_RC, O_RDONLY | O_CLOEXEC, 0);
if (!src) return false; if (src_rc < 0) return false;
if (writable) unlinkat(src_fd, INIT_RC, 0); 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"); 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); 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 // Do not start vaultkeeper
if (str_contains(line, "start vaultkeeper")) { if (line.sv().contains("start vaultkeeper")) {
LOGD("Remove vaultkeeper\n"); LOGD("Remove vaultkeeper\n");
return true; return true;
} }
// Do not run flash_recovery // 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"); 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; return true;
} }
// Samsung's persist.sys.zygote.early will cause Zygote to start before post-fs-data // 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"); 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; return true;
} }
// Else just write the line // Else just write the line
fprintf(dest.get(), "%s", line.data()); fprintf(dest_rc.get(), "%s", line.c_str());
return true; return true;
}); });
fprintf(dest.get(), "\n"); fprintf(dest_rc.get(), "\n");
// Inject custom rc scripts // Inject custom rc scripts
for (auto &script : rc_list) { for (auto &script : rc_list) {
// Replace template arguments of rc scripts with dynamic paths // Replace template arguments of rc scripts with dynamic paths
replace_all(script, "${MAGISKTMP}", tmp_path); 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(); rc_list.clear();
// Inject Magisk rc scripts // 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 // Then patch init.zygote*.rc
for (dirent *entry; (entry = readdir(src_dir.get()));) { 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 name = std::string_view(entry->d_name);
auto src = xopen_file(xopenat(src_fd, name.data(), O_RDONLY | O_CLOEXEC, 0), "re"); if (!name.starts_with("init.zygote") || !name.ends_with(".rc")) continue;
if (!src) continue; }
if (writable) unlinkat(src_fd, name.data(), 0); owned_fd src_rc = xopenat(src_fd, entry->d_name, O_RDONLY | O_CLOEXEC, 0);
auto dest = xopen_file( if (src_rc < 0) continue;
xopenat(dest_fd, name.data(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we"); if (writable) unlinkat(src_fd, entry->d_name, 0);
if (!dest) continue; auto dest_rc = xopen_file(
LOGD("Patching %s in %s\n", name.data(), src_path); xopenat(dest_fd, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we");
file_readline(false, src.get(), [&dest, &tmp_path](string_view line) -> bool { if (!dest_rc) continue;
if (line.starts_with("service zygote ")) { 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"); LOGD("Inject zygote restart\n");
fprintf(dest.get(), "%s", line.data()); fprintf(dest_rc.get(), "%s", line.c_str());
fprintf(dest.get(), fprintf(dest_rc.get(),
" onrestart exec " MAGISK_PROC_CON " 0 0 -- %s/magisk --zygote-restart\n", tmp_path); " onrestart exec " MAGISK_PROC_CON " 0 0 -- %s/magisk --zygote-restart\n", tmp_path);
return true; return true;
} }
fprintf(dest.get(), "%s", line.data()); fprintf(dest_rc.get(), "%s", line.c_str());
return true; 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; 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); mkdirs(ROOTOVL "/system/bin", 0755);
if (auto target_fissiond = xopen_file(ROOTOVL "/system/bin/fissiond", "we")) { 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"); 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"); LOGD("hijacked isolated\n");
xumount2("/sys/devices/system/cpu/isolated", MNT_DETACH); xumount2("/sys/devices/system/cpu/isolated", MNT_DETACH);
unlink(INTLROOT "/isolated"); unlink(INTLROOT "/isolated");
string content; string content = full_read(fileno(hijack.get()));
full_read(fileno(hijack.get()), content);
{ {
string target = "/dev/cells/cell2"s + tmp_path; string target = "/dev/cells/cell2"s + tmp_path;
xmkdirs(target.data(), 0); xmkdirs(target.data(), 0);
@@ -185,7 +186,7 @@ static void load_overlay_rc(const char *overlay) {
char buf[NAME_MAX + 2]; char buf[NAME_MAX + 2];
buf[0] = '/'; buf[0] = '/';
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (!str_ends(entry->d_name, ".rc")) { if (!string_view(entry->d_name).ends_with(".rc")) {
continue; continue;
} }
strscpy(buf + 1, entry->d_name, sizeof(buf) - 1); 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); LOGD("Patch @ %08zX [android,fstab] -> [xxx]\n", off);
} }
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC, 0); 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); fclone_attr(src, dest);
close(src); close(src);
close(dest); close(dest);
@@ -408,7 +409,7 @@ static void unxz_init(const char *init_xz, const char *init) {
unlink(init_xz); unlink(init_xz);
} }
rust::Utf8CStr backup_init() { Utf8CStr backup_init() {
if (access("/.backup/init.xz", F_OK) == 0) if (access("/.backup/init.xz", F_OK) == 0)
unxz_init("/.backup/init.xz", "/.backup/init"); unxz_init("/.backup/init.xz", "/.backup/init");
return "/.backup/init"; return "/.backup/init";

View File

@@ -46,7 +46,7 @@ impl MagiskInit {
pub(crate) fn parse_config_file(&mut self) { pub(crate) fn parse_config_file(&mut self) {
if let Ok(fd) = cstr!("/data/.backup/.magisk").open(O_RDONLY) { if let Ok(fd) = cstr!("/data/.backup/.magisk").open(O_RDONLY) {
let mut reader = BufReader::new(fd); let mut reader = BufReader::new(fd);
reader.foreach_props(|key, val| { reader.for_each_prop(|key, val| {
if key == "PREINITDEVICE" { if key == "PREINITDEVICE" {
self.preinit_dev = val.to_string(); self.preinit_dev = val.to_string();
return false; return false;

View File

@@ -2,8 +2,8 @@ use crate::ffi::SePolicy;
use crate::statement::format_statement_help; use crate::statement::format_statement_help;
use argh::FromArgs; use argh::FromArgs;
use base::{ use base::{
EarlyExitExt, FmtAdaptor, LoggedResult, Utf8CStr, cmdline_logging, cstr, libc::umask, log_err, CmdArgs, EarlyExitExt, FmtAdaptor, LoggedResult, Utf8CString, cmdline_logging, cstr,
map_args, libc::umask, log_err,
}; };
use std::ffi::c_char; use std::ffi::c_char;
use std::io::stderr; use std::io::stderr;
@@ -26,13 +26,13 @@ struct Cli {
print_rules: bool, print_rules: bool,
#[argh(option)] #[argh(option)]
load: Option<String>, load: Option<Utf8CString>,
#[argh(option)] #[argh(option)]
save: Option<String>, save: Option<Utf8CString>,
#[argh(option)] #[argh(option)]
apply: Vec<String>, apply: Vec<Utf8CString>,
#[argh(positional)] #[argh(positional)]
polices: Vec<String>, polices: Vec<String>,
@@ -79,22 +79,23 @@ pub unsafe extern "C" fn main(
} }
let res: LoggedResult<()> = try { let res: LoggedResult<()> = try {
let cmds = map_args(argc, argv)?; let cmds = CmdArgs::new(argc, argv);
let cmds = cmds.as_slice();
if argc < 2 { if argc < 2 {
print_usage(cmds.first().unwrap_or(&"magiskpolicy")); print_usage(cmds.first().unwrap_or(&"magiskpolicy"));
return 1; 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) { let mut sepol = match (cli.load, cli.load_split, cli.compile_split) {
(Some(file), false, false) => SePolicy::from_file(Utf8CStr::from_string(file)), (Some(file), false, false) => SePolicy::from_file(&file),
(None, true, false) => SePolicy::from_split(), (None, true, false) => SePolicy::from_split(),
(None, false, true) => SePolicy::compile_split(), (None, false, true) => SePolicy::compile_split(),
(None, false, false) => SePolicy::from_file(cstr!("/sys/fs/selinux/policy")), (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() { if sepol._impl.is_null() {
Err(log_err!("Cannot load policy"))?; log_err!("Cannot load policy")?;
} }
if cli.print_rules { if cli.print_rules {
@@ -104,7 +105,7 @@ pub unsafe extern "C" fn main(
|| cli.live || cli.live
|| cli.save.is_some() || cli.save.is_some()
{ {
Err(log_err!("Cannot print rules with other options"))?; log_err!("Cannot print rules with other options")?;
} }
sepol.print_rules(); sepol.print_rules();
return 0; return 0;
@@ -114,8 +115,8 @@ pub unsafe extern "C" fn main(
sepol.magisk_rules(); sepol.magisk_rules();
} }
for file in &mut cli.apply { for file in cli.apply {
sepol.load_rule_file(Utf8CStr::from_string(file)); sepol.load_rule_file(&file);
} }
for statement in &cli.polices { 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")) { 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 let Some(file) = cli.save
if !sepol.to_file(Utf8CStr::from_string(file)) { && !sepol.to_file(&file)
Err(log_err!("Cannot dump policy to {}", file))?; {
} log_err!("Cannot dump policy to {}", file)?;
} }
}; };
if res.is_ok() { 0 } else { 1 } if res.is_ok() { 0 } else { 1 }

View File

@@ -30,7 +30,6 @@ pub mod ffi {
include!("policy.hpp"); include!("policy.hpp");
include!("../base/include/base.hpp"); include!("../base/include/base.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"] #[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>; type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;

View File

@@ -4,7 +4,7 @@
#include <map> #include <map>
#include <string_view> #include <string_view>
#include <cxx.h> #include <rust/cxx.h>
#include <sepol/policydb/policydb.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) { static void load_cil(struct cil_db *db, const char *file) {
mmap_data d(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); 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)}; 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()); LOGD("Load policy from: %.*s\n", static_cast<int>(file.size()), file.data());
policy_file_t pf; policy_file_t pf;
@@ -235,7 +235,7 @@ static int vec_write(void *v, const char *buf, int len) {
return 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 // 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 // first dump everything into memory, then directly call write system call
vector<char> out; 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, ()> { fn match_string<'a>(tokens: &mut Tokens<'a>, pattern: &str) -> ParseResult<'a, ()> {
if let Some(Token::ID(s)) = tokens.next() { if let Some(Token::ID(s)) = tokens.next()
if s == pattern { && s == pattern
return Ok(()); {
} return Ok(());
} }
Err(ParseError::General) 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(); let mut tokens = Vec::new();
for s in statement.split_whitespace() { for s in statement.split_whitespace() {
extract_token(s, &mut tokens); extract_token(s, &mut tokens);
@@ -287,7 +287,7 @@ impl SePolicy {
} }
fn load_rules_from_reader<T: BufRead>(&mut self, reader: &mut T) { 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); self.parse_statement(line);
true true
}); });

View File

@@ -4,10 +4,7 @@ set -e
shopt -s extglob shopt -s extglob
. scripts/test_common.sh . scripts/test_common.sh
emu_port=5682 emu_args_base="-no-window -no-audio -no-boot-anim -gpu swiftshader_indirect -read-only -no-snapshot -cores $core_count"
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"
log_args="-show-kernel -logcat '' -logcat-output logcat.log" log_args="-show-kernel -logcat '' -logcat-output logcat.log"
emu_args= emu_args=
emu_pid= emu_pid=
@@ -175,6 +172,11 @@ test_main() {
local avd_pkg ramdisk local avd_pkg ramdisk
eval $(resolve_vars "emu_args avd_pkg ramdisk" $1 $2) 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" setup_emu "$avd_pkg"
# Restart ADB daemon just in case # Restart ADB daemon just in case